@series-inc/venus-sdk 3.0.4 → 3.1.0-beta.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.
Files changed (39) hide show
  1. package/README.md +324 -123
  2. package/dist/{AdsApi-CNGRf6j0.d.mts → AdsApi-meVfUcZy.d.mts} +37 -25
  3. package/dist/{AdsApi-CNGRf6j0.d.ts → AdsApi-meVfUcZy.d.ts} +37 -25
  4. package/dist/chunk-2PDL7CQK.mjs +26 -0
  5. package/dist/chunk-2PDL7CQK.mjs.map +1 -0
  6. package/dist/{chunk-PXWCNWJ6.mjs → chunk-EMVTVSGL.mjs} +421 -109
  7. package/dist/chunk-EMVTVSGL.mjs.map +1 -0
  8. package/dist/chunk-IZLOB7DV.mjs +343 -0
  9. package/dist/chunk-IZLOB7DV.mjs.map +1 -0
  10. package/dist/{chunk-W7IPHM67.mjs → chunk-QABXMFND.mjs} +3 -26
  11. package/dist/chunk-QABXMFND.mjs.map +1 -0
  12. package/dist/core-5JLON75E.mjs +4 -0
  13. package/dist/{core-R3FHW62G.mjs.map → core-5JLON75E.mjs.map} +1 -1
  14. package/dist/index.cjs +768 -105
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.mts +140 -25
  17. package/dist/index.d.ts +140 -25
  18. package/dist/index.mjs +5 -3
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/venus-api/index.cjs +452 -101
  21. package/dist/venus-api/index.cjs.map +1 -1
  22. package/dist/venus-api/index.d.mts +2 -2
  23. package/dist/venus-api/index.d.ts +2 -2
  24. package/dist/venus-api/index.mjs +46 -3
  25. package/dist/venus-api/index.mjs.map +1 -1
  26. package/dist/vite/index.cjs +534 -0
  27. package/dist/vite/index.cjs.map +1 -0
  28. package/dist/vite/index.mjs +527 -0
  29. package/dist/vite/index.mjs.map +1 -0
  30. package/dist/webview/index.cjs +346 -0
  31. package/dist/webview/index.cjs.map +1 -0
  32. package/dist/webview/index.d.mts +17 -0
  33. package/dist/webview/index.d.ts +17 -0
  34. package/dist/webview/index.mjs +4 -0
  35. package/dist/webview/index.mjs.map +1 -0
  36. package/package.json +19 -1
  37. package/dist/chunk-PXWCNWJ6.mjs.map +0 -1
  38. package/dist/chunk-W7IPHM67.mjs.map +0 -1
  39. package/dist/core-R3FHW62G.mjs +0 -3
