@camstack/types 1.2.443 → 1.2.445
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/addon.d.ts +5 -1
- package/dist/addon.js +21 -5
- package/dist/addon.mjs +17 -6
- package/dist/automation/export-entities.d.ts +98 -0
- package/dist/capabilities/capability-definition.d.ts +1 -1
- package/dist/capabilities/composer.cap.d.ts +34 -0
- package/dist/capabilities/consumables.cap.d.ts +4 -5
- package/dist/capabilities/core-blocks.cap.d.ts +244 -0
- package/dist/capabilities/device-export.cap.d.ts +111 -1
- package/dist/capabilities/index.d.ts +6 -4
- package/dist/capabilities/metrics-provider.cap.d.ts +16 -16
- package/dist/capabilities/nodes.cap.d.ts +2 -2
- package/dist/capabilities/pet-fountain.cap.d.ts +138 -0
- package/dist/capabilities/pipeline-executor.cap.d.ts +6 -22
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +2 -2
- package/dist/capabilities/pipeline-runner.cap.d.ts +2 -2
- package/dist/capabilities/pipeline-step-input.d.ts +33 -0
- package/dist/capabilities/server-management.cap.d.ts +28 -0
- package/dist/client.js +820 -799
- package/dist/client.mjs +820 -799
- package/dist/composition/composition-items.d.ts +3 -3
- package/dist/composition/composition-report.d.ts +17 -0
- package/dist/composition/composition.d.ts +68 -1
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/presets/pet-fountain.d.ts +36 -0
- package/dist/composition/validate-forward.d.ts +31 -0
- package/dist/{composition-C0dlyG9U.js → composition-CO6Sak-s.js} +49 -5
- package/dist/{composition-bIBW5_13.mjs → composition-kSuOifuG.mjs} +32 -6
- package/dist/device/cap-vocabulary/entries/appliances.d.ts +1 -1
- package/dist/device/device-control-resolution.d.ts +1 -1
- package/dist/device/device-type.d.ts +4 -0
- package/dist/{err-msg-BCdlR2Li.mjs → err-msg-DWXWc0Di.mjs} +4 -0
- package/dist/{err-msg-COHgq7Mb.js → err-msg-EJhsFHqa.js} +4 -0
- package/dist/generated/addon-api.d.ts +148 -32
- package/dist/generated/addon-cap-facts.d.ts +1 -1
- package/dist/generated/cap-status-types.d.ts +3 -1
- package/dist/generated/capability-router-map.d.ts +5 -2
- package/dist/generated/collection-array-methods.d.ts +2 -2
- package/dist/generated/customize-catalog.d.ts +1 -1
- package/dist/generated/device-local-state.d.ts +3 -0
- package/dist/generated/device-proxy.d.ts +3 -0
- package/dist/generated/device-scoped-caps.d.ts +1 -1
- package/dist/generated/history-catalog.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +1 -1
- package/dist/generated/runtime-state-durability.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1465 -603
- package/dist/index.mjs +1433 -592
- package/dist/interfaces/agent.d.ts +6 -2
- package/dist/interfaces/event-bus.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/node.mjs +2 -2
- package/dist/{sleep-C_u6cEAJ.mjs → sleep-6eG9C8Xl.mjs} +223 -72
- package/dist/{sleep-E7qAQwTE.js → sleep-UXDHn-oJ.js} +246 -71
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -697,6 +697,10 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
697
697
|
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
698
698
|
* integrations sharing the same food/desiccant/hopper surface. */
|
|
699
699
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
700
|
+
/** Pet water fountain — tank level, water drunk, pump and its duty cycle.
|
|
701
|
+
* Installed with the `pet-fountain` cap. Reaches CamStack as loose entities
|
|
702
|
+
* of a container and is BUILT as a composed device (D967). */
|
|
703
|
+
DeviceType["PetFountain"] = "pet-fountain";
|
|
700
704
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
701
705
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
702
706
|
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
@@ -1115,6 +1119,431 @@ function optionalMethod(input, output, options) {
|
|
|
1115
1119
|
};
|
|
1116
1120
|
}
|
|
1117
1121
|
//#endregion
|
|
1122
|
+
//#region src/capabilities/notification-output.cap.ts
|
|
1123
|
+
/**
|
|
1124
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
1125
|
+
*
|
|
1126
|
+
* Apprise-derived model (see
|
|
1127
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
1128
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
1129
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
1130
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
1131
|
+
* message to what the kind supports — callers never special-case a service.
|
|
1132
|
+
*
|
|
1133
|
+
* DESIGN DECISIONS (locked):
|
|
1134
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
1135
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
1136
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
1137
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
1138
|
+
* alternative would fork the UI per addon and cannot host the
|
|
1139
|
+
* discovery→adopt flow.
|
|
1140
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
1141
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
1142
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
1143
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
1144
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
1145
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
1146
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
1147
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
1148
|
+
* base64 fallback needed.
|
|
1149
|
+
*
|
|
1150
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
1151
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
1152
|
+
* admin "Integrations" page.
|
|
1153
|
+
*/
|
|
1154
|
+
/**
|
|
1155
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
1156
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
1157
|
+
*/
|
|
1158
|
+
var AttachmentMediaTypeSchema = zod.z.enum([
|
|
1159
|
+
"image",
|
|
1160
|
+
"video",
|
|
1161
|
+
"gif",
|
|
1162
|
+
"audio",
|
|
1163
|
+
"icon"
|
|
1164
|
+
]);
|
|
1165
|
+
/**
|
|
1166
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
1167
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
1168
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
1169
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
1170
|
+
*/
|
|
1171
|
+
var AttachmentSchema = zod.z.object({
|
|
1172
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
1173
|
+
url: zod.z.string().optional(),
|
|
1174
|
+
bytes: zod.z.instanceof(Uint8Array).optional(),
|
|
1175
|
+
mime: zod.z.string().optional(),
|
|
1176
|
+
name: zod.z.string().optional()
|
|
1177
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
1178
|
+
var NotificationFormatSchema = zod.z.enum([
|
|
1179
|
+
"text",
|
|
1180
|
+
"markdown",
|
|
1181
|
+
"html"
|
|
1182
|
+
]);
|
|
1183
|
+
/**
|
|
1184
|
+
* The CLOSED icon vocabulary an action button may use.
|
|
1185
|
+
*
|
|
1186
|
+
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
1187
|
+
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
1188
|
+
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
1189
|
+
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
1190
|
+
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
1191
|
+
*
|
|
1192
|
+
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
1193
|
+
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
1194
|
+
* differently.
|
|
1195
|
+
*
|
|
1196
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
1197
|
+
*
|
|
1198
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
1199
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
1200
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
1201
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
1202
|
+
* decides its glyph, which is the only place that decision can be made
|
|
1203
|
+
* honestly.
|
|
1204
|
+
*
|
|
1205
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
1206
|
+
* `disarm` straight through; iOS feeds that string to
|
|
1207
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
1208
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
1209
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
1210
|
+
*
|
|
1211
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
1212
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
1213
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
1214
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
1215
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
1216
|
+
*/
|
|
1217
|
+
var NotificationActionIconSchema = zod.z.enum([
|
|
1218
|
+
"acknowledge",
|
|
1219
|
+
"dismiss",
|
|
1220
|
+
"silence",
|
|
1221
|
+
"snooze",
|
|
1222
|
+
"view",
|
|
1223
|
+
"play",
|
|
1224
|
+
"open",
|
|
1225
|
+
"close",
|
|
1226
|
+
"lock",
|
|
1227
|
+
"unlock",
|
|
1228
|
+
"arm",
|
|
1229
|
+
"arm-home",
|
|
1230
|
+
"arm-away",
|
|
1231
|
+
"arm-night",
|
|
1232
|
+
"disarm",
|
|
1233
|
+
"light",
|
|
1234
|
+
"alert",
|
|
1235
|
+
"camera"
|
|
1236
|
+
]);
|
|
1237
|
+
/** A single tap-through action button. */
|
|
1238
|
+
var NotificationActionSchema = zod.z.object({
|
|
1239
|
+
id: zod.z.string(),
|
|
1240
|
+
label: zod.z.string(),
|
|
1241
|
+
url: zod.z.string().optional(),
|
|
1242
|
+
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
1243
|
+
icon: NotificationActionIconSchema.optional(),
|
|
1244
|
+
/**
|
|
1245
|
+
* Renders in a warning style where the notifier supports it.
|
|
1246
|
+
*
|
|
1247
|
+
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
1248
|
+
* else — see `notification-center/action-token.ts` for what that does and
|
|
1249
|
+
* does not buy.
|
|
1250
|
+
*/
|
|
1251
|
+
destructive: zod.z.boolean().optional(),
|
|
1252
|
+
/**
|
|
1253
|
+
* How the tap should REACH the url.
|
|
1254
|
+
*
|
|
1255
|
+
* `navigate` (absent, and every button authored before this field) opens it:
|
|
1256
|
+
* the phone leaves the notification and shows whatever the callback returns.
|
|
1257
|
+
* That is right for a button whose answer the operator wants to read.
|
|
1258
|
+
*
|
|
1259
|
+
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
1260
|
+
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
1261
|
+
* an answer to the notification, and being thrown into a browser tab to
|
|
1262
|
+
* confirm it costs more attention than the notification did. A backend that
|
|
1263
|
+
* cannot do a background call renders it as an ordinary link (the adapters
|
|
1264
|
+
* fall back rather than dropping the button), so this is a preference, never
|
|
1265
|
+
* a requirement.
|
|
1266
|
+
*/
|
|
1267
|
+
mode: zod.z.enum(["navigate", "background"]).optional()
|
|
1268
|
+
});
|
|
1269
|
+
/** A delivery parameter value: what a notifier declared it takes. */
|
|
1270
|
+
var DeliveryParamValueSchema = zod.z.union([
|
|
1271
|
+
zod.z.string(),
|
|
1272
|
+
zod.z.number(),
|
|
1273
|
+
zod.z.boolean()
|
|
1274
|
+
]);
|
|
1275
|
+
/**
|
|
1276
|
+
* One extra delivery parameter a notifier KIND declares (D938). A rule that
|
|
1277
|
+
* addresses a target of that kind may set it; a kind that declares nothing is
|
|
1278
|
+
* never handed one. The admin UI derives its control from this descriptor —
|
|
1279
|
+
* no editor knows a service by name.
|
|
1280
|
+
*
|
|
1281
|
+
* Optional fields are `.optional()`, never `.default()` (a Zod default does not
|
|
1282
|
+
* run on the addon cap path).
|
|
1283
|
+
*/
|
|
1284
|
+
var DeliveryParamSchema = zod.z.object({
|
|
1285
|
+
key: zod.z.string().min(1),
|
|
1286
|
+
label: zod.z.string(),
|
|
1287
|
+
/** i18n key of the label, resolved by the declaring addon's namespace. */
|
|
1288
|
+
labelKey: zod.z.string().optional(),
|
|
1289
|
+
type: zod.z.enum([
|
|
1290
|
+
"enum",
|
|
1291
|
+
"number",
|
|
1292
|
+
"boolean",
|
|
1293
|
+
"string"
|
|
1294
|
+
]),
|
|
1295
|
+
/** `enum` only: the closed set of accepted values. */
|
|
1296
|
+
options: zod.z.array(zod.z.object({
|
|
1297
|
+
value: zod.z.string(),
|
|
1298
|
+
label: zod.z.string()
|
|
1299
|
+
})).optional(),
|
|
1300
|
+
/** `number` only. */
|
|
1301
|
+
min: zod.z.number().optional(),
|
|
1302
|
+
max: zod.z.number().optional(),
|
|
1303
|
+
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
1304
|
+
default: DeliveryParamValueSchema.optional()
|
|
1305
|
+
});
|
|
1306
|
+
/**
|
|
1307
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
1308
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
1309
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
1310
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
1311
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
1312
|
+
* `priority` for that one target.
|
|
1313
|
+
*/
|
|
1314
|
+
var NotificationSchema = zod.z.object({
|
|
1315
|
+
body: zod.z.string(),
|
|
1316
|
+
title: zod.z.string().optional(),
|
|
1317
|
+
format: NotificationFormatSchema.default("text"),
|
|
1318
|
+
priority: zod.z.number().int().min(1).max(5).default(3),
|
|
1319
|
+
level: zod.z.string().optional(),
|
|
1320
|
+
attachments: zod.z.array(AttachmentSchema).optional(),
|
|
1321
|
+
clickUrl: zod.z.string().optional(),
|
|
1322
|
+
actions: zod.z.array(NotificationActionSchema).optional(),
|
|
1323
|
+
sound: zod.z.string().optional(),
|
|
1324
|
+
ttl: zod.z.number().optional(),
|
|
1325
|
+
tag: zod.z.string().optional(),
|
|
1326
|
+
deviceId: zod.z.number().optional(),
|
|
1327
|
+
eventId: zod.z.string().optional(),
|
|
1328
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
1329
|
+
/**
|
|
1330
|
+
* Values for the parameters the target's kind DECLARES in
|
|
1331
|
+
* {@link TargetKindSchema} `deliveryParams`. Already validated against the
|
|
1332
|
+
* declaration by the sender; a notifier reads only the keys it declared.
|
|
1333
|
+
*/
|
|
1334
|
+
delivery: zod.z.record(zod.z.string(), DeliveryParamValueSchema).optional()
|
|
1335
|
+
});
|
|
1336
|
+
/** One declared native severity/priority level for a kind. */
|
|
1337
|
+
var TargetKindLevelSchema = zod.z.object({
|
|
1338
|
+
id: zod.z.string(),
|
|
1339
|
+
label: zod.z.string(),
|
|
1340
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
1341
|
+
ordinal: zod.z.number().int().min(1).max(5).nullable(),
|
|
1342
|
+
flags: zod.z.object({
|
|
1343
|
+
critical: zod.z.boolean().optional(),
|
|
1344
|
+
silent: zod.z.boolean().optional(),
|
|
1345
|
+
noPush: zod.z.boolean().optional()
|
|
1346
|
+
}).optional(),
|
|
1347
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
1348
|
+
requires: zod.z.array(zod.z.string()).optional(),
|
|
1349
|
+
description: zod.z.string().optional()
|
|
1350
|
+
});
|
|
1351
|
+
/** Attachment capabilities for a kind (drives the degrade engine + test panel). */
|
|
1352
|
+
var TargetKindAttachmentsCapsSchema = zod.z.object({
|
|
1353
|
+
mediaTypes: zod.z.array(AttachmentMediaTypeSchema),
|
|
1354
|
+
mode: zod.z.enum([
|
|
1355
|
+
"url",
|
|
1356
|
+
"bytes",
|
|
1357
|
+
"both"
|
|
1358
|
+
]),
|
|
1359
|
+
max: zod.z.number().int().nonnegative(),
|
|
1360
|
+
maxBytes: zod.z.number().int().positive().optional()
|
|
1361
|
+
});
|
|
1362
|
+
/** The full capability block consulted before dispatch. */
|
|
1363
|
+
var TargetKindCapsSchema = zod.z.object({
|
|
1364
|
+
attachments: TargetKindAttachmentsCapsSchema,
|
|
1365
|
+
/** Max action buttons (0 = none). */
|
|
1366
|
+
actions: zod.z.number().int().nonnegative(),
|
|
1367
|
+
/**
|
|
1368
|
+
* Whether this kind renders a per-action ICON.
|
|
1369
|
+
*
|
|
1370
|
+
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
1371
|
+
* run on the addon cap path — three production failures in one day taught
|
|
1372
|
+
* this repo that once. Absent is read as false by the degrade engine, which
|
|
1373
|
+
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
1374
|
+
* assumed and dropped costs the operator's trust in the field.
|
|
1375
|
+
*/
|
|
1376
|
+
actionIcons: zod.z.boolean().optional(),
|
|
1377
|
+
levels: zod.z.array(TargetKindLevelSchema),
|
|
1378
|
+
format: zod.z.array(NotificationFormatSchema),
|
|
1379
|
+
clickUrl: zod.z.boolean(),
|
|
1380
|
+
sound: zod.z.boolean(),
|
|
1381
|
+
ttl: zod.z.boolean(),
|
|
1382
|
+
bodyMaxLen: zod.z.number().int().positive()
|
|
1383
|
+
});
|
|
1384
|
+
/**
|
|
1385
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
1386
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
1387
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
1388
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
1389
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
1390
|
+
*/
|
|
1391
|
+
var ConfigSchemaPassthrough = zod.z.unknown();
|
|
1392
|
+
var TargetKindSchema = zod.z.object({
|
|
1393
|
+
kind: zod.z.string(),
|
|
1394
|
+
label: zod.z.string(),
|
|
1395
|
+
icon: zod.z.string(),
|
|
1396
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
1397
|
+
addonId: zod.z.string(),
|
|
1398
|
+
/**
|
|
1399
|
+
* URL of the kind's bundled BRAND icon, served by the providing addon over
|
|
1400
|
+
* its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
|
|
1401
|
+
* when the addon bundles no icon for that kind — the client then falls back
|
|
1402
|
+
* to a neutral glyph rather than rendering the raw `icon` NAME as text.
|
|
1403
|
+
*
|
|
1404
|
+
* Root-relative on purpose: it resolves against whatever origin serves a web
|
|
1405
|
+
* client, and a native client joins it onto its own hub base.
|
|
1406
|
+
*
|
|
1407
|
+
* DECLARED here deliberately. It used to travel as an undeclared passthrough
|
|
1408
|
+
* field that survived only because the runtime cap-router forwards provider
|
|
1409
|
+
* output verbatim — so every consumer had to re-declare it by hand to stop
|
|
1410
|
+
* its own Zod parse from stripping it, and the whole arrangement would have
|
|
1411
|
+
* broken silently the moment output validation was tightened anywhere.
|
|
1412
|
+
*/
|
|
1413
|
+
iconUrl: zod.z.string().optional(),
|
|
1414
|
+
/**
|
|
1415
|
+
* Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
|
|
1416
|
+
*
|
|
1417
|
+
* The server knows this and therefore says it, because the client cannot
|
|
1418
|
+
* safely guess: a React-Native client renders SVG and raster through two
|
|
1419
|
+
* DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
|
|
1420
|
+
* not decode SVG on iOS/Android), so without this it silently fell back to a
|
|
1421
|
+
* placeholder glyph for every vector icon while the web build looked fine.
|
|
1422
|
+
*
|
|
1423
|
+
* Absent when {@link iconUrl} is absent, or for a legacy provider that has
|
|
1424
|
+
* not been updated — a client that cannot determine the type should prefer
|
|
1425
|
+
* its raster path, which is the safe default for an unknown image.
|
|
1426
|
+
*/
|
|
1427
|
+
iconMediaType: zod.z.string().optional(),
|
|
1428
|
+
configSchema: ConfigSchemaPassthrough,
|
|
1429
|
+
/**
|
|
1430
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
1431
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
1432
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
1433
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
1434
|
+
* on every edit.
|
|
1435
|
+
*
|
|
1436
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
1437
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
1438
|
+
* it absent must assume ANY edit may need the secret.
|
|
1439
|
+
*/
|
|
1440
|
+
destinationKeys: zod.z.array(zod.z.string()).optional(),
|
|
1441
|
+
supportsDiscovery: zod.z.boolean(),
|
|
1442
|
+
/**
|
|
1443
|
+
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
1444
|
+
* Absent = none; the rule editor then offers nothing.
|
|
1445
|
+
*/
|
|
1446
|
+
deliveryParams: zod.z.array(DeliveryParamSchema).optional(),
|
|
1447
|
+
caps: TargetKindCapsSchema
|
|
1448
|
+
});
|
|
1449
|
+
/**
|
|
1450
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
1451
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
1452
|
+
* round-trip a stored secret to the UI.
|
|
1453
|
+
*/
|
|
1454
|
+
var TargetSchema = zod.z.object({
|
|
1455
|
+
id: zod.z.string(),
|
|
1456
|
+
name: zod.z.string(),
|
|
1457
|
+
kind: zod.z.string(),
|
|
1458
|
+
addonId: zod.z.string(),
|
|
1459
|
+
enabled: zod.z.boolean(),
|
|
1460
|
+
config: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
1461
|
+
});
|
|
1462
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
1463
|
+
var DiscoveredTargetSchema = zod.z.object({
|
|
1464
|
+
kind: zod.z.string(),
|
|
1465
|
+
suggestedName: zod.z.string(),
|
|
1466
|
+
config: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
1467
|
+
});
|
|
1468
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
1469
|
+
var RenderedAsSchema = zod.z.object({
|
|
1470
|
+
level: zod.z.string(),
|
|
1471
|
+
format: NotificationFormatSchema,
|
|
1472
|
+
attachmentsSent: zod.z.number().int().nonnegative(),
|
|
1473
|
+
actionsSent: zod.z.number().int().nonnegative(),
|
|
1474
|
+
truncated: zod.z.boolean(),
|
|
1475
|
+
dropped: zod.z.array(zod.z.string())
|
|
1476
|
+
});
|
|
1477
|
+
var SendResultSchema = zod.z.object({
|
|
1478
|
+
success: zod.z.boolean(),
|
|
1479
|
+
error: zod.z.string().optional(),
|
|
1480
|
+
renderedAs: RenderedAsSchema.optional()
|
|
1481
|
+
});
|
|
1482
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
1483
|
+
var TestResultSchema = SendResultSchema;
|
|
1484
|
+
/**
|
|
1485
|
+
* Pre-save credential probe against a partial (unsaved) target config.
|
|
1486
|
+
* `resolvedUrl` never includes secrets (token stripped from path).
|
|
1487
|
+
*/
|
|
1488
|
+
var ProbeTargetConfigResultSchema = zod.z.object({
|
|
1489
|
+
ok: zod.z.boolean(),
|
|
1490
|
+
resolvedUrl: zod.z.string(),
|
|
1491
|
+
httpStatus: zod.z.number().int().optional(),
|
|
1492
|
+
stateInstance: zod.z.string().optional(),
|
|
1493
|
+
error: zod.z.string().optional()
|
|
1494
|
+
});
|
|
1495
|
+
method(zod.z.object({}), zod.z.array(TargetKindSchema)), method(zod.z.object({}), zod.z.array(TargetSchema), { auth: "admin" }), method(zod.z.object({ targetId: zod.z.string() }), TargetSchema, { auth: "admin" }), method(zod.z.object({
|
|
1496
|
+
kind: zod.z.string(),
|
|
1497
|
+
config: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
1498
|
+
/**
|
|
1499
|
+
* When editing / duplicating, the blank password field never round-trips
|
|
1500
|
+
* the stored token. Pass the source target id so the provider can restore
|
|
1501
|
+
* secrets server-side before dialling (same merge as upsert).
|
|
1502
|
+
*/
|
|
1503
|
+
targetId: zod.z.string().optional()
|
|
1504
|
+
}), zod.z.array(DiscoveredTargetSchema), { auth: "admin" }), method(zod.z.object({
|
|
1505
|
+
kind: zod.z.string(),
|
|
1506
|
+
config: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
1507
|
+
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
1508
|
+
targetId: zod.z.string().optional()
|
|
1509
|
+
}), ProbeTargetConfigResultSchema, {
|
|
1510
|
+
kind: "mutation",
|
|
1511
|
+
auth: "admin"
|
|
1512
|
+
}), method(zod.z.object({
|
|
1513
|
+
targetId: zod.z.string(),
|
|
1514
|
+
notification: NotificationSchema
|
|
1515
|
+
}), SendResultSchema, {
|
|
1516
|
+
kind: "mutation",
|
|
1517
|
+
auth: "admin"
|
|
1518
|
+
}), method(zod.z.object({
|
|
1519
|
+
targetId: zod.z.string(),
|
|
1520
|
+
sample: NotificationSchema.optional()
|
|
1521
|
+
}), TestResultSchema, {
|
|
1522
|
+
kind: "mutation",
|
|
1523
|
+
auth: "admin"
|
|
1524
|
+
}), method(zod.z.object({
|
|
1525
|
+
target: TargetSchema,
|
|
1526
|
+
/**
|
|
1527
|
+
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
1528
|
+
* existing row. The UI never holds the stored token; blank / redacted
|
|
1529
|
+
* password fields are restored from this source (D634 still applies if
|
|
1530
|
+
* destinations diverge without a re-typed secret).
|
|
1531
|
+
*/
|
|
1532
|
+
cloneSecretsFrom: zod.z.string().optional()
|
|
1533
|
+
}), TargetSchema, {
|
|
1534
|
+
kind: "mutation",
|
|
1535
|
+
auth: "admin"
|
|
1536
|
+
}), method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), {
|
|
1537
|
+
kind: "mutation",
|
|
1538
|
+
auth: "admin"
|
|
1539
|
+
}), method(zod.z.object({
|
|
1540
|
+
targetId: zod.z.string(),
|
|
1541
|
+
enabled: zod.z.boolean()
|
|
1542
|
+
}), zod.z.void(), {
|
|
1543
|
+
kind: "mutation",
|
|
1544
|
+
auth: "admin"
|
|
1545
|
+
});
|
|
1546
|
+
//#endregion
|
|
1118
1547
|
//#region src/device/icon-catalog.generated.ts
|
|
1119
1548
|
/** Lucide category ids, alphabetical. `ICON_META` indexes into this. */
|
|
1120
1549
|
var ICON_CATEGORY_IDS = [
|
|
@@ -20776,827 +21205,402 @@ var ICON_SYNONYMS = {
|
|
|
20776
21205
|
store: [
|
|
20777
21206
|
"negozio",
|
|
20778
21207
|
"ingresso",
|
|
20779
|
-
"vetrina",
|
|
20780
|
-
"shop"
|
|
20781
|
-
],
|
|
20782
|
-
sun: [
|
|
20783
|
-
"sole",
|
|
20784
|
-
"luminosita",
|
|
20785
|
-
"giorno",
|
|
20786
|
-
"esterno",
|
|
20787
|
-
"outdoor"
|
|
20788
|
-
],
|
|
20789
|
-
"sun-medium": ["sole", "luminosita"],
|
|
20790
|
-
sunrise: ["alba"],
|
|
20791
|
-
tablet: ["ipad"],
|
|
20792
|
-
thermometer: [
|
|
20793
|
-
"temperatura",
|
|
20794
|
-
"termometro",
|
|
20795
|
-
"termostato"
|
|
20796
|
-
],
|
|
20797
|
-
"thermometer-snowflake": [
|
|
20798
|
-
"freddo",
|
|
20799
|
-
"temperatura",
|
|
20800
|
-
"bassa"
|
|
20801
|
-
],
|
|
20802
|
-
"thermometer-sun": [
|
|
20803
|
-
"caldo",
|
|
20804
|
-
"temperatura",
|
|
20805
|
-
"alta"
|
|
20806
|
-
],
|
|
20807
|
-
timer: ["conto", "rovescia"],
|
|
20808
|
-
"toggle-left": [
|
|
20809
|
-
"interruttore",
|
|
20810
|
-
"switch",
|
|
20811
|
-
"toggle"
|
|
20812
|
-
],
|
|
20813
|
-
"toggle-right": [
|
|
20814
|
-
"interruttore",
|
|
20815
|
-
"switch",
|
|
20816
|
-
"toggle",
|
|
20817
|
-
"acceso"
|
|
20818
|
-
],
|
|
20819
|
-
tractor: [
|
|
20820
|
-
"trattore",
|
|
20821
|
-
"tosaerba",
|
|
20822
|
-
"giardino"
|
|
20823
|
-
],
|
|
20824
|
-
"train-front": ["treno"],
|
|
20825
|
-
"tree-pine": [
|
|
20826
|
-
"albero",
|
|
20827
|
-
"pino",
|
|
20828
|
-
"giardino"
|
|
20829
|
-
],
|
|
20830
|
-
trees: [
|
|
20831
|
-
"giardino",
|
|
20832
|
-
"alberi",
|
|
20833
|
-
"esterno",
|
|
20834
|
-
"parco",
|
|
20835
|
-
"garden"
|
|
20836
|
-
],
|
|
20837
|
-
truck: [
|
|
20838
|
-
"camion",
|
|
20839
|
-
"furgone",
|
|
20840
|
-
"consegna"
|
|
20841
|
-
],
|
|
20842
|
-
tv: ["televisore"],
|
|
20843
|
-
umbrella: [
|
|
20844
|
-
"tenda",
|
|
20845
|
-
"markise",
|
|
20846
|
-
"ombrellone",
|
|
20847
|
-
"pergola",
|
|
20848
|
-
"veranda",
|
|
20849
|
-
"terrace"
|
|
20850
|
-
],
|
|
20851
|
-
user: ["persona", "utente"],
|
|
20852
|
-
users: [
|
|
20853
|
-
"persone",
|
|
20854
|
-
"gruppo",
|
|
20855
|
-
"famiglia"
|
|
20856
|
-
],
|
|
20857
|
-
utensils: [
|
|
20858
|
-
"cucina",
|
|
20859
|
-
"posate",
|
|
20860
|
-
"sala",
|
|
20861
|
-
"pranzo",
|
|
20862
|
-
"kitchen"
|
|
20863
|
-
],
|
|
20864
|
-
vibrate: [
|
|
20865
|
-
"vibrazione",
|
|
20866
|
-
"urto",
|
|
20867
|
-
"tamper"
|
|
20868
|
-
],
|
|
20869
|
-
video: [],
|
|
20870
|
-
"video-off": ["video", "spento"],
|
|
20871
|
-
"volume-2": [
|
|
20872
|
-
"volume",
|
|
20873
|
-
"audio",
|
|
20874
|
-
"altoparlante",
|
|
20875
|
-
"suono"
|
|
20876
|
-
],
|
|
20877
|
-
"volume-x": [
|
|
20878
|
-
"muto",
|
|
20879
|
-
"silenziato",
|
|
20880
|
-
"volume",
|
|
20881
|
-
"spento"
|
|
20882
|
-
],
|
|
20883
|
-
warehouse: [
|
|
20884
|
-
"magazzino",
|
|
20885
|
-
"capanno",
|
|
20886
|
-
"deposito",
|
|
20887
|
-
"cantina",
|
|
20888
|
-
"warehouse"
|
|
20889
|
-
],
|
|
20890
|
-
"washing-machine": [
|
|
20891
|
-
"lavatrice",
|
|
20892
|
-
"lavastoviglie",
|
|
20893
|
-
"asciugatrice"
|
|
20894
|
-
],
|
|
20895
|
-
waves: [
|
|
20896
|
-
"onde",
|
|
20897
|
-
"acqua",
|
|
20898
|
-
"vibrazione"
|
|
20899
|
-
],
|
|
20900
|
-
"waves-ladder": [
|
|
20901
|
-
"piscina",
|
|
20902
|
-
"vasca",
|
|
20903
|
-
"pool"
|
|
20904
|
-
],
|
|
20905
|
-
wifi: ["rete", "segnale"],
|
|
20906
|
-
"wifi-off": [
|
|
20907
|
-
"wifi",
|
|
20908
|
-
"assente",
|
|
20909
|
-
"offline",
|
|
20910
|
-
"disconnesso"
|
|
20911
|
-
],
|
|
20912
|
-
wind: [
|
|
20913
|
-
"vento",
|
|
20914
|
-
"aria",
|
|
20915
|
-
"gas",
|
|
20916
|
-
"monossido",
|
|
20917
|
-
"co"
|
|
20918
|
-
],
|
|
20919
|
-
workflow: [
|
|
20920
|
-
"automazione",
|
|
20921
|
-
"flusso",
|
|
20922
|
-
"scenario"
|
|
21208
|
+
"vetrina",
|
|
21209
|
+
"shop"
|
|
20923
21210
|
],
|
|
20924
|
-
|
|
20925
|
-
"
|
|
20926
|
-
"
|
|
20927
|
-
"
|
|
20928
|
-
"
|
|
21211
|
+
sun: [
|
|
21212
|
+
"sole",
|
|
21213
|
+
"luminosita",
|
|
21214
|
+
"giorno",
|
|
21215
|
+
"esterno",
|
|
21216
|
+
"outdoor"
|
|
20929
21217
|
],
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
"
|
|
20935
|
-
"
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
20967
|
-
|
|
20968
|
-
|
|
20969
|
-
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
|
|
20994
|
-
|
|
20995
|
-
|
|
20996
|
-
|
|
20997
|
-
|
|
20998
|
-
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
|
|
21002
|
-
|
|
21003
|
-
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
|
|
21030
|
-
|
|
21031
|
-
"
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
|
|
21035
|
-
|
|
21036
|
-
|
|
21037
|
-
|
|
21038
|
-
|
|
21039
|
-
|
|
21040
|
-
]
|
|
21041
|
-
|
|
21042
|
-
|
|
21043
|
-
|
|
21044
|
-
|
|
21045
|
-
|
|
21046
|
-
|
|
21047
|
-
|
|
21048
|
-
|
|
21049
|
-
|
|
21050
|
-
|
|
21051
|
-
|
|
21052
|
-
|
|
21053
|
-
|
|
21054
|
-
|
|
21055
|
-
|
|
21056
|
-
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21061
|
-
|
|
21062
|
-
|
|
21063
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
|
|
21077
|
-
|
|
21078
|
-
|
|
21079
|
-
function wordsOf(entry) {
|
|
21080
|
-
const cached = WORDS_CACHE.get(entry.key);
|
|
21081
|
-
if (cached !== void 0) return cached;
|
|
21082
|
-
const built = {
|
|
21083
|
-
key: normalise(entry.key),
|
|
21084
|
-
synonyms: entry.synonyms.map(normalise),
|
|
21085
|
-
tags: entry.tags.map(normalise)
|
|
21086
|
-
};
|
|
21087
|
-
WORDS_CACHE.set(entry.key, built);
|
|
21088
|
-
return built;
|
|
21089
|
-
}
|
|
21090
|
-
/**
|
|
21091
|
-
* Rank the catalog against a free-text query.
|
|
21092
|
-
*
|
|
21093
|
-
* An empty query returns the WHOLE catalog in key order, because a picker's
|
|
21094
|
-
* resting state is the full list. `extraWords` lets a caller add the words IT
|
|
21095
|
-
* can translate without this module knowing what a locale is; they score like a
|
|
21096
|
-
* synonym. The key outranks a synonym, a synonym outranks a lucide tag, and
|
|
21097
|
-
* ties keep key order so the list does not reshuffle between keystrokes.
|
|
21098
|
-
*/
|
|
21099
|
-
function searchIcons(query, extraWords) {
|
|
21100
|
-
const q = normalise(query);
|
|
21101
|
-
if (q.length === 0) return ENTRIES.map((entry) => ({
|
|
21102
|
-
entry,
|
|
21103
|
-
score: 0
|
|
21104
|
-
}));
|
|
21105
|
-
const hits = [];
|
|
21106
|
-
for (const entry of ENTRIES) {
|
|
21107
|
-
const words = wordsOf(entry);
|
|
21108
|
-
const keyScore = scoreWord(q, words.key, true);
|
|
21109
|
-
let best = keyScore > 0 ? keyScore + 2 : 0;
|
|
21110
|
-
for (const word of [...words.synonyms, ...(extraWords?.get(entry.key) ?? []).map(normalise)]) {
|
|
21111
|
-
const score = scoreWord(q, word, true);
|
|
21112
|
-
if (score > 0 && score + 1 > best) best = score + 1;
|
|
21113
|
-
}
|
|
21114
|
-
for (const word of words.tags) {
|
|
21115
|
-
const score = scoreWord(q, word, false);
|
|
21116
|
-
if (score > best) best = score;
|
|
21117
|
-
}
|
|
21118
|
-
if (best > 0) hits.push({
|
|
21119
|
-
entry,
|
|
21120
|
-
score: best
|
|
21121
|
-
});
|
|
21122
|
-
}
|
|
21123
|
-
return hits.toSorted((a, b) => b.score - a.score);
|
|
21124
|
-
}
|
|
21125
|
-
/** A readable label derived from a lucide category id (`food-beverage` → `Food beverage`). */
|
|
21126
|
-
function iconCategoryLabel(category) {
|
|
21127
|
-
const spaced = category.replace(/-/g, " ");
|
|
21128
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
21129
|
-
}
|
|
21130
|
-
//#endregion
|
|
21131
|
-
//#region src/device/location-icon.ts
|
|
21132
|
-
/**
|
|
21133
|
-
* What a location's icon is, and the ONE key its label joins on.
|
|
21134
|
-
*
|
|
21135
|
-
* A location is not an entity — it is a free-form label (`PersistedDeviceMeta.
|
|
21136
|
-
* location`) plus an operator-curated registry of labels in the device-manager
|
|
21137
|
-
* addon store. The icon is a second map beside that registry, keyed the same
|
|
21138
|
-
* way the registry matches everywhere else: {@link locationIconKey}, i.e.
|
|
21139
|
-
* trimmed + lower-cased.
|
|
21140
|
-
*
|
|
21141
|
-
* The icon itself is a key of the ONE icon catalog (`icon-catalog.ts`, D854):
|
|
21142
|
-
* a location may wear any icon both surfaces can draw. There used to be a
|
|
21143
|
-
* closed 20-icon subset here (D511); it was a second vocabulary on the same two
|
|
21144
|
-
* surfaces and is gone. The names below keep their meaning:
|
|
21145
|
-
*
|
|
21146
|
-
* DEGRADATION IS A FALLBACK, NEVER A HOLE. A surface that cannot draw a stored
|
|
21147
|
-
* value — a persisted value from a newer hub, a hand-edited settings row —
|
|
21148
|
-
* draws {@link LOCATION_ICON_FALLBACK} (`map-pin`, the one every surface has
|
|
21149
|
-
* always had). Read a stored/wire value through
|
|
21150
|
-
* `resolveIconKey(value, LOCATION_ICON_FALLBACK)` and that is what you get;
|
|
21151
|
-
* nothing anywhere renders an empty box.
|
|
21152
|
-
*/
|
|
21153
|
-
/**
|
|
21154
|
-
* The id every surface is guaranteed to draw, and what an unrecognised value
|
|
21155
|
-
* degrades to. It has been in both icon sets since before the catalog existed
|
|
21156
|
-
* (the Locations page's own page icon), so "the fallback is missing" is not a
|
|
21157
|
-
* state a build can reach.
|
|
21158
|
-
*/
|
|
21159
|
-
var LOCATION_ICON_FALLBACK = "map-pin";
|
|
21160
|
-
zod.z.object({
|
|
21161
|
-
locationKey: zod.z.string(),
|
|
21162
|
-
icon: IconKeySchema
|
|
21163
|
-
});
|
|
21218
|
+
"sun-medium": ["sole", "luminosita"],
|
|
21219
|
+
sunrise: ["alba"],
|
|
21220
|
+
tablet: ["ipad"],
|
|
21221
|
+
thermometer: [
|
|
21222
|
+
"temperatura",
|
|
21223
|
+
"termometro",
|
|
21224
|
+
"termostato"
|
|
21225
|
+
],
|
|
21226
|
+
"thermometer-snowflake": [
|
|
21227
|
+
"freddo",
|
|
21228
|
+
"temperatura",
|
|
21229
|
+
"bassa"
|
|
21230
|
+
],
|
|
21231
|
+
"thermometer-sun": [
|
|
21232
|
+
"caldo",
|
|
21233
|
+
"temperatura",
|
|
21234
|
+
"alta"
|
|
21235
|
+
],
|
|
21236
|
+
timer: ["conto", "rovescia"],
|
|
21237
|
+
"toggle-left": [
|
|
21238
|
+
"interruttore",
|
|
21239
|
+
"switch",
|
|
21240
|
+
"toggle"
|
|
21241
|
+
],
|
|
21242
|
+
"toggle-right": [
|
|
21243
|
+
"interruttore",
|
|
21244
|
+
"switch",
|
|
21245
|
+
"toggle",
|
|
21246
|
+
"acceso"
|
|
21247
|
+
],
|
|
21248
|
+
tractor: [
|
|
21249
|
+
"trattore",
|
|
21250
|
+
"tosaerba",
|
|
21251
|
+
"giardino"
|
|
21252
|
+
],
|
|
21253
|
+
"train-front": ["treno"],
|
|
21254
|
+
"tree-pine": [
|
|
21255
|
+
"albero",
|
|
21256
|
+
"pino",
|
|
21257
|
+
"giardino"
|
|
21258
|
+
],
|
|
21259
|
+
trees: [
|
|
21260
|
+
"giardino",
|
|
21261
|
+
"alberi",
|
|
21262
|
+
"esterno",
|
|
21263
|
+
"parco",
|
|
21264
|
+
"garden"
|
|
21265
|
+
],
|
|
21266
|
+
truck: [
|
|
21267
|
+
"camion",
|
|
21268
|
+
"furgone",
|
|
21269
|
+
"consegna"
|
|
21270
|
+
],
|
|
21271
|
+
tv: ["televisore"],
|
|
21272
|
+
umbrella: [
|
|
21273
|
+
"tenda",
|
|
21274
|
+
"markise",
|
|
21275
|
+
"ombrellone",
|
|
21276
|
+
"pergola",
|
|
21277
|
+
"veranda",
|
|
21278
|
+
"terrace"
|
|
21279
|
+
],
|
|
21280
|
+
user: ["persona", "utente"],
|
|
21281
|
+
users: [
|
|
21282
|
+
"persone",
|
|
21283
|
+
"gruppo",
|
|
21284
|
+
"famiglia"
|
|
21285
|
+
],
|
|
21286
|
+
utensils: [
|
|
21287
|
+
"cucina",
|
|
21288
|
+
"posate",
|
|
21289
|
+
"sala",
|
|
21290
|
+
"pranzo",
|
|
21291
|
+
"kitchen"
|
|
21292
|
+
],
|
|
21293
|
+
vibrate: [
|
|
21294
|
+
"vibrazione",
|
|
21295
|
+
"urto",
|
|
21296
|
+
"tamper"
|
|
21297
|
+
],
|
|
21298
|
+
video: [],
|
|
21299
|
+
"video-off": ["video", "spento"],
|
|
21300
|
+
"volume-2": [
|
|
21301
|
+
"volume",
|
|
21302
|
+
"audio",
|
|
21303
|
+
"altoparlante",
|
|
21304
|
+
"suono"
|
|
21305
|
+
],
|
|
21306
|
+
"volume-x": [
|
|
21307
|
+
"muto",
|
|
21308
|
+
"silenziato",
|
|
21309
|
+
"volume",
|
|
21310
|
+
"spento"
|
|
21311
|
+
],
|
|
21312
|
+
warehouse: [
|
|
21313
|
+
"magazzino",
|
|
21314
|
+
"capanno",
|
|
21315
|
+
"deposito",
|
|
21316
|
+
"cantina",
|
|
21317
|
+
"warehouse"
|
|
21318
|
+
],
|
|
21319
|
+
"washing-machine": [
|
|
21320
|
+
"lavatrice",
|
|
21321
|
+
"lavastoviglie",
|
|
21322
|
+
"asciugatrice"
|
|
21323
|
+
],
|
|
21324
|
+
waves: [
|
|
21325
|
+
"onde",
|
|
21326
|
+
"acqua",
|
|
21327
|
+
"vibrazione"
|
|
21328
|
+
],
|
|
21329
|
+
"waves-ladder": [
|
|
21330
|
+
"piscina",
|
|
21331
|
+
"vasca",
|
|
21332
|
+
"pool"
|
|
21333
|
+
],
|
|
21334
|
+
wifi: ["rete", "segnale"],
|
|
21335
|
+
"wifi-off": [
|
|
21336
|
+
"wifi",
|
|
21337
|
+
"assente",
|
|
21338
|
+
"offline",
|
|
21339
|
+
"disconnesso"
|
|
21340
|
+
],
|
|
21341
|
+
wind: [
|
|
21342
|
+
"vento",
|
|
21343
|
+
"aria",
|
|
21344
|
+
"gas",
|
|
21345
|
+
"monossido",
|
|
21346
|
+
"co"
|
|
21347
|
+
],
|
|
21348
|
+
workflow: [
|
|
21349
|
+
"automazione",
|
|
21350
|
+
"flusso",
|
|
21351
|
+
"scenario"
|
|
21352
|
+
],
|
|
21353
|
+
wrench: [
|
|
21354
|
+
"officina",
|
|
21355
|
+
"manutenzione",
|
|
21356
|
+
"attrezzi",
|
|
21357
|
+
"utenze"
|
|
21358
|
+
],
|
|
21359
|
+
zap: [
|
|
21360
|
+
"energia",
|
|
21361
|
+
"corrente",
|
|
21362
|
+
"consumo",
|
|
21363
|
+
"watt",
|
|
21364
|
+
"elettricita"
|
|
21365
|
+
]
|
|
21366
|
+
};
|
|
21164
21367
|
/**
|
|
21165
|
-
*
|
|
21166
|
-
*
|
|
21167
|
-
*
|
|
21368
|
+
* Spellings an operator may already have PERSISTED that no lucide package
|
|
21369
|
+
* accepts any more, and the catalog key they draw. They resolve
|
|
21370
|
+
* (`canonicalIconKey`) but are never offered. `gauge2` was this repo's own
|
|
21371
|
+
* pre-catalog spelling of `gauge`.
|
|
21168
21372
|
*/
|
|
21169
|
-
|
|
21170
|
-
return label.trim().toLowerCase();
|
|
21171
|
-
}
|
|
21172
|
-
zod.z.object({
|
|
21173
|
-
locationKey: zod.z.string(),
|
|
21174
|
-
description: zod.z.string()
|
|
21175
|
-
});
|
|
21373
|
+
var LEGACY_ICON_ALIASES = { gauge2: "gauge" };
|
|
21176
21374
|
//#endregion
|
|
21177
|
-
//#region src/
|
|
21178
|
-
/**
|
|
21179
|
-
* notification-output — canonical, capability-gated notification delivery.
|
|
21180
|
-
*
|
|
21181
|
-
* Apprise-derived model (see
|
|
21182
|
-
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
21183
|
-
* callers emit ONE canonical `Notification`; each provider declares a
|
|
21184
|
-
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
21185
|
-
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
21186
|
-
* message to what the kind supports — callers never special-case a service.
|
|
21187
|
-
*
|
|
21188
|
-
* DESIGN DECISIONS (locked):
|
|
21189
|
-
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
21190
|
-
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
21191
|
-
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
21192
|
-
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
21193
|
-
* alternative would fork the UI per addon and cannot host the
|
|
21194
|
-
* discovery→adopt flow.
|
|
21195
|
-
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
21196
|
-
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
21197
|
-
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
21198
|
-
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
21199
|
-
* `addonId` the generated collection router extracts from the call input.
|
|
21200
|
-
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
21201
|
-
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
21202
|
-
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
21203
|
-
* base64 fallback needed.
|
|
21204
|
-
*
|
|
21205
|
-
* TODO (deferred, closed-set change — separate decision): add
|
|
21206
|
-
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
21207
|
-
* admin "Integrations" page.
|
|
21208
|
-
*/
|
|
21209
|
-
/**
|
|
21210
|
-
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
21211
|
-
* adapter picks what it supports and the degrade engine filters the rest.
|
|
21212
|
-
*/
|
|
21213
|
-
var AttachmentMediaTypeSchema = zod.z.enum([
|
|
21214
|
-
"image",
|
|
21215
|
-
"video",
|
|
21216
|
-
"gif",
|
|
21217
|
-
"audio",
|
|
21218
|
-
"icon"
|
|
21219
|
-
]);
|
|
21220
|
-
/**
|
|
21221
|
-
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
21222
|
-
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
21223
|
-
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
21224
|
-
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
21225
|
-
*/
|
|
21226
|
-
var AttachmentSchema = zod.z.object({
|
|
21227
|
-
mediaType: AttachmentMediaTypeSchema,
|
|
21228
|
-
url: zod.z.string().optional(),
|
|
21229
|
-
bytes: zod.z.instanceof(Uint8Array).optional(),
|
|
21230
|
-
mime: zod.z.string().optional(),
|
|
21231
|
-
name: zod.z.string().optional()
|
|
21232
|
-
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
21233
|
-
var NotificationFormatSchema = zod.z.enum([
|
|
21234
|
-
"text",
|
|
21235
|
-
"markdown",
|
|
21236
|
-
"html"
|
|
21237
|
-
]);
|
|
21375
|
+
//#region src/device/icon-catalog.ts
|
|
21238
21376
|
/**
|
|
21239
|
-
*
|
|
21240
|
-
*
|
|
21241
|
-
* A closed set, not a free string, and that is the whole point: an arbitrary
|
|
21242
|
-
* icon name is one that ntfy renders, zentik silently drops, and nobody
|
|
21243
|
-
* notices — the same class of gap as a zone vocabulary nothing produced
|
|
21244
|
-
* ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
|
|
21245
|
-
* declares `actionIcons: false` and the degrade engine strips the field.
|
|
21377
|
+
* icon-catalog — THE icon vocabulary (D854).
|
|
21246
21378
|
*
|
|
21247
|
-
*
|
|
21248
|
-
*
|
|
21249
|
-
*
|
|
21379
|
+
* Every place that picks or stores an icon — a device's display override, a
|
|
21380
|
+
* location, a site zone, a viewer accessory, an `icon` settings field — names
|
|
21381
|
+
* it by a key of this catalog and nothing else. There is no curated subset and
|
|
21382
|
+
* no second list: the catalog is every lucide icon that both surfaces can draw
|
|
21383
|
+
* (the admin UI's `lucide-react` and the viewer's `lucide-react-native`), the
|
|
21384
|
+
* intersection computed by `scripts/generate-icon-catalog.ts` into the
|
|
21385
|
+
* committed `icon-catalog.generated.ts`.
|
|
21250
21386
|
*
|
|
21251
|
-
*
|
|
21387
|
+
* Like the vocabularies it replaces, this module holds STRINGS only — no React,
|
|
21388
|
+
* no lucide import, no glyph. Each surface owns its glyph table (the web one
|
|
21389
|
+
* lives in `ui-library`, loaded lazily), and a test on each side pins that it
|
|
21390
|
+
* covers {@link ICON_KEYS}.
|
|
21252
21391
|
*
|
|
21253
|
-
*
|
|
21254
|
-
* the whole set onto its own renderer's vocabulary through a
|
|
21255
|
-
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
21256
|
-
* fallback, so adding a member here fails every adapter's build until someone
|
|
21257
|
-
* decides its glyph, which is the only place that decision can be made
|
|
21258
|
-
* honestly.
|
|
21392
|
+
* ## What is persisted
|
|
21259
21393
|
*
|
|
21260
|
-
*
|
|
21261
|
-
* `
|
|
21262
|
-
* `
|
|
21263
|
-
*
|
|
21264
|
-
* mapping, and "the field is documented" is not "the value renders".
|
|
21394
|
+
* The canonical kebab-case lucide id ({@link IconKey}). Spellings that a lucide
|
|
21395
|
+
* package still accepts (`home` for `house`) and this repo's own legacy ones
|
|
21396
|
+
* (`gauge2`) RESOLVE ({@link canonicalIconKey}) so a stored value never goes
|
|
21397
|
+
* stale, but they are never OFFERED by search.
|
|
21265
21398
|
*
|
|
21266
|
-
*
|
|
21267
|
-
*
|
|
21268
|
-
*
|
|
21269
|
-
*
|
|
21270
|
-
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
21399
|
+
* ## Search
|
|
21400
|
+
*
|
|
21401
|
+
* Over the key, the Italian/English overlay in `icon-synonyms.ts`, any words
|
|
21402
|
+
* the caller adds, and lucide's English tags — in that order of weight.
|
|
21271
21403
|
*/
|
|
21272
|
-
var
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
|
|
21276
|
-
"
|
|
21277
|
-
|
|
21278
|
-
"play",
|
|
21279
|
-
"open",
|
|
21280
|
-
"close",
|
|
21281
|
-
"lock",
|
|
21282
|
-
"unlock",
|
|
21283
|
-
"arm",
|
|
21284
|
-
"arm-home",
|
|
21285
|
-
"arm-away",
|
|
21286
|
-
"arm-night",
|
|
21287
|
-
"disarm",
|
|
21288
|
-
"light",
|
|
21289
|
-
"alert",
|
|
21290
|
-
"camera"
|
|
21291
|
-
]);
|
|
21292
|
-
/** A single tap-through action button. */
|
|
21293
|
-
var NotificationActionSchema = zod.z.object({
|
|
21294
|
-
id: zod.z.string(),
|
|
21295
|
-
label: zod.z.string(),
|
|
21296
|
-
url: zod.z.string().optional(),
|
|
21297
|
-
/** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
|
|
21298
|
-
icon: NotificationActionIconSchema.optional(),
|
|
21299
|
-
/**
|
|
21300
|
-
* Renders in a warning style where the notifier supports it.
|
|
21301
|
-
*
|
|
21302
|
-
* A HINT, never a gate. The callback's authority is its token and nothing
|
|
21303
|
-
* else — see `notification-center/action-token.ts` for what that does and
|
|
21304
|
-
* does not buy.
|
|
21305
|
-
*/
|
|
21306
|
-
destructive: zod.z.boolean().optional(),
|
|
21307
|
-
/**
|
|
21308
|
-
* How the tap should REACH the url.
|
|
21309
|
-
*
|
|
21310
|
-
* `navigate` (absent, and every button authored before this field) opens it:
|
|
21311
|
-
* the phone leaves the notification and shows whatever the callback returns.
|
|
21312
|
-
* That is right for a button whose answer the operator wants to read.
|
|
21313
|
-
*
|
|
21314
|
-
* `background` fires it as a POST and stays put. It exists for the buttons
|
|
21315
|
-
* whose whole point is not to interrupt — "silence this for 30 minutes" is
|
|
21316
|
-
* an answer to the notification, and being thrown into a browser tab to
|
|
21317
|
-
* confirm it costs more attention than the notification did. A backend that
|
|
21318
|
-
* cannot do a background call renders it as an ordinary link (the adapters
|
|
21319
|
-
* fall back rather than dropping the button), so this is a preference, never
|
|
21320
|
-
* a requirement.
|
|
21321
|
-
*/
|
|
21322
|
-
mode: zod.z.enum(["navigate", "background"]).optional()
|
|
21323
|
-
});
|
|
21324
|
-
/** A delivery parameter value: what a notifier declared it takes. */
|
|
21325
|
-
var DeliveryParamValueSchema = zod.z.union([
|
|
21326
|
-
zod.z.string(),
|
|
21327
|
-
zod.z.number(),
|
|
21328
|
-
zod.z.boolean()
|
|
21329
|
-
]);
|
|
21404
|
+
var KEY_SET$1 = new Set(ICON_KEYS);
|
|
21405
|
+
var ALIAS_TO_KEY = new Map([...Object.entries(ICON_ALIASES), ...Object.entries(LEGACY_ICON_ALIASES)]);
|
|
21406
|
+
/** Is this a canonical key of the catalog? (An alias is not — see {@link canonicalIconKey}.) */
|
|
21407
|
+
function isIconKey(value) {
|
|
21408
|
+
return typeof value === "string" && KEY_SET$1.has(value);
|
|
21409
|
+
}
|
|
21330
21410
|
/**
|
|
21331
|
-
*
|
|
21332
|
-
*
|
|
21333
|
-
|
|
21334
|
-
|
|
21411
|
+
* A stored value as a catalog key: the key itself, the key a still-accepted or
|
|
21412
|
+
* legacy spelling stands for, or `undefined` — never a guess.
|
|
21413
|
+
*/
|
|
21414
|
+
function canonicalIconKey(value) {
|
|
21415
|
+
if (typeof value !== "string") return void 0;
|
|
21416
|
+
if (isIconKey(value)) return value;
|
|
21417
|
+
return ALIAS_TO_KEY.get(value);
|
|
21418
|
+
}
|
|
21419
|
+
/**
|
|
21420
|
+
* A stored icon value as a catalog key: its canonical key when it is (or
|
|
21421
|
+
* aliases) one, `fallback` otherwise. For an `icon` settings field whose stored
|
|
21422
|
+
* value may predate the catalog (a free-text lucide name) — the field's default
|
|
21423
|
+
* is drawn rather than nothing.
|
|
21424
|
+
*/
|
|
21425
|
+
function resolveIconKey(value, fallback) {
|
|
21426
|
+
return canonicalIconKey(value) ?? fallback;
|
|
21427
|
+
}
|
|
21428
|
+
/**
|
|
21429
|
+
* Wire/storage schema for one icon key.
|
|
21335
21430
|
*
|
|
21336
|
-
*
|
|
21337
|
-
*
|
|
21431
|
+
* Deliberately NOT a `z.enum` of the catalog: ~1,700 literals would be inlined
|
|
21432
|
+
* into every cap router, type and API-reference row that carries an icon. The
|
|
21433
|
+
* check is membership in the catalog and the type is {@link IconKey}.
|
|
21338
21434
|
*/
|
|
21339
|
-
var
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
label: zod.z.string()
|
|
21354
|
-
})).optional(),
|
|
21355
|
-
/** `number` only. */
|
|
21356
|
-
min: zod.z.number().optional(),
|
|
21357
|
-
max: zod.z.number().optional(),
|
|
21358
|
-
/** What the notifier does when the rule sets nothing; shown as the placeholder. */
|
|
21359
|
-
default: DeliveryParamValueSchema.optional()
|
|
21435
|
+
var IconKeySchema = zod.z.custom(isIconKey, "Not an icon of the catalog");
|
|
21436
|
+
var ENTRIES = ICON_KEYS.map((key, index) => {
|
|
21437
|
+
const [categoryIndexes, tags] = ICON_META[index] ?? [[], []];
|
|
21438
|
+
const categories = categoryIndexes.flatMap((i) => {
|
|
21439
|
+
const id = ICON_CATEGORY_IDS[i];
|
|
21440
|
+
return id === void 0 ? [] : [id];
|
|
21441
|
+
});
|
|
21442
|
+
return {
|
|
21443
|
+
key,
|
|
21444
|
+
categories,
|
|
21445
|
+
primaryCategory: categories[0] ?? ICON_CATEGORY_IDS[0],
|
|
21446
|
+
tags,
|
|
21447
|
+
synonyms: ICON_SYNONYMS[key] ?? []
|
|
21448
|
+
};
|
|
21360
21449
|
});
|
|
21450
|
+
var ENTRY_BY_KEY = new Map(ENTRIES.map((e) => [e.key, e]));
|
|
21451
|
+
/** The entry for a canonical key, or `undefined`. */
|
|
21452
|
+
function iconEntry(key) {
|
|
21453
|
+
return ENTRY_BY_KEY.get(key);
|
|
21454
|
+
}
|
|
21455
|
+
/** Every entry, in key order. */
|
|
21456
|
+
var ICON_ENTRIES = ENTRIES;
|
|
21457
|
+
/** Categories a picker offers first: the ones this product's icons mostly live in. */
|
|
21458
|
+
var LEADING_CATEGORIES = [
|
|
21459
|
+
"devices",
|
|
21460
|
+
"home",
|
|
21461
|
+
"security",
|
|
21462
|
+
"buildings",
|
|
21463
|
+
"connectivity",
|
|
21464
|
+
"multimedia",
|
|
21465
|
+
"nature",
|
|
21466
|
+
"weather",
|
|
21467
|
+
"transportation",
|
|
21468
|
+
"animals"
|
|
21469
|
+
];
|
|
21470
|
+
var PRIMARY_CATEGORIES = new Set(ENTRIES.map((e) => e.primaryCategory));
|
|
21361
21471
|
/**
|
|
21362
|
-
* The
|
|
21363
|
-
*
|
|
21364
|
-
*
|
|
21365
|
-
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
21366
|
-
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
21367
|
-
* `priority` for that one target.
|
|
21472
|
+
* The categories a picker files icons under, in picker order: the leading ones,
|
|
21473
|
+
* then the rest alphabetically. Only categories that are some icon's PRIMARY
|
|
21474
|
+
* one — a category lucide lists only as a secondary has no section of its own.
|
|
21368
21475
|
*/
|
|
21369
|
-
var
|
|
21370
|
-
|
|
21371
|
-
|
|
21372
|
-
|
|
21373
|
-
|
|
21374
|
-
|
|
21375
|
-
|
|
21376
|
-
|
|
21377
|
-
|
|
21378
|
-
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
|
|
21386
|
-
|
|
21387
|
-
|
|
21388
|
-
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
21394
|
-
|
|
21395
|
-
|
|
21396
|
-
|
|
21397
|
-
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
|
|
21405
|
-
|
|
21406
|
-
|
|
21407
|
-
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21412
|
-
"both"
|
|
21413
|
-
]),
|
|
21414
|
-
max: zod.z.number().int().nonnegative(),
|
|
21415
|
-
maxBytes: zod.z.number().int().positive().optional()
|
|
21416
|
-
});
|
|
21417
|
-
/** The full capability block consulted before dispatch. */
|
|
21418
|
-
var TargetKindCapsSchema = zod.z.object({
|
|
21419
|
-
attachments: TargetKindAttachmentsCapsSchema,
|
|
21420
|
-
/** Max action buttons (0 = none). */
|
|
21421
|
-
actions: zod.z.number().int().nonnegative(),
|
|
21422
|
-
/**
|
|
21423
|
-
* Whether this kind renders a per-action ICON.
|
|
21424
|
-
*
|
|
21425
|
-
* `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
|
|
21426
|
-
* run on the addon cap path — three production failures in one day taught
|
|
21427
|
-
* this repo that once. Absent is read as false by the degrade engine, which
|
|
21428
|
-
* is the safe direction: an icon that is not rendered costs nothing, an icon
|
|
21429
|
-
* assumed and dropped costs the operator's trust in the field.
|
|
21430
|
-
*/
|
|
21431
|
-
actionIcons: zod.z.boolean().optional(),
|
|
21432
|
-
levels: zod.z.array(TargetKindLevelSchema),
|
|
21433
|
-
format: zod.z.array(NotificationFormatSchema),
|
|
21434
|
-
clickUrl: zod.z.boolean(),
|
|
21435
|
-
sound: zod.z.boolean(),
|
|
21436
|
-
ttl: zod.z.boolean(),
|
|
21437
|
-
bodyMaxLen: zod.z.number().int().positive()
|
|
21438
|
-
});
|
|
21476
|
+
var ICON_CATEGORIES = [...LEADING_CATEGORIES, ...ICON_CATEGORY_IDS.filter((c) => !LEADING_CATEGORIES.includes(c))].filter((c) => PRIMARY_CATEGORIES.has(c));
|
|
21477
|
+
/** The icons filed under a category as their PRIMARY one, in key order. */
|
|
21478
|
+
function iconsInCategory(category) {
|
|
21479
|
+
return ENTRIES.filter((e) => e.primaryCategory === category);
|
|
21480
|
+
}
|
|
21481
|
+
function normalise(text) {
|
|
21482
|
+
return text.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().replace(/[_-]+/g, " ").trim();
|
|
21483
|
+
}
|
|
21484
|
+
/** Does `needle` appear in `haystack` in order, though not adjacently? */
|
|
21485
|
+
function isSubsequence(needle, haystack) {
|
|
21486
|
+
let i = 0;
|
|
21487
|
+
for (const ch of haystack) {
|
|
21488
|
+
if (ch === needle[i]) i += 1;
|
|
21489
|
+
if (i === needle.length) return true;
|
|
21490
|
+
}
|
|
21491
|
+
return needle.length === 0;
|
|
21492
|
+
}
|
|
21493
|
+
/**
|
|
21494
|
+
* How well one word answers the query. 0 = not at all. An exact word beats a
|
|
21495
|
+
* prefix, a prefix beats a word STARTING inside the phrase, and a loose
|
|
21496
|
+
* subsequence comes last because it matches almost anything.
|
|
21497
|
+
*/
|
|
21498
|
+
function scoreWord(query, word, loose) {
|
|
21499
|
+
if (word === query) return 100;
|
|
21500
|
+
if (word.startsWith(query)) return 80;
|
|
21501
|
+
if (word.split(" ").some((w) => w.startsWith(query))) return 60;
|
|
21502
|
+
if (word.includes(query)) return 40;
|
|
21503
|
+
if (loose && isSubsequence(query, word)) return 15;
|
|
21504
|
+
return 0;
|
|
21505
|
+
}
|
|
21506
|
+
var WORDS_CACHE = /* @__PURE__ */ new Map();
|
|
21507
|
+
/** Normalised once per entry — search runs on every keystroke over ~1,700 entries. */
|
|
21508
|
+
function wordsOf(entry) {
|
|
21509
|
+
const cached = WORDS_CACHE.get(entry.key);
|
|
21510
|
+
if (cached !== void 0) return cached;
|
|
21511
|
+
const built = {
|
|
21512
|
+
key: normalise(entry.key),
|
|
21513
|
+
synonyms: entry.synonyms.map(normalise),
|
|
21514
|
+
tags: entry.tags.map(normalise)
|
|
21515
|
+
};
|
|
21516
|
+
WORDS_CACHE.set(entry.key, built);
|
|
21517
|
+
return built;
|
|
21518
|
+
}
|
|
21439
21519
|
/**
|
|
21440
|
-
*
|
|
21441
|
-
*
|
|
21442
|
-
*
|
|
21443
|
-
*
|
|
21444
|
-
*
|
|
21520
|
+
* Rank the catalog against a free-text query.
|
|
21521
|
+
*
|
|
21522
|
+
* An empty query returns the WHOLE catalog in key order, because a picker's
|
|
21523
|
+
* resting state is the full list. `extraWords` lets a caller add the words IT
|
|
21524
|
+
* can translate without this module knowing what a locale is; they score like a
|
|
21525
|
+
* synonym. The key outranks a synonym, a synonym outranks a lucide tag, and
|
|
21526
|
+
* ties keep key order so the list does not reshuffle between keystrokes.
|
|
21445
21527
|
*/
|
|
21446
|
-
|
|
21447
|
-
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
|
|
21456
|
-
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
|
|
21462
|
-
|
|
21463
|
-
|
|
21464
|
-
|
|
21465
|
-
|
|
21466
|
-
|
|
21467
|
-
|
|
21468
|
-
|
|
21469
|
-
|
|
21470
|
-
|
|
21471
|
-
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
21475
|
-
|
|
21476
|
-
|
|
21477
|
-
|
|
21478
|
-
|
|
21479
|
-
* not been updated — a client that cannot determine the type should prefer
|
|
21480
|
-
* its raster path, which is the safe default for an unknown image.
|
|
21481
|
-
*/
|
|
21482
|
-
iconMediaType: zod.z.string().optional(),
|
|
21483
|
-
configSchema: ConfigSchemaPassthrough,
|
|
21484
|
-
/**
|
|
21485
|
-
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
21486
|
-
* base URL, instance, chat, recipient). A save that changes any of them
|
|
21487
|
-
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
21488
|
-
* editor uses this to ask for the secret on exactly those edits instead of
|
|
21489
|
-
* on every edit.
|
|
21490
|
-
*
|
|
21491
|
-
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
21492
|
-
* kinds) and from a provider older than this field — an editor that finds
|
|
21493
|
-
* it absent must assume ANY edit may need the secret.
|
|
21494
|
-
*/
|
|
21495
|
-
destinationKeys: zod.z.array(zod.z.string()).optional(),
|
|
21496
|
-
supportsDiscovery: zod.z.boolean(),
|
|
21497
|
-
/**
|
|
21498
|
-
* Extra delivery parameters a rule may set for a target of this kind (D938).
|
|
21499
|
-
* Absent = none; the rule editor then offers nothing.
|
|
21500
|
-
*/
|
|
21501
|
-
deliveryParams: zod.z.array(DeliveryParamSchema).optional(),
|
|
21502
|
-
caps: TargetKindCapsSchema
|
|
21503
|
-
});
|
|
21528
|
+
function searchIcons(query, extraWords) {
|
|
21529
|
+
const q = normalise(query);
|
|
21530
|
+
if (q.length === 0) return ENTRIES.map((entry) => ({
|
|
21531
|
+
entry,
|
|
21532
|
+
score: 0
|
|
21533
|
+
}));
|
|
21534
|
+
const hits = [];
|
|
21535
|
+
for (const entry of ENTRIES) {
|
|
21536
|
+
const words = wordsOf(entry);
|
|
21537
|
+
const keyScore = scoreWord(q, words.key, true);
|
|
21538
|
+
let best = keyScore > 0 ? keyScore + 2 : 0;
|
|
21539
|
+
for (const word of [...words.synonyms, ...(extraWords?.get(entry.key) ?? []).map(normalise)]) {
|
|
21540
|
+
const score = scoreWord(q, word, true);
|
|
21541
|
+
if (score > 0 && score + 1 > best) best = score + 1;
|
|
21542
|
+
}
|
|
21543
|
+
for (const word of words.tags) {
|
|
21544
|
+
const score = scoreWord(q, word, false);
|
|
21545
|
+
if (score > best) best = score;
|
|
21546
|
+
}
|
|
21547
|
+
if (best > 0) hits.push({
|
|
21548
|
+
entry,
|
|
21549
|
+
score: best
|
|
21550
|
+
});
|
|
21551
|
+
}
|
|
21552
|
+
return hits.toSorted((a, b) => b.score - a.score);
|
|
21553
|
+
}
|
|
21554
|
+
/** A readable label derived from a lucide category id (`food-beverage` → `Food beverage`). */
|
|
21555
|
+
function iconCategoryLabel(category) {
|
|
21556
|
+
const spaced = category.replace(/-/g, " ");
|
|
21557
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
21558
|
+
}
|
|
21559
|
+
//#endregion
|
|
21560
|
+
//#region src/device/location-icon.ts
|
|
21504
21561
|
/**
|
|
21505
|
-
*
|
|
21506
|
-
*
|
|
21507
|
-
*
|
|
21562
|
+
* What a location's icon is, and the ONE key its label joins on.
|
|
21563
|
+
*
|
|
21564
|
+
* A location is not an entity — it is a free-form label (`PersistedDeviceMeta.
|
|
21565
|
+
* location`) plus an operator-curated registry of labels in the device-manager
|
|
21566
|
+
* addon store. The icon is a second map beside that registry, keyed the same
|
|
21567
|
+
* way the registry matches everywhere else: {@link locationIconKey}, i.e.
|
|
21568
|
+
* trimmed + lower-cased.
|
|
21569
|
+
*
|
|
21570
|
+
* The icon itself is a key of the ONE icon catalog (`icon-catalog.ts`, D854):
|
|
21571
|
+
* a location may wear any icon both surfaces can draw. There used to be a
|
|
21572
|
+
* closed 20-icon subset here (D511); it was a second vocabulary on the same two
|
|
21573
|
+
* surfaces and is gone. The names below keep their meaning:
|
|
21574
|
+
*
|
|
21575
|
+
* DEGRADATION IS A FALLBACK, NEVER A HOLE. A surface that cannot draw a stored
|
|
21576
|
+
* value — a persisted value from a newer hub, a hand-edited settings row —
|
|
21577
|
+
* draws {@link LOCATION_ICON_FALLBACK} (`map-pin`, the one every surface has
|
|
21578
|
+
* always had). Read a stored/wire value through
|
|
21579
|
+
* `resolveIconKey(value, LOCATION_ICON_FALLBACK)` and that is what you get;
|
|
21580
|
+
* nothing anywhere renders an empty box.
|
|
21508
21581
|
*/
|
|
21509
|
-
var TargetSchema = zod.z.object({
|
|
21510
|
-
id: zod.z.string(),
|
|
21511
|
-
name: zod.z.string(),
|
|
21512
|
-
kind: zod.z.string(),
|
|
21513
|
-
addonId: zod.z.string(),
|
|
21514
|
-
enabled: zod.z.boolean(),
|
|
21515
|
-
config: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
21516
|
-
});
|
|
21517
|
-
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
21518
|
-
var DiscoveredTargetSchema = zod.z.object({
|
|
21519
|
-
kind: zod.z.string(),
|
|
21520
|
-
suggestedName: zod.z.string(),
|
|
21521
|
-
config: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
21522
|
-
});
|
|
21523
|
-
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
21524
|
-
var RenderedAsSchema = zod.z.object({
|
|
21525
|
-
level: zod.z.string(),
|
|
21526
|
-
format: NotificationFormatSchema,
|
|
21527
|
-
attachmentsSent: zod.z.number().int().nonnegative(),
|
|
21528
|
-
actionsSent: zod.z.number().int().nonnegative(),
|
|
21529
|
-
truncated: zod.z.boolean(),
|
|
21530
|
-
dropped: zod.z.array(zod.z.string())
|
|
21531
|
-
});
|
|
21532
|
-
var SendResultSchema = zod.z.object({
|
|
21533
|
-
success: zod.z.boolean(),
|
|
21534
|
-
error: zod.z.string().optional(),
|
|
21535
|
-
renderedAs: RenderedAsSchema.optional()
|
|
21536
|
-
});
|
|
21537
|
-
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
21538
|
-
var TestResultSchema = SendResultSchema;
|
|
21539
21582
|
/**
|
|
21540
|
-
*
|
|
21541
|
-
*
|
|
21583
|
+
* The id every surface is guaranteed to draw, and what an unrecognised value
|
|
21584
|
+
* degrades to. It has been in both icon sets since before the catalog existed
|
|
21585
|
+
* (the Locations page's own page icon), so "the fallback is missing" is not a
|
|
21586
|
+
* state a build can reach.
|
|
21542
21587
|
*/
|
|
21543
|
-
var
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
stateInstance: zod.z.string().optional(),
|
|
21548
|
-
error: zod.z.string().optional()
|
|
21588
|
+
var LOCATION_ICON_FALLBACK = "map-pin";
|
|
21589
|
+
zod.z.object({
|
|
21590
|
+
locationKey: zod.z.string(),
|
|
21591
|
+
icon: IconKeySchema
|
|
21549
21592
|
});
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
config: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
21562
|
-
/** See `discoverTargets.targetId` — restore stored secrets for a blank password. */
|
|
21563
|
-
targetId: zod.z.string().optional()
|
|
21564
|
-
}), ProbeTargetConfigResultSchema, {
|
|
21565
|
-
kind: "mutation",
|
|
21566
|
-
auth: "admin"
|
|
21567
|
-
}), method(zod.z.object({
|
|
21568
|
-
targetId: zod.z.string(),
|
|
21569
|
-
notification: NotificationSchema
|
|
21570
|
-
}), SendResultSchema, {
|
|
21571
|
-
kind: "mutation",
|
|
21572
|
-
auth: "admin"
|
|
21573
|
-
}), method(zod.z.object({
|
|
21574
|
-
targetId: zod.z.string(),
|
|
21575
|
-
sample: NotificationSchema.optional()
|
|
21576
|
-
}), TestResultSchema, {
|
|
21577
|
-
kind: "mutation",
|
|
21578
|
-
auth: "admin"
|
|
21579
|
-
}), method(zod.z.object({
|
|
21580
|
-
target: TargetSchema,
|
|
21581
|
-
/**
|
|
21582
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
21583
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
21584
|
-
* password fields are restored from this source (D634 still applies if
|
|
21585
|
-
* destinations diverge without a re-typed secret).
|
|
21586
|
-
*/
|
|
21587
|
-
cloneSecretsFrom: zod.z.string().optional()
|
|
21588
|
-
}), TargetSchema, {
|
|
21589
|
-
kind: "mutation",
|
|
21590
|
-
auth: "admin"
|
|
21591
|
-
}), method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), {
|
|
21592
|
-
kind: "mutation",
|
|
21593
|
-
auth: "admin"
|
|
21594
|
-
}), method(zod.z.object({
|
|
21595
|
-
targetId: zod.z.string(),
|
|
21596
|
-
enabled: zod.z.boolean()
|
|
21597
|
-
}), zod.z.void(), {
|
|
21598
|
-
kind: "mutation",
|
|
21599
|
-
auth: "admin"
|
|
21593
|
+
/**
|
|
21594
|
+
* The join key for a location label: trimmed, lower-cased. The ONE place this
|
|
21595
|
+
* derivation lives — a caller that re-spells it is a rename away from an
|
|
21596
|
+
* orphaned icon.
|
|
21597
|
+
*/
|
|
21598
|
+
function locationIconKey(label) {
|
|
21599
|
+
return label.trim().toLowerCase();
|
|
21600
|
+
}
|
|
21601
|
+
zod.z.object({
|
|
21602
|
+
locationKey: zod.z.string(),
|
|
21603
|
+
description: zod.z.string()
|
|
21600
21604
|
});
|
|
21601
21605
|
//#endregion
|
|
21602
21606
|
//#region src/catalogs/audio-classmap.ts
|
|
@@ -25798,7 +25802,8 @@ var TopologyNodeSchema = zod.z.object({
|
|
|
25798
25802
|
isHub: zod.z.boolean(),
|
|
25799
25803
|
isOnline: zod.z.boolean(),
|
|
25800
25804
|
cpuPercent: zod.z.number(),
|
|
25801
|
-
|
|
25805
|
+
/** `null` when the node's memory read failed or it has not reported one (D393). */
|
|
25806
|
+
memoryPercent: zod.z.number().nullable(),
|
|
25802
25807
|
uptime: zod.z.number(),
|
|
25803
25808
|
lastSeen: zod.z.string(),
|
|
25804
25809
|
localIps: zod.z.array(zod.z.string()).readonly(),
|
|
@@ -27017,6 +27022,14 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
27017
27022
|
setVolume: (input) => dispatch("pet-feeder", "petFeeder", "setVolume", "mutation", input),
|
|
27018
27023
|
getStatus: (input) => dispatch("pet-feeder", "petFeeder", "getStatus", "query", input)
|
|
27019
27024
|
};
|
|
27025
|
+
const petFountainInterface = {
|
|
27026
|
+
setPump: (input) => dispatch("pet-fountain", "petFountain", "setPump", "mutation", input),
|
|
27027
|
+
setPumpInterval: (input) => dispatch("pet-fountain", "petFountain", "setPumpInterval", "mutation", input),
|
|
27028
|
+
setPumpOnMinutes: (input) => dispatch("pet-fountain", "petFountain", "setPumpOnMinutes", "mutation", input),
|
|
27029
|
+
setPumpOffMinutes: (input) => dispatch("pet-fountain", "petFountain", "setPumpOffMinutes", "mutation", input),
|
|
27030
|
+
resetDailyConsumption: (input) => dispatch("pet-fountain", "petFountain", "resetDailyConsumption", "mutation", input),
|
|
27031
|
+
getStatus: (input) => dispatch("pet-fountain", "petFountain", "getStatus", "query", input)
|
|
27032
|
+
};
|
|
27020
27033
|
const pipelineAnalyticsInterface = {
|
|
27021
27034
|
getActiveTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getActiveTracks", "query", input),
|
|
27022
27035
|
getTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrack", "query", input),
|
|
@@ -27485,6 +27498,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
27485
27498
|
notifier: createSliceHandle(stateSource, binding.deviceId, "notifier"),
|
|
27486
27499
|
numericSensor: createSliceHandle(stateSource, binding.deviceId, "numeric-sensor"),
|
|
27487
27500
|
petFeeder: createSliceHandle(stateSource, binding.deviceId, "pet-feeder"),
|
|
27501
|
+
petFountain: createSliceHandle(stateSource, binding.deviceId, "pet-fountain"),
|
|
27488
27502
|
powerMeter: createSliceHandle(stateSource, binding.deviceId, "power-meter"),
|
|
27489
27503
|
presence: createSliceHandle(stateSource, binding.deviceId, "presence"),
|
|
27490
27504
|
pressureSensor: createSliceHandle(stateSource, binding.deviceId, "pressure-sensor"),
|
|
@@ -27575,6 +27589,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
27575
27589
|
...bound("numeric-sensor") ? { numericSensor: numericSensorInterface } : {},
|
|
27576
27590
|
...bound("osd") ? { osd: osdInterface } : {},
|
|
27577
27591
|
...bound("pet-feeder") ? { petFeeder: petFeederInterface } : {},
|
|
27592
|
+
...bound("pet-fountain") ? { petFountain: petFountainInterface } : {},
|
|
27578
27593
|
...bound("pipeline-analytics") ? { pipelineAnalytics: pipelineAnalyticsInterface } : {},
|
|
27579
27594
|
...bound("power-meter") ? { powerMeter: powerMeterInterface } : {},
|
|
27580
27595
|
...bound("presence") ? { presence: presenceInterface } : {},
|
|
@@ -28501,7 +28516,11 @@ function createSystemProxy(api) {
|
|
|
28501
28516
|
listSupportedDeviceKinds: (input) => dispatch("deviceExport", "listSupportedDeviceKinds", "query", input),
|
|
28502
28517
|
listExposedDevices: (input) => dispatch("deviceExport", "listExposedDevices", "query", input),
|
|
28503
28518
|
exposeDevice: (input) => dispatch("deviceExport", "exposeDevice", "mutation", input),
|
|
28504
|
-
unexposeDevice: (input) => dispatch("deviceExport", "unexposeDevice", "mutation", input)
|
|
28519
|
+
unexposeDevice: (input) => dispatch("deviceExport", "unexposeDevice", "mutation", input),
|
|
28520
|
+
listSupportedEntityKinds: (input) => dispatch("deviceExport", "listSupportedEntityKinds", "query", input),
|
|
28521
|
+
listExposedEntities: (input) => dispatch("deviceExport", "listExposedEntities", "query", input),
|
|
28522
|
+
exposeEntity: (input) => dispatch("deviceExport", "exposeEntity", "mutation", input),
|
|
28523
|
+
unexposeEntity: (input) => dispatch("deviceExport", "unexposeEntity", "mutation", input)
|
|
28505
28524
|
},
|
|
28506
28525
|
deviceHistory: {
|
|
28507
28526
|
listNodeSeries: (input) => dispatch("deviceHistory", "listNodeSeries", "query", input),
|
|
@@ -28951,7 +28970,9 @@ function createSystemProxy(api) {
|
|
|
28951
28970
|
checkServerUpdate: (input) => dispatch("serverManagement", "checkServerUpdate", "mutation", input),
|
|
28952
28971
|
applyServerUpdate: (input) => dispatch("serverManagement", "applyServerUpdate", "mutation", input),
|
|
28953
28972
|
rollbackServerUpdate: (input) => dispatch("serverManagement", "rollbackServerUpdate", "mutation", input),
|
|
28954
|
-
restartServer: (input) => dispatch("serverManagement", "restartServer", "mutation", input)
|
|
28973
|
+
restartServer: (input) => dispatch("serverManagement", "restartServer", "mutation", input),
|
|
28974
|
+
getLaunchOverrides: (input) => dispatch("serverManagement", "getLaunchOverrides", "query", input),
|
|
28975
|
+
setLaunchOverrides: (input) => dispatch("serverManagement", "setLaunchOverrides", "mutation", input)
|
|
28955
28976
|
},
|
|
28956
28977
|
settingsStore: {
|
|
28957
28978
|
get: (input) => dispatch("settingsStore", "get", "query", input),
|