@taphubhq/sdk-core 0.24.1 → 0.24.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/index.cjs +48 -2
- package/dist/index.d.mts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +48 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -64,6 +64,35 @@ __export(index_exports, {
|
|
|
64
64
|
});
|
|
65
65
|
module.exports = __toCommonJS(index_exports);
|
|
66
66
|
|
|
67
|
+
// src/clock/ClockSync.ts
|
|
68
|
+
var ClockSync = class {
|
|
69
|
+
#offsetMs = 0;
|
|
70
|
+
#synced = false;
|
|
71
|
+
/** True once an offset has been recorded. */
|
|
72
|
+
get synced() {
|
|
73
|
+
return this.#synced;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Milliseconds to add to the client clock to reach the server clock.
|
|
77
|
+
* Returns 0 until the first successful sync.
|
|
78
|
+
*/
|
|
79
|
+
getOffset() {
|
|
80
|
+
return this.#offsetMs;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Record a single clock sample from a round-trip that returned the server time.
|
|
84
|
+
* No-op if already synced (set-once), or if any input is not a finite number — a
|
|
85
|
+
* missing/garbage `serverTime` must never corrupt the offset.
|
|
86
|
+
*/
|
|
87
|
+
recordSync(sample) {
|
|
88
|
+
if (this.#synced) return;
|
|
89
|
+
const { t0, serverTime, t1 } = sample;
|
|
90
|
+
if (!Number.isFinite(t0) || !Number.isFinite(serverTime) || !Number.isFinite(t1)) return;
|
|
91
|
+
this.#offsetMs = Math.round(serverTime + (t1 - t0) / 2 - t1);
|
|
92
|
+
this.#synced = true;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
67
96
|
// src/errors/index.ts
|
|
68
97
|
var TaphubError = class _TaphubError extends Error {
|
|
69
98
|
code;
|
|
@@ -518,8 +547,10 @@ var isCancelled = (bid) => bid.status === "cancelled";
|
|
|
518
547
|
// src/modules/bid/index.ts
|
|
519
548
|
var BidModule = class {
|
|
520
549
|
#graphql;
|
|
550
|
+
#getClockOffset;
|
|
521
551
|
constructor(deps) {
|
|
522
552
|
this.#graphql = deps.graphql;
|
|
553
|
+
this.#getClockOffset = deps.getClockOffset;
|
|
523
554
|
}
|
|
524
555
|
async placeBid(input, opts) {
|
|
525
556
|
const variables = {
|
|
@@ -537,6 +568,12 @@ var BidModule = class {
|
|
|
537
568
|
coefficient: input.coefficient,
|
|
538
569
|
amount: input.amount,
|
|
539
570
|
slippage: input.slippage,
|
|
571
|
+
// bid-260619-client-time-meta: auto-stamp the client's RAW send time and the
|
|
572
|
+
// measured clock offset so the server can record skew-corrected latency into
|
|
573
|
+
// bid.meta. Stamped here at dispatch (not caller-supplied) so `ct` is the actual
|
|
574
|
+
// send moment. The server treats both as untrusted tracing data only.
|
|
575
|
+
ct: Date.now(),
|
|
576
|
+
clockOffset: this.#getClockOffset(),
|
|
540
577
|
// QA-only late-bid passthrough (bid-260610). Omitted entirely when absent
|
|
541
578
|
// so no `bidToken: null` is sent. sdk-react sets it from localStorage.
|
|
542
579
|
...input.bidToken ? { bidToken: input.bidToken } : {}
|
|
@@ -930,6 +967,7 @@ function normalisePairInfo(node) {
|
|
|
930
967
|
|
|
931
968
|
// src/modules/pair/queries.ts
|
|
932
969
|
var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
|
|
970
|
+
serverTime
|
|
933
971
|
agencyPair(pairId: $pairId) {
|
|
934
972
|
id pair { id pair source thumb } status createdAt
|
|
935
973
|
config {
|
|
@@ -959,8 +997,10 @@ var BUILDER_AVAILABLE_GAME_PAIRS_QUERY = `query BuilderAvailableGamePairs($gamep
|
|
|
959
997
|
// src/modules/pair/index.ts
|
|
960
998
|
var PairModule = class {
|
|
961
999
|
#graphql;
|
|
1000
|
+
#clockSync;
|
|
962
1001
|
constructor(deps) {
|
|
963
1002
|
this.#graphql = deps.graphql;
|
|
1003
|
+
this.#clockSync = deps.clockSync;
|
|
964
1004
|
}
|
|
965
1005
|
// REVIEW[bid-260602-v2]: keyed by pairId (the #2 game_pairs.id, e.g. "grid-ETH-USD").
|
|
966
1006
|
// Dropped the `pair` symbol arg + `gameplaySlug` option — the gameplay is already
|
|
@@ -968,6 +1008,7 @@ var PairModule = class {
|
|
|
968
1008
|
async get(pairId, opts) {
|
|
969
1009
|
const variables = { pairId };
|
|
970
1010
|
let body;
|
|
1011
|
+
const t0 = Date.now();
|
|
971
1012
|
try {
|
|
972
1013
|
body = await this.#graphql.request(PAIR_QUERY, variables, opts);
|
|
973
1014
|
} catch (err) {
|
|
@@ -979,6 +1020,7 @@ var PairModule = class {
|
|
|
979
1020
|
}
|
|
980
1021
|
throw err;
|
|
981
1022
|
}
|
|
1023
|
+
this.#clockSync.recordSync({ t0, serverTime: body.serverTime, t1: Date.now() });
|
|
982
1024
|
if (body.agencyPair === null) {
|
|
983
1025
|
throw new TaphubValidationError("Game not found for pair", {
|
|
984
1026
|
code: "GameNotFound",
|
|
@@ -2858,8 +2900,12 @@ var TaphubClient = class {
|
|
|
2858
2900
|
this.user.clearCurrencies();
|
|
2859
2901
|
}
|
|
2860
2902
|
});
|
|
2861
|
-
|
|
2862
|
-
this.
|
|
2903
|
+
const clockSync = new ClockSync();
|
|
2904
|
+
this.pair = new PairModule({ graphql: this.#graphql, clockSync });
|
|
2905
|
+
this.bid = new BidModule({
|
|
2906
|
+
graphql: this.#graphql,
|
|
2907
|
+
getClockOffset: () => clockSync.getOffset()
|
|
2908
|
+
});
|
|
2863
2909
|
this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
|
|
2864
2910
|
this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
|
|
2865
2911
|
this.locale = new LocaleModule({ graphql: this.#graphql });
|
package/dist/index.d.mts
CHANGED
|
@@ -270,6 +270,7 @@ declare const isCancelled: (bid: Bid) => boolean;
|
|
|
270
270
|
|
|
271
271
|
interface BidModuleDeps {
|
|
272
272
|
graphql: GraphQLTransport;
|
|
273
|
+
getClockOffset: () => number;
|
|
273
274
|
}
|
|
274
275
|
declare class BidModule {
|
|
275
276
|
#private;
|
|
@@ -496,6 +497,44 @@ declare class LocaleModule {
|
|
|
496
497
|
}): Promise<LocaleRefreshResult>;
|
|
497
498
|
}
|
|
498
499
|
|
|
500
|
+
/**
|
|
501
|
+
* ClockSync estimates the offset between the client clock and the server clock, so that
|
|
502
|
+
* timestamps the client reports (e.g. a bid's send time) can be expressed on the server's
|
|
503
|
+
* timeline and latency measurements are not distorted by client clock skew.
|
|
504
|
+
*
|
|
505
|
+
* The offset is measured ONCE per session (set-once) from a single round-trip whose
|
|
506
|
+
* response carries the server time, using Cristian's algorithm:
|
|
507
|
+
*
|
|
508
|
+
* offset = serverTime + (t1 - t0) / 2 - t1
|
|
509
|
+
*
|
|
510
|
+
* where `t0` / `t1` are the client clock immediately before / after the request. `offset`
|
|
511
|
+
* is the number of milliseconds to ADD to the client clock to obtain the server clock.
|
|
512
|
+
*
|
|
513
|
+
* This is adequate for millisecond-grade latency tracing (the `bid-260619-server-time-sync`
|
|
514
|
+
* capability); it is intentionally NOT a high-precision NTP implementation, and it does not
|
|
515
|
+
* re-sync to correct drift.
|
|
516
|
+
*/
|
|
517
|
+
declare class ClockSync {
|
|
518
|
+
#private;
|
|
519
|
+
/** True once an offset has been recorded. */
|
|
520
|
+
get synced(): boolean;
|
|
521
|
+
/**
|
|
522
|
+
* Milliseconds to add to the client clock to reach the server clock.
|
|
523
|
+
* Returns 0 until the first successful sync.
|
|
524
|
+
*/
|
|
525
|
+
getOffset(): number;
|
|
526
|
+
/**
|
|
527
|
+
* Record a single clock sample from a round-trip that returned the server time.
|
|
528
|
+
* No-op if already synced (set-once), or if any input is not a finite number — a
|
|
529
|
+
* missing/garbage `serverTime` must never corrupt the offset.
|
|
530
|
+
*/
|
|
531
|
+
recordSync(sample: {
|
|
532
|
+
t0: number;
|
|
533
|
+
serverTime: number;
|
|
534
|
+
t1: number;
|
|
535
|
+
}): void;
|
|
536
|
+
}
|
|
537
|
+
|
|
499
538
|
interface Pair {
|
|
500
539
|
id: string;
|
|
501
540
|
pair: string;
|
|
@@ -572,6 +611,7 @@ interface Candle {
|
|
|
572
611
|
|
|
573
612
|
interface PairModuleDeps {
|
|
574
613
|
graphql: GraphQLTransport;
|
|
614
|
+
clockSync: ClockSync;
|
|
575
615
|
}
|
|
576
616
|
declare class PairModule {
|
|
577
617
|
#private;
|
package/dist/index.d.ts
CHANGED
|
@@ -270,6 +270,7 @@ declare const isCancelled: (bid: Bid) => boolean;
|
|
|
270
270
|
|
|
271
271
|
interface BidModuleDeps {
|
|
272
272
|
graphql: GraphQLTransport;
|
|
273
|
+
getClockOffset: () => number;
|
|
273
274
|
}
|
|
274
275
|
declare class BidModule {
|
|
275
276
|
#private;
|
|
@@ -496,6 +497,44 @@ declare class LocaleModule {
|
|
|
496
497
|
}): Promise<LocaleRefreshResult>;
|
|
497
498
|
}
|
|
498
499
|
|
|
500
|
+
/**
|
|
501
|
+
* ClockSync estimates the offset between the client clock and the server clock, so that
|
|
502
|
+
* timestamps the client reports (e.g. a bid's send time) can be expressed on the server's
|
|
503
|
+
* timeline and latency measurements are not distorted by client clock skew.
|
|
504
|
+
*
|
|
505
|
+
* The offset is measured ONCE per session (set-once) from a single round-trip whose
|
|
506
|
+
* response carries the server time, using Cristian's algorithm:
|
|
507
|
+
*
|
|
508
|
+
* offset = serverTime + (t1 - t0) / 2 - t1
|
|
509
|
+
*
|
|
510
|
+
* where `t0` / `t1` are the client clock immediately before / after the request. `offset`
|
|
511
|
+
* is the number of milliseconds to ADD to the client clock to obtain the server clock.
|
|
512
|
+
*
|
|
513
|
+
* This is adequate for millisecond-grade latency tracing (the `bid-260619-server-time-sync`
|
|
514
|
+
* capability); it is intentionally NOT a high-precision NTP implementation, and it does not
|
|
515
|
+
* re-sync to correct drift.
|
|
516
|
+
*/
|
|
517
|
+
declare class ClockSync {
|
|
518
|
+
#private;
|
|
519
|
+
/** True once an offset has been recorded. */
|
|
520
|
+
get synced(): boolean;
|
|
521
|
+
/**
|
|
522
|
+
* Milliseconds to add to the client clock to reach the server clock.
|
|
523
|
+
* Returns 0 until the first successful sync.
|
|
524
|
+
*/
|
|
525
|
+
getOffset(): number;
|
|
526
|
+
/**
|
|
527
|
+
* Record a single clock sample from a round-trip that returned the server time.
|
|
528
|
+
* No-op if already synced (set-once), or if any input is not a finite number — a
|
|
529
|
+
* missing/garbage `serverTime` must never corrupt the offset.
|
|
530
|
+
*/
|
|
531
|
+
recordSync(sample: {
|
|
532
|
+
t0: number;
|
|
533
|
+
serverTime: number;
|
|
534
|
+
t1: number;
|
|
535
|
+
}): void;
|
|
536
|
+
}
|
|
537
|
+
|
|
499
538
|
interface Pair {
|
|
500
539
|
id: string;
|
|
501
540
|
pair: string;
|
|
@@ -572,6 +611,7 @@ interface Candle {
|
|
|
572
611
|
|
|
573
612
|
interface PairModuleDeps {
|
|
574
613
|
graphql: GraphQLTransport;
|
|
614
|
+
clockSync: ClockSync;
|
|
575
615
|
}
|
|
576
616
|
declare class PairModule {
|
|
577
617
|
#private;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
// src/clock/ClockSync.ts
|
|
2
|
+
var ClockSync = class {
|
|
3
|
+
#offsetMs = 0;
|
|
4
|
+
#synced = false;
|
|
5
|
+
/** True once an offset has been recorded. */
|
|
6
|
+
get synced() {
|
|
7
|
+
return this.#synced;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Milliseconds to add to the client clock to reach the server clock.
|
|
11
|
+
* Returns 0 until the first successful sync.
|
|
12
|
+
*/
|
|
13
|
+
getOffset() {
|
|
14
|
+
return this.#offsetMs;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Record a single clock sample from a round-trip that returned the server time.
|
|
18
|
+
* No-op if already synced (set-once), or if any input is not a finite number — a
|
|
19
|
+
* missing/garbage `serverTime` must never corrupt the offset.
|
|
20
|
+
*/
|
|
21
|
+
recordSync(sample) {
|
|
22
|
+
if (this.#synced) return;
|
|
23
|
+
const { t0, serverTime, t1 } = sample;
|
|
24
|
+
if (!Number.isFinite(t0) || !Number.isFinite(serverTime) || !Number.isFinite(t1)) return;
|
|
25
|
+
this.#offsetMs = Math.round(serverTime + (t1 - t0) / 2 - t1);
|
|
26
|
+
this.#synced = true;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
1
30
|
// src/errors/index.ts
|
|
2
31
|
var TaphubError = class _TaphubError extends Error {
|
|
3
32
|
code;
|
|
@@ -452,8 +481,10 @@ var isCancelled = (bid) => bid.status === "cancelled";
|
|
|
452
481
|
// src/modules/bid/index.ts
|
|
453
482
|
var BidModule = class {
|
|
454
483
|
#graphql;
|
|
484
|
+
#getClockOffset;
|
|
455
485
|
constructor(deps) {
|
|
456
486
|
this.#graphql = deps.graphql;
|
|
487
|
+
this.#getClockOffset = deps.getClockOffset;
|
|
457
488
|
}
|
|
458
489
|
async placeBid(input, opts) {
|
|
459
490
|
const variables = {
|
|
@@ -471,6 +502,12 @@ var BidModule = class {
|
|
|
471
502
|
coefficient: input.coefficient,
|
|
472
503
|
amount: input.amount,
|
|
473
504
|
slippage: input.slippage,
|
|
505
|
+
// bid-260619-client-time-meta: auto-stamp the client's RAW send time and the
|
|
506
|
+
// measured clock offset so the server can record skew-corrected latency into
|
|
507
|
+
// bid.meta. Stamped here at dispatch (not caller-supplied) so `ct` is the actual
|
|
508
|
+
// send moment. The server treats both as untrusted tracing data only.
|
|
509
|
+
ct: Date.now(),
|
|
510
|
+
clockOffset: this.#getClockOffset(),
|
|
474
511
|
// QA-only late-bid passthrough (bid-260610). Omitted entirely when absent
|
|
475
512
|
// so no `bidToken: null` is sent. sdk-react sets it from localStorage.
|
|
476
513
|
...input.bidToken ? { bidToken: input.bidToken } : {}
|
|
@@ -864,6 +901,7 @@ function normalisePairInfo(node) {
|
|
|
864
901
|
|
|
865
902
|
// src/modules/pair/queries.ts
|
|
866
903
|
var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
|
|
904
|
+
serverTime
|
|
867
905
|
agencyPair(pairId: $pairId) {
|
|
868
906
|
id pair { id pair source thumb } status createdAt
|
|
869
907
|
config {
|
|
@@ -893,8 +931,10 @@ var BUILDER_AVAILABLE_GAME_PAIRS_QUERY = `query BuilderAvailableGamePairs($gamep
|
|
|
893
931
|
// src/modules/pair/index.ts
|
|
894
932
|
var PairModule = class {
|
|
895
933
|
#graphql;
|
|
934
|
+
#clockSync;
|
|
896
935
|
constructor(deps) {
|
|
897
936
|
this.#graphql = deps.graphql;
|
|
937
|
+
this.#clockSync = deps.clockSync;
|
|
898
938
|
}
|
|
899
939
|
// REVIEW[bid-260602-v2]: keyed by pairId (the #2 game_pairs.id, e.g. "grid-ETH-USD").
|
|
900
940
|
// Dropped the `pair` symbol arg + `gameplaySlug` option — the gameplay is already
|
|
@@ -902,6 +942,7 @@ var PairModule = class {
|
|
|
902
942
|
async get(pairId, opts) {
|
|
903
943
|
const variables = { pairId };
|
|
904
944
|
let body;
|
|
945
|
+
const t0 = Date.now();
|
|
905
946
|
try {
|
|
906
947
|
body = await this.#graphql.request(PAIR_QUERY, variables, opts);
|
|
907
948
|
} catch (err) {
|
|
@@ -913,6 +954,7 @@ var PairModule = class {
|
|
|
913
954
|
}
|
|
914
955
|
throw err;
|
|
915
956
|
}
|
|
957
|
+
this.#clockSync.recordSync({ t0, serverTime: body.serverTime, t1: Date.now() });
|
|
916
958
|
if (body.agencyPair === null) {
|
|
917
959
|
throw new TaphubValidationError("Game not found for pair", {
|
|
918
960
|
code: "GameNotFound",
|
|
@@ -2792,8 +2834,12 @@ var TaphubClient = class {
|
|
|
2792
2834
|
this.user.clearCurrencies();
|
|
2793
2835
|
}
|
|
2794
2836
|
});
|
|
2795
|
-
|
|
2796
|
-
this.
|
|
2837
|
+
const clockSync = new ClockSync();
|
|
2838
|
+
this.pair = new PairModule({ graphql: this.#graphql, clockSync });
|
|
2839
|
+
this.bid = new BidModule({
|
|
2840
|
+
graphql: this.#graphql,
|
|
2841
|
+
getClockOffset: () => clockSync.getOffset()
|
|
2842
|
+
});
|
|
2797
2843
|
this.leaderboard = new LeaderboardModule({ graphql: this.#graphql });
|
|
2798
2844
|
this.agencyPairs = new AgencyPairModule({ graphql: this.#graphql });
|
|
2799
2845
|
this.locale = new LocaleModule({ graphql: this.#graphql });
|