refactor(scanner): more basic work to get the scanner just running
All checks were successful
Build and Push LST Docker Image / docker (push) Successful in 2m33s

This commit is contained in:
2026-04-19 17:20:57 -05:00
parent 3734d9daac
commit 82f8369640
19 changed files with 642 additions and 323 deletions

View File

@@ -1,6 +1,6 @@
{
"editor.defaultFormatter": "biomejs.biome",
"workbench.colorTheme": "Default Dark+",
"workbench.colorTheme": "Dark+",
"terminal.integrated.env.windows": {},
"editor.formatOnSave": true,
"typescript.preferences.importModuleSpecifier": "relative",

View File

@@ -39,7 +39,8 @@
"react-native-web": "~0.21.0",
"react-native-worklets": "0.7.2",
"socket.io-client": "^4.8.3",
"zod": "^4.3.6"
"zod": "^4.3.6",
"zustand": "^5.0.12"
},
"devDependencies": {
"@types/react": "~19.2.2",
@@ -13898,6 +13899,35 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zustand": {
"version": "5.0.12",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=18.0.0",
"immer": ">=9.0.6",
"react": ">=18.0.0",
"use-sync-external-store": ">=1.2.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
},
"use-sync-external-store": {
"optional": true
}
}
}
}
}

View File

@@ -44,7 +44,8 @@
"react-native-web": "~0.21.0",
"react-native-worklets": "0.7.2",
"socket.io-client": "^4.8.3",
"zod": "^4.3.6"
"zod": "^4.3.6",
"zustand": "^5.0.12"
},
"devDependencies": {
"@types/react": "~19.2.2",

View File

@@ -1,38 +0,0 @@
import { Tabs } from 'expo-router'
import React from 'react'
import { colors } from '../../stlyes/global'
import { Home,Settings } from 'lucide-react-native'
export default function TabLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle:{
},
tabBarActiveTintColor: 'black',
tabBarInactiveTintColor: colors.textSecondary,
}}
>
<Tabs.Screen
name='index'
options={{
title:'Home',
tabBarIcon: ({color, size})=>(
<Home color={color} size={size}/>
)
}}
/>
<Tabs.Screen
name='config'
options={{
title: 'Config',
tabBarIcon: ({color, size})=> (
<Settings size={size} color={color}/>
)
}}
/>
</Tabs>
)
}

View File

@@ -1,92 +0,0 @@
// app/config.tsx
import { useEffect, useState } from "react";
import { View, Text, TextInput, Button, Alert } from "react-native";
import { useRouter } from "expo-router";
import { AppConfig, getConfig, saveConfig } from "../../lib/storage";
import Constants from "expo-constants";
export default function Config() {
const [serverUrl, setServerUrl] = useState("");
const [scannerId, setScannerId] = useState("");
const [config, setConfig] = useState<AppConfig | null>(null)
const [loading, setLoading] = useState(true);
const router = useRouter()
const version = Constants.expoConfig?.version;
const build = Constants.expoConfig?.android?.versionCode ?? 1;
useEffect(() => {
const loadConfig = async () => {
const existing = await getConfig();
if (existing) {
setServerUrl(existing.serverUrl);
setScannerId(existing.scannerId);
setConfig(existing)
}
setLoading(false);
};
loadConfig();
}, []);
const handleSave = async () => {
if (!serverUrl.trim() || !scannerId.trim()) {
Alert.alert("Missing info", "Please fill in both fields.");
return;
}
await saveConfig({
serverUrl: serverUrl.trim(),
scannerId: scannerId.trim(),
});
Alert.alert("Saved", "Config saved to device.");
//router.replace("/");
};
if (loading) {
return <Text>Loading config...</Text>;
}
return (
<View style={{ flex: 1, padding: 16, gap: 12 }}>
<View style={{alignItems: "center", margin: 10}}>
<Text style={{ fontSize: 20, fontWeight: "600"}}>LST Scanner Config</Text>
</View>
<Text>Server IP</Text>
<TextInput
value={serverUrl}
onChangeText={setServerUrl}
placeholder="192.168.1.1"
autoCapitalize="none"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<Text>Server port</Text>
<TextInput
value={scannerId}
onChangeText={setScannerId}
placeholder="3000"
autoCapitalize="characters"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8, }}
/>
<View style={{flexDirection: 'row',justifyContent: 'center', padding: 3}}>
<Button title="Save Config" onPress={handleSave} />
</View>
<View style={{ marginTop: "auto", alignItems: "center", padding: 10 }}>
<Text style={{ fontSize: 12, color: "#666" }}>
LST Scanner v{version}-{build}
</Text>
</View>
</View>
);
}

