@symbiote-native/network 0.2.0 → 1.0.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.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Port of [`expo-network`](https://docs.expo.dev/versions/latest/sdk/network/) for
4
4
  [SymbioteNative](../../README.md) — network connection state (type/connected/internet-reachable)
5
5
  with a change listener, device IP address, and airplane-mode detection, reachable from every
6
- adapter (React, Vue, Angular), not just React.
6
+ adapter (React, Vue, Svelte, Solid, Angular), not just React.
7
7
 
8
8
  Built the same way as [`@symbiote-native/battery`](../battery) and
9
9
  [`@symbiote-native/local-auth`](../local-auth), an `expo-modules-core`-based wrapper (see the
@@ -31,12 +31,12 @@ into the native host app **once**, covering this package and every other `expo-m
31
31
  package (`@symbiote-native/battery`, `@symbiote-native/sensors`, `@symbiote-native/local-auth`)
32
32
  with zero further changes:
33
33
 
34
- | Platform | Touches |
35
- |---|---|
36
- | iOS | `ios/Podfile` — add `use_expo_modules!` |
37
- | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
38
- | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
39
- | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
34
+ | Platform | Touches |
35
+ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
36
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
37
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
38
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
39
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
40
40
 
41
41
  Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
42
42
  peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
@@ -56,37 +56,50 @@ src/core/ network.ts — get*Async functions + addNetworkStateList
56
56
  NetworkStateEvent, hand-ported from Network.types.ts.
57
57
  src/react/hooks/ @symbiote-native/network/react — useNetworkState
58
58
  src/vue/composables/ @symbiote-native/network/vue — useNetworkState (same name)
59
+ src/svelte/runes/ @symbiote-native/network/svelte — useNetworkState (same name)
60
+ src/solid/primitives/ @symbiote-native/network/solid — createNetworkState (returns an
61
+ Accessor; Solid reserves `use*` for consuming existing state)
59
62
  src/angular/services/ @symbiote-native/network/angular — NetworkStateService (`.connect()`
60
63
  returns a Signal)
61
64
  ```
62
65
 
63
- Each adapter's hook/composable/service is a thin lifecycle wrapper (seed from the one-shot
64
- `getNetworkStateAsync()` call, subscribe to `addNetworkStateListener`, unsubscribe on unmount) over
65
- the same `core` functions — the subscription logic is written once and shared by all three.
66
+ Each adapter's hook/composable/rune/primitive/service is a thin lifecycle wrapper (seed from the
67
+ one-shot `getNetworkStateAsync()` call, subscribe to `addNetworkStateListener`, unsubscribe on
68
+ unmount) over the same `core` functions — the subscription logic is written once and shared by all
69
+ of them.
66
70
 
67
71
  ## Use it
68
72
 
