State & Data

State management (Pinia)

terminal
npm install pinia @vue/devtools-api
npx vastx-native init android   # re-wires the bootstrap now that pinia is present
app/stores/counter.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() { count.value++ }
  return { count, increment }
})

vastx-native init/create detects a pinia dependency and wires createApp(RootFrame).use(createPinia()) into the generated bootstrap automatically. This works because Pinia's core is pure Vue reactivity with no DOM dependency at all, and every DOM/devtools-only code path it has is gated behind a typeof window !== 'undefined' check that's simply always false in NativeScript's runtime.

Not every Nuxt module works this way — most inject their functionality through Nuxt's own Vite/Nitro build hooks, which the native build never runs at all. Pinia works because its actual logic has neither dependency; check the same before adding another module.

Third-party backends (Supabase)

@supabase/supabase-js and its sub-packages (auth/postgrest/storage/functions) are plain JS/TS — no native Android/iOS code at all, so there's no linking step:

terminal
npm install @supabase/supabase-js

Database queries, auth, storage, and edge functions work as-is: every one of them falls back to a bare fetch(...) call if not overridden, and NativeScript's own @nativescript/core/globals already installs a real global.fetch/Headers/Request/Response. The auth client's session storage falls back to in-memory unless you give it a real one:

app/composables/useSupabase.ts
import { createClient } from '@supabase/supabase-js'
import { getString, setString, remove } from '@nativescript/core/application-settings'

const nativeStorage = {
  getItem: (key: string) => getString(key, ''),
  setItem: (key: string, value: string) => setString(key, value),
  removeItem: (key: string) => remove(key)
}

export const supabase = createClient(
  'https://YOUR-PROJECT.supabase.co',
  'YOUR-ANON-KEY',
  { auth: { storage: nativeStorage } }
)

Realtime (supabase.channel(...)) needs one more piece — @supabase/realtime-js requires a global WebSocket constructor, which NativeScript doesn't have. It accepts a custom options.transport, so this is solvable with an adapter wrapping useWebSocket()'s native mechanism — not shipped yet.

MCP client (useMcpClient())

Apps built with vastx-native can connect to MCP servers and call their tools:

<script setup lang="ts">
import { useMcpClient } from 'vastx-native/runtime/composables/useMcpClient.js'

const mcp = useMcpClient()
await mcp.connect('https://your-mcp-server.example.com/mcp')
const tools = await mcp.listTools()
const result = await mcp.callTool('search', { query: 'vastx native' })
</script>

This implements MCP's "Streamable HTTP" transport in its non-streaming mode — the only mode a NativeScript app can actually speak (the runtime has fetch/XMLHttpRequest but no WebSocket and no EventSource). A server that insists on streaming responses gets a clear error, not a silent hang.

Real-time networking (useWebSocket())

<script setup lang="ts">
import { useWebSocket } from 'vastx-native/runtime/composables/useWebSocket'

const ws = useWebSocket('wss://your-server.example.com/socket', {
  onOpen: () => console.log('connected'),
  onMessage: (data) => console.log('got:', data),
  onClose: (code, reason) => console.log('closed', code, reason)
})
ws.send('hello')
</script>

Import this one without a .js suffix, unlike every other composable (a real, confirmed resolution-order interaction between this package's exports field and webpack's platform-extension list).

NativeScript has no global WebSocket, and no maintained NativeScript WebSocket plugin exists on npm. This talks to each platform's real native WebSocket capability directly: Android via OkHttp, iOS via NSURLSessionWebSocketTask (a first-party OS API since iOS 13).

Verification status, honestly: the Android implementation is fully verified on real hardware — real webpack build, real Gradle assemble, real adb install, a real Node.js WebSocket server bridged over USB, live message round-trips confirmed on-device. The iOS implementation is a real, best-effort implementation against Apple's documented API, reviewed the same way, but not yet compiled or run on a device.