JavaScript 変換
デフォルトでは、購読者は canonical な JSON レコードをそのまま受け取り ます。サーバーが別の形 — Markdown、異なる JSON 構造、プレーンテキスト 1 行 — を求めるときは、その購読者に変換スクリプトを設定し、ボディを 自分で組み立ててください。
契約
あなたが用意するのは関数 1 つです。
js
function transform(data, context) {
// HTTP リクエストの「ボディ」を return する
}- 文字列を返す → そのまま送信されます(Markdown、プレーンテキスト、 フォームエンコード、…)。
- オブジェクトまたは配列を返す →
JSON.stringify(...)の結果が送信 され、Content-Typeはapplication/jsonのままです。 - それ以外(
undefined、数値、真偽値)を返す → 送信は意図的に 失敗します。Courier は空のデータや作りかけのデータを決して送りま せん。
スクリプトが制御するのはボディだけです。URL、メソッド、ヘッダー、 認証は購読者の設定どおりに維持されます。スクリプトを設定すると、静的な ボディパラメータはバイパスされます — ボディはあなた自身が組み立てます。
サンドボックス
スクリプトは、デバイス上の密閉された JavaScriptCore サンドボックスで実行 されます。
- ネットワークなし、ファイルシステムなし、DOM なし、
requireなし、setTimeoutなし。 - エラーや無限ループは送信を失敗させ、その理由は発行履歴に表示され ます。
console.log/warn/errorはキャプチャされ、テストプレビュー でのみ表示されます。
context リファレンス
すべての値は文字列です。
| フィールド | 例 |
|---|---|
context.endpointName | "My Obsidian relay" |
context.url | 送り先の URL |
context.kind | "workout" | "sleep" | "note" | "weather" |
context.now | この発行の ISO-8601 タイムスタンプ |
context.timeZone | "Asia/Shanghai" |
context.locale | "zh-Hans" |
context.appVersion / context.appBuild | "1.0.0" / "1" |
context.idempotencyKey | この送信に付く Idempotency-Key |
data リファレンス
data は、その購読者の種類に対応する canonical レコードです。日付は ISO-8601 文字列で(new Date(data.start) でパースできます)、オプションの フィールドは存在しないことがあります。
kind === "workout"
| Field | Type | 備考 |
|---|---|---|
id | string | HealthKit の UUID — 冪等性の基点 |
activityType | string | "running"、"cycling"、"walking"、… |
source | object | { name, bundleIdentifier?, productType?, version? } |
start, end | string | ISO-8601 |
duration | number | 秒 |
distanceMeters | number? | |
activeEnergyKcal, totalEnergyKcal | number? | |
elevationGainMeters | number? | |
heartRate | object? | { averageBpm?, minBpm?, maxBpm? } |
avgPaceSecPerKm | number? | |
laps | array? | [{ index, start, duration, distanceMeters? }] |
heartRateSeries | array? | 心拍系列のトグルがオンのときのみ |
route | array? | ルートのトグルがオンのときのみ。大きい |
metadata | object? | HealthKit のメタデータをそのまま透過 |
schemaVersion | number |
kind === "sleep"
| Field | Type | 備考 |
|---|---|---|
id | string | |
nightOf | string | この一晩が帰属する日 |
inBedStart, inBedEnd | string? | |
sleepStart, sleepEnd | string | |
timeInBedSec | number? | |
totalAsleepSec | number | |
stages | object | { awakeSec, remSec, coreSec, deepSec, unspecifiedSec } |
awakenings | number | |
efficiencyPct | number? | 0–100 |
vitals | object? | { averageHeartRateBpm?, averageHRVms?, respiratoryRate?, oxygenSaturation?, wristTemperatureDeltaC? } |
segments | array | [{ stage, start, end }]、stage は inBed|awake|rem|core|deep|unspecified |
sources | array | [{ name, bundleIdentifier?, … }] |
schemaVersion | number |
kind === "note"
| Field | Type | 備考 |
|---|---|---|
id | string | |
createdAt | string | ISO-8601 |
text | string | 記録された思考(入力または書き起こし) |
source | string | "text" | "audio" |
metadata | object? | |
schemaVersion | number |
kind === "weather"
| Field | Type | 備考 |
|---|---|---|
id | string | "{sleepID}:weather" |
capturedAt | string | 測定時刻(≈ 起床時刻) |
weatherDate | string | 対になる睡眠の nightOf |
symbolName | string | SF Symbol 形式。例:"cloud.sun" |
conditionText | string | 例:"Cloudy" |
temperatureC, apparentTemperatureC | number | |
humidityPct | number | 0–100 |
windKmh | number | |
uvIndex | number | |
precipitationChancePct | number | 0–100 |
location | object? | { lat, lon } — 座標のトグルがオンのときのみ |
schemaVersion | number |
例
独自の JSON 構造:
js
function transform(data, context) {
return {
kind: "workout",
activity: data.activityType,
km: data.distanceMeters ? +(data.distanceMeters / 1000).toFixed(2) : null,
minutes: Math.round(data.duration / 60),
avgHr: (data.heartRate && data.heartRate.averageBpm) || null,
at: data.start,
via: context.endpointName,
};
}プレーンテキスト 1 行:
js
function transform(data) {
const h = Math.floor(data.totalAsleepSec / 3600);
const m = Math.round((data.totalAsleepSec % 3600) / 60);
return "Slept " + h + "h" + m + "m on " + data.nightOf.slice(0, 10) +
" (" + data.awakenings + " awakenings)";
}注釈を Markdown の引用ブロックに:
js
function transform(data) {
return "> " + data.text + "\n>\n> — " + data.createdAt;
}ワークフロー
- 購読者エディタでスクリプトセクションを開き、
.jsファイルを インポートするか、コードを貼り付けます。出発点として、コメントが ひととおり付いたテンプレートをアプリから受け取ることもできます。 - テストを押します。スクリプトが最近のサンプルに対して実行され、 実際に何かが送られる前に、出力ボディそのもの、コンソールログ、 エンドポイントの HTTP レスポンスを確認できます。
- 保存します。以後、この購読者へのすべての発行はスクリプトを通ります。 スクリプトのエラーは、壊れたデータを届ける代わりに、発行履歴上で 目に見える形で送信を失敗させます。
スクリプトは設定バックアップに含まれます
シークレット(キーチェーン保管、決して書き出されません)とは異なり、 変換スクリプトは設定バックアップに平文で含まれます — スクリプト内に 認証情報をハードコードしないでください。