View File

@@ -1,134 +0,0 @@
import * as Application from "expo-application";
import * as Device from "expo-device";
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import {
Alert,
Platform,
ScrollView,
Text,
View,
} from "react-native";
import HomeHeader from "../../components/HomeHeader";
import { type AppConfig, getConfig, hasValidConfig } from "../../lib/storage";
import {
evaluateVersion,
type ServerVersionInfo,
type StartupStatus,
} from "../../lib/versionValidation";
import { globalStyles } from "../../stlyes/global";
import axios from 'axios'
export default function Index() {
const [config, setConfig] = useState<AppConfig | null>(null);
const [loading, setLoading] = useState(true);
const [startupStatus, setStartupStatus] = useState<StartupStatus>({state: "checking"});
const [serverInfo, setServerInfo] = useState<ServerVersionInfo>()
const router = useRouter();
const versionName = Application.nativeApplicationVersion ?? "unknown";
const versionCode = Number(Application.nativeBuildVersion ?? "0");
useEffect(() => {
let isMounted = true;
const startUp = async () => {
try {
const savedConfig = await getConfig();
if (!hasValidConfig(savedConfig)) {
router.replace("/config");
return;
}
if (!isMounted) return;
setConfig(savedConfig);
// temp while testing
const appBuildCode = 1;
try {
const res = await axios.get(`http://${savedConfig?.serverUrl}:${savedConfig?.scannerId}/lst/api/mobile/version`);
console.log(res)
const server = (await res.data) as ServerVersionInfo;
if (!isMounted) return;
const result = evaluateVersion(appBuildCode, server);
setStartupStatus(result);
setServerInfo(server)
if (result.state === "warning") {
Alert.alert("Update available", result.message);
}
} catch {
if (!isMounted) return;
setStartupStatus({ state: "offline" });
}
} finally {
if (isMounted) {
setLoading(false);
}
}
};
startUp();
return () => {
isMounted = false;
};
}, [router]);
if (loading) {
return <Text>Validating Configs.</Text>;
}
if (startupStatus.state === "checking") {
return <Text>Checking device and server status...</Text>;
}
if (startupStatus.state === "blocked") {
return (
<View>
<Text>Update Required</Text>
<Text>This scanner must be updated before it can be used.</Text>
<Text>Scan the update code to continue.</Text>
</View>
);
}
if (startupStatus.state === "offline") {
// app still renders, but show disconnected state
}
return (
<ScrollView >
<View style={globalStyles.container}>
<HomeHeader />
<Text>
Welcome.{versionName} - {versionCode}
</Text>
<Text>Running on: {Platform.OS}</Text>
<Text>Device model: {Device.modelName}</Text>
<Text>Device Brand: {Device.brand}</Text>
<Text> OS Version: {Device.osVersion}</Text>
<View style={{ flex: 1, padding: 16, gap: 12 }}>
<Text style={{ fontSize: 22, fontWeight: "600" }}>Welcome</Text>
{config ? (
<>
<Text>Server: {config.serverUrl}</Text>
<Text>Scanner: {config.scannerId}</Text>
<Text>Server: v{serverInfo?.versionName}</Text>
</>
) : (
<Text>No config found yet.</Text>
)}
</View></View>
</ScrollView>
);
}

