@thestatic-tv/dcl-sdk 1.0.1 → 1.0.3

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.d.mts CHANGED
@@ -84,6 +84,26 @@ interface InteractionResponse {
84
84
  alreadyExists: boolean;
85
85
  version: string;
86
86
  }
87
+ /**
88
+ * Scene stats for display in-world
89
+ */
90
+ interface SceneStats {
91
+ date: string;
92
+ totalSessions: number;
93
+ uniqueVisitors: number;
94
+ totalMinutes: number;
95
+ visitorNumber: number | null;
96
+ isFirstVisitor: boolean;
97
+ }
98
+ /**
99
+ * Scene stats response from the API
100
+ */
101
+ interface SceneStatsResponse {
102
+ success: boolean;
103
+ stats: SceneStats;
104
+ sceneName: string | null;
105
+ version: string;
106
+ }
87
107
 
88
108
  /**
89
109
  * Guide module - fetch channel lineup from thestatic.tv
@@ -128,7 +148,7 @@ declare class GuideModule {
128
148
  declare class SessionModule {
129
149
  private client;
130
150
  private sessionId;
131
- private heartbeatInterval;
151
+ private heartbeatTimerId;
132
152
  private isActive;
133
153
  constructor(client: StaticTVClient);
134
154
  /**
@@ -141,7 +161,7 @@ declare class SessionModule {
141
161
  */
142
162
  startSession(metadata?: Record<string, unknown>): Promise<string | null>;
143
163
  /**
144
- * Start the heartbeat interval
164
+ * Start the heartbeat interval using DCL-compatible timer
145
165
  */
146
166
  private startHeartbeat;
147
167
  /**
@@ -160,6 +180,11 @@ declare class SessionModule {
160
180
  * Check if a session is currently active
161
181
  */
162
182
  isSessionActive(): boolean;
183
+ /**
184
+ * Get scene stats (visitors, sessions, etc.)
185
+ * Useful for displaying metrics in-world
186
+ */
187
+ getStats(): Promise<SceneStatsResponse['stats'] | null>;
163
188
  }
164
189
 
165
190
  /**
@@ -168,7 +193,7 @@ declare class SessionModule {
168
193
 
169
194
  declare class HeartbeatModule {
170
195
  private client;
171
- private watchInterval;
196
+ private watchTimerId;
172
197
  private currentChannel;
173
198
  private isWatching;
174
199
  constructor(client: StaticTVClient);
@@ -294,4 +319,4 @@ declare class StaticTVClient {
294
319
  destroy(): Promise<void>;
295
320
  }
296
321
 
297
- export { type Channel, GuideModule, type GuideResponse, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type Vod };
322
+ export { type Channel, GuideModule, type GuideResponse, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, type SceneStats, type SceneStatsResponse, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type Vod };
package/dist/index.d.ts CHANGED
@@ -84,6 +84,26 @@ interface InteractionResponse {
84
84
  alreadyExists: boolean;
85
85
  version: string;
86
86
  }
87
+ /**
88
+ * Scene stats for display in-world
89
+ */
90
+ interface SceneStats {
91
+ date: string;
92
+ totalSessions: number;
93
+ uniqueVisitors: number;
94
+ totalMinutes: number;
95
+ visitorNumber: number | null;
96
+ isFirstVisitor: boolean;
97
+ }
98
+ /**
99
+ * Scene stats response from the API
100
+ */
101
+ interface SceneStatsResponse {
102
+ success: boolean;
103
+ stats: SceneStats;
104
+ sceneName: string | null;
105
+ version: string;
106
+ }
87
107
 
88
108
  /**
89
109
  * Guide module - fetch channel lineup from thestatic.tv
@@ -128,7 +148,7 @@ declare class GuideModule {
128
148
  declare class SessionModule {
129
149
  private client;
130
150
  private sessionId;
131
- private heartbeatInterval;
151
+ private heartbeatTimerId;
132
152
  private isActive;
133
153
  constructor(client: StaticTVClient);
134
154
  /**
@@ -141,7 +161,7 @@ declare class SessionModule {
141
161
  */
142
162
  startSession(metadata?: Record<string, unknown>): Promise<string | null>;
143
163
  /**
144
- * Start the heartbeat interval
164
+ * Start the heartbeat interval using DCL-compatible timer
145
165
  */
146
166
  private startHeartbeat;
147
167
  /**
@@ -160,6 +180,11 @@ declare class SessionModule {
160
180
  * Check if a session is currently active
161
181
  */
162
182
  isSessionActive(): boolean;
