@octane-xplat/platform 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +37 -0
  2. package/package.json +21 -8
  3. package/src/a11y.native.ts +9 -5
  4. package/src/a11y.web.ts +13 -9
  5. package/src/app-info.native.ts +35 -0
  6. package/src/app-info.web.ts +10 -0
  7. package/src/biometrics.native.ts +10 -10
  8. package/src/biometrics.web.ts +9 -17
  9. package/src/clipboard.native.ts +28 -15
  10. package/src/clipboard.web.ts +29 -17
  11. package/src/connectivity.native.ts +86 -0
  12. package/src/connectivity.web.ts +62 -0
  13. package/src/deep-links.native.ts +47 -20
  14. package/src/deep-links.web.ts +16 -11
  15. package/src/device.native.ts +3 -3
  16. package/src/device.web.ts +5 -4
  17. package/src/files.native.ts +122 -16
  18. package/src/files.web.ts +20 -19
  19. package/src/geolocation.native.ts +44 -0
  20. package/src/geolocation.web.ts +50 -0
  21. package/src/haptics.native.ts +14 -10
  22. package/src/haptics.web.ts +5 -4
  23. package/src/index.ts +49 -22
  24. package/src/lifecycle.native.ts +1 -1
  25. package/src/lifecycle.native.tsrx +2 -0
  26. package/src/lifecycle.web.ts +1 -1
  27. package/src/lifecycle.web.tsrx +1 -0
  28. package/src/locale.native.ts +3 -3
  29. package/src/locale.web.ts +5 -9
  30. package/src/media.native.ts +74 -14
  31. package/src/media.web.ts +71 -9
  32. package/src/notifications.native.ts +7 -7
  33. package/src/notifications.web.ts +18 -7
  34. package/src/open-url.native.ts +45 -0
  35. package/src/open-url.web.ts +15 -0
  36. package/src/permissions.native.ts +10 -10
  37. package/src/permissions.web.ts +13 -31
  38. package/src/safe-area.native.ts +1 -1
  39. package/src/safe-area.native.tsrx +76 -17
  40. package/src/safe-area.web.ts +1 -1
  41. package/src/safe-area.web.tsrx +3 -0
  42. package/src/screen.native.ts +1 -1
  43. package/src/screen.native.tsrx +1 -0
  44. package/src/screen.web.ts +1 -1
  45. package/src/screen.web.tsrx +1 -0
  46. package/src/secure-storage.native.ts +5 -5
  47. package/src/secure-storage.web.ts +2 -2
  48. package/src/share.native.ts +6 -6
  49. package/src/share.web.ts +17 -13
  50. package/src/storage.native.ts +8 -4
  51. package/src/storage.web.ts +7 -3
  52. package/src/system-bars.native.ts +10 -8
  53. package/src/system-bars.web.ts +9 -6
  54. package/src/types.ts +101 -32
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @octane-xplat/platform
2
+
3
+ > Headless platform capabilities for Octane xplat — one shared API surface,
4
+ > resolved per target (`*.web` under web conditions, `*.native`/`.ios`/
5
+ > `.android` under native).
6
+ >
7
+ > Status: `0.x` — the API surface is still moving.
8
+
9
+ ```sh
10
+ pnpm add @octane-xplat/platform octane
11
+ ```
12
+
13
+ `octane` is a required peer; the `@nativescript/*` peers are optional and
14
+ only needed for native targets.
15
+
16
+ ```ts
17
+ import { storage, permissions } from '@octane-xplat/platform'
18
+
19
+ storage.setString('has-seen-welcome', 'true')
20
+ const seen = storage.getString('has-seen-welcome')
21
+
22
+ const result = await permissions.ensure('camera')
23
+ ```
24
+
25
+ Services cover storage, permissions, share, haptics, media picking, deep
26
+ links, and more. The full capability map lives in the docs:
27
+
28
+ - [Using device features](https://octane-xplat.goddardai.org/platform-services) — guide
29
+ - [Platform-service notes](https://octane-xplat.goddardai.org/notes/platform-notes) — per-capability impl map
30
+
31
+ Rules for consumers:
32
+
33
+ - Import from the package (`'@octane-xplat/platform'` or a submodule like
34
+ `'@octane-xplat/platform/storage'`) — never a leaf impl file.
35
+ - Optional capabilities return `{ supported: boolean }` rather than throwing.
36
+ - This package is headless only. UI-shaped plugins (drawer, menu) are leaf
37
+ primitives in [`@octane-xplat/ui`](https://octane-xplat.goddardai.org/primitives).
package/package.json CHANGED
@@ -1,32 +1,45 @@
1
1
  {
2
2
  "name": "@octane-xplat/platform",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
+ "description": "Headless platform capabilities for Octane xplat — web + NativeScript leaf pairs",
5
+ "files": [
6
+ "src"
7
+ ],
4
8
  "type": "module",
5
9
  "exports": {
6
10
  ".": "./src/index.ts",
7
11
  "./*": "./src/*"
8
12
  },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
9
16
  "dependencies": {
10
17
  "@nativescript/biometrics": "1.3.1",
18
+ "@nativescript/geolocation": "9.0.0",
11
19
  "@nativescript/haptics": "3.1.0",
12
20
  "@nativescript/imagepicker": "5.0.0",
13
21
  "@nativescript/local-notifications": "7.0.0",
22
+ "@nativescript-community/ui-document-picker": "1.1.29",
14
23
  "@nativescript/secure-storage": "4.0.2",
15
24
  "@nativescript/social-share": "2.3.0",
16
25
  "nativescript-clipboard": "2.1.1"
17
26
  },
18
27
  "devDependencies": {
28
+ "@nativescript-community/octane": "0.2.1",
19
29
  "@nativescript/core": "9.1.2",
20
- "octane": "0.4.0"
30
+ "octane": "0.5.0"
21
31
  },
22
32
  "peerDependencies": {
33
+ "@nativescript-community/octane": ">=0.2.1 <1",
34
+ "@nativescript/core": ">=9.1.0 <10",
23
35
  "octane": ">=0.1.51 <1"
24
36
  },
25
- "description": "Headless platform capabilities for Octane xplat — web + NativeScript leaf pairs",
26
- "files": [
27
- "src"
28
- ],
29
- "publishConfig": {
30
- "access": "public"
37
+ "peerDependenciesMeta": {
38
+ "@nativescript-community/octane": {
39
+ "optional": true
40
+ },
41
+ "@nativescript/core": {
42
+ "optional": true
43
+ }
31
44
  }
32
45
  }
@@ -1,14 +1,18 @@
1
1
  // A11y announce — native leaf. UIAccessibility announcement notification on
2
2
  // iOS; View.announceForAccessibility on Android (posted on the content view).
3
- import { Application } from '@nativescript/core';
3
+ import { Application } from '@nativescript/core'
4
4
 
5
5
  export function announce(text: string): void {
6
6
  if (Application.ios) {
7
- UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, text);
8
- return;
7
+ UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, text)
8
+ return
9
9
  }
10
+
10
11
  if (Application.android) {
11
- const view = Application.android.foregroundActivity?.findViewById?.(16908290 /* android.R.id.content */);
12
- view?.announceForAccessibility?.(text);
12
+ const view = Application.android.foregroundActivity?.findViewById?.(
13
+ 16908290 /* android.R.id.content */,
14
+ )
15
+
16
+ view?.announceForAccessibility?.(text)
13
17
  }
14
18
  }
package/src/a11y.web.ts CHANGED
@@ -1,19 +1,23 @@
1
1
  // A11y announce — web leaf. A polite aria-live region receives the text;
2
2
  // screen readers speak the DOM mutation.
3
- let region: HTMLElement | null = null;
3
+ let region: HTMLElement | null = null
4
4
 
5
5
  export function announce(text: string): void {
6
6
  if (!region) {
7
- region = document.createElement('div');
8
- region.setAttribute('aria-live', 'polite');
9
- region.setAttribute('role', 'status');
7
+ region = document.createElement('div')
8
+ region.setAttribute('aria-live', 'polite')
9
+ region.setAttribute('role', 'status')
10
10
  region.style.cssText =
11
- 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);';
12
- document.body.appendChild(region);
11
+ 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);'
12
+
13
+ document.body.appendChild(region)
13
14
  }
14
- region.textContent = '';
15
+
16
+ region.textContent = ''
15
17
  // Two writes in one frame collapse — force the SR to see a change.
16
18
  requestAnimationFrame(() => {
17
- if (region) region.textContent = text;
18
- });
19
+ if (region) {
20
+ region.textContent = text
21
+ }
22
+ })
19
23
  }