package/README.md CHANGED
@@ -500,6 +500,95 @@ const shareResult = await VenusAPI.post.sharePostAsync({
500
500
 
501
501
  ---
502
502
 
503
+ ### Social API (BETA)
504
+
505
+ Share links and QR codes for challenges and UGC (user-generated content):
506
+
507
+ ```typescript
508
+ // Share a high score challenge - automatically opens share dialog!
509
+ const { shareUrl } = await VenusAPI.social.shareLinkAsync({
510
+ launchParams: {
511
+ challengeType: 'highscore',
512
+ scoreTobeat: '1500',
513
+ challengerId: VenusAPI.profile.getCurrentProfile().id
514
+ },
515
+ metadata: {
516
+ title: 'Beat my score!',
517
+ description: 'Can you beat 1500 points in Word Search?',
518
+ imageUrl: 'https://cdn.example.com/share-highscore.png'
519
+ }
520
+ })
521
+ // This function automatically triggers the share UI:
522
+ // - iOS/Android: Opens native share sheet
523
+ // - Web: Uses navigator.share() if available, otherwise copies to clipboard
524
+ // The returned shareUrl is the generated link that was shared
525
+
526
+ // Generate QR code for in-person challenges (does NOT open share dialog)
527
+ const { shareUrl, qrCode } = await VenusAPI.social.createQRCodeAsync({
528
+ launchParams: {
529
+ challengeType: 'daily',
530
+ dailyPuzzleId: '2024-11-04',
531
+ invitedBy: VenusAPI.profile.getCurrentProfile().username
532
+ },
533
+ metadata: {
534
+ title: 'Daily Challenge',
535
+ description: 'Scan to play today\'s puzzle with me!'
536
+ },
537
+ qrOptions: {
538
+ size: 512, // Size in pixels (default: 256)
539
+ margin: 4, // Margin in modules (default: 2)
540
+ format: 'png' // 'png' or 'svg'
541
+ }
542
+ })
543
+ // This only generates the QR code - you control how to display it
544
+
545
+ // Display the QR code in your UI
546
+ const img = document.createElement('img')
547
+ img.src = qrCode // Data URL (e.g., 'data:image/png;base64,...')
548
+ document.body.appendChild(img)
549
+
550
+ // Handle incoming challenge when app opens from share
551
+ const launchParams = VenusAPI.getLaunchParams()
552
+ if (launchParams.challengeType === 'highscore') {
553
+ const targetScore = parseInt(launchParams.scoreTobeat, 10)
554
+ const challengerId = launchParams.challengerId
555
+ startHighScoreChallenge(targetScore, challengerId)
556
+ }
557
+
558
+ // Handle daily puzzle invites
559
+ if (launchParams.challengeType === 'daily') {
560
+ const puzzleId = launchParams.dailyPuzzleId
561
+ const inviter = launchParams.invitedBy
562
+ loadDailyPuzzle(puzzleId, inviter)
563
+ }
564
+
565
+ // Referral system example
566
+ if (launchParams.referredBy) {
567
+ await rewardReferrer(launchParams.referredBy)
568
+ showMessage(`You were invited by ${launchParams.referredBy}!`)
569
+ }
570
+ ```
571
+
572
+ **Launch Parameters:**
573
+
574
+ Launch parameters are completely arbitrary - define whatever your game needs:
575
+
576
+ Examples from the code above:
577
+ - `challengeType`, `scoreTobeat`, `challengerId` - For beat-my-score challenges
578
+ - `dailyPuzzleId`, `invitedBy` - For daily puzzle invites
579
+ - `referredBy` - For referral tracking
580
+ - `levelId`, `replayId`, `customMapId` - For UGC content
581
+
582
+ All parameters are custom game data - no reserved or required fields.
583
+
584
+ **Size Limits:**
585
+
586
+ - **Max total size**: Keep `launchParams` under ~100KB (share data stored in Firestore with 1MB document limit)
587
+ - **Use compact formats**: IDs and short strings work best
588
+ - **Best practice**: Store large UGC separately and only pass an ID in `launchParams`
589
+
590
+ ---
591
+
503
592
  ### Analytics API
504
593
 
505
594
  ```typescript
@@ -575,24 +664,26 @@ await VenusAPI.rooms.sendMessage({
575
664
 
576
665
  ### Time API
577
666
 
578
- Server time synchronization and formatting:
667
+ Server time synchronization and formatting with automatic locale detection:
579
668
 
580
669
  ```typescript
581
670
  // Get server time
582
671
  const serverTimeData = await VenusAPI.time.requestTimeAsync()
583
672
  // Returns: { serverTime, localTime, timezoneOffset, formattedTime, locale }
584
673
 
585
- // Format time with locale
674
+ // Format time (locale is automatically determined from user settings)
586
675
  const formatted = VenusAPI.time.formatTime(Date.now(), {
587
- locale: 'en-US',
588
- format: 'full' // 'full', 'long', 'medium', 'short'
676
+ dateStyle: 'full', // 'full', 'long', 'medium', 'short'
677
+ timeStyle: 'medium',
678
+ hour12: true
589
679
  })
590
680
 
591
- // Format number with locale
681
+ // Format number (locale is automatically determined from user settings)
592
682
  const formattedNumber = VenusAPI.time.formatNumber(1234567.89, {
593
- locale: 'en-US',
594
683
  style: 'currency',
595
- currency: 'USD'
684
+ currency: 'USD',
685
+ minimumFractionDigits: 2,
686
+ maximumFractionDigits: 2
596
687
  })
597
688
 
598
689
  // Get future time
@@ -692,151 +783,221 @@ VenusAPI.logging.debug('Debug info')
692
783
 
693
784
  ### Leaderboard API (BETA)
694
785
 
695
- Competitive leaderboards with anti-cheat, multiple modes, and time periods.
786
+ Competitive leaderboards with three security levels. Choose based on your game's requirements.
696
787
 
697
- #### API Methods
788
+ ---
698
789
 
699
- ```typescript
700
- // Start a leaderboard session
701
- const session = await VenusAPI.leaderboard.startRunAsync({
702
- mode: 'classic' // Optional: 'classic', 'hard', etc. Defaults to 'default'
703
- })
704
- // Returns: { sessionId, startTime, expiresAt, hashNonce?, mode }
790
+ #### 🟢 Simple Mode (Default - Casual Games)
705
791
 
706
- // Submit a score (within 1 hour of starting session)
792
+ Submit scores directly without tokens:
793
+
794
+ ```typescript
795
+ // One call - no token needed!
707
796
  const result = await VenusAPI.leaderboard.submitScoreAsync({
708
- sessionId: session.sessionId,
709
797
  score: 1500,
710
- durationSec: 120,
711
- mode: 'classic', // Optional: must match session mode
712
- metadata: { // Optional: game-specific data (max 20KB)
798
+ duration: 120,
799
+ mode: 'classic',
800
+ metadata: {
713
801
  levelCompleted: 10,
714
802
  powerUpsUsed: 3
715
803
  },
716
- telemetry: { // Optional: anti-cheat data (max 2KB, primitives only)
804
+ telemetry: {
717
805
  clickCount: 245,
718
806
  avgReactionTime: 0.32
719
- },
720
- hash: computedHash // Required if leaderboard uses hash verification
807
+ }
808
+ })
809
+
810
+ console.log(`Your rank: ${result.rank}`)
811
+ ```
812
+
813
+ **Configuration:** None needed (uses defaults)
814
+
815
+ **Security provided:**
816
+ - ✅ Score/duration bounds validation
817
+ - ✅ Rate limiting (60 second cooldown per player)
818
+ - ✅ Trust scores & shadow banning for repeat offenders
819
+ - ❌ No session replay protection
820
+ - ❌ No tamper protection
821
+
822
+ **Best for:** Simple integration
823
+
824
+ ---
825
+
826
+ #### 🟡 Token Mode (Competitive Games)
827
+
828
+ Add session validation for replay protection:
829
+
830
+ ```typescript
831
+ // Step 1: Create score token
832
+ const scoreToken = await VenusAPI.leaderboard.createScoreTokenAsync({
833
+ mode: 'ranked'
834
+ })
835
+ // Returns: { token, startTime, expiresAt, mode }
836
+
837
+ // Step 2: Play game...
838
+
839
+ // Step 3: Submit with token
840
+ const result = await VenusAPI.leaderboard.submitScoreAsync({
841
+ token: scoreToken.token,
842
+ score: 1500,
843
+ duration: 120,
844
+ metadata: { ... }
845
+ })
846
+ ```
847
+
848
+ **Configuration:**
849
+ ```json
850
+ {
851
+ "leaderboard": {
852
+ "requiresToken": true
853
+ }
854
+ }
855
+ ```
856
+
857
+ **Additional security:**
858
+ - ✅ All simple mode security
859
+ - ✅ Session validation (tokens expire in 1 hour)
860
+ - ✅ Replay attack prevention (one-time use)
861
+ - ✅ Mode locking (token locks game mode)
862
+ - ❌ No tamper protection
863
+
864
+ **Best for:** Preventing replay attacks
865
+
866
+ ---
867
+
868
+ #### 🔴 Score Sealing Mode (High-Stakes Games)
869
+
870
+ Add cryptographic tamper protection:
871
+
872
+ ```typescript
873
+ // Step 1: Create score token (sealing data included automatically if enabled)
874
+ const scoreToken = await VenusAPI.leaderboard.createScoreTokenAsync({
875
+ mode: 'tournament'
721
876
  })
722
- // Returns: { accepted, rank, zScore, isAnomaly }
723
-
724
- // Get leaderboard (paginated)
725
- const leaderboard = await VenusAPI.leaderboard.getLeaderboardAsync({
726
- mode: 'classic', // Optional: game mode
727
- period: 'daily', // Optional: 'daily', 'weekly', 'monthly', 'alltime'
728
- periodDate: Date.now(), // Optional: specific period to view
729
- limit: 50, // Optional: entries per page (max 50)
730
- cursor: nextCursor // Optional: for pagination
877
+ // Returns: { token, sealingNonce, sealingSecret, ... }
878
+
879
+ // Step 2: Play game...
880
+
881
+ // Step 3: Submit score - SDK auto-computes hash internally!
882
+ const result = await VenusAPI.leaderboard.submitScoreAsync({
883
+ token: scoreToken.token,
884
+ score: 1500,
885
+ duration: 120,
731
886
  })
732
- // Returns: {
733
- // variant: 'standard',
734
- // entries: [{ profileId, username, avatarUrl, score, rank, ... }],
735
- // totalEntries: 1234,
736
- // nextCursor: 'abc123...',
737
- // playerRank: 42,
738
- // periodInstance: 'daily_2025-11-04'
739
- // }
740
-
741
- // Get leaderboard highlight (top players + context around you)
742
- const highlight = await VenusAPI.leaderboard.getLeaderboardHighlightAsync({
887
+ ```
888
+
889
+ **Configuration:**
890
+ ```json
891
+ {
892
+ "leaderboard": {
893
+ "requiresToken": true,
894
+ "enableScoreSealing": true,
895
+ "scoreSealingSecret": "your-secret-key-change-in-production"
896
+ }
897
+ }
898
+ ```
899
+
900
+ **Note:** Hash computation is handled internally by the SDK using Web Crypto API (HMAC-SHA256). Games never need to implement cryptographic hashing manually. The hash always includes: `score`, `duration`, and `token`.
901
+
902
+ **Maximum security:**
903
+ - ✅ All token mode security
904
+ - ✅ Tamper-proof scores (HMAC-SHA256 verification)
905
+ - ✅ Client-side cheat detection
906
+ - ✅ Automatic hash computation (no crypto code needed in games)
907
+
908
+ **Best for:** Reduced hacking
909
+
910
+ ---
911
+
912
+ #### Query Methods (Same for All Modes)
913
+
914
+ All query methods work identically regardless of security mode:
915
+
916
+ ```typescript
917
+ // Get paginated scores
918
+ const pagedScores = await VenusAPI.leaderboard.getPagedScoresAsync({
743
919
  mode: 'classic',
744
920
  period: 'daily',
745
- topCount: 3, // Top N players (podium)
746
- contextAhead: 4, // Players ahead of you
747
- contextBehind: 2 // Players behind you
921
+ limit: 50,
922
+ cursor: nextCursor
748
923
  })
749
- // Returns: {
750
- // variant: 'highlight',
751
- // entries: [...],
752
- // context: {
753
- // topEntries: [...], // Top 3
754
- // beforePlayer: [...], // 4 players ahead
755
- // playerEntry: {...}, // Your entry
756
- // afterPlayer: [...], // 2 players behind
757
- // omittedBefore: 35, // Gap between top 3 and you
758
- // omittedAfter: 100 // Players below you
759
- // },
760
- // playerRank: 42,
761
- // totalEntries: 150
762
- // }
763
-
764
- // Get player stats
765
- const stats = await VenusAPI.leaderboard.getPlayerStatsAsync({
924
+
925
+ // Get podium + player context
926
+ const podiumScores = await VenusAPI.leaderboard.getPodiumScoresAsync({
927
+ mode: 'classic',
928
+ period: 'daily',
929
+ topCount: 3,
930
+ contextAhead: 4,
931
+ contextBehind: 2
932
+ })
933
+
934
+ // Get my rank
935
+ const rank = await VenusAPI.leaderboard.getMyRankAsync({
766
936
  mode: 'classic',
767
937
  period: 'daily'
768
938
  })
769
- // Returns: {
770
- // rank: 42,
771
- // score: 1500,
772
- // totalPlayers: 150,
773
- // percentile: 0.72, // 72nd percentile
774
- // trustScore: 85,
775
- // periodInstance: 'daily_2025-11-04'
776
- // }
777
939
  ```
778
940
 
779
- #### Configuration
941
+ ---
942
+
943
+ #### Configuration Reference
780
944
 
781
945
  Add a `leaderboard` object to your game's `config.json` file:
782
946
 
783
947
  ```json
784
948
  {
785
949
  "leaderboard": {
786
- "isDisabled": false,
787
- "disabledMessage": "Leaderboards are temporarily unavailable.",
788
- "requiresHash": true,
789
- "hashSecret": "your-secret-key-change-in-production",
790
- "scoringFields": ["score", "durationSec", "sessionId"],
791
- "minDurationSec": 10,
792
- "maxDurationSec": 600,
950
+ // Basic settings
793
951
  "minScore": 0,
794
952
  "maxScore": 999999999,
953
+ "minDurationSec": 10,
954
+ "maxDurationSec": 600,
955
+
956
+ // Security (progressive levels)
957
+ "requiresToken": false, // Simple mode (default)
958
+ "enableScoreSealing": false, // Requires requiresToken=true
959
+ "scoreSealingSecret": "secret", // Required if enableScoreSealing=true
960
+
961
+ // Game modes
795
962
  "modes": {
796
- "default": {
797
- "displayName": "Classic Mode"
798
- },
799
- "hard": {
800
- "displayName": "Hard Mode",
801
- "minDurationSec": 5,
802
- "maxDurationSec": 300
803
- }
963
+ "default": { "displayName": "Normal" },
964
+ "hard": { "displayName": "Hard Mode" }
804
965
  },
966
+
967
+ // Time periods
805
968
  "periods": {
806
- "daily": {
807
- "displayName": "Daily",
808
- "type": "daily"
809
- },
810
- "alltime": {
811
- "displayName": "All Time",
812
- "type": "alltime"
813
- }
969
+ "daily": { "displayName": "Daily", "type": "daily" },
970
+ "alltime": { "displayName": "All Time", "type": "alltime" }
814
971
  },
972
+
973
+ // Anti-cheat
815
974
  "antiCheat": {
816
- "enableZScoreDetection": false,
817
- "zScoreThreshold": 3,
818
975
  "enableRateLimit": true,
819
976
  "minTimeBetweenSubmissionsSec": 60,
820
977
  "trustScoreDecayPerFlag": 10,
821
- "shadowBanThreshold": 20
978
+ "shadowBanThreshold": 20,
979
+ "enableZScoreDetection": false,
980
+ "zScoreThreshold": 3
822
981
  },
982
+
983
+ // Display
823
984
  "displaySettings": {
824
985
  "maxEntriesPerPage": 50
825
986
  },
987
+
988
+ // Seed data (optional)
826
989
  "seedEntries": {
827
990
  "default": {
828
991
  "daily": [
829
992
  {
830
993
  "score": 18500,
831
994
  "username": "ProPlayer",
832
- "avatarUrl": null,
833
- "durationSec": 180
995
+ "duration": 180
834
996
  },
835
997
  {
836
998
  "score": 15200,
837
999
  "username": "Challenger",
838
- "avatarUrl": null,
839
- "durationSec": 210
1000
+ "duration": 210
840
1001
  }
841
1002
  ]
842
1003
  }
@@ -845,34 +1006,18 @@ Add a `leaderboard` object to your game's `config.json` file:
845
1006
  }
846
1007
  ```
847
1008
 
848
- **Configuration Options:**
849
1009
 
850
- - `isDisabled`: Globally disable leaderboards
851
- - `requiresHash`: Enable HMAC-based score verification (recommended for production)
852
- - `hashSecret`: Secret key for HMAC verification (keep this secure!)
853
- - `scoringFields`: Fields included in hash verification (can include nested fields like `metadata.level`)
854
- - `minDurationSec` / `maxDurationSec`: Valid gameplay duration range
855
- - `minScore` / `maxScore`: Valid score bounds
856
- - `modes`: Game modes (can override min/max values per mode)
857
- - `periods`: Time periods for leaderboards (`daily`, `weekly`, `monthly`, `alltime`)
858
- - `antiCheat`: Anti-cheat settings
859
- - `seedEntries`: Pre-populate leaderboards with NPC scores (nested by mode → period)
1010
+
860
1011
 
861
1012
  **Features:**
1013
+ - **Three Security Levels**: Simple, Token, Sealed - choose based on game stakes
862
1014
  - **Multiple Modes**: Support different game modes (classic, hard, etc.)
863
1015
  - **Time Periods**: Daily, weekly, monthly, and all-time leaderboards
864
- - **Anti-Cheat**: Session validation, rate limiting, hash verification, z-score anomaly detection
865
- - **Shadow Banning**: Cheaters see their own scores but are hidden from others
866
- - **Trust Scores**: Per-player reputation that decays with suspicious behavior
1016
+ - **Anti-Cheat**: Rate limiting, trust scores, shadow banning, optional session validation & sealing
867
1017
  - **Seed Entries**: Pre-populate leaderboards with NPC scores
868
1018
  - **Pagination**: Cursor-based pagination for large leaderboards
869
1019
  - **UTC-Based Periods**: All players globally compete in same daily/weekly/monthly periods
870
1020
 
871
- **Security Notes:**
872
- - Session tokens expire after 1 hour and can only be used once to prevent replay attacks
873
- - Use `requiresHash: true` in production with a strong `hashSecret`
874
- - Keep `hashSecret` out of version control (use environment variables or local config)
875
- - Store `hashSecret` in `config.local.json` (gitignored) for local development
876
1021
 
877
1022
  ---
878
1023
 
@@ -984,9 +1129,65 @@ Tests are written using Jest with JSDOM environment for browser API simulation.
984
1129
 
985
1130
  ---
986
1131
 
987
- ## License
1132
+ ## Embedded Libraries (Vite)
1133
+
1134
+ For **Vite projects**, use the included plugin to automatically reduce bundle size by using embedded libraries:
1135
+
1136
+ ```bash
1137
+ npm install @series-inc/venus-sdk
1138
+ ```
1139
+
1140
+ ```typescript
1141
+ // vite.config.ts
1142
+ import { venusLibrariesPlugin } from '@series-inc/venus-sdk/vite';
1143
+
1144
+ export default defineConfig({
1145
+ plugins: [venusLibrariesPlugin({ appName: 'my-game' })],
1146
+ build: { target: 'es2022' } // Required for top-level await
1147
+ });
1148
+ ```
1149
+
1150
+ **Build commands:**
1151
+ - `npm run build` → Embedded libraries (default, optimal)
1152
+ - `VENUS_DISABLE_EMBEDDED_LIBS=true npm run build` → Bundled (standalone)
1153
+
1154
+ **Supported libraries:** Phaser, React, ReactDOM, Three.js, Matter.js, inkjs, Zustand
1155
+
1156
+ See `src/vite/README.md` for details.
1157
+
1158
+ ### For Non-Vite Bundlers
1159
+
1160
+ The Vite plugin does three things you can recreate in any bundler:
1161
+
1162
+ **1. Externalize library imports** - Replace imports with virtual modules:
1163
+ ```javascript
1164
+ // Input: import Phaser from 'phaser';
1165
+ // Output: const lib = await (async () => {
1166
+ // await window.__venusLibraryShim.ready();
1167
+ // return window.__venusLibraryExports['phaser@3.90.0'];
1168
+ // })();
1169
+ // export default lib;
1170
+ ```
1171
+
1172
+ **2. Inject config into HTML `<head>`:**
1173
+ ```html
1174
+ <script>
1175
+ window.__venusLibrariesConfig = {
1176
+ enabled: true,
1177
+ required: ["phaser@3.90.0"],
1178
+ manifest: { "phaser@3.90.0": { cdnPath: "...", globalVar: "Phaser" } },
1179
+ cdnBase: "https://venus-static-01293ak.web.app/libs/"
1180
+ };
1181
+ </script>
1182
+ ```
1183
+
1184
+ **3. Inject minimal shim** (loads from CDN, resolves `ready()` promise):
1185
+ ```javascript
1186
+ window.__venusLibraryShim = { ready: () => Promise };
1187
+ // Fetch libraries from CDN, evaluate, register in __venusLibraryExports
1188
+ ```
988
1189
 
989
- MIT
1190
+ See `embeddedLibrariesManifest.ts` for library definitions and `webviewLibraryShimSource.ts` for the full shim (used on mobile).
990
1191
 
991
1192
  ---
992
1193