69
73
  ```tsx
70
74
  // React — examples/expo-react/screens/NetworkScreen.tsx
71
75
  import { useEffect, useState } from 'react';
72
- import { getIpAddressAsync, isAirplaneModeEnabledAsync } from '@symbiote-native/network';
76
+ import {
77
+ getIpAddressAsync,
78
+ isAirplaneModeEnabledAsync,
79
+ } from '@symbiote-native/network';
73
80
  import { useNetworkState } from '@symbiote-native/network/react';
74
81
 
75
82
  function NetworkScreen() {
76
- const networkState = useNetworkState(); // { type, isConnected, isInternetReachable }
83
+ const networkState = useNetworkState(); // { type, isConnected, isInternetReachable }
77
84
  const [ipAddress, setIpAddress] = useState<string | null>(null);
78
85
  const [isAirplaneMode, setIsAirplaneMode] = useState<boolean | null>(null);
79
86
 
80
87
  useEffect(() => {
81
- Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip, airplaneMode]) => {
82
- setIpAddress(ip);
83
- setIsAirplaneMode(airplaneMode);
84
- });
88
+ Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(
89
+ ([ip, airplaneMode]) => {
90
+ setIpAddress(ip);
91
+ setIsAirplaneMode(airplaneMode);
92
+ },
93
+ );
85
94
  }, [networkState]);
86
95
 
87
96
  return (
88
97
  <>
89
- <Text>{networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline'}</Text>
98
+ <Text>
99
+ {networkState.isConnected
100
+ ? `Connected via ${networkState.type}`
101
+ : 'Offline'}
102
+ </Text>
90
103
  <Text>{ipAddress ?? 'checking…'}</Text>
91
104
  <Text>{isAirplaneMode ? 'Airplane mode: On' : 'Airplane mode: Off'}</Text>
92
105
  </>
@@ -99,30 +112,68 @@ function NetworkScreen() {
99
112
  <script setup lang="ts">
100
113
  import { ref, watch } from 'vue';
101
114
  import { Text } from '@symbiote-native/vue';
102
- import { getIpAddressAsync, isAirplaneModeEnabledAsync } from '@symbiote-native/network';
115
+ import {
116
+ getIpAddressAsync,
117
+ isAirplaneModeEnabledAsync,
118
+ } from '@symbiote-native/network';
103
119
  import { useNetworkState } from '@symbiote-native/network/vue';
104
120
 
105
- const networkState = useNetworkState(); // Ref<NetworkState>
121
+ const networkState = useNetworkState(); // Ref<NetworkState>
106
122
  const ipAddress = ref<string | null>(null);
107
123
  const isAirplaneMode = ref<boolean | null>(null);
108
124
 
109
125
  watch(
110
126
  networkState,
111
127
  () => {
112
- void Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip, airplaneMode]) => {
113
- ipAddress.value = ip;
114
- isAirplaneMode.value = airplaneMode;
115
- });
128
+ void Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(
129
+ ([ip, airplaneMode]) => {
130
+ ipAddress.value = ip;
131
+ isAirplaneMode.value = airplaneMode;
132
+ },
133
+ );
116
134
  },
117
135
  { immediate: true },
118
136
  );
119
137
  </script>
120
138
  <template>
121
- <Text>{{ networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline' }}</Text>
139
+ <Text>{{
140
+ networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline'
141
+ }}</Text>
122
142
  <Text>{{ ipAddress ?? 'checking…' }}</Text>
123
143
  </template>
124
144
  ```
125
145
 
146
+ ```svelte
147
+ <!-- Svelte -->
148
+ <script lang="ts">
149
+ import {
150
+ getIpAddressAsync,
151
+ isAirplaneModeEnabledAsync,
152
+ } from '@symbiote-native/network';
153
+ import { useNetworkState } from '@symbiote-native/network/svelte';
154
+
155
+ const networkState = useNetworkState(); // { readonly current: NetworkState }
156
+ let ipAddress = $state<string | null>(null);
157
+ let isAirplaneMode = $state<boolean | null>(null);
158
+
159
+ $effect(() => {
160
+ networkState.current; // establishes the dependency, so this re-runs on network change
161
+ void Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(
162
+ ([ip, airplaneMode]) => {
163
+ ipAddress = ip;
164
+ isAirplaneMode = airplaneMode;
165
+ },
166
+ );
167
+ });
168
+ </script>
169
+ <Text>
170
+ {networkState.current.isConnected
171
+ ? `Connected via ${networkState.current.type}`
172
+ : 'Offline'}
173
+ </Text>
174
+ <Text>{ipAddress ?? 'checking…'}</Text>
175
+ ```
176
+
126
177
  ```ts
127
178
  // Angular — examples/expo-angular/src/screens/NetworkScreen.ts
128
179
  import { Component, effect, inject, signal } from '@angular/core';
@@ -145,15 +196,59 @@ export class NetworkScreen {
145
196
  constructor() {
146
197
  effect(() => {
147
198
  this.networkState();
148
- Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip]) => {
149
- this.ipAddress.set(ip);
150
- });
199
+ Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(
200
+ ([ip]) => {
201
+ this.ipAddress.set(ip);
202
+ },
203
+ );
151
204
  });
152
205
  }
153
206
  }
154
207
  ```
155
208
 
