From 660bc39c19971d7f32f1a7a3d769040aa203c0fc Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Fri, 13 Nov 2026 19:44:16 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(weather):=20=E5=85=88=E5=B0=86=E5=BD=A9?= =?UTF-8?q?=E4=BA=91=E4=BA=91=E9=87=8F=E6=8D=A2=E7=AE=97=E4=B8=BA=E7=99=BE?= =?UTF-8?q?=E5=88=86=E6=AF=94=E5=86=8D=E5=8F=96=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/class/ColorfulClouds.mjs | 6 ++-- tests/dailyPrecipitationTotals.test.mjs | 38 ++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/class/ColorfulClouds.mjs b/src/class/ColorfulClouds.mjs index 0a7f1c6a..b3c78ca6 100644 --- a/src/class/ColorfulClouds.mjs +++ b/src/class/ColorfulClouds.mjs @@ -519,7 +519,7 @@ export default class ColorfulClouds { daytimeForecast: { forecastStart: timeStamp + 8 * 3600, // 8 hours forecastEnd: timeStamp + 8 * 3600 + 12 * 3600, // 8 + 12 hours - cloudCover: body?.result?.daily?.cloudrate?.[i]?.avg, + cloudCover: Math.round(body?.result?.daily?.cloudrate?.[i]?.avg * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given @@ -543,7 +543,7 @@ export default class ColorfulClouds { overnightForecast: { forecastStart: timeStamp + 20 * 3600, // 20 hours forecastEnd: timeStamp + 20 * 3600 + 12 * 3600, // 20 + 12 hours - cloudCover: body?.result?.daily?.cloudrate?.[i]?.avg, + cloudCover: Math.round(body?.result?.daily?.cloudrate?.[i]?.avg * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given @@ -839,7 +839,7 @@ export default class ColorfulClouds { return { metadata: this.#Metadata(hourly.result.server_time, hourly.location), hours: Array.from({ length: hourly.result.hourly.skycon.length }, (_, i) => ({ - cloudCover: hourly.result.hourly.cloudrate[i].value, + cloudCover: Math.round(hourly.result.hourly.cloudrate[i].value * 100), // cloudCoverHighAltPct: 0, // Not given // cloudCoverLowAltPct: 0, // Not given // cloudCoverMidAltPct: 0, // Not given diff --git a/tests/dailyPrecipitationTotals.test.mjs b/tests/dailyPrecipitationTotals.test.mjs index ed99a25a..1290a629 100644 --- a/tests/dailyPrecipitationTotals.test.mjs +++ b/tests/dailyPrecipitationTotals.test.mjs @@ -7,7 +7,8 @@ globalThis.$argument = { LogLevel: "OFF", Storage: "database" }; globalThis.$httpClient = { get(request, callback) { let body; - if (request.url.includes("api.caiyunapp.com")) body = colorfulCloudsDaily; + if (request.url.includes("/hourly?")) body = colorfulCloudsHourly; + else if (request.url.includes("api.caiyunapp.com")) body = colorfulCloudsDaily; else if (request.url.includes("/v7/weather/")) body = qWeatherDaily; else throw new Error(`unexpected request: ${request.url}`); callback(null, { headers: {}, status: 200 }, JSON.stringify(body)); @@ -35,6 +36,8 @@ test("ColorfulClouds keeps WeatherKit daily amount pairs while replacing support assert.equal(appleDay.precipitationChance, 80); assert.equal(appleDay.daytimeForecast.precipitationChance, 70); assert.equal(appleDay.overnightForecast.precipitationChance, 60); + assert.equal(appleDay.daytimeForecast.cloudCover, 80); + assert.equal(appleDay.overnightForecast.cloudCover, 80); }); test("QWeather keeps WeatherKit daily amount pairs instead of reusing one daily total", async () => { @@ -129,3 +132,36 @@ const qWeatherDaily = { fxLink: "https://www.qweather.com/", updateTime: "2026-07-16T08:00:00+08:00", }; + +// The provider boundary converts fractions once, before either WK2 encoding or WK3 JSON rounding. +const colorfulCloudsHourly = { + status: "ok", + location: [30, 120], + result: { + hourly: { + status: "ok", + skycon: [{ value: "CLOUDY", datetime: "2026-11-13T12:00:00Z" }], + cloudrate: [{ value: 0.5 }], + humidity: [{ value: 0.5 }], + precipitation: [{ value: 0, probability: 0 }], + pressure: [{ value: 101300 }], + temperature: [{ value: 20 }], + apparent_temperature: [{ value: 20 }], + visibility: [{ value: 10 }], + wind: [{ direction: 0, speed: 1 }], + }, + }, +}; +test("Caiyun hourly cloud-cover fractions are converted to percentages before rounding", async () => { + for (const [fraction, percent] of [ + [0, 0], + [0.005, 1], + [0.5, 50], + [0.8, 80], + [1, 100], + ]) { + colorfulCloudsHourly.result.hourly.cloudrate[0].value = fraction; + const forecast = await new ColorfulClouds({ ...parameters, version: "v3" }, "token").ForecastHourly(1); + assert.equal(forecast.hours[0].cloudCover, percent); + } +}); From e6c30473836586f69f4946335096ad94a495b512 Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Sat, 14 Nov 2026 17:13:38 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(dev):=20=E6=8E=A5=E5=85=A5=20WK3=20Fla?= =?UTF-8?q?tBuffer=20=E5=A4=A9=E6=B0=94=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复用协议包的 WK3.Codec 替换当前、逐日与逐小时天气,按产品保留其他原始数据。附供应商失败、未来字段与字节保留回归及 27.3 真机记录。 依赖 apple.WeatherKit 的 codec 分支;协议包发布后更新依赖和 lockfile,当前保留原版本以供 Draft 审阅。 --- docs/wk3-binary-weather.md | 52 +++ src/class/WeatherKit3.mjs | 396 ++---------------- src/process/Response.dev.mjs | 33 +- tests/fixtures/wk3-paired/README.md | 11 + tests/fixtures/wk3-paired/hangzhou/apple.bin | Bin 0 -> 11332 bytes tests/fixtures/wk3-paired/hangzhou/apple.json | 1 + .../wk3-paired/hangzhou/manifest.json | 82 ++++ tests/weatherKit3.test.mjs | 11 +- tests/weatherKit3WeatherReplacement.test.mjs | 71 ++++ 9 files changed, 292 insertions(+), 365 deletions(-) create mode 100644 docs/wk3-binary-weather.md create mode 100644 tests/fixtures/wk3-paired/README.md create mode 100644 tests/fixtures/wk3-paired/hangzhou/apple.bin create mode 100644 tests/fixtures/wk3-paired/hangzhou/apple.json create mode 100644 tests/fixtures/wk3-paired/hangzhou/manifest.json create mode 100644 tests/weatherKit3WeatherReplacement.test.mjs diff --git a/docs/wk3-binary-weather.md b/docs/wk3-binary-weather.md new file mode 100644 index 00000000..98000c43 --- /dev/null +++ b/docs/wk3-binary-weather.md @@ -0,0 +1,52 @@ +# WK3 FlatBuffer 天气替换 + +本分支接入 `NSRingo/apple.WeatherKit` 的完整 WK3 schema 与 `WK3.Codec`,在 +`Response.dev.mjs` 中替换当前、逐日、逐小时天气。`WeatherKit3.mjs` 保留产品注册和 +供应商数据形态适配,字段布局与枚举读写统一由协议包负责。 + +## 与其他改动的关系 + +- #110 负责主 App 的 JSON 路径,独立审阅。本分支以 dev 为基础,不包含 #110 的 JSON 实现。 +- 本分支先保持 Draft,等待协议包合并、发布,再更新 `@nsringo/weatherkit` 依赖和 lockfile。 + 现有 2.0.5 没有 `WK3.Schema` / `WK3.Codec`,无法直接运行本分支。 +- 分支中的彩云 cloudCover 修复与 #110 的对应修复相同;#110 合并后同步 dev,消除这部分重复。 +- macOS 小组件的 JSON 白名单是另一项客户端策略变更,单独提交。 + +## 验证 + +本地使用协议包分支生成的 `dist/index.js` 替换本 worktree 的 +`node_modules/@nsringo/weatherkit/dist/index.js`,package.json 和 lockfile 未修改。 +协议包发布前可按这一方式复现;两个仓库的分支名均为 `codex/wk3-binary-codecs`。 + +```sh +# 在 apple.WeatherKit 中 +npm run WK3 +npm run tsc +npm test +npm run build + +# 在 WeatherKit 中,协议包位于相邻的 ../apple.WeatherKit +cp ../apple.WeatherKit/dist/index.js node_modules/@nsringo/weatherkit/dist/index.js +node --test tests/*.test.mjs +npm run build +npm run build:dev +``` + +- 协议包 26 项测试通过;主仓库 141 项测试通过,正式和开发构建通过。 +- 回归用例验证三类天气注入、供应商失败时单产品保留、未来字段保留,以及未选中产品 + 的完整原始 arena 逐字节保留。关闭替换或选择 WeatherKit 时响应逐字节不变。 +- 2026-11-14,macOS 27.3 (26C5035r) + Surge 6.12.0,成都、苏州主 App 实测请求和响应 + 均为 `application/vnd.apple.flatbuffer;messageType=WK3.Weather`。 +- 成都响应:当前 17.20°,今日 18.81° / 12.03°;彩云逐日 11 项、逐小时 274 项。 +- 苏州响应与页面对齐如下;当前、逐日、逐小时 metadata.providerName 均为彩云天气。 + +| 项目 | 二进制解码值 | 页面显示 | +| --- | --- | --- | +| 当前温度 | 18.16° | 18° | +| 今日最高 / 最低 | 18.16° / 16.41° | 18° / 16° | +| 17 时 | 18.00° | 18° | +| 18 时 | 17.68° | 18° | +| 19 时 | 17.49° | 17° | + +后续客户端验收按入口分别记录:macOS 小组件的 JSON 路径补实际显示和连续刷新; +Siri、地图等入口先确认请求与响应格式,再测试该入口,不预先声明客户端覆盖。 diff --git a/src/class/WeatherKit3.mjs b/src/class/WeatherKit3.mjs index ab59c65b..b84ae0ea 100644 --- a/src/class/WeatherKit3.mjs +++ b/src/class/WeatherKit3.mjs @@ -1,367 +1,51 @@ -import { FlatBufferRootProcessor, inspectTable } from "@nsnanocat/flatbuffer-root"; +import { FlatBufferRootProcessor } from "@nsnanocat/flatbuffer-root"; import { WK3 } from "@nsringo/weatherkit"; -import { createUuidOffset, decodeUuid } from "./WeatherKit2.mjs"; -/** - * 使用 Weather 根模型和产品 codec 配置通用根表处理器。 - * WK3 根槽位稀疏(0/5/9),各 codec 以显式 slot 对齐线槽位;其余产品保持原始 arena。 - * Configure the generic root-table processor with the Weather model and product codecs. - * WK3 root slots are sparse (0/5/9); each codec aligns to its wire slot explicitly, other products keep their original arenas. - */ +// Product boundaries preserve opaque/future roots. Table layout and enum handling live +// in the protocol package and are shared by every registered WK3 product. +const roots = WK3.Schema.tables.Weather; const WeatherKit3 = new FlatBufferRootProcessor({ - codecs: { - airQuality: { decode: decodeAirQuality, encode: encodeAirQuality, tableClass: WK3.AirQuality }, - forecastNextHour: { decode: decodeForecastNextHour, encode: encodeForecastNextHour, tableClass: WK3.NextHourForecastData }, - weatherAlerts: { decode: decodeWeatherAlerts, encode: encodeWeatherAlerts, tableClass: WK3.WeatherAlertCollectionData }, - }, - configurableRootNames: ["airQuality", "forecastNextHour", "weatherAlerts"], name: "WeatherKit3", rootClass: WK3.Weather, + configurableRootNames: roots.map(field => field.name), + codecs: Object.fromEntries(roots.map(({ name, type }) => [name, { + tableClass: WK3[type], + decode: table => WK3.Codec.decode(type, table, { uuidStrings: true }), + encode: (builder, data) => WK3.Codec.encode(type, builder, data), + }])), }); - export default WeatherKit3; -/** - * 编码空气质量产品表。标量超宽或非有限浓度抛 RangeError,由处理器隔离为单 slot 失败。 - * Encode the air quality product table. Out-of-range scalars or non-finite amounts throw. - * @param {import("flatbuffers").Builder} builder - FlatBuffer 构建器 / FlatBuffer builder. - * @param {object} data - 业务空气质量对象 / Air quality payload. - * @returns {number} 产品表偏移 / Table offset. - */ -function encodeAirQuality(builder, data) { - if (!Number.isInteger(data.index) || data.index < 0 || data.index > 32767 || !Number.isInteger(data.categoryIndex) || data.categoryIndex < 0 || data.categoryIndex > 127) { - throw new RangeError("WK3 AQ scalar out of range"); - } - const metadataOffset = encodeMetadata(builder, data.metadata); - const pollutantsOffset = WK3.AirQuality.createPollutantsVector( - builder, - data.pollutants.map(pollutant => { - if (!Number.isFinite(Math.fround(pollutant.amount))) throw new RangeError("WK3 pollutant amount must be finite"); - return WK3.Pollutant.createPollutant(builder, enumValue(WK3.PollutantType, pollutant.pollutantType), pollutant.amount, enumValue(WK3.UnitType, pollutant.units)); - }), - ); - const scaleOffset = builder.createString(data.scale); - return WK3.AirQuality.createAirQuality(builder, metadataOffset, data.categoryIndex, data.index, data.isSignificant, pollutantsOffset, enumValue(WK3.ComparisonTrend, data.previousDayComparison), enumValue(WK3.PollutantType, data.primaryPollutant), scaleOffset); -} - -/** - * 解码空气质量产品表;未验证字段或未知枚举值抛 RangeError。 - * Decode the air quality product table; unverified fields or unknown enums throw. - * @param {WK3.AirQuality} data - 生成的 AQ 表实例 / Generated AQ table instance. - * @returns {object} 业务空气质量对象 / Air quality payload. - */ -function decodeAirQuality(data) { - checkFields(data, 8); - const pollutants = []; - for (let i = 0; i < data.pollutantsLength(); i++) { - const pollutant = data.pollutants(i); - checkFields(pollutant, 3); - pollutants.push({ - pollutantType: enumName(WK3.PollutantType, pollutant.pollutantType()), - amount: pollutant.amount(), - units: enumName(WK3.UnitType, pollutant.units()), - }); - } - return { - metadata: decodeMetadata(data.metadata()), - categoryIndex: data.categoryIndex(), - index: data.index(), - isSignificant: data.isSignificant(), - pollutants, - previousDayComparison: enumName(WK3.ComparisonTrend, data.previousDayComparison()), - primaryPollutant: enumName(WK3.PollutantType, data.primaryPollutant()), - scale: data.scale(), - }; -} - -/** - * 编码下一小时降水产品表;仅供注入缺失产品使用,不重编码 Apple 原始数据。 - * Encode the next-hour precipitation product table; only used to inject a missing product, never to re-encode Apple's own data. - * @param {import("flatbuffers").Builder} builder - FlatBuffer 构建器 / FlatBuffer builder. - * @param {object} data - 业务下一小时降水对象 / Next-hour precipitation payload. - * @returns {number} 产品表偏移 / Table offset. - */ -function encodeForecastNextHour(builder, data) { - const metadataOffset = encodeMetadata(builder, data.metadata); - // 防御第三方数据:未知枚举传给 FlatBuffer 会静默编码为 UNKNOWN0。 - // Defend against third-party data: unknown enums would otherwise be silently encoded as UNKNOWN0. - const conditionOffsets = data.condition - .filter(condition => typeof WK3.ForecastToken[condition.forecastToken] === "number" && [condition.beginCondition, condition.endCondition].every(value => typeof WK3.ConditionType[value] === "number")) - .map(condition => { - const parametersOffset = WK3.Condition.createParametersVector( - builder, - condition.parameters.map(parameter => WK3.Parameter.createParameter(builder, enumValue(WK3.ParameterType, parameter.type), parameter.date)), - ); - return WK3.Condition.createCondition(builder, condition.startTime, condition.endTime, enumValue(WK3.ForecastToken, condition.forecastToken), enumValue(WK3.ConditionType, condition.beginCondition), enumValue(WK3.ConditionType, condition.endCondition), parametersOffset); - }); - const conditionOffset = WK3.NextHourForecastData.createConditionVector(builder, conditionOffsets); - // 摘要槽位 2 的 condition 枚举向量:业务摘要恒为单条件分段(v3 形态为单元素数组), - // 单元素 u8 向量与 u16 向量字节一致,可安全写入(PR #108 实机验证); - // 多元素数组的元素宽度未验证、未知降水类型未定义,均抛错由处理器隔离。 - // Summary slot 2 (condition enum vector): business summaries are single-condition segments - // (a single-element array in v3 shape), and a singleton u8 vector is byte-identical to u16, - // so writing is safe (device-verified in PR #108); multi-element arrays (unverified element - // width) and unknown precipitation types throw and are isolated by the processor. - const summaryOffsets = data.summary.map(summary => { - if (!Array.isArray(summary.condition) || summary.condition.length !== 1) throw new RangeError("WK3 summary condition must be a single-element array"); - const conditionVectorOffset = builder.createByteVector([enumValue(WK3.PrecipitationType, summary.condition[0])]); - WK3.ForecastPeriodSummary.startForecastPeriodSummary(builder); - WK3.ForecastPeriodSummary.addStartTime(builder, summary.startTime); - WK3.ForecastPeriodSummary.addEndTime(builder, summary.endTime); - builder.addFieldOffset(2, conditionVectorOffset, 0); - WK3.ForecastPeriodSummary.addPrecipitationChance(builder, summary.precipitationChance); - WK3.ForecastPeriodSummary.addPrecipitationIntensity(builder, summary.precipitationIntensity); - return WK3.ForecastPeriodSummary.endForecastPeriodSummary(builder); - }); - const summaryOffset = WK3.NextHourForecastData.createSummaryVector(builder, summaryOffsets); - const minutesOffset = WK3.NextHourForecastData.createMinutesVector( - builder, - data.minutes.map(minute => WK3.Common.ForecastMinute.createForecastMinute(builder, minute.startTime, minute.precipitationChance, minute.precipitationIntensity, minute.perceivedPrecipitationIntensity)), - ); - return WK3.NextHourForecastData.createNextHourForecastData(builder, metadataOffset, conditionOffset, summaryOffset, data.forecastStart, data.forecastEnd, minutesOffset); -} - -/** - * 解码下一小时降水产品表;未验证字段或未知枚举值抛 RangeError。 - * Decode the next-hour precipitation product table; unverified fields or unknown enums throw. - * @param {WK3.NextHourForecastData} data - 生成的下一小时降水表实例 / Generated next-hour table instance. - * @returns {object} 业务下一小时降水对象 / Next-hour precipitation payload. - */ -function decodeForecastNextHour(data) { - checkFields(data, 6); - const NextHourForecastData = data; - const condition = []; - for (let i = 0; i < NextHourForecastData.conditionLength(); i++) { - const conditionData = NextHourForecastData.condition(i); - checkFields(conditionData, 6); - const entry = { - beginCondition: enumName(WK3.ConditionType, conditionData.beginCondition()), - endCondition: enumName(WK3.ConditionType, conditionData.endCondition()), - endTime: conditionData.endTime(), - forecastToken: enumName(WK3.ForecastToken, conditionData.forecastToken()), - parameters: [], - startTime: conditionData.startTime(), - }; - for (let j = 0; j < conditionData.parametersLength(); j++) { - const parameter = conditionData.parameters(j); - checkFields(parameter, 2); - entry.parameters.push({ date: parameter.date(), type: enumName(WK3.ParameterType, parameter.type()) }); +/** Translate existing provider products into native WK3 value shapes before encoding. */ +export function prepareWeatherKit3Product(name, product) { + const type = roots.find(root => root.name === name)?.type; + if (!type) throw new RangeError(`Unknown WK3 product ${name}`); + function visit(table, data) { + if (table === "PrecipitationRange" && typeof data === "number") return { expected: data, minimum: data, maximum: data }; + if (table === "UUID" && typeof data === "string") return data; + if (!data || typeof data !== "object" || Array.isArray(data)) throw new TypeError(`WK3 ${table} object expected`); + const result = {}; + if (table === "NextHourForecastData" && Array.isArray(data.condition)) { + data = { ...data, condition: data.condition.filter(item => + Object.hasOwn(WK3.Schema.enums.ForecastToken.values, item.forecastToken) && + [item.beginCondition, item.endCondition].every(value => Object.hasOwn(WK3.Schema.enums.ConditionType.values, value))) }; } - condition.push(entry); - } - const minutes = []; - for (let i = 0; i < NextHourForecastData.minutesLength(); i++) { - const minute = NextHourForecastData.minutes(i); - checkFields(minute, 4); - minutes.push({ - perceivedPrecipitationIntensity: minute.perceivedPrecipitationIntensity(), - precipitationChance: minute.precipitationChance(), - precipitationIntensity: minute.precipitationIntensity(), - startTime: minute.startTime(), - }); - } - const summary = []; - for (let i = 0; i < NextHourForecastData.summaryLength(); i++) { - const summaryData = NextHourForecastData.summary(i); - checkFields(summaryData, 5); - // 摘要槽位 2 的 condition 枚举向量元素宽度未验证,不解码。 - // Slot 2 of summaries (condition enum vector) is unverified and not decoded. - summary.push({ - endTime: summaryData.endTime(), - precipitationChance: summaryData.precipitationChance(), - precipitationIntensity: summaryData.precipitationIntensity(), - startTime: summaryData.startTime(), - }); - } - return { - metadata: decodeMetadata(NextHourForecastData.metadata()), - condition, - forecastEnd: NextHourForecastData.forecastEnd(), - forecastStart: NextHourForecastData.forecastStart(), - minutes, - summary, - }; -} - -/** - * 编码气象预警集合产品表。 - * Encode the weather alert collection product table. - * @param {import("flatbuffers").Builder} builder - FlatBuffer 构建器 / FlatBuffer builder. - * @param {object} data - 业务气象预警集合对象 / Weather alert collection payload. - * @returns {number} 产品表偏移 / Table offset. - */ -function encodeWeatherAlerts(builder, data) { - // 缺省字符串不写入(0 偏移),保留 Apple 原始字段缺席状态。 - // Absent strings are not written (0 offset), preserving Apple's original field absence. - const stringOffset = value => (value ? builder.createString(value) : 0); - const metadataOffset = encodeMetadata(builder, data.metadata); - const alertsOffsets = data.alerts.map(alert => { - const responsesOffset = WK3.WeatherAlertSummary.createResponsesVector( - builder, - alert.responses.map(response => enumValue(WK3.ResponseType, response)), - ); - return WK3.WeatherAlertSummary.createWeatherAlertSummary( - builder, - createUuidOffset(builder, alert.id), - stringOffset(alert.areaId), - stringOffset(alert.attributionUrl), - stringOffset(alert.countryCode), - stringOffset(alert.description), - stringOffset(alert.token), - alert.effectiveTime, - alert.expireTime, - alert.issuedTime, - alert.eventOnsetTime, - alert.eventEndTime, - stringOffset(alert.detailsUrl), - stringOffset(alert.phenomenon), - enumValue(WK3.Severity, alert.severity), - enumValue(WK3.SignificanceType, alert.significance), - stringOffset(alert.source), - stringOffset(alert.eventSource), - enumValue(WK3.Urgency, alert.urgency), - enumValue(WK3.Certainty, alert.certainty), - enumValue(WK3.ImportanceType, alert.importance), - responsesOffset, - ); - }); - const alertsOffset = WK3.WeatherAlertCollectionData.createAlertsVector(builder, alertsOffsets); - return WK3.WeatherAlertCollectionData.createWeatherAlertCollectionData(builder, metadataOffset, stringOffset(data.detailsUrl), alertsOffset); -} - -/** - * 解码气象预警集合产品表;未验证字段或未知枚举值抛 RangeError。 - * Decode the weather alert collection product table; unverified fields or unknown enums throw. - * @param {WK3.WeatherAlertCollectionData} data - 生成的预警集合表实例 / Generated alert collection table instance. - * @returns {object} 业务气象预警集合对象 / Weather alert collection payload. - */ -function decodeWeatherAlerts(data) { - checkFields(data, 3); - const WeatherAlertCollectionData = data; - const alerts = []; - for (let i = 0; i < WeatherAlertCollectionData.alertsLength(); i++) { - const alert = WeatherAlertCollectionData.alerts(i); - checkFields(alert, 22); - const summary = { - areaId: alert.areaId(), - attributionUrl: alert.attributionUrl(), - certainty: enumName(WK3.Certainty, alert.certainty()), - countryCode: alert.countryCode(), - description: alert.description(), - detailsUrl: alert.detailsUrl(), - effectiveTime: alert.effectiveTime(), - eventEndTime: alert.eventEndTime(), - eventOnsetTime: alert.eventOnsetTime(), - eventSource: alert.eventSource(), - expireTime: alert.expireTime(), - id: decodeUuid(alert.id()), - importance: enumName(WK3.ImportanceType, alert.importance()), - issuedTime: alert.issuedTime(), - phenomenon: alert.phenomenon(), - responses: [], - severity: enumName(WK3.Severity, alert.severity()), - significance: enumName(WK3.SignificanceType, alert.significance()), - source: alert.source(), - token: alert.token(), - urgency: enumName(WK3.Urgency, alert.urgency()), - }; - for (let j = 0; j < alert.responsesLength(); j++) summary.responses.push(enumName(WK3.ResponseType, alert.responses(j))); - alerts.push(summary); + for (const field of WK3.Schema.tables[table]) { + let value = data[field.name]; + if (value == null || (field.optional && value === 0)) continue; + const tableType = WK3.Schema.tables[field.type]; + if (field.vector) { + if (WK3.Schema.enums[field.type] && typeof value === "string") value = [value]; + if (!Array.isArray(value)) throw new TypeError(`WK3 ${field.name} vector expected`); + result[field.name] = tableType ? value.map(item => visit(field.type, item)) : value; + } else if (tableType) result[field.name] = visit(field.type, value); + else { + if (table === "Metadata" && ["latitude", "longitude"].includes(field.name) && typeof value === "string") value = Number(value); + if (["byte", "ubyte", "short", "ushort", "int", "uint"].includes(field.type) && typeof value === "number") value = field.type === "uint" ? Math.trunc(value) : Math.round(value); + result[field.name] = value; + } + } + return result; } - return { - metadata: decodeMetadata(WeatherAlertCollectionData.metadata()), - alerts, - detailsUrl: WeatherAlertCollectionData.detailsUrl(), - }; -} - -/** - * 编码产品元数据子表;缺失时返回 0(不写入)。 - * Encode the metadata child table; returns 0 when absent. - * @param {import("flatbuffers").Builder} builder - FlatBuffer 构建器 / FlatBuffer builder. - * @param {object} [data] - 元数据对象 / Metadata payload. - * @returns {number} 子表偏移 / Table offset. - */ -function encodeMetadata(builder, data) { - if (!data) return 0; - // 缺省字符串不写入(0 偏移),保留 Apple 原始字段缺席状态。 - // Absent strings are not written (0 offset), preserving Apple's original field absence. - const stringOffset = value => (value ? builder.createString(value) : 0); - return WK3.Metadata.createMetadata( - builder, - stringOffset(data.attributionUrl), - data.expireTime, - stringOffset(data.language), - data.latitude, - data.longitude, - stringOffset(data.providerLogo), - stringOffset(data.providerName), - data.readTime, - data.reportedTime, - data.temporarilyUnavailable, - enumValue(WK3.SourceType, data.sourceType), - ); -} - -/** - * 解码产品元数据子表;缺失时返回 undefined。 - * Decode the metadata child table; returns undefined when absent. - * @param {WK3.Metadata} [data] - 生成的元数据表实例 / Generated metadata table instance. - * @returns {object|undefined} 元数据对象 / Metadata payload. - */ -function decodeMetadata(data) { - if (!data) return undefined; - checkFields(data, 11); - return { - attributionUrl: data.attributionUrl(), - expireTime: data.expireTime(), - language: data.language(), - latitude: data.latitude(), - longitude: data.longitude(), - providerLogo: data.providerLogo(), - providerName: data.providerName(), - readTime: data.readTime(), - reportedTime: data.reportedTime(), - temporarilyUnavailable: data.temporarilyUnavailable(), - sourceType: enumName(WK3.SourceType, data.sourceType()), - }; -} - -/** - * 校验表的字段数不超过已验证范围;超出时抛 RangeError,由处理器隔离为单 slot 失败。 - * Validate that a table declares no more than the verified field count; excess throws. - * @param {object} table - 生成的表实例 / Generated table instance. - * @param {number} count - 已验证的字段数 / Verified field count. - * @returns {void} - */ -function checkFields(table, count) { - // 超出已验证字段数的表结构抛 RangeError,由处理器隔离为单 slot 失败。 - // Tables declaring more than the verified field count throw, isolated as per-slot failures. - inspectTable(table.bb, table.bb_pos, count); -} - -/** - * 将 wire 枚举值映射为名称;未知或占位值抛 RangeError。 - * Map a wire enum value to its name; unknown or placeholder values throw. - * @param {object} values - 版本枚举表 / Versioned enum table. - * @param {number} value - wire 枚举值 / Wire enum value. - * @returns {string} 枚举名称 / Enum name. - */ -function enumName(values, value) { - const name = values[value]; - if (typeof name !== "string" || /^UNKNOWN\d+$/.test(name)) throw new RangeError(`Unverified WK3 enum: ${value}`); - return name; -} - -/** - * 将枚举名称映射为 wire 枚举值;未知或占位名抛 RangeError。 - * Map an enum name to its wire value; unknown or placeholder names throw. - * @param {object} values - 版本枚举表 / Versioned enum table. - * @param {string} name - 枚举名称 / Enum name. - * @returns {number} wire 枚举值 / Wire enum value. - */ -function enumValue(values, name) { - const value = values[name]; - if (typeof value !== "number" || /^UNKNOWN\d+$/.test(name)) throw new RangeError(`Unverified WK3 enum: ${name}`); - return value; + return visit(type, product); } diff --git a/src/process/Response.dev.mjs b/src/process/Response.dev.mjs index 59d33872..e298acf2 100644 --- a/src/process/Response.dev.mjs +++ b/src/process/Response.dev.mjs @@ -8,7 +8,7 @@ import WAQI from "../class/WAQI.mjs"; import Weather from "../class/Weather.mjs"; import WeatherAlerts from "../class/WeatherAlerts.mjs"; import WeatherKit2 from "../class/WeatherKit2.mjs"; -import WeatherKit3 from "../class/WeatherKit3.mjs"; +import WeatherKit3, { prepareWeatherKit3Product } from "../class/WeatherKit3.mjs"; import database from "../function/database.mjs"; import mergeWeatherKitAvailability from "../function/mergeWeatherKitAvailability.mjs"; import parseWeatherKitURL from "../function/parseWeatherKitURL.mjs"; @@ -191,9 +191,8 @@ export async function Response($request, $response) { case url.pathname.startsWith("/api/v3/weather/"): { Console.info("🟪 WeatherKit.api.v3.weather"); const parameters = parseWeatherKitURL(url); - // WK3 仅注册了这三个产品的 codec,其余产品保持原始 arena。 - // Only these three products have registered WK3 codecs; other products keep their original arenas. - const dataSets = parameters.dataSets.filter(dataSet => Settings.DataSets.includes(dataSet) && ["airQuality", "forecastNextHour", "weatherAlerts"].includes(dataSet)); + // Only selected injectable products are decoded; all other roots retain their original arena. + const dataSets = parameters.dataSets.filter(dataSet => Settings.DataSets.includes(dataSet) && ["airQuality", "currentWeather", "forecastDaily", "forecastHourly", "forecastNextHour", "weatherAlerts"].includes(dataSet)); if (dataSets.length === 0) break; const rootNames = dataSets.map(dataSet => Configs.DataSets[dataSet] ?? dataSet); const { json, failures } = WeatherKit3.decodeWithReport(ByteBuffer, rootNames); @@ -213,6 +212,16 @@ export async function Response($request, $response) { country: parameters.country, }; const patch = {}; + const replaceWeather = async (name, inject) => { + const original = body[name]; + try { + const copy = original ? JSON.parse(JSON.stringify(original)) : undefined; + const result = await inject(copy, Settings, enviroments); + if (result?.metadata && result.metadata.temporarilyUnavailable !== true && JSON.stringify(result) !== JSON.stringify(original)) patch[name] = result; + } catch (error) { + Console.warn("WeatherKit.api.v3.weather", `preserved ${name}: ${error?.name ?? "Error"}`); + } + }; await Promise.all( dataSets.map(async dataSet => { @@ -221,6 +230,18 @@ export async function Response($request, $response) { return; } switch (dataSet) { + case "currentWeather": { + await replaceWeather("currentWeather", InjectCurrentWeather); + break; + } + case "forecastDaily": { + if (body.forecastDaily) await replaceWeather("forecastDaily", InjectForecastDaily); + break; + } + case "forecastHourly": { + if (body.forecastHourly) await replaceWeather("forecastHourly", InjectForecastHourly); + break; + } case "airQuality": { if (Settings?.LogLevel === "DEBUG" || Settings?.LogLevel === "ALL") { matchEnum.airQuality(); @@ -266,6 +287,10 @@ export async function Response($request, $response) { } }), ); + for (const name of Object.keys(patch)) { + try { patch[name] = prepareWeatherKit3Product(name, patch[name]); } + catch (error) { delete patch[name]; Console.warn("WeatherKit.api.v3.weather", `preserved ${name}: ${error?.name ?? "Error"}`); } + } if (Object.keys(patch).length > 0) rawBody = WeatherKit3.encode(ByteBuffer, patch); break; } diff --git a/tests/fixtures/wk3-paired/README.md b/tests/fixtures/wk3-paired/README.md new file mode 100644 index 00000000..3e1a7a77 --- /dev/null +++ b/tests/fixtures/wk3-paired/README.md @@ -0,0 +1,11 @@ +# WK3 integration fixture + +`hangzhou/` is a sanitized native macOS 27.3 (26C5035r) JSON/FlatBuffer pair from +2026-11-13, schema `lib-rc-3.0.0-b66`. The manifest records acquisition times, +response media types, hashes and in-place redaction offsets. Coordinates are zero; +no request credentials or device identifiers are included. The binary is not re-encoded. + +`weatherKit3WeatherReplacement.test.mjs` uses this binary to verify current/daily/hourly +replacement, provider failure isolation, future-field preservation and unchanged +native/disabled-provider responses. Other protocol samples and schema tests belong +to the companion `NSRingo/apple.WeatherKit` change. diff --git a/tests/fixtures/wk3-paired/hangzhou/apple.bin b/tests/fixtures/wk3-paired/hangzhou/apple.bin new file mode 100644 index 0000000000000000000000000000000000000000..bcebe1759069106cb65f67593be92aa43153436c GIT binary patch literal 11332 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 { mockProvider(t); - for (const patch of [{ primaryPollutant: "UNVERIFIED" }, { index: 40000 }, { index: NaN }]) { + for (const patch of [{ primaryPollutant: "UNVERIFIED" }, { index: 65536 }, { index: NaN }]) { t.mock.method(ColorfulClouds.prototype, "CurrentAirQuality", async () => ({ ...structuredClone(replacement), ...patch })); assert.deepEqual((await response()).body, fixture); } @@ -255,7 +255,7 @@ function buildWK3Alerts(builder, detailsUrl = "https://www.qweather.com/severe-w ), ); const responses = WK3.WeatherAlertSummary.createResponsesVector(builder, [7]); - const alert = WK3.WeatherAlertSummary.createWeatherAlertSummary(builder, id, builder.createString("101010100"), 0, builder.createString("CN"), builder.createString("大风蓝色预警"), 0, 2000000000, 2000003600, 2000000000, 2000000000, 2000003600, 0, 0, 4, 10, 0, 0, 2, 2, 2, responses); + const alert = WK3.WeatherAlertSummary.createWeatherAlertSummary(builder, id, builder.createString("101010100"), 0, 0, builder.createString("CN"), builder.createString("大风蓝色预警"), 0, 2000000000, 2000003600, 2000000000, 2000000000, 2000003600, 0, 0, 4, 10, 0, 0, 2, 2, 2, responses, 0, 0); const alerts = WK3.WeatherAlertCollectionData.createAlertsVector(builder, [alert]); return WK3.WeatherAlertCollectionData.createWeatherAlertCollectionData(builder, buildWK3AlertsMetadata(builder), builder.createString(detailsUrl), alerts); } @@ -398,7 +398,7 @@ test("Summary generates condition shape by target version at creation", async () assert.deepEqual(ForecastNextHour.Summary(minutes, "v3")[0].condition, ["RAIN"]); }); -test("WK3 rejects multi-element summary condition arrays on injection", async t => { +test("WK3 writes multi-element UInt8 summary condition vectors", async t => { t.mock.method(ColorfulClouds.prototype, "CurrentAirQuality", async () => structuredClone(replacement)); t.mock.method(ColorfulClouds.prototype, "YesterdayAirQuality", async () => ({ ...structuredClone(replacement), index: 25, categoryIndex: 1 })); t.mock.method(ColorfulClouds.prototype, "Minutely", async () => ({ @@ -407,7 +407,8 @@ test("WK3 rejects multi-element summary condition arrays on injection", async t })); const output = (await response(buildWK3([[0, buildWK3AirQuality]]))).body; const weather = WK3.Weather.getRootAsWeather(new ByteBuffer(output)); - // 多元素向量的元素宽度未验证:注入被隔离,其余产品不受影响 / Multi-element vectors are unverified: injection is isolated while other products proceed - assert.equal(weather.forecastNextHour(), null); + const summary = weather.forecastNextHour().summary(0); + assert.equal(summary.conditionLength(), 2); + assert.deepEqual([summary.condition(0), summary.condition(1)], [2, 3]); assert.equal(weather.airQuality().index(), 175); }); diff --git a/tests/weatherKit3WeatherReplacement.test.mjs b/tests/weatherKit3WeatherReplacement.test.mjs new file mode 100644 index 00000000..a2e930f0 --- /dev/null +++ b/tests/weatherKit3WeatherReplacement.test.mjs @@ -0,0 +1,71 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test, { beforeEach } from "node:test"; +import { ByteBuffer } from "flatbuffers"; +import { WK3 } from "@nsringo/weatherkit"; + +globalThis.$environment = { "surge-version": "test" }; +globalThis.$persistentStore = { read: () => null, write: () => true }; +globalThis.$argument = { LogLevel: "OFF", Storage: "Argument" }; +const [{ Response }, { Request }, { default: ColorfulClouds }] = await Promise.all([ + import("../src/process/Response.dev.mjs"), import("../src/process/Request.dev.mjs"), import("../src/class/ColorfulClouds.mjs"), +]); +const source = new Uint8Array(readFileSync(new URL("./fixtures/wk3-paired/hangzhou/apple.bin", import.meta.url))); +const original = WK3.Codec.decode("Weather", WK3.Weather.getRootAsWeather(new ByteBuffer(source))); +const metadata = { providerName: "Controlled provider", sourceType: "MODELED", temporarilyUnavailable: false, latitude: "30", longitude: "120" }; +const request = () => ({ method: "GET", url: "https://weatherkit.apple.com/api/v3/weather/en-US/30/120?country=CN&dataSets=currentWeather,forecastDaily,forecastHourly,weatherAlerts", headers: { Accept: "application/vnd.apple.flatbuffer;messageType=WK3.Weather", "User-Agent": "WeatherKit_Weather_macOS_Version 27.3" } }); +const response = (body = source) => Response(request(), { status: 200, headers: { "Content-Type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather" }, bodyBytes: body }); +function pointer(bytes, slot) { const b = new ByteBuffer(bytes), root = b.readUint32(0), off = b.__offset(root, 4 + 2 * slot); return off ? b.__indirect(root + off) : 0; } +beforeEach(() => { globalThis.$request = request(); globalThis.$argument = { LogLevel: "OFF", Storage: "Argument", DataSets: "currentWeather,forecastDaily,forecastHourly", Weather: { Replace: "CN", Provider: "ColorfulClouds" } }; }); +function mockWeather(t) { + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => ({ metadata, temperature: 27.125, conditionCode: "RAIN" })); + t.mock.method(ColorfulClouds.prototype, "Daily", async () => ({ metadata, days: [{ forecastStart: original.forecastDaily.days[0].forecastStart, temperatureMax: 31.25, conditionCode: "SNOW", daytimeForecast: { cloudCover: 80 } }] })); + t.mock.method(ColorfulClouds.prototype, "ForecastHourly", async () => ({ metadata, hours: [{ forecastStart: original.forecastHourly.hours[0].forecastStart, temperature: 24.5, conditionCode: "RAIN", precipitationAmount: 2.5 }] })); +} + +test("WK3 weather replacement stays binary and preserves every unrelated root arena", async t => { + mockWeather(t); + const req = (await Request(request())).$request; + assert.equal(req.headers.Accept, "application/vnd.apple.flatbuffer;messageType=WK3.Weather"); + const output = (await response()).body; + assert.ok(output instanceof Uint8Array); + const body = WK3.Codec.decode("Weather", WK3.Weather.getRootAsWeather(new ByteBuffer(output))); + assert.equal(body.currentWeather.temperature, 27.125); + assert.deepEqual(body.currentWeather.conditionCode, ["RAIN"]); + assert.equal(body.forecastDaily.days[0].temperatureMax, 31.25); + assert.deepEqual(body.forecastDaily.days[0].conditionCode, ["SNOW"]); + assert.equal(body.forecastDaily.days[0].daytimeForecast.cloudCover, 80); + assert.equal(body.forecastHourly.hours[0].temperature, 24.5); + assert.deepEqual(body.forecastHourly.hours[0].precipitationAmount, { expected: 2.5, minimum: 2.5, maximum: 2.5 }); + const shift = pointer(output, 9) - pointer(source, 9); + assert.deepEqual(output.subarray(shift, shift + source.length), source); + for (const slot of [0, 4, 6, 7, 9, 14]) assert.equal(pointer(output, slot) - shift, pointer(source, slot)); +}); + +test("a failed current-weather provider leaves that arena intact while hourly replacement succeeds", async t => { + mockWeather(t); + t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => { throw new Error("offline"); }); + const output = (await response()).body, shift = pointer(output, 9) - pointer(source, 9); + assert.equal(pointer(output, 1) - shift, pointer(source, 1)); + assert.equal(WK3.Weather.getRootAsWeather(new ByteBuffer(output)).forecastHourly().hours(0).temperature(), 24.5); +}); + +test("a future current-weather field is kept opaque and never reaches the provider", async t => { + mockWeather(t); + const now = t.mock.method(ColorfulClouds.prototype, "CurrentWeather", async () => assert.fail("future product must not be rewritten")); + const bytes = source.slice(), bb = new ByteBuffer(bytes), p = pointer(bytes, 1), vtable = p - bb.readInt32(p); + new DataView(bytes.buffer).setUint16(vtable, 4 + 34 * 2, true); + const output = (await response(bytes)).body, shift = pointer(output, 9) - pointer(bytes, 9); + assert.equal(now.mock.callCount(), 0); + assert.equal(pointer(output, 1) - shift, pointer(bytes, 1)); + assert.deepEqual(output.subarray(shift, shift + bytes.length), bytes); +}); + +test("disabled or native-provider weather stays byte-identical", async t => { + mockWeather(t); + globalThis.$argument.DataSets = "[]"; + assert.deepEqual((await response()).body, source); + globalThis.$argument.DataSets = "currentWeather,forecastDaily,forecastHourly"; + globalThis.$argument.Weather.Provider = "WeatherKit"; + assert.deepEqual((await response()).body, source); +}); From 2bb5d5a5789ab5801d79d4fdc9a325d99e9122a3 Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Sat, 14 Nov 2026 17:29:03 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test(wk3):=20=E5=AF=B9=E9=BD=90=20JSON=20?= =?UTF-8?q?=E4=BC=98=E5=85=88=E7=AD=96=E7=95=A5=E4=B8=8E=E8=B7=A8=20PR=20?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/wk3-binary-weather.md | 37 ++++++++++++++++--- .../README.md => wk3-binary-integration.md} | 0 tests/weatherKit3WeatherReplacement.test.mjs | 4 +- 3 files changed, 33 insertions(+), 8 deletions(-) rename tests/fixtures/{wk3-paired/README.md => wk3-binary-integration.md} (100%) diff --git a/docs/wk3-binary-weather.md b/docs/wk3-binary-weather.md index 98000c43..75d5cd6e 100644 --- a/docs/wk3-binary-weather.md +++ b/docs/wk3-binary-weather.md @@ -4,13 +4,31 @@ `Response.dev.mjs` 中替换当前、逐日、逐小时天气。`WeatherKit3.mjs` 保留产品注册和 供应商数据形态适配,字段布局与枚举读写统一由协议包负责。 -## 与其他改动的关系 +## 三个 PR 的衔接 -- #110 负责主 App 的 JSON 路径,独立审阅。本分支以 dev 为基础,不包含 #110 的 JSON 实现。 -- 本分支先保持 Draft,等待协议包合并、发布,再更新 `@nsringo/weatherkit` 依赖和 lockfile。 - 现有 2.0.5 没有 `WK3.Schema` / `WK3.Codec`,无法直接运行本分支。 -- 分支中的彩云 cloudCover 修复与 #110 的对应修复相同;#110 合并后同步 dev,消除这部分重复。 -- macOS 小组件的 JSON 白名单是另一项客户端策略变更,单独提交。 +- [apple.WeatherKit #1](https://github.com/NSRingo/apple.WeatherKit/pull/1) 提供 Schema/Codec; + 协议包只负责表示层,不管理请求或供应商。 +- [WeatherKit #110](https://github.com/NSRingo/WeatherKit/pull/110) 负责 JSON 协商和 JSON 响应。 + 符合地区、供应商、数据集设置的主 App 请求继续协商 `Accept: application/json`。 +- [WeatherKit #111](https://github.com/NSRingo/WeatherKit/pull/111) 按实际响应 Content-Type + 处理 WK3 FlatBuffer;不修改 Request.dev.mjs,也不覆盖 #110 的 Accept 选择。 + +正常主 App JSON 响应在 JSON 分支返回,二进制 decoder/encoder 不执行;每个产品 +只调用一次供应商。仍收到 FlatBuffer 的请求进入本 PR 的产品替换与原始数据保留链。 +这让两种格式共享既有产品注入函数,不需要第二套客户端白名单或自动重试协议。 + +建议保留上述 JSON 优先策略,由维护者确认长期路线。下方主 App 二进制验收使用专用 +测试模块保留原生 Accept,用于证明 codec 与原生客户端兼容,不表示切换主 App 默认路由。 +macOS 小组件 JSON 白名单在补完显示与连续刷新验收后,可沿 #110 的协商边界扩展。 + +## 合并顺序 + +#110 和协议包 #1 可分别审阅。#111 保持 Draft,等待 #1 发布,再更新协议依赖及 +lockfile、同步已合并的 #110、重跑组合验证后合并。现有 2.0.5 没有 WK3.Schema/Codec, +本分支暂时使用协议包源码构建产物验证。 + +本分支的 cloudCover 修复与 #110 相同,同步到已合并 #110 的 dev 后消除这部分重复。 +样本说明使用独立文件名,避免两个 PR 同时新增 README 的冲突。 ## 验证 @@ -48,5 +66,12 @@ npm run build:dev | 18 时 | 17.68° | 18° | | 19 时 | 17.49° | 17° | +## 组合验证 + +将 #110 的 JSON 改动与本分支合并到独立 checkout,并使用协议包 #1 的构建产物。 +组合回归覆盖主 App JSON 协商、JSON 不进入 binary codec、每个产品只调用一次供应商、 +未协商 JSON 的 Widget 请求保持 FlatBuffer,以及新旧 schema 的成对样本对照。 +组合后的 161 项测试全部通过(0 跳过),正式和开发构建通过。对应提交见 PR 正文。 + 后续客户端验收按入口分别记录:macOS 小组件的 JSON 路径补实际显示和连续刷新; Siri、地图等入口先确认请求与响应格式,再测试该入口,不预先声明客户端覆盖。 diff --git a/tests/fixtures/wk3-paired/README.md b/tests/fixtures/wk3-binary-integration.md similarity index 100% rename from tests/fixtures/wk3-paired/README.md rename to tests/fixtures/wk3-binary-integration.md diff --git a/tests/weatherKit3WeatherReplacement.test.mjs b/tests/weatherKit3WeatherReplacement.test.mjs index a2e930f0..9a585971 100644 --- a/tests/weatherKit3WeatherReplacement.test.mjs +++ b/tests/weatherKit3WeatherReplacement.test.mjs @@ -13,7 +13,7 @@ const [{ Response }, { Request }, { default: ColorfulClouds }] = await Promise.a const source = new Uint8Array(readFileSync(new URL("./fixtures/wk3-paired/hangzhou/apple.bin", import.meta.url))); const original = WK3.Codec.decode("Weather", WK3.Weather.getRootAsWeather(new ByteBuffer(source))); const metadata = { providerName: "Controlled provider", sourceType: "MODELED", temporarilyUnavailable: false, latitude: "30", longitude: "120" }; -const request = () => ({ method: "GET", url: "https://weatherkit.apple.com/api/v3/weather/en-US/30/120?country=CN&dataSets=currentWeather,forecastDaily,forecastHourly,weatherAlerts", headers: { Accept: "application/vnd.apple.flatbuffer;messageType=WK3.Weather", "User-Agent": "WeatherKit_Weather_macOS_Version 27.3" } }); +const request = () => ({ method: "GET", url: "https://weatherkit.apple.com/api/v3/weather/en-US/30/120?country=CN&dataSets=currentWeather,forecastDaily,forecastHourly,weatherAlerts", headers: { Accept: "application/vnd.apple.flatbuffer;messageType=WK3.Weather", "User-Agent": "WeatherWidget" } }); const response = (body = source) => Response(request(), { status: 200, headers: { "Content-Type": "application/vnd.apple.flatbuffer;messageType=WK3.Weather" }, bodyBytes: body }); function pointer(bytes, slot) { const b = new ByteBuffer(bytes), root = b.readUint32(0), off = b.__offset(root, 4 + 2 * slot); return off ? b.__indirect(root + off) : 0; } beforeEach(() => { globalThis.$request = request(); globalThis.$argument = { LogLevel: "OFF", Storage: "Argument", DataSets: "currentWeather,forecastDaily,forecastHourly", Weather: { Replace: "CN", Provider: "ColorfulClouds" } }; }); @@ -23,7 +23,7 @@ function mockWeather(t) { t.mock.method(ColorfulClouds.prototype, "ForecastHourly", async () => ({ metadata, hours: [{ forecastStart: original.forecastHourly.hours[0].forecastStart, temperature: 24.5, conditionCode: "RAIN", precipitationAmount: 2.5 }] })); } -test("WK3 weather replacement stays binary and preserves every unrelated root arena", async t => { +test("WK3 FlatBuffer requests stay binary and preserve every unrelated root arena", async t => { mockWeather(t); const req = (await Request(request())).$request; assert.equal(req.headers.Accept, "application/vnd.apple.flatbuffer;messageType=WK3.Weather"); From 7ad62fa63a7f14e8334caedb1c071e38f6438304 Mon Sep 17 00:00:00 2001 From: hhh2210 Date: Sat, 14 Nov 2026 18:59:10 +0800 Subject: [PATCH 4/4] docs(wk3): add native widget binary validation --- docs/images/wk3-widget-tianjin.png | Bin 0 -> 113887 bytes docs/wk3-binary-weather.md | 30 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 docs/images/wk3-widget-tianjin.png diff --git a/docs/images/wk3-widget-tianjin.png b/docs/images/wk3-widget-tianjin.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae93d52b0792c4000731affa2c673a734813d69 GIT binary patch literal 113887 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 QB6%NX#Uon?qtNwvVT`TxKQ6G2t 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 V!Nkl!BDMek002ovPDHLkV1j9K-G2Z8 literal 0 HcmV?d00001 diff --git a/docs/wk3-binary-weather.md b/docs/wk3-binary-weather.md index 75d5cd6e..7d3d5e42 100644 --- a/docs/wk3-binary-weather.md +++ b/docs/wk3-binary-weather.md @@ -19,7 +19,7 @@ 建议保留上述 JSON 优先策略,由维护者确认长期路线。下方主 App 二进制验收使用专用 测试模块保留原生 Accept,用于证明 codec 与原生客户端兼容,不表示切换主 App 默认路由。 -macOS 小组件 JSON 白名单在补完显示与连续刷新验收后,可沿 #110 的协商边界扩展。 +macOS 小组件已通过下方二进制实测,本轮保留 FlatBuffer 路径,暂不扩展 JSON 白名单。 ## 合并顺序 @@ -73,5 +73,29 @@ npm run build:dev 未协商 JSON 的 Widget 请求保持 FlatBuffer,以及新旧 schema 的成对样本对照。 组合后的 161 项测试全部通过(0 跳过),正式和开发构建通过。对应提交见 PR 正文。 -后续客户端验收按入口分别记录:macOS 小组件的 JSON 路径补实际显示和连续刷新; -Siri、地图等入口先确认请求与响应格式,再测试该入口,不预先声明客户端覆盖。 +## macOS 小组件实测(2026-11-14) + +macOS 27.3(26C5035r)、Surge 6.12.0(12840)。主 App 隐藏,用户在桌面小组件中 +依次选择天津市津南区、昆明市、福州市,再恢复原来的当前位置。三次抓到的请求进程均为 +`WeatherWidget`,UA 均为 `WeatherKit_WeatherWidget_macOS_Version 27.3 (Build 26C5035r)`。 +Accept 和响应 Content-Type 均为 `application/vnd.apple.flatbuffer;messageType=WK3.Weather`。 + +| 地点 / 请求时间(UTC+8) | 当前温度:解码 → 显示 | 当日高 / 低:解码 → 显示 | 成功构建 timeline | +| --- | --- | --- | --- | +| 天津市津南区 / 18:50:29 | 7.74° → 8° | 13.42° / 5.07° → 13° / 5° | 2 次 | +| 昆明市 / 18:54:16 | 16.44° → 16° | 18.65° / 9.36° → 19° / 9° | 2 次 | +| 福州市 / 18:54:27 | 22.07° → 22° | 23.10° / 22.00° → 23° / 22° | 1 次 | + +三次改写响应的当前、逐日、逐小时 metadata.providerName 均为彩云天气,分别含 +10 天和 24 小时预报。请求完成后的原生日志成功获取天气并构建 timeline,观察窗口内 +`failedToCreateResponseModel`、`Unable to decode`、`FlatbuffersErrors` 均为零。 + +津南区同时保存了像素截图;昆明、福州用原生 Accessibility 文本核对完整显示结果。 +津南区 19–0 时温度的二进制值为 6.63、6.58、6.54、6.54、6.43、6.33°, +截图显示 7、7、7、7、6、6°。 + +![macOS 天气小组件:天津市津南区的 WK3 二进制替换结果](images/wk3-widget-tianjin.png) + +因此本轮保留“主 App JSON、小组件 FlatBuffer”的分工,暂不扩展小组件 JSON 白名单。 +Siri、地图、watchOS 等入口后续分别抓请求和验收。测试结束后恢复了原模块、当前位置 +与原桌面,并移除了只针对 WeatherKit 的临时抓包规则。