despia-native 1.0.0 → 1.0.2

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 (4) hide show
  1. package/README.md +67 -53
  2. package/index.d.ts +4 -6
  3. package/index.js +183 -0
  4. package/package.json +25 -5
package/README.md CHANGED
@@ -27,11 +27,11 @@ npm install despia
27
27
  ```javascript
28
28
  import despia from 'despia';
29
29
 
30
- // Execute a Despia protocol command (no response)
31
- await despia('lighthaptic://');
30
+ // Execute a Despia protocol command (no response needed)
31
+ despia('lighthaptic://');
32
32
 
33
- // Execute command and watch for response variables
34
- const result = await despia('getappversion://', ['versionNumber', 'bundleNumber'], 5000);
33
+ // Execute command and watch for response variables (await needed)
34
+ const result = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
35
35
  console.log(result); // { versionNumber: '1.0.0', bundleNumber: '123' }
36
36
  ```
37
37
 
@@ -39,74 +39,74 @@ console.log(result); // { versionNumber: '1.0.0', bundleNumber: '123' }
39
39
 
40
40
  ```javascript
41
41
  // Native Widgets
42
- await despia('widget://${svg}?refresh=${refresh_time}');
42
+ despia('widget://${svg}?refresh=${refresh_time}');
43
43
 
44
44
  // RevenueCat In-App Purchases
45
- await despia('revenuecat://purchase?external_id=user_777&product=monthly_premium');
45
+ despia('revenuecat://purchase?external_id=user_777&product=monthly_premium');
46
46
 
47
47
  // Contact Permissions
48
- await despia('requestcontactpermission://');
48
+ despia('requestcontactpermission://');
49
49
  const contacts = await despia('readcontacts://', ['contacts']);
50
50
 
51
51
  // Background Location
52
- await despia('backgroundlocationon://');
52
+ despia('backgroundlocationon://');
53
53
  // Use native browser geolocation API (not despia wrapper)
54
54
  const watchId = navigator.geolocation.watchPosition(
55
55
  (position) => console.log('Location:', position),
56
56
  (error) => console.error('Location error:', error)
57
57
  );
58
58
  // To stop
59
- await despia('backgroundlocationoff://');
59
+ despia('backgroundlocationoff://');
60
60
  navigator.geolocation.clearWatch(watchId);
61
61
 
62
62
  // Push Notifications
63
- await despia('registerpush://');
64
- await despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
63
+ despia('registerpush://');
64
+ despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
65
65
  const oneSignalData = await despia('getonesignalplayerid://', ['onesignalplayerid']);
66
66
 
67
67
  // Haptic Feedback
68
- await despia('lighthaptic://');
69
- await despia('heavyhaptic://');
70
- await despia('successhaptic://');
71
- await despia('warninghaptic://');
72
- await despia('errorhaptic://');
68
+ despia('lighthaptic://');
69
+ despia('heavyhaptic://');
70
+ despia('successhaptic://');
71
+ despia('warninghaptic://');
72
+ despia('errorhaptic://');
73
73
 
74
74
  // App Information
75
75
  const appInfo = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
76
76
  const deviceInfo = await despia('get-uuid://', ['uuid']);
77
77
 
78
78
  // Screenshots and Scanning
79
- await despia('takescreenshot://');
80
- await despia('scanningmode://auto');
81
- await despia('scanningmode://on');
82
- await despia('scanningmode://off');
79
+ despia('takescreenshot://');
80
+ despia('scanningmode://auto');
81
+ despia('scanningmode://on');
82
+ despia('scanningmode://off');
83
83
 
84
84
  // Store and Location
85
85
  const storeData = await despia('getstorelocation://', ['storeLocation']);
86
86
 
87
87
  // Image and File Operations
88
- await despia('savethisimage://?url=${image_url}');
89
- await despia('file://${file_url}');
88
+ despia('savethisimage://?url=${image_url}');
89
+ despia('file://${file_url}');
90
90
 
91
91
  // App Control
92
- await despia('reset://');
92
+ despia('reset://');
93
93
  const trackingData = await despia('user-disable-tracking://', ['trackingDisabled']);
94
94
 
95
95
  // UI Controls
96
- await despia('spinneron://');
97
- await despia('spinneroff://');
98
- await despia('hidebars://on');
99
- await despia('hidebars://off');
96
+ despia('spinneron://');
97
+ despia('spinneroff://');
98
+ despia('hidebars://on');
99
+ despia('hidebars://off');
100
100
 
101
101
  // Sharing
102
- await despia('shareapp://message?=${message}&url=${url}');
102
+ despia('shareapp://message?=${message}&url=${url}');
103
103
 
104
104
  // Status Bar Styling
105
- await despia('statusbarcolor://{255, 255, 255}');
106
- await despia('statusbartextcolor://{black}');
105
+ despia('statusbarcolor://{255, 255, 255}');
106
+ despia('statusbartextcolor://{black}');
107
107
 
108
108
  // Biometric Authentication
109
- await despia('bioauth://');
109
+ despia('bioauth://');
110
110
  ```