156
- Each of the three demo screens above also re-fetches the IP address and airplane-mode card
209
+ ```tsx
210
+ // Solid — the accessor is CALLED; a Solid component body runs once, so a snapshot would freeze.
211
+ import { createSignal, onMount } from 'solid-js';
212
+ import { Text } from '@symbiote-native/solid';
213
+ import {
214
+ getIpAddressAsync,
215
+ isAirplaneModeEnabledAsync,
216
+ } from '@symbiote-native/network';
217
+ import { createNetworkState } from '@symbiote-native/network/solid';
218
+
219
+ function NetworkScreen() {
220
+ const networkState = createNetworkState(); // Accessor<NetworkState>
221
+ const [ipAddress, setIpAddress] = createSignal<string | null>(null);
222
+ const [isAirplaneMode, setIsAirplaneMode] = createSignal<boolean | null>(
223
+ null,
224
+ );
225
+
226
+ onMount(() => {
227
+ void Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(
228
+ ([ip, airplaneMode]) => {
229
+ setIpAddress(ip);
230
+ setIsAirplaneMode(airplaneMode);
231
+ },
232
+ );
233
+ });
234
+
235
+ return (
236
+ <>
237
+ <Text>
238
+ {networkState().isConnected
239
+ ? `Connected via ${networkState().type}`
240
+ : 'Offline'}
241
+ </Text>
242
+ <Text>{ipAddress() ?? 'checking…'}</Text>
243
+ <Text>
244
+ {isAirplaneMode() ? 'Airplane mode: On' : 'Airplane mode: Off'}
245
+ </Text>
246
+ </>
247
+ );
248
+ }
249
+ ```
250
+
251
+ Each of the demo screens above also re-fetches the IP address and airplane-mode card
157
252
  whenever the live network state changes (toggling Wi-Fi/airplane mode on the device updates both
158
253
  cards together) — see the linked files for the full version, including the connection-type label
159
254
  switch and the layout around it.
@@ -174,29 +269,37 @@ Plus `NetworkStateType` (enum: `NONE`/`UNKNOWN`/`CELLULAR`/`WIFI`/`BLUETOOTH`/`E
174
269
  `VPN`/`OTHER`), `NetworkState`, `NetworkStateEvent` — ported from upstream's `Network.types.ts`.
175
270
 
176
271
  ```ts
177
- import { getNetworkStateAsync, addNetworkStateListener } from '@symbiote-native/network';
272
+ import {
273
+ getNetworkStateAsync,
274
+ addNetworkStateListener,
275
+ } from '@symbiote-native/network';
178
276
 
179
277
  // framework-scoped entry points re-export the same free functions, plus a lifecycle
180
278
  // hook/composable/service:
181
279
  import { useNetworkState } from '@symbiote-native/network/react';
182
280
  import { useNetworkState } from '@symbiote-native/network/vue';
281
+ import { useNetworkState } from '@symbiote-native/network/svelte';
282
+ import { createNetworkState } from '@symbiote-native/network/solid';
183
283
  import { NetworkStateService } from '@symbiote-native/network/angular';
184
284
  ```
185
285
 
186
- `useNetworkState` seeds its initial value from a one-shot `getNetworkStateAsync()` call, then
187
- subscribes to `addNetworkStateListener` for updates, and unsubscribes on unmount — mirroring
188
- upstream's own `useNetworkState`.
286
+ `useNetworkState` (Solid: `createNetworkState`) seeds its initial value from a one-shot
287
+ `getNetworkStateAsync()` call, then subscribes to `addNetworkStateListener` for updates, and
288
+ unsubscribes on unmount — mirroring upstream's own `useNetworkState`. The Solid primitive returns
289
+ an `Accessor<NetworkState>` and subscribes from its body rather than an effect, so nothing can slip
290
+ between the seed and the subscription.
189
291
 
190
292
  ## Test it
191
293
 
192
294
  No Fabric/Descriptor angle at all — network is a pure async-function + `EventEmitter` listener
193
295
  surface, never a view. Tests inject a fake native-module object in place of the real
194
296
  `requireNativeModule` resolution (`src/core/network.test.ts`,
195
- `src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
297
+ `src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
196
298
  `@symbiote-native/battery`/`@symbiote-native/sensors`/`@symbiote-native/local-auth` use — no
197
299
  `installFabric()`, no ViewConfig. Native rendering itself is verified on-device — see the parent
198
300
  [README](../../README.md).
199
301
 
200
- The Android/iOS native wiring is done across all four `examples/expo-*` canary apps
201
- (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`);
202
- this package isn't yet in the public, non-Expo `examples/react` canary.
302
+ The Android/iOS native wiring is done across all six `examples/expo-*` canary apps
303
+ (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
304
+ `examples/expo-solid`, `examples/expo-angular`); this package isn't yet in the public, non-Expo
305
+ `examples/react` canary.
@@ -32,8 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
32
32
  }
33
33
  return useValue ? value : void 0;
34
34
  };
35
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
36
- import { addNetworkStateListener, getNetworkStateAsync } from '../../../core/index.js';
35
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
36
+ import { addNetworkStateListener, getNetworkStateAsync, } from '../../../core/index.js';
37
37
  // Angular twin of React's `useNetworkState` hook and Vue's `useNetworkState` composable.