View File

@@ -1,12 +1,14 @@
import { Stack } from "expo-router";
import {StatusBar} from 'expo-status-bar'
import { colors } from "../stlyes/global";
import { StatusBar } from "expo-status-bar";
export default function RootLayout() {
return <>
<StatusBar style="dark" />
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name='(tabs)' />
</Stack>
</>;
return (
<>
<StatusBar style="dark" />
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="index" />
{/* <Stack.Screen name="(tabs)" /> */}
</Stack>
</>
);
}

View File

@@ -0,0 +1,9 @@
import { Text, View } from "react-native";
export default function blocked() {
return (
<View>
<Text>Blocked</Text>
</View>
);
}

View File

@@ -0,0 +1,72 @@
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { ActivityIndicator, Text, View } from "react-native";
import { useAppStore } from "../hooks/useAppStore";
import { devDelay } from "../lib/devMode";
export default function Index() {
const router = useRouter();
const [message, setMessage] = useState(<Text>Starting app...</Text>);
const hasHydrated = useAppStore((s) => s.hasHydrated);
const serverPort = useAppStore((s) => s.serverPort);
const hasValidSetup = useAppStore((s) => s.hasValidSetup);
useEffect(() => {
if (!hasHydrated) {
setMessage(<Text>Loading app...</Text>);
return;
}
const startup = async () => {
try {
await devDelay(1500);
setMessage(<Text>Validating data...</Text>);
await devDelay(1500);
if (!hasValidSetup()) {
router.replace("/setup");
return;
}
setMessage(<Text>Checking scanner mode...</Text>);
await devDelay(1500);
if (parseInt(serverPort || "0", 10) >= 50000) {
setMessage(
<Text>
Starting normal alplaprod scanner that has no LST rules
</Text>,
);
await devDelay(1500);
router.replace("/setup");
return;
}
setMessage(<Text>Opening LST scan app</Text>);
await devDelay(3250);
router.replace("/scanner");
} catch (error) {
console.log("Startup error", error);
setMessage(<Text>Something went wrong during startup.</Text>);
}
};
startup();
}, [hasHydrated, hasValidSetup, serverPort, router]);
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 12,
}}
>
{message}
<ActivityIndicator size="large" />
</View>
);
}

View File

@@ -0,0 +1,32 @@
import React from "react";
import { Text, View } from "react-native";
export default function scanner() {
return (
<View
style={{
flex: 1,
//justifyContent: "center",
alignItems: "center",
marginTop: 50,
}}
>
<View style={{ alignItems: "center", margin: 10 }}>
<Text style={{ fontSize: 20, fontWeight: "600" }}>LST Scanner</Text>
</View>
<View
style={{
marginTop: 50,
alignItems: "center",
}}
>
<Text>Relocate</Text>
<Text>0 / 4</Text>
</View>
{/* <View>
<Text>List of recent scanned pallets TBA</Text>
</View> */}
</View>
);
}

156
lstMobile/src/app/setup.tsx Normal file
View File

