mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-27 00:51:55 +08:00
feat: point third-party mode at upstream Yu9191 modules
- subscription URLs now resolve directly to upstream modules (gh-proxy mirror / raw), dropping project-owned module and script copies - remove Resources/ThirdPartyProxyModules and ThirdParty/WlocScripts, their pbxproj references, and the contract-test module checks - third-party save sends lon/lat/acc matching the upstream settings script; motion-state simulation is unavailable (upstream lacks it) - surface the iOS 27 beta 6 gs-loc MITM limitation in the app setup and README (zh/en)
This commit is contained in:
@@ -394,8 +394,22 @@ struct FirstSetupView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var thirdPartyMITMWarning: some View {
|
||||||
|
Label {
|
||||||
|
Text("iOS 27 beta 6 起,系统已禁止对 gs-loc.apple.com 进行 MITM 拦截。该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。")
|
||||||
|
.font(.footnote)
|
||||||
|
} icon: {
|
||||||
|
Image(systemName: "exclamationmark.triangle.fill")
|
||||||
|
}
|
||||||
|
.foregroundStyle(.orange)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.padding(10)
|
||||||
|
.background(.orange.opacity(0.12), in: RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
|
||||||
private var thirdPartyClientStep: some View {
|
private var thirdPartyClientStep: some View {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
thirdPartyMITMWarning
|
||||||
if !setup.message.isEmpty {
|
if !setup.message.isEmpty {
|
||||||
Label(setup.message, systemImage: "exclamationmark.triangle.fill")
|
Label(setup.message, systemImage: "exclamationmark.triangle.fill")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
@@ -476,6 +490,7 @@ struct FirstSetupView: View {
|
|||||||
private var thirdPartyImportStep: some View {
|
private var thirdPartyImportStep: some View {
|
||||||
let client = thirdPartyClient.selectedClient
|
let client = thirdPartyClient.selectedClient
|
||||||
return VStack(alignment: .leading, spacing: 16) {
|
return VStack(alignment: .leading, spacing: 16) {
|
||||||
|
thirdPartyMITMWarning
|
||||||
if showThirdPartyRepairReason {
|
if showThirdPartyRepairReason {
|
||||||
Label(
|
Label(
|
||||||
"检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。",
|
"检测到第三方代理连接异常,请检查模块、MITM 和代理连接后重新检测。",
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ An open-source project for **iOS location-service research, software development
|
|||||||
The project uses either an on-device proxy or a third-party proxy client to simulate selected Apple location-service
|
The project uses either an on-device proxy or a third-party proxy client to simulate selected Apple location-service
|
||||||
responses in a controlled test environment.
|
responses in a controlled test environment.
|
||||||
|
|
||||||
|
> ⚠️ **Starting with iOS 27 beta 6, the system blocks MITM interception of `gs-loc.apple.com`.** This project is
|
||||||
|
> temporarily unusable on that version and later betas until a workaround is found.
|
||||||
|
|
||||||
[](project.yml)
|
[](project.yml)
|
||||||
[](project.yml)
|
[](project.yml)
|
||||||
[](Core/go.mod)
|
[](Core/go.mod)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
项目通过本机代理或第三方代理客户端,对 Apple 定位服务的指定响应进行测试环境模拟,帮助开发者验证应用在
|
项目通过本机代理或第三方代理客户端,对 Apple 定位服务的指定响应进行测试环境模拟,帮助开发者验证应用在
|
||||||
不同地理位置和定位场景下的行为。
|
不同地理位置和定位场景下的行为。
|
||||||
|
|
||||||
|
> ⚠️ **iOS 27 beta 6 起,系统已禁止对 `gs-loc.apple.com` 进行 MITM 拦截。** 目前该版本及之后的 beta 版本暂时无法使用本项目,等待后续适配方案。
|
||||||
|
|
||||||
[](project.yml)
|
[](project.yml)
|
||||||
[](project.yml)
|
[](project.yml)
|
||||||
[](Core/go.mod)
|
[](Core/go.mod)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
|
|
||||||
[rewrite_local]
|
|
||||||
^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc url script-response-body https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js
|
|
||||||
^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save url script-echo-response https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[mitm]
|
|
||||||
hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f
|
|
||||||
#!icon=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!openUrl=https://wloc-pages.pages.dev/
|
|
||||||
|
|
||||||
[Argument]
|
|
||||||
longitude = input, "113.94114", tag=经度(在线选点优先)
|
|
||||||
latitude = input, "22.544577", tag=纬度(在线选点优先)
|
|
||||||
accuracy = input, "25", tag=精度(米)
|
|
||||||
randomRadius = input, "0", tag=扰动半径(米,0为关闭)
|
|
||||||
logLevel = select, "info", "off", "error", "warn", "debug", "all", tag=日志级别
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
http-response ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, requires-body=true, binary-body-mode=true, timeout=30, tag=Apple WLOC, argument=[{longitude},{latitude},{accuracy},{randomRadius},{logLevel}]
|
|
||||||
http-request ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js, timeout=10, tag=WLOC Settings
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 (Shadowrocket 小火箭) | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!icon=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
#!category=Tools
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
Apple WLOC = type=http-response,pattern=^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc,requires-body=1,binary-body-mode=1,max-size=0,timeout=30,script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js,argument=longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info
|
|
||||||
WLOC Settings = type=http-request,pattern=^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save,requires-body=0,max-size=0,timeout=10,script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!category=Tools
|
|
||||||
#!arguments=经度:113.94114, 纬度:22.544577, 精度:25, 扰动半径:0, 日志级别:info
|
|
||||||
#!arguments-desc=经度/纬度: 默认坐标(在线选点储存后优先)\n精度: GPS精度(米)\n扰动半径: 每次响应在目标点周围随机偏移的最大距离(米),0为关闭\n日志级别: off/error/warn/info/debug/all\n\n使用方法: 打开选点页面 -> 选位置 -> 储存到设备
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
Apple WLOC = type=http-response, pattern="^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc", requires-body=1, binary-body-mode=1, max-size=0, timeout=30, script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, argument=longitude={{{经度}}}&latitude={{{纬度}}}&accuracy={{{精度}}}&randomRadius={{{扰动半径}}}&logLevel={{{日志级别}}}
|
|
||||||
WLOC Settings = type=http-request, pattern="^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save", requires-body=0, max-size=0, timeout=10, script-path=https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
name: Apple WLOC 定位修改
|
|
||||||
desc: "修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/"
|
|
||||||
author: Yu9191 Rewrite
|
|
||||||
homepage: https://github.com/Yu9191/wloc
|
|
||||||
icon: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
category: Tools
|
|
||||||
|
|
||||||
http:
|
|
||||||
mitm:
|
|
||||||
- "gs-loc.apple.com"
|
|
||||||
- "gs-loc-cn.apple.com"
|
|
||||||
- "gsp-ssl.ls.apple.com"
|
|
||||||
- "bluedot.is.autonavi.com"
|
|
||||||
- "bluedot.is.autonavi.com.gds.alibabadns.com"
|
|
||||||
script:
|
|
||||||
- match: ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc
|
|
||||||
name: WLOC.Location
|
|
||||||
type: response
|
|
||||||
require-body: true
|
|
||||||
binary-mode: true
|
|
||||||
max-size: 0
|
|
||||||
timeout: 30
|
|
||||||
argument: longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info
|
|
||||||
- match: ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save
|
|
||||||
name: WLOC.Settings
|
|
||||||
type: request
|
|
||||||
require-body: false
|
|
||||||
timeout: 10
|
|
||||||
|
|
||||||
script-providers:
|
|
||||||
WLOC.Location:
|
|
||||||
url: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js
|
|
||||||
interval: 86400
|
|
||||||
WLOC.Settings:
|
|
||||||
url: https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
interval: 86400
|
|
||||||
@@ -127,8 +127,7 @@ final class ThirdPartyProxyManager: ObservableObject {
|
|||||||
let response = try await perform(action: .save(
|
let response = try await perform(action: .save(
|
||||||
latitude: wgs84.latitude,
|
latitude: wgs84.latitude,
|
||||||
longitude: wgs84.longitude,
|
longitude: wgs84.longitude,
|
||||||
accuracy: favorite.accuracy,
|
accuracy: favorite.accuracy
|
||||||
motionEnabled: MotionSimulationStore.shared.isEnabled
|
|
||||||
))
|
))
|
||||||
guard response.success else {
|
guard response.success else {
|
||||||
throw ThirdPartyProxyError.rejected(response.error ?? "第三方代理拒绝保存坐标")
|
throw ThirdPartyProxyError.rejected(response.error ?? "第三方代理拒绝保存坐标")
|
||||||
@@ -150,25 +149,13 @@ final class ThirdPartyProxyManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateMotionSimulation(_ enabled: Bool) async throws -> ThirdPartyProxySettingsResponse {
|
func updateMotionSimulation(_ enabled: Bool) async throws -> ThirdPartyProxySettingsResponse {
|
||||||
guard let current = activeSettings,
|
// Upstream Yu9191/wloc scripts do not implement motion-state simulation,
|
||||||
let latitude = current.latitude,
|
// so this can never succeed in third-party mode.
|
||||||
let longitude = current.longitude else {
|
RuntimeLogger.warning("APP", "ThirdPartyProxy", "第三方模式不支持运动状态模拟", details: [
|
||||||
throw ThirdPartyProxyError.rejected("第三方虚拟定位尚未开启")
|
"请求动作": "motion simulation",
|
||||||
}
|
"处理建议": "运动状态模拟仅在 APP模式(内置代理)下可用"
|
||||||
guard await refreshAdvancedFeatureAvailability() else {
|
])
|
||||||
throw ThirdPartyProxyError.moduleOutdated
|
throw ThirdPartyProxyError.moduleOutdated
|
||||||
}
|
|
||||||
let response = try await perform(action: .save(
|
|
||||||
latitude: latitude,
|
|
||||||
longitude: longitude,
|
|
||||||
accuracy: current.accuracy ?? 25,
|
|
||||||
motionEnabled: enabled
|
|
||||||
))
|
|
||||||
guard response.success else {
|
|
||||||
throw ThirdPartyProxyError.rejected(response.error ?? "第三方代理拒绝更新运动状态")
|
|
||||||
}
|
|
||||||
activeSettings = response
|
|
||||||
return response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateConnection() async throws -> ThirdPartyProxySettingsResponse {
|
func validateConnection() async throws -> ThirdPartyProxySettingsResponse {
|
||||||
@@ -251,7 +238,7 @@ final class ThirdPartyProxyManager: ObservableObject {
|
|||||||
|
|
||||||
private enum Action {
|
private enum Action {
|
||||||
case query
|
case query
|
||||||
case save(latitude: Double, longitude: Double, accuracy: Int, motionEnabled: Bool)
|
case save(latitude: Double, longitude: Double, accuracy: Int)
|
||||||
case clear
|
case clear
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,15 +255,11 @@ final class ThirdPartyProxyManager: ObservableObject {
|
|||||||
components.queryItems = [URLQueryItem(name: "action", value: "query")]
|
components.queryItems = [URLQueryItem(name: "action", value: "query")]
|
||||||
case .clear:
|
case .clear:
|
||||||
components.queryItems = [URLQueryItem(name: "action", value: "clear")]
|
components.queryItems = [URLQueryItem(name: "action", value: "clear")]
|
||||||
case .save(let latitude, let longitude, let accuracy, let motionEnabled):
|
case .save(let latitude, let longitude, let accuracy):
|
||||||
components.queryItems = [
|
components.queryItems = [
|
||||||
URLQueryItem(name: "lon", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), longitude)),
|
URLQueryItem(name: "lon", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), longitude)),
|
||||||
URLQueryItem(name: "lat", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), latitude)),
|
URLQueryItem(name: "lat", value: String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), latitude)),
|
||||||
URLQueryItem(name: "acc", value: String(accuracy)),
|
URLQueryItem(name: "acc", value: String(accuracy))
|
||||||
URLQueryItem(
|
|
||||||
name: "motion",
|
|
||||||
value: motionEnabled ? "1" : "0"
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
guard let url = components.url else { throw ThirdPartyProxyError.invalidResponse }
|
guard let url = components.url else { throw ThirdPartyProxyError.invalidResponse }
|
||||||
@@ -309,8 +292,6 @@ final class ThirdPartyProxyManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ThirdPartyProxyClient: String, CaseIterable, Identifiable {
|
enum ThirdPartyProxyClient: String, CaseIterable, Identifiable {
|
||||||
static let moduleSubscriptionVersion = "1.0.1"
|
|
||||||
|
|
||||||
case shadowrocket
|
case shadowrocket
|
||||||
case surge
|
case surge
|
||||||
case quantumultX
|
case quantumultX
|
||||||
@@ -347,26 +328,21 @@ enum ThirdPartyProxyClient: String, CaseIterable, Identifiable {
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
var subscriptionURL: URL {
|
var subscriptionURL: URL {
|
||||||
let url: String
|
// Third-party modules are served directly from the upstream Yu9191/wloc
|
||||||
let directory = ThirdPartyModuleSourceStore.shared.useMirror
|
// repository (mirror via gh-proxy when enabled). We no longer maintain
|
||||||
? "Resources/ThirdPartyProxyModules"
|
// project-owned module copies, so the URL tracks upstream releases.
|
||||||
: "ThirdParty/WlocScripts/modules/direct"
|
let fileName: String
|
||||||
let prefix = ThirdPartyModuleSourceStore.shared.useMirror
|
|
||||||
? "https://gh-proxy.org/https://raw.githubusercontent.com/xweiba/location-spoofer/main/"
|
|
||||||
: "https://raw.githubusercontent.com/xweiba/location-spoofer/main/"
|
|
||||||
switch self {
|
switch self {
|
||||||
case .surge, .egern:
|
case .surge, .egern: fileName = "wloc.sgmodule"
|
||||||
url = "\(prefix)\(directory)/wloc.sgmodule"
|
case .quantumultX: fileName = "wloc.conf"
|
||||||
case .quantumultX:
|
case .loon: fileName = "wloc.lpx"
|
||||||
url = "\(prefix)\(directory)/wloc.conf"
|
case .stash: fileName = "wloc.stoverride"
|
||||||
case .loon:
|
case .shadowrocket: fileName = "wloc.module"
|
||||||
url = "\(prefix)\(directory)/wloc.lpx"
|
|
||||||
case .stash:
|
|
||||||
url = "\(prefix)\(directory)/wloc.stoverride"
|
|
||||||
case .shadowrocket:
|
|
||||||
url = "\(prefix)\(directory)/wloc.module"
|
|
||||||
}
|
}
|
||||||
return URL(string: "\(url)?v=\(Self.moduleSubscriptionVersion)")!
|
let base = ThirdPartyModuleSourceStore.shared.useMirror
|
||||||
|
? "https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/\(fileName)"
|
||||||
|
: "https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/\(fileName)"
|
||||||
|
return URL(string: base)!
|
||||||
}
|
}
|
||||||
|
|
||||||
var launchURL: URL? {
|
var launchURL: URL? {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
|
|||||||
XCTAssertEqual(latitude, wgs84.latitude, accuracy: 0.000_000_01)
|
XCTAssertEqual(latitude, wgs84.latitude, accuracy: 0.000_000_01)
|
||||||
XCTAssertEqual(longitude, wgs84.longitude, accuracy: 0.000_000_01)
|
XCTAssertEqual(longitude, wgs84.longitude, accuracy: 0.000_000_01)
|
||||||
XCTAssertEqual(values["acc"], "20")
|
XCTAssertEqual(values["acc"], "20")
|
||||||
XCTAssertEqual(values["motion"], "0")
|
|
||||||
XCTAssertEqual(manager.connectionState, .connected(active: true))
|
XCTAssertEqual(manager.connectionState, .connected(active: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,16 +143,16 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClientLinksUseProjectOwnedMirrorModulesAndVerificationLabels() {
|
func testClientLinksUseUpstreamModulesAndVerificationLabels() {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
ThirdPartyProxyManager.interceptionHostnamesText,
|
ThirdPartyProxyManager.interceptionHostnamesText,
|
||||||
"gs-loc.apple.com, gs-loc-cn.apple.com"
|
"gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com"
|
||||||
)
|
)
|
||||||
XCTAssertNil(ThirdPartyProxyClient.shadowrocket.verificationText)
|
XCTAssertNil(ThirdPartyProxyClient.shadowrocket.verificationText)
|
||||||
XCTAssertTrue(ThirdPartyProxyClient.surge.verificationText?.contains("尚未验证") == true)
|
XCTAssertTrue(ThirdPartyProxyClient.surge.verificationText?.contains("尚未验证") == true)
|
||||||
XCTAssertEqual(ThirdPartyProxyClient.egern.subscriptionURL, ThirdPartyProxyClient.surge.subscriptionURL)
|
XCTAssertEqual(ThirdPartyProxyClient.egern.subscriptionURL, ThirdPartyProxyClient.surge.subscriptionURL)
|
||||||
XCTAssertTrue(ThirdPartyProxyClient.stash.subscriptionURL.absoluteString.hasPrefix(
|
XCTAssertTrue(ThirdPartyProxyClient.stash.subscriptionURL.absoluteString.hasPrefix(
|
||||||
"https://gh-proxy.org/https://raw.githubusercontent.com/xweiba/location-spoofer/main/"
|
"https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/"
|
||||||
))
|
))
|
||||||
let stashComponents = URLComponents(
|
let stashComponents = URLComponents(
|
||||||
url: ThirdPartyProxyClient.stash.subscriptionURL,
|
url: ThirdPartyProxyClient.stash.subscriptionURL,
|
||||||
@@ -163,10 +162,10 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
|
|||||||
url: ThirdPartyProxyClient.shadowrocket.subscriptionURL,
|
url: ThirdPartyProxyClient.shadowrocket.subscriptionURL,
|
||||||
resolvingAgainstBaseURL: false
|
resolvingAgainstBaseURL: false
|
||||||
)
|
)
|
||||||
XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.stoverride")
|
XCTAssertEqual(stashComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.stoverride")
|
||||||
XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/xweiba/location-spoofer/main/Resources/ThirdPartyProxyModules/wloc.module")
|
XCTAssertEqual(shadowrocketComponents?.path, "/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/wloc.module")
|
||||||
XCTAssertEqual(stashComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion)
|
XCTAssertTrue(stashComponents?.queryItems?.isEmpty ?? true)
|
||||||
XCTAssertEqual(shadowrocketComponents?.queryItems?.first?.value, ThirdPartyProxyClient.moduleSubscriptionVersion)
|
XCTAssertTrue(shadowrocketComponents?.queryItems?.isEmpty ?? true)
|
||||||
XCTAssertEqual(ThirdPartyProxyClient.shadowrocket.launchURL?.scheme, "shadowrocket")
|
XCTAssertEqual(ThirdPartyProxyClient.shadowrocket.launchURL?.scheme, "shadowrocket")
|
||||||
XCTAssertEqual(ThirdPartyProxyClient.surge.launchURL?.scheme, "surge")
|
XCTAssertEqual(ThirdPartyProxyClient.surge.launchURL?.scheme, "surge")
|
||||||
XCTAssertEqual(ThirdPartyProxyClient.quantumultX.launchURL?.scheme, "quantumult-x")
|
XCTAssertEqual(ThirdPartyProxyClient.quantumultX.launchURL?.scheme, "quantumult-x")
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ MANAGER="$ROOT/Shared/ThirdPartyProxyManager.swift"
|
|||||||
CONTENT="$ROOT/App/ContentView.swift"
|
CONTENT="$ROOT/App/ContentView.swift"
|
||||||
SETUP="$ROOT/App/FirstSetupView.swift"
|
SETUP="$ROOT/App/FirstSetupView.swift"
|
||||||
SETTINGS="$ROOT/App/SettingsView.swift"
|
SETTINGS="$ROOT/App/SettingsView.swift"
|
||||||
MODULES="$ROOT/Resources/ThirdPartyProxyModules"
|
|
||||||
|
|
||||||
grep -q 'return "APP模式"' "$MODE" || fail "APP mode display name is missing"
|
grep -q 'return "APP模式"' "$MODE" || fail "APP mode display name is missing"
|
||||||
grep -q 'return "第三方代理模式"' "$MODE" || fail "third-party mode display name is missing"
|
grep -q 'return "第三方代理模式"' "$MODE" || fail "third-party mode display name is missing"
|
||||||
@@ -38,14 +37,8 @@ grep -Fq '保存:GET ?lon=<经度>&lat=<纬度>&acc=<精度>' "$SETUP" \
|
|||||||
grep -Fq '清除:GET ?action=clear' "$SETUP" \
|
grep -Fq '清除:GET ?action=clear' "$SETUP" \
|
||||||
|| fail "client integration guidance must document the clear action"
|
|| fail "client integration guidance must document the clear action"
|
||||||
|
|
||||||
for file in wloc.module wloc.sgmodule wloc.conf wloc.lpx wloc.stoverride; do
|
grep -q 'Yu9191/wloc/refs/heads/main/modules' "$MANAGER" \
|
||||||
test -s "$MODULES/$file" || fail "missing bundled module: $file"
|
|| fail "third-party subscription must point at upstream Yu9191 modules"
|
||||||
grep -q 'gs-loc.apple.com' "$MODULES/$file" \
|
|
||||||
|| fail "$file must include gs-loc.apple.com in its MITM hostnames"
|
|
||||||
grep -q 'gs-loc-cn.apple.com' "$MODULES/$file" \
|
|
||||||
|| fail "$file must include gs-loc-cn.apple.com in its MITM hostnames"
|
|
||||||
done
|
|
||||||
|
|
||||||
grep -q 'wloc.sgmodule' "$MANAGER" || fail "Surge/Egern module mapping is missing"
|
grep -q 'wloc.sgmodule' "$MANAGER" || fail "Surge/Egern module mapping is missing"
|
||||||
grep -q 'wloc.stoverride' "$MANAGER" || fail "Stash must use .stoverride directly"
|
grep -q 'wloc.stoverride' "$MANAGER" || fail "Stash must use .stoverride directly"
|
||||||
grep -q 'shadowrocket://' "$MANAGER" || fail "Shadowrocket launch URL is missing"
|
grep -q 'shadowrocket://' "$MANAGER" || fail "Shadowrocket launch URL is missing"
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# Third-Party Notices
|
|
||||||
|
|
||||||
The generated proxy scripts bundle:
|
|
||||||
|
|
||||||
- `pako` 2.1.0, Copyright (C) 2014-2017 Vitaly Puzrin and contributors, MIT License.
|
|
||||||
- `esbuild` is used only as a development/build dependency and is distributed under the MIT License.
|
|
||||||
Vendored
-19
@@ -1,19 +0,0 @@
|
|||||||
import { build } from "esbuild";
|
|
||||||
import { mkdir } from "node:fs/promises";
|
|
||||||
|
|
||||||
await mkdir(new URL("./dist/v1/", import.meta.url), { recursive: true });
|
|
||||||
|
|
||||||
for (const [entry, outfile] of [
|
|
||||||
["src/response-entry.js", "dist/v1/wloc.js"],
|
|
||||||
["src/settings-entry.js", "dist/v1/wloc-settings.js"]
|
|
||||||
]) {
|
|
||||||
await build({
|
|
||||||
entryPoints: [entry],
|
|
||||||
outfile,
|
|
||||||
bundle: true,
|
|
||||||
format: "iife",
|
|
||||||
target: ["es2017"],
|
|
||||||
minify: true,
|
|
||||||
legalComments: "eof"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
-2
File diff suppressed because one or more lines are too long
-11
@@ -1,11 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
|
|
||||||
[rewrite_local]
|
|
||||||
^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc url script-response-body https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js
|
|
||||||
^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save url script-echo-response https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[mitm]
|
|
||||||
hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f
|
|
||||||
#!icon=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!openUrl=https://wloc-pages.pages.dev/
|
|
||||||
|
|
||||||
[Argument]
|
|
||||||
longitude = input, "113.94114", tag=经度(在线选点优先)
|
|
||||||
latitude = input, "22.544577", tag=纬度(在线选点优先)
|
|
||||||
accuracy = input, "25", tag=精度(米)
|
|
||||||
randomRadius = input, "0", tag=扰动半径(米,0为关闭)
|
|
||||||
logLevel = select, "info", "off", "error", "warn", "debug", "all", tag=日志级别
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
http-response ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, requires-body=true, binary-body-mode=true, timeout=30, tag=Apple WLOC, argument=[{longitude},{latitude},{accuracy},{randomRadius},{logLevel}]
|
|
||||||
http-request ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js, timeout=10, tag=WLOC Settings
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 (Shadowrocket 小火箭) | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!icon=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
#!category=Tools
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
Apple WLOC = type=http-response,pattern=^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc,requires-body=1,binary-body-mode=1,max-size=0,timeout=30,script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js,argument=longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info
|
|
||||||
WLOC Settings = type=http-request,pattern=^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save,requires-body=0,max-size=0,timeout=10,script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!name=Apple WLOC 定位修改
|
|
||||||
#!desc=修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/
|
|
||||||
#!author=Yu9191 Rewrite
|
|
||||||
#!homepage=https://github.com/Yu9191/wloc
|
|
||||||
#!category=Tools
|
|
||||||
#!arguments=经度:113.94114, 纬度:22.544577, 精度:25, 扰动半径:0, 日志级别:info
|
|
||||||
#!arguments-desc=经度/纬度: 默认坐标(在线选点储存后优先)\n精度: GPS精度(米)\n扰动半径: 每次响应在目标点周围随机偏移的最大距离(米),0为关闭\n日志级别: off/error/warn/info/debug/all\n\n使用方法: 打开选点页面 -> 选位置 -> 储存到设备
|
|
||||||
|
|
||||||
[Script]
|
|
||||||
Apple WLOC = type=http-response, pattern="^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc", requires-body=1, binary-body-mode=1, max-size=0, timeout=30, script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js, argument=longitude={{{经度}}}&latitude={{{纬度}}}&accuracy={{{精度}}}&randomRadius={{{扰动半径}}}&logLevel={{{日志级别}}}
|
|
||||||
WLOC Settings = type=http-request, pattern="^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save", requires-body=0, max-size=0, timeout=10, script-path=https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
|
|
||||||
[MITM]
|
|
||||||
hostname = %APPEND% gs-loc.apple.com, gs-loc-cn.apple.com, gsp-ssl.ls.apple.com, bluedot.is.autonavi.com, bluedot.is.autonavi.com.gds.alibabadns.com
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
name: Apple WLOC 定位修改
|
|
||||||
desc: "修改 Apple 网络定位返回坐标 | 快捷指令(推荐): 设置地理位置 https://www.icloud.com/shortcuts/a82717d8fdad4e6280866fcf911173f7 清理恢复位置 https://www.icloud.com/shortcuts/f42632d406504f24a2cd163af4fe012f | 选点页面: https://wloc-pages.pages.dev/"
|
|
||||||
author: Yu9191 Rewrite
|
|
||||||
homepage: https://github.com/Yu9191/wloc
|
|
||||||
icon: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/wloc.jpg
|
|
||||||
category: Tools
|
|
||||||
|
|
||||||
http:
|
|
||||||
mitm:
|
|
||||||
- "gs-loc.apple.com"
|
|
||||||
- "gs-loc-cn.apple.com"
|
|
||||||
- "gsp-ssl.ls.apple.com"
|
|
||||||
- "bluedot.is.autonavi.com"
|
|
||||||
- "bluedot.is.autonavi.com.gds.alibabadns.com"
|
|
||||||
script:
|
|
||||||
- match: ^https?:\/\/(?:gs-loc(?:-cn)?\.apple\.com|gsp-ssl\.ls\.apple\.com|bluedot\.is\.autonavi\.com(?:\.gds\.alibabadns\.com)?)\/clls\/wloc
|
|
||||||
name: WLOC.Location
|
|
||||||
type: response
|
|
||||||
require-body: true
|
|
||||||
binary-mode: true
|
|
||||||
max-size: 0
|
|
||||||
timeout: 30
|
|
||||||
argument: longitude=113.94114&latitude=22.544577&accuracy=25&randomRadius=0&logLevel=info
|
|
||||||
- match: ^https?:\/\/gs-loc(-cn)?\.apple\.com\/wloc-settings\/save
|
|
||||||
name: WLOC.Settings
|
|
||||||
type: request
|
|
||||||
require-body: false
|
|
||||||
timeout: 10
|
|
||||||
|
|
||||||
script-providers:
|
|
||||||
WLOC.Location:
|
|
||||||
url: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc.js
|
|
||||||
interval: 86400
|
|
||||||
WLOC.Settings:
|
|
||||||
url: https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/dist/wloc-settings.js
|
|
||||||
interval: 86400
|
|
||||||
-508
@@ -1,508 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@location-spoofer/wloc-scripts",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "@location-spoofer/wloc-scripts",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"pako": "2.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"esbuild": "0.25.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"aix"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
|
|
||||||
"cpu": [
|
|
||||||
"mips64el"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openharmony"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"sunos"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-x64": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
|
||||||
"version": "0.25.8",
|
|
||||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
|
|
||||||
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"esbuild": "bin/esbuild"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@esbuild/aix-ppc64": "0.25.8",
|
|
||||||
"@esbuild/android-arm": "0.25.8",
|
|
||||||
"@esbuild/android-arm64": "0.25.8",
|
|
||||||
"@esbuild/android-x64": "0.25.8",
|
|
||||||
"@esbuild/darwin-arm64": "0.25.8",
|
|
||||||
"@esbuild/darwin-x64": "0.25.8",
|
|
||||||
"@esbuild/freebsd-arm64": "0.25.8",
|
|
||||||
"@esbuild/freebsd-x64": "0.25.8",
|
|
||||||
"@esbuild/linux-arm": "0.25.8",
|
|
||||||
"@esbuild/linux-arm64": "0.25.8",
|
|
||||||
"@esbuild/linux-ia32": "0.25.8",
|
|
||||||
"@esbuild/linux-loong64": "0.25.8",
|
|
||||||
"@esbuild/linux-mips64el": "0.25.8",
|
|
||||||
"@esbuild/linux-ppc64": "0.25.8",
|
|
||||||
"@esbuild/linux-riscv64": "0.25.8",
|
|
||||||
"@esbuild/linux-s390x": "0.25.8",
|
|
||||||
"@esbuild/linux-x64": "0.25.8",
|
|
||||||
"@esbuild/netbsd-arm64": "0.25.8",
|
|
||||||
"@esbuild/netbsd-x64": "0.25.8",
|
|
||||||
"@esbuild/openbsd-arm64": "0.25.8",
|
|
||||||
"@esbuild/openbsd-x64": "0.25.8",
|
|
||||||
"@esbuild/openharmony-arm64": "0.25.8",
|
|
||||||
"@esbuild/sunos-x64": "0.25.8",
|
|
||||||
"@esbuild/win32-arm64": "0.25.8",
|
|
||||||
"@esbuild/win32-ia32": "0.25.8",
|
|
||||||
"@esbuild/win32-x64": "0.25.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pako": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
|
||||||
"license": "(MIT AND Zlib)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-16
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@location-spoofer/wloc-scripts",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build": "node build.mjs",
|
|
||||||
"test": "node --test"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"pako": "2.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"esbuild": "0.25.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-263
@@ -1,263 +0,0 @@
|
|||||||
export const MOTION_ACTIVITY_TYPE = 63;
|
|
||||||
export const MOTION_ACTIVITY_CONFIDENCE = 467;
|
|
||||||
export const WLOC_MARKER = Uint8Array.from([0, 0, 0, 1, 0, 0]);
|
|
||||||
const UINT32_RANGE = 0x100000000;
|
|
||||||
|
|
||||||
const concat = (...parts) => {
|
|
||||||
const length = parts.reduce((sum, part) => sum + part.length, 0);
|
|
||||||
const out = new Uint8Array(length);
|
|
||||||
let offset = 0;
|
|
||||||
for (const part of parts) {
|
|
||||||
out.set(part, offset);
|
|
||||||
offset += part.length;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
const equal = (left, right) =>
|
|
||||||
left.length === right.length && left.every((value, index) => value === right[index]);
|
|
||||||
|
|
||||||
function readVarint(data, offset) {
|
|
||||||
let value = 0;
|
|
||||||
let multiplier = 1;
|
|
||||||
for (let index = 0; index < 10 && offset + index < data.length; index += 1) {
|
|
||||||
const byte = data[offset + index];
|
|
||||||
const chunk = byte & 0x7f;
|
|
||||||
if (value !== null && chunk <= Math.floor((Number.MAX_SAFE_INTEGER - value) / multiplier)) {
|
|
||||||
value += chunk * multiplier;
|
|
||||||
} else {
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
if ((byte & 0x80) === 0) return { value, next: offset + index + 1 };
|
|
||||||
multiplier *= 0x80;
|
|
||||||
}
|
|
||||||
throw new Error("invalid varint");
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeVarint(input) {
|
|
||||||
const value = Math.trunc(Number(input));
|
|
||||||
if (!Number.isSafeInteger(value)) throw new Error("varint value is not a safe integer");
|
|
||||||
let low = value >>> 0;
|
|
||||||
let high = Math.floor(value / UINT32_RANGE) >>> 0;
|
|
||||||
const bytes = [];
|
|
||||||
do {
|
|
||||||
const byte = low & 0x7f;
|
|
||||||
low = ((low >>> 7) | ((high & 0x7f) << 25)) >>> 0;
|
|
||||||
high >>>= 7;
|
|
||||||
const hasMore = high !== 0 || low !== 0;
|
|
||||||
bytes.push(byte | (hasMore ? 0x80 : 0));
|
|
||||||
} while (high !== 0 || low !== 0);
|
|
||||||
return Uint8Array.from(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeTag = (number, wireType) => writeVarint((number << 3) | wireType);
|
|
||||||
|
|
||||||
function writeLengthDelimited(number, value) {
|
|
||||||
return concat(writeTag(number, 2), writeVarint(value.length), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseFields(data) {
|
|
||||||
const fields = [];
|
|
||||||
let offset = 0;
|
|
||||||
while (offset < data.length) {
|
|
||||||
const start = offset;
|
|
||||||
const tag = readVarint(data, offset);
|
|
||||||
offset = tag.next;
|
|
||||||
if (!Number.isSafeInteger(tag.value)) throw new Error("protobuf tag is too large");
|
|
||||||
const number = Math.floor(tag.value / 8);
|
|
||||||
const wireType = tag.value & 7;
|
|
||||||
if (number === 0) throw new Error("invalid protobuf field 0");
|
|
||||||
let value;
|
|
||||||
if (wireType === 0) {
|
|
||||||
const item = readVarint(data, offset);
|
|
||||||
value = data.slice(offset, item.next);
|
|
||||||
offset = item.next;
|
|
||||||
} else if (wireType === 1) {
|
|
||||||
if (offset + 8 > data.length) throw new Error("truncated fixed64");
|
|
||||||
value = data.slice(offset, offset + 8);
|
|
||||||
offset += 8;
|
|
||||||
} else if (wireType === 2) {
|
|
||||||
const length = readVarint(data, offset);
|
|
||||||
offset = length.next;
|
|
||||||
const size = length.value;
|
|
||||||
if (!Number.isSafeInteger(size) || offset + size > data.length) {
|
|
||||||
throw new Error("truncated length-delimited field");
|
|
||||||
}
|
|
||||||
value = data.slice(offset, offset + size);
|
|
||||||
offset += size;
|
|
||||||
} else if (wireType === 5) {
|
|
||||||
if (offset + 4 > data.length) throw new Error("truncated fixed32");
|
|
||||||
value = data.slice(offset, offset + 4);
|
|
||||||
offset += 4;
|
|
||||||
} else {
|
|
||||||
throw new Error(`unsupported wire type ${wireType}`);
|
|
||||||
}
|
|
||||||
fields.push({ number, wireType, value, raw: data.slice(start, offset) });
|
|
||||||
}
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchLocation(data, config, stats) {
|
|
||||||
const fields = parseFields(data);
|
|
||||||
if (!fields.some((field) => field.number === 1 && field.wireType === 0) ||
|
|
||||||
!fields.some((field) => field.number === 2 && field.wireType === 0)) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
let hasMotionType = false;
|
|
||||||
let hasMotionConfidence = false;
|
|
||||||
const parts = fields.map((field) => {
|
|
||||||
if (field.wireType !== 0) return field.raw;
|
|
||||||
if (field.number === 1) return concat(writeTag(1, 0), writeVarint(Math.round(config.latitude * 1e8)));
|
|
||||||
if (field.number === 2) return concat(writeTag(2, 0), writeVarint(Math.round(config.longitude * 1e8)));
|
|
||||||
if (field.number === 3) return concat(writeTag(3, 0), writeVarint(config.accuracy));
|
|
||||||
if (config.motionSimulationEnabled && field.number === 11) {
|
|
||||||
hasMotionType = true;
|
|
||||||
return concat(writeTag(11, 0), writeVarint(MOTION_ACTIVITY_TYPE));
|
|
||||||
}
|
|
||||||
if (config.motionSimulationEnabled && field.number === 12) {
|
|
||||||
hasMotionConfidence = true;
|
|
||||||
return concat(writeTag(12, 0), writeVarint(MOTION_ACTIVITY_CONFIDENCE));
|
|
||||||
}
|
|
||||||
return field.raw;
|
|
||||||
});
|
|
||||||
if (config.motionSimulationEnabled && !hasMotionType) {
|
|
||||||
parts.push(concat(writeTag(11, 0), writeVarint(MOTION_ACTIVITY_TYPE)));
|
|
||||||
}
|
|
||||||
if (config.motionSimulationEnabled && !hasMotionConfidence) {
|
|
||||||
parts.push(concat(writeTag(12, 0), writeVarint(MOTION_ACTIVITY_CONFIDENCE)));
|
|
||||||
}
|
|
||||||
const out = concat(...parts);
|
|
||||||
if (!equal(out, data)) stats.locations += 1;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchWifi(data, config, stats) {
|
|
||||||
const fields = parseFields(data);
|
|
||||||
const hasMac = fields.some((field) =>
|
|
||||||
field.number === 1 && field.wireType === 2 &&
|
|
||||||
/^[0-9a-fA-F]{1,2}(:[0-9a-fA-F]{1,2}){5}$/.test(
|
|
||||||
Array.from(field.value, (byte) => String.fromCharCode(byte)).join("")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (!hasMac) return data;
|
|
||||||
let changed = false;
|
|
||||||
const parts = fields.map((field) => {
|
|
||||||
if (field.number !== 2 || field.wireType !== 2) return field.raw;
|
|
||||||
const value = patchLocation(field.value, config, stats);
|
|
||||||
changed ||= !equal(value, field.value);
|
|
||||||
return writeLengthDelimited(2, value);
|
|
||||||
});
|
|
||||||
if (changed) stats.wifi += 1;
|
|
||||||
return concat(...parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchCell(data, config, stats) {
|
|
||||||
let changed = false;
|
|
||||||
const parts = parseFields(data).map((field) => {
|
|
||||||
if (field.number !== 5 || field.wireType !== 2) return field.raw;
|
|
||||||
const value = patchLocation(field.value, config, stats);
|
|
||||||
changed ||= !equal(value, field.value);
|
|
||||||
return writeLengthDelimited(5, value);
|
|
||||||
});
|
|
||||||
if (changed) stats.cell += 1;
|
|
||||||
return concat(...parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchPayload(data, config, stats = { wifi: 0, cell: 0, locations: 0 }) {
|
|
||||||
const parts = parseFields(data).map((field) => {
|
|
||||||
if (field.number === 2 && field.wireType === 2) {
|
|
||||||
return writeLengthDelimited(2, patchWifi(field.value, config, stats));
|
|
||||||
}
|
|
||||||
if ((field.number === 22 || field.number === 24) && field.wireType === 2) {
|
|
||||||
return writeLengthDelimited(field.number, patchCell(field.value, config, stats));
|
|
||||||
}
|
|
||||||
return field.raw;
|
|
||||||
});
|
|
||||||
return { data: concat(...parts), stats };
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint16 = (data, offset) => (data[offset] << 8) | data[offset + 1];
|
|
||||||
const uint32 = (data, offset) =>
|
|
||||||
((data[offset] * 0x1000000) + (data[offset + 1] << 16) +
|
|
||||||
(data[offset + 2] << 8) + data[offset + 3]) >>> 0;
|
|
||||||
const writeUint16 = (value) => Uint8Array.from([(value >>> 8) & 0xff, value & 0xff]);
|
|
||||||
const writeUint32 = (value) => Uint8Array.from([
|
|
||||||
(value >>> 24) & 0xff, (value >>> 16) & 0xff, (value >>> 8) & 0xff, value & 0xff
|
|
||||||
]);
|
|
||||||
|
|
||||||
function findBytes(data, marker) {
|
|
||||||
outer: for (let offset = 0; offset <= data.length - marker.length; offset += 1) {
|
|
||||||
for (let index = 0; index < marker.length; index += 1) {
|
|
||||||
if (data[offset + index] !== marker[index]) continue outer;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchARPC(body, config) {
|
|
||||||
if (body.length < 2) throw new Error("short ARPC");
|
|
||||||
let offset = 2;
|
|
||||||
for (let index = 0; index < 3; index += 1) {
|
|
||||||
if (offset + 2 > body.length) throw new Error("truncated ARPC string");
|
|
||||||
offset += 2 + uint16(body, offset);
|
|
||||||
}
|
|
||||||
const lengthOffset = offset + 4;
|
|
||||||
const payloadOffset = lengthOffset + 4;
|
|
||||||
if (payloadOffset > body.length) throw new Error("truncated ARPC header");
|
|
||||||
const length = uint32(body, lengthOffset);
|
|
||||||
if (!length || payloadOffset + length > body.length) throw new Error("invalid ARPC length");
|
|
||||||
const patched = patchPayload(body.slice(payloadOffset, payloadOffset + length), config);
|
|
||||||
if (equal(patched.data, body.slice(payloadOffset, payloadOffset + length))) throw new Error("unchanged ARPC");
|
|
||||||
return { data: concat(body.slice(0, lengthOffset), writeUint32(patched.data.length),
|
|
||||||
patched.data, body.slice(payloadOffset + length)), stats: patched.stats };
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchMarker(body, config) {
|
|
||||||
const markerOffset = findBytes(body, WLOC_MARKER);
|
|
||||||
if (markerOffset < 0) throw new Error("marker not found");
|
|
||||||
const lengthOffset = markerOffset + WLOC_MARKER.length;
|
|
||||||
const payloadOffset = lengthOffset + 2;
|
|
||||||
const length = uint16(body, lengthOffset);
|
|
||||||
if (!length || payloadOffset + length > body.length) throw new Error("invalid marker length");
|
|
||||||
const patched = patchPayload(body.slice(payloadOffset, payloadOffset + length), config);
|
|
||||||
if (patched.data.length > 65535 || equal(patched.data, body.slice(payloadOffset, payloadOffset + length))) {
|
|
||||||
throw new Error("unchanged marker");
|
|
||||||
}
|
|
||||||
return { data: concat(body.slice(0, lengthOffset), writeUint16(patched.data.length),
|
|
||||||
patched.data, body.slice(payloadOffset + length)), stats: patched.stats };
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchSynthetic(body, offset, config) {
|
|
||||||
if (offset + 10 > body.length) throw new Error("short frame");
|
|
||||||
const length = uint16(body, offset + 8);
|
|
||||||
if (!length || offset + 10 + length > body.length) throw new Error("invalid frame");
|
|
||||||
const patched = patchPayload(body.slice(offset + 10, offset + 10 + length), config);
|
|
||||||
if (patched.data.length > 65535 || equal(patched.data, body.slice(offset + 10, offset + 10 + length))) {
|
|
||||||
throw new Error("unchanged frame");
|
|
||||||
}
|
|
||||||
return { data: concat(body.slice(0, offset + 8), writeUint16(patched.data.length),
|
|
||||||
patched.data, body.slice(offset + 10 + length)), stats: patched.stats };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patchWlocBody(body, config) {
|
|
||||||
for (const patcher of [patchARPC, patchMarker]) {
|
|
||||||
try { return patcher(body, config); } catch {}
|
|
||||||
}
|
|
||||||
const offsets = [...new Set([0, 2, 4, 6, 8, 10, 12, 14, 16,
|
|
||||||
...Array.from({ length: Math.min(96, Math.max(0, body.length - 10)) + 1 }, (_, index) => index)])];
|
|
||||||
for (const offset of offsets) {
|
|
||||||
try { return patchSynthetic(body, offset, config); } catch {}
|
|
||||||
}
|
|
||||||
for (let offset = 0; offset <= Math.min(256, body.length); offset += 1) {
|
|
||||||
try {
|
|
||||||
const patched = patchPayload(body.slice(offset), config);
|
|
||||||
if (!equal(patched.data, body.slice(offset))) {
|
|
||||||
return { data: concat(body.slice(0, offset), patched.data), stats: patched.stats };
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
throw new Error("no patchable wloc payload found");
|
|
||||||
}
|
|
||||||
|
|
||||||
export const internals = { concat, writeVarint, writeTag, writeLengthDelimited, equal };
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
import { ungzip } from "pako";
|
|
||||||
import { patchWlocBody } from "./core.js";
|
|
||||||
import {
|
|
||||||
STORAGE_KEY, finishBinary, finishPassthrough, readPersistent, responseBytes
|
|
||||||
} from "./runtime.js";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const settings = readPersistent(STORAGE_KEY);
|
|
||||||
const input = responseBytes();
|
|
||||||
if (!settings || !settings.enabled || !input.length) {
|
|
||||||
finishPassthrough();
|
|
||||||
} else {
|
|
||||||
const isGzip = input.length >= 2 && input[0] === 0x1f && input[1] === 0x8b;
|
|
||||||
const body = isGzip ? ungzip(input) : input;
|
|
||||||
const patched = patchWlocBody(body, {
|
|
||||||
latitude: Number(settings.latitude),
|
|
||||||
longitude: Number(settings.longitude),
|
|
||||||
accuracy: Number(settings.accuracy != null ? settings.accuracy : 25),
|
|
||||||
motionSimulationEnabled: settings.motionSimulationEnabled === true
|
|
||||||
});
|
|
||||||
finishBinary(patched.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`[Location Spoofer] ${error && error.message ? error.message : error}`);
|
|
||||||
finishPassthrough();
|
|
||||||
}
|
|
||||||
-109
@@ -1,109 +0,0 @@
|
|||||||
export const MODULE_VERSION = "1.0.0";
|
|
||||||
export const PROTOCOL_VERSION = 1;
|
|
||||||
export const CAPABILITIES = [
|
|
||||||
"wifi", "cellTower", "arpc", "marker", "synthetic", "bare", "motionSimulation"
|
|
||||||
];
|
|
||||||
export const STORAGE_KEY = "locationSpoofer.settings.v1";
|
|
||||||
|
|
||||||
export function environment() {
|
|
||||||
if (typeof $task !== "undefined") return "quantumultX";
|
|
||||||
if (typeof $loon !== "undefined") return "loon";
|
|
||||||
if (typeof $rocket !== "undefined") return "shadowrocket";
|
|
||||||
if (typeof Egern !== "undefined") return "egern";
|
|
||||||
if (typeof $environment !== "undefined" && $environment["stash-version"]) return "stash";
|
|
||||||
if (typeof $environment !== "undefined" && $environment["surge-version"]) return "surge";
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readPersistent(key) {
|
|
||||||
const raw = environment() === "quantumultX"
|
|
||||||
? $prefs.valueForKey(key)
|
|
||||||
: $persistentStore.read(key);
|
|
||||||
if (!raw) return null;
|
|
||||||
try { return JSON.parse(raw); } catch { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writePersistent(key, value) {
|
|
||||||
const raw = value == null ? "" : JSON.stringify(value);
|
|
||||||
return environment() === "quantumultX"
|
|
||||||
? $prefs.setValueForKey(raw, key)
|
|
||||||
: $persistentStore.write(raw, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function responseBytes() {
|
|
||||||
const response = typeof $response === "undefined" ? null : $response;
|
|
||||||
const value = response && response.bodyBytes != null ? response.bodyBytes : response && response.body;
|
|
||||||
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
||||||
if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
||||||
if (typeof value === "string") {
|
|
||||||
return Uint8Array.from(value, (character) => character.charCodeAt(0) & 0xff);
|
|
||||||
}
|
|
||||||
return new Uint8Array();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanHeaders(headers, length) {
|
|
||||||
const out = Object.assign({}, headers || {});
|
|
||||||
for (const name of ["Content-Encoding", "content-encoding", "Transfer-Encoding", "transfer-encoding"]) {
|
|
||||||
delete out[name];
|
|
||||||
}
|
|
||||||
out["Content-Length"] = String(length);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function finishBinary(bytes) {
|
|
||||||
const response = typeof $response === "undefined" ? {} : $response;
|
|
||||||
const headers = cleanHeaders(response.headers, bytes.length);
|
|
||||||
const env = environment();
|
|
||||||
if (env === "quantumultX") {
|
|
||||||
delete headers["Content-Length"];
|
|
||||||
$done({ status: "HTTP/1.1 200 OK", headers, bodyBytes: bytes.buffer });
|
|
||||||
} else if (env === "stash") {
|
|
||||||
$done(Object.assign({}, response, { status: 200, headers, body: bytes }));
|
|
||||||
} else {
|
|
||||||
$done({ response: Object.assign({}, response, { status: 200, headers, body: bytes }) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function finishPassthrough() {
|
|
||||||
$done({});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function finishJSON(value) {
|
|
||||||
const response = {
|
|
||||||
status: 200,
|
|
||||||
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
||||||
body: JSON.stringify(value)
|
|
||||||
};
|
|
||||||
if (environment() === "quantumultX") {
|
|
||||||
$done(Object.assign({}, response, { status: "HTTP/1.1 200 OK" }));
|
|
||||||
} else if (environment() === "stash") {
|
|
||||||
$done(response);
|
|
||||||
} else {
|
|
||||||
$done({ response });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function queryParameters(url) {
|
|
||||||
const query = url.split("?")[1] || "";
|
|
||||||
const values = {};
|
|
||||||
query.split("&").forEach((item) => {
|
|
||||||
if (!item) return;
|
|
||||||
const separator = item.indexOf("=");
|
|
||||||
const rawKey = separator < 0 ? item : item.slice(0, separator);
|
|
||||||
const rawValue = separator < 0 ? "" : item.slice(separator + 1);
|
|
||||||
let key = rawKey;
|
|
||||||
let value = rawValue;
|
|
||||||
try { key = decodeURIComponent(rawKey.replace(/\+/g, " ")); } catch {}
|
|
||||||
try { value = decodeURIComponent(rawValue.replace(/\+/g, " ")); } catch {}
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(values, key)) values[key] = value;
|
|
||||||
});
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requestPath(url) {
|
|
||||||
const withoutQuery = url.split("?")[0];
|
|
||||||
const scheme = withoutQuery.indexOf("://");
|
|
||||||
if (scheme < 0) return withoutQuery;
|
|
||||||
const path = withoutQuery.indexOf("/", scheme + 3);
|
|
||||||
return path < 0 ? "/" : withoutQuery.slice(path);
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
import {
|
|
||||||
CAPABILITIES, MODULE_VERSION, PROTOCOL_VERSION, STORAGE_KEY,
|
|
||||||
finishJSON, queryParameters, readPersistent, requestPath, writePersistent
|
|
||||||
} from "./runtime.js";
|
|
||||||
|
|
||||||
const url = typeof $request === "undefined" ? "" : ($request.url || "");
|
|
||||||
const path = requestPath(url);
|
|
||||||
const parameters = queryParameters(url);
|
|
||||||
|
|
||||||
if (path === "/wloc-settings/version") {
|
|
||||||
finishJSON({
|
|
||||||
success: true,
|
|
||||||
moduleVersion: MODULE_VERSION,
|
|
||||||
protocolVersion: PROTOCOL_VERSION,
|
|
||||||
capabilities: CAPABILITIES
|
|
||||||
});
|
|
||||||
} else if (parameters.action === "query") {
|
|
||||||
const settings = readPersistent(STORAGE_KEY);
|
|
||||||
finishJSON(settings && settings.enabled
|
|
||||||
? { success: true, longitude: settings.longitude, latitude: settings.latitude,
|
|
||||||
accuracy: settings.accuracy, motionSimulationEnabled: settings.motionSimulationEnabled === true }
|
|
||||||
: { success: false, error: "无已保存的坐标" });
|
|
||||||
} else if (parameters.action === "clear") {
|
|
||||||
writePersistent(STORAGE_KEY, null);
|
|
||||||
finishJSON({ success: true });
|
|
||||||
} else {
|
|
||||||
const longitude = Number(parameters.lon != null ? parameters.lon : parameters.longitude);
|
|
||||||
const latitude = Number(parameters.lat != null ? parameters.lat : parameters.latitude);
|
|
||||||
const accuracy = Number(parameters.acc != null
|
|
||||||
? parameters.acc
|
|
||||||
: (parameters.accuracy != null ? parameters.accuracy : 25));
|
|
||||||
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
|
|
||||||
finishJSON({ success: false, error: "缺少 lon/lat 参数" });
|
|
||||||
} else {
|
|
||||||
const settings = {
|
|
||||||
enabled: true,
|
|
||||||
longitude,
|
|
||||||
latitude,
|
|
||||||
accuracy,
|
|
||||||
motionSimulationEnabled: parameters.motion === "1"
|
|
||||||
};
|
|
||||||
const success = writePersistent(STORAGE_KEY, settings);
|
|
||||||
finishJSON(success
|
|
||||||
? { success: true, longitude, latitude, accuracy,
|
|
||||||
motionSimulationEnabled: settings.motionSimulationEnabled }
|
|
||||||
: { success: false, error: "保存配置失败" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import test from "node:test";
|
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { readFile } from "node:fs/promises";
|
|
||||||
import vm from "node:vm";
|
|
||||||
|
|
||||||
const bundles = [
|
|
||||||
new URL("../dist/v1/wloc.js", import.meta.url),
|
|
||||||
new URL("../dist/v1/wloc-settings.js", import.meta.url)
|
|
||||||
];
|
|
||||||
|
|
||||||
test("generated bundles avoid newer JavaScriptCore runtime requirements", async () => {
|
|
||||||
for (const bundle of bundles) {
|
|
||||||
const source = await readFile(bundle, "utf8");
|
|
||||||
for (const unsupported of [
|
|
||||||
/\bBigInt\b/,
|
|
||||||
/\bglobalThis\b/,
|
|
||||||
/\bURLSearchParams\b/,
|
|
||||||
/\bObject\.fromEntries\b/,
|
|
||||||
/\?\./,
|
|
||||||
/\?\?/
|
|
||||||
]) {
|
|
||||||
assert.equal(unsupported.test(source), false, `${bundle.pathname} contains ${unsupported}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("settings bundle runs without modern URL and text globals", async () => {
|
|
||||||
const source = await readFile(bundles[1], "utf8");
|
|
||||||
let result;
|
|
||||||
const storage = new Map();
|
|
||||||
vm.runInNewContext(source, {
|
|
||||||
$rocket: {},
|
|
||||||
$request: {
|
|
||||||
url: "https://gs-loc.apple.com/wloc-settings/save?lon=121.1&lat=31.2&acc=25"
|
|
||||||
},
|
|
||||||
$persistentStore: {
|
|
||||||
read: (key) => storage.get(key) || null,
|
|
||||||
write: (value, key) => {
|
|
||||||
storage.set(key, value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
$done: (value) => { result = value; },
|
|
||||||
JSON,
|
|
||||||
Number,
|
|
||||||
Object,
|
|
||||||
String,
|
|
||||||
decodeURIComponent
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(JSON.parse(result.response.body).success, true);
|
|
||||||
});
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
import test from "node:test";
|
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import {
|
|
||||||
MOTION_ACTIVITY_CONFIDENCE, MOTION_ACTIVITY_TYPE, internals, parseFields, patchWlocBody
|
|
||||||
} from "../src/core.js";
|
|
||||||
|
|
||||||
const { concat, writeVarint, writeTag, writeLengthDelimited } = internals;
|
|
||||||
|
|
||||||
function location(withMotion = false) {
|
|
||||||
const fields = [
|
|
||||||
concat(writeTag(1, 0), writeVarint(100)),
|
|
||||||
concat(writeTag(2, 0), writeVarint(200)),
|
|
||||||
concat(writeTag(3, 0), writeVarint(25))
|
|
||||||
];
|
|
||||||
if (withMotion) {
|
|
||||||
fields.push(concat(writeTag(11, 0), writeVarint(7)));
|
|
||||||
fields.push(concat(writeTag(12, 0), writeVarint(88)));
|
|
||||||
}
|
|
||||||
return concat(...fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wifiPayload(value = location()) {
|
|
||||||
const device = concat(
|
|
||||||
writeLengthDelimited(1, new TextEncoder().encode("aa:bb:cc:dd:ee:ff")),
|
|
||||||
writeLengthDelimited(2, value)
|
|
||||||
);
|
|
||||||
return writeLengthDelimited(2, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
function frame(payload) {
|
|
||||||
return concat(Uint8Array.from([0, 1, 0, 0, 0, 1, 0, 0]),
|
|
||||||
Uint8Array.from([payload.length >> 8, payload.length & 0xff]), payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
latitude: 31.230416,
|
|
||||||
longitude: 121.473701,
|
|
||||||
accuracy: 50,
|
|
||||||
motionSimulationEnabled: false
|
|
||||||
};
|
|
||||||
|
|
||||||
test("patches synthetic Wi-Fi response", () => {
|
|
||||||
const result = patchWlocBody(frame(wifiPayload()), config);
|
|
||||||
assert.equal(result.stats.wifi, 1);
|
|
||||||
assert.equal(result.stats.locations, 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("preserves motion fields while disabled", () => {
|
|
||||||
const result = patchWlocBody(frame(wifiPayload(location(true))), config);
|
|
||||||
const payload = result.data.slice(10);
|
|
||||||
assert.ok(payload.includes(7));
|
|
||||||
assert.ok(payload.includes(88));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("replaces motion fields while enabled", () => {
|
|
||||||
const result = patchWlocBody(frame(wifiPayload(location(true))), {
|
|
||||||
...config, motionSimulationEnabled: true
|
|
||||||
});
|
|
||||||
const root = parseFields(result.data.slice(10));
|
|
||||||
const device = parseFields(root[0].value);
|
|
||||||
const fields = parseFields(device.find((field) => field.number === 2).value);
|
|
||||||
const motionType = fields.find((field) => field.number === 11);
|
|
||||||
const motionConfidence = fields.find((field) => field.number === 12);
|
|
||||||
assert.deepEqual(motionType.value, writeVarint(MOTION_ACTIVITY_TYPE));
|
|
||||||
assert.deepEqual(motionConfidence.value, writeVarint(MOTION_ACTIVITY_CONFIDENCE));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("patches CellTower fields 22 and 24", () => {
|
|
||||||
for (const number of [22, 24]) {
|
|
||||||
const cell = writeLengthDelimited(5, location());
|
|
||||||
const result = patchWlocBody(frame(writeLengthDelimited(number, cell)), config);
|
|
||||||
assert.equal(result.stats.cell, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test("encodes signed int64 coordinates without BigInt", () => {
|
|
||||||
assert.deepEqual(
|
|
||||||
Array.from(writeVarint(-18_000_000_000)),
|
|
||||||
[128, 152, 247, 248, 188, 255, 255, 255, 255, 1]
|
|
||||||
);
|
|
||||||
assert.deepEqual(
|
|
||||||
Array.from(writeVarint(18_000_000_000)),
|
|
||||||
[128, 232, 136, 135, 67]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
+23
-47
@@ -1,26 +1,20 @@
|
|||||||
# Third-party proxy modules
|
# Third-party proxy modules
|
||||||
|
|
||||||
The files under `Resources/ThirdPartyProxyModules/` are project-owned module
|
Third-party proxy mode (Surge / Quantumult X / Loon / Shadowrocket / Stash /
|
||||||
definitions used by the App's default domestic-mirror subscription path. Their
|
Egern) now relies entirely on the upstream
|
||||||
executable scripts are built from `ThirdParty/WlocScripts/src/` and checked in
|
[Yu9191/wloc](https://github.com/Yu9191/wloc) modules. The App no longer
|
||||||
under the versioned `ThirdParty/WlocScripts/dist/v1/` directory.
|
maintains or ships project-owned module/script copies, so it never drifts
|
||||||
|
from the upstream protocol.
|
||||||
|
|
||||||
The direct GitHub Raw variants are stored under
|
## Subscription addresses
|
||||||
`ThirdParty/WlocScripts/modules/direct/`. The Settings switch selects which
|
|
||||||
module URL the App copies:
|
|
||||||
|
|
||||||
- enabled by default: `gh-proxy.org` in front of GitHub Raw;
|
The App builds each client's module subscription URL directly from the
|
||||||
- disabled: GitHub Raw directly.
|
upstream repository:
|
||||||
|
|
||||||
The App appends the module version as a query parameter, such as `?v=1.0.1`.
|
- default mirror (gh-proxy):
|
||||||
Increment this value whenever an existing module path changes so proxy clients
|
`https://gh-proxy.org/https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/<file>`
|
||||||
do not reuse a previously cached subscription body.
|
- direct:
|
||||||
|
`https://raw.githubusercontent.com/Yu9191/wloc/refs/heads/main/modules/<file>`
|
||||||
The Apple WLOC response rule and MITM hostname list cover all known network
|
|
||||||
location endpoints: `gs-loc.apple.com`, `gs-loc-cn.apple.com`,
|
|
||||||
`gsp-ssl.ls.apple.com`, `bluedot.is.autonavi.com`, and
|
|
||||||
`bluedot.is.autonavi.com.gds.alibabadns.com`. The settings endpoint
|
|
||||||
(`/wloc-settings/*`) stays scoped to the `gs-loc` hostnames.
|
|
||||||
|
|
||||||
| Module file | Client |
|
| Module file | Client |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -30,35 +24,17 @@ location endpoints: `gs-loc.apple.com`, `gs-loc-cn.apple.com`,
|
|||||||
| `wloc.lpx` | Loon |
|
| `wloc.lpx` | Loon |
|
||||||
| `wloc.stoverride` | Stash |
|
| `wloc.stoverride` | Stash |
|
||||||
|
|
||||||
Egern reuses the Surge module. Stash imports `.stoverride` directly.
|
No `?v=` cache-bust is appended: the URL points at upstream's latest content,
|
||||||
|
and re-importing the subscription in the proxy client re-fetches it.
|
||||||
|
|
||||||
Both script entry points are owned by this repository:
|
## Script protocol
|
||||||
|
|
||||||
- `wloc.js` patches Apple WLOC response bodies;
|
The upstream `wloc.js` patches Apple WLOC responses and reads coordinates from
|
||||||
- `wloc-settings.js` implements `/wloc-settings/save` and
|
the `wloc_settings` persistent key or the module `argument` config. The
|
||||||
`/wloc-settings/version`.
|
upstream `wloc-settings.js` implements `wloc-settings/save` (query/clear/save)
|
||||||
|
using `lon`/`lat`/`acc`/`randomRadius` parameters.
|
||||||
|
|
||||||
The generated scripts target ES2017 and avoid hard dependencies on `BigInt`,
|
The App's third-party save sends `lon`/`lat`/`acc`, matching the upstream
|
||||||
optional chaining, nullish coalescing, `globalThis`, `URLSearchParams`, and
|
script. Motion-state simulation (fields 11/12) is **not** implemented by the
|
||||||
`Object.fromEntries`. This keeps them usable by older proxy-client releases
|
upstream scripts and is unavailable in third-party mode; it remains available
|
||||||
that still implement the established module syntax, binary response body,
|
in APP mode (built-in proxy).
|
||||||
`$done`, and persistent-storage APIs.
|
|
||||||
|
|
||||||
Already-installed legacy modules that point to another repository are not
|
|
||||||
silently migrated because their remote script URL is outside this project's
|
|
||||||
control. Those users must re-import the project-owned module before using the
|
|
||||||
versioned protocol and motion setting.
|
|
||||||
|
|
||||||
Current bundled module SHA-256 values:
|
|
||||||
|
|
||||||
```text
|
|
||||||
1987adf691738544ee44ee67749872ac3ef36efbe54cd8751cf874eaa029d7f4 wloc.conf
|
|
||||||
5562020a8de1a25e3162584fa77c50d8c7e1052cdc08c068180fc2e46d69e7da wloc.lpx
|
|
||||||
cad43570ffb0a16cf3ba7d06d9c0d99ea1b7994ff665eea192011961f1385e87 wloc.module
|
|
||||||
96be7cc7e710436ffa320ccc2e2d45d55d9db7fe26dc6a5bab1fa54333102a99 wloc.sgmodule
|
|
||||||
0cefbabf294acff3382c575578ed7e4c84fa16242cf0808df581aec004ca8058 wloc.stoverride
|
|
||||||
```
|
|
||||||
|
|
||||||
The project acknowledges [Yu9191/wloc](https://github.com/Yu9191/wloc) as a
|
|
||||||
reference for earlier WLOC implementation ideas. That acknowledgement is not
|
|
||||||
an executable dependency or subscription source.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user