183
+ /**
184
+ * Get scene stats (visitors, sessions, etc.)
185
+ * Useful for displaying metrics in-world
186
+ */
187
+ getStats(): Promise<SceneStatsResponse['stats'] | null>;
163
188
  }
164
189
 
165
190
  /**
@@ -168,7 +193,7 @@ declare class SessionModule {
168
193
 
169
194
  declare class HeartbeatModule {
170
195
  private client;
171
- private watchInterval;
196
+ private watchTimerId;
172
197
  private currentChannel;
173
198
  private isWatching;
174
199
  constructor(client: StaticTVClient);
@@ -294,4 +319,4 @@ declare class StaticTVClient {
294
319
  destroy(): Promise<void>;
295
320
  }
296
321
 
297
- export { type Channel, GuideModule, type GuideResponse, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type Vod };
322
+ export { type Channel, GuideModule, type GuideResponse, HeartbeatModule, type HeartbeatResponse, type InteractionResponse, InteractionsModule, KEY_TYPE_CHANNEL, KEY_TYPE_SCENE, type SceneStats, type SceneStatsResponse, SessionModule, type SessionResponse, StaticTVClient, type StaticTVConfig, type Vod };
package/dist/index.js CHANGED
@@ -99,7 +99,7 @@ var GuideModule = class {
99
99
  // src/utils/identity.ts
100
100
  function getPlayerWallet() {
101
101
  try {
102
- const { getPlayer } = require("@dcl/sdk/src/players");
102
+ const { getPlayer } = require("@dcl/sdk/players");
103
103
  const player = getPlayer();
104
104
  return player?.userId ?? null;
105
105
  } catch {
@@ -108,7 +108,7 @@ function getPlayerWallet() {
108
108
  }
109
109
  function getPlayerDisplayName() {
110
110
  try {
111
- const { getPlayer } = require("@dcl/sdk/src/players");
111
+ const { getPlayer } = require("@dcl/sdk/players");
112
112
  const player = getPlayer();
113
113
  return player?.name ?? null;
114
114
  } catch {
@@ -116,11 +116,54 @@ function getPlayerDisplayName() {
116
116
  }
117
117
  }
118
118
 
119
+ // src/utils/timer.ts
120
+ var import_ecs = require("@dcl/sdk/ecs");
121
+ var nextTimerId = 1;
122
+ var timers = /* @__PURE__ */ new Map();
123
+ var systemAdded = false;
124
+ function ensureTimerSystem() {
125
+ if (systemAdded) return;
126
+ import_ecs.engine.addSystem((dt) => {
127
+ for (const timer of timers.values()) {
128
+ if (!timer.active) continue;
129
+ timer.elapsedTime += dt;
130
+ if (timer.elapsedTime >= timer.intervalSeconds) {
131
+ timer.elapsedTime = 0;
132
+ try {
133
+ timer.callback();
134
+ } catch (e) {
135
+ console.error("[StaticTV Timer] Callback error:", e);
136
+ }
137
+ }
138
+ }
139
+ });
140
+ systemAdded = true;
141
+ }
142
+ function dclSetInterval(callback, intervalMs) {
143
+ ensureTimerSystem();
144
+ const id = nextTimerId++;
145
+ timers.set(id, {
146
+ id,
147
+ callback,
148
+ intervalSeconds: intervalMs / 1e3,
149
+ elapsedTime: 0,
150
+ active: true
151
+ });
152
+ return id;
153
+ }
154
+ function dclClearInterval(timerId) {
155
+ const timer = timers.get(timerId);
156
+ if (timer) {
157
+ timer.active = false;
158
+ timers.delete(timerId);
159
+ }
160
+ }
161
+
119
162
  // src/modules/session.ts
120
163
  var SessionModule = class {
121
164
  constructor(client) {
122
165
  this.sessionId = null;
123
- this.heartbeatInterval = null;
166
+ this.heartbeatTimerId = null;
124
167
  this.isActive = false;
125
168
  this.client = client;
126
169
  }
@@ -163,12 +206,12 @@ var SessionModule = class {
163
206
  }
164
207
  }
165
208
  /**
166
- * Start the heartbeat interval
209
+ * Start the heartbeat interval using DCL-compatible timer
167
210
  */
168
211
  startHeartbeat() {
169
- if (this.heartbeatInterval) return;
212
+ if (this.heartbeatTimerId !== null) return;
170
213
  const interval = this.client.getConfig().sessionHeartbeatInterval || 3e4;
171
- this.heartbeatInterval = setInterval(() => {
214
+ this.heartbeatTimerId = dclSetInterval(() => {
172
215
  this.sendHeartbeat();
173
216
  }, interval);
174
217
  }
@@ -195,9 +238,9 @@ var SessionModule = class {
195
238
  */
196
239
  async endSession() {
197
240
  if (!this.isActive) return;
198
- if (this.heartbeatInterval) {
199
- clearInterval(this.heartbeatInterval);
200
- this.heartbeatInterval = null;
241
+ if (this.heartbeatTimerId !== null) {
242
+ dclClearInterval(this.heartbeatTimerId);
243
+ this.heartbeatTimerId = null;
201
244
  }
202
245
  if (this.sessionId) {
203
246
  try {
@@ -228,12 +271,34 @@ var SessionModule = class {
228
271
  isSessionActive() {
229
272
  return this.isActive;
230
273
  }
274
+ /**
275
+ * Get scene stats (visitors, sessions, etc.)
276
+ * Useful for displaying metrics in-world
277
+ */
278
+ async getStats() {
279
+ try {
280
+ const wallet = getPlayerWallet();
281
+ const queryParam = wallet ? `?wallet=${wallet}` : "";
282
+ const response = await this.client.request(
283
+ `/scene-stats${queryParam}`,
284
+ { method: "GET" }
285
+ );
286
+ if (response.success && response.stats) {
287
+ this.client.log("Stats fetched:", response.stats);
288
+ return response.stats;
289
+ }
290
+ return null;
291
+ } catch (error) {
292
+ this.client.log(`Failed to fetch stats: ${error}`);
293
+ return null;
294
+ }
295
+ }
231
296
  };
232
297
 
233
298
  // src/modules/heartbeat.ts
234
299
  var HeartbeatModule = class {
235
300
  constructor(client) {
236
- this.watchInterval = null;
301
+ this.watchTimerId = null;
237
302
  this.currentChannel = null;
238
303
  this.isWatching = false;
239
304
  this.client = client;
@@ -256,7 +321,7 @@ var HeartbeatModule = class {
256
321
  this.isWatching = true;
257
322
  this.sendHeartbeat();
258
323
  const interval = this.client.getConfig().watchHeartbeatInterval || 6e4;
259
- this.watchInterval = setInterval(() => {
324
+ this.watchTimerId = dclSetInterval(() => {
260
325
  this.sendHeartbeat();
261
326
  }, interval);
262
327
  this.client.log(`Started watching ${channelSlug}`);
@@ -266,9 +331,9 @@ var HeartbeatModule = class {
266
331
  */
267
332
  stopWatching() {
268
333
  if (!this.isWatching) return;
269
- if (this.watchInterval) {
270
- clearInterval(this.watchInterval);
271
- this.watchInterval = null;
334
+ if (this.watchTimerId !== null) {
335
+ dclClearInterval(this.watchTimerId);
336
+ this.watchTimerId = null;
272
337
  }
273
338
  this.client.log(`Stopped watching ${this.currentChannel}`);
274
339
  this.currentChannel = null;
package/dist/index.mjs CHANGED
@@ -74,7 +74,7 @@ var GuideModule = class {
74
74
  // src/utils/identity.ts
75
75
  function getPlayerWallet() {
76
76
  try {
77
- const { getPlayer } = __require("@dcl/sdk/src/players");
77
+ const { getPlayer } = __require("@dcl/sdk/players");
78
78
  const player = getPlayer();
79
79
  return player?.userId ?? null;
80
80
  } catch {
@@ -83,7 +83,7 @@ function getPlayerWallet() {
83
83
  }
84
84
  function getPlayerDisplayName() {
85
85
  try {
86
- const { getPlayer } = __require("@dcl/sdk/src/players");
86
+ const { getPlayer } = __require("@dcl/sdk/players");
87
87
  const player = getPlayer();
88
88
  return player?.name ?? null;
89
89
  } catch {
@@ -91,11 +91,54 @@ function getPlayerDisplayName() {
91
91
  }
92
92
  }
93
93
 
94
+ // src/utils/timer.ts
95
+ import { engine } from "@dcl/sdk/ecs";
96
+ var nextTimerId = 1;
97
+ var timers = /* @__PURE__ */ new Map();
98
+ var systemAdded = false;
99
+ function ensureTimerSystem() {
100
+ if (systemAdded) return;
101
+ engine.addSystem((dt) => {
102
+ for (const timer of timers.values()) {
103
+ if (!timer.active) continue;
104
+ timer.elapsedTime += dt;
105
+ if (timer.elapsedTime >= timer.intervalSeconds) {
106
+ timer.elapsedTime = 0;
107
+ try {
108
+ timer.callback();
109
+ } catch (e) {
110
+ console.error("[StaticTV Timer] Callback error:", e);
111
+ }
112
+ }
113
+ }
114
+ });
115
+ systemAdded = true;
116
+ }
117
+ function dclSetInterval(callback, intervalMs) {
118
+ ensureTimerSystem();
119
+ const id = nextTimerId++;
120
+ timers.set(id, {
121
+ id,
122
+ callback,
123
+ intervalSeconds: intervalMs / 1e3,
124
+ elapsedTime: 0,
125
+ active: true
126
+ });
127
+ return id;
128
+ }
129
+ function dclClearInterval(timerId) {
130
+ const timer = timers.get(timerId);
131
+ if (timer) {
132
+ timer.active = false;
133
+ timers.delete(timerId);
134
+ }
135
+ }
136
+
94
137
  // src/modules/session.ts
95
138
  var SessionModule = class {
96
139
  constructor(client) {
97
140
  this.sessionId = null;
98
- this.heartbeatInterval = null;
141
+ this.heartbeatTimerId = null;
99
142
  this.isActive = false;
100
143
  this.client = client;
101
144
  }
@@ -138,12 +181,12 @@ var SessionModule = class {
138
181
  }
139
182
  }
140
183
  /**
141
- * Start the heartbeat interval
184
+ * Start the heartbeat interval using DCL-compatible timer
142
185
  */
143
186
  startHeartbeat() {
144
- if (this.heartbeatInterval) return;
187
+ if (this.heartbeatTimerId !== null) return;
145
188
  const interval = this.client.getConfig().sessionHeartbeatInterval || 3e4;
146
- this.heartbeatInterval = setInterval(() => {
189
+ this.heartbeatTimerId = dclSetInterval(() => {
147
190
  this.sendHeartbeat();
148
191
  }, interval);
149
192
  }
@@ -170,9 +213,9 @@ var SessionModule = class {
170
213
  */
171
214
  async endSession() {
172
215
  if (!this.isActive) return;
173
- if (this.heartbeatInterval) {
174
- clearInterval(this.heartbeatInterval);
175
- this.heartbeatInterval = null;
216
+ if (this.heartbeatTimerId !== null) {
217
+ dclClearInterval(this.heartbeatTimerId);
218
+ this.heartbeatTimerId = null;
176
219
  }
177
220
  if (this.sessionId) {
178
221
  try {
@@ -203,12 +246,34 @@ var SessionModule = class {
203
246
  isSessionActive() {
204
247
  return this.isActive;
205
248
  }
249
+ /**
250
+ * Get scene stats (visitors, sessions, etc.)
251
+ * Useful for displaying metrics in-world
252
+ */
253
+ async getStats() {
254
+ try {
255
+ const wallet = getPlayerWallet();
256
+ const queryParam = wallet ? `?wallet=${wallet}` : "";
257
+ const response = await this.client.request(
258
+ `/scene-stats${queryParam}`,
259
+ { method: "GET" }
260
+ );
261
+ if (response.success && response.stats) {
262
+ this.client.log("Stats fetched:", response.stats);
263
+ return response.stats;
264
+ }
265
+ return null;
266
+ } catch (error) {
267
+ this.client.log(`Failed to fetch stats: ${error}`);
268
+ return null;
269
+ }
270
+ }
206
271
  };
207
272
 
208
273
  // src/modules/heartbeat.ts
209
274
  var HeartbeatModule = class {
210
275
  constructor(client) {
211
- this.watchInterval = null;
276
+ this.watchTimerId = null;
212
277
  this.currentChannel = null;
213
278
  this.isWatching = false;
214
279
  this.client = client;
@@ -231,7 +296,7 @@ var HeartbeatModule = class {
231
296
  this.isWatching = true;
232
297
  this.sendHeartbeat();
233
298
  const interval = this.client.getConfig().watchHeartbeatInterval || 6e4;
234
- this.watchInterval = setInterval(() => {
299
+ this.watchTimerId = dclSetInterval(() => {
235
300
  this.sendHeartbeat();
236
301
  }, interval);
237
302
  this.client.log(`Started watching ${channelSlug}`);
@@ -241,9 +306,9 @@ var HeartbeatModule = class {
241
306
  */
242
307
  stopWatching() {
243
308
  if (!this.isWatching) return;
244
- if (this.watchInterval) {
245
- clearInterval(this.watchInterval);
246
- this.watchInterval = null;
309
+ if (this.watchTimerId !== null) {
310
+ dclClearInterval(this.watchTimerId);
311
+ this.watchTimerId = null;
247
312
  }
248
313
  this.client.log(`Stopped watching ${this.currentChannel}`);
249
314
  this.currentChannel = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thestatic-tv/dcl-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Connect your Decentraland scene to thestatic.tv - full channel lineup, metrics tracking, and interactions",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",