@flayerlabs/gamemode-client 0.4.0 → 0.4.2
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/embed.d.ts +11 -1
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.js +19 -0
- package/dist/embed.js.map +1 -1
- package/dist/host.d.ts +18 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +11 -0
- package/dist/host.js.map +1 -1
- package/package.json +2 -2
- package/src/embed.ts +29 -2
- package/src/host.ts +31 -0
package/dist/embed.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Host } from './live.js';
|
|
2
|
+
import type { MarketState, Readable } from './index.js';
|
|
2
3
|
import { type EmbedContext } from '@flayerlabs/gamemode-spec/frame';
|
|
3
4
|
/**
|
|
4
5
|
* Where frames come from and go to. The default is the real window; tests inject a pair of these
|
|
@@ -20,6 +21,15 @@ export interface ConnectHostOptions {
|
|
|
20
21
|
export interface EmbeddedHost {
|
|
21
22
|
host: Host;
|
|
22
23
|
context: EmbedContext;
|
|
24
|
+
/**
|
|
25
|
+
* The coin's market as the embedding page sees it, for `joinEconomy`'s `platform.market`.
|
|
26
|
+
*
|
|
27
|
+
* Always present, because "no market yet" is a state a chart can render — it starts
|
|
28
|
+
* `unavailable` and becomes `live` if and when the page pushes one. A page that never sends a
|
|
29
|
+
* `gm:market` frame leaves it `unavailable` forever, which is the honest answer and the one a
|
|
30
|
+
* game should draw an empty chart for rather than waiting on.
|
|
31
|
+
*/
|
|
32
|
+
market: Readable<MarketState>;
|
|
23
33
|
dispose(): void;
|
|
24
34
|
}
|
|
25
35
|
/**
|
package/dist/embed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,IAAI,EAAwB,MAAM,WAAW,CAAC;AACxE,OAAO,KAAK,EAAa,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnE,OAAO,EAIL,KAAK,YAAY,EAGlB,MAAM,iCAAiC,CAAC;AAuBzC;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACtF,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACnD,2FAA2F;IAC3F,YAAY,IAAI,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;;OAOG;IACH,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB;AAqCD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA8IhG"}
|
package/dist/embed.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { marketFrom } from './live.js';
|
|
2
|
+
import { Signal } from './signal.js';
|
|
1
3
|
import { FRAME_PROTOCOL_VERSION, hostFrameFrom, } from '@flayerlabs/gamemode-spec/frame';
|
|
2
4
|
/**
|
|
3
5
|
* The game's end of the frame protocol: a {@link Host} reconstructed over `postMessage`.
|
|
@@ -61,6 +63,12 @@ export async function connectHost(options = {}) {
|
|
|
61
63
|
const parent = endpoint.parentSource();
|
|
62
64
|
if (parent === null)
|
|
63
65
|
return null;
|
|
66
|
+
const marketSignal = new Signal({
|
|
67
|
+
status: 'unavailable',
|
|
68
|
+
prices: [],
|
|
69
|
+
trades: [],
|
|
70
|
+
marketCapUsd: null,
|
|
71
|
+
});
|
|
64
72
|
const pending = new Map();
|
|
65
73
|
let unlisten = () => { };
|
|
66
74
|
let disposed = false;
|
|
@@ -95,6 +103,12 @@ export async function connectHost(options = {}) {
|
|
|
95
103
|
resolveContext(frame.context);
|
|
96
104
|
return;
|
|
97
105
|
}
|
|
106
|
+
if (frame.type === 'gm:market') {
|
|
107
|
+
const market = marketFrom(frame.market);
|
|
108
|
+
if (market)
|
|
109
|
+
marketSignal.set(market);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
98
112
|
if (frame.type === 'gm:progress') {
|
|
99
113
|
const request = pending.get(frame.id);
|
|
100
114
|
if (request?.kind === 'buy') {
|
|
@@ -168,10 +182,15 @@ export async function connectHost(options = {}) {
|
|
|
168
182
|
return {
|
|
169
183
|
host,
|
|
170
184
|
context,
|
|
185
|
+
market: {
|
|
186
|
+
current: () => marketSignal.current(),
|
|
187
|
+
subscribe: (listener) => marketSignal.subscribe(listener),
|
|
188
|
+
},
|
|
171
189
|
dispose() {
|
|
172
190
|
disposed = true;
|
|
173
191
|
unlisten();
|
|
174
192
|
pending.clear();
|
|
193
|
+
marketSignal.clear();
|
|
175
194
|
},
|
|
176
195
|
};
|
|
177
196
|
}
|
package/dist/embed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"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"}
|
package/dist/host.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Host } from './live.js';
|
|
2
2
|
import { type EmbedContext, type HostErrorCode } from '@flayerlabs/gamemode-spec/frame';
|
|
3
|
+
import type { WireMarketState } from '@flayerlabs/gamemode-spec/live';
|
|
3
4
|
/**
|
|
4
5
|
* The page's end of the frame protocol: service a game iframe's Host calls.
|
|
5
6
|
*
|
|
@@ -31,6 +32,18 @@ export interface PostTarget {
|
|
|
31
32
|
export interface HostListenSource {
|
|
32
33
|
listen(handler: (data: unknown, origin: string, source: unknown) => void): () => void;
|
|
33
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The page's live market, if it has one to share.
|
|
37
|
+
*
|
|
38
|
+
* Structurally a `Signal` (and anything else with these two methods), so a page that already
|
|
39
|
+
* keeps its chart in an observable hands the same one over rather than building a second.
|
|
40
|
+
* `current()` may answer null: "I have no market yet" is not the same claim as an `unavailable`
|
|
41
|
+
* market, and a page should not have to invent one to attach a host.
|
|
42
|
+
*/
|
|
43
|
+
export interface MarketSource {
|
|
44
|
+
current(): WireMarketState | null;
|
|
45
|
+
subscribe(listener: (market: WireMarketState) => void): () => void;
|
|
46
|
+
}
|
|
34
47
|
export interface AttachGameHostOptions {
|
|
35
48
|
/** The iframe element (or anything with its `contentWindow`). Read at send time, not attach time. */
|
|
36
49
|
frame: {
|
|
@@ -41,6 +54,11 @@ export interface AttachGameHostOptions {
|
|
|
41
54
|
/** What the game needs to join its round. A function is read fresh per send. */
|
|
42
55
|
context: EmbedContext | (() => EmbedContext);
|
|
43
56
|
host: Host;
|
|
57
|
+
/**
|
|
58
|
+
* Where the coin's market comes from. Optional — a page with no chart of its own attaches
|
|
59
|
+
* exactly as before, and the game's market simply stays `unavailable`.
|
|
60
|
+
*/
|
|
61
|
+
market?: MarketSource;
|
|
44
62
|
endpoint?: HostListenSource;
|
|
45
63
|
}
|
|
46
64
|
/** Attach the page's Host to a game iframe. Returns detach; after it, nothing is ever posted again. */
|
package/dist/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAElB,IAAI,EAAE,aAAa;gBAAnB,IAAI,EAAE,aAAa,EACnC,OAAO,EAAE,MAAM;CAKlB;AAED,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D;AAED,wFAAwF;AACxF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACvF;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,IAAI,eAAe,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACpE;AAED,MAAM,WAAW,qBAAqB;IACpC,qGAAqG;IACrG,KAAK,EAAE;QAAE,aAAa,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;IAC5C,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,OAAO,EAAE,YAAY,GAAG,CAAC,MAAM,YAAY,CAAC,CAAC;IAC7C,IAAI,EAAE,IAAI,CAAC;IACX;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAiBD,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAiHzE"}
|
package/dist/host.js
CHANGED
|
@@ -54,6 +54,7 @@ export function attachGameHost(options) {
|
|
|
54
54
|
const context = typeof options.context === 'function' ? options.context() : options.context;
|
|
55
55
|
send({ v: FRAME_PROTOCOL_VERSION, type: 'gm:context', context });
|
|
56
56
|
};
|
|
57
|
+
const sendMarket = (market) => send({ v: FRAME_PROTOCOL_VERSION, type: 'gm:market', market });
|
|
57
58
|
const unlisten = endpoint.listen((data, origin, source) => {
|
|
58
59
|
// Both checks, always: origin says who wrote the frame, source says which window sent it.
|
|
59
60
|
// This listener shares the page's window with every other frame the page hosts.
|
|
@@ -64,6 +65,12 @@ export function attachGameHost(options) {
|
|
|
64
65
|
return;
|
|
65
66
|
if (parsed.type === 'gm:hello') {
|
|
66
67
|
sendContext();
|
|
68
|
+
// A hello means the game has only just started listening, so the market it missed is
|
|
69
|
+
// re-sent alongside the context. Cheap, and it spares the game an empty first chart while
|
|
70
|
+
// it waits for whatever moves the price next.
|
|
71
|
+
const current = options.market?.current();
|
|
72
|
+
if (current)
|
|
73
|
+
sendMarket(current);
|
|
67
74
|
return;
|
|
68
75
|
}
|
|
69
76
|
const { id, call } = parsed;
|
|
@@ -134,8 +141,12 @@ export function attachGameHost(options) {
|
|
|
134
141
|
});
|
|
135
142
|
// Unprompted, because the frame may have said hello before this listener existed.
|
|
136
143
|
sendContext();
|
|
144
|
+
// Subscribing delivers the current value immediately, so this both seeds the game and keeps it
|
|
145
|
+
// current; `send` is a no-op once detached, so a late tick cannot reach a frame we let go of.
|
|
146
|
+
const unsubscribeMarket = options.market?.subscribe(sendMarket) ?? (() => { });
|
|
137
147
|
return () => {
|
|
138
148
|
detached = true;
|
|
149
|
+
unsubscribeMarket();
|
|
139
150
|
unlisten();
|
|
140
151
|
};
|
|
141
152
|
}
|
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,EACtB,aAAa,GAId,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,EACtB,aAAa,GAId,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAElB;IADlB,YACkB,IAAmB,EACnC,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAe;QAInC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAyCD,SAAS,kBAAkB;IACzB,OAAO;QACL,MAAM,CAAC,OAAO;YACZ,MAAM,SAAS,GAAG,CAAC,KAAmB,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC3F,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC9C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,KAAc,EAA4C,EAAE,CAC7E,KAAK,YAAY,WAAW;IAC1B,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;IAC9C,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC;AAEnG,uGAAuG;AACvG,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,kBAAkB,EAAE,CAAC;IAC1D,gGAAgG;IAChG,4FAA4F;IAC5F,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,IAAI,GAAG,CAAC,OAAwB,EAAQ,EAAE;QAC9C,IAAI,QAAQ;YAAE,OAAO;QACrB,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACxD,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,GAAS,EAAE;QAC7B,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QAC5F,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,MAAuB,EAAQ,EAAE,CACnD,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QACxD,0FAA0F;QAC1F,gFAAgF;QAChF,IAAI,QAAQ,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,KAAK,CAAC,aAAa;YAAE,OAAO;QAChF,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,WAAW,EAAE,CAAC;YACd,qFAAqF;YACrF,0FAA0F;YAC1F,8CAA8C;YAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC1C,IAAI,OAAO;gBAAE,UAAU,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAED,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;QAC5B,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC9B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;oBACrC,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;oBAC1G,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC7B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAClD,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;oBAC3G,OAAO;gBACT,CAAC;gBACD,qFAAqF;gBACrF,uFAAuF;gBACvF,wEAAwE;gBACxE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;gBACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wCAAwC,EAAE,EAAE,CAAC,CAAC;oBACjJ,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE;oBAC/E,IAAI,CAAC;wBACH,CAAC,EAAE,sBAAsB;wBACzB,IAAI,EAAE,aAAa;wBACnB,EAAE;wBACF,QAAQ,EACN,QAAQ,CAAC,eAAe,KAAK,SAAS;4BACpC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,CAAC,eAAe,EAAE;4BACjE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE;qBAC3B,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBACH,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;oBAC1B,IAAI,CAAC;wBACH,CAAC,EAAE,sBAAsB;wBACzB,IAAI,EAAE,QAAQ;wBACd,EAAE;wBACF,EAAE,EAAE,IAAI;wBACR,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE;qBAC9E,CAAC,CAAC;gBACL,CAAC;qBAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;oBAC3C,IAAI,CAAC;wBACH,CAAC,EAAE,sBAAsB;wBACzB,IAAI,EAAE,QAAQ;wBACd,EAAE;wBACF,EAAE,EAAE,IAAI;wBACR,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE;qBAC/D,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,oFAAoF;oBACpF,kDAAkD;oBAClD,IAAI,CAAC;wBACH,CAAC,EAAE,sBAAsB;wBACzB,IAAI,EAAE,QAAQ;wBACd,EAAE;wBACF,EAAE,EAAE,IAAI;wBACR,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE;qBACrF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,EAAE,CAAC,EAAE,sBAAsB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,kFAAkF;IAClF,WAAW,EAAE,CAAC;IACd,+FAA+F;IAC/F,8FAA8F;IAC9F,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAE9E,OAAO,GAAG,EAAE;QACV,QAAQ,GAAG,IAAI,CAAC;QAChB,iBAAiB,EAAE,CAAC;QACpB,QAAQ,EAAE,CAAC;IACb,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flayerlabs/gamemode-client",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Browser and mock-room client for Flaunch Game Modes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Flayer Labs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"ws": "^8.21.3"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@flayerlabs/gamemode-spec": "^0.4.
|
|
36
|
+
"@flayerlabs/gamemode-spec": "^0.4.2"
|
|
37
37
|
},
|
|
38
38
|
"types": "./dist/index.d.ts",
|
|
39
39
|
"exports": {
|
package/src/embed.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { BuyResult } from './index.js';
|
|
1
|
+
import { marketFrom, type Host, type HostBuyProgress } from './live.js';
|
|
2
|
+
import type { BuyResult, MarketState, Readable } from './index.js';
|
|
3
|
+
import { Signal } from './signal.js';
|
|
3
4
|
import {
|
|
4
5
|
FRAME_PROTOCOL_VERSION,
|
|
5
6
|
hostFrameFrom,
|
|
@@ -52,6 +53,15 @@ export interface ConnectHostOptions {
|
|
|
52
53
|
export interface EmbeddedHost {
|
|
53
54
|
host: Host;
|
|
54
55
|
context: EmbedContext;
|
|
56
|
+
/**
|
|
57
|
+
* The coin's market as the embedding page sees it, for `joinEconomy`'s `platform.market`.
|
|
58
|
+
*
|
|
59
|
+
* Always present, because "no market yet" is a state a chart can render — it starts
|
|
60
|
+
* `unavailable` and becomes `live` if and when the page pushes one. A page that never sends a
|
|
61
|
+
* `gm:market` frame leaves it `unavailable` forever, which is the honest answer and the one a
|
|
62
|
+
* game should draw an empty chart for rather than waiting on.
|
|
63
|
+
*/
|
|
64
|
+
market: Readable<MarketState>;
|
|
55
65
|
dispose(): void;
|
|
56
66
|
}
|
|
57
67
|
|
|
@@ -106,6 +116,12 @@ export async function connectHost(options: ConnectHostOptions = {}): Promise<Emb
|
|
|
106
116
|
const parent = endpoint.parentSource();
|
|
107
117
|
if (parent === null) return null;
|
|
108
118
|
|
|
119
|
+
const marketSignal = new Signal<MarketState>({
|
|
120
|
+
status: 'unavailable',
|
|
121
|
+
prices: [],
|
|
122
|
+
trades: [],
|
|
123
|
+
marketCapUsd: null,
|
|
124
|
+
});
|
|
109
125
|
const pending = new Map<string, Pending>();
|
|
110
126
|
let unlisten: () => void = () => {};
|
|
111
127
|
let disposed = false;
|
|
@@ -141,6 +157,12 @@ export async function connectHost(options: ConnectHostOptions = {}): Promise<Emb
|
|
|
141
157
|
return;
|
|
142
158
|
}
|
|
143
159
|
|
|
160
|
+
if (frame.type === 'gm:market') {
|
|
161
|
+
const market = marketFrom(frame.market);
|
|
162
|
+
if (market) marketSignal.set(market);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
144
166
|
if (frame.type === 'gm:progress') {
|
|
145
167
|
const request = pending.get(frame.id);
|
|
146
168
|
if (request?.kind === 'buy') {
|
|
@@ -217,10 +239,15 @@ export async function connectHost(options: ConnectHostOptions = {}): Promise<Emb
|
|
|
217
239
|
return {
|
|
218
240
|
host,
|
|
219
241
|
context,
|
|
242
|
+
market: {
|
|
243
|
+
current: () => marketSignal.current(),
|
|
244
|
+
subscribe: (listener) => marketSignal.subscribe(listener),
|
|
245
|
+
},
|
|
220
246
|
dispose() {
|
|
221
247
|
disposed = true;
|
|
222
248
|
unlisten();
|
|
223
249
|
pending.clear();
|
|
250
|
+
marketSignal.clear();
|
|
224
251
|
},
|
|
225
252
|
};
|
|
226
253
|
}
|
package/src/host.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type HostErrorCode,
|
|
7
7
|
type HostToGameFrame,
|
|
8
8
|
} from '@flayerlabs/gamemode-spec/frame';
|
|
9
|
+
import type { WireMarketState } from '@flayerlabs/gamemode-spec/live';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* The page's end of the frame protocol: service a game iframe's Host calls.
|
|
@@ -47,6 +48,19 @@ export interface HostListenSource {
|
|
|
47
48
|
listen(handler: (data: unknown, origin: string, source: unknown) => void): () => void;
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* The page's live market, if it has one to share.
|
|
53
|
+
*
|
|
54
|
+
* Structurally a `Signal` (and anything else with these two methods), so a page that already
|
|
55
|
+
* keeps its chart in an observable hands the same one over rather than building a second.
|
|
56
|
+
* `current()` may answer null: "I have no market yet" is not the same claim as an `unavailable`
|
|
57
|
+
* market, and a page should not have to invent one to attach a host.
|
|
58
|
+
*/
|
|
59
|
+
export interface MarketSource {
|
|
60
|
+
current(): WireMarketState | null;
|
|
61
|
+
subscribe(listener: (market: WireMarketState) => void): () => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
export interface AttachGameHostOptions {
|
|
51
65
|
/** The iframe element (or anything with its `contentWindow`). Read at send time, not attach time. */
|
|
52
66
|
frame: { contentWindow: PostTarget | null };
|
|
@@ -55,6 +69,11 @@ export interface AttachGameHostOptions {
|
|
|
55
69
|
/** What the game needs to join its round. A function is read fresh per send. */
|
|
56
70
|
context: EmbedContext | (() => EmbedContext);
|
|
57
71
|
host: Host;
|
|
72
|
+
/**
|
|
73
|
+
* Where the coin's market comes from. Optional — a page with no chart of its own attaches
|
|
74
|
+
* exactly as before, and the game's market simply stays `unavailable`.
|
|
75
|
+
*/
|
|
76
|
+
market?: MarketSource;
|
|
58
77
|
endpoint?: HostListenSource;
|
|
59
78
|
}
|
|
60
79
|
|
|
@@ -91,6 +110,9 @@ export function attachGameHost(options: AttachGameHostOptions): () => void {
|
|
|
91
110
|
send({ v: FRAME_PROTOCOL_VERSION, type: 'gm:context', context });
|
|
92
111
|
};
|
|
93
112
|
|
|
113
|
+
const sendMarket = (market: WireMarketState): void =>
|
|
114
|
+
send({ v: FRAME_PROTOCOL_VERSION, type: 'gm:market', market });
|
|
115
|
+
|
|
94
116
|
const unlisten = endpoint.listen((data, origin, source) => {
|
|
95
117
|
// Both checks, always: origin says who wrote the frame, source says which window sent it.
|
|
96
118
|
// This listener shares the page's window with every other frame the page hosts.
|
|
@@ -100,6 +122,11 @@ export function attachGameHost(options: AttachGameHostOptions): () => void {
|
|
|
100
122
|
|
|
101
123
|
if (parsed.type === 'gm:hello') {
|
|
102
124
|
sendContext();
|
|
125
|
+
// A hello means the game has only just started listening, so the market it missed is
|
|
126
|
+
// re-sent alongside the context. Cheap, and it spares the game an empty first chart while
|
|
127
|
+
// it waits for whatever moves the price next.
|
|
128
|
+
const current = options.market?.current();
|
|
129
|
+
if (current) sendMarket(current);
|
|
103
130
|
return;
|
|
104
131
|
}
|
|
105
132
|
|
|
@@ -170,9 +197,13 @@ export function attachGameHost(options: AttachGameHostOptions): () => void {
|
|
|
170
197
|
|
|
171
198
|
// Unprompted, because the frame may have said hello before this listener existed.
|
|
172
199
|
sendContext();
|
|
200
|
+
// Subscribing delivers the current value immediately, so this both seeds the game and keeps it
|
|
201
|
+
// current; `send` is a no-op once detached, so a late tick cannot reach a frame we let go of.
|
|
202
|
+
const unsubscribeMarket = options.market?.subscribe(sendMarket) ?? (() => {});
|
|
173
203
|
|
|
174
204
|
return () => {
|
|
175
205
|
detached = true;
|
|
206
|
+
unsubscribeMarket();
|
|
176
207
|
unlisten();
|
|
177
208
|
};
|
|
178
209
|
}
|