@@ -0,0 +1,156 @@
import Constants from "expo-constants";
import { useRouter } from "expo-router";
import { useState } from "react";
import { Alert, Button, Text, TextInput, View } from "react-native";
import { useAppStore } from "../hooks/useAppStore";
export default function setup() {
const router = useRouter();
const [auth, setAuth] = useState(false);
const [pin, setPin] = useState("");
const version = Constants.expoConfig?.version;
const build = Constants.expoConfig?.android?.versionCode ?? 1;
const serverIpFromStore = useAppStore((s) => s.serverIp);
const serverPortFromStore = useAppStore((s) => s.serverPort);
const updateAppState = useAppStore((s) => s.updateAppState);
// local form state
const [serverIp, setLocalServerIp] = useState(serverIpFromStore);
const [serverPort, setLocalServerPort] = useState(serverPortFromStore);
const authCheck = () => {
if (pin === "6971") {
setAuth(true);
} else {
Alert.alert("Incorrect pin entered please try again");
setPin("");
}
};
const handleSave = async () => {
if (!serverIp.trim() || !serverPort.trim()) {
Alert.alert("Missing info", "Please fill in both fields.");
return;
}
updateAppState({
serverIp: serverIp.trim(),
serverPort: serverPort.trim(),
setupCompleted: true,
isRegistered: true,
});
Alert.alert("Saved", "Config saved to device.");
//router.replace("/");
};
return (
<View
style={{
flex: 1,
//justifyContent: "center",
alignItems: "center",
marginTop: 50,
}}
>
<View style={{ alignItems: "center", margin: 10 }}>
<Text style={{ fontSize: 20, fontWeight: "600" }}>
LST Scanner Config
</Text>
</View>
{!auth ? (
<View>
<Text>Pin Number</Text>
<TextInput
value={pin}
onChangeText={setPin}
placeholder=""
//autoCapitalize="none"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8, width: 128 }}
/>
<View
style={{
flexDirection: "row",
justifyContent: "center",
padding: 3,
borderRadius: 8,
}}
>
<Button title="Save Config" onPress={authCheck} />
</View>
</View>
) : (
<View>
<Text>Server IP</Text>
<TextInput
value={serverIp}
onChangeText={setLocalServerIp}
placeholder="192.168.1.1"
//autoCapitalize="none"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<Text>Server port</Text>
<TextInput
value={serverPort}
onChangeText={setLocalServerPort}
placeholder="3000"
autoCapitalize="characters"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
{parseInt(serverPort ?? "0", 10) >= 50000 && (
<View>
<Text>Scanner ID</Text>
<Text style={{ width: 250 }}>
This is needed as you will be redirected to the standard scanner
with no rules except the rules that alplaprod puts in
</Text>
<TextInput
value={scannerId}
onChangeText={setScannerId}
placeholder="0001"
autoCapitalize="characters"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
</View>
)}
<View
style={{
flexDirection: "row",
justifyContent: "center",
padding: 3,
gap: 3,
}}
>
<Button title="Save Config" onPress={handleSave} />
<Button
title="Home"
onPress={() => {
router.push("/");
}}
/>
</View>
</View>
)}
<View
style={{
marginTop: "auto",
alignItems: "center",
padding: 10,
marginBottom: 12,
}}
>
<Text style={{ fontSize: 12, color: "#666" }}>
LST Scanner v{version}-{build}
</Text>
</View>
</View>
);
}

View File