@@ -0,0 +1,35 @@
1
+ // App info — native leaf. Read the values from the installed app bundle.
2
+ import { Application } from '@nativescript/core'
3
+ import type { AppInfo } from './types'
4
+
5
+ function value(value: unknown): string | null {
6
+ return value === undefined || value === null ? null : String(value)
7
+ }
8
+
9
+ function read(): AppInfo {
10
+ if (Application.android) {
11
+ const context = Application.android.context
12
+ const packageId = context.getPackageName()
13
+ const packageInfo = context.getPackageManager().getPackageInfo(packageId, 0)
14
+ return {
15
+ supported: true,
16
+ version: value(packageInfo.versionName),
17
+ build: value(packageInfo.versionCode),
18
+ bundleId: packageId,
19
+ }
20
+ }
21
+
22
+ if (Application.ios) {
23
+ const bundle = NSBundle.mainBundle
24
+ return {
25
+ supported: true,
26
+ version: value(bundle.objectForInfoDictionaryKey('CFBundleShortVersionString')),
27
+ build: value(bundle.objectForInfoDictionaryKey('CFBundleVersion')),
28
+ bundleId: value(bundle.bundleIdentifier),
29
+ }
30
+ }
31
+
32
+ return { supported: false, version: null, build: null, bundleId: null }
33
+ }
34
+
35
+ export const appInfo = read()
@@ -0,0 +1,10 @@
1
+ // App info — web leaf. A browser bundle has no trustworthy native version,
2
+ // build, or bundle identifier, so the contract reports that honestly.
3
+ import type { AppInfo } from './types'
4
+
5
+ export const appInfo: AppInfo = {
6
+ supported: false,
7
+ version: null,
8
+ build: null,
9
+ bundleId: null,
10
+ }
@@ -1,24 +1,24 @@
1
1
  // Biometrics — FaceID/TouchID/fingerprint via @nativescript/biometrics.
2
- import { BiometricAuth } from '@nativescript/biometrics';
3
- import type { Capability } from './types';
4
- import type { BiometricsImpl } from './types';
2
+ import { BiometricAuth } from '@nativescript/biometrics'
3
+ import type { Capability } from './types'
4
+ import type { BiometricsImpl } from './types'
5
5
 
6
- const bio = new BiometricAuth();
6
+ const bio = new BiometricAuth()
7
7
 
8
8
  export const biometrics: Capability<BiometricsImpl> = {
9
9
  supported: true,
10
10
  async ensure() {
11
- const r = await bio.available();
12
- return r.any ? 'granted' : 'unsupported';
11
+ const r = await bio.available()
12
+ return r.any ? 'granted' : 'unsupported'
13
13
  },
14
14
  impl: {
15
15
  async verify(reason) {
16
16
  try {
17
- const r = await bio.verifyBiometric({ title: reason });
18
- return r.code === 0;
17
+ const r = await bio.verifyBiometric({ title: reason })
18
+ return r.code === 0
19
19
  } catch {
20
- return false;
20
+ return false
21
21
  }
22
22
  },
23
23
  },
24
- };
24
+ }
@@ -1,21 +1,13 @@
1
- // Biometrics — web leaf. Platform authenticator presence is the honest
2
- // signal; an actual WebAuthn ceremony is an auth-flow concern, not this seam.
3
- import type { BiometricsImpl, Capability } from './types';
4
-
5
- const pkc = () => (globalThis as any).PublicKeyCredential;
1
+ // Biometrics — web leaf. WebAuthn is an authentication ceremony, not a
2
+ // standalone local-presence API: it requires an RP challenge and a registered
3
+ // credential, and may require a user gesture. Do not claim it implements the
4
+ // native verify(reason) contract by creating an ephemeral passkey.
5
+ import type { BiometricsImpl, Capability } from './types'
6
6
 
7
7
  export const biometrics: Capability<BiometricsImpl> = {
8
- supported: typeof pkc() !== 'undefined',
8
+ supported: false,
9
9
  async ensure() {
10
- if (!pkc()) return 'unsupported';
11
- const ok = await pkc().isUserVerifyingPlatformAuthenticatorAvailable();
12
- return ok ? 'granted' : 'unsupported';
13
- },
14
- impl: {
15
- // A bare verify() has no ceremony target — WebAuthn needs a challenge.
16
- // Report presence; wire real attestation when an auth flow exists.
17
- async verify(_reason: string) {
18
- return false;
19
- },
10
+ return 'unsupported'
20
11
  },
21
- };
12
+ impl: null,
13
+ }
@@ -1,17 +1,30 @@
1
- // Clipboard — pasteboard via nativescript-clipboard (sync API wrapped async
2
- // to keep the shared contract awaitable on both sides).
3
- import { getTextSync, setTextSync } from 'nativescript-clipboard';
1
+ // Core provides native clipboard writes. The plugin remains for reading text,
2
+ // which Core does not expose.
3
+ import { Utils } from '@nativescript/core'
4
+ import { getTextSync } from 'nativescript-clipboard'
5
+
6
+ async function writeText(text: string): Promise<boolean> {
7
+ try {
8
+ Utils.copyToClipboard(text)
9
+ return true
10
+ } catch {
11
+ return false
12
+ }
13
+ }
14
+
15
+ async function readText(): Promise<string | null> {
16
+ try {
17
+ return getTextSync()
18
+ } catch {
19
+ return null
20
+ }
21
+ }
4
22
 
