despia-native 1.0.20 → 1.0.21

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 (2) hide show
  1. package/README.md +432 -1089
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,1326 +1,669 @@
1
- # Despia SDK
1
+ # Despia Native
2
2
 
3
- JavaScript SDK for [Despia](https://despia.com) - Add real native device features to your React web app, Vue app, Angular app, or any web framework. Transform your web app into a native iOS & Android app without writing Swift or Kotlin. This npm package provides command queuing and variable watching for seamless integration with Despia's GPU-accelerated native runtime, enabling access to 25+ device APIs through simple JavaScript calls.
3
+ JavaScript SDK for [Despia](https://despia.com). Build with any web framework, access 50+ native device capabilities through a single JavaScript function, and publish to iOS and Android from a browser. No Swift, no Kotlin, no terminal.
4
4
 
5
- ---
6
-
7
- ## Quick Start
8
-
9
- **Install the SDK:**
10
- ```bash
11
- npm install despia-native
12
- ```
13
-
14
- **Use it immediately:**
15
- ```javascript
16
- import despia from 'despia-native';
5
+ [![npm](https://img.shields.io/npm/v/despia-native)](https://www.npmjs.com/package/despia-native)
6
+ [![license](https://img.shields.io/npm/l/despia-native)](LICENSE)
17
7
 
18
- // Simple commands
19
- despia('lighthaptic://');
8
+ **[Documentation](https://setup.despia.com)** | **[AI Agent Index](https://setup.despia.com/llms.txt)** | **[iOS Deployment](https://setup.despia.com/deployment/apple-ios/automatic)** | **[Android Deployment](https://setup.despia.com/deployment/google-android/automatic)**
20
9
 
21
- // Commands with responses
22
- const appInfo = await despia('getappversion://', ['versionNumber']);
23
- ```
10
+ Despia is the next generation of web-native development. Unlike earlier hybrid frameworks that routed files through JavaScript, forced Base64 encoding, imposed storage quotas, and ran on insecure `file://` origins, Despia runs your app on a real secure origin (`http://localhost` via Local Server, or your remote URL). Standard web APIs work without restrictions. File operations bypass JavaScript entirely and go directly to the native file system. A 500MB video file uses roughly 100 bytes of JS heap rather than 1.6GB.
24
11
 
25
- **Note:** Always use the real SDK package. Mock implementations will not work on actual devices.
12
+ The runtime has been in production since 2011, powering over 7,500 apps on the foundation that became Despia in 2023. Native capabilities are implemented in Swift and Java and called from JavaScript through a single typed function.
26
13
 
27
14
  ---
28
15
 
29
- **Note: This is for web apps (React, Vue, Angular, etc.) to add native features - NOT for React Native apps.**
30
-
31
- ## Web Apps vs React Native
32
-
33
- **This SDK is for:**
34
- - React web apps (create-react-app, Next.js, Vite, etc.)
35
- - Vue web apps
36
- - Angular web apps
37
- - Svelte web apps
38
- - Any web framework or vanilla JavaScript
39
-
40
- **This SDK is NOT for:**
41
- - React Native apps
42
- - Expo apps
43
- - Native mobile development
44
-
45
- **If you're building a React Native app, this SDK won't work for you.**
46
-
47
- **Import:** `import despia from 'despia-native';` (default export, not destructured)
48
-
49
- **IMPORTANT: This SDK package is REQUIRED for TypeScript, React, Vue, and other modern frameworks!** While `window.despia = ""` works in vanilla JavaScript, this package provides type safety, command queuing, and variable watching for professional development environments.
50
-
51
- ## About Despia
52
-
53
- Despia bridges the gap between web and native mobile development. Build your React web app, Vue app, Angular app, or any web framework using the technologies you already know, then deploy it as a truly native application to the App Store and Google Play - complete with hardware acceleration, offline support, and deep OS integration.
54
-
55
- Our visual editor allows you to configure native widgets, shortcuts, and dynamic app behaviors without touching Xcode or Android Studio. Ship to both app stores with one-click deployment, automatic CI/CD pipelines, and over-the-air updates. Export clean, human-readable Swift and Kotlin source code anytime - you own everything, no vendor lock-in.
56
-
57
- **Go from web app to app store in a weekend - with full native capabilities.**
58
-
59
- ### Key Features:
60
- - **Web Framework Support** - Works with React web apps, Vue web apps, Angular web apps, Svelte web apps, vanilla JS, or any web framework
61
- - **NOT for React Native** - This is for web apps to add native features, not React Native apps
62
- - **Visual Configuration** - Set up native features through an intuitive interface
63
- - **Zero Native Coding** - Access device APIs without writing Swift or Kotlin
64
- - **Source Code Export** - Get complete Xcode and Android Studio projects you can modify
65
- - **True Ownership** - Full access to generated code, no restrictions
66
- - **One-Click Publishing** - Direct deployment to App Store & Google Play
67
- - **Automated Pipeline** - Built-in CI/CD with code signing and provisioning
68
- - **OTA Updates** - Push updates instantly without app store review
69
- - **Performance** - 60fps GPU-accelerated rendering
70
- - **Device API Access** - NFC, HealthKit, Siri, RevenueCat, and 20+ more
71
- - **Single Codebase** - Maintain one web project for iOS, Android, and web
72
-
73
- ### Native Device Features:
74
- - **App Links & Deep Linking** - Universal links and app-to-app communication
75
- - **Widgets & App Clips** - Home screen widgets and instant app experiences
76
- - **In-App Purchases** - RevenueCat integration for subscriptions and payments
77
- - **Push Notifications** - Local and remote notifications with rich media
78
- - **Camera & Media** - Camera access, photo library, and file management
79
- - **Biometric Authentication** - Face ID, Touch ID, and fingerprint
80
- - **Location Services** - Foreground and background GPS tracking
81
- - **Contacts** - Address book access with proper permissions
82
- - **Haptic Feedback** - Tactile responses for user interactions
83
- - **App Analytics** - Version tracking, bundle IDs, device identifiers
84
- - **Screenshots** - Programmatic screen capture
85
- - **Status Bar** - Dynamic styling and visibility control
86
- - **Privacy Controls** - IDFA tracking and permission management
87
- - **Share Sheets** - Native sharing UI
88
- - **UI Controls** - Loading indicators, navigation bars, dynamic layouts
89
- - **Plus 8+ additional native capabilities**
90
-
91
- ## Installation (REQUIRED - NO EXCEPTIONS)
92
-
93
- ```bash
94
- # npm
95
- npm install despia-native
96
-
97
- # pnpm
98
- pnpm install despia-native
99
-
100
- # yarn
101
- yarn add despia-native
102
- ```
103
-
104
- **This is NOT optional. Your app will NOT work without this package.**
105
-
106
- **Package Manager Support:**
107
- - ✅ **npm** - Fully supported
108
- - ✅ **pnpm** - Fully supported (v1.0.19+)
109
- - ✅ **yarn** - Fully supported
110
-
111
- **Do not attempt to:**
112
- - Write your own version
113
- - Copy/paste a mock implementation
114
- - Use window.despia directly in frameworks
115
- - Create a "simplified" version
116
-
117
- **The real SDK is required for ANY Despia functionality to work.**
118
-
119
- ## Important Notes
120
-
121
- ### Simple Function API
122
- The SDK exports a single function - no initialization or setup required:
123
-
124
- ```javascript
125
- import despia from 'despia-native';
126
-
127
- // Just call it directly
128
- despia('lighthaptic://');
129
- despia('takescreenshot://');
130
- ```
131
-
132
- ### Common Mistakes to Avoid
133
- ```javascript
134
- // Don't create mock implementations
135
- const despia = (command) => console.log(command); // Won't work on devices
136
-
137
- // Don't use non-existent methods
138
- despia.ready(); // This method doesn't exist
139
- despia.init(); // This method doesn't exist
140
- despia.setup(); // This method doesn't exist
141
- ```
142
-
143
- ## Getting Started
144
-
145
- ### Step 1: Import Despia SDK
146
-
147
- **IMPORTANT: Always import as `despia` (default export), NOT as `{Commands}` or destructured imports!**
148
-
149
- ```javascript
150
- // CORRECT - ES6/ES2015 modules (default import)
151
- import despia from 'despia-native';
16
+ ## MCP Server
152
17
 
153
- // CORRECT - CommonJS
154
- const despia = require('despia-native');
18
+ Add the Despia MCP to give your AI assistant full knowledge of the `despia-native` API.
155
19
 
156
- // CORRECT - Browser (if using UMD build)
157
- // <script src="despia-native.js"></script>
158
- // despia is available globally
20
+ [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=Despia&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vc2V0dXAuZGVzcGlhLmNvbS9tY3AifQ==)
21
+ [![Install in VS Code](https://img.shields.io/badge/Install_in_VS_Code-007ACC?logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22Despia%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fsetup.despia.com%2Fmcp%22%7D)
159
22
 
160
- // WRONG - Don't do this!
161
- // import { Commands } from 'despia-native';
162
- // import { despia } from 'despia-native';
163
23
  ```
164
-
165
- **The SDK exports a single function called `despia` as the default export.**
166
-
167
- ### Step 2: Use Native Features (No Setup Required)
168
-
169
- ```javascript
170
- // That's it! No initialization needed. Just call despia() directly:
171
-
172
- // Simple commands (no response needed)
173
- despia('lighthaptic://'); // Light haptic feedback
174
- despia('takescreenshot://'); // Take screenshot
175
- despia('spinneron://'); // Show loading spinner
176
-
177
- // Commands that return data (use await)
178
- const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
179
- console.log(appInfo); // { versionNumber: '1.0.0', bundleNumber: '123' }
180
-
181
- const contacts = await despia('readcontacts://', ['contacts']);
182
- console.log(contacts); // { contacts: [...] }
24
+ https://setup.despia.com/mcp
183
25
  ```
184
26
 
185
- ### Step 3: Handle Responses
27
+ Look for "Add MCP", "MCP Settings", or "Personal Connectors" in your builder. Requires Node.js v18+ for local tools.
186
28
 
187
- ```javascript
188
- // For commands that set variables, watch for them
189
- const result = await despia('get-uuid://', ['uuid']);
190
- console.log('Device UUID:', result.uuid);
191
-
192
- // For commands with no response, just call them
193
- despia('lighthaptic://');
194
- despia('takescreenshot://');
195
- ```
196
-
197
- ### Quick Examples
29
+ ---
198
30
 
199
- ```javascript
200
- // Haptic feedback
201
- despia('lighthaptic://'); // Light vibration
202
- despia('heavyhaptic://'); // Heavy vibration
203
- despia('successhaptic://'); // Success vibration
31
+ ## Table of Contents
32
+
33
+ - [MCP Server](#mcp-server)
34
+ - [Installation](#installation)
35
+ - [Quick Start](#quick-start)
36
+ - [Environment Detection](#environment-detection)
37
+ - [AI Agent Rules](#ai-agent-rules)
38
+ - [API Reference](#api-reference)
39
+ - [Features](#features)
40
+ - [Haptic Feedback](#haptic-feedback)
41
+ - [Identity Vault](#identity-vault)
42
+ - [GPS Location](#gps-location)
43
+ - [RevenueCat In-App Purchases](#revenuecat-in-app-purchases)
44
+ - [Push Notifications](#push-notifications)
45
+ - [OAuth Authentication](#oauth-authentication)
46
+ - [Clipboard](#clipboard)
47
+ - [Contacts](#contacts)
48
+ - [App Information and Device Data](#app-information-and-device-data)
49
+ - [UI Controls and Styling](#ui-controls-and-styling)
50
+ - [File and Media Operations](#file-and-media-operations)
51
+ - [Web Storage APIs](#web-storage-apis)
52
+ - [Local CDN](#local-cdn)
53
+ - [Local Server](#local-server)
54
+ - [Safe Area](#safe-area)
55
+ - [Web Apps vs React Native](#web-apps-vs-react-native)
56
+ - [License](#license)
204
57
 
205
- // App information
206
- const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
207
- const deviceId = await despia('get-uuid://', ['uuid']);
58
+ ---
208
59
 
209
- // UI controls
210
- despia('spinneron://'); // Show loading
211
- despia('spinneroff://'); // Hide loading
212
- despia('hidebars://on'); // Hide status bar
60
+ ## Installation
213
61
 
214
- // Screenshots and sharing
215
- despia('takescreenshot://');
216
- despia('shareapp://message?=Hello&url=https://myapp.com');
62
+ ```bash
63
+ npm install despia-native
64
+ # or
65
+ pnpm add despia-native
66
+ # or
67
+ yarn add despia-native
217
68
  ```
218
69
 
219
- ### Common Import Issues
70
+ Do not write mock implementations or use `window.despia` directly in modern frameworks. The real SDK is required for any Despia functionality to work.
220
71
 
221
- **If you get errors like "Commands is not a function" or "despia is not defined":**
72
+ ---
222
73
 
223
- ```javascript
224
- // WRONG - This will cause errors
225
- import { Commands } from 'despia-native';
226
- import { despia } from 'despia-native';
74
+ ## Quick Start
227
75
 
228
- // CORRECT - Always use default import
76
+ ```js
229
77
  import despia from 'despia-native';
230
78
 
231
- // Now you can use it
79
+ // Fire-and-forget commands
232
80
  despia('lighthaptic://');
233
- ```
234
-
235
- **The package exports a single function as the default export, not named exports.**
236
-
237
- ## When to Use This SDK Package
238
-
239
- ### Vanilla JavaScript (works without this package)
240
-
241
- ```javascript
242
- // This WORKS in vanilla JavaScript:
243
- window.despia = 'lighthaptic://';
244
- window.despia = 'getappversion://';
245
- ```
246
-
247
- **Vanilla JS is fine for simple cases, but lacks:**
248
- - **TypeScript Support** - No type definitions or autocomplete
249
- - **Command Queuing** - Commands may be lost or executed out of order
250
- - **Variable Watching** - Can't wait for responses from native commands
251
- - **Error Handling** - No timeout or error management
252
- - **Type Safety** - No validation or IntelliSense
253
-
254
- ### Modern Frameworks Need This Package
255
81
 
256
- ```javascript
257
- // This WON'T work in TypeScript, React, Vue, etc.:
258
- window.despia = 'lighthaptic://'; // TypeScript errors, no type safety
259
- window.despia = 'getappversion://'; // No command queuing, no variable watching
82
+ // Commands that return data
83
+ const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
84
+ console.log(appInfo.versionNumber); // "1.0.0"
260
85
  ```
261
86
 
262
- ### Modern Frameworks (TypeScript, React, Vue, etc.)
87
+ The SDK exports a single function as the default export. Always use a default import, not a named import.
263
88
 
264
- ```javascript
265
- // This WORKS perfectly in modern frameworks:
89
+ ```js
90
+ // Correct
266
91
  import despia from 'despia-native';
267
92
 
268
- despia('lighthaptic://'); // Type-safe, queued, with error handling
269
- const result = await despia('getappversion://', ['versionNumber']); // Variable watching
93
+ // Wrong
94
+ import { despia } from 'despia-native';
95
+ import { Commands } from 'despia-native';
270
96
  ```
271
97
 
272
- **Benefits of using this SDK:**
273
- - **TypeScript Support** - Full type definitions and autocomplete
274
- - **Command Queuing** - Sequential execution, no lost commands
275
- - **Variable Watching** - Automatic waiting for native responses
276
- - **Error Handling** - Timeouts, error management, debugging
277
- - **Type Safety** - Validated commands, autocomplete, IntelliSense
278
-
279
- **This package is REQUIRED for TypeScript, React, Vue, Angular, and other modern frameworks.**
280
-
281
- ## Development Notes
282
-
283
- ### Environment Detection
284
- The SDK only works within the Despia native runtime. For development and testing:
285
-
286
- ```javascript
287
- import despia from 'despia-native';
98
+ ---
288
99
 
289
- if (navigator.userAgent.includes('despia')) {
290
- // Use native features
291
- despia('lighthaptic://');
292
- } else {
293
- // Handle non-Despia environment
294
- console.log('Running outside Despia runtime');
295
- }
296
- ```
100
+ ## Environment Detection
297
101
 
298
- ### Always Use the Real Package
299
- - Use the real SDK in all environments (development, testing, production)
300
- - Don't create mock implementations - they won't work on actual devices
301
- - The SDK handles missing runtime gracefully
102
+ The SDK only activates inside the Despia native runtime. Gate native calls using these checks:
302
103
 
303
- ## Handling Non-Despia Environments
104
+ ```js
105
+ const isDespia = navigator.userAgent.toLowerCase().includes('despia');
304
106
 
305
- The SDK won't work outside the Despia native runtime, but you can detect and handle this:
107
+ const isDespiaIOS = isDespia &&
108
+ (navigator.userAgent.toLowerCase().includes('iphone') ||
109
+ navigator.userAgent.toLowerCase().includes('ipad'));
306
110
 
307
- ```javascript
308
- import despia from 'despia-native';
111
+ const isDespiaAndroid = isDespia &&
112
+ navigator.userAgent.toLowerCase().includes('android');
309
113
 
310
- // Check if running in Despia native runtime
311
- if (navigator.userAgent.includes('despia')) {
312
- // Use Despia native features
114
+ if (isDespia) {
313
115
  despia('lighthaptic://');
314
- const appInfo = await despia('getappversion://', ['versionNumber']);
315
116
  } else {
316
- // Handle non-Despia environment (browser, development, etc.)
317
- console.log('Running outside Despia runtime - native features unavailable');
318
- // Provide fallback behavior or show appropriate message
117
+ // Fallback for browser or development environment
319
118
  }
320
119
  ```
321
120
 
322
- **This is the correct way to handle different environments - use the real SDK with proper detection, never mock it.**
323
-
324
- ## Usage
121
+ ---
325
122
 
326
- ### Basic Despia Command Execution
123
+ ## AI Agent Rules
327
124
 
328
- ```javascript
329
- import despia from 'despia-native';
125
+ If you are an AI coding agent generating Despia app code, follow these rules:
330
126
 
331
- // Execute a Despia protocol command (no response needed)
332
- despia('lighthaptic://');
127
+ - Import as a default: `import despia from 'despia-native'`
128
+ - Do not write to `window.despia` directly; it is used internally by the SDK
129
+ - Gate all native calls using the environment checks above
130
+ - For offline (Local Server) apps, bundle the SDK via a package manager so it is available when the app is served from `http://localhost`
131
+ - For Local CDN downloads, do not `await` `localcdn://write` with a watch key; use `window.contentServerChange(item)` or poll with `localcdn://read`
132
+ - For the HTTP upload API, do not hardcode localhost ports; use `const host = window.location.host`
333
133
 
334
- // Execute command and watch for response variables (await needed)
335
- const result = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
336
- console.log(result); // { versionNumber: '1.0.0', bundleNumber: '123' }
337
- ```
134
+ ---
338
135
 
339
- ### Despia Command Examples
136
+ ## API Reference
340
137
 
341
- ```javascript
342
- // Native Widgets
343
- despia('widget://${svg}?refresh=${refresh_time}');
138
+ ### `despia(command, watch?)`
344
139
 
345
- // RevenueCat In-App Purchases
346
- despia('revenuecat://purchase?external_id=user_777&product=monthly_premium');
140
+ | Parameter | Type | Description |
141
+ |-----------|------|-------------|
142
+ | `command` | `string` | A Despia protocol URL, e.g. `'lighthaptic://'` |
143
+ | `watch` | `string[]` | Optional. Array of variable names to wait for in the response. |
347
144
 
348
- // Restore Purchases
349
- const purchaseData = await despia("getpurchasehistory://", ["restoredData"]);
145
+ Returns a `Promise` that resolves when all watched variables are set by the native runtime.
350
146
 
351
- // RevenueCat Paywalls
352
- despia('revenuecat://launchPaywall?external_id=user_777&offering=default');
147
+ **Timeout behavior**
353
148
 
354
- // Identity Vault
355
- await despia(`setvault://?key=userId&value=user123&locked=false`);
356
- const vaultData = await despia(`readvault://?key=userId`, ['userId']);
149
+ The SDK waits up to 30 seconds for a single watched variable. If the native runtime never sets it, the Promise resolves with `undefined` and logs a timeout to the console.
357
150
 
358
- // OAuth Authentication
359
- despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
151
+ **Fresh-data behavior**
360
152
 
361
- // Local Storage
362
- const encoded = encodeURIComponent(JSON.stringify(userData));
363
- await despia(`writevalue://${encoded}`);
364
- const data = await despia("readvalue://", ["storedValues"]);
153
+ Watched variables are cleared before each call to prevent resolving on stale values.
365
154
 
366
- // Read Clipboard
367
- const clipboardData = await despia('getclipboard://', ['clipboarddata']);
155
+ - Single variable: `null` is treated as a valid resolved value. Other empty placeholders (`undefined`, `"n/a"`, `{}`, `[]`) are ignored.
156
+ - Multiple variables: all variables must be non-null before the Promise resolves.
368
157
 
369
- // Open App Settings
370
- despia("settingsapp://");
158
+ **Direct property access**
371
159
 
372
- // Contact Permissions
373
- despia('requestcontactpermission://');
374
- const contacts = await despia('readcontacts://', ['contacts']);
160
+ ```js
161
+ despia.variableName // Equivalent to window.variableName
162
+ ```
375
163
 
376
- // Background Location
377
- despia('backgroundlocationon://');
378
- // Use native browser geolocation API (not despia native runtime)
379
- const watchId = navigator.geolocation.watchPosition(
380
- (position) => console.log('Location:', position),
381
- (error) => console.error('Location error:', error)
382
- );
383
- // To stop
384
- despia('backgroundlocationoff://');
385
- navigator.geolocation.clearWatch(watchId);
164
+ ### Protocol format
386
165
 
387
- // Push Notifications
388
- despia('registerpush://');
389
- despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
390
- // Set OneSignal external user ID (call on every app load)
391
- despia(`setonesignalplayerid://?user_id=${YOUR_LOGGED_IN_USER_ID}`);
166
+ ```
167
+ feature://action?param1=value1&param2=value2
168
+ ```
392
169
 
393
- // Haptic Feedback
394
- despia('lighthaptic://');
395
- despia('heavyhaptic://');
396
- despia('successhaptic://');
397
- despia('warninghaptic://');
398
- despia('errorhaptic://');
170
+ ---
399
171
 
400
- // App Information
401
- const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
402
- const deviceInfo = await despia('get-uuid://', ['uuid']);
172
+ ## Features
403
173
 
404
- // Screenshots and Scanning
405
- despia('takescreenshot://');
406
- despia('scanningmode://auto');
407
- despia('scanningmode://on');
408
- despia('scanningmode://off');
174
+ ### Haptic Feedback
409
175
 
410
- // Store and Location
411
- const storeData = await despia('getstorelocation://', ['storeLocation']);
176
+ ```js
177
+ despia('lighthaptic://'); // Subtle vibration
178
+ despia('heavyhaptic://'); // Strong vibration
179
+ despia('successhaptic://'); // Positive confirmation
180
+ despia('warninghaptic://'); // Attention alert
181
+ despia('errorhaptic://'); // Negative feedback
182
+ ```
412
183
 
413
- // Image and File Operations
414
- despia('savethisimage://?url=${image_url}');
415
- despia('file://${file_url}');
184
+ ---
416
185
 
417
- // App Control
418
- despia('reset://');
419
- const trackingData = await despia('user-disable-tracking://', ['trackingDisabled']);
186
+ ### Identity Vault
420
187
 
421
- // UI Controls
422
- despia('spinneron://');
423
- despia('spinneroff://');
424
- despia('hidebars://on');
425
- despia('hidebars://off');
188
+ Encrypted key-value storage backed by iCloud KV on iOS and Android App Backup on Android. Persists across uninstalls and reinstalls. Data syncs automatically across all devices sharing the same Apple ID or Google account.
426
189
 
427
- // Sharing
428
- despia('shareapp://message?=${message}&url=${url}');
190
+ Set `locked=true` on any key to require Face ID or Touch ID before the value can be read back. Because the vault stores the actual value server-side and only returns it after biometric success, you can store real JWT tokens, session cookies, and API keys behind biometrics. This is a hardware-enforced security guarantee, not a client-side check that can be bypassed.
429
191
 
430
- // Status Bar Styling
431
- despia('statusbarcolor://{255, 255, 255}');
432
- despia('statusbartextcolor://{black}');
192
+ ```js
193
+ // Store a JWT token (reading it back requires Face ID / Touch ID)
194
+ await despia('setvault://?key=sessionToken&value=abc123&locked=true');
433
195
 
434
- // Biometric Authentication
435
- despia('bioauth://');
196
+ // Read triggers the biometric prompt; token is only returned on success
197
+ const data = await despia('readvault://?key=sessionToken', ['sessionToken']);
198
+ const token = data.sessionToken;
436
199
  ```
437
200
 
438
- ### Direct Window Variable Access
201
+ If the key does not exist, `readvault://` throws. Wrap in try/catch to handle first-time users.
439
202
 
440
- ```javascript
441
- // Access any window variable directly (useful for Despia response data)
442
- const currentUser = despia.currentUser;
443
- const deviceInfo = despia.deviceInfo;
444
- const appVersion = despia.appVersion;
445
- ```
203
+ | Parameter | Description |
204
+ |-----------|-------------|
205
+ | `key` | Storage key, e.g. `"userId"` or `"sessionToken"` |
206
+ | `value` | String value to store |
207
+ | `locked` | `"true"` requires biometrics on read. `"false"` for open access. |
446
208
 
447
- ### Advanced Usage with Variable Watching
209
+ **Store a value without biometric protection**
448
210
 
449
- ```javascript
450
- // Watch multiple response variables
451
- const appData = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
211
+ ```js
212
+ await despia('setvault://?key=userId&value=user123&locked=false');
452
213
 
453
- // Chain multiple Despia commands
454
- despia('lighthaptic://');
455
- const appData2 = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
456
- despia('successhaptic://');
214
+ const { userId } = await despia('readvault://?key=userId', ['userId']);
457
215
  ```
458
216
 
459
- ### Background Location Workflow
460
-
461
- Background location tracking requires a two-step process:
462
-
463
- ```javascript
464
- // Step 1: Enable native background location tracking via Despia
465
- despia('backgroundlocationon://');
217
+ **Protect a sensitive action with Face ID**
466
218
 
467
- // Step 2: Use native browser geolocation API for actual tracking (not despia native runtime)
468
- const watchId = navigator.geolocation.watchPosition(
469
- (position) => {
470
- console.log('Location update:', {
471
- latitude: position.coords.latitude,
472
- longitude: position.coords.longitude,
473
- accuracy: position.coords.accuracy,
474
- timestamp: position.timestamp
475
- });
476
- },
477
- (error) => {
478
- console.error('Location error:', error);
479
- },
480
- {
481
- enableHighAccuracy: true,
482
- timeout: 10000,
483
- maximumAge: 60000
219
+ ```js
220
+ async function confirmWithBiometrics() {
221
+ await despia('setvault://?key=confirm&value=yes&locked=true');
222
+ try {
223
+ const data = await despia('readvault://?key=confirm', ['confirm']);
224
+ if (data.confirm === 'yes') {
225
+ await performSensitiveAction();
226
+ await despia('setvault://?key=confirm&value=&locked=false');
227
+ }
228
+ } catch {
229
+ // User cancelled or biometric failed
484
230
  }
485
- );
486
-
487
- // To stop tracking:
488
- // Step 1: Disable native background tracking via Despia
489
- despia('backgroundlocationoff://');
490
- // Step 2: Clear browser geolocation watch (native API)
491
- navigator.geolocation.clearWatch(watchId);
231
+ }
492
232
  ```
493
233
 
494
- ### Contact Access Workflow
234
+ **Prevent free trial abuse**
495
235
 
496
- ```javascript
497
- // Step 1: Request contact permission
498
- despia('requestcontactpermission://');
499
-
500
- // Step 2: Read contacts after permission granted
501
- const contactData = await despia('readcontacts://', ['contacts']);
502
- console.log('Contacts:', contactData.contacts);
236
+ ```js
237
+ async function checkTrialEligibility() {
238
+ try {
239
+ const data = await despia('readvault://?key=hasUsedTrial', ['hasUsedTrial']);
240
+ return data.hasUsedTrial !== 'yes';
241
+ } catch {
242
+ // Key not found, first-time user
243
+ await despia('setvault://?key=hasUsedTrial&value=yes&locked=false');
244
+ return true;
245
+ }
246
+ }
503
247
  ```
504
248
 
505
- ### RevenueCat Paywall Workflow
506
-
507
- Create a payment system that uses RevenueCat Paywalls by launching native paywall interfaces configured in your RevenueCat dashboard:
249
+ ---
508
250
 
509
- ```javascript
510
- // First, install the package:
511
- // npm install despia-native
251
+ ### GPS Location
512
252
 
513
- // Then import it:
514
- import despia from 'despia-native';
253
+ ```js
254
+ // Set up the live update callback
255
+ window.onLocationChange = (data) => {
256
+ if (!data.active) return;
257
+ console.log(data.latitude, data.longitude, data.horizontalAccuracy);
258
+ };
515
259
 
516
- // Launch a paywall for a specific offering
517
- despia('revenuecat://launchPaywall?external_id=USER_ID&offering=default');
260
+ // Start tracking (buffer in seconds, movement threshold in centimetres)
261
+ despia('location://?buffer=60&movement=100');
518
262
 
519
- // Example offerings you might configure:
520
- // - "default" - your main offering
521
- // - "premium" - premium tier offering
522
- // - "annual_sale" - special promotional offering
263
+ // Stop tracking and retrieve the session
264
+ const { locationSession } = await despia('stoplocation://', ['locationSession']);
523
265
  ```
524
266
 
525
- **Handling Purchase Success:**
267
+ **Server delivery**
526
268
 
527
- The Despia Native Runtime will call the global function `onRevenueCatPurchase()` when an in-app purchase or subscription is successfully made on the client side. Although this should not grant access immediately, it's a good time to start polling your backend to check if the RevenueCat webhook has already updated the user's status or plan permissions.
269
+ When `server` is set, each GPS point is POSTed to your endpoint as it is recorded. Server delivery, `window.onLocationChange`, and local session storage all run simultaneously and independently. Loss of network does not affect local storage or frontend callbacks.
528
270
 
529
- ```javascript
530
- // Define the global callback function
531
- window.onRevenueCatPurchase = function() {
532
- console.log('Purchase successful! Polling backend for status update...');
533
-
534
- // Start polling your backend to check if RevenueCat webhook
535
- // has updated the user's status or plan permissions
536
- // This ensures you don't grant access before the webhook processes
537
- };
271
+ ```js
272
+ despia('location://?server=https://api.example.com/track?user=USER_ID&buffer=30&movement=100');
538
273
  ```
539
274
 
540
- This will launch a native paywall interface configured in your RevenueCat dashboard, handling purchases through Apple App Store and Google Play billing.
275
+ Each POST body matches the location object shape returned by `stoplocation://`.
541
276
 
542
- ### Identity Vault Workflow
277
+ Full docs: https://setup.despia.com/native-features/gps-location
543
278
 
544
- The identity vault provides persistent, cross-device storage with optional biometric protection:
279
+ ---
545
280
 
546
- ```javascript
547
- // First, install the package:
548
- // npm install despia-native
281
+ ### RevenueCat In-App Purchases
549
282
 
550
- // Then import it:
551
- import despia from 'despia-native';
283
+ **Launch a paywall**
552
284
 
553
- // Store identity data
554
- await despia(`setvault://?key=${keyName}&value=${value}&locked=${isLocked}`);
555
-
556
- // Retrieve identity data
557
- const data = await despia(`readvault://?key=${keyName}`, [keyName]);
558
- const value = data[keyName];
285
+ ```js
286
+ despia(`revenuecat://launchPaywall?external_id=${userId}&offering=default`);
559
287
  ```
560
288
 
561
- **Parameters:**
562
-
563
- - **key** - Name for your stored data (use simple names like "userId", "deviceId", "sessionToken")
564
- - **value** - The data to store (text/string)
565
- - **locked** - Set to `'true'` to require Face ID/fingerprint, `'false'` for normal storage
566
-
567
- **Features:**
568
-
569
- - **Persistent storage** - Data survives app restarts, updates, and even uninstall/reinstall
570
- - **Cross-device sync** - Works across all user's devices with the same Apple ID or Google account
571
- - **User tracking** - Identify the same user even after they uninstall and reinstall your app
572
- - **Face ID protection** - Optional biometric lock for sensitive actions
573
- - **Automatic timeout** - 30-second timeout prevents app freezing
289
+ | Parameter | Required | Description |
290
+ |-----------|----------|-------------|
291
+ | `external_id` | Yes | Your user ID in RevenueCat |
292
+ | `offering` | Yes | RevenueCat offering ID. Use `"default"` for your default offering. |
574
293
 
575
- **Perfect for:**
294
+ **Direct purchase without paywall UI**
576
295
 
577
- - Identifying users across sessions
578
- - Preventing free trial abuse (track device even after uninstall)
579
- - Storing login session tokens
580
- - Protecting sensitive actions with Face ID/Touch ID
581
- - Saving user preferences and app settings
296
+ ```js
297
+ // iOS
298
+ despia(`revenuecat://purchase?external_id=${userId}&product=monthly_premium_ios`);
582
299
 
583
- **Example Usage:**
300
+ // Android
301
+ despia(`revenuecat://purchase?external_id=${userId}&product=premium:monthly_premium_android`);
302
+ ```
584
303
 
585
- ```javascript
586
- // Store user ID (normal storage)
587
- await despia(`setvault://?key=userId&value=user123&locked=false`);
304
+ **Handle purchase success**
588
305
 
589
- // Store session token with biometric protection
590
- await despia(`setvault://?key=sessionToken&value=abc123xyz&locked=true`);
306
+ The native runtime calls `window.onRevenueCatPurchase()` after a successful purchase:
591
307
 
592
- // Retrieve stored data
593
- const userData = await despia(`readvault://?key=userId`, ['userId']);
594
- console.log('User ID:', userData.userId);
308
+ ```js
309
+ window.onRevenueCatPurchase = async () => {
310
+ const { restoredData } = await despia('getpurchasehistory://', ['restoredData']);
311
+ const active = (restoredData ?? []).filter(p => p.isActive);
595
312
 
596
- const sessionData = await despia(`readvault://?key=sessionToken`, ['sessionToken']);
597
- console.log('Session Token:', sessionData.sessionToken);
313
+ if (active.some(p => p.entitlementId === 'premium')) unlockPremium();
314
+ if (active.some(p => p.entitlementId === 'no_ads')) removeAds();
315
+ };
598
316
  ```
599
317
 
600
- ### OAuth Authentication Workflow
601
-
602
- Launch OAuth authentication in a secure native browser session:
603
-
604
- ```javascript
605
- // First, install the package:
606
- // npm install despia-native
318
+ **Restore purchases**
607
319
 
608
- // Then import it:
609
- import despia from 'despia-native';
610
-
611
- // Launch OAuth flow
612
- const oauthUrl = 'https://your-provider.com/oauth/authorize?client_id=xxx&redirect_uri=xxx';
613
- despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
320
+ ```js
321
+ const { restoredData } = await despia('getpurchasehistory://', ['restoredData']);
322
+ const hasPremium = restoredData
323
+ .filter(p => p.isActive)
324
+ .some(p => p.entitlementId === 'premium');
614
325
  ```
615
326
 
616
- **How it works:**
327
+ Each purchase object includes `transactionId`, `productId`, `type`, `entitlementId`, `isActive`, `willRenew`, `purchaseDate`, `expirationDate`, `store`, `receipt`, and more. The response shape is normalized across iOS and Android.
617
328
 
618
- 1. **Opens secure browser session:**
619
- - **iOS**: ASWebAuthenticationSession (secure Safari sheet)
620
- - **Android**: Chrome Custom Tabs (secure Chrome overlay)
329
+ ---
621
330
 
622
- 2. **User completes OAuth** in the secure browser session
331
+ ### Push Notifications
623
332
 
624
- 3. **Parse tokens from callback URL** - OAuth providers typically return tokens in the URL hash or query parameters:
625
- ```javascript
626
- // Example: Parse tokens from URL hash (implicit flow)
627
- const hash = window.location.hash.substring(1);
628
- const hashParams = new URLSearchParams(hash);
629
- const accessToken = hashParams.get('access_token');
630
- const refreshToken = hashParams.get('refresh_token');
631
- ```
333
+ ```js
334
+ // Register the device
335
+ despia('registerpush://');
632
336
 
633
- 4. **Close browser and return to app** - Use your app's deeplink scheme with the `oauth/` prefix:
634
- ```javascript
635
- // From your callback page (still in secure browser session)
636
- window.location.href = `myapp://oauth/auth?access_token=${accessToken}&refresh_token=${refreshToken}`;
637
- ```
337
+ // Connect your user ID to this device registration (call on every app load)
338
+ despia(`setonesignalplayerid://?user_id=${userId}`);
638
339
 
639
- 5. **Handle deeplink in app** - The native app intercepts the deeplink, closes the browser session, and navigates your WebView to the specified path with query parameters.
340
+ // Send a local scheduled notification (fires after 60 seconds)
341
+ despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
342
+ ```
640
343
 
641
- **Deeplink format:** `{scheme}://oauth/{path}?params`
344
+ **Send to a specific user from your backend**
642
345
 
643
- - `myapp://` - Your app's deeplink scheme
644
- - `oauth/` - Required prefix that tells native code to close the browser session
645
- - `{path}` - Where to navigate in your app (e.g., `auth`, `home`, `profile`)
646
- - `?params` - Query parameters passed to that page
346
+ ```js
347
+ await fetch('https://onesignal.com/api/v1/notifications', {
348
+ method: 'POST',
349
+ headers: {
350
+ 'Content-Type': 'application/json',
351
+ 'Authorization': 'Basic YOUR_REST_API_KEY',
352
+ },
353
+ body: JSON.stringify({
354
+ app_id: 'ONESIGNAL-APP-ID',
355
+ include_external_user_ids: [externalUserId],
356
+ headings: { en: title },
357
+ contents: { en: message },
358
+ }),
359
+ });
360
+ ```
647
361
 
648
- **Examples:**
649
- - `myapp://oauth/auth?access_token=xxx` - Closes browser, opens `/auth?access_token=xxx`
650
- - `myapp://oauth/home` - Closes browser, opens `/home`
651
- - `myapp://oauth/profile?tab=settings` - Closes browser, opens `/profile?tab=settings`
362
+ When configuring OneSignal, select **Native iOS** and **Native Android** as the platforms, since Despia apps are native mobile applications.
652
363
 
653
- **Perfect for:**
654
- - Google, Facebook, Apple, GitHub, and other OAuth providers
655
- - Secure authentication flows without leaving your app
656
- - Native browser sessions with automatic cleanup
364
+ ---
657
365
 
658
- ### Local Storage Workflow
366
+ ### OAuth Authentication
659
367
 
660
- Create a local storage system with cross-platform support (data is cleared on app uninstall):
368
+ The flow uses two Despia URL protocols. `oauth://` opens a secure browser session (ASWebAuthenticationSession on iOS, Chrome Custom Tabs on Android). The `{scheme}://oauth/` prefix on the return deeplink tells Despia to close that session and navigate your WebView to the path that follows.
661
369
 
662
- ```javascript
663
- // First, install the package:
664
- // npm install despia-native
370
+ When running in Despia, use a native-specific redirect URI pointing to `/native-callback.html` rather than your regular web auth callback. This is a different redirect URI from your web flow — register both with your OAuth provider.
665
371
 
666
- // Then import it:
667
- import despia from 'despia-native';
372
+ ```js
373
+ const isDespia = navigator.userAgent.toLowerCase().includes('despia');
668
374
 
669
- // This SDK is compatible only with the Native Despia Runtime.
670
- // Ensure that the User Agent string includes "despia" before running this code.
671
- // If the User Agent string doesn't include "despia" you can use Local Storage as a web fallback.
375
+ const redirectUri = isDespia
376
+ ? 'https://yourapp.com/native-callback.html'
377
+ : 'https://yourapp.com/auth/callback';
672
378
 
673
- // Save data
674
- const userData = { refresh_token: "SSBMT1ZFIERFU1BJQSBOQVRJVkUgU08gTVVDSCBJIFdBTk5BIEtJU1MgSVQh" };
675
- const encoded = encodeURIComponent(JSON.stringify(userData));
676
- await despia(`writevalue://${encoded}`);
379
+ const oauthUrl = `https://provider.com/oauth/authorize?client_id=xxx&redirect_uri=${encodeURIComponent(redirectUri)}`;
677
380
 
678
- // Retrieve data
679
- const data = await despia("readvalue://", ["storedValues"]);
680
- const userData = JSON.parse(decodeURIComponent(data.storedValues));
681
- console.log(userData.refresh_token);
381
+ if (isDespia) {
382
+ // Step 1: open a secure native browser session
383
+ despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
384
+ } else {
385
+ // Regular web flow
386
+ window.location.href = oauthUrl;
387
+ }
682
388
  ```
683
389
 
684
- **How it works:**
390
+ `/native-callback.html` runs inside the secure browser session. It receives the tokens or authorization code from the provider, handles the exchange if needed, then fires the deeplink to close the session and return to the app:
685
391
 
686
- - **Save data**: Use `writevalue://` with a JSON-encoded string to store data on the device
687
- - **Retrieve data**: Use `readvalue://` with `["storedValues"]` to get the stored data
688
- - **Data format**: Data is stored as a single string and returned in the response object
689
- - **Cross-platform**: Works on both iOS and Android
690
- - **Lifecycle**: Data persists across app restarts but is cleared on app uninstall
691
-
692
- **Important Notes:**
392
+ ```js
393
+ // Step 2: from inside the callback page, fire the deeplink to return to your app
394
+ window.location.href = `{yourscheme}://oauth/auth?access_token=${token}`;
395
+ ```
693
396
 
694
- - **Runtime compatibility**: Only works with Despia Native Runtime (check User Agent for "despia")
695
- - **Web fallback**: Use Local Storage as a fallback if not running in Despia Native Runtime
696
- - **UI blocking**: Refrain from blocking any UI elements or adding loading screens before data is loaded, as most sessions will not have initial data yet if no data has been stored
397
+ Despia intercepts the deeplink, closes the browser session, and navigates your WebView to `/auth?access_token=xxx`.
697
398
 
698
- **Perfect for:**
399
+ Deeplink format: `{yourscheme}://oauth/{path}?params`
699
400
 
700
- - Storing user preferences and settings
701
- - Caching temporary data
702
- - Storing session tokens (non-sensitive)
703
- - App configuration data
401
+ Your deeplink scheme is your app name in lowercase with no spaces (e.g. `myapp://`), or a custom Despialink set in the Despia editor.
704
402
 
705
- ### Restore Purchases Workflow
403
+ | Deeplink | Result |
404
+ |----------|--------|
405
+ | `{yourscheme}://oauth/auth?access_token=xxx` | Browser closes, WebView navigates to `/auth?access_token=xxx` |
406
+ | `{yourscheme}://oauth/home` | Browser closes, WebView navigates to `/home` |
407
+ | `{yourscheme}://auth?access_token=xxx` | Browser stays open, user is stuck |
706
408
 
707
- Retrieve purchase history from the native app stores to implement "Restore Purchases" functionality and verify user entitlements:
409
+ **Callback page**
708
410
 
709
- ```javascript
710
- // First, install the package:
711
- // npm install despia-native
411
+ Use a plain HTML file at `public/native-callback.html` rather than a React or Vue route. React Router can strip the `#access_token` hash fragment during a route change, causing tokens to disappear before your callback logic runs. A plain HTML file bypasses the router entirely and reads the hash directly from the browser.
712
412
 
713
- // Then import it:
714
- import despia from 'despia-native';
413
+ ```html
414
+ <!-- public/native-callback.html -->
415
+ <script>
416
+ var params = new URLSearchParams(window.location.search);
417
+ var hash = new URLSearchParams(window.location.hash.substring(1));
418
+ var code = params.get('code'); // authorization code flow
419
+ var accessToken = hash.get('access_token'); // implicit flow
715
420
 
716
- // Retrieve purchase history
717
- const data = await despia("getpurchasehistory://", ["restoredData"]);
718
- const purchases = data.restoredData;
719
- console.log(purchases);
421
+ if (code) {
422
+ // exchange code via your backend, then fire deeplink with tokens
423
+ } else if (accessToken) {
424
+ window.location.href = '{yourscheme}://oauth/auth?access_token=' + encodeURIComponent(accessToken);
425
+ }
426
+ </script>
720
427
  ```
721
428
 
722
- **How it works:**
723
-
724
- Despia queries the native platform's billing system to retrieve all purchases associated with the current user's App Store or Google Play account. This includes active subscriptions, expired subscriptions, consumables, and non-consumable (lifetime) purchases. The data is normalized into a consistent format across both iOS and Android platforms.
725
-
726
- **Response Structure:**
727
-
728
- Each purchase object in the response array includes:
729
-
730
- - **transactionId** - Unique identifier for this specific transaction
731
- - **originalTransactionId** - Identifier linking to the original purchase (useful for subscription renewals)
732
- - **productId** - The product identifier configured in App Store Connect / Google Play Console
733
- - **type** - Either `"subscription"` or `"product"` (one-time purchase)
734
- - **entitlementId** - The entitlement/access level this purchase grants
735
- - **externalUserId** - External user identifier if configured
736
- - **isAnonymous** - Boolean indicating if the purchase is anonymous
737
- - **isActive** - Boolean indicating if the purchase currently grants access
738
- - **willRenew** - Boolean indicating if a subscription will auto-renew
739
- - **purchaseDate** - ISO timestamp of the most recent transaction
740
- - **originalPurchaseDate** - ISO timestamp of the initial purchase
741
- - **expirationDate** - ISO timestamp when access expires (`null` for lifetime purchases)
742
- - **store** - Either `"app_store"` or `"play_store"`
743
- - **country** - User's country code
744
- - **environment** - `"production"` or `"sandbox"`
745
- - **receipt** - The raw receipt data for server-side validation
746
-
747
- **Example Response (iOS):**
748
-
749
- ```javascript
750
- [
751
- {
752
- "transactionId": "1000000987654321",
753
- "originalTransactionId": "1000000123456789",
754
- "productId": "com.app.premium.monthly",
755
- "type": "subscription",
756
- "entitlementId": "premium",
757
- "externalUserId": "abc123",
758
- "isAnonymous": false,
759
- "isActive": true,
760
- "willRenew": true,
761
- "purchaseDate": "2024-01-15T14:32:05Z",
762
- "originalPurchaseDate": "2023-06-20T09:15:33Z",
763
- "expirationDate": "2024-02-15T14:32:05Z",
764
- "store": "app_store",
765
- "country": "USA",
766
- "receipt": "MIIbngYJKoZIhvcNAQcCoIIbajCCG2YCAQExDzAN...",
767
- "environment": "production"
768
- },
769
- {
770
- "transactionId": "1000000555555555",
771
- "originalTransactionId": "1000000555555555",
772
- "productId": "com.app.removeads",
773
- "type": "product",
774
- "entitlementId": "no_ads",
775
- "externalUserId": "abc123",
776
- "isAnonymous": false,
777
- "isActive": true,
778
- "willRenew": false,
779
- "purchaseDate": "2023-12-01T08:00:00Z",
780
- "originalPurchaseDate": "2023-12-01T08:00:00Z",
781
- "expirationDate": null,
782
- "store": "app_store",
783
- "country": "USA",
784
- "receipt": "MIIbngYJKoZIhvcNAQcCoIIbajCCG2YCAQExDzAN...",
785
- "environment": "production"
786
- }
787
- ]
788
- ```
429
+ **Already-mounted `/auth` page**
789
430
 
790
- **Example Response (Android):**
791
-
792
- ```javascript
793
- [
794
- {
795
- "transactionId": "GPA.3372-4150-9088-12345",
796
- "originalTransactionId": "GPA.3372-4150-9088-12345",
797
- "productId": "com.app.premium.monthly",
798
- "type": "subscription",
799
- "entitlementId": "premium",
800
- "externalUserId": "abc123",
801
- "isAnonymous": false,
802
- "isActive": true,
803
- "willRenew": true,
804
- "purchaseDate": "2024-01-15T14:32:05Z",
805
- "originalPurchaseDate": "2023-06-20T09:15:33Z",
806
- "expirationDate": "2024-02-15T14:32:05Z",
807
- "store": "play_store",
808
- "country": "US",
809
- "receipt": "kefhajglhaljhfajkfajk.AO-J1OxBnT3hAjkl5FjpKc9...",
810
- "environment": "production"
811
- },
812
- {
813
- "transactionId": "GPA.3372-4150-9088-67890",
814
- "originalTransactionId": "GPA.3372-4150-9088-67890",
815
- "productId": "com.app.removeads",
816
- "type": "product",
817
- "entitlementId": "no_ads",
818
- "externalUserId": "abc123",
819
- "isAnonymous": false,
820
- "isActive": true,
821
- "willRenew": false,
822
- "purchaseDate": "2023-12-01T08:00:00Z",
823
- "originalPurchaseDate": "2023-12-01T08:00:00Z",
824
- "expirationDate": null,
825
- "store": "play_store",
826
- "country": "US",
827
- "receipt": "minodkpfokbofclncmaa.AO-J1Oy2fXpTml7rKxE3vNc9...",
828
- "environment": "production"
829
- }
830
- ]
831
- ```
431
+ When Despia navigates the WebView to `/auth`, if that route is already active your framework does not remount the component. Token-reading logic that only runs on mount will not fire again. Fix per framework:
832
432
 
833
- **Check Active Entitlements:**
433
+ - React: include `searchParams` in your `useEffect` dependency array
434
+ - Vue: use `watch: { '$route.query': { immediate: true, handler } }` instead of reading params in `mounted()`
435
+ - Vanilla JS: call your handler on load and add `window.addEventListener('popstate', handler)`
834
436
 
835
- ```javascript
836
- const data = await despia("getpurchasehistory://", ["restoredData"]);
837
- const purchases = data.restoredData;
437
+ Full docs: https://setup.despia.com/native-features/o-auth-2-0
838
438
 
839
- // Filter for active purchases only
840
- const activePurchases = purchases.filter(p => p.isActive);
439
+ ---
841
440
 
842
- // Check if user has premium access
843
- const hasPremium = activePurchases.some(p => p.entitlementId === "premium");
441
+ ### Clipboard
844
442
 
845
- if (hasPremium) {
846
- // Grant premium features
847
- }
443
+ ```js
444
+ const { clipboarddata } = await despia('getclipboard://', ['clipboarddata']);
848
445
  ```
849
446
 
850
- **Perfect for:**
851
-
852
- - Implementing "Restore Purchases" buttons required by App Store guidelines
853
- - Verifying user entitlements on app launch
854
- - Checking subscription status and renewal information
855
- - Server-side receipt validation using raw receipt data
856
- - Cross-platform purchase history retrieval
857
-
858
- **Important Notes:**
447
+ ---
859
448
 
860
- - This feature requires native capabilities which are fully provided by the `despia-native` npm package
861
- - No additional native libraries are needed
862
- - Please follow the installation instructions for the `despia-native` npm package closely
863
- - Implementation as mentioned is critical for App Store compliance
449
+ ### Contacts
864
450
 
865
- ### Read Clipboard Workflow
451
+ ```js
452
+ const { contacts } = await despia('readcontacts://', ['contacts']);
453
+ ```
866
454
 
867
- Read clipboard data from the device:
455
+ ---
868
456
 
869
- ```javascript
870
- // First, install the package:
871
- // npm install despia-native
457
+ ### App Information and Device Data
872
458
 
873
- // Then import it:
874
- import despia from 'despia-native';
459
+ ```js
460
+ const { versionNumber, bundleNumber } = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
461
+ const { uuid } = await despia('get-uuid://', ['uuid']);
462
+ const { storeLocation } = await despia('getstorelocation://', ['storeLocation']);
463
+ const { trackingDisabled } = await despia('user-disable-tracking://', ['trackingDisabled']);
464
+ ```
875
465
 
876
- // Read clipboard data
877
- const clipboardData = await despia('getclipboard://', ['clipboarddata']);
466
+ ---
878
467
 
879
- // Access the clipboard content
880
- const content = clipboardData.clipboarddata;
468
+ ### UI Controls and Styling
881
469
 
882
- // Display or process the clipboard content in your application
883
- console.log('Clipboard content:', content);
470
+ ```js
471
+ despia('spinneron://'); // Show loading spinner
472
+ despia('spinneroff://'); // Hide loading spinner
473
+ despia('hidebars://on'); // Hide status bar (full screen)
474
+ despia('hidebars://off'); // Restore status bar
475
+ despia('statusbarcolor://{255, 255, 255}'); // Set status bar background (RGB)
476
+ despia('statusbartextcolor://{black}'); // Set status bar text color: black | white
477
+ despia('settingsapp://'); // Open native app settings
478
+ despia('reset://'); // Reset the app
884
479
  ```
885
480
 
886
- **How it works:**
481
+ ---
887
482
 
888
- - **Read clipboard**: Use `getclipboard://` with `['clipboarddata']` to retrieve the current clipboard content
889
- - **Access content**: The clipboard content is available through the `.clipboarddata` property of the returned object
890
- - **Native support**: Works on both iOS and Android platforms
483
+ ### File and Media Operations
891
484
 
892
- **Perfect for:**
485
+ ```js
486
+ despia('takescreenshot://');
487
+ despia('savethisimage://?url=https://example.com/image.jpg');
488
+ despia('file://https://example.com/document.pdf');
489
+ despia('shareapp://message?=Check%20out%20this%20app&url=https://myapp.com');
490
+ despia('scanningmode://auto'); // auto | on | off
491
+ ```
893
492
 
894
- - Reading text from the clipboard
895
- - Processing clipboard content in your app
896
- - Implementing paste functionality
897
- - Clipboard content validation
493
+ ---
898
494
 
899
- ### Open App Settings Workflow
495
+ ### Web Storage APIs
900
496
 
901
- Open your app's native settings page where users can manage permissions:
497
+ Despia runs your app on a secure origin (`http://localhost` via Local Server, or your remote URL). This means all standard web storage APIs work without any restrictions or workarounds, unlike other hybrid frameworks that require hacks or fall back to `file://` origins:
902
498
 
903
- ```javascript
904
- // First, install the package:
905
- // npm install despia-native
499
+ ```js
500
+ // localStorage works normally
501
+ localStorage.setItem('userId', 'user123');
502
+ const userId = localStorage.getItem('userId');
906
503
 
907
- // Then import it:
908
- import despia from 'despia-native';
504
+ // IndexedDB works normally
505
+ const db = await indexedDB.open('myapp', 1);
909
506
 
910
- // Open native app settings
911
- despia("settingsapp://");
507
+ // Web Crypto works normally
508
+ const key = await crypto.subtle.generateKey(
509
+ { name: 'AES-GCM', length: 256 },
510
+ true,
511
+ ['encrypt', 'decrypt']
512
+ );
912
513
  ```
913
514
 
914
- **How it works:**
515
+ For data that needs to survive uninstall and reinstall, or be locked behind Face ID, use [Identity Vault](#identity-vault) instead.
915
516
 
916
- - **Opens native settings**: Navigates to your app's settings page in the device's system settings
917
- - **Permission management**: Users can manage permissions like notifications, location, camera, microphone, and more
918
- - **One-time prompts**: Great for directing users to activate features that you can only ask once, like location or push notifications
919
-
920
- **Perfect for:**
517
+ ---
921
518
 
922
- - Directing users to enable location services after they've denied the initial prompt
923
- - Helping users enable push notifications if they initially declined
924
- - Managing camera, microphone, or other permission settings
925
- - Providing a way to access app settings when permission prompts can't be shown again
519
+ ### Local Server
926
520
 
927
- **Example Usage:**
521
+ Most hybrid frameworks approximate offline support with service workers. Service workers are a browser-level cache that intercepts network requests, they are fragile, complex to configure, and cannot truly boot an app without any network activity. Despia takes a different approach entirely.
928
522
 
929
- ```javascript
930
- // Check if location permission is needed
931
- if (!navigator.geolocation) {
932
- // Open settings so user can enable location
933
- despia("settingsapp://");
934
- }
523
+ The Local Server downloads your complete web build to the device and serves it from a native on-device HTTP server at `http://localhost`. There are no service workers involved. The app loads from device storage at native speed, works completely offline from the first launch after hydration, and every web API works because it is running on a real secure origin.
935
524
 
936
- // After user denies push notification permission
937
- // Provide a button to open settings
938
- function openNotificationSettings() {
939
- despia("settingsapp://");
940
- }
525
+ ```bash
526
+ npm install --save-dev @despia/local
941
527
  ```
942
528
 
943
- ### OneSignal Push Notifications Workflow
944
-
945
- Set up OneSignal push notifications with external user IDs to connect your database user IDs with device registrations:
946
-
947
- ```javascript
948
- // First, install the package:
949
- // npm install despia-native
950
-
951
- // Then import it:
952
- import despia from 'despia-native';
529
+ Add the plugin to your build tool to generate the update manifest automatically:
953
530
 
954
- // On every app load, set the external user ID
955
- // This connects your logged-in user ID with the device's OneSignal registration
956
- despia(`setonesignalplayerid://?user_id=${YOUR_LOGGED_IN_USER_ID}`);
957
- ```
531
+ ```js
532
+ // vite.config.js (also available for Webpack, Rollup, Nuxt, SvelteKit, Astro, Remix, esbuild)
533
+ import { defineConfig } from 'vite';
534
+ import { despiaLocalPlugin } from '@despia/local/vite';
958
535
 
959
- **Setup Requirements:**
960
-
961
- 1. **Create a OneSignal account** and configure your app
962
- 2. **Set up iOS (Apple Push Key) and Android (Firebase)** configurations in OneSignal
963
- 3. **Important**: When configuring OneSignal, select **"Native iOS"** and **"Native Android"** platforms since Despia apps are native mobile applications
964
- 4. **Add your OneSignal App ID** to your Despia project settings
965
-
966
- **How it works:**
967
-
968
- - **External User IDs**: External IDs (your database user IDs) are now the default and recommended approach
969
- - **Player IDs**: Player IDs still work but are no longer suggested
970
- - **Device Registration**: Devices are automatically registered in OneSignal when the app is installed
971
- - **User Connection**: Calling `setonesignalplayerid://` on every app load connects your user ID with the device registration
972
-
973
- **Backend Integration Required:**
974
-
975
- You'll need to create a backend endpoint to send notifications using OneSignal's REST API with external user IDs:
976
-
977
- ```javascript
978
- // Backend API endpoint - send to specific user
979
- const sendNotification = async (externalUserId, title, message) => {
980
- const response = await fetch('https://onesignal.com/api/v1/notifications', {
981
- method: 'POST',
982
- headers: {
983
- 'Content-Type': 'application/json',
984
- 'Authorization': 'Basic YOUR_REST_API_KEY'
985
- },
986
- body: JSON.stringify({
987
- app_id: 'ONESIGNAL-APP-ID',
988
- include_external_user_ids: [externalUserId], // Array with single ID
989
- headings: { en: title },
990
- contents: { en: message }
536
+ export default defineConfig({
537
+ plugins: [
538
+ despiaLocalPlugin({
539
+ outDir: 'dist',
540
+ entryHtml: 'index.html'
991
541
  })
992
- });
993
- return response.json();
994
- };
542
+ ]
543
+ });
995
544
  ```
996
545
 
997
- **Example Usage:**
998
-
999
- ```javascript
1000
- // In your app initialization or login flow
1001
- function initializeApp(userId) {
1002
- // Set external user ID on every app load
1003
- despia(`setonesignalplayerid://?user_id=${userId}`);
1004
-
1005
- // Register for push notifications
1006
- despia('registerpush://');
1007
- }
546
+ Or run via CLI after any build:
1008
547
 
1009
- // Send local push notification
1010
- despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
548
+ ```bash
549
+ npx despia-local dist
1011
550
  ```
1012
551
 
1013
- **Important Notes:**
1014
-
1015
- - This feature requires native capabilities which are fully provided by the `despia-native` npm package
1016
- - No additional native libraries are needed
1017
- - Please follow the installation instructions for the `despia-native` npm package closely
1018
- - Implementation as mentioned is critical for proper push notification delivery
1019
- - Call `setonesignalplayerid://` on every app load to ensure the user ID is always connected
1020
-
1021
- **Perfect for:**
1022
-
1023
- - Sending targeted notifications to specific users
1024
- - User-based notification management
1025
- - Cross-device notification delivery
1026
- - Personalized push notification campaigns
1027
-
1028
- ### Haptic Feedback
552
+ This generates `despia/local.json` in your output directory. Despia reads this manifest on startup, compares the `deployed_at` timestamp with the cached value, and downloads a new build in the background only when something has actually changed. The running app is never interrupted. Updates apply on the next launch.
1029
553
 
1030
- All haptic feedback commands have no response - they provide immediate tactile feedback:
1031
-
1032
- ```javascript
1033
- // Basic haptic feedback
1034
- despia('lighthaptic://'); // Light haptic feedback - subtle vibration
1035
- despia('heavyhaptic://'); // Heavy haptic feedback - strong vibration
1036
-
1037
- // Contextual haptic feedback
1038
- despia('successhaptic://'); // Success haptic feedback - positive confirmation
1039
- despia('warninghaptic://'); // Warning haptic feedback - attention alert
1040
- despia('errorhaptic://'); // Error haptic feedback - negative feedback
1041
-
1042
- // Use cases:
1043
- // - Button press feedback (light/heavy)
1044
- // - Success notifications (successhaptic)
1045
- // - Warning alerts (warninghaptic)
1046
- // - Error feedback (errorhaptic)
1047
- // - UI interaction confirmation
554
+ ```json
555
+ {
556
+ "entry": "/index.html",
557
+ "deployed_at": "1737225600000",
558
+ "assets": [
559
+ "/index.html",
560
+ "/assets/app.abc123.css",
561
+ "/assets/app.def456.js"
562
+ ]
563
+ }
1048
564
  ```
1049
565
 
1050
- ### Biometric Authentication
1051
-
1052
- Biometric authentication requires setting up callback functions before running the command:
1053
-
1054
- ```javascript
1055
- // Step 1: Set up the biometric authentication SDK
1056
- if (!document.getElementById("bioauth-sdk")) {
1057
- const script = document.createElement("script")
1058
- script.id = "bioauth-sdk"
1059
- script.type = "text/javascript"
1060
- script.textContent = `
1061
- function onBioAuthSuccess() {
1062
- window.bioauthSuccess()
1063
- }
1064
- function onBioAuthFailure(errorCode, errorMessage) {
1065
- window.bioauthFailure(errorCode, errorMessage)
1066
- }
1067
- function onBioAuthUnavailable() {
1068
- window.bioauthUnavailable()
1069
- }
1070
- `
1071
- document.head.appendChild(script)
1072
- }
566
+ What this means in practice: your app boots in milliseconds from local storage, works indefinitely without any connectivity, and receives UI updates silently in the background with no app store submission required for HTML, CSS, JavaScript, image, or font changes.
1073
567
 
1074
- // Step 2: Define your callback functions
1075
- window.bioauthSuccess = function() {
1076
- if (navigator.userAgent.includes("despia")) {
1077
- console.log("Biometric authentication successful");
1078
- // Handle successful authentication
1079
- // Redirect user, unlock features, etc.
1080
- }
1081
- }
568
+ Full docs: https://setup.despia.com/local-server/introduction
1082
569
 
1083
- window.bioauthFailure = function(errorCode, errorMessage) {
1084
- if (navigator.userAgent.includes("despia")) {
1085
- console.log("Biometric authentication failed:", errorCode, errorMessage);
1086
- // Handle authentication failure
1087
- // Show error message, fallback to password, etc.
1088
- }
1089
- }
1090
570
 
1091
- window.bioauthUnavailable = function() {
1092
- if (navigator.userAgent.includes("despia")) {
1093
- console.log("Biometric authentication unavailable");
1094
- // Handle when biometric auth is not available
1095
- // Fallback to alternative authentication method
1096
- }
1097
- }
571
+ ---
1098
572
 
1099
- // Step 3: Trigger biometric authentication
1100
- despia('bioauth://');
1101
- ```
573
+ ### Local CDN
1102
574
 
1103
- ### App Information & Device Data
575
+ Cache individual remote files on-device for offline playback and background downloads. Downloads use native OS transfer APIs (NSURLSession on iOS, WorkManager on Android) and continue when the app is closed, with automatic retry on network failure. On iOS a Live Activity shows real-time download progress. On Android a native notification appears in the system tray. Both require no setup.
1104
576
 
1105
- ```javascript
1106
- // Get app version information
1107
- const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
1108
- console.log('App Version:', appInfo.versionNumber);
1109
- console.log('Bundle Number:', appInfo.bundleNumber);
577
+ ```js
578
+ // Set up the completion callback before triggering a download
579
+ window.contentServerChange = (item) => {
580
+ console.log('Cached:', item.index, item.local_cdn);
581
+ // item.local_cdn is the localhost URL to use for playback
582
+ };
1110
583
 
1111
- // Get device UUID (native device ID)
1112
- const deviceData = await despia('get-uuid://', ['uuid']);
1113
- console.log('Device UUID:', deviceData.uuid);
584
+ // Fire and forget. Do not await with a watch key; large files outlive the 30s bridge timeout.
585
+ despia(
586
+ `localcdn://write?url=${remoteUrl}&filename=videos/clip.mp4&index=clip_1&push=true&pushmessage="Download complete"`
587
+ );
1114
588
 
1115
- // Get store location
1116
- const storeData = await despia('getstorelocation://', ['storeLocation']);
1117
- console.log('Store Location:', storeData.storeLocation);
589
+ // Read cached items by ID
590
+ const { cdnItems } = await despia(
591
+ `localcdn://read?index=${encodeURIComponent(JSON.stringify(['clip_1']))}`,
592
+ ['cdnItems']
593
+ );
1118
594
 
1119
- // Check tracking permission
1120
- const trackingData = await despia('user-disable-tracking://', ['trackingDisabled']);
1121
- console.log('Tracking Disabled:', trackingData.trackingDisabled);
595
+ // Or query everything in the cache
596
+ const { cdnItems: all } = await despia('localcdn://query', ['cdnItems']);
1122
597
  ```
1123
598
 
1124
- ### UI Controls & Styling
1125
-
1126
- ```javascript
1127
- // Loading spinner controls
1128
- await despia('spinneron://'); // Show loading spinner
1129
- await despia('spinneroff://'); // Hide loading spinner
599
+ Use the `local_cdn` URL for playback:
1130
600
 
1131
- // Full screen mode
1132
- await despia('hidebars://on'); // Hide status bar (full screen)
1133
- await despia('hidebars://off'); // Show status bar
1134
-
1135
- // Status bar styling
1136
- await despia('statusbarcolor://{255, 255, 255}'); // Set status bar background color (RGB)
1137
- await despia('statusbartextcolor://{black}'); // Set status bar text color (black/white)
601
+ ```html
602
+ <video src="http://localhost:7777/localcdn/videos/clip.mp4" controls></video>
1138
603
  ```
1139
604
 
1140
- ### File & Media Operations
1141
-
1142
- ```javascript
1143
- // Take screenshot (saves to device)
1144
- await despia('takescreenshot://');
605
+ **HTTP upload API** (Local Server only)
1145
606
 
1146
- // Save image from URL
1147
- await despia('savethisimage://?url=https://example.com/image.jpg');
607
+ ```js
608
+ const host = window.location.host; // Do not hardcode the port; it rotates per session
609
+ const fd = new FormData();
610
+ fd.append('file', fileInput.files[0]);
1148
611
 
1149
- // Download file from URL
1150
- await despia('file://https://example.com/document.pdf');
1151
-
1152
- // Share app with message and URL
1153
- await despia('shareapp://message?=Check%20out%20this%20app&url=https://myapp.com');
612
+ const res = await fetch(`http://${host}/api/upload`, { method: 'POST', body: fd });
613
+ const data = await res.json();
614
+ // { success: true, fileName: "video.mp4", url: "http://localhost:7777/files/video.mp4" }
1154
615
  ```
1155
616
 
1156
- ### Scanning Mode
1157
-
1158
- ```javascript
1159
- // Control scanning mode
1160
- await despia('scanningmode://auto'); // Auto scanning mode
1161
- await despia('scanningmode://on'); // Enable scanning
1162
- await despia('scanningmode://off'); // Disable scanning
1163
- ```
617
+ | Method | Storage Path | URL Pattern |
618
+ |--------|-------------|-------------|
619
+ | `localcdn://write` | `/localcdn/` | `localhost:{PORT}/localcdn/{filepath}` |
620
+ | `/api/upload` | `/files/` | `localhost:{PORT}/files/{filename}` |
1164
621
 
1165
- ### App Reset
622
+ Full docs: https://setup.despia.com/local-cdn/introduction
1166
623
 
1167
- ```javascript
1168
- // Reset app (use with caution)
1169
- await despia('reset://');
1170
- ```
624
+ ---
1171
625
 
1172
- ### Native Safe Area
626
+ ## Safe Area
1173
627
 
1174
- Access native safe area insets via CSS custom properties:
628
+ Despia exposes top and bottom safe area insets as CSS custom properties set by the native runtime.
1175
629
 
1176
630
  ```css
1177
- /* Use native safe area insets in your CSS */
1178
- .my-element {
631
+ .header {
1179
632
  padding-top: var(--safe-area-top);
633
+ }
634
+
635
+ .footer {
1180
636
  padding-bottom: var(--safe-area-bottom);
1181
637
  }
1182
638
 
1183
- /* Full height with safe area consideration */
1184
639
  .full-height {
1185
640
  height: calc(100vh - var(--safe-area-top) - var(--safe-area-bottom));
1186
641
  }
1187
642
  ```
1188
643
 
1189
- **Note:** Despia only supports top and bottom safe area insets. Left and right safe area variables are not available.
1190
-
1191
- These CSS variables are automatically provided by the Despia native runtime and represent the device's safe area insets (notches, home indicators, etc.).
1192
-
1193
-
1194
-
1195
- ## API Reference
644
+ Note: left and right safe area variables are not available.
1196
645
 
1197
- ### `despia(command, watch?)`
1198
-
1199
- - **command** (string): The Despia protocol command (e.g., `'lighthaptic://'`)
1200
- - **watch** (string[], optional): Array of variable names to watch for in the response
1201
-
1202
- Returns a Promise that resolves when all watched variables are available:
1203
- - **Single variable**: 30-second timeout with observation. `null` values resolve immediately (useful for error/not-found signals). Other empty placeholders (`undefined`, `"n/a"`, `{}`, `[]`) are ignored. Promise always resolves; on timeout it resolves with `undefined`.
1204
- - **Multiple variables**: Uses VariableTracker with 5-minute auto-cleanup. All variables must have non-null values. Any `null` value blocks resolution until all variables have real values.
1205
-
1206
- ### Timeout behavior
1207
-
1208
- When you call `despia(command, ['someVariable'])`, the SDK waits up to 30 seconds for
1209
- `window.someVariable` to be set by the native runtime. If it appears earlier, the
1210
- Promise resolves with that value. If it is never set, the Promise still resolves
1211
- after 30 seconds with `undefined` and a timeout is logged to the console. This
1212
- prevents hanging Promises for long-running or failing native operations.
1213
-
1214
- ### Fresh-data behavior
1215
-
1216
- Before observing, watched variables are cleared to avoid resolving on stale values.
1217
- The observer behavior differs by variable count:
1218
-
1219
- - **Single variable**: `null` is treated as a valid resolution value (useful for error/not-found signals). The observer ignores other empty placeholders (`undefined`, `"n/a"`, `{}`, `[]`) and requires the value to change from its baseline before resolving.
1220
-
1221
- - **Multiple variables**: All variables must have non-null values. The observer ignores empty placeholders (`undefined`, `null`, `"n/a"`, `{}`, `[]`) and requires all values to change from their baseline before resolving.
1222
-
1223
- This ensures each call waits for a fresh write from the native side.
1224
-
1225
- ### Direct Property Access
1226
-
1227
- Access any window variable directly through the despia object:
1228
-
1229
- ```javascript
1230
- despia.variableName // Equivalent to window.variableName
1231
- ```
1232
-
1233
- ## Despia Protocol Format
1234
-
1235
- Despia uses a simple protocol format for all native integrations:
1236
-
1237
- ```
1238
- feature://action?parameters
1239
- ```
1240
-
1241
- Examples:
1242
- - `lighthaptic://`
1243
- - `getappversion://`
1244
- - `revenuecat://purchase?external_id=user_777&product=monthly_premium`
1245
- - `revenuecat://launchPaywall?external_id=user_777&offering=default`
1246
- - `getpurchasehistory://`
1247
- - `getclipboard://`
1248
- - `settingsapp://`
1249
- - `setonesignalplayerid://?user_id=user123`
1250
- - `registerpush://`
1251
- - `setvault://?key=userId&value=user123&locked=false`
1252
- - `readvault://?key=userId`
1253
- - `writevalue://{JSON-ENCODED-STRING}`
1254
- - `readvalue://`
1255
- - `oauth://?url=https://provider.com/oauth/authorize`
1256
- - `requestcontactpermission://`
1257
- - `savethisimage://?url=https://example.com/image.jpg`
1258
-
1259
- ## Available Despia Features
1260
-
1261
- Your app can access these native features:
1262
-
1263
- - **Native Widgets** - Create widgets with SVG and refresh time
1264
- - **In-App Purchases** - RevenueCat integration with external user IDs
1265
- - **Restore Purchases** - Retrieve purchase history from App Store and Google Play
1266
- - **Contact Access** - Request permissions and read contacts
1267
- - **Background Location** - Native tracking with browser geolocation API
1268
- - **Push Notifications** - OneSignal integration with external user IDs and local push messages
1269
- - **Haptic Feedback** - Light, heavy, success, warning, and error feedback
1270
- - **App Information** - Version numbers, bundle numbers, device UUID
1271
- - **Clipboard Access** - Read clipboard content from device
1272
- - **Screenshots** - Take device screenshots
1273
- - **Scanning Mode** - Auto, on, and off scanning controls
1274
- - **Store Location** - Get store location data
1275
- - **File Operations** - Save images and download files
1276
- - **Identity Vault** - Persistent cross-device storage with optional biometric protection
1277
- - **Local Storage** - Cross-platform device storage (cleared on uninstall)
1278
- - **OAuth Authentication** - Secure OAuth flows with native browser sessions
1279
- - **App Control** - Reset app and disable tracking
1280
- - **App Settings** - Open native app settings for permission management
1281
- - **UI Controls** - Loading spinners and full screen mode
1282
- - **Sharing** - Share app with custom messages and URLs
1283
- - **Status Bar** - Control colors and text colors
1284
- - **Biometric Authentication** - Native biometric auth with callbacks
1285
-
1286
- ## TypeScript Support
1287
-
1288
- Full TypeScript definitions are included:
1289
-
1290
- ```typescript
1291
- import despia from 'despia-native';
1292
-
1293
- // Type-safe usage with Despia commands
1294
- const result: { versionNumber: string; bundleNumber: string } = await despia(
1295
- 'getappversion://',
1296
- ['versionNumber', 'bundleNumber']
1297
- );
646
+ ---
1298
647
 
1299
- // Direct property access
1300
- const deviceInfo: any = despia.deviceInfo;
1301
- ```
648
+ ## Web Apps vs React Native
1302
649
 
1303
- ## Integration with Despia
650
+ This SDK is for web apps running inside the Despia runtime: React, Vue, Angular, Svelte, Next.js, Vite, Nuxt, and vanilla JavaScript.
1304
651
 
1305
- Despia operates through a streamlined protocol handler system, allowing you to invoke native features using the global `window.despia` object. This npm package is the JavaScript SDK that makes your web app communicate with Despia's native runtime. The SDK provides:
652
+ It is not for React Native, Expo, or native mobile development.
1306
653
 
1307
- - **Command Queuing** - Sequential execution of Despia commands via `window.despia` setter
1308
- - **Variable Watching** - Async monitoring of response variables
1309
- - **Hybrid Framework Compatible** - Works with Despia's hybrid app framework
1310
- - **Direct Access** - Proxy-based access to window variables
654
+ ---
1311
655
 
1312
- ### How It Works
1313
- Despia's protocol handler system eliminates the need for complex libraries or dependencies, making it compatible across various frameworks and platforms. The SDK uses the setter pattern to execute commands:
656
+ ## Open Source
1314
657
 
1315
- ```javascript
1316
- // When you call:
1317
- despia('lighthaptic://');
658
+ | Package | Description | License |
659
+ |---------|-------------|---------|
660
+ | [despia-native](https://www.npmjs.com/package/despia-native) | JavaScript SDK | MIT |
661
+ | [@despia/local](https://www.npmjs.com/package/@despia/local) | Offline asset bundler | MIT |
662
+ | [despia-version-guard](https://www.npmjs.com/package/despia-version-guard) | OTA version gating | MIT |
1318
663
 
1319
- // It internally executes:
1320
- window.despia = 'lighthaptic://';
1321
- ```
664
+ Native capability implementations are written in Swift and Java and included in full on project export.
1322
665
 
1323
- This streamlined approach triggers Despia's native runtime to handle the native command, providing seamless access to device capabilities directly from your web codebase.
666
+ ---
1324
667
 
1325
668
  ## License
1326
669