@@ -0,0 +1,151 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
export type ValidationStatus = "idle" | "pending" | "passed" | "failed";
export type AppState = {
serverIp: string;
serverPort: string;
scannerId?: string;
stageId?: string;
deviceName?: string;
setupCompleted: boolean;
isRegistered: boolean;
lastValidationStatus: ValidationStatus;
lastValidationAt?: string;
appVersion?: string;
hasHydrated: boolean;
};
type AppActions = {
setServerIp: (value: string) => void;
setServerPort: (value: string) => void;
setScannerId: (value?: string) => void;
setStageId: (value?: string) => void;
setDeviceName: (value?: string) => void;
setSetupCompleted: (value: boolean) => void;
setIsRegistered: (value: boolean) => void;
setValidationStatus: (status: ValidationStatus, validatedAt?: string) => void;
setAppVersion: (value?: string) => void;
setHasHydrated: (value: boolean) => void;
updateAppState: (updates: Partial<AppState>) => void;
resetApp: () => void;
hasValidSetup: () => boolean;
canEnterApp: () => boolean;
getServerUrl: () => string;
};
export type AppStore = AppState & AppActions;
const defaultAppState: AppState = {
serverIp: "",
serverPort: "",
scannerId: "0001",
stageId: undefined,
deviceName: undefined,
setupCompleted: false,
isRegistered: false,
lastValidationStatus: "idle",
lastValidationAt: undefined,
appVersion: undefined,
hasHydrated: false,
};
export const useAppStore = create<AppStore>()(
persist(
(set, get) => ({
...defaultAppState,
setServerIp: (value) => set({ serverIp: value }),
setServerPort: (value) => set({ serverPort: value }),
setScannerId: (value) => set({ scannerId: value }),
setStageId: (value) => set({ stageId: value }),
setDeviceName: (value) => set({ deviceName: value }),
setSetupCompleted: (value) => set({ setupCompleted: value }),
setIsRegistered: (value) => set({ isRegistered: value }),
setValidationStatus: (status, validatedAt) =>
set({
lastValidationStatus: status,
lastValidationAt: validatedAt,
}),
setAppVersion: (value) => set({ appVersion: value }),
setHasHydrated: (value) => set({ hasHydrated: value }),
updateAppState: (updates) =>
set((state) => ({
...state,
...updates,
})),
resetApp: () =>
set({
...defaultAppState,
hasHydrated: true,
}),
hasValidSetup: () => {
const state = get();
return Boolean(
state.serverIp?.trim() &&
state.serverPort?.trim() &&
state.setupCompleted,
);
},
canEnterApp: () => {
const state = get();
return Boolean(
state.serverIp?.trim() &&
state.serverPort?.trim() &&
state.setupCompleted &&
state.isRegistered,
);
},
getServerUrl: () => {
const { serverIp, serverPort } = get();
if (!serverIp?.trim() || !serverPort?.trim()) return "";
return `http://${serverIp.trim()}:${serverPort.trim()}`;
},
}),
{
name: "lst_mobile_app_store",
storage: createJSONStorage(() => AsyncStorage),
onRehydrateStorage: () => (state, error) => {
if (error) {
console.log("Failed to hydrate app state", error);
}
state?.setHasHydrated(true);
},
partialize: (state) => ({
serverIp: state.serverIp,
serverPort: state.serverPort,
scannerId: state.scannerId,
stageId: state.stageId,
deviceName: state.deviceName,
setupCompleted: state.setupCompleted,
isRegistered: state.isRegistered,
lastValidationStatus: state.lastValidationStatus,
lastValidationAt: state.lastValidationAt,
appVersion: state.appVersion,
}),
},
),
);

View File

@@ -0,0 +1 @@
export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));

View File

@@ -0,0 +1,7 @@
import { delay } from "./delay";
export const devDelay = async (ms: number) => {
if (__DEV__) {
await delay(ms);
}
};

View File

@@ -1,36 +0,0 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
export type AppConfig = {
serverUrl: string;
scannerId: string;
};
const CONFIG_KEY = "scanner_app_config";
export async function saveConfig(config: AppConfig) {
await AsyncStorage.setItem(CONFIG_KEY, JSON.stringify(config));
}
export async function getConfig(): Promise<AppConfig | null> {
const raw = await AsyncStorage.getItem(CONFIG_KEY);
if (!raw) return null;
try {
return JSON.parse(raw) as AppConfig;
} catch (error) {
console.log("Error", error)
return null;
}
}
export function hasValidConfig(config: AppConfig | null) {
if (!config) return false;
return Boolean(
config.serverUrl?.trim() &&
config.scannerId?.trim()
);
}

View File