5
23
  export const clipboard = {
6
- async write(text: string): Promise<boolean> {
7
- setTextSync(text);
8
- return true;
9
- },
10
- async read(): Promise<string | null> {
11
- try {
12
- return getTextSync();
13
- } catch {
14
- return null;
15
- }
16
- },
17
- };
24
+ canCopy: true,
25
+ writeText,
26
+ readText,
27
+ // Keep the original names available for existing consumers.
28
+ write: writeText,
29
+ read: readText,
30
+ }
@@ -1,18 +1,30 @@
1
- // Clipboard — async permission-gated on web; read may be denied silently.
1
+ // Clipboard — browser support is feature-detected; writes can still be denied
2
+ // by browser policy or permission state.
3
+ const canCopy =
4
+ typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'
5
+
6
+ async function writeText(text: string): Promise<boolean> {
7
+ try {
8
+ await navigator.clipboard.writeText(text)
9
+ return true
10
+ } catch {
11
+ return false
12
+ }
13
+ }
14
+
15
+ async function readText(): Promise<string | null> {
16
+ try {
17
+ return await navigator.clipboard.readText()
18
+ } catch {
19
+ return null
20
+ }
21
+ }
22
+
2
23
  export const clipboard = {
3
- async write(text: string): Promise<boolean> {
4
- try {
5
- await navigator.clipboard.writeText(text);
6
- return true;
7
- } catch {
8
- return false;
9
- }
10
- },
11
- async read(): Promise<string | null> {
12
- try {
13
- return await navigator.clipboard.readText();
14
- } catch {
15
- return null;
16
- }
17
- },
18
- };
24
+ canCopy,
25
+ writeText,
26
+ readText,
27
+ // Keep the original names available for existing consumers.
28
+ write: writeText,
29
+ read: readText,
30
+ }
@@ -0,0 +1,86 @@
1
+ // Connectivity — native leaf. NativeScript exposes this through @nativescript/core;
2
+ // it is a built-in service rather than a separate @nativescript/connectivity plugin.
3
+ import { Connectivity } from '@nativescript/core'
4
+ import type { ConnectionType, ConnectivityImpl, ConnectivityState } from './types'
5
+
6
+ const listeners = new Set<(state: ConnectivityState) => void>()
7
+ let monitoring = false
8
+
9
+ function connectionType(value = Connectivity.getConnectionType()): ConnectionType {
10
+ const types = Connectivity.connectionType as any
11
+ if (value === types.none) {
12
+ return 'none'
13
+ }
14
+
15
+ if (value === types.wifi) {
16
+ return 'wifi'
17
+ }
18
+
19
+ if (value === types.mobile) {
20
+ return 'mobile'
21
+ }
22
+
23
+ if (value === types.ethernet) {
24
+ return 'ethernet'
25
+ }
26
+
27
+ if (value === types.bluetooth) {
28
+ return 'bluetooth'
29
+ }
30
+
31
+ if (value === types.vpn) {
32
+ return 'vpn'
33
+ }
34
+
35
+ return 'unknown'
36
+ }
37
+
38
+ function read(): ConnectivityState {
39
+ const type = connectionType()
40
+ return { online: type !== 'none', type }
41
+ }
42
+
43
+ function notify(value: number) {
44
+ if (listeners.size === 0) {
45
+ return
46
+ }
47
+
48
+ const type = connectionType(value)
49
+ const state = { online: type !== 'none', type }
50
+ for (const listener of listeners) {
51
+ listener(state)
52
+ }
53
+ }
54
+
55
+ function startMonitoring() {
56
+ if (monitoring) {
57
+ return
58
+ }
59
+
60
+ Connectivity.startMonitoring(notify)
61
+ monitoring = true
62
+ }
63
+
64
+ function stopMonitoring() {
65
+ if (!monitoring) {
66
+ return
67
+ }
68
+
69
+ Connectivity.stopMonitoring()
70
+ monitoring = false
71
+ }
72
+
73
+ export const connectivity: ConnectivityImpl = {
74
+ getState: read,
75
+ subscribe(listener) {
76
+ listeners.add(listener)
77
+ listener(read())
78
+ startMonitoring()
79
+ return () => {
80
+ listeners.delete(listener)
81
+ if (listeners.size === 0) {
82
+ stopMonitoring()
83
+ }
84
+ }
85
+ },
86
+ }
@@ -0,0 +1,62 @@
1
+ // Connectivity — web leaf. Online state comes from navigator.onLine and
2
+ // changes are delivered through the browser's online/offline events.
3
+ import type { ConnectionType, ConnectivityImpl, ConnectivityState } from './types'
4
+
5
+ const listeners = new Set<(state: ConnectivityState) => void>()
6
+
7
+ function connectionType(): ConnectionType {
8
+ const type = (navigator as Navigator & { connection?: { type?: string } }).connection?.type
9
+ switch (type) {
10
+ case 'wifi':
11
+ return 'wifi'
12
+ case 'cellular':
13
+ return 'mobile'
14
+ case 'ethernet':
15
+ return 'ethernet'
16
+ case 'bluetooth':
17
+ return 'bluetooth'
18
+ case 'vpn':
19
+ return 'vpn'
20
+ default:
21
+ return navigator.onLine ? 'unknown' : 'none'
22
+ }
23
+ }
24
+
25
+ function read(): ConnectivityState {
26
+ return { online: navigator.onLine, type: connectionType() }
27
+ }
28
+
29
+ function notify() {
30
+ const state = read()
31
+ for (const listener of listeners) {
32
+ listener(state)
33
+ }
34
+ }
35
+
36
+ function addListeners() {
37
+ window.addEventListener('online', notify)
38
+ window.addEventListener('offline', notify)
39
+ }
40
+
41
+ function removeListeners() {
42
+ window.removeEventListener('online', notify)
43
+ window.removeEventListener('offline', notify)
44
+ }
45
+
46
+ export const connectivity: ConnectivityImpl = {
47
+ getState: read,
48
+ subscribe(listener) {
49
+ if (listeners.size === 0) {
50
+ addListeners()
51
+ }
52
+
53
+ listeners.add(listener)
54
+ listener(read())
55
+ return () => {
56
+ listeners.delete(listener)
57
+ if (listeners.size === 0) {
58
+ removeListeners()
59
+ }
60
+ }
61
+ },
62
+ }
@@ -1,39 +1,66 @@
1
1
  // Deep links — native leaf. iOS delivers openUrl + launchOptions; Android
2
2
  // delivers an intent on resume/newIntent. Handlers get the raw URL string —
3
3
  // the app maps it onto its route table.
4
- import { Application } from '@nativescript/core';
4
+ import { Application } from '@nativescript/core'
5
5
 
6
- type LinkHandler = (url: string) => void;
7
- const handlers = new Set<LinkHandler>();
8
- let wired = false;
9
- let initial: string | null = null;
6
+ type LinkHandler = (url: string) => void
7
+ const handlers = new Set<LinkHandler>()
8
+ let wired = false
9
+ let initial: string | null = null
10
+ let lastUrl: string | null = null
11
+
12
+ function dispatch(url: string | null | undefined) {
13
+ if (!url || url === lastUrl) {
14
+ return
15
+ }
16
+
17
+ lastUrl = url
18
+ for (const handler of handlers) {
19
+ handler(url)
20
+ }
21
+ }
10
22
 
11
23
  function wire() {
12
- if (wired) return;
13
- wired = true;
24
+ if (wired) {
25
+ return
26
+ }
27
+
28
+ wired = true
29
+ Application.on(Application.launchEvent, (args: any) => {
30
+ const androidUrl = args.android?.getDataString?.()
31
+ const iosUrl = args.ios?.objectForKey?.('UIApplicationLaunchOptionsURLKey')?.absoluteString
32
+ initial = androidUrl || iosUrl || initial
33
+ dispatch(initial)
34
+ })
35
+
14
36
  if (Application.ios) {
15
37
  Application.on('openUrl', (args: any) => {
16
- const url = args.url?.absoluteString ?? String(args.url ?? '');
17
- for (const h of handlers) h(url);
18
- });
38
+ const url = args.url?.absoluteString ?? String(args.url ?? '')
39
+ dispatch(url)
40
+ })
41
+
42
+ Application.on('continueActivity', (args: any) => {
43
+ dispatch(args.activity?.webpageURL?.absoluteString)
44
+ })
19
45
  }