38
38
  //
39
39
  // readonly networkState = inject(NetworkStateService).connect();
@@ -1,3 +1,3 @@
1
1
  export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, } from './network';
2
- export { NetworkStateType, type NetworkState, type NetworkStateEvent } from './types';
2
+ export { NetworkStateType, type NetworkState, type NetworkStateEvent, } from './types';
3
3
  export type { EventSubscription } from 'expo-modules-core';
@@ -1,2 +1,2 @@
1
1
  export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, } from './network.js';
2
- export { NetworkStateType } from './types.js';
2
+ export { NetworkStateType, } from './types.js';
@@ -2,7 +2,7 @@
2
2
  // value with a one-shot getNetworkStateAsync() call before the first native event fires, matching
3
3
  // upstream's own useNetworkState.
4
4
  import { useEffect, useState } from 'react';
5
- import { addNetworkStateListener, getNetworkStateAsync } from '../../../core/index.js';
5
+ import { addNetworkStateListener, getNetworkStateAsync, } from '../../../core/index.js';
6
6
  export function useNetworkState() {
7
7
  const [networkState, setNetworkState] = useState({});
8
8
  useEffect(() => {
@@ -0,0 +1,2 @@
1
+ export { createNetworkState } from './primitives/create-network-state';
2
+ export * from '../core';
@@ -0,0 +1,5 @@
1
+ // @symbiote-native/network/solid: the Solid entry over the framework-agnostic core — mirrors the
2
+ // lifecycle-bucket naming convention of adapters/solid/src/primitives (never `hooks`/`composables`/
3
+ // `runes`, those are React's, Vue's and Svelte's terms).
4
+ export { createNetworkState } from './primitives/create-network-state.js';
5
+ export * from '../core/index.js';
@@ -0,0 +1,3 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import { type NetworkState } from '../../core';
3
+ export declare function createNetworkState(): Accessor<NetworkState>;
@@ -0,0 +1,32 @@
1
+ // createNetworkState — the Solid twin of React's `useNetworkState` hook, Vue's composable and
2
+ // Svelte's rune, over the framework-agnostic core (core/network.ts). Seeds with a one-shot
3
+ // getNetworkStateAsync() call before the first native event fires, matching upstream's own
4
+ // useNetworkState.
5
+ //
6
+ // `primitives/` and `create*`, never `hooks/`+`use*`: Solid's ecosystem calls a composable
7
+ // reactive function a PRIMITIVE and reserves `use*` for consuming something that already exists.
8
+ // Full rationale in adapters/solid/src/primitives/create-color-scheme.ts.
9
+ //
10
+ // Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned object
11
+ // would pin the caller to the empty seed forever.
12
+ //
13
+ // Outside a component or `createRoot` there is no owner for `onCleanup` — Solid warns and the
14
+ // native listener lives for the process. The accessor still tracks; only the teardown is lost.
15
+ import { createSignal, onCleanup } from 'solid-js';
16
+ import { addNetworkStateListener, getNetworkStateAsync, } from '../../core/index.js';
17
+ export function createNetworkState() {
18
+ const [networkState, setNetworkState] = createSignal({});
19
+ getNetworkStateAsync().then(state => {
20
+ setNetworkState(state);
21
+ });
22
+ // Subscribed from the primitive body, not from an effect: React/Vue/Svelte start listening a
23
+ // tick after their seed runs, so a change landing in that window is lost. Both statements here
24
+ // run in one synchronous tick and nothing can interleave.
25
+ const subscription = addNetworkStateListener(event => {
26
+ setNetworkState(event);
27
+ });
28
+ onCleanup(() => {
29
+ subscription.remove();
30
+ });
31
+ return networkState;
32
+ }
@@ -1,5 +1,5 @@
1
- import { effect, inject, Injectable, Injector, signal } from '@angular/core';
2
- import { addNetworkStateListener, getNetworkStateAsync } from '../../../core';
1
+ import { effect, inject, Injectable, Injector, signal, } from '@angular/core';
2
+ import { addNetworkStateListener, getNetworkStateAsync, } from '../../../core';
3
3
  import * as i0 from "@angular/core";
4
4
  // Angular twin of React's `useNetworkState` hook and Vue's `useNetworkState` composable.
5
5
  //
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/network-state.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAqB,MAAM,eAAe,CAAC;;AAEjG,yFAAyF;AACzF,EAAE;AACF,mEAAmE;AACnE,2CAA2C;AAE3C,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAe,EAAE;yFAAC,CAAC;QAE9C,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,oBAAoB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/E,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAhBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/network-state.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GAErB,MAAM,eAAe,CAAC;;AAEvB,yFAAyF;AACzF,EAAE;AACF,mEAAmE;AACnE,2CAA2C;AAE3C,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAe,EAAE;yFAAC,CAAC;QAE9C,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,oBAAoB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CACnD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CACxB,CAAC;YACF,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAlBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
@@ -1,3 +1,3 @@
1
1
  export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, } from './network';
2
- export { NetworkStateType, type NetworkState, type NetworkStateEvent } from './types';
2
+ export { NetworkStateType, type NetworkState, type NetworkStateEvent, } from './types';
3
3
  export type { EventSubscription } from 'expo-modules-core';
@@ -1,3 +1,3 @@
1
1
  export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, } from './network';
2
- export { NetworkStateType } from './types';
2
+ export { NetworkStateType, } from './types';
3
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAA6C,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,GAGjB,MAAM,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAc/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAuB,wBAAwB,CAAC,CAAC"}
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAc/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAC5C,wBAAwB,CACzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/core/network.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,0FAA0F;AAC1F,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAA0B,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,WAAW,CAAC,oBAAoB,EAAE,CAAC;AAC5C,CAAC;AAED,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,WAAW,CAAC,iBAAiB,EAAE,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,IAAI,CAAC,WAAW,CAAC,0BAA0B,EAAE,CAAC;QAC5C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,WAAW,CAAC,0BAA0B,EAAE,CAAC;AAClD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,uBAAuB,CACrC,QAA4C;IAE5C,OAAO,WAAW,CAAC,WAAW,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/core/network.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,0FAA0F;AAC1F,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAA0B,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC;QACtC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,WAAW,CAAC,oBAAoB,EAAE,CAAC;AAC5C,CAAC;AAED,gGAAgG;AAChG,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,WAAW,CAAC,iBAAiB,EAAE,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,IAAI,CAAC,WAAW,CAAC,0BAA0B,EAAE,CAAC;QAC5C,MAAM,IAAI,mBAAmB,CAC3B,kBAAkB,EAClB,4BAA4B,CAC7B,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,0BAA0B,EAAE,CAAC;AAClD,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,uBAAuB,CACrC,QAA4C;IAE5C,OAAO,WAAW,CAAC,WAAW,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC;AACpE,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@symbiote-native/network",
3
- "version": "0.2.0",
4
- "description": "expo-network wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Network state (type/connected/internet-reachable) with a change listener, device IP address, and airplane-mode detection.",
3
+ "version": "1.0.0",
4
+ "description": "expo-network wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Network state (type/connected/internet-reachable) with a change listener, device IP address, and airplane-mode detection.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,6 +13,18 @@
13
13
  "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
14
  },
