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