@proappstore/sdk 1.7.0 → 1.9.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/dist/usage.js ADDED
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Usage telemetry — heartbeats `POST /v1/usage/ping` while the tab is visible.
3
+ *
4
+ * Auto-started by `initPro()` unless `usage.auto === false` in the options.
5
+ * The collected (app, user, day) counts drive the usage-proportional creator
6
+ * payouts described in proappstore.online/pricing.
7
+ *
8
+ * Design notes:
9
+ *
10
+ * - **Visible-time only.** We start a stopwatch when the tab is visible and
11
+ * pause on `visibilitychange`. A tab that's been hidden for 90s contributes
12
+ * 0 seconds for that interval.
13
+ * - **Browser-only.** All methods no-op when `document` or `window` is
14
+ * undefined so the import is SSR-safe.
15
+ * - **Silent failures.** Telemetry must never break an app. Every network
16
+ * path catches + ignores errors.
17
+ * - **Page-close flush.** On `pagehide` we send a final ping with the
18
+ * residual elapsed seconds via `navigator.sendBeacon` (survives unload)
19
+ * falling back to `fetch(..., { keepalive: true })`.
20
+ */
21
+ const HEARTBEAT_MS = 60_000;
22
+ const MAX_DELTA_SECONDS = 90;
23
+ function hasBrowser() {
24
+ return typeof document !== 'undefined' && typeof window !== 'undefined';
25
+ }
26
+ export class Usage {
27
+ appId;
28
+ apiBase;
29
+ auth;
30
+ running = false;
31
+ timer = null;
32
+ visibleSince = null;
33
+ /** Accumulated visible-time since the last successful ping, in milliseconds. */
34
+ accruedMs = 0;
35
+ pendingApiCalls = 0;
36
+ onVisibility = null;
37
+ onPageHide = null;
38
+ constructor(appId, apiBase, auth) {
39
+ this.appId = appId;
40
+ this.apiBase = apiBase;
41
+ this.auth = auth;
42
+ }
43
+ /** Begin heartbeat reporting. Idempotent — calling twice is a no-op. */
44
+ start() {
45
+ if (this.running)
46
+ return;
47
+ if (!hasBrowser())
48
+ return;
49
+ this.running = true;
50
+ // Initial visibility snapshot.
51
+ if (document.visibilityState === 'visible') {
52
+ this.visibleSince = performance.now();
53
+ }
54
+ this.onVisibility = () => {
55
+ if (document.visibilityState === 'visible') {
56
+ if (this.visibleSince == null)
57
+ this.visibleSince = performance.now();
58
+ }
59
+ else {
60
+ this.bankVisible();
61
+ }
62
+ };
63
+ document.addEventListener('visibilitychange', this.onVisibility);
64
+ this.onPageHide = () => {
65
+ this.flush();
66
+ };
67
+ window.addEventListener('pagehide', this.onPageHide);
68
+ this.timer = setInterval(() => {
69
+ this.tick().catch(() => { });
70
+ }, HEARTBEAT_MS);
71
+ }
72
+ /** Stop heartbeats. Idempotent. Doesn't flush; call `flush()` if you need to. */
73
+ stop() {
74
+ if (!this.running)
75
+ return;
76
+ this.running = false;
77
+ if (this.timer) {
78
+ clearInterval(this.timer);
79
+ this.timer = null;
80
+ }
81
+ if (hasBrowser()) {
82
+ if (this.onVisibility)
83
+ document.removeEventListener('visibilitychange', this.onVisibility);
84
+ if (this.onPageHide)
85
+ window.removeEventListener('pagehide', this.onPageHide);
86
+ }
87
+ this.onVisibility = null;
88
+ this.onPageHide = null;
89
+ this.visibleSince = null;
90
+ this.accruedMs = 0;
91
+ this.pendingApiCalls = 0;
92
+ }
93
+ /**
94
+ * Record API calls. Bumps a local counter that piggybacks on the next
95
+ * heartbeat. Cheap to call from hot paths — no network until the next tick.
96
+ */
97
+ recordApiCall(n = 1) {
98
+ if (!Number.isFinite(n) || n <= 0)
99
+ return;
100
+ this.pendingApiCalls += Math.floor(n);
101
+ }
102
+ /**
103
+ * Send a final ping (intended for page-close paths). Best-effort,
104
+ * fire-and-forget. Uses sendBeacon when available so the request survives
105
+ * unload; falls back to keepalive fetch otherwise.
106
+ */
107
+ flush() {
108
+ if (!hasBrowser())
109
+ return;
110
+ this.bankVisible();
111
+ const seconds = this.drainSeconds();
112
+ const apiCalls = this.drainApiCalls();
113
+ if (seconds === 0 && apiCalls === 0)
114
+ return;
115
+ this.send(seconds, apiCalls, /* keepalive */ true);
116
+ }
117
+ // ── internal ──────────────────────────────────────────────────────────────
118
+ /** Add any in-progress visible-time to the running accrual. */
119
+ bankVisible() {
120
+ if (this.visibleSince == null)
121
+ return;
122
+ const now = performance.now();
123
+ this.accruedMs += Math.max(0, now - this.visibleSince);
124
+ this.visibleSince = null;
125
+ }
126
+ /** Round accrued ms to whole seconds, clamp to MAX_DELTA_SECONDS, return + reset. */
127
+ drainSeconds() {
128
+ const whole = Math.floor(this.accruedMs / 1000);
129
+ if (whole <= 0)
130
+ return 0;
131
+ const sent = Math.min(MAX_DELTA_SECONDS, whole);
132
+ this.accruedMs -= sent * 1000;
133
+ // Cap residual so a long-hidden flush after a long visible run doesn't
134
+ // ride along on the next ping forever.
135
+ if (this.accruedMs < 0)
136
+ this.accruedMs = 0;
137
+ return sent;
138
+ }
139
+ drainApiCalls() {
140
+ const n = this.pendingApiCalls;
141
+ this.pendingApiCalls = 0;
142
+ return n;
143
+ }
144
+ async tick() {
145
+ this.bankVisible();
146
+ if (document.visibilityState === 'visible') {
147
+ this.visibleSince = performance.now();
148
+ }
149
+ const seconds = this.drainSeconds();
150
+ const apiCalls = this.drainApiCalls();
151
+ if (seconds === 0 && apiCalls === 0)
152
+ return;
153
+ if (!this.auth.token) {
154
+ // Re-pocket the work — the user may sign in later and we'd like the
155
+ // accrued time to count from then. Note: a session-token signin BEFORE
156
+ // any visible time was banked won't have anything to attribute, and
157
+ // that's fine — anonymous usage isn't part of the payout math anyway.
158
+ this.accruedMs += seconds * 1000;
159
+ this.pendingApiCalls += apiCalls;
160
+ return;
161
+ }
162
+ await this.send(seconds, apiCalls, /* keepalive */ false);
163
+ }
164
+ async send(seconds, apiCalls, keepalive) {
165
+ const token = this.auth.token;
166
+ if (!token)
167
+ return;
168
+ const body = JSON.stringify({
169
+ appId: this.appId,
170
+ deltaSeconds: seconds,
171
+ deltaApiCalls: apiCalls,
172
+ });
173
+ const url = `${this.apiBase}/v1/usage/ping`;
174
+ // Prefer sendBeacon for keepalive (unload survivor) paths. sendBeacon
175
+ // doesn't allow setting an Authorization header, so we encode the token
176
+ // into the URL only for keepalive — server accepts it as a fallback.
177
+ // (If your server doesn't support that, fall through to keepalive fetch.)
178
+ if (keepalive && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {
179
+ try {
180
+ const blob = new Blob([body], { type: 'application/json' });
181
+ const ok = navigator.sendBeacon(url, blob);
182
+ if (ok)
183
+ return;
184
+ }
185
+ catch {
186
+ // fall through
187
+ }
188
+ }
189
+ try {
190
+ await fetch(url, {
191
+ method: 'POST',
192
+ headers: {
193
+ 'Content-Type': 'application/json',
194
+ Authorization: `Bearer ${token}`,
195
+ },
196
+ body,
197
+ keepalive,
198
+ });
199
+ }
200
+ catch {
201
+ // Telemetry never breaks an app.
202
+ }
203
+ }
204
+ }
205
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proappstore/sdk",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Browser SDK for paid apps on proappstore.online — subscriptions, license keys, premium modules.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,12 +29,8 @@
29
29
  "dist",
30
30
  "README.md"
31
31
  ],
32
- "scripts": {
33
- "build": "tsc",
34
- "typecheck": "tsc --noEmit"
35
- },
36
32
  "dependencies": {
37
- "@freeappstore/sdk": "^0.6.0"
33
+ "@freeappstore/sdk": "^0.7.1"
38
34
  },
39
35
  "peerDependencies": {
40
36
  "react": "^18.0.0 || ^19.0.0"
@@ -49,5 +45,9 @@
49
45
  "react": "^19.0.0",
50
46
  "typescript": "^5.7.0"
51
47
  },
52
- "sideEffects": false
53
- }
48
+ "sideEffects": false,
49
+ "scripts": {
50
+ "build": "tsc",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }