From fb1d480316ade612a38f0ac1764a5bac57614428 Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Tue, 17 Feb 2026 17:30:16 +0100 Subject: [PATCH 1/5] [dev-launcher] fix crash when disabling notification permissions (#43217) --- packages/expo-dev-launcher/CHANGELOG.md | 2 ++ .../activitydelegates/DevLauncherReactActivityNOPDelegate.kt | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/expo-dev-launcher/CHANGELOG.md b/packages/expo-dev-launcher/CHANGELOG.md index 4af4adafbd19f3..ac59d44b79b8ee 100644 --- a/packages/expo-dev-launcher/CHANGELOG.md +++ b/packages/expo-dev-launcher/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🐛 Bug fixes +- [android] fixed crash when returning from notification settings after disabling notification permissions ([#43217](https://github.com/expo/expo/pull/43217) by [@vonovak](https://github.com/vonovak)) + ### 💡 Others ## 55.0.7 — 2026-02-16 diff --git a/packages/expo-dev-launcher/android/src/debug/java/expo/modules/devlauncher/react/activitydelegates/DevLauncherReactActivityNOPDelegate.kt b/packages/expo-dev-launcher/android/src/debug/java/expo/modules/devlauncher/react/activitydelegates/DevLauncherReactActivityNOPDelegate.kt index b8d2bc15a1f431..ad1f826161ab13 100644 --- a/packages/expo-dev-launcher/android/src/debug/java/expo/modules/devlauncher/react/activitydelegates/DevLauncherReactActivityNOPDelegate.kt +++ b/packages/expo-dev-launcher/android/src/debug/java/expo/modules/devlauncher/react/activitydelegates/DevLauncherReactActivityNOPDelegate.kt @@ -16,6 +16,7 @@ open class DevLauncherReactActivityNOPDelegate(activity: ReactActivity) : override fun onNewIntent(intent: Intent?): Boolean = true override fun onBackPressed(): Boolean = true override fun onWindowFocusChanged(hasFocus: Boolean) {} + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {} override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {} override fun onConfigurationChanged(newConfig: Configuration) {} } From 87f137e24a113e12f665a5e7053e26fa717e7f47 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Tue, 17 Feb 2026 22:52:32 +0530 Subject: [PATCH 2/5] [docs] Fix auto-generated docs for colors type in LinearGradient (#43205) # Why Fix ENG-19531 # How - Move the rest type handling earlier in the conditional chain in `resolveTypeName` so it is evaluated before the generic `elementType?.type === 'array'` branch. - Wrap array type returns in JSX fragments (`<>...[]`) instead of string concatenation, so the resolved child type (which may itself be a JSX element with links) renders correctly. - Update the corresponding test snapshot to reflect the new JSX fragment rendering. # Test Plan Run docs locally and visit: http://localhost:3002/versions/latest/sdk/linear-gradient/#colors CleanShot 2026-02-17 at 16 54 50@2x # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- docs/components/plugins/api/APISectionUtils.tsx | 8 ++++---- .../api/__snapshots__/APISectionUtils.test.tsx.snap | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/components/plugins/api/APISectionUtils.tsx b/docs/components/plugins/api/APISectionUtils.tsx index fdb596675cc3cb..21bdd4a0f94df1 100644 --- a/docs/components/plugins/api/APISectionUtils.tsx +++ b/docs/components/plugins/api/APISectionUtils.tsx @@ -239,11 +239,13 @@ export const resolveTypeName = ( sdkVersion, }); } else if (type === 'array') { - return resolveTypeName(elementType, sdkVersion) + '[]'; + return <>{resolveTypeName(elementType, sdkVersion)}[]; } return elementType.name + type; + } else if (type === 'rest' && elementType) { + return <>...{resolveTypeName(elementType, sdkVersion)}; } else if (elementType?.type === 'array') { - return resolveTypeName(elementType, sdkVersion) + '[]'; + return <>{resolveTypeName(elementType, sdkVersion)}[]; } else if (elementType?.declaration) { if (type === 'array') { const { parameters, type: paramType } = elementType.declaration.indexSignature ?? {}; @@ -395,8 +397,6 @@ export const resolveTypeName = ( return operator ?? 'undefined'; } else if (type === 'intrinsic') { return name ?? 'undefined'; - } else if (type === 'rest' && elementType) { - return <>...{resolveTypeName(elementType, sdkVersion)}; } else if (value === null) { return 'null'; } diff --git a/docs/components/plugins/api/__snapshots__/APISectionUtils.test.tsx.snap b/docs/components/plugins/api/__snapshots__/APISectionUtils.test.tsx.snap index 9c3c3fa9fe8066..7542fed94f3151 100644 --- a/docs/components/plugins/api/__snapshots__/APISectionUtils.test.tsx.snap +++ b/docs/components/plugins/api/__snapshots__/APISectionUtils.test.tsx.snap @@ -619,7 +619,8 @@ exports[`APISectionUtils.resolveTypeName union of array values 1`] = ` exports[`APISectionUtils.resolveTypeName union with array 1`] = `
- number[] + number + [] From 0e1c33b57513e6dbe8b088fdc45e94aeefc7e872 Mon Sep 17 00:00:00 2001 From: Alan Hughes <30924086+alanjhughes@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:57:01 +0000 Subject: [PATCH 3/5] [ios][dev-launcher] Make network permission UI informational only (#43216) --- .../ios/EXDevLauncherController.m | 8 +- .../ios/SwiftUI/DevLauncherViewModel.swift | 38 +-- .../ios/SwiftUI/DevLauncherViews.swift | 13 +- .../SwiftUI/LocalNetworkPermissionView.swift | 317 +++--------------- .../ios/SwiftUI/SettingsTabView.swift | 16 +- 5 files changed, 67 insertions(+), 325 deletions(-) diff --git a/packages/expo-dev-launcher/ios/EXDevLauncherController.m b/packages/expo-dev-launcher/ios/EXDevLauncherController.m index e6cb1a05599ba9..c9ca6fbfe758e6 100644 --- a/packages/expo-dev-launcher/ios/EXDevLauncherController.m +++ b/packages/expo-dev-launcher/ios/EXDevLauncherController.m @@ -197,13 +197,13 @@ - (void)start:(id)delegate launchOptions:(NSDic }; #if TARGET_OS_SIMULATOR - BOOL hasCompletedPermissionFlow = YES; + BOOL hasGrantedNetworkPermission = YES; #else - BOOL hasCompletedPermissionFlow = [[NSUserDefaults standardUserDefaults] boolForKey:@"expo.devlauncher.hasCompletedNetworkPermissionFlow"]; + BOOL hasGrantedNetworkPermission = [[NSUserDefaults standardUserDefaults] boolForKey:@"expo.devlauncher.hasGrantedNetworkPermission"]; #endif NSURL* initialUrl = [EXDevLauncherController initialUrlFromProcessInfo]; - if (initialUrl && hasCompletedPermissionFlow) { + if (initialUrl && hasGrantedNetworkPermission) { [self loadApp:initialUrl withProjectUrl:nil onSuccess:nil onError:navigateToLauncher]; return; } @@ -211,7 +211,7 @@ - (void)start:(id)delegate launchOptions:(NSDic NSNumber *devClientTryToLaunchLastBundleValue = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"DEV_CLIENT_TRY_TO_LAUNCH_LAST_BUNDLE"]; BOOL shouldTryToLaunchLastOpenedBundle = (devClientTryToLaunchLastBundleValue != nil) ? [devClientTryToLaunchLastBundleValue boolValue] : YES; - if (!hasCompletedPermissionFlow) { + if (!hasGrantedNetworkPermission) { shouldTryToLaunchLastOpenedBundle = NO; } diff --git a/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViewModel.swift b/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViewModel.swift index 8f764fa35d8b99..26400e30654014 100644 --- a/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViewModel.swift +++ b/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViewModel.swift @@ -10,7 +10,12 @@ private let sessionKey = "expo-session-secret" private let DEV_LAUNCHER_DEFAULT_SCHEME = "expo-dev-launcher" private let BONJOUR_TYPE = "_expo._tcp" -private let networkPermissionFlowKey = "expo.devlauncher.hasCompletedNetworkPermissionFlow" +private let networkPermissionGrantedKey = "expo.devlauncher.hasGrantedNetworkPermission" + +enum LocalNetworkPermissionStatus: Equatable, Sendable { + case unknown + case denied +} @MainActor class DevLauncherViewModel: ObservableObject { @@ -205,41 +210,22 @@ class DevLauncherViewModel: ObservableObject { return } - let hasCompletedPermissionFlow = UserDefaults.standard.bool( - forKey: networkPermissionFlowKey - ) - - #if targetEnvironment(simulator) - // Simulators don't need permission, continue - #else - if !hasCompletedPermissionFlow { - return - } - #endif - - stopServerDiscovery() - startDevServerBrowser() - startLocalDevServerScanner() - } - - func startDiscoveryForPermissionCheck() { - permissionStatus = .checking stopServerDiscovery() startDevServerBrowser() startLocalDevServerScanner() } - func markPermissionFlowCompleted() { - UserDefaults.standard.set(true, forKey: networkPermissionFlowKey) + func markNetworkPermissionGranted() { + UserDefaults.standard.set(true, forKey: networkPermissionGrantedKey) } func resetPermissionFlowState() { - UserDefaults.standard.removeObject(forKey: networkPermissionFlowKey) + UserDefaults.standard.removeObject(forKey: networkPermissionGrantedKey) permissionStatus = .unknown } - var isFirstPermissionCheck: Bool { - !UserDefaults.standard.bool(forKey: networkPermissionFlowKey) + var hasGrantedNetworkPermission: Bool { + UserDefaults.standard.bool(forKey: networkPermissionGrantedKey) } func stopServerDiscovery() { @@ -283,7 +269,7 @@ class DevLauncherViewModel: ObservableObject { guard let self else { return } switch state { case .ready: - self.permissionStatus = .granted + self.markNetworkPermissionGranted() case .waiting(let error): if case .dns(let dnsError) = error, dnsError == kDNSServiceErr_PolicyDenied { self.permissionStatus = .denied diff --git a/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViews.swift b/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViews.swift index c5b3cb7ca7b104..2d677a27d66a0c 100644 --- a/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViews.swift +++ b/packages/expo-dev-launcher/ios/SwiftUI/DevLauncherViews.swift @@ -9,8 +9,8 @@ public struct DevLauncherRootView: View { init(viewModel: DevLauncherViewModel) { self.viewModel = viewModel - let shouldSkipPermissionFlow = Self.isSimulator - || UserDefaults.standard.bool(forKey: "expo.devlauncher.hasCompletedNetworkPermissionFlow") + let shouldSkipPermissionFlow = Self.isSimulator + || UserDefaults.standard.bool(forKey: "expo.devlauncher.hasGrantedNetworkPermission") _hasCompletedPermissionFlow = State(initialValue: shouldSkipPermissionFlow) } @@ -24,12 +24,9 @@ public struct DevLauncherRootView: View { public var body: some View { if !hasCompletedPermissionFlow { - LocalNetworkPermissionView( - viewModel: viewModel, - onPermissionGranted: { - hasCompletedPermissionFlow = true - } - ) + LocalNetworkPermissionView { + hasCompletedPermissionFlow = true + } } else { mainContent } diff --git a/packages/expo-dev-launcher/ios/SwiftUI/LocalNetworkPermissionView.swift b/packages/expo-dev-launcher/ios/SwiftUI/LocalNetworkPermissionView.swift index 4afb30805a0f32..d83f5f515fa12d 100644 --- a/packages/expo-dev-launcher/ios/SwiftUI/LocalNetworkPermissionView.swift +++ b/packages/expo-dev-launcher/ios/SwiftUI/LocalNetworkPermissionView.swift @@ -2,315 +2,76 @@ import SwiftUI -enum LocalNetworkPermissionStatus: Equatable, Sendable { - case unknown - case checking - case granted - case denied -} - struct LocalNetworkPermissionView: View { - @ObservedObject var viewModel: DevLauncherViewModel - let onPermissionGranted: () -> Void - @State private var isCheckingPermission = false - @State private var timeoutTask: Task? - @State private var hasTimedOut = false + let onContinue: () -> Void - private var isLoading: Bool { - isCheckingPermission || viewModel.permissionStatus == .checking - } - var body: some View { VStack(spacing: 0) { Spacer() - - if hasTimedOut { - PermissionTimeoutView { - retryPermissionCheck() - } continueWithoutPermission: { - continueWithoutPermission() - } - } else { - switch viewModel.permissionStatus { - case .unknown, .checking: - RequestPermissionView(isLoading: isLoading) { - triggerPermissionCheck() - } - case .granted: - ProgressView() - case .denied: - PermissionsDeniedView(appName: appName) { - openSettings() - } continueWithoutPermission: { - continueWithoutPermission() - } - } - } - - Spacer() - - Footer() - } - .padding(.horizontal, 24) - .padding(.vertical, 32) - .background(Color.expoSystemBackground) - .onDisappear { - timeoutTask?.cancel() - timeoutTask = nil - } - .onChange(of: viewModel.permissionStatus) { newStatus in - timeoutTask?.cancel() - timeoutTask = nil - if newStatus == .granted { - hasTimedOut = false - viewModel.markPermissionFlowCompleted() - onPermissionGranted() - } else if newStatus == .denied { - isCheckingPermission = false - hasTimedOut = false - } - } - } - - private var appName: String { - Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String - ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String - ?? "this app" - } - - private func triggerPermissionCheck() { - isCheckingPermission = true - hasTimedOut = false - viewModel.startDiscoveryForPermissionCheck() + VStack(spacing: 24) { + Image(systemName: "wifi") + .font(.system(size: 64)) + .foregroundColor(.accentColor) - timeoutTask?.cancel() - timeoutTask = Task { - do { - try await Task.sleep(nanoseconds: 15_000_000_000) - await MainActor.run { - if viewModel.permissionStatus == .checking { - hasTimedOut = true - isCheckingPermission = false - viewModel.stopServerDiscovery() - viewModel.permissionStatus = .unknown - } - } - } catch {} - } - } + VStack(spacing: 12) { + Text("Find Dev Servers") + .font(.title) + .fontWeight(.bold) - private func retryPermissionCheck() { - hasTimedOut = false - triggerPermissionCheck() - } - - private func openSettings() { - #if os(iOS) - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) - } - #endif - } - - private func continueWithoutPermission() { - viewModel.markPermissionFlowCompleted() - onPermissionGranted() - } -} + Text("Expo Dev Launcher needs to access your local network to discover development servers running on your computer.") + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + } -struct RequestPermissionView: View { - let isLoading: Bool - let triggerPermissionCheck: () -> Void + HStack(alignment: .center, spacing: 8) { + Image(systemName: "info.circle") + Text("You'll see a system prompt asking for local network access.\nTap \"Allow\" to continue.") + .multilineTextAlignment(.center) + } + .frame(maxWidth: .infinity) + .padding() + .background(Color.expoSecondarySystemBackground) + .cornerRadius(12) + .font(.callout) + .foregroundColor(.secondary) - var body: some View { - VStack(spacing: 24) { - Image(systemName: "wifi") - .font(.system(size: 64)) - .foregroundColor(.accentColor) - - VStack(spacing: 12) { - Text("Find Dev Servers") - .font(.title) - .fontWeight(.bold) - - Text("Expo Dev Launcher needs to access your local network to discover development servers running on your computer.") - .font(.body) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } - - VStack(spacing: 8) { - Image(systemName: "info.circle") - .foregroundColor(.secondary) - Text("You'll see a system prompt asking for local network access. Tap \"Allow\" to continue.") - .font(.footnote) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } - .padding() - .background(Color.expoSecondarySystemBackground) - .cornerRadius(12) - - Button { - triggerPermissionCheck() - } label: { - if isLoading { - ProgressView() - .progressViewStyle(CircularProgressViewStyle(tint: .white)) - .frame(maxWidth: .infinity) - .padding() - } else { + Button { + onContinue() + } label: { Text("Continue") .fontWeight(.semibold) .frame(maxWidth: .infinity) .padding() } - } - .background(Color.accentColor) - .foregroundColor(.white) - .cornerRadius(12) - .disabled(isLoading) - } - } -} - -struct PermissionsDeniedView: View { - let appName: String - let openSettings: () -> Void - let continueWithoutPermission: () -> Void - - var body: some View { - VStack(spacing: 24) { - Image(systemName: "wifi.slash") - .font(.system(size: 64)) - .foregroundColor(.orange) - - VStack(spacing: 12) { - Text("Local Network Access Required") - .font(.title) - .fontWeight(.bold) - - Text("Without local network access, Dev Launcher can't find development servers on your network. You can still enter server URLs manually.") - .font(.body) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } - - VStack(spacing: 12) { - Button { - openSettings() - } label: { - HStack { - Image(systemName: "gear") - Text("Open Settings") - .fontWeight(.semibold) - } - .frame(maxWidth: .infinity) - .padding() - } .background(Color.accentColor) .foregroundColor(.white) .cornerRadius(12) - - Button { - continueWithoutPermission() - } label: { - Text("Continue Without Discovery") - .fontWeight(.medium) - .frame(maxWidth: .infinity) - .padding() - } - .foregroundColor(.accentColor) } - - Text("To enable later: Settings → Privacy & Security → Local Network → \(appName)") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } - } -} - -struct PermissionTimeoutView: View { - let retryPermissionCheck: () -> Void - let continueWithoutPermission: () -> Void - - var body: some View { - VStack(spacing: 24) { - Image(systemName: "exclamationmark.triangle") - .font(.system(size: 64)) - .foregroundColor(.orange) - VStack(spacing: 12) { - Text("Permission Check Timed Out") - .font(.title) - .fontWeight(.bold) + Spacer() - Text("The permission check is taking longer than expected. This might happen if you dismissed the system dialog or if there's a network issue.") - .font(.body) + VStack(spacing: 4) { + Text("Why is this needed?") + .font(.footnote) + .fontWeight(.medium) + Text("Dev servers advertise themselves on your local network using Bonjour. This permission allows the app to discover them automatically.") + .font(.caption) .foregroundColor(.secondary) .multilineTextAlignment(.center) } - - VStack(spacing: 12) { - Button { - retryPermissionCheck() - } - label: { - HStack { - Image(systemName: "arrow.clockwise") - Text("Try Again") - .fontWeight(.semibold) - } - .frame(maxWidth: .infinity) - .padding() - } - .background(Color.accentColor) - .foregroundColor(.white) - .cornerRadius(12) - - Button { - continueWithoutPermission() - } - label: { - Text("Continue Without Discovery") - .fontWeight(.medium) - .frame(maxWidth: .infinity) - .padding() - } - .foregroundColor(.accentColor) - } - - Text("You can enable discovery later in Settings if needed.") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) - } - } -} - -struct Footer: View { - var body: some View { - VStack(spacing: 4) { - Text("Why is this needed?") - .font(.footnote) - .fontWeight(.medium) - Text("Dev servers advertise themselves on your local network using Bonjour. This permission allows the app to discover them automatically.") - .font(.caption) - .foregroundColor(.secondary) - .multilineTextAlignment(.center) } + .padding(.horizontal, 24) + .padding(.vertical, 32) + .background(Color.expoSystemBackground) } } #if DEBUG struct LocalNetworkPermissionView_Previews: PreviewProvider { static var previews: some View { - LocalNetworkPermissionView( - viewModel: DevLauncherViewModel(), - onPermissionGranted: {} - ) + LocalNetworkPermissionView(onContinue: {}) } } #endif diff --git a/packages/expo-dev-launcher/ios/SwiftUI/SettingsTabView.swift b/packages/expo-dev-launcher/ios/SwiftUI/SettingsTabView.swift index b38554c8bd0d00..ede6a5bc3bf39c 100644 --- a/packages/expo-dev-launcher/ios/SwiftUI/SettingsTabView.swift +++ b/packages/expo-dev-launcher/ios/SwiftUI/SettingsTabView.swift @@ -259,7 +259,7 @@ struct SettingsTabView: View { HStack { Text("First Launch Check") Spacer() - Text(viewModel.isFirstPermissionCheck ? "Pending" : "Completed") + Text(viewModel.hasGrantedNetworkPermission ? "Granted" : "Pending") .foregroundColor(.secondary) } .padding() @@ -327,20 +327,18 @@ struct SettingsTabView: View { private func checkNetworkPermission() { isCheckingPermission = true permissionCheckResult = "Checking..." - viewModel.startDiscoveryForPermissionCheck() + viewModel.stopServerDiscovery() + viewModel.startServerDiscovery() } - + private func updatePermissionResultFromStatus() { isCheckingPermission = false - switch viewModel.permissionStatus { - case .granted: + if viewModel.hasGrantedNetworkPermission { permissionCheckResult = "✅ Granted" - case .denied: + } else if viewModel.permissionStatus == .denied { permissionCheckResult = "❌ Denied" - case .unknown: + } else { permissionCheckResult = "⚠️ Unknown" - case .checking: - permissionCheckResult = "🔄 Checking" } } From 22e5bbc824ec114127e194ca586a9911297fb810 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 00:19:15 +0530 Subject: [PATCH 4/5] Bump qs from 6.14.1 to 6.15.0 in /docs (#43175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.15.0.
Changelog

Sourced from qs's changelog.

6.15.0

  • [New] parse: add strictMerge option to wrap object/primitive conflicts in an array (#425, #122)
  • [Fix] duplicates option should not apply to bracket notation keys (#514)

6.14.2

  • [Fix] parse: mark overflow objects for indexed notation exceeding arrayLimit (#546)
  • [Fix] arrayLimit means max count, not max index, in combine/merge/parseArrayValue
  • [Fix] parse: throw on arrayLimit exceeded with indexed notation when throwOnLimitExceeded is true (#529)
  • [Fix] parse: enforce arrayLimit on comma-parsed values
  • [Fix] parse: fix error message to reflect arrayLimit as max index; remove extraneous comments (#545)
  • [Robustness] avoid .push, use void
  • [readme] document that addQueryPrefix does not add ? to empty output (#418)
  • [readme] clarify parseArrays and arrayLimit documentation (#543)
  • [readme] replace runkit CI badge with shields.io check-runs badge
  • [meta] fix changelog typo (arrayLengtharrayLimit)
  • [actions] fix rebase workflow permissions
Commits
  • d9b4c66 v6.15.0
  • cb41a54 [New] parse: add strictMerge option to wrap object/primitive conflicts in...
  • 88e1563 [Fix] duplicates option should not apply to bracket notation keys
  • 9d441d2 Merge backport release tags v6.0.6–v6.13.3 into main
  • 85cc8ca v6.12.5
  • ffc12aa v6.11.4
  • 0506b11 [actions] update reusable workflows
  • 6a37faf [actions] update reusable workflows
  • 8e8df5a [Fix] fix regressions from robustness refactor
  • d60bab3 v6.10.7
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=qs&package-manager=npm_and_yarn&previous-version=6.14.1&new-version=6.15.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/expo/expo/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/yarn.lock b/docs/yarn.lock index d08929b38ddde2..5ba5aaa77e82c0 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -12787,11 +12787,11 @@ __metadata: linkType: hard "qs@npm:^6.4.0": - version: 6.14.1 - resolution: "qs@npm:6.14.1" + version: 6.15.0 + resolution: "qs@npm:6.15.0" dependencies: side-channel: "npm:^1.1.0" - checksum: 10c0/0e3b22dc451f48ce5940cbbc7c7d9068d895074f8c969c0801ac15c1313d1859c4d738e46dc4da2f498f41a9ffd8c201bd9fb12df67799b827db94cc373d2613 + checksum: 10c0/ff341078a78a991d8a48b4524d52949211447b4b1ad907f489cac0770cbc346a28e47304455c0320e5fb000f8762d64b03331e3b71865f663bf351bcba8cdb4b languageName: node linkType: hard From aef50346596ac6aab187379852627268710cbd7d Mon Sep 17 00:00:00 2001 From: Kadi Kraman Date: Tue, 17 Feb 2026 18:58:35 +0000 Subject: [PATCH 5/5] [docs] Fix lint for Windows (#43223) # Why Resolves ENG-18267 `yarn lint` on Windows errors for the docs site. image This is because `scripts/lint.js` uses `spawn('tsc', ...)` and `spawnSync('eslint', ...)` without `shell: true`. On Windows, Node's spawn cannot resolve `.cmd` shims (like `tsc.cmd` and `eslint.cmd` in `node_modules/.bin`) without a shell. On macOS/Linux this works fine because the binaries are plain scripts with shebangs, but on Windows they're `.cmd` wrappers that require a shell to execute. # How Added shell: true to all three spawn calls in `scripts/lint.js` # Test Plan Run `yarn lint` on Windows to verify it no longer errors # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- docs/scripts/lint.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/scripts/lint.js b/docs/scripts/lint.js index 92dce76bad28d3..3f52869316be2c 100644 --- a/docs/scripts/lint.js +++ b/docs/scripts/lint.js @@ -21,7 +21,10 @@ const eslintArgs = [ function runTsc() { return new Promise(resolve => { const chunks = []; - const proc = spawn('tsc', ['--noEmit', '--pretty'], { stdio: ['ignore', 'pipe', 'pipe'] }); + const proc = spawn('tsc', ['--noEmit', '--pretty'], { + stdio: ['ignore', 'pipe', 'pipe'], + shell: true, + }); proc.stdout.on('data', d => chunks.push(d)); proc.stderr.on('data', d => chunks.push(d)); proc.on('close', status => { @@ -34,6 +37,7 @@ function runTsc() { function runEslint() { const { status, stderr } = spawnSync('eslint', eslintArgs, { stdio: ['inherit', 'inherit', 'pipe'], + shell: true, }); // If ESLint fails with a fatal error, the cache may be stale. Clear it and retry once. @@ -44,6 +48,7 @@ function runEslint() { } catch {} const retry = spawnSync('eslint', eslintArgs, { stdio: ['inherit', 'inherit', 'pipe'], + shell: true, }); return { status: retry.status, stderr: retry.stderr }; }