111
111
 
112
112
  ### Direct Window Variable Access
@@ -121,13 +121,13 @@ const appVersion = despia.appVersion;
121
121
  ### Advanced Usage with Variable Watching
122
122
 
123
123
  ```javascript
124
- // Watch multiple response variables with custom timeout
125
- const appData = await despia('getappversion://', ['versionNumber', 'bundleNumber'], 15000);
124
+ // Watch multiple response variables
125
+ const appData = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
126
126
 
127
127
  // Chain multiple Despia commands
128
- await despia('lighthaptic://');
129
- await despia('getappversion://', ['versionNumber', 'bundleNumber']);
130
- await despia('successhaptic://');
128
+ despia('lighthaptic://');
129
+ const appData2 = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
130
+ despia('successhaptic://');
131
131
  ```
132
132
 
133
133
  ### Background Location Workflow
@@ -136,7 +136,7 @@ Background location tracking requires a two-step process:
136
136
 
137
137
  ```javascript
138
138
  // Step 1: Enable native background location tracking via Despia
139
- await despia('backgroundlocationon://');
139
+ despia('backgroundlocationon://');
140
140
 
141
141
  // Step 2: Use native browser geolocation API for actual tracking (not despia wrapper)
142
142
  const watchId = navigator.geolocation.watchPosition(
@@ -160,7 +160,7 @@ const watchId = navigator.geolocation.watchPosition(
160
160
 
161
161
  // To stop tracking:
162
162
  // Step 1: Disable native background tracking via Despia
163
- await despia('backgroundlocationoff://');
163
+ despia('backgroundlocationoff://');
164
164
  // Step 2: Clear browser geolocation watch (native API)
165
165
  navigator.geolocation.clearWatch(watchId);
166
166
  ```
@@ -169,7 +169,7 @@ navigator.geolocation.clearWatch(watchId);
169
169
 
170
170
  ```javascript
171
171
  // Step 1: Request contact permission
172
- await despia('requestcontactpermission://');
172
+ despia('requestcontactpermission://');
173
173
 
174
174
  // Step 2: Read contacts after permission granted
175
175
  const contactData = await despia('readcontacts://', ['contacts']);
@@ -182,13 +182,13 @@ All haptic feedback commands have no response - they provide immediate tactile f
182
182
 
183
183
  ```javascript
184
184
  // Basic haptic feedback
185
- await despia('lighthaptic://'); // Light haptic feedback - subtle vibration
186
- await despia('heavyhaptic://'); // Heavy haptic feedback - strong vibration
185
+ despia('lighthaptic://'); // Light haptic feedback - subtle vibration
186
+ despia('heavyhaptic://'); // Heavy haptic feedback - strong vibration
187
187
 
188
188
  // Contextual haptic feedback
189
- await despia('successhaptic://'); // Success haptic feedback - positive confirmation
190
- await despia('warninghaptic://'); // Warning haptic feedback - attention alert
191
- await despia('errorhaptic://'); // Error haptic feedback - negative feedback
189
+ despia('successhaptic://'); // Success haptic feedback - positive confirmation
190
+ despia('warninghaptic://'); // Warning haptic feedback - attention alert
191
+ despia('errorhaptic://'); // Error haptic feedback - negative feedback
192
192
 