@@ -0,0 +1,33 @@
import { Tabs } from "expo-router";
import { Home, Settings } from "lucide-react-native";
import { colors } from "../../stlyes/global";
export default function TabLayout() {
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle: {},
tabBarActiveTintColor: "black",
tabBarInactiveTintColor: colors.textSecondary,
}}
>
<Tabs.Screen
name="home"
options={{
title: "Home",
tabBarIcon: ({ color, size }) => <Home color={color} size={size} />,
}}
/>
<Tabs.Screen
name="config"
options={{
title: "Config",
tabBarIcon: ({ color, size }) => (
<Settings size={size} color={color} />
),
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,94 @@
// app/config.tsx
import Constants from "expo-constants";
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { Alert, Button, Text, TextInput, View } from "react-native";
export default function Config() {
const [serverUrl, setServerUrl] = useState("");
const [scannerId, setScannerId] = useState("");
const [config, setConfig] = useState<AppConfig | null>(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
const version = Constants.expoConfig?.version;
const build = Constants.expoConfig?.android?.versionCode ?? 1;
useEffect(() => {
const loadConfig = async () => {
const existing = await getConfig();
if (existing) {
setServerUrl(existing.serverUrl);
setScannerId(existing.scannerId);
setConfig(existing);
}
setLoading(false);
};
loadConfig();
}, []);
const handleSave = async () => {
if (!serverUrl.trim() || !scannerId.trim()) {
Alert.alert("Missing info", "Please fill in both fields.");
return;
}
await saveConfig({
serverUrl: serverUrl.trim(),
scannerId: scannerId.trim(),
});
Alert.alert("Saved", "Config saved to device.");
//router.replace("/");
};
if (loading) {
return <Text>Loading config...</Text>;
}
return (
<View style={{ flex: 1, padding: 16, gap: 12 }}>
<View style={{ alignItems: "center", margin: 10 }}>
<Text style={{ fontSize: 20, fontWeight: "600" }}>
LST Scanner Config
</Text>
</View>
<Text>Server IP</Text>
<TextInput
value={serverUrl}
onChangeText={setServerUrl}
placeholder="192.168.1.1"
autoCapitalize="none"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<Text>Server port</Text>
<TextInput
value={scannerId}
onChangeText={setScannerId}
placeholder="3000"
autoCapitalize="characters"
keyboardType="numeric"
style={{ borderWidth: 1, padding: 10, borderRadius: 8 }}
/>
<View
style={{ flexDirection: "row", justifyContent: "center", padding: 3 }}
>
<Button title="Save Config" onPress={handleSave} />
</View>
<View style={{ marginTop: "auto", alignItems: "center", padding: 10 }}>
<Text style={{ fontSize: 12, color: "#666" }}>
LST Scanner v{version}-{build}
</Text>
</View>
</View>
);
}

View File

@@ -0,0 +1,43 @@
import axios from "axios";
import * as Application from "expo-application";
import * as Device from "expo-device";
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import { Alert, Platform, ScrollView, Text, View } from "react-native";
import HomeHeader from "../../components/HomeHeader";
import { hasValidSetup, type PersistedAppState } from "../../lib/storage";
import {
evaluateVersion,
type ServerVersionInfo,
type StartupStatus,
} from "../../lib/versionValidation";
import { globalStyles } from "../../stlyes/global";
export default function Index() {
return (
<ScrollView>
<View style={globalStyles.container}>
<HomeHeader />
<Text>Welcome. Blake</Text>
<Text>Running on: {Platform.OS}</Text>
<Text>Device model: {Device.modelName}</Text>
<Text>Device Brand: {Device.brand}</Text>
<Text> OS Version: {Device.osVersion}</Text>
<View style={{ flex: 1, padding: 16, gap: 12 }}>
<Text style={{ fontSize: 22, fontWeight: "600" }}>Welcome</Text>
{/* {config ? (
<>
<Text>Server: {config.serverUrl}</Text>
<Text>Scanner: {config.scannerId}</Text>
<Text>Server: v{serverInfo?.versionName}</Text>
</>
) : (
<Text>No config found yet.</Text>
)} */}
</View>
</View>
</ScrollView>
);
}

12
package-lock.json generated
View File

@@ -536,9 +536,6 @@
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
@@ -556,9 +553,6 @@
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
@@ -576,9 +570,6 @@
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
@@ -596,9 +587,6 @@
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [