@looplay/sdk 0.4.0 → 0.5.0
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 +46 -1
- package/dist/looplay-sdk.cjs.js +161 -15
- package/dist/looplay-sdk.cjs.js.map +1 -1
- package/dist/looplay-sdk.esm.js +161 -15
- package/dist/looplay-sdk.esm.js.map +1 -1
- package/dist/looplay-sdk.min.js +2 -2
- package/dist/looplay-sdk.min.js.map +1 -1
- package/dist/types/iframe/iframe-auth.d.ts +20 -3
- package/dist/types/iframe/types.d.ts +20 -0
- package/package.json +1 -1
package/dist/looplay-sdk.esm.js
CHANGED
|
@@ -738,6 +738,7 @@ var TelegramAuthProvider = class {
|
|
|
738
738
|
// src/iframe/iframe-auth.ts
|
|
739
739
|
var DEFAULT_ANON_ID_STORAGE_KEY = "looplay:anon-id";
|
|
740
740
|
var DEFAULT_ANONYMOUS_FALLBACK_TIMEOUT_MS = 4e3;
|
|
741
|
+
var DEFAULT_REQUEST_ADS_TIMEOUT_MS = 5e3;
|
|
741
742
|
var INITIAL_STATE = {
|
|
742
743
|
jwt: null,
|
|
743
744
|
gameId: null,
|
|
@@ -787,6 +788,12 @@ var LooplayIframeAuth = class {
|
|
|
787
788
|
options;
|
|
788
789
|
messageListener = null;
|
|
789
790
|
anonymousFallbackTimer = null;
|
|
791
|
+
pendingAdsRequests = /* @__PURE__ */ new Map();
|
|
792
|
+
requestAdsSequence = 0;
|
|
793
|
+
adsRequestInFlight = false;
|
|
794
|
+
adsRequestsSent = 0;
|
|
795
|
+
lastAdsRequestAt = 0;
|
|
796
|
+
autoAdsActionCount = 0;
|
|
790
797
|
constructor(options = {}) {
|
|
791
798
|
this.options = options;
|
|
792
799
|
}
|
|
@@ -821,8 +828,57 @@ var LooplayIframeAuth = class {
|
|
|
821
828
|
this.debug("standalone mode detected; auth request skipped");
|
|
822
829
|
return;
|
|
823
830
|
}
|
|
824
|
-
this.debug("requesting auth from parent", {
|
|
825
|
-
|
|
831
|
+
this.debug("requesting auth from parent", {
|
|
832
|
+
targetOrigin: this.resolveParentOrigin() ?? "*"
|
|
833
|
+
});
|
|
834
|
+
window.parent.postMessage(
|
|
835
|
+
{ type: "LOOPLAY_AUTH_REQUEST" },
|
|
836
|
+
this.resolveParentOrigin() ?? "*"
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Requests the parent host to show a rewarded ad.
|
|
841
|
+
* In a Telegram Mini App setup, the game sends this request from the iframe
|
|
842
|
+
* and the parent page (the Mini App shell) bridges it to Adsgram, then
|
|
843
|
+
* responds with `LOOPLAY_ADS_WATCHED` after the ad finishes.
|
|
844
|
+
*/
|
|
845
|
+
requestAds(options = {}) {
|
|
846
|
+
if (typeof window === "undefined" || window.parent === window) {
|
|
847
|
+
this.debug("requestAds skipped; standalone mode detected");
|
|
848
|
+
return Promise.resolve(false);
|
|
849
|
+
}
|
|
850
|
+
if (this.adsRequestInFlight) {
|
|
851
|
+
this.debug("requestAds skipped; another ad request is already in flight");
|
|
852
|
+
return Promise.resolve(false);
|
|
853
|
+
}
|
|
854
|
+
const parentOrigin = this.resolveParentOrigin() ?? "*";
|
|
855
|
+
const requestId = `ads-${Date.now()}-${++this.requestAdsSequence}`;
|
|
856
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_ADS_TIMEOUT_MS;
|
|
857
|
+
this.adsRequestInFlight = true;
|
|
858
|
+
this.adsRequestsSent += 1;
|
|
859
|
+
this.lastAdsRequestAt = Date.now();
|
|
860
|
+
this.debug("requesting rewarded ad from parent", {
|
|
861
|
+
requestId,
|
|
862
|
+
targetOrigin: parentOrigin,
|
|
863
|
+
gameId: this.getGameId()
|
|
864
|
+
});
|
|
865
|
+
return new Promise((resolve) => {
|
|
866
|
+
const timer = setTimeout(() => {
|
|
867
|
+
this.pendingAdsRequests.delete(requestId);
|
|
868
|
+
this.adsRequestInFlight = false;
|
|
869
|
+
this.debug("requestAds timed out", { requestId, timeoutMs });
|
|
870
|
+
resolve(false);
|
|
871
|
+
}, timeoutMs);
|
|
872
|
+
this.pendingAdsRequests.set(requestId, { resolve, timer });
|
|
873
|
+
window.parent.postMessage(
|
|
874
|
+
{
|
|
875
|
+
type: "LOOPLAY_REQUEST_ADS",
|
|
876
|
+
requestId,
|
|
877
|
+
gameId: this.getGameId()
|
|
878
|
+
},
|
|
879
|
+
parentOrigin
|
|
880
|
+
);
|
|
881
|
+
});
|
|
826
882
|
}
|
|
827
883
|
/** Idempotent; attaches the postMessage listener (if embedded) and arms the anonymous fallback. */
|
|
828
884
|
init() {
|
|
@@ -846,14 +902,13 @@ var LooplayIframeAuth = class {
|
|
|
846
902
|
this.messageListener = (event) => {
|
|
847
903
|
if (event.source !== window.parent) return;
|
|
848
904
|
if (parentOrigin && event.origin !== parentOrigin) {
|
|
849
|
-
this.debug("ignored
|
|
905
|
+
this.debug("ignored message from unexpected origin", {
|
|
850
906
|
expectedOrigin: parentOrigin,
|
|
851
907
|
receivedOrigin: event.origin
|
|
852
908
|
});
|
|
853
909
|
return;
|
|
854
910
|
}
|
|
855
|
-
|
|
856
|
-
this.handleAuthMessage(event.data, event.origin);
|
|
911
|
+
this.handleIncomingMessage(event.data, event.origin);
|
|
857
912
|
};
|
|
858
913
|
window.addEventListener("message", this.messageListener);
|
|
859
914
|
this.anonymousFallbackTimer = setTimeout(() => {
|
|
@@ -870,6 +925,11 @@ var LooplayIframeAuth = class {
|
|
|
870
925
|
if (this.anonymousFallbackTimer !== null) {
|
|
871
926
|
clearTimeout(this.anonymousFallbackTimer);
|
|
872
927
|
}
|
|
928
|
+
for (const pending of this.pendingAdsRequests.values()) {
|
|
929
|
+
if (pending.timer !== null) clearTimeout(pending.timer);
|
|
930
|
+
pending.resolve(false);
|
|
931
|
+
}
|
|
932
|
+
this.pendingAdsRequests.clear();
|
|
873
933
|
this.messageListener = null;
|
|
874
934
|
this.anonymousFallbackTimer = null;
|
|
875
935
|
this.initialized = false;
|
|
@@ -892,7 +952,9 @@ var LooplayIframeAuth = class {
|
|
|
892
952
|
trackView() {
|
|
893
953
|
const gameId = this.getGameId();
|
|
894
954
|
if (!this.canTrack() || !gameId) {
|
|
895
|
-
this.debug("trackView skipped; auth is not ready", {
|
|
955
|
+
this.debug("trackView skipped; auth is not ready", {
|
|
956
|
+
hasGameId: Boolean(gameId)
|
|
957
|
+
});
|
|
896
958
|
return Promise.resolve(false);
|
|
897
959
|
}
|
|
898
960
|
this.debug("trackView dispatched", { gameId });
|
|
@@ -905,11 +967,17 @@ var LooplayIframeAuth = class {
|
|
|
905
967
|
trackPlay(playTimeSeconds) {
|
|
906
968
|
const gameId = this.getGameId();
|
|
907
969
|
if (!this.canTrack() || !gameId) {
|
|
908
|
-
this.debug("trackPlay skipped; auth is not ready", {
|
|
970
|
+
this.debug("trackPlay skipped; auth is not ready", {
|
|
971
|
+
hasGameId: Boolean(gameId)
|
|
972
|
+
});
|
|
909
973
|
return Promise.resolve(false);
|
|
910
974
|
}
|
|
911
975
|
this.debug("trackPlay dispatched", { gameId, playTimeSeconds });
|
|
912
|
-
|
|
976
|
+
const result = this.getClient().trackPlay(gameId, playTimeSeconds);
|
|
977
|
+
void result.then((ok) => {
|
|
978
|
+
if (ok) this.recordAutoRequestAdsActivity();
|
|
979
|
+
}).catch(() => void 0);
|
|
980
|
+
return result;
|
|
913
981
|
}
|
|
914
982
|
trackMatch(matchId, opts) {
|
|
915
983
|
const gameId = this.getGameId();
|
|
@@ -918,12 +986,16 @@ var LooplayIframeAuth = class {
|
|
|
918
986
|
return Promise.resolve(false);
|
|
919
987
|
}
|
|
920
988
|
this.debug("trackMatch dispatched", { gameId, matchId, ...opts });
|
|
921
|
-
|
|
989
|
+
const result = this.getClient().trackMatch(gameId, {
|
|
922
990
|
matchId,
|
|
923
991
|
matchDurationSeconds: opts.durationSeconds,
|
|
924
992
|
isCompleted: opts.isCompleted,
|
|
925
993
|
isWin: opts.isWin
|
|
926
994
|
});
|
|
995
|
+
void result.then((ok) => {
|
|
996
|
+
if (ok) this.recordAutoRequestAdsActivity();
|
|
997
|
+
}).catch(() => void 0);
|
|
998
|
+
return result;
|
|
927
999
|
}
|
|
928
1000
|
emitGameEvent(actionCode, opts) {
|
|
929
1001
|
const gameId = this.getGameId();
|
|
@@ -936,7 +1008,8 @@ var LooplayIframeAuth = class {
|
|
|
936
1008
|
}
|
|
937
1009
|
/** Idempotent; auto-emits GAME_STARTED exactly once per distinct auth session (bearer or anonymous). */
|
|
938
1010
|
initLifecycleTracking() {
|
|
939
|
-
if (this.lifecycleTrackingInitialized || typeof window === "undefined")
|
|
1011
|
+
if (this.lifecycleTrackingInitialized || typeof window === "undefined")
|
|
1012
|
+
return;
|
|
940
1013
|
this.lifecycleTrackingInitialized = true;
|
|
941
1014
|
this.debug("lifecycle tracking initialized");
|
|
942
1015
|
this.subscribe(() => {
|
|
@@ -986,7 +1059,9 @@ ${identity}`;
|
|
|
986
1059
|
origin,
|
|
987
1060
|
receivedAt: Date.now()
|
|
988
1061
|
});
|
|
989
|
-
this.debug(
|
|
1062
|
+
this.debug(
|
|
1063
|
+
"auth message missing game identity; falling back to anonymous tracking"
|
|
1064
|
+
);
|
|
990
1065
|
this.activateAnonymousMode();
|
|
991
1066
|
return;
|
|
992
1067
|
}
|
|
@@ -1000,10 +1075,13 @@ ${identity}`;
|
|
|
1000
1075
|
origin,
|
|
1001
1076
|
receivedAt: Date.now()
|
|
1002
1077
|
});
|
|
1003
|
-
this.debug(
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1078
|
+
this.debug(
|
|
1079
|
+
"auth message missing jwt; falling back to anonymous tracking",
|
|
1080
|
+
{
|
|
1081
|
+
gameId: resolvedGameId,
|
|
1082
|
+
apiUrl: resolvedApiUrl
|
|
1083
|
+
}
|
|
1084
|
+
);
|
|
1007
1085
|
this.activateAnonymousMode();
|
|
1008
1086
|
return;
|
|
1009
1087
|
}
|
|
@@ -1017,6 +1095,45 @@ ${identity}`;
|
|
|
1017
1095
|
receivedAt: Date.now()
|
|
1018
1096
|
});
|
|
1019
1097
|
}
|
|
1098
|
+
handleRequestAdsResult(data) {
|
|
1099
|
+
if (data.type !== "LOOPLAY_ADS_WATCHED") return;
|
|
1100
|
+
const requestId = toOptionalString(data.requestId);
|
|
1101
|
+
if (!requestId) return;
|
|
1102
|
+
const pending = this.pendingAdsRequests.get(requestId);
|
|
1103
|
+
if (!pending) return;
|
|
1104
|
+
this.pendingAdsRequests.delete(requestId);
|
|
1105
|
+
if (pending.timer !== null) clearTimeout(pending.timer);
|
|
1106
|
+
this.adsRequestInFlight = false;
|
|
1107
|
+
this.debug("requestAds watched message received", { requestId });
|
|
1108
|
+
pending.resolve(true);
|
|
1109
|
+
}
|
|
1110
|
+
handleRequestAdsUnavailable(data) {
|
|
1111
|
+
if (data.type !== "LOOPLAY_ADS_UNAVAILABLE") return;
|
|
1112
|
+
const requestId = toOptionalString(data.requestId);
|
|
1113
|
+
if (!requestId) return;
|
|
1114
|
+
const pending = this.pendingAdsRequests.get(requestId);
|
|
1115
|
+
if (!pending) return;
|
|
1116
|
+
this.pendingAdsRequests.delete(requestId);
|
|
1117
|
+
if (pending.timer !== null) clearTimeout(pending.timer);
|
|
1118
|
+
this.adsRequestInFlight = false;
|
|
1119
|
+
this.debug("requestAds unavailable message received", { requestId });
|
|
1120
|
+
pending.resolve(false);
|
|
1121
|
+
}
|
|
1122
|
+
handleIncomingMessage(data, origin) {
|
|
1123
|
+
if (!isRecord(data)) return;
|
|
1124
|
+
if (data.type === "LOOPLAY_AUTH") {
|
|
1125
|
+
this.handleAuthMessage(data, origin);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (data.type === "LOOPLAY_ADS_WATCHED") {
|
|
1129
|
+
this.handleRequestAdsResult(data);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (data.type === "LOOPLAY_ADS_UNAVAILABLE") {
|
|
1133
|
+
this.handleRequestAdsUnavailable(data);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1020
1137
|
setState(next) {
|
|
1021
1138
|
this.state = next;
|
|
1022
1139
|
this.client = null;
|
|
@@ -1041,6 +1158,35 @@ ${identity}`;
|
|
|
1041
1158
|
debug(message, details) {
|
|
1042
1159
|
this.options.onDebug?.(message, details);
|
|
1043
1160
|
}
|
|
1161
|
+
recordAutoRequestAdsActivity() {
|
|
1162
|
+
this.autoAdsActionCount += 1;
|
|
1163
|
+
const config = this.options.autoRequestAds;
|
|
1164
|
+
if (!config?.enabled) return;
|
|
1165
|
+
if (this.adsRequestInFlight) return;
|
|
1166
|
+
if (!this.canTrack()) return;
|
|
1167
|
+
const now = Date.now();
|
|
1168
|
+
if (config.cooldownMs && now - this.lastAdsRequestAt < config.cooldownMs) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
if (config.maxRequestsPerSession && this.adsRequestsSent >= config.maxRequestsPerSession) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const threshold = config.afterActionCount ?? 0;
|
|
1175
|
+
const shouldRequest = threshold > 0 && this.autoAdsActionCount % threshold === 0;
|
|
1176
|
+
if (!shouldRequest) return;
|
|
1177
|
+
const chance = config.chance ?? 1;
|
|
1178
|
+
if (chance <= 0 || Math.random() > chance) {
|
|
1179
|
+
this.debug("auto requestAds skipped by chance", {
|
|
1180
|
+
actionCount: this.autoAdsActionCount,
|
|
1181
|
+
chance
|
|
1182
|
+
});
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
this.debug("auto requestAds triggered", {
|
|
1186
|
+
actionCount: this.autoAdsActionCount
|
|
1187
|
+
});
|
|
1188
|
+
void this.requestAds();
|
|
1189
|
+
}
|
|
1044
1190
|
};
|
|
1045
1191
|
|
|
1046
1192
|
export { ApiClient, BrowserLocalStorageAuthStorage, HttpClient, HttpError, LooplayAuth, LooplayIframeAuth, LooplaySDK, LooplaySDKError, MemoryAuthStorage, MissingAuthError, MissingBaseUrlError, NotAuthenticatedError, NotInitializedError, ServiceClient, TelegramAuthProvider };
|