193
193
  // Use cases:
194
194
  // - Button press feedback (light/heavy)
@@ -248,7 +248,7 @@ window.bioauthUnavailable = function() {
248
248
  }
249
249
 
250
250
  // Step 3: Trigger biometric authentication
251
- await despia('bioauth://');
251
+ despia('bioauth://');
252
252
  ```
253
253
 
254
254
  ### App Information & Device Data
@@ -329,8 +329,6 @@ Access native safe area insets via CSS custom properties:
329
329
  .my-element {
330
330
  padding-top: var(--safe-area-top);
331
331
  padding-bottom: var(--safe-area-bottom);
332
- padding-left: var(--safe-area-left);
333
- padding-right: var(--safe-area-right);
334
332
  }
335
333
 
336
334
  /* Full height with safe area consideration */
@@ -339,19 +337,22 @@ Access native safe area insets via CSS custom properties:
339
337
  }
340
338
  ```
341
339
 
340
+ **Note:** Despia only supports top and bottom safe area insets. Left and right safe area variables are not available.
341
+
342
342
  These CSS variables are automatically provided by the Despia native runtime and represent the device's safe area insets (notches, home indicators, etc.).
343
343
 
344
344
 
345
345
 
346
346
  ## API Reference
347
347
 
348
- ### `despia(command, watch?, timeout?)`
348
+ ### `despia(command, watch?)`
349
349
 
350
- - **command** (string): The Despia protocol command (e.g., `'applinks://open?url=...'`)
350
+ - **command** (string): The Despia protocol command (e.g., `'lighthaptic://'`)
351
351
  - **watch** (string[], optional): Array of variable names to watch for in the response
352
- - **timeout** (number, optional): Timeout in milliseconds (default: 10000)
353
352
 
354
- Returns a Promise that resolves when all watched variables are available or timeout is reached.
353
+ Returns a Promise that resolves when all watched variables are available:
354
+ - **Single variable**: 5-second timeout with simple observation
355
+ - **Multiple variables**: Uses VariableTracker with 5-minute auto-cleanup
355
356
 
356
357
  ### Direct Property Access
357
358
 
@@ -418,11 +419,24 @@ const deviceInfo: any = despia.deviceInfo;
418
419
 
419
420
  This SDK is designed to work with [Despia's native integration system](https://docs.despia.com/docs/native-integrations/getting-started). The SDK provides:
420
421
 
421
- - **Command Queuing** - Sequential execution of Despia commands
422
+ - **Command Queuing** - Sequential execution of Despia commands via `window.despia` setter
422
423
  - **Variable Watching** - Async monitoring of response variables
423
- - **Timeout Handling** - Configurable timeouts for long-running operations
424
+ - **iOS Web View Compatible** - Works with Despia's iOS web view wrapper
424
425
  - **Direct Access** - Proxy-based access to window variables
425
426
 
427
+ ### How It Works
428
+
429
+ The SDK uses the setter pattern to execute commands:
430
+ ```javascript
431
+ // When you call:
432
+ despia('lighthaptic://');
433
+
434
+ // It internally executes:
435
+ window.despia = 'lighthaptic://';
436
+ ```
437
+
438
+ This triggers the iOS web view wrapper's setter function to handle the native command.
439
+
426
440
  ## License
427
441
 
428
442
  MIT
package/index.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  * Despia protocol command types
5
5
  */
6
6
  type DespiaCommand =
7
- | `applinks://${string}`
8
7
  | `widget://${string}`
9
8
  | `revenuecat://${string}`
10
9
  | `requestcontactpermission://`
@@ -70,16 +69,15 @@ interface DespiaFunction {
70
69
  * Execute a Despia protocol command and watch for specific response variables
71
70
  * @param command - The Despia protocol command
72
71
  * @param watch - Array of variable names to watch for in the response
73
- * @param timeout - Timeout in milliseconds (default: 10000)
74
- * @returns Promise that resolves with the watched variables or times out
72
+ * @returns Promise that resolves with the watched variables
75
73
  *
76
74
  * @example
77
75
  * ```typescript
78
- * const result = await despia('biometric://authenticate', ['authResult', 'userID']);
79
- * const purchase = await despia('purchase://product?sku=premium', ['purchaseResult', 'transactionID']);
76
+ * const result = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
77
+ * const purchase = await despia('revenuecat://purchase?external_id=user_777&product=monthly_premium', ['purchaseResult', 'transactionID']);
80
78
  * ```
81
79
  */
82
- <T = Record<string, any>>(command: DespiaCommand, watch: string[], timeout?: number): Promise<T>;
80
+ <T = Record<string, any>>(command: DespiaCommand, watch: string[]): Promise<T>;
83
81
 
84
82
  /**
85
83
  * Access any window variable directly (useful for Despia response data)
package/index.js CHANGED
@@ -0,0 +1,183 @@
1
+ // index.js
2
+ (function (root, factory) {
3
+ if (typeof define === 'function' && define.amd) {
4
+ define([], factory);
5
+ } else if (typeof module === 'object' && module.exports) {
6
+ module.exports = factory();
7
+ } else {
8
+ root.despia = factory();
9
+ }
10
+ }(typeof self !== 'undefined' ? self : this, function () {
11
+ 'use strict';
12
+
13
+ // Command queue for sequential execution
14
+ const commandQueue = [];
15
+ let processing = false;
16
+
17
+ // Process command queue with 1ms delay between commands
18
+ function processQueue() {
19
+ if (processing || commandQueue.length === 0) return;
20
+
21
+ processing = true;
22
+ const { command } = commandQueue.shift();
23
+
24
+ try {
25
+ // Use the setter pattern: window.despia = command
26
+ window.despia = command;
27
+ } catch (e) {
28
+ console.error('Despia command failed:', e);
29
+ }
30
+
31
+ setTimeout(() => {
32
+ processing = false;
33
+ processQueue();
34
+ }, 1);
35
+ }
36
+
37
+ // Add command to queue
38
+ function queueCommand(command) {
39
+ if (typeof window !== 'undefined') {
40
+ commandQueue.push({ command });
41
+ processQueue();
42
+ }
43
+ }
44
+
45
+ // Simple variable observer with 5-second timeout
46
+ function observeDespiaVariable(variableName, callback, timeout = 5000) {
47
+ const startTime = Date.now();
48
+
49
+ function checkVariable() {
50
+ if (window[variableName] !== undefined) {
51
+ callback(window[variableName]);
52
+ } else if (Date.now() - startTime < timeout) {
53
+ setTimeout(checkVariable, 100);
54
+ } else {
55
+ console.error(`Despia timeout: ${variableName} was not set within ${timeout} ms`);
56
+ }
57
+ }
58
+
59
+ checkVariable();
60
+ }
61
+
62
+ // VariableTracker class for complex multi-variable observation
63
+ class VariableTracker {
64
+ constructor(variables, onReady) {
65
+ this.variables = variables;
66
+ this.onReady = onReady;
67
+ this.triggered = false;
68
+ this.processing = false;
69
+
70
+ // Create tracker element
71
+ this.tracker = document.createElement('div');
72
+ this.tracker.style.display = 'none';
73
+ document.body.appendChild(this.tracker);
74
+
75
+ // Setup observer with debounce
76
+ let timeout;
77
+ this.observer = new MutationObserver(() => {
78
+ clearTimeout(timeout);
79
+ timeout = setTimeout(() => this.check(), 100);
80
+ });
81
+
82
+ // Start observing and checking
83
+ this.observer.observe(this.tracker, { attributes: true });
84
+ this.check();
85
+ this.interval = setInterval(() => this.check(), 1000);
86
+ }
87
+
88
+ check() {
89
+ if (this.processing || this.triggered) return;
90
+ this.processing = true;
91
+
92
+ try {
93
+ const values = {};
94
+ const allSet = this.variables.every(name => {
95
+ const val = window[name];
96
+ // Check for undefined, "n/a" string, or null values
97
+ if (val === undefined || val === "n/a" || val === null) return false;
98
+ values[name] = val;
99
+ return true;
100
+ });
101
+
102
+ if (allSet && !this.triggered) {
103
+ this.triggered = true;
104
+ this.cleanup();
105
+ this.onReady(values);
106
+ }
107
+ } catch (err) {
108
+ console.error("Error during check:", err);
109
+ }
110
+
111
+ this.processing = false;
112
+ }
113
+
114
+ cleanup() {
115
+ this.observer.disconnect();
116
+ clearInterval(this.interval);
117
+ this.tracker.remove();
118
+ }
119
+ }
120
+
121
+ // Observe variables - simple for single variable, tracker for multiple
122
+ function observeVariables(variables, timeout) {
123
+ if (!variables || variables.length === 0) {
124
+ return Promise.resolve({});
125
+ }
126
+
127
+ if (variables.length === 1) {
128
+ // Simple single variable observation with 5-second timeout
129
+ return new Promise((resolve) => {
130
+ observeDespiaVariable(variables[0], (value) => {
131
+ resolve({ [variables[0]]: value });
132
+ }, 5000);
133
+ });
134
+ } else {
135
+ // Multiple variables - use VariableTracker (no timeout, expires after 5 minutes)
136
+ return new Promise((resolve) => {
137
+ const tracker = new VariableTracker(variables, resolve);
138
+ // Auto-cleanup after 5 minutes if not triggered
139
+ setTimeout(() => {
140
+ if (!tracker.triggered) {
141
+ tracker.cleanup();
142
+ resolve({});
143
+ }
144
+ }, 300000); // 5 minutes
145
+ });
146
+ }
147
+ }
148
+
149
+ // Main despia function
150
+ function despiaFunction(command, watch = []) {
151
+ // Queue command execution
152
+ queueCommand(command);
153
+
154
+ // No variables to watch
155
+ if (!watch || watch.length === 0) {
156
+ return Promise.resolve();
157
+ }
158
+
159
+ // Watch for variables (timeout handled internally)
160
+ return observeVariables(watch);
161
+ }
162
+
163
+ // Create proxy for window access
164
+ const despia = new Proxy(despiaFunction, {
165
+ get(target, prop) {
166
+ if (prop in target || prop === 'then' || typeof prop === 'symbol') {
167
+ return target[prop];
168
+ }
169
+
170
+ if (typeof window !== 'undefined' && prop in window) {
171
+ return window[prop];
172
+ }
173
+
174
+ return undefined;
175
+ },
176
+
177
+ apply(target, thisArg, args) {
178
+ return target(...args);
179
+ }
180
+ });
181
+
182
+ return despia;
183
+ }));
package/package.json CHANGED
@@ -1,17 +1,37 @@
1
1
  {
2
2
  "name": "despia-native",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "JavaScript SDK for Despia native integrations with command queuing and variable watching",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
7
7
  "types": "index.d.ts",
8
- "keywords": ["despia", "sdk", "mobile", "native", "bridge", "applinks", "biometric", "purchase", "push", "camera", "location", "download", "haptic", "widget", "revenuecat", "contacts"],
8
+ "keywords": [
9
+ "despia",
10
+ "sdk",
11
+ "mobile",
12
+ "native",
13
+ "bridge",
14
+ "applinks",
15
+ "biometric",
16
+ "purchase",
17
+ "push",
18
+ "camera",
19
+ "location",
20
+ "download",
21
+ "haptic",
22
+ "widget",
23
+ "revenuecat",
24
+ "contacts"
25
+ ],
9
26
  "author": "Despia Native <developers@despia.com>",
10
27
  "license": "MIT",
11
-
12
28
  "scripts": {
13
29
  "test": "echo \"No tests specified\"",
14
30
  "prepublishOnly": "npm test"
15
31
  },
16
- "files": ["index.js", "index.d.ts", "README.md"]
17
- }
32
+ "files": [
33
+ "index.js",
34
+ "index.d.ts",
35
+ "README.md"
36
+ ]
37
+ }