@camstack/addon-advanced-notifier 1.1.13 → 1.1.14

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/dist/addon.js CHANGED
@@ -4632,7 +4632,7 @@ function _instanceof(cls, params = {}) {
4632
4632
  return inst;
4633
4633
  }
4634
4634
  //#endregion
4635
- //#region ../types/dist/sleep-C2M2zF7x.mjs
4635
+ //#region ../types/dist/sleep-MHm--th-.mjs
4636
4636
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4637
4637
  EventCategory["SystemBoot"] = "system.boot";
4638
4638
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5894,6 +5894,7 @@ var CamStreamKindSchema = _enum([
5894
5894
  "pull-rtsp",
5895
5895
  "pull-rtmp",
5896
5896
  "pull-http",
5897
+ "pull-flv",
5897
5898
  "pull-rfc4571",
5898
5899
  "push-annexb",
5899
5900
  "derived"
@@ -13217,7 +13218,7 @@ var AddBrokerInputSchema = object({
13217
13218
  });
13218
13219
  var AddBrokerResultSchema = object({ id: string() });
13219
13220
  var IdInputSchema = object({ id: string() });
13220
- var TestResultSchema = discriminatedUnion("ok", [object({
13221
+ var TestResultSchema$1 = discriminatedUnion("ok", [object({
13221
13222
  ok: literal(true),
13222
13223
  latencyMs: number()
13223
13224
  }), object({
@@ -13240,7 +13241,7 @@ var StatusSchema = object({
13240
13241
  brokerCount: number(),
13241
13242
  embeddedRunning: boolean()
13242
13243
  });
13243
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13244
+ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13244
13245
  var NetworkEndpointSchema = object({
13245
13246
  url: string(),
13246
13247
  hostname: string(),
@@ -13274,29 +13275,210 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
13274
13275
  sourcePort: number().optional()
13275
13276
  });
13276
13277
  method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
13278
+ /**
13279
+ * notification-output — canonical, capability-gated notification delivery.
13280
+ *
13281
+ * Apprise-derived model (see
13282
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
13283
+ * callers emit ONE canonical `Notification`; each provider declares a
13284
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
13285
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
13286
+ * message to what the kind supports — callers never special-case a service.
13287
+ *
13288
+ * DESIGN DECISIONS (locked):
13289
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
13290
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
13291
+ * cap. Rationale: the admin UI needs one uniform surface across the
13292
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
13293
+ * alternative would fork the UI per addon and cannot host the
13294
+ * discovery→adopt flow.
13295
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
13296
+ * the generated cap-mount auto-`concatCollection`-fans them across every
13297
+ * registered provider (notifiers addon + HA addon) so one catalog is
13298
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
13299
+ * `addonId` the generated collection router extracts from the call input.
13300
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
13301
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
13302
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
13303
+ * base64 fallback needed.
13304
+ *
13305
+ * TODO (deferred, closed-set change — separate decision): add
13306
+ * `providerKind: 'notify'` so notification providers surface on the unified
13307
+ * admin "Integrations" page.
13308
+ */
13309
+ /**
13310
+ * Zentik-derived typed-media enum — the superset across every kind. Each
13311
+ * adapter picks what it supports and the degrade engine filters the rest.
13312
+ */
13313
+ var AttachmentMediaTypeSchema = _enum([
13314
+ "image",
13315
+ "video",
13316
+ "gif",
13317
+ "audio",
13318
+ "icon"
13319
+ ]);
13320
+ /**
13321
+ * A single attachment. Exactly one of `url` (remote source, most adapters
13322
+ * prefer this) or `bytes` (inline source; required for Pushover-style
13323
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
13324
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
13325
+ */
13326
+ var AttachmentSchema = object({
13327
+ mediaType: AttachmentMediaTypeSchema,
13328
+ url: string().optional(),
13329
+ bytes: _instanceof(Uint8Array).optional(),
13330
+ mime: string().optional(),
13331
+ name: string().optional()
13332
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
13333
+ var NotificationFormatSchema = _enum([
13334
+ "text",
13335
+ "markdown",
13336
+ "html"
13337
+ ]);
13338
+ /** A single tap-through action button. */
13339
+ var NotificationActionSchema = object({
13340
+ id: string(),
13341
+ label: string(),
13342
+ url: string().optional()
13343
+ });
13344
+ /**
13345
+ * The canonical notification. `body` is the only hard field (Apprise model).
13346
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
13347
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
13348
+ * the adapter maps this ordinal onto its native level. `level?` is an
13349
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
13350
+ * `priority` for that one target.
13351
+ */
13352
+ var NotificationSchema = object({
13353
+ body: string(),
13354
+ title: string().optional(),
13355
+ format: NotificationFormatSchema.default("text"),
13356
+ priority: number().int().min(1).max(5).default(3),
13357
+ level: string().optional(),
13358
+ attachments: array(AttachmentSchema).optional(),
13359
+ clickUrl: string().optional(),
13360
+ actions: array(NotificationActionSchema).optional(),
13361
+ sound: string().optional(),
13362
+ ttl: number().optional(),
13363
+ tag: string().optional(),
13364
+ deviceId: number().optional(),
13365
+ eventId: string().optional(),
13366
+ metadata: record(string(), unknown()).optional()
13367
+ });
13368
+ /** One declared native severity/priority level for a kind. */
13369
+ var TargetKindLevelSchema = object({
13370
+ id: string(),
13371
+ label: string(),
13372
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
13373
+ ordinal: number().int().min(1).max(5).nullable(),
13374
+ flags: object({
13375
+ critical: boolean().optional(),
13376
+ silent: boolean().optional(),
13377
+ noPush: boolean().optional()
13378
+ }).optional(),
13379
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
13380
+ requires: array(string()).optional(),
13381
+ description: string().optional()
13382
+ });
13383
+ /** The full capability block consulted before dispatch. */
13384
+ var TargetKindCapsSchema = object({
13385
+ attachments: object({
13386
+ mediaTypes: array(AttachmentMediaTypeSchema),
13387
+ mode: _enum([
13388
+ "url",
13389
+ "bytes",
13390
+ "both"
13391
+ ]),
13392
+ max: number().int().nonnegative(),
13393
+ maxBytes: number().int().positive().optional()
13394
+ }),
13395
+ /** Max action buttons (0 = none). */
13396
+ actions: number().int().nonnegative(),
13397
+ levels: array(TargetKindLevelSchema),
13398
+ format: array(NotificationFormatSchema),
13399
+ clickUrl: boolean(),
13400
+ sound: boolean(),
13401
+ ttl: boolean(),
13402
+ bodyMaxLen: number().int().positive()
13403
+ });
13404
+ /**
13405
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
13406
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
13407
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
13408
+ * the union is large and not meant for runtime validation here; the exported
13409
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
13410
+ */
13411
+ var ConfigSchemaPassthrough = unknown();
13412
+ var TargetKindSchema = object({
13413
+ kind: string(),
13414
+ label: string(),
13415
+ icon: string(),
13416
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
13417
+ addonId: string(),
13418
+ configSchema: ConfigSchemaPassthrough,
13419
+ supportsDiscovery: boolean(),
13420
+ caps: TargetKindCapsSchema
13421
+ });
13422
+ /**
13423
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
13424
+ * (return a presence marker only) when serving `listTargets` — never
13425
+ * round-trip a stored secret to the UI.
13426
+ */
13427
+ var TargetSchema = object({
13428
+ id: string(),
13429
+ name: string(),
13430
+ kind: string(),
13431
+ addonId: string(),
13432
+ enabled: boolean(),
13433
+ config: record(string(), unknown())
13434
+ });
13435
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
13436
+ var DiscoveredTargetSchema = object({
13437
+ kind: string(),
13438
+ suggestedName: string(),
13439
+ config: record(string(), unknown())
13440
+ });
13441
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
13442
+ var RenderedAsSchema = object({
13443
+ level: string(),
13444
+ format: NotificationFormatSchema,
13445
+ attachmentsSent: number().int().nonnegative(),
13446
+ actionsSent: number().int().nonnegative(),
13447
+ truncated: boolean(),
13448
+ dropped: array(string())
13449
+ });
13450
+ var SendResultSchema = object({
13451
+ success: boolean(),
13452
+ error: string().optional(),
13453
+ renderedAs: RenderedAsSchema.optional()
13454
+ });
13455
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
13456
+ var TestResultSchema = SendResultSchema;
13277
13457
  var notificationOutputCapability = {
13278
13458
  name: "notification-output",
13279
13459
  scope: "system",
13280
13460
  mode: "collection",
13281
13461
  methods: {
13462
+ listTargetKinds: method(object({}), array(TargetKindSchema)),
13463
+ listTargets: method(object({}), array(TargetSchema)),
13464
+ discoverTargets: method(object({
13465
+ kind: string(),
13466
+ config: record(string(), unknown()).optional()
13467
+ }), array(DiscoveredTargetSchema)),
13282
13468
  send: method(object({
13283
- title: string(),
13284
- body: string(),
13285
- imageUrl: string().optional(),
13286
- deviceId: number().optional(),
13287
- eventId: string().optional(),
13288
- priority: _enum([
13289
- "low",
13290
- "normal",
13291
- "high",
13292
- "critical"
13293
- ]).default("normal"),
13294
- metadata: record(string(), unknown()).optional()
13295
- }), _void(), { kind: "mutation" }),
13296
- sendTest: method(_void(), object({
13297
- success: boolean(),
13298
- error: string().optional()
13299
- }), { kind: "mutation" })
13469
+ targetId: string(),
13470
+ notification: NotificationSchema
13471
+ }), SendResultSchema, { kind: "mutation" }),
13472
+ testTarget: method(object({
13473
+ targetId: string(),
13474
+ sample: NotificationSchema.optional()
13475
+ }), TestResultSchema, { kind: "mutation" }),
13476
+ upsertTarget: method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
13477
+ deleteTarget: method(object({ targetId: string() }), _void(), { kind: "mutation" }),
13478
+ setTargetEnabled: method(object({
13479
+ targetId: string(),
13480
+ enabled: boolean()
13481
+ }), _void(), { kind: "mutation" })
13300
13482
  }
13301
13483
  };
13302
13484
  /**
@@ -19293,13 +19475,49 @@ Object.freeze({
19293
19475
  addonId: null,
19294
19476
  access: "create"
19295
19477
  },
19478
+ "notificationOutput.deleteTarget": {
19479
+ capName: "notification-output",
19480
+ capScope: "system",
19481
+ addonId: null,
19482
+ access: "delete"
19483
+ },
19484
+ "notificationOutput.discoverTargets": {
19485
+ capName: "notification-output",
19486
+ capScope: "system",
19487
+ addonId: null,
19488
+ access: "view"
19489
+ },
19490
+ "notificationOutput.listTargetKinds": {
19491
+ capName: "notification-output",
19492
+ capScope: "system",
19493
+ addonId: null,
19494
+ access: "view"
19495
+ },
19496
+ "notificationOutput.listTargets": {
19497
+ capName: "notification-output",
19498
+ capScope: "system",
19499
+ addonId: null,
19500
+ access: "view"
19501
+ },
19296
19502
  "notificationOutput.send": {
19297
19503
  capName: "notification-output",
19298
19504
  capScope: "system",
19299
19505
  addonId: null,
19300
19506
  access: "create"
19301
19507
  },
19302
- "notificationOutput.sendTest": {
19508
+ "notificationOutput.setTargetEnabled": {
19509
+ capName: "notification-output",
19510
+ capScope: "system",
19511
+ addonId: null,
19512
+ access: "create"
19513
+ },
19514
+ "notificationOutput.testTarget": {
19515
+ capName: "notification-output",
19516
+ capScope: "system",
19517
+ addonId: null,
19518
+ access: "create"
19519
+ },
19520
+ "notificationOutput.upsertTarget": {
19303
19521
  capName: "notification-output",
19304
19522
  capScope: "system",
19305
19523
  addonId: null,
@@ -21427,15 +21645,14 @@ var ConsoleOutput = class {
21427
21645
  this.logger = logger;
21428
21646
  }
21429
21647
  async send(notification) {
21430
- this.logger.info(`[${notification.severity}] ${notification.title}: ${notification.message}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
21648
+ this.logger.info(`[p${notification.priority}] ${notification.title ?? ""}: ${notification.body}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
21431
21649
  }
21432
21650
  async sendTest() {
21433
21651
  await this.send({
21434
21652
  title: "Test",
21435
- message: "Console output working",
21436
- severity: "info",
21437
- category: "test",
21438
- timestamp: Date.now()
21653
+ body: "Console output working",
21654
+ format: "text",
21655
+ priority: 3
21439
21656
  });
21440
21657
  return { success: true };
21441
21658
  }
@@ -21703,12 +21920,11 @@ var AdvancedNotifierAddon = class extends BaseAddon {
21703
21920
  const deviceId = Number.isFinite(deviceIdNum) ? deviceIdNum : void 0;
21704
21921
  const notification = {
21705
21922
  title,
21706
- message,
21707
- severity: this.priorityToSeverity(rule.priority),
21708
- category: event.category,
21923
+ body: message,
21924
+ format: "text",
21925
+ priority: this.priorityToOrdinal(rule.priority),
21709
21926
  deviceId,
21710
- data: event.data,
21711
- timestamp: event.timestamp.getTime()
21927
+ metadata: event.data
21712
21928
  };
21713
21929
  let success = true;
21714
21930
  let dispatchError;
@@ -21753,10 +21969,18 @@ var AdvancedNotifierAddon = class extends BaseAddon {
21753
21969
  timestamp: event.timestamp.toISOString()
21754
21970
  };
21755
21971
  }
21756
- priorityToSeverity(priority) {
21757
- if (priority === "critical") return "critical";
21758
- if (priority === "high") return "warning";
21759
- return "info";
21972
+ /**
21973
+ * Map a rule's qualitative priority onto the canonical 1..5 ordinal.
21974
+ * Callers now pass ordinal priority; each `notification-output` provider
21975
+ * maps the ordinal onto its declared native level.
21976
+ */
21977
+ priorityToOrdinal(priority) {
21978
+ switch (priority) {
21979
+ case "critical": return 5;
21980
+ case "high": return 4;
21981
+ case "low": return 2;
21982
+ default: return 3;
21983
+ }
21760
21984
  }
21761
21985
  };
21762
21986
  //#endregion
package/dist/addon.mjs CHANGED
@@ -4628,7 +4628,7 @@ function _instanceof(cls, params = {}) {
4628
4628
  return inst;
4629
4629
  }
4630
4630
  //#endregion
4631
- //#region ../types/dist/sleep-C2M2zF7x.mjs
4631
+ //#region ../types/dist/sleep-MHm--th-.mjs
4632
4632
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4633
4633
  EventCategory["SystemBoot"] = "system.boot";
4634
4634
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5890,6 +5890,7 @@ var CamStreamKindSchema = _enum([
5890
5890
  "pull-rtsp",
5891
5891
  "pull-rtmp",
5892
5892
  "pull-http",
5893
+ "pull-flv",
5893
5894
  "pull-rfc4571",
5894
5895
  "push-annexb",
5895
5896
  "derived"
@@ -13213,7 +13214,7 @@ var AddBrokerInputSchema = object({
13213
13214
  });
13214
13215
  var AddBrokerResultSchema = object({ id: string() });
13215
13216
  var IdInputSchema = object({ id: string() });
13216
- var TestResultSchema = discriminatedUnion("ok", [object({
13217
+ var TestResultSchema$1 = discriminatedUnion("ok", [object({
13217
13218
  ok: literal(true),
13218
13219
  latencyMs: number()
13219
13220
  }), object({
@@ -13236,7 +13237,7 @@ var StatusSchema = object({
13236
13237
  brokerCount: number(),
13237
13238
  embeddedRunning: boolean()
13238
13239
  });
13239
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13240
+ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13240
13241
  var NetworkEndpointSchema = object({
13241
13242
  url: string(),
13242
13243
  hostname: string(),
@@ -13270,29 +13271,210 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
13270
13271
  sourcePort: number().optional()
13271
13272
  });
13272
13273
  method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
13274
+ /**
13275
+ * notification-output — canonical, capability-gated notification delivery.
13276
+ *
13277
+ * Apprise-derived model (see
13278
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
13279
+ * callers emit ONE canonical `Notification`; each provider declares a
13280
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
13281
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
13282
+ * message to what the kind supports — callers never special-case a service.
13283
+ *
13284
+ * DESIGN DECISIONS (locked):
13285
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
13286
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
13287
+ * cap. Rationale: the admin UI needs one uniform surface across the
13288
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
13289
+ * alternative would fork the UI per addon and cannot host the
13290
+ * discovery→adopt flow.
13291
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
13292
+ * the generated cap-mount auto-`concatCollection`-fans them across every
13293
+ * registered provider (notifiers addon + HA addon) so one catalog is
13294
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
13295
+ * `addonId` the generated collection router extracts from the call input.
13296
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
13297
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
13298
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
13299
+ * base64 fallback needed.
13300
+ *
13301
+ * TODO (deferred, closed-set change — separate decision): add
13302
+ * `providerKind: 'notify'` so notification providers surface on the unified
13303
+ * admin "Integrations" page.
13304
+ */
13305
+ /**
13306
+ * Zentik-derived typed-media enum — the superset across every kind. Each
13307
+ * adapter picks what it supports and the degrade engine filters the rest.
13308
+ */
13309
+ var AttachmentMediaTypeSchema = _enum([
13310
+ "image",
13311
+ "video",
13312
+ "gif",
13313
+ "audio",
13314
+ "icon"
13315
+ ]);
13316
+ /**
13317
+ * A single attachment. Exactly one of `url` (remote source, most adapters
13318
+ * prefer this) or `bytes` (inline source; required for Pushover-style
13319
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
13320
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
13321
+ */
13322
+ var AttachmentSchema = object({
13323
+ mediaType: AttachmentMediaTypeSchema,
13324
+ url: string().optional(),
13325
+ bytes: _instanceof(Uint8Array).optional(),
13326
+ mime: string().optional(),
13327
+ name: string().optional()
13328
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
13329
+ var NotificationFormatSchema = _enum([
13330
+ "text",
13331
+ "markdown",
13332
+ "html"
13333
+ ]);
13334
+ /** A single tap-through action button. */
13335
+ var NotificationActionSchema = object({
13336
+ id: string(),
13337
+ label: string(),
13338
+ url: string().optional()
13339
+ });
13340
+ /**
13341
+ * The canonical notification. `body` is the only hard field (Apprise model).
13342
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
13343
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
13344
+ * the adapter maps this ordinal onto its native level. `level?` is an
13345
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
13346
+ * `priority` for that one target.
13347
+ */
13348
+ var NotificationSchema = object({
13349
+ body: string(),
13350
+ title: string().optional(),
13351
+ format: NotificationFormatSchema.default("text"),
13352
+ priority: number().int().min(1).max(5).default(3),
13353
+ level: string().optional(),
13354
+ attachments: array(AttachmentSchema).optional(),
13355
+ clickUrl: string().optional(),
13356
+ actions: array(NotificationActionSchema).optional(),
13357
+ sound: string().optional(),
13358
+ ttl: number().optional(),
13359
+ tag: string().optional(),
13360
+ deviceId: number().optional(),
13361
+ eventId: string().optional(),
13362
+ metadata: record(string(), unknown()).optional()
13363
+ });
13364
+ /** One declared native severity/priority level for a kind. */
13365
+ var TargetKindLevelSchema = object({
13366
+ id: string(),
13367
+ label: string(),
13368
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
13369
+ ordinal: number().int().min(1).max(5).nullable(),
13370
+ flags: object({
13371
+ critical: boolean().optional(),
13372
+ silent: boolean().optional(),
13373
+ noPush: boolean().optional()
13374
+ }).optional(),
13375
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
13376
+ requires: array(string()).optional(),
13377
+ description: string().optional()
13378
+ });
13379
+ /** The full capability block consulted before dispatch. */
13380
+ var TargetKindCapsSchema = object({
13381
+ attachments: object({
13382
+ mediaTypes: array(AttachmentMediaTypeSchema),
13383
+ mode: _enum([
13384
+ "url",
13385
+ "bytes",
13386
+ "both"
13387
+ ]),
13388
+ max: number().int().nonnegative(),
13389
+ maxBytes: number().int().positive().optional()
13390
+ }),
13391
+ /** Max action buttons (0 = none). */
13392
+ actions: number().int().nonnegative(),
13393
+ levels: array(TargetKindLevelSchema),
13394
+ format: array(NotificationFormatSchema),
13395
+ clickUrl: boolean(),
13396
+ sound: boolean(),
13397
+ ttl: boolean(),
13398
+ bodyMaxLen: number().int().positive()
13399
+ });
13400
+ /**
13401
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
13402
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
13403
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
13404
+ * the union is large and not meant for runtime validation here; the exported
13405
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
13406
+ */
13407
+ var ConfigSchemaPassthrough = unknown();
13408
+ var TargetKindSchema = object({
13409
+ kind: string(),
13410
+ label: string(),
13411
+ icon: string(),
13412
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
13413
+ addonId: string(),
13414
+ configSchema: ConfigSchemaPassthrough,
13415
+ supportsDiscovery: boolean(),
13416
+ caps: TargetKindCapsSchema
13417
+ });
13418
+ /**
13419
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
13420
+ * (return a presence marker only) when serving `listTargets` — never
13421
+ * round-trip a stored secret to the UI.
13422
+ */
13423
+ var TargetSchema = object({
13424
+ id: string(),
13425
+ name: string(),
13426
+ kind: string(),
13427
+ addonId: string(),
13428
+ enabled: boolean(),
13429
+ config: record(string(), unknown())
13430
+ });
13431
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
13432
+ var DiscoveredTargetSchema = object({
13433
+ kind: string(),
13434
+ suggestedName: string(),
13435
+ config: record(string(), unknown())
13436
+ });
13437
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
13438
+ var RenderedAsSchema = object({
13439
+ level: string(),
13440
+ format: NotificationFormatSchema,
13441
+ attachmentsSent: number().int().nonnegative(),
13442
+ actionsSent: number().int().nonnegative(),
13443
+ truncated: boolean(),
13444
+ dropped: array(string())
13445
+ });
13446
+ var SendResultSchema = object({
13447
+ success: boolean(),
13448
+ error: string().optional(),
13449
+ renderedAs: RenderedAsSchema.optional()
13450
+ });
13451
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
13452
+ var TestResultSchema = SendResultSchema;
13273
13453
  var notificationOutputCapability = {
13274
13454
  name: "notification-output",
13275
13455
  scope: "system",
13276
13456
  mode: "collection",
13277
13457
  methods: {
13458
+ listTargetKinds: method(object({}), array(TargetKindSchema)),
13459
+ listTargets: method(object({}), array(TargetSchema)),
13460
+ discoverTargets: method(object({
13461
+ kind: string(),
13462
+ config: record(string(), unknown()).optional()
13463
+ }), array(DiscoveredTargetSchema)),
13278
13464
  send: method(object({
13279
- title: string(),
13280
- body: string(),
13281
- imageUrl: string().optional(),
13282
- deviceId: number().optional(),
13283
- eventId: string().optional(),
13284
- priority: _enum([
13285
- "low",
13286
- "normal",
13287
- "high",
13288
- "critical"
13289
- ]).default("normal"),
13290
- metadata: record(string(), unknown()).optional()
13291
- }), _void(), { kind: "mutation" }),
13292
- sendTest: method(_void(), object({
13293
- success: boolean(),
13294
- error: string().optional()
13295
- }), { kind: "mutation" })
13465
+ targetId: string(),
13466
+ notification: NotificationSchema
13467
+ }), SendResultSchema, { kind: "mutation" }),
13468
+ testTarget: method(object({
13469
+ targetId: string(),
13470
+ sample: NotificationSchema.optional()
13471
+ }), TestResultSchema, { kind: "mutation" }),
13472
+ upsertTarget: method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
13473
+ deleteTarget: method(object({ targetId: string() }), _void(), { kind: "mutation" }),
13474
+ setTargetEnabled: method(object({
13475
+ targetId: string(),
13476
+ enabled: boolean()
13477
+ }), _void(), { kind: "mutation" })
13296
13478
  }
13297
13479
  };
13298
13480
  /**
@@ -19289,13 +19471,49 @@ Object.freeze({
19289
19471
  addonId: null,
19290
19472
  access: "create"
19291
19473
  },
19474
+ "notificationOutput.deleteTarget": {
19475
+ capName: "notification-output",
19476
+ capScope: "system",
19477
+ addonId: null,
19478
+ access: "delete"
19479
+ },
19480
+ "notificationOutput.discoverTargets": {
19481
+ capName: "notification-output",
19482
+ capScope: "system",
19483
+ addonId: null,
19484
+ access: "view"
19485
+ },
19486
+ "notificationOutput.listTargetKinds": {
19487
+ capName: "notification-output",
19488
+ capScope: "system",
19489
+ addonId: null,
19490
+ access: "view"
19491
+ },
19492
+ "notificationOutput.listTargets": {
19493
+ capName: "notification-output",
19494
+ capScope: "system",
19495
+ addonId: null,
19496
+ access: "view"
19497
+ },
19292
19498
  "notificationOutput.send": {
19293
19499
  capName: "notification-output",
19294
19500
  capScope: "system",
19295
19501
  addonId: null,
19296
19502
  access: "create"
19297
19503
  },
19298
- "notificationOutput.sendTest": {
19504
+ "notificationOutput.setTargetEnabled": {
19505
+ capName: "notification-output",
19506
+ capScope: "system",
19507
+ addonId: null,
19508
+ access: "create"
19509
+ },
19510
+ "notificationOutput.testTarget": {
19511
+ capName: "notification-output",
19512
+ capScope: "system",
19513
+ addonId: null,
19514
+ access: "create"
19515
+ },
19516
+ "notificationOutput.upsertTarget": {
19299
19517
  capName: "notification-output",
19300
19518
  capScope: "system",
19301
19519
  addonId: null,
@@ -21423,15 +21641,14 @@ var ConsoleOutput = class {
21423
21641
  this.logger = logger;
21424
21642
  }
21425
21643
  async send(notification) {
21426
- this.logger.info(`[${notification.severity}] ${notification.title}: ${notification.message}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
21644
+ this.logger.info(`[p${notification.priority}] ${notification.title ?? ""}: ${notification.body}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
21427
21645
  }
21428
21646
  async sendTest() {
21429
21647
  await this.send({
21430
21648
  title: "Test",
21431
- message: "Console output working",
21432
- severity: "info",
21433
- category: "test",
21434
- timestamp: Date.now()
21649
+ body: "Console output working",
21650
+ format: "text",
21651
+ priority: 3
21435
21652
  });
21436
21653
  return { success: true };
21437
21654
  }
@@ -21699,12 +21916,11 @@ var AdvancedNotifierAddon = class extends BaseAddon {
21699
21916
  const deviceId = Number.isFinite(deviceIdNum) ? deviceIdNum : void 0;
21700
21917
  const notification = {
21701
21918
  title,
21702
- message,
21703
- severity: this.priorityToSeverity(rule.priority),
21704
- category: event.category,
21919
+ body: message,
21920
+ format: "text",
21921
+ priority: this.priorityToOrdinal(rule.priority),
21705
21922
  deviceId,
21706
- data: event.data,
21707
- timestamp: event.timestamp.getTime()
21923
+ metadata: event.data
21708
21924
  };
21709
21925
  let success = true;
21710
21926
  let dispatchError;
@@ -21749,10 +21965,18 @@ var AdvancedNotifierAddon = class extends BaseAddon {
21749
21965
  timestamp: event.timestamp.toISOString()
21750
21966
  };
21751
21967
  }
21752
- priorityToSeverity(priority) {
21753
- if (priority === "critical") return "critical";
21754
- if (priority === "high") return "warning";
21755
- return "info";
21968
+ /**
21969
+ * Map a rule's qualitative priority onto the canonical 1..5 ordinal.
21970
+ * Callers now pass ordinal priority; each `notification-output` provider
21971
+ * maps the ordinal onto its declared native level.
21972
+ */
21973
+ priorityToOrdinal(priority) {
21974
+ switch (priority) {
21975
+ case "critical": return 5;
21976
+ case "high": return 4;
21977
+ case "low": return 2;
21978
+ default: return 3;
21979
+ }
21756
21980
  }
21757
21981
  };
21758
21982
  //#endregion
package/dist/index.js CHANGED
@@ -26,6 +26,11 @@ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_OUTPUT_TIMEOUT_MS
26
26
  }
27
27
  //#endregion
28
28
  //#region src/outputs/webhook.ts
29
+ /**
30
+ * @deprecated Superseded by the `addon-notifiers` generic-webhook adapter
31
+ * (the `notification-output` cap collection provider). Kept only for
32
+ * compile-compat; not registered as a live output.
33
+ */
29
34
  var WebhookOutput = class {
30
35
  id = "webhook";
31
36
  name = "Webhook";
@@ -45,10 +50,9 @@ var WebhookOutput = class {
45
50
  try {
46
51
  await this.send({
47
52
  title: "Test Notification",
48
- message: "This is a test from CamStack Advanced Notifier",
49
- severity: "info",
50
- category: "test",
51
- timestamp: Date.now()
53
+ body: "This is a test from CamStack Advanced Notifier",
54
+ format: "text",
55
+ priority: 3
52
56
  });
53
57
  return { success: true };
54
58
  } catch (err) {
@@ -61,6 +65,12 @@ var WebhookOutput = class {
61
65
  };
62
66
  //#endregion
63
67
  //#region src/outputs/home-assistant.ts
68
+ /**
69
+ * @deprecated Superseded by the `homeassistant` kind in
70
+ * `addon-provider-homeassistant` (the `notification-output` cap collection
71
+ * provider, reusing the HA broker connection). Kept only for compile-compat;
72
+ * not registered as a live output.
73
+ */
64
74
  var HomeAssistantOutput = class {
65
75
  id = "home-assistant";
66
76
  name = "Home Assistant";
@@ -80,11 +90,10 @@ var HomeAssistantOutput = class {
80
90
  },
81
91
  body: JSON.stringify({
82
92
  title: notification.title,
83
- message: notification.message,
84
- severity: notification.severity,
85
- category: notification.category,
93
+ message: notification.body,
94
+ priority: notification.priority,
86
95
  device_id: notification.deviceId,
87
- data: notification.data
96
+ data: notification.metadata
88
97
  })
89
98
  });
90
99
  }
@@ -92,10 +101,9 @@ var HomeAssistantOutput = class {
92
101
  try {
93
102
  await this.send({
94
103
  title: "CamStack Test",
95
- message: "Connection verified",
96
- severity: "info",
97
- category: "test",
98
- timestamp: Date.now()
104
+ body: "Connection verified",
105
+ format: "text",
106
+ priority: 3
99
107
  });
100
108
  return { success: true };
101
109
  } catch (err) {
package/dist/index.mjs CHANGED
@@ -25,6 +25,11 @@ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_OUTPUT_TIMEOUT_MS
25
25
  }
26
26
  //#endregion
27
27
  //#region src/outputs/webhook.ts
28
+ /**
29
+ * @deprecated Superseded by the `addon-notifiers` generic-webhook adapter
30
+ * (the `notification-output` cap collection provider). Kept only for
31
+ * compile-compat; not registered as a live output.
32
+ */
28
33
  var WebhookOutput = class {
29
34
  id = "webhook";
30
35
  name = "Webhook";
@@ -44,10 +49,9 @@ var WebhookOutput = class {
44
49
  try {
45
50
  await this.send({
46
51
  title: "Test Notification",
47
- message: "This is a test from CamStack Advanced Notifier",
48
- severity: "info",
49
- category: "test",
50
- timestamp: Date.now()
52
+ body: "This is a test from CamStack Advanced Notifier",
53
+ format: "text",
54
+ priority: 3
51
55
  });
52
56
  return { success: true };
53
57
  } catch (err) {
@@ -60,6 +64,12 @@ var WebhookOutput = class {
60
64
  };
61
65
  //#endregion
62
66
  //#region src/outputs/home-assistant.ts
67
+ /**
68
+ * @deprecated Superseded by the `homeassistant` kind in
69
+ * `addon-provider-homeassistant` (the `notification-output` cap collection
70
+ * provider, reusing the HA broker connection). Kept only for compile-compat;
71
+ * not registered as a live output.
72
+ */
63
73
  var HomeAssistantOutput = class {
64
74
  id = "home-assistant";
65
75
  name = "Home Assistant";
@@ -79,11 +89,10 @@ var HomeAssistantOutput = class {
79
89
  },
80
90
  body: JSON.stringify({
81
91
  title: notification.title,
82
- message: notification.message,
83
- severity: notification.severity,
84
- category: notification.category,
92
+ message: notification.body,
93
+ priority: notification.priority,
85
94
  device_id: notification.deviceId,
86
- data: notification.data
95
+ data: notification.metadata
87
96
  })
88
97
  });
89
98
  }
@@ -91,10 +100,9 @@ var HomeAssistantOutput = class {
91
100
  try {
92
101
  await this.send({
93
102
  title: "CamStack Test",
94
- message: "Connection verified",
95
- severity: "info",
96
- category: "test",
97
- timestamp: Date.now()
103
+ body: "Connection verified",
104
+ format: "text",
105
+ priority: 3
98
106
  });
99
107
  return { success: true };
100
108
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-advanced-notifier",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "Rules-based notification engine for CamStack",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",