46
+
20
47
  if (Application.android) {
21
48
  Application.on(Application.resumeEvent, () => {
22
- const intent = Application.android.foregroundActivity?.getIntent?.();
23
- const url = intent?.getDataString?.();
24
- if (url) for (const h of handlers) h(url);
25
- });
49
+ const intent = Application.android.foregroundActivity?.getIntent?.()
50
+ const url = intent?.getDataString?.()
51
+ dispatch(url)
52
+ })
26
53
  }
27
54
  }
28
55
 
29
56
  export function onDeepLink(cb: LinkHandler): () => void {
30
- wire();
31
- handlers.add(cb);
32
- return () => handlers.delete(cb);
57
+ wire()
58
+ handlers.add(cb)
59
+ return () => handlers.delete(cb)
33
60
  }
34
61
 
35
62
  export function consumeInitialUrl(): string | null {
36
- const u = initial;
37
- initial = null;
38
- return u;
63
+ const u = initial
64
+ initial = null
65
+ return u
39
66
  }
@@ -1,23 +1,28 @@
1
1
  // Deep links — web leaf. The URL IS the link: consumeInitialUrl returns the
2
2
  // current path; onDeepLink listens for popstate (back/forward = link events).
3
- type LinkHandler = (url: string) => void;
4
- const handlers = new Set<LinkHandler>();
5
- let wired = false;
3
+ type LinkHandler = (url: string) => void
4
+ const handlers = new Set<LinkHandler>()
5
+ let wired = false
6
6
 
7
7
  function wire() {
8
- if (wired) return;
9
- wired = true;
8
+ if (wired) {
9
+ return
10
+ }
11
+
12
+ wired = true
10
13
  window.addEventListener('popstate', () => {
11
- for (const h of handlers) h(location.pathname + location.search);
12
- });
14
+ for (const h of handlers) {
15
+ h(location.pathname + location.search)
16
+ }
17
+ })
13
18
  }
14
19
 
15
20
  export function onDeepLink(cb: LinkHandler): () => void {
16
- wire();
17
- handlers.add(cb);
18
- return () => handlers.delete(cb);
21
+ wire()
22
+ handlers.add(cb)
23
+ return () => handlers.delete(cb)
19
24
  }
20
25
 
21
26
  export function consumeInitialUrl(): string | null {
22
- return location.pathname === '/' ? null : location.pathname + location.search;
27
+ return location.pathname === '/' ? null : location.pathname + location.search
23
28
  }
@@ -1,5 +1,5 @@
1
- import { Device } from '@nativescript/core';
2
- import type { DeviceInfo } from './types';
1
+ import { Device } from '@nativescript/core'
2
+ import type { DeviceInfo } from './types'
3
3
 
4
4
  export const device: DeviceInfo = {
5
5
  os: Device.os.toLowerCase() === 'ios' ? 'ios' : 'android',
@@ -8,4 +8,4 @@ export const device: DeviceInfo = {
8
8
  manufacturer: Device.manufacturer,
9
9
  language: Device.language.split('-')[0] ?? '',
10
10
  region: Device.region,
11
- };
11
+ }
package/src/device.web.ts CHANGED
@@ -1,7 +1,8 @@
1
- import type { DeviceInfo } from './types';
1
+ import type { DeviceInfo } from './types'
2
2
 
3
- const ua = navigator.userAgent;
4
- const osVersion = ua.match(/(?:Mac OS X|Windows NT|Android|OS) ([\d._]+)/)?.[1]?.replace(/_/g, '.') ?? '';
3
+ const ua = navigator.userAgent
4
+ const osVersion =
5
+ ua.match(/(?:Mac OS X|Windows NT|Android|OS) ([\d._]+)/)?.[1]?.replace(/_/g, '.') ?? ''
5
6
 
6
7
  export const device: DeviceInfo = {
7
8
  os: 'web',
@@ -10,4 +11,4 @@ export const device: DeviceInfo = {
10
11
  manufacturer: '',
11
12
  language: navigator.language.split('-')[0] ?? '',
12
13
  region: navigator.language.split('-')[1] ?? '',
13
- };
14
+ }