sitepong 0.1.8 → 0.1.10
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 +138 -0
- package/dist/entries/rn.js +61 -71
- package/dist/entries/rn.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -596,6 +596,144 @@ Content inside `<SensitiveView>` is replaced with a black rectangle in recorded
|
|
|
596
596
|
| `bufferDuration` | `10000` (10s) | `60000` (60s) | Rolling buffer size in ms |
|
|
597
597
|
| `maxDuration` | `3600000` | `3600000` | Max recording duration in ms |
|
|
598
598
|
|
|
599
|
+
### Push Notifications & Live Activities
|
|
600
|
+
|
|
601
|
+
Native APNs (iOS) and FCM (Android) push notifications, plus iOS Live Activity updates. SitePong sends directly to Apple/Google — no Expo Push service in the path.
|
|
602
|
+
|
|
603
|
+
#### Token lifecycle
|
|
604
|
+
|
|
605
|
+
There are two halves to push: the **client SDK** registers device tokens with the SitePong ingest server, and the **server-side Push API** sends notifications to those tokens.
|
|
606
|
+
|
|
607
|
+
```
|
|
608
|
+
[App launches]
|
|
609
|
+
↓
|
|
610
|
+
[Notifications.getDevicePushTokenAsync()] ← OS gives you a native token
|
|
611
|
+
↓
|
|
612
|
+
[registerDeviceToken(token, opts)] ← SDK POSTs to /api/push/tokens
|
|
613
|
+
↓ with X-API-Key: sp_live_xxx
|
|
614
|
+
[Token stored in SitePong]
|
|
615
|
+
↓
|
|
616
|
+
[identify('user-123')] ← SDK auto re-registers token
|
|
617
|
+
↓ with user_id (via lazy hook)
|
|
618
|
+
[Your backend → POST /api/push/send] ← server-only sp_push_xxx key
|
|
619
|
+
↓
|
|
620
|
+
[SitePong → APNs / FCM → Device]
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
#### 1. Install permission and get the native token
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
npx expo install expo-notifications
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import * as Notifications from 'expo-notifications';
|
|
631
|
+
import * as Device from 'expo-device';
|
|
632
|
+
import { Platform } from 'react-native';
|
|
633
|
+
|
|
634
|
+
if (!Device.isDevice) return; // simulator can't receive push
|
|
635
|
+
|
|
636
|
+
const { status } = await Notifications.requestPermissionsAsync();
|
|
637
|
+
if (status !== 'granted') return;
|
|
638
|
+
|
|
639
|
+
// Returns the raw native token (APNs hex on iOS, FCM token on Android)
|
|
640
|
+
const { data: token } = await Notifications.getDevicePushTokenAsync();
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
#### 2. Register the token with SitePong
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
import { registerDeviceToken } from '@sitepong/sdk/react-native';
|
|
647
|
+
import { Platform } from 'react-native';
|
|
648
|
+
|
|
649
|
+
registerDeviceToken(token, {
|
|
650
|
+
platform: Platform.OS as 'ios' | 'android',
|
|
651
|
+
environment: __DEV__ ? 'sandbox' : 'production',
|
|
652
|
+
userId: 'user-123', // optional — can be set later via identify()
|
|
653
|
+
});
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
This makes a `POST /api/push/tokens` request to the ingest server with `X-API-Key: <your SDK key>`. The body includes the token, `token_type` (`apns`/`fcm`), `device_id`, `user_id`, `platform`, app version, device model, and OS version. The server upserts by `(token, environment)` so re-registration is idempotent.
|
|
657
|
+
|
|
658
|
+
#### 3. User identification (automatic)
|
|
659
|
+
|
|
660
|
+
When you call `identify(userId)`, the SDK automatically re-registers all cached push, Live Activity, and push-to-start tokens with the new `user_id`. **You do not need to call `registerDeviceToken()` again after login.**
|
|
661
|
+
|
|
662
|
+
```typescript
|
|
663
|
+
import { identify } from '@sitepong/sdk/react-native';
|
|
664
|
+
|
|
665
|
+
// Token already registered anonymously at app launch
|
|
666
|
+
// Now user logs in:
|
|
667
|
+
identify('user-123');
|
|
668
|
+
// → SDK re-POSTs /api/push/tokens with user_id: "user-123"
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
The hook is installed lazily the first time any `register*` function is called, so it works whether you use `SitePongRNProvider` or call `initRN()` directly.
|
|
672
|
+
|
|
673
|
+
#### 4. Send notifications from your backend
|
|
674
|
+
|
|
675
|
+
Generate a separate **Push API key** (prefix `sp_push_`) from the Notifications tab in the SitePong dashboard. This is **not** the SDK key — it's server-only and has permission to send pushes.
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
curl -X POST https://api.sitepong.com/api/push/send \
|
|
679
|
+
-H "Authorization: Bearer sp_push_xxxxxxxxxxxxxxxx" \
|
|
680
|
+
-H "Content-Type: application/json" \
|
|
681
|
+
-d '{
|
|
682
|
+
"title": "Order Shipped",
|
|
683
|
+
"body": "Your order #1234 is on its way",
|
|
684
|
+
"data": { "orderId": "1234" },
|
|
685
|
+
"target": { "user_ids": ["user-123"] }
|
|
686
|
+
}'
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Targeting options (provide at least one):
|
|
690
|
+
|
|
691
|
+
- `user_ids: string[]` — sends to all active devices for these users
|
|
692
|
+
- `device_ids: string[]` — sends to specific SitePong device IDs
|
|
693
|
+
- `tokens: string[]` — sends directly to raw APNs/FCM tokens
|
|
694
|
+
|
|
695
|
+
#### Live Activities (iOS)
|
|
696
|
+
|
|
697
|
+
Register the per-instance push token from your native bridge:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
import {
|
|
701
|
+
registerLiveActivityToken,
|
|
702
|
+
registerPushToStartToken,
|
|
703
|
+
endLiveActivity,
|
|
704
|
+
} from '@sitepong/sdk/react-native';
|
|
705
|
+
|
|
706
|
+
// When ActivityKit gives you a push token for a running activity
|
|
707
|
+
registerLiveActivityToken('DeliveryActivityAttributes', activity.id, pushToken, {
|
|
708
|
+
environment: 'production',
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Or register a push-to-start token (iOS 17.2+) so the activity can be launched remotely
|
|
712
|
+
registerPushToStartToken('DeliveryActivityAttributes', pushToStartToken, {
|
|
713
|
+
environment: 'production',
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// When the activity ends, deactivate its token
|
|
717
|
+
endLiveActivity('DeliveryActivityAttributes', activity.id);
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
#### Token invalidation
|
|
721
|
+
|
|
722
|
+
When APNs or FCM reports a token as invalid (app uninstalled, notifications disabled, token rotated), SitePong marks it inactive on the server side and stops sending. No client action needed. To force a refresh after an OS-level token change, call `registerDeviceToken()` again with the new token.
|
|
723
|
+
|
|
724
|
+
#### Endpoint reference
|
|
725
|
+
|
|
726
|
+
| Method | Path | Auth | SDK function |
|
|
727
|
+
|---|---|---|---|
|
|
728
|
+
| POST | `/api/push/tokens` | `X-API-Key: sp_live_*` | `registerDeviceToken()` |
|
|
729
|
+
| POST | `/api/push/live-activity-tokens` | `X-API-Key: sp_live_*` | `registerLiveActivityToken()` |
|
|
730
|
+
| POST | `/api/push/push-to-start-tokens` | `X-API-Key: sp_live_*` | `registerPushToStartToken()` |
|
|
731
|
+
| DELETE | `/api/push/live-activity-tokens` | `X-API-Key: sp_live_*` | `endLiveActivity()` |
|
|
732
|
+
| POST | `/api/push/send` | `Authorization: Bearer sp_push_*` | (server-side only) |
|
|
733
|
+
| POST | `/api/push/live-activity` | `Authorization: Bearer sp_push_*` | (server-side only) |
|
|
734
|
+
|
|
735
|
+
Default ingest endpoint: `https://ingest.sitepong.com`. Override via the `endpoint` option in `initRN()` or `SitePongRNProvider`.
|
|
736
|
+
|
|
599
737
|
### Device Intelligence (React Native)
|
|
600
738
|
|
|
601
739
|
Persistent device identification that **survives app reinstalls**. Requires the `@sitepong/device-id` native module.
|
package/dist/entries/rn.js
CHANGED
|
@@ -2811,83 +2811,21 @@ function createAsyncStorageAdapter(asyncStorage) {
|
|
|
2811
2811
|
removeItem: (key) => asyncStorage.removeItem(key)
|
|
2812
2812
|
};
|
|
2813
2813
|
}
|
|
2814
|
-
var deviceIdModule = null;
|
|
2815
|
-
function getDeviceIdModule() {
|
|
2816
|
-
if (!deviceIdModule) {
|
|
2817
|
-
try {
|
|
2818
|
-
const n = ["@sitepong", "device-id"].join("/");
|
|
2819
|
-
deviceIdModule = __require(n);
|
|
2820
|
-
} catch {
|
|
2821
|
-
deviceIdModule = null;
|
|
2822
|
-
}
|
|
2823
|
-
}
|
|
2824
|
-
return deviceIdModule;
|
|
2825
|
-
}
|
|
2826
2814
|
function collectDeviceInfo() {
|
|
2827
|
-
const
|
|
2815
|
+
const screen = reactNative.Dimensions.get("screen");
|
|
2816
|
+
return {
|
|
2828
2817
|
platform: reactNative.Platform.OS,
|
|
2829
|
-
osVersion: String(reactNative.Platform.Version)
|
|
2818
|
+
osVersion: String(reactNative.Platform.Version),
|
|
2819
|
+
screenWidth: screen.width,
|
|
2820
|
+
screenHeight: screen.height,
|
|
2821
|
+
screenScale: screen.scale
|
|
2830
2822
|
};
|
|
2831
|
-
const screen = reactNative.Dimensions.get("screen");
|
|
2832
|
-
info.screenWidth = screen.width;
|
|
2833
|
-
info.screenHeight = screen.height;
|
|
2834
|
-
info.screenScale = screen.scale;
|
|
2835
|
-
let ExpoDevice = null;
|
|
2836
|
-
try {
|
|
2837
|
-
const n = ["expo", "device"].join("-");
|
|
2838
|
-
ExpoDevice = __require(n);
|
|
2839
|
-
} catch {
|
|
2840
|
-
}
|
|
2841
|
-
if (ExpoDevice) {
|
|
2842
|
-
if (ExpoDevice.brand) info.deviceBrand = ExpoDevice.brand;
|
|
2843
|
-
if (ExpoDevice.modelName) info.deviceModel = ExpoDevice.modelName;
|
|
2844
|
-
if (ExpoDevice.deviceName) info.deviceName = ExpoDevice.deviceName;
|
|
2845
|
-
info.isEmulator = !ExpoDevice.isDevice;
|
|
2846
|
-
}
|
|
2847
|
-
let ExpoApplication = null;
|
|
2848
|
-
try {
|
|
2849
|
-
const n = ["expo", "application"].join("-");
|
|
2850
|
-
ExpoApplication = __require(n);
|
|
2851
|
-
} catch {
|
|
2852
|
-
}
|
|
2853
|
-
if (ExpoApplication) {
|
|
2854
|
-
if (ExpoApplication.nativeApplicationVersion) {
|
|
2855
|
-
info.appVersion = ExpoApplication.nativeApplicationVersion;
|
|
2856
|
-
}
|
|
2857
|
-
if (ExpoApplication.nativeBuildVersion) {
|
|
2858
|
-
info.appBuildNumber = ExpoApplication.nativeBuildVersion;
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
return info;
|
|
2862
2823
|
}
|
|
2863
2824
|
async function fetchPersistentDeviceId() {
|
|
2864
|
-
|
|
2865
|
-
if (!mod) return null;
|
|
2866
|
-
try {
|
|
2867
|
-
return await mod.getDeviceId();
|
|
2868
|
-
} catch {
|
|
2869
|
-
return null;
|
|
2870
|
-
}
|
|
2825
|
+
return null;
|
|
2871
2826
|
}
|
|
2872
2827
|
async function fetchNativeDeviceSignals() {
|
|
2873
|
-
|
|
2874
|
-
if (!mod) return null;
|
|
2875
|
-
try {
|
|
2876
|
-
const signals = await mod.getDeviceSignals();
|
|
2877
|
-
return {
|
|
2878
|
-
platform: signals.platform,
|
|
2879
|
-
osVersion: signals.osVersion,
|
|
2880
|
-
deviceModel: signals.model,
|
|
2881
|
-
deviceBrand: signals.manufacturer,
|
|
2882
|
-
isEmulator: signals.isEmulator,
|
|
2883
|
-
screenScale: signals.screenScale,
|
|
2884
|
-
deviceId: signals.deviceId,
|
|
2885
|
-
identifierForVendor: signals.identifierForVendor,
|
|
2886
|
-
androidId: signals.androidId
|
|
2887
|
-
};
|
|
2888
|
-
} catch {
|
|
2889
|
-
return null;
|
|
2890
|
-
}
|
|
2828
|
+
return null;
|
|
2891
2829
|
}
|
|
2892
2830
|
|
|
2893
2831
|
// src/react-native/screen-recorder.ts
|
|
@@ -3222,7 +3160,20 @@ var RNPerformanceManager = class {
|
|
|
3222
3160
|
};
|
|
3223
3161
|
|
|
3224
3162
|
// src/react-native/push.ts
|
|
3163
|
+
var identifyHookInstalled = false;
|
|
3164
|
+
function ensureIdentifyHookInstalled() {
|
|
3165
|
+
if (identifyHookInstalled) return;
|
|
3166
|
+
identifyHookInstalled = true;
|
|
3167
|
+
try {
|
|
3168
|
+
const c = sitepong;
|
|
3169
|
+
if (typeof c.registerIdentifyHook === "function") {
|
|
3170
|
+
c.registerIdentifyHook(reRegisterTokensWithUserId);
|
|
3171
|
+
}
|
|
3172
|
+
} catch {
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3225
3175
|
var cachedDeviceContext = null;
|
|
3176
|
+
var registeredPushToken = null;
|
|
3226
3177
|
var registeredLiveActivityTokens = /* @__PURE__ */ new Map();
|
|
3227
3178
|
var registeredPushToStartTokens = /* @__PURE__ */ new Map();
|
|
3228
3179
|
function getEndpoint() {
|
|
@@ -3303,9 +3254,10 @@ async function deleteFromIngest(path, body) {
|
|
|
3303
3254
|
}
|
|
3304
3255
|
}
|
|
3305
3256
|
function registerDeviceToken(token, options) {
|
|
3257
|
+
ensureIdentifyHookInstalled();
|
|
3306
3258
|
const device = getDeviceContext();
|
|
3307
3259
|
const tokenType = options.platform === "ios" ? "apns" : "fcm";
|
|
3308
|
-
|
|
3260
|
+
registeredPushToken = { token, environment: options.environment };
|
|
3309
3261
|
postToIngest("/api/push/tokens", {
|
|
3310
3262
|
native_device_token: token,
|
|
3311
3263
|
token_type: tokenType,
|
|
@@ -3319,6 +3271,7 @@ function registerDeviceToken(token, options) {
|
|
|
3319
3271
|
});
|
|
3320
3272
|
}
|
|
3321
3273
|
function registerLiveActivityToken(activityType, activityId, pushToken, options) {
|
|
3274
|
+
ensureIdentifyHookInstalled();
|
|
3322
3275
|
const device = getDeviceContext();
|
|
3323
3276
|
const userId = getUserId();
|
|
3324
3277
|
const key = `${activityType}:${activityId}`;
|
|
@@ -3338,6 +3291,7 @@ function registerLiveActivityToken(activityType, activityId, pushToken, options)
|
|
|
3338
3291
|
});
|
|
3339
3292
|
}
|
|
3340
3293
|
function registerPushToStartToken(activityType, pushToStartToken, options) {
|
|
3294
|
+
ensureIdentifyHookInstalled();
|
|
3341
3295
|
const device = getDeviceContext();
|
|
3342
3296
|
const userId = getUserId();
|
|
3343
3297
|
registeredPushToStartTokens.set(activityType, {
|
|
@@ -3361,6 +3315,42 @@ function endLiveActivity(activityType, activityId) {
|
|
|
3361
3315
|
activity_id: activityId
|
|
3362
3316
|
});
|
|
3363
3317
|
}
|
|
3318
|
+
function reRegisterTokensWithUserId(userId) {
|
|
3319
|
+
if (registeredPushToken) {
|
|
3320
|
+
const device = getDeviceContext();
|
|
3321
|
+
postToIngest("/api/push/tokens", {
|
|
3322
|
+
expo_push_token: registeredPushToken.token,
|
|
3323
|
+
environment: registeredPushToken.environment,
|
|
3324
|
+
device_id: device.deviceId,
|
|
3325
|
+
user_id: userId,
|
|
3326
|
+
platform: device.platform,
|
|
3327
|
+
app_version: device.appVersion,
|
|
3328
|
+
device_model: device.deviceModel,
|
|
3329
|
+
os_version: device.osVersion
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
for (const entry of registeredLiveActivityTokens.values()) {
|
|
3333
|
+
const device = getDeviceContext();
|
|
3334
|
+
postToIngest("/api/push/live-activity-tokens", {
|
|
3335
|
+
activity_type: entry.activityType,
|
|
3336
|
+
activity_id: entry.activityId,
|
|
3337
|
+
push_token: entry.token,
|
|
3338
|
+
environment: entry.environment,
|
|
3339
|
+
device_id: device.deviceId,
|
|
3340
|
+
user_id: userId
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
for (const entry of registeredPushToStartTokens.values()) {
|
|
3344
|
+
const device = getDeviceContext();
|
|
3345
|
+
postToIngest("/api/push/push-to-start-tokens", {
|
|
3346
|
+
activity_type: entry.activityType,
|
|
3347
|
+
push_to_start_token: entry.token,
|
|
3348
|
+
environment: entry.environment,
|
|
3349
|
+
device_id: device.deviceId,
|
|
3350
|
+
user_id: userId
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3364
3354
|
var SitePongRNContext = react.createContext({
|
|
3365
3355
|
isInitialized: false,
|
|
3366
3356
|
performanceManager: null
|