15
15
  "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "keywords": [
17
+ "react-native",
18
+ "symbiote-native",
19
+ "expo-network",
20
+ "react",
21
+ "vue",
22
+ "svelte",
23
+ "solid",
24
+ "angular",
25
+ "connectivity",
26
+ "network-state"
27
+ ],
16
28
  "type": "module",
17
29
  "main": "./build/core/index.js",
18
30
  "module": "./build/core/index.js",
@@ -34,6 +46,10 @@
34
46
  "types": "./build/svelte/index.d.ts",
35
47
  "default": "./build/svelte/index.js"
36
48
  },
49
+ "./solid": {
50
+ "types": "./build/solid/index.d.ts",
51
+ "default": "./build/solid/index.js"
52
+ },
37
53
  "./angular": {
38
54
  "types": "./build-ngc/angular/index.d.ts",
39
55
  "react-native": "./build-ngc/angular/index.js",
@@ -57,17 +73,19 @@
57
73
  "expo-modules-core": "57.0.5"
58
74
  },
59
75
  "peerDependencies": {
60
- "@symbiote-native/angular": ">=0.6.2",
61
- "@symbiote-native/engine": ">=0.1.7",
62
- "@symbiote-native/react": ">=0.2.8",
63
- "@symbiote-native/svelte": ">=0.1.0",
64
- "@symbiote-native/vue": ">=0.3.8",
65
76
  "@angular/core": ">=20",
66
77
  "@vue/runtime-core": "^3.5.13",
67
78
  "react": ">=19.0.0",
68
79
  "react-native": ">=0.86",
80
+ "solid-js": ">=1.9.0",
69
81
  "svelte": ">=5.56.0",
70
- "vue": ">=3.5.0"
82
+ "vue": ">=3.5.0",
83
+ "@symbiote-native/angular": "^1.0.0",
84
+ "@symbiote-native/engine": "^0.4.0",
85
+ "@symbiote-native/react": "^1.0.0",
86
+ "@symbiote-native/solid": "^1.0.0",
87
+ "@symbiote-native/svelte": "^1.0.0",
88
+ "@symbiote-native/vue": "^1.0.0"
71
89
  },
72
90
  "peerDependenciesMeta": {
73
91
  "@symbiote-native/angular": {
@@ -76,6 +94,9 @@
76
94
  "@symbiote-native/react": {
77
95
  "optional": true
78
96
  },
97
+ "@symbiote-native/solid": {
98
+ "optional": true
99
+ },
79
100
  "@symbiote-native/svelte": {
80
101
  "optional": true
81
102
  },
@@ -91,6 +112,9 @@
91
112
  "react": {
92
113
  "optional": true
93
114
  },
115
+ "solid-js": {
116
+ "optional": true
117
+ },
94
118
  "svelte": {
95
119
  "optional": true
96
120
  },
@@ -106,14 +130,16 @@
106
130
  "@types/react": "^19.2.0",
107
131
  "@vue/runtime-core": "^3.5.13",
108
132
  "react": "19.2.3",
133
+ "solid-js": "^1.9.14",
109
134
  "svelte": "^5.56.0",
110
135
  "typescript": "~6.0.0",
111
- "@symbiote-native/angular": "0.7.0",
112
- "@symbiote-native/engine": "0.2.0",
113
- "@symbiote-native/react": "0.3.0",
114
- "@symbiote-native/svelte": "0.2.0",
115
- "@symbiote-native/test-utils": "0.1.6",
116
- "@symbiote-native/vue": "0.4.0"
136
+ "@symbiote-native/angular": "1.0.0",
137
+ "@symbiote-native/engine": "0.4.0",
138
+ "@symbiote-native/react": "1.0.0",
139
+ "@symbiote-native/solid": "1.0.0",
140
+ "@symbiote-native/svelte": "1.0.0",
141
+ "@symbiote-native/test-utils": "0.3.0",
142
+ "@symbiote-native/vue": "1.0.0"
117
143
  },
118
144
  "scripts": {
119
145
  "typecheck": "tsc --build",
@@ -1,5 +1,16 @@
1
- import { effect, inject, Injectable, Injector, signal, type Signal } from '@angular/core';
2
- import { addNetworkStateListener, getNetworkStateAsync, type NetworkState } from '../../../core';
1
+ import {
2
+ effect,
3
+ inject,
4
+ Injectable,
5
+ Injector,
6
+ signal,
7
+ type Signal,
8
+ } from '@angular/core';
9
+ import {
10
+ addNetworkStateListener,
11
+ getNetworkStateAsync,
12
+ type NetworkState,
13
+ } from '../../../core';
3
14
 
4
15
  // Angular twin of React's `useNetworkState` hook and Vue's `useNetworkState` composable.
5
16
  //
@@ -15,7 +26,9 @@ export class NetworkStateService {
15
26
  effect(
16
27
  onCleanup => {
17
28
  getNetworkStateAsync().then(state => networkState.set(state));
18
- const subscription = addNetworkStateListener(event => networkState.set(event));
29
+ const subscription = addNetworkStateListener(event =>
30
+ networkState.set(event),
31
+ );
19
32
  onCleanup(() => subscription.remove());
20
33
  },
21
34
  { injector: this.injector },
package/src/core/index.ts CHANGED
@@ -4,5 +4,9 @@ export {
4
4
  isAirplaneModeEnabledAsync,
5
5
  addNetworkStateListener,
6
6
  } from './network';
7
- export { NetworkStateType, type NetworkState, type NetworkStateEvent } from './types';
7
+ export {
8
+ NetworkStateType,
9
+ type NetworkState,
10
+ type NetworkStateEvent,
11
+ } from './types';
8
12
  export type { EventSubscription } from 'expo-modules-core';
@@ -16,4 +16,6 @@ export type INativeNetworkModule = {
16
16
  ): EventSubscription;
17
17
  };
18
18
 
19
- export const expoNetwork = requireNativeModule<INativeNetworkModule>(EXPO_NETWORK_MODULE_NAME);
19
+ export const expoNetwork = requireNativeModule<INativeNetworkModule>(
20
+ EXPO_NETWORK_MODULE_NAME,
21
+ );
@@ -29,7 +29,10 @@ export async function getIpAddressAsync(): Promise<string> {
29
29
  */
30
30
  export async function isAirplaneModeEnabledAsync(): Promise<boolean> {
31
31
  if (!expoNetwork.isAirplaneModeEnabledAsync) {
32
- throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isAirplaneModeEnabledAsync');
32
+ throw new UnavailabilityError(
33
+ NATIVE_MODULE_NAME,
34
+ 'isAirplaneModeEnabledAsync',
35
+ );
33
36
  }
34
37
  return expoNetwork.isAirplaneModeEnabledAsync();
35
38
  }
@@ -2,14 +2,20 @@
2
2
  // value with a one-shot getNetworkStateAsync() call before the first native event fires, matching
3
3
  // upstream's own useNetworkState.
4
4
  import { useEffect, useState } from 'react';
5
- import { addNetworkStateListener, getNetworkStateAsync, type NetworkState } from '../../../core';
5
+ import {
6
+ addNetworkStateListener,
7
+ getNetworkStateAsync,
8
+ type NetworkState,
9
+ } from '../../../core';
6
10
 
7
11
  export function useNetworkState(): NetworkState {
8
12
  const [networkState, setNetworkState] = useState<NetworkState>({});
9
13
 
10
14
  useEffect(() => {
11
15
  getNetworkStateAsync().then(setNetworkState);
12
- const subscription = addNetworkStateListener(event => setNetworkState(event));
16
+ const subscription = addNetworkStateListener(event =>
17
+ setNetworkState(event),
18
+ );
13
19
  return () => subscription.remove();
14
20
  }, []);
15
21
 
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/network/solid: the Solid entry over the framework-agnostic core — mirrors the
2
+ // lifecycle-bucket naming convention of adapters/solid/src/primitives (never `hooks`/`composables`/
3
+ // `runes`, those are React's, Vue's and Svelte's terms).
4
+
5
+ export { createNetworkState } from './primitives/create-network-state';
6
+ export * from '../core';
@@ -0,0 +1,43 @@
1
+ // createNetworkState — the Solid twin of React's `useNetworkState` hook, Vue's composable and
2
+ // Svelte's rune, over the framework-agnostic core (core/network.ts). Seeds with a one-shot
3
+ // getNetworkStateAsync() call before the first native event fires, matching upstream's own
4
+ // useNetworkState.
5
+ //
6
+ // `primitives/` and `create*`, never `hooks/`+`use*`: Solid's ecosystem calls a composable
7
+ // reactive function a PRIMITIVE and reserves `use*` for consuming something that already exists.
8
+ // Full rationale in adapters/solid/src/primitives/create-color-scheme.ts.
9
+ //
10
+ // Returns an ACCESSOR, never a snapshot: a Solid component body runs ONCE, so a returned object
11
+ // would pin the caller to the empty seed forever.
12
+ //
13
+ // Outside a component or `createRoot` there is no owner for `onCleanup` — Solid warns and the
14
+ // native listener lives for the process. The accessor still tracks; only the teardown is lost.
15
+
16
+ import { createSignal, onCleanup, type Accessor } from 'solid-js';
17
+ import {
18
+ addNetworkStateListener,
19
+ getNetworkStateAsync,
20
+ type EventSubscription,
21
+ type NetworkState,
22
+ } from '../../core';
23
+
24
+ export function createNetworkState(): Accessor<NetworkState> {
25
+ const [networkState, setNetworkState] = createSignal<NetworkState>({});
26
+
27
+ getNetworkStateAsync().then(state => {
28
+ setNetworkState(state);
29
+ });
30
+
31
+ // Subscribed from the primitive body, not from an effect: React/Vue/Svelte start listening a
32
+ // tick after their seed runs, so a change landing in that window is lost. Both statements here
33
+ // run in one synchronous tick and nothing can interleave.
34
+ const subscription: EventSubscription = addNetworkStateListener(event => {
35
+ setNetworkState(event);
36
+ });
37
+
38
+ onCleanup(() => {
39
+ subscription.remove();
40
+ });
41
+
42
+ return networkState;
43
+ }