@camstack/types 1.2.31 → 1.2.32

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/index.js CHANGED
@@ -4505,8 +4505,11 @@ function prepareNotification(caps, n) {
4505
4505
  });
4506
4506
  }
4507
4507
  const inActions = n.actions ?? [];
4508
- const actions = inActions.slice(0, Math.max(0, caps.actions));
4509
- if (inActions.length > actions.length) dropped.push("actions");
4508
+ const kept = inActions.slice(0, Math.max(0, caps.actions));
4509
+ if (inActions.length > kept.length) dropped.push("actions");
4510
+ const iconsSupported = caps.actionIcons === true;
4511
+ if (!iconsSupported && kept.some((a) => a.icon !== void 0)) dropped.push("actionIcons");
4512
+ const actions = iconsSupported ? kept : kept.map(({ icon: _icon, ...rest }) => rest);
4510
4513
  let clickUrl = null;
4511
4514
  if (n.clickUrl !== void 0) if (caps.clickUrl) clickUrl = n.clickUrl;
4512
4515
  else dropped.push("clickUrl");
@@ -4717,6 +4720,294 @@ var MaskGridDimsSchema = zod.z.object({
4717
4720
  height: zod.z.number()
4718
4721
  });
4719
4722
  //#endregion
4723
+ //#region src/capabilities/notification-output.cap.ts
4724
+ /**
4725
+ * notification-output — canonical, capability-gated notification delivery.
4726
+ *
4727
+ * Apprise-derived model (see
4728
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
4729
+ * callers emit ONE canonical `Notification`; each provider declares a
4730
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
4731
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
4732
+ * message to what the kind supports — callers never special-case a service.
4733
+ *
4734
+ * DESIGN DECISIONS (locked):
4735
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
4736
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
4737
+ * cap. Rationale: the admin UI needs one uniform surface across the
4738
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
4739
+ * alternative would fork the UI per addon and cannot host the
4740
+ * discovery→adopt flow.
4741
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
4742
+ * the generated cap-mount auto-`concatCollection`-fans them across every
4743
+ * registered provider (notifiers addon + HA addon) so one catalog is
4744
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
4745
+ * `addonId` the generated collection router extracts from the call input.
4746
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
4747
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
4748
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
4749
+ * base64 fallback needed.
4750
+ *
4751
+ * TODO (deferred, closed-set change — separate decision): add
4752
+ * `providerKind: 'notify'` so notification providers surface on the unified
4753
+ * admin "Integrations" page.
4754
+ */
4755
+ /**
4756
+ * Zentik-derived typed-media enum — the superset across every kind. Each
4757
+ * adapter picks what it supports and the degrade engine filters the rest.
4758
+ */
4759
+ var AttachmentMediaTypeSchema = zod.z.enum([
4760
+ "image",
4761
+ "video",
4762
+ "gif",
4763
+ "audio",
4764
+ "icon"
4765
+ ]);
4766
+ /**
4767
+ * A single attachment. Exactly one of `url` (remote source, most adapters
4768
+ * prefer this) or `bytes` (inline source; required for Pushover-style
4769
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
4770
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
4771
+ */
4772
+ var AttachmentSchema = zod.z.object({
4773
+ mediaType: AttachmentMediaTypeSchema,
4774
+ url: zod.z.string().optional(),
4775
+ bytes: zod.z.instanceof(Uint8Array).optional(),
4776
+ mime: zod.z.string().optional(),
4777
+ name: zod.z.string().optional()
4778
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
4779
+ var NotificationFormatSchema = zod.z.enum([
4780
+ "text",
4781
+ "markdown",
4782
+ "html"
4783
+ ]);
4784
+ /**
4785
+ * The CLOSED icon vocabulary an action button may use.
4786
+ *
4787
+ * A closed set, not a free string, and that is the whole point: an arbitrary
4788
+ * icon name is one that ntfy renders, zentik silently drops, and nobody
4789
+ * notices — the same class of gap as a zone vocabulary nothing produced
4790
+ * ([D35](../../../docs/decisions/adr-0035.md)). Every adapter maps this set or
4791
+ * declares `actionIcons: false` and the degrade engine strips the field.
4792
+ *
4793
+ * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
4794
+ * renderer's icon set; "acknowledge" survives an adapter that draws it
4795
+ * differently.
4796
+ */
4797
+ var NotificationActionIconSchema = zod.z.enum([
4798
+ "acknowledge",
4799
+ "dismiss",
4800
+ "silence",
4801
+ "view",
4802
+ "play",
4803
+ "open",
4804
+ "close",
4805
+ "lock",
4806
+ "unlock",
4807
+ "arm",
4808
+ "disarm",
4809
+ "light",
4810
+ "alert"
4811
+ ]);
4812
+ /** A single tap-through action button. */
4813
+ var NotificationActionSchema = zod.z.object({
4814
+ id: zod.z.string(),
4815
+ label: zod.z.string(),
4816
+ url: zod.z.string().optional(),
4817
+ /** Dropped by the degrade engine for a kind with `caps.actionIcons: false`. */
4818
+ icon: NotificationActionIconSchema.optional(),
4819
+ /**
4820
+ * Renders in a warning style where the notifier supports it.
4821
+ *
4822
+ * A HINT, never a gate. The callback's authority is its token and nothing
4823
+ * else — see `notification-center/action-token.ts` for what that does and
4824
+ * does not buy.
4825
+ */
4826
+ destructive: zod.z.boolean().optional()
4827
+ });
4828
+ /**
4829
+ * The canonical notification. `body` is the only hard field (Apprise model).
4830
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
4831
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
4832
+ * the adapter maps this ordinal onto its native level. `level?` is an
4833
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
4834
+ * `priority` for that one target.
4835
+ */
4836
+ var NotificationSchema = zod.z.object({
4837
+ body: zod.z.string(),
4838
+ title: zod.z.string().optional(),
4839
+ format: NotificationFormatSchema.default("text"),
4840
+ priority: zod.z.number().int().min(1).max(5).default(3),
4841
+ level: zod.z.string().optional(),
4842
+ attachments: zod.z.array(AttachmentSchema).optional(),
4843
+ clickUrl: zod.z.string().optional(),
4844
+ actions: zod.z.array(NotificationActionSchema).optional(),
4845
+ sound: zod.z.string().optional(),
4846
+ ttl: zod.z.number().optional(),
4847
+ tag: zod.z.string().optional(),
4848
+ deviceId: zod.z.number().optional(),
4849
+ eventId: zod.z.string().optional(),
4850
+ metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
4851
+ });
4852
+ /** One declared native severity/priority level for a kind. */
4853
+ var TargetKindLevelSchema = zod.z.object({
4854
+ id: zod.z.string(),
4855
+ label: zod.z.string(),
4856
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
4857
+ ordinal: zod.z.number().int().min(1).max(5).nullable(),
4858
+ flags: zod.z.object({
4859
+ critical: zod.z.boolean().optional(),
4860
+ silent: zod.z.boolean().optional(),
4861
+ noPush: zod.z.boolean().optional()
4862
+ }).optional(),
4863
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
4864
+ requires: zod.z.array(zod.z.string()).optional(),
4865
+ description: zod.z.string().optional()
4866
+ });
4867
+ /** Attachment capabilities for a kind (drives the degrade engine + test panel). */
4868
+ var TargetKindAttachmentsCapsSchema = zod.z.object({
4869
+ mediaTypes: zod.z.array(AttachmentMediaTypeSchema),
4870
+ mode: zod.z.enum([
4871
+ "url",
4872
+ "bytes",
4873
+ "both"
4874
+ ]),
4875
+ max: zod.z.number().int().nonnegative(),
4876
+ maxBytes: zod.z.number().int().positive().optional()
4877
+ });
4878
+ /** The full capability block consulted before dispatch. */
4879
+ var TargetKindCapsSchema = zod.z.object({
4880
+ attachments: TargetKindAttachmentsCapsSchema,
4881
+ /** Max action buttons (0 = none). */
4882
+ actions: zod.z.number().int().nonnegative(),
4883
+ /**
4884
+ * Whether this kind renders a per-action ICON.
4885
+ *
4886
+ * `.optional()`, deliberately NOT `.default(false)`: a Zod default does not
4887
+ * run on the addon cap path — three production failures in one day taught
4888
+ * this repo that once. Absent is read as false by the degrade engine, which
4889
+ * is the safe direction: an icon that is not rendered costs nothing, an icon
4890
+ * assumed and dropped costs the operator's trust in the field.
4891
+ */
4892
+ actionIcons: zod.z.boolean().optional(),
4893
+ levels: zod.z.array(TargetKindLevelSchema),
4894
+ format: zod.z.array(NotificationFormatSchema),
4895
+ clickUrl: zod.z.boolean(),
4896
+ sound: zod.z.boolean(),
4897
+ ttl: zod.z.boolean(),
4898
+ bodyMaxLen: zod.z.number().int().positive()
4899
+ });
4900
+ /**
4901
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
4902
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
4903
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
4904
+ * the union is large and not meant for runtime validation here; the exported
4905
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
4906
+ */
4907
+ var ConfigSchemaPassthrough$1 = zod.z.unknown();
4908
+ var TargetKindSchema = zod.z.object({
4909
+ kind: zod.z.string(),
4910
+ label: zod.z.string(),
4911
+ icon: zod.z.string(),
4912
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
4913
+ addonId: zod.z.string(),
4914
+ /**
4915
+ * URL of the kind's bundled BRAND icon, served by the providing addon over
4916
+ * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
4917
+ * when the addon bundles no icon for that kind — the client then falls back
4918
+ * to a neutral glyph rather than rendering the raw `icon` NAME as text.
4919
+ *
4920
+ * Root-relative on purpose: it resolves against whatever origin serves a web
4921
+ * client, and a native client joins it onto its own hub base.
4922
+ *
4923
+ * DECLARED here deliberately. It used to travel as an undeclared passthrough
4924
+ * field that survived only because the runtime cap-router forwards provider
4925
+ * output verbatim — so every consumer had to re-declare it by hand to stop
4926
+ * its own Zod parse from stripping it, and the whole arrangement would have
4927
+ * broken silently the moment output validation was tightened anywhere.
4928
+ */
4929
+ iconUrl: zod.z.string().optional(),
4930
+ /**
4931
+ * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
4932
+ *
4933
+ * The server knows this and therefore says it, because the client cannot
4934
+ * safely guess: a React-Native client renders SVG and raster through two
4935
+ * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
4936
+ * not decode SVG on iOS/Android), so without this it silently fell back to a
4937
+ * placeholder glyph for every vector icon while the web build looked fine.
4938
+ *
4939
+ * Absent when {@link iconUrl} is absent, or for a legacy provider that has
4940
+ * not been updated — a client that cannot determine the type should prefer
4941
+ * its raster path, which is the safe default for an unknown image.
4942
+ */
4943
+ iconMediaType: zod.z.string().optional(),
4944
+ configSchema: ConfigSchemaPassthrough$1,
4945
+ supportsDiscovery: zod.z.boolean(),
4946
+ caps: TargetKindCapsSchema
4947
+ });
4948
+ /**
4949
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
4950
+ * (return a presence marker only) when serving `listTargets` — never
4951
+ * round-trip a stored secret to the UI.
4952
+ */
4953
+ var TargetSchema = zod.z.object({
4954
+ id: zod.z.string(),
4955
+ name: zod.z.string(),
4956
+ kind: zod.z.string(),
4957
+ addonId: zod.z.string(),
4958
+ enabled: zod.z.boolean(),
4959
+ config: zod.z.record(zod.z.string(), zod.z.unknown())
4960
+ });
4961
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
4962
+ var DiscoveredTargetSchema = zod.z.object({
4963
+ kind: zod.z.string(),
4964
+ suggestedName: zod.z.string(),
4965
+ config: zod.z.record(zod.z.string(), zod.z.unknown())
4966
+ });
4967
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
4968
+ var RenderedAsSchema = zod.z.object({
4969
+ level: zod.z.string(),
4970
+ format: NotificationFormatSchema,
4971
+ attachmentsSent: zod.z.number().int().nonnegative(),
4972
+ actionsSent: zod.z.number().int().nonnegative(),
4973
+ truncated: zod.z.boolean(),
4974
+ dropped: zod.z.array(zod.z.string())
4975
+ });
4976
+ var SendResultSchema = zod.z.object({
4977
+ success: zod.z.boolean(),
4978
+ error: zod.z.string().optional(),
4979
+ renderedAs: RenderedAsSchema.optional()
4980
+ });
4981
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
4982
+ var TestResultSchema = SendResultSchema;
4983
+ var notificationOutputCapability = {
4984
+ name: "notification-output",
4985
+ scope: "system",
4986
+ mode: "collection",
4987
+ methods: {
4988
+ listTargetKinds: require_sleep.method(zod.z.object({}), zod.z.array(TargetKindSchema)),
4989
+ listTargets: require_sleep.method(zod.z.object({}), zod.z.array(TargetSchema)),
4990
+ discoverTargets: require_sleep.method(zod.z.object({
4991
+ kind: zod.z.string(),
4992
+ config: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
4993
+ }), zod.z.array(DiscoveredTargetSchema)),
4994
+ send: require_sleep.method(zod.z.object({
4995
+ targetId: zod.z.string(),
4996
+ notification: NotificationSchema
4997
+ }), SendResultSchema, { kind: "mutation" }),
4998
+ testTarget: require_sleep.method(zod.z.object({
4999
+ targetId: zod.z.string(),
5000
+ sample: NotificationSchema.optional()
5001
+ }), TestResultSchema, { kind: "mutation" }),
5002
+ upsertTarget: require_sleep.method(zod.z.object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
5003
+ deleteTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
5004
+ setTargetEnabled: require_sleep.method(zod.z.object({
5005
+ targetId: zod.z.string(),
5006
+ enabled: zod.z.boolean()
5007
+ }), zod.z.void(), { kind: "mutation" })
5008
+ }
5009
+ };
5010
+ //#endregion
4720
5011
  //#region src/capabilities/notification-rules.cap.ts
4721
5012
  /**
4722
5013
  * notification-rules — the Notification Center rule surface (P1 core).
@@ -4909,6 +5200,28 @@ var NcRuleActionSequenceSchema = zod.z.object({
4909
5200
  actions: zod.z.array(NcRuleActionSchema).min(1)
4910
5201
  });
4911
5202
  /**
5203
+ * One button carried by the notification, running a named sequence on tap.
5204
+ *
5205
+ * **Read this before adding a button that does something physical.** The tap
5206
+ * arrives over a link that travelled through third-party infrastructure — ntfy,
5207
+ * a push relay, whatever forwarded the message — and the callback's ONLY
5208
+ * authority is the token in that link: single-use, short-lived, bound to this
5209
+ * one action of this one notification. It does not identify who tapped.
5210
+ * Whoever holds the notification can run the button, once, inside the window.
5211
+ * That is the operator's explicit choice (2026-08-05), and `destructive` is a
5212
+ * rendering hint, not a second gate. [D47](decisions/adr-0047.md).
5213
+ */
5214
+ var NcRuleNotificationButtonSchema = zod.z.object({
5215
+ /** Stable id — travels in the callback and identifies the button in logs. */
5216
+ id: zod.z.string().min(1).max(64),
5217
+ label: zod.z.string().min(1).max(40),
5218
+ /** Name of a sequence in `onTrigger`. The dispatcher drops a button whose
5219
+ * sequence does not exist rather than minting a token for nothing. */
5220
+ sequence: zod.z.string().min(1).max(120),
5221
+ icon: NotificationActionIconSchema.optional(),
5222
+ destructive: zod.z.boolean().optional()
5223
+ });
5224
+ /**
4912
5225
  * Sequences a rule runs, by hook point.
4913
5226
  *
4914
5227
  * ONLY `onTrigger` is here, deliberately. The reference also has activation /
@@ -4916,9 +5229,26 @@ var NcRuleActionSequenceSchema = zod.z.object({
4916
5229
  * repo's expensive failure mode is declaring a surface nothing produces, so a
4917
5230
  * hook appears here in the same change that produces its edge, never before.
4918
5231
  */
4919
- var NcRuleActionsSchema = zod.z.object({
4920
- /** Runs when the rule MATCHES. */
4921
- onTrigger: zod.z.array(NcRuleActionSequenceSchema).optional() });
5232
+ var NcRuleActionsSchema = zod.z.object({
5233
+ /** Runs when the rule MATCHES. */
5234
+ onTrigger: zod.z.array(NcRuleActionSequenceSchema).optional(),
5235
+ /**
5236
+ * Buttons the NOTIFICATION carries, each running one of this rule's
5237
+ * sequences when tapped.
5238
+ *
5239
+ * Deliberately a REFERENCE to a sequence rather than a second place to
5240
+ * author steps. A button that could define its own actions would be a
5241
+ * parallel actuation vocabulary — the executor's device-scope check, the
5242
+ * stop-at-first-failure rule and the per-sequence throttle all live on
5243
+ * sequences, and a second authoring surface would drift from every one of
5244
+ * them.
5245
+ *
5246
+ * A sequence reachable ONLY by a button simply appears in `onTrigger` with
5247
+ * `enabled: false`: it is then authored, throttled and validated like the
5248
+ * rest, and nothing runs it automatically.
5249
+ */
5250
+ buttons: zod.z.array(NcRuleNotificationButtonSchema).max(8).optional()
5251
+ });
4922
5252
  /**
4923
5253
  * "This rule applies only while `deviceId` is in one of `states`."
4924
5254
  *
@@ -19845,14 +20175,14 @@ var LlmProfileSchema = zod.z.object({
19845
20175
  /** ConfigUISchema tree passed through untyped on the wire (the
19846
20176
  * notification-output `ConfigSchemaPassthrough` precedent at
19847
20177
  * notification-output.cap.ts:151); the exported TS type re-tightens it. */
19848
- var ConfigSchemaPassthrough$1 = zod.z.unknown();
20178
+ var ConfigSchemaPassthrough = zod.z.unknown();
19849
20179
  var LlmProfileKindDescriptorSchema = zod.z.object({
19850
20180
  kind: LlmProfileKindSchema,
19851
20181
  label: zod.z.string(),
19852
20182
  icon: zod.z.string(),
19853
20183
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
19854
20184
  addonId: zod.z.string(),
19855
- configSchema: ConfigSchemaPassthrough$1
20185
+ configSchema: ConfigSchemaPassthrough
19856
20186
  });
19857
20187
  var LlmDefaultSelectorSchema = zod.z.union([zod.z.object({ consumer: zod.z.string() }), zod.z.object({ purpose: zod.z.enum(["text", "vision"]) })]);
19858
20188
  var LlmDefaultSchema = zod.z.object({
@@ -20584,246 +20914,6 @@ var networkAccessCapability = {
20584
20914
  }
20585
20915
  };
20586
20916
  //#endregion
20587
- //#region src/capabilities/notification-output.cap.ts
20588
- /**
20589
- * notification-output — canonical, capability-gated notification delivery.
20590
- *
20591
- * Apprise-derived model (see
20592
- * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
20593
- * callers emit ONE canonical `Notification`; each provider declares a
20594
- * per-kind capability descriptor (`TargetKind`), and the pure degrade
20595
- * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
20596
- * message to what the kind supports — callers never special-case a service.
20597
- *
20598
- * DESIGN DECISIONS (locked):
20599
- * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
20600
- * `setTargetEnabled`), each provider persisting via the `settings-store`
20601
- * cap. Rationale: the admin UI needs one uniform surface across the
20602
- * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
20603
- * alternative would fork the UI per addon and cannot host the
20604
- * discovery→adopt flow.
20605
- * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
20606
- * the generated cap-mount auto-`concatCollection`-fans them across every
20607
- * registered provider (notifiers addon + HA addon) so one catalog is
20608
- * routable. `send` / `testTarget` / CRUD route to ONE provider by the
20609
- * `addonId` the generated collection router extracts from the call input.
20610
- * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
20611
- * transformer) + UDS MsgPack both round-trip typed arrays — already used by
20612
- * `storage` / `storage-provider` / `recording` caps over the same path. No
20613
- * base64 fallback needed.
20614
- *
20615
- * TODO (deferred, closed-set change — separate decision): add
20616
- * `providerKind: 'notify'` so notification providers surface on the unified
20617
- * admin "Integrations" page.
20618
- */
20619
- /**
20620
- * Zentik-derived typed-media enum — the superset across every kind. Each
20621
- * adapter picks what it supports and the degrade engine filters the rest.
20622
- */
20623
- var AttachmentMediaTypeSchema = zod.z.enum([
20624
- "image",
20625
- "video",
20626
- "gif",
20627
- "audio",
20628
- "icon"
20629
- ]);
20630
- /**
20631
- * A single attachment. Exactly one of `url` (remote source, most adapters
20632
- * prefer this) or `bytes` (inline source; required for Pushover-style
20633
- * bytes-only kinds) MUST be present — the degrade engine expresses a
20634
- * url→bytes fetch as a `needsFetch` directive the adapter executes.
20635
- */
20636
- var AttachmentSchema = zod.z.object({
20637
- mediaType: AttachmentMediaTypeSchema,
20638
- url: zod.z.string().optional(),
20639
- bytes: zod.z.instanceof(Uint8Array).optional(),
20640
- mime: zod.z.string().optional(),
20641
- name: zod.z.string().optional()
20642
- }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
20643
- var NotificationFormatSchema = zod.z.enum([
20644
- "text",
20645
- "markdown",
20646
- "html"
20647
- ]);
20648
- /** A single tap-through action button. */
20649
- var NotificationActionSchema = zod.z.object({
20650
- id: zod.z.string(),
20651
- label: zod.z.string(),
20652
- url: zod.z.string().optional()
20653
- });
20654
- /**
20655
- * The canonical notification. `body` is the only hard field (Apprise model).
20656
- * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
20657
- * NOT a fixed severity enum — each kind declares its own `caps.levels` and
20658
- * the adapter maps this ordinal onto its native level. `level?` is an
20659
- * optional kind-native level id (`emergency`, `silent`, …) that overrides
20660
- * `priority` for that one target.
20661
- */
20662
- var NotificationSchema = zod.z.object({
20663
- body: zod.z.string(),
20664
- title: zod.z.string().optional(),
20665
- format: NotificationFormatSchema.default("text"),
20666
- priority: zod.z.number().int().min(1).max(5).default(3),
20667
- level: zod.z.string().optional(),
20668
- attachments: zod.z.array(AttachmentSchema).optional(),
20669
- clickUrl: zod.z.string().optional(),
20670
- actions: zod.z.array(NotificationActionSchema).optional(),
20671
- sound: zod.z.string().optional(),
20672
- ttl: zod.z.number().optional(),
20673
- tag: zod.z.string().optional(),
20674
- deviceId: zod.z.number().optional(),
20675
- eventId: zod.z.string().optional(),
20676
- metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
20677
- });
20678
- /** One declared native severity/priority level for a kind. */
20679
- var TargetKindLevelSchema = zod.z.object({
20680
- id: zod.z.string(),
20681
- label: zod.z.string(),
20682
- /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
20683
- ordinal: zod.z.number().int().min(1).max(5).nullable(),
20684
- flags: zod.z.object({
20685
- critical: zod.z.boolean().optional(),
20686
- silent: zod.z.boolean().optional(),
20687
- noPush: zod.z.boolean().optional()
20688
- }).optional(),
20689
- /** e.g. Pushover `emergency` requires `retry` / `expire`. */
20690
- requires: zod.z.array(zod.z.string()).optional(),
20691
- description: zod.z.string().optional()
20692
- });
20693
- /** Attachment capabilities for a kind (drives the degrade engine + test panel). */
20694
- var TargetKindAttachmentsCapsSchema = zod.z.object({
20695
- mediaTypes: zod.z.array(AttachmentMediaTypeSchema),
20696
- mode: zod.z.enum([
20697
- "url",
20698
- "bytes",
20699
- "both"
20700
- ]),
20701
- max: zod.z.number().int().nonnegative(),
20702
- maxBytes: zod.z.number().int().positive().optional()
20703
- });
20704
- /** The full capability block consulted before dispatch. */
20705
- var TargetKindCapsSchema = zod.z.object({
20706
- attachments: TargetKindAttachmentsCapsSchema,
20707
- /** Max action buttons (0 = none). */
20708
- actions: zod.z.number().int().nonnegative(),
20709
- levels: zod.z.array(TargetKindLevelSchema),
20710
- format: zod.z.array(NotificationFormatSchema),
20711
- clickUrl: zod.z.boolean(),
20712
- sound: zod.z.boolean(),
20713
- ttl: zod.z.boolean(),
20714
- bodyMaxLen: zod.z.number().int().positive()
20715
- });
20716
- /**
20717
- * `configSchema` is a `ConfigUISchema` tree passed through to the admin
20718
- * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
20719
- * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
20720
- * the union is large and not meant for runtime validation here; the exported
20721
- * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
20722
- */
20723
- var ConfigSchemaPassthrough = zod.z.unknown();
20724
- var TargetKindSchema = zod.z.object({
20725
- kind: zod.z.string(),
20726
- label: zod.z.string(),
20727
- icon: zod.z.string(),
20728
- /** Stamped by each provider so the concat-fanned catalog stays routable. */
20729
- addonId: zod.z.string(),
20730
- /**
20731
- * URL of the kind's bundled BRAND icon, served by the providing addon over
20732
- * its own `addon-routes` surface (`/addon/<addonId>/icons/<kind>`). Absent
20733
- * when the addon bundles no icon for that kind — the client then falls back
20734
- * to a neutral glyph rather than rendering the raw `icon` NAME as text.
20735
- *
20736
- * Root-relative on purpose: it resolves against whatever origin serves a web
20737
- * client, and a native client joins it onto its own hub base.
20738
- *
20739
- * DECLARED here deliberately. It used to travel as an undeclared passthrough
20740
- * field that survived only because the runtime cap-router forwards provider
20741
- * output verbatim — so every consumer had to re-declare it by hand to stop
20742
- * its own Zod parse from stripping it, and the whole arrangement would have
20743
- * broken silently the moment output validation was tightened anywhere.
20744
- */
20745
- iconUrl: zod.z.string().optional(),
20746
- /**
20747
- * Media type of {@link iconUrl} (`image/svg+xml`, `image/png`, …).
20748
- *
20749
- * The server knows this and therefore says it, because the client cannot
20750
- * safely guess: a React-Native client renders SVG and raster through two
20751
- * DIFFERENT components (`react-native-svg` vs `expo-image` — expo-image does
20752
- * not decode SVG on iOS/Android), so without this it silently fell back to a
20753
- * placeholder glyph for every vector icon while the web build looked fine.
20754
- *
20755
- * Absent when {@link iconUrl} is absent, or for a legacy provider that has
20756
- * not been updated — a client that cannot determine the type should prefer
20757
- * its raster path, which is the safe default for an unknown image.
20758
- */
20759
- iconMediaType: zod.z.string().optional(),
20760
- configSchema: ConfigSchemaPassthrough,
20761
- supportsDiscovery: zod.z.boolean(),
20762
- caps: TargetKindCapsSchema
20763
- });
20764
- /**
20765
- * A persisted target. `config` holds secrets; providers REDACT secret fields
20766
- * (return a presence marker only) when serving `listTargets` — never
20767
- * round-trip a stored secret to the UI.
20768
- */
20769
- var TargetSchema = zod.z.object({
20770
- id: zod.z.string(),
20771
- name: zod.z.string(),
20772
- kind: zod.z.string(),
20773
- addonId: zod.z.string(),
20774
- enabled: zod.z.boolean(),
20775
- config: zod.z.record(zod.z.string(), zod.z.unknown())
20776
- });
20777
- /** A discovery-surfaced candidate (config is partial + non-secret). */
20778
- var DiscoveredTargetSchema = zod.z.object({
20779
- kind: zod.z.string(),
20780
- suggestedName: zod.z.string(),
20781
- config: zod.z.record(zod.z.string(), zod.z.unknown())
20782
- });
20783
- /** The degrade engine's report — what was resolved / dropped / degraded. */
20784
- var RenderedAsSchema = zod.z.object({
20785
- level: zod.z.string(),
20786
- format: NotificationFormatSchema,
20787
- attachmentsSent: zod.z.number().int().nonnegative(),
20788
- actionsSent: zod.z.number().int().nonnegative(),
20789
- truncated: zod.z.boolean(),
20790
- dropped: zod.z.array(zod.z.string())
20791
- });
20792
- var SendResultSchema = zod.z.object({
20793
- success: zod.z.boolean(),
20794
- error: zod.z.string().optional(),
20795
- renderedAs: RenderedAsSchema.optional()
20796
- });
20797
- /** Same shape as SendResult — kept as a distinct name for the test panel. */
20798
- var TestResultSchema = SendResultSchema;
20799
- var notificationOutputCapability = {
20800
- name: "notification-output",
20801
- scope: "system",
20802
- mode: "collection",
20803
- methods: {
20804
- listTargetKinds: require_sleep.method(zod.z.object({}), zod.z.array(TargetKindSchema)),
20805
- listTargets: require_sleep.method(zod.z.object({}), zod.z.array(TargetSchema)),
20806
- discoverTargets: require_sleep.method(zod.z.object({
20807
- kind: zod.z.string(),
20808
- config: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
20809
- }), zod.z.array(DiscoveredTargetSchema)),
20810
- send: require_sleep.method(zod.z.object({
20811
- targetId: zod.z.string(),
20812
- notification: NotificationSchema
20813
- }), SendResultSchema, { kind: "mutation" }),
20814
- testTarget: require_sleep.method(zod.z.object({
20815
- targetId: zod.z.string(),
20816
- sample: NotificationSchema.optional()
20817
- }), TestResultSchema, { kind: "mutation" }),
20818
- upsertTarget: require_sleep.method(zod.z.object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
20819
- deleteTarget: require_sleep.method(zod.z.object({ targetId: zod.z.string() }), zod.z.void(), { kind: "mutation" }),
20820
- setTargetEnabled: require_sleep.method(zod.z.object({
20821
- targetId: zod.z.string(),
20822
- enabled: zod.z.boolean()
20823
- }), zod.z.void(), { kind: "mutation" })
20824
- }
20825
- };
20826
- //#endregion
20827
20917
  //#region src/capabilities/core-blocks.cap.ts
20828
20918
  /**
20829
20919
  * core-blocks — user-authored TypeScript, stored in the kernel and executed in
@@ -34980,6 +35070,7 @@ exports.NcRuleActionSchema = NcRuleActionSchema;
34980
35070
  exports.NcRuleActionSequenceSchema = NcRuleActionSequenceSchema;
34981
35071
  exports.NcRuleActionsSchema = NcRuleActionsSchema;
34982
35072
  exports.NcRuleInputSchema = NcRuleInputSchema;
35073
+ exports.NcRuleNotificationButtonSchema = NcRuleNotificationButtonSchema;
34983
35074
  exports.NcRulePatchSchema = NcRulePatchSchema;
34984
35075
  exports.NcRuleSchema = NcRuleSchema;
34985
35076
  exports.NcRuleTargetSchema = NcRuleTargetSchema;
@@ -34998,6 +35089,7 @@ exports.NcZoneConditionSchema = NcZoneConditionSchema;
34998
35089
  exports.NetworkAccessStatusSchema = NetworkAccessStatusSchema;
34999
35090
  exports.NetworkAddressSchema = NetworkAddressSchema;
35000
35091
  exports.NetworkEndpointSchema = NetworkEndpointSchema;
35092
+ exports.NotificationActionIconSchema = NotificationActionIconSchema;
35001
35093
  exports.NotificationActionSchema = NotificationActionSchema;
35002
35094
  exports.NotificationFormatSchema = NotificationFormatSchema;
35003
35095
  exports.NotificationSchema = NotificationSchema;