@camstack/types 1.2.88 → 1.2.90

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.
@@ -60,6 +60,7 @@ export type RecordingDeviceUsage = z.infer<typeof RecordingDeviceUsageSchema>;
60
60
  export declare const RecordingLocationUsageSchema: z.ZodObject<{
61
61
  locationId: z.ZodNullable<z.ZodString>;
62
62
  locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
63
+ root: z.ZodOptional<z.ZodString>;
63
64
  usedBytes: z.ZodNumber;
64
65
  availableBytes: z.ZodNullable<z.ZodNumber>;
65
66
  totalBytes: z.ZodNullable<z.ZodNumber>;
@@ -76,13 +77,14 @@ export declare const RecordingStorageUsageSchema: z.ZodObject<{
76
77
  usedBytes: z.ZodNumber;
77
78
  oldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
78
79
  }, z.core.$strip>>;
79
- locations: z.ZodArray<z.ZodObject<{
80
+ locations: z.ZodOptional<z.ZodArray<z.ZodObject<{
80
81
  locationId: z.ZodNullable<z.ZodString>;
81
82
  locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
83
+ root: z.ZodOptional<z.ZodString>;
82
84
  usedBytes: z.ZodNumber;
83
85
  availableBytes: z.ZodNullable<z.ZodNumber>;
84
86
  totalBytes: z.ZodNullable<z.ZodNumber>;
85
- }, z.core.$strip>>;
87
+ }, z.core.$strip>>>;
86
88
  }, z.core.$strip>;
87
89
  export type RecordingStorageUsage = z.infer<typeof RecordingStorageUsageSchema>;
88
90
  /**
@@ -263,13 +265,14 @@ export declare const recordingCapability: {
263
265
  usedBytes: z.ZodNumber;
264
266
  oldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
265
267
  }, z.core.$strip>>;
266
- locations: z.ZodArray<z.ZodObject<{
268
+ locations: z.ZodOptional<z.ZodArray<z.ZodObject<{
267
269
  locationId: z.ZodNullable<z.ZodString>;
268
270
  locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
271
+ root: z.ZodOptional<z.ZodString>;
269
272
  usedBytes: z.ZodNumber;
270
273
  availableBytes: z.ZodNullable<z.ZodNumber>;
271
274
  totalBytes: z.ZodNullable<z.ZodNumber>;
272
- }, z.core.$strip>>;
275
+ }, z.core.$strip>>>;
273
276
  }, z.core.$strip>, "query">;
274
277
  readonly getDeviceConfig: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
275
278
  deviceId: z.ZodNumber;
package/dist/index.d.ts CHANGED
@@ -194,6 +194,7 @@ export { audioIsFailClosed, audioLabelChoices, audioModeOf, audioOrDefaults, isA
194
194
  export { isBaseConditionKey, knownValues, NC_BASE_CONDITION_KEYS, type NcBaseConditionKey, pickerForCondition, type TaxonomyGroup, type TaxonomyOption, type TaxonomyPicker, } from './notification/condition-taxonomy.js';
195
195
  export { type PreparedAction, type PreparedAttachment, type PreparedNotification, prepareNotification, type ResolvedLevel, } from './notification/degrade-engine.js';
196
196
  export { htmlToText, markdownToHtmlLite, markdownToText, type NotificationFormat as NotificationBodyFormat, resolveFormat, textToHtml, transcodeBody, } from './notification/format-transcode.js';
197
+ export { conditionExclusionReason, conditionVisibleForKind, defaultDeliveryForSection, droppedConditionsForKind, isAudioRule, isOccupancyRule, isSystemDelivery, NC_AUDIO_SEED, NC_OCCUPANCY_DEFAULTS, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_RULE_SECTIONS, NC_SYSTEM_DELIVERY, type NcDroppedCondition, type NcKindVisibilitySubject, type NcRuleEditorSection, type NcRuleEditorSectionKey, type NcRuleKind, type NcRuleKindSpec, type NcRuleOwningSectionKey, type NcRuleSection, type NcRuleSectionKey, type NcRuleSectionSubject, type NcRuleSeed, parseRuleSection, ruleEditorSectionsForKind, ruleKindOf, ruleKindSpec, ruleMatchesSection, ruleSection, ruleSectionOf, ruleSeedForSection, } from './notification/rule-kinds.js';
197
198
  export { isScheduleActive } from './notification/schedule.js';
198
199
  export { NC_SYSTEM_EVENT_FILTER_KEYS, type NcSystemEventFilterKey, systemEventFilterApplies, systemEventFilterAppliesToAnyKind, } from './notification/system-event-filters.js';
199
200
  export type { TimelapseCadencePair, TimelapsePreviewMode, TimelapseRule, TimelapseRuleInput, TimelapseRulePatch, TimelapseTemplate, } from './notification/timelapse-rule.js';
package/dist/index.js CHANGED
@@ -27445,9 +27445,21 @@ var RecordingLocationUsageSchema = zod.z.object({
27445
27445
  * previously rendered as two identical "disks" with a nonsensical used
27446
27446
  * split — hydrate attribution across aliases is arbitrary by nature. */
27447
27447
  locationIds: zod.z.array(zod.z.string()).optional(),
27448
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
27448
+ /**
27449
+ * Filesystem root of the physical volume this row accounts — the only
27450
+ * operator-legible identity when a location has no `displayName` yet, and
27451
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
27452
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
27453
+ * it, and this schema must keep validating that older provider's payload.
27454
+ */
27455
+ root: zod.z.string().optional(),
27456
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
27457
+ * the footage's SHARE of the volume, not how full the volume is — the disk
27458
+ * also holds everything that is not recordings. Volume fill is
27459
+ * `(totalBytes - availableBytes) / totalBytes`. */
27449
27460
  usedBytes: zod.z.number(),
27450
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
27461
+ /** Free bytes on the location's volume; null when capacity is unknown
27462
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
27451
27463
  availableBytes: zod.z.number().nullable(),
27452
27464
  /** Total bytes of the location's volume; null when unknown. */
27453
27465
  totalBytes: zod.z.number().nullable()
@@ -27459,7 +27471,15 @@ var RecordingStorageUsageSchema = zod.z.object({
27459
27471
  nodeId: zod.z.string(),
27460
27472
  totalUsedBytes: zod.z.number(),
27461
27473
  devices: zod.z.array(RecordingDeviceUsageSchema),
27462
- locations: zod.z.array(RecordingLocationUsageSchema)
27474
+ /**
27475
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
27476
+ * of the payload without it: the hub serves this method through a BAKED
27477
+ * router, so a hub whose framework train predates a change to this array
27478
+ * strips it out of an answer a newer recorder happily produced. A UI that
27479
+ * dereferences it unconditionally crashes the whole Storage page over a
27480
+ * missing disk bar.
27481
+ */
27482
+ locations: zod.z.array(RecordingLocationUsageSchema).optional()
27463
27483
  });
27464
27484
  /**
27465
27485
  * The OPERATOR-ARMED half of multi-location recordings (D116).
@@ -43560,6 +43580,340 @@ function prepareNotification(caps, n) {
43560
43580
  };
43561
43581
  }
43562
43582
  //#endregion
43583
+ //#region src/notification/rule-kinds.ts
43584
+ /**
43585
+ * Reasons, written once. Each names the SUBJECT FIELD the engine reads and
43586
+ * finds absent — that is what makes the exclusion checkable against the engine
43587
+ * rather than an opinion about tidiness.
43588
+ */
43589
+ var NO_SENSOR_KIND = "An occupancy edge is not a sensor event: it carries no sensor kind, so this condition can never match one.";
43590
+ var NO_EVENT_TOKEN = "An occupancy edge carries no device event-type token — that comes from an event-emitter slice, which a count crossing has none of.";
43591
+ var OCCUPANCY_SOURCE = "An occupancy edge is always produced by the pipeline, so any other source can never match and “pipeline” narrows nothing.";
43592
+ var AUDIO_SOURCE = "A confirmed sound window is always stamped as an audio subject, so any other source can never match.";
43593
+ var SOUND_NOT_A_PICTURE = "A sound rule fires on what the camera HEARD. Its subject carries no detection, so this condition can never match one — name the sounds in the Sound condition instead.";
43594
+ var SOUND_NO_CONFIDENCE = "A sound window’s evidence is a percentage of samples, not a detector score, so a confidence threshold can never be satisfied.";
43595
+ var SOUND_NO_GEOMETRY = "A sound has no position in the frame, so a zone or a drawn polygon can never match it.";
43596
+ var NC_RULE_KIND_SPECS = [
43597
+ {
43598
+ kind: "detection",
43599
+ label: "Conditions",
43600
+ excluded: {},
43601
+ carriesMedia: true,
43602
+ carriesSubjectCrop: true
43603
+ },
43604
+ {
43605
+ kind: "sensor",
43606
+ label: "Conditions",
43607
+ /** Same argument as `detection`: `occupancy` converts, it does not confuse. */
43608
+ excluded: {},
43609
+ carriesMedia: true,
43610
+ carriesSubjectCrop: true,
43611
+ blurb: "A doorbell press or a linked sensor changing state. The picture comes from the camera the sensor is attributed to."
43612
+ },
43613
+ {
43614
+ kind: "occupancy",
43615
+ label: "Occupancy",
43616
+ discriminator: "occupancy",
43617
+ excluded: {
43618
+ sensorKinds: NO_SENSOR_KIND,
43619
+ eventTypeTokens: NO_EVENT_TOKEN,
43620
+ source: OCCUPANCY_SOURCE
43621
+ },
43622
+ carriesMedia: true,
43623
+ carriesSubjectCrop: true,
43624
+ blurb: "Fires when the count in a camera frame or a zone crosses your threshold and holds. Pick the camera first — the zone list is that camera’s."
43625
+ },
43626
+ {
43627
+ kind: "sound",
43628
+ label: "Sound",
43629
+ discriminator: "audio",
43630
+ excluded: {
43631
+ classes: SOUND_NOT_A_PICTURE,
43632
+ classesExclude: SOUND_NOT_A_PICTURE,
43633
+ minConfidence: SOUND_NO_CONFIDENCE,
43634
+ zones: SOUND_NO_GEOMETRY,
43635
+ zonesExclude: SOUND_NO_GEOMETRY,
43636
+ customZones: SOUND_NO_GEOMETRY,
43637
+ crossing: SOUND_NO_GEOMETRY,
43638
+ labelEquals: SOUND_NOT_A_PICTURE,
43639
+ identities: SOUND_NOT_A_PICTURE,
43640
+ identitiesExclude: SOUND_NOT_A_PICTURE,
43641
+ plates: SOUND_NOT_A_PICTURE,
43642
+ source: AUDIO_SOURCE
43643
+ },
43644
+ carriesMedia: true,
43645
+ carriesSubjectCrop: false,
43646
+ blurb: "Fires on a confirmed sampling window of audio, never on a picture. Name the sounds and/or keep a level threshold — with neither it can never match.",
43647
+ mediaBlurb: "A sound owns no frame, so the picture is a photograph of the camera taken at the moment of the match — the whole scene, never a crop. The GIF and the clip are cut around the same instant, from the camera."
43648
+ },
43649
+ {
43650
+ kind: "system",
43651
+ label: "Events",
43652
+ discriminator: "systemEvent",
43653
+ excluded: {},
43654
+ carriesMedia: false,
43655
+ carriesSubjectCrop: false,
43656
+ blurb: "The installation itself: a camera or stream dropping, a node leaving, an update landing. There is no detection behind it and no frame to attach."
43657
+ }
43658
+ ];
43659
+ /** The spec for a kind. Total by construction — every member has an entry. */
43660
+ function ruleKindSpec(kind) {
43661
+ return NC_RULE_KIND_SPECS.find((s) => s.kind === kind) ?? NC_RULE_KIND_SPECS[0];
43662
+ }
43663
+ /** The delivery a system rule rides — named once, compared everywhere. */
43664
+ var NC_SYSTEM_DELIVERY = "system-event";
43665
+ /**
43666
+ * Is this a SYSTEM rule — one about the installation rather than about anything
43667
+ * a camera saw? Takes a plain `string` so a rule carrying a delivery this build
43668
+ * has never heard of still gets an answer instead of a type error.
43669
+ */
43670
+ function isSystemDelivery(delivery) {
43671
+ return delivery === NC_SYSTEM_DELIVERY;
43672
+ }
43673
+ /**
43674
+ * Which kind of rule is this?
43675
+ *
43676
+ * The CONDITION wins over the delivery wherever the engine says it does, and
43677
+ * the order below is the engine's own: `evaluateRule` checks the system branch
43678
+ * first, then the occupancy and audio gates. Takes a plain `delivery` string so
43679
+ * a trigger this build does not know still classifies (as `detection`, which is
43680
+ * the read-only path's own fallback).
43681
+ */
43682
+ function ruleKindOf(rule) {
43683
+ if (isSystemDelivery(rule.delivery)) return "system";
43684
+ if (rule.conditions.occupancy !== void 0 && rule.delivery === "device-event") return "occupancy";
43685
+ if (rule.conditions.audio !== void 0 && rule.delivery === "immediate") return "sound";
43686
+ if (rule.delivery === "device-event") return "sensor";
43687
+ return "detection";
43688
+ }
43689
+ /**
43690
+ * Is this condition visible — and authorable — for a rule of this kind?
43691
+ *
43692
+ * The COMPOSITION, and the only function that decides it: `appliesTo` is the
43693
+ * server's answer about the trigger, the kind's `excluded` map is this build's
43694
+ * answer about the sub-type, and the discriminator overrides both (a sound rule
43695
+ * must be able to edit its sound even though `audio` is excluded for every
43696
+ * OTHER kind that rides `immediate`).
43697
+ */
43698
+ function conditionVisibleForKind(descriptor, delivery, kind) {
43699
+ const spec = ruleKindSpec(kind);
43700
+ if (descriptor.id === spec.discriminator) return true;
43701
+ if (!descriptor.appliesTo.includes(delivery)) return false;
43702
+ return spec.excluded[descriptor.id] === void 0;
43703
+ }
43704
+ /** Why a condition is not offered for this kind, or `null` when it IS. */
43705
+ function conditionExclusionReason(descriptorId, kind) {
43706
+ const spec = ruleKindSpec(kind);
43707
+ if (descriptorId === spec.discriminator) return null;
43708
+ return spec.excluded[descriptorId] ?? null;
43709
+ }
43710
+ function droppedConditionsForKind(conditions, kind) {
43711
+ const spec = ruleKindSpec(kind);
43712
+ const out = [];
43713
+ for (const [conditionId, value] of Object.entries(conditions)) {
43714
+ if (value === void 0) continue;
43715
+ if (Array.isArray(value) && value.length === 0) continue;
43716
+ const reason = spec.excluded[conditionId];
43717
+ if (reason !== void 0) out.push({
43718
+ conditionId,
43719
+ reason
43720
+ });
43721
+ }
43722
+ return out;
43723
+ }
43724
+ var NC_RULE_SECTIONS = [
43725
+ {
43726
+ key: "all",
43727
+ label: "All rules",
43728
+ blurb: "Every notification rule, whatever its trigger. Each rule picks a trigger, matches conditions and fans out to one or more delivery targets.",
43729
+ newRuleLabel: "New rule",
43730
+ emptyHint: "No notification rules yet. Create one to be told what your cameras see."
43731
+ },
43732
+ {
43733
+ key: "detection",
43734
+ label: "Detections & devices",
43735
+ blurb: "Rules driven by what the cameras and sensors SEE — detections, tracks, doorbell presses and package events.",
43736
+ newRuleLabel: "New detection rule",
43737
+ emptyHint: "No detection rules yet. Create one to be told what your cameras see."
43738
+ },
43739
+ {
43740
+ key: "audio",
43741
+ label: "Sound",
43742
+ blurb: "Rules on what the cameras HEAR: a sustained window of sound loud enough, or classified as one of the sounds you name. A sound rule fires only on a confirmed sampling window, never on a picture.",
43743
+ newRuleLabel: "New sound rule",
43744
+ emptyHint: "No sound rules yet. A new sound rule opens on the sound condition already filled in with a level threshold, so you only have to name the sounds and the targets."
43745
+ },
43746
+ {
43747
+ key: "occupancy",
43748
+ label: "Occupancy",
43749
+ blurb: "Rules on how many objects are in a camera frame or a zone, and for how long: \"the driveway became occupied\", \"the garden emptied\".",
43750
+ newRuleLabel: "New occupancy rule",
43751
+ emptyHint: "No occupancy rules yet. A new occupancy rule opens on the occupancy condition; pick the camera first, then the zone and the count."
43752
+ },
43753
+ {
43754
+ key: "system",
43755
+ label: "System events",
43756
+ blurb: "Rules about the installation itself: a camera or stream dropping, a node leaving the cluster, an addon or server update becoming available. These carry no image and are not tied to a detection.",
43757
+ newRuleLabel: "New system rule",
43758
+ emptyHint: "No system-event rules yet. These tell you when a camera drops, a node leaves or an update lands."
43759
+ }
43760
+ ];
43761
+ /** The section for a key, or `undefined` — the lookup behind every label. */
43762
+ function ruleSection(key) {
43763
+ return NC_RULE_SECTIONS.find((s) => s.key === key) ?? NC_RULE_SECTIONS[0];
43764
+ }
43765
+ /** Narrow a raw section string back to a known key (no cast). */
43766
+ function parseRuleSection(value) {
43767
+ return NC_RULE_SECTIONS.find((s) => s.key === value)?.key;
43768
+ }
43769
+ /** True for a rule whose sound condition makes it a SOUND rule. */
43770
+ function isAudioRule(subject) {
43771
+ return subject.conditions.audio !== void 0;
43772
+ }
43773
+ /** True for a rule whose occupancy condition makes it an OCCUPANCY rule. */
43774
+ function isOccupancyRule(subject) {
43775
+ return subject.conditions.occupancy !== void 0;
43776
+ }
43777
+ /**
43778
+ * Does a rule belong in this section?
43779
+ *
43780
+ * `all` always matches — a section that hides a rule is how an operator
43781
+ * concludes it was deleted. The other four PARTITION the list: sound and
43782
+ * occupancy are claimed by their condition, system by its trigger, and
43783
+ * everything left over — including a trigger this build has never heard of —
43784
+ * counts as a detection rule so it stays visible somewhere.
43785
+ *
43786
+ * Keyed on the CONDITION for two of them, because the trigger alone cannot
43787
+ * answer: a sound rule is `immediate` and an occupancy rule is `device-event`,
43788
+ * and in each case the condition is the discriminator the engine itself uses.
43789
+ * A section keyed on delivery would put both back in the pile they are
43790
+ * invisible in.
43791
+ */
43792
+ function ruleMatchesSection(subject, section) {
43793
+ if (section === "all") return true;
43794
+ if (section === "system") return isSystemDelivery(subject.delivery);
43795
+ if (section === "audio") return isAudioRule(subject);
43796
+ if (section === "occupancy") return isOccupancyRule(subject);
43797
+ return !isSystemDelivery(subject.delivery) && !isAudioRule(subject) && !isOccupancyRule(subject);
43798
+ }
43799
+ /**
43800
+ * Which section OWNS this rule — the counter behind a section strip.
43801
+ *
43802
+ * Derived from {@link ruleMatchesSection} rather than restated, so the strip's
43803
+ * counts and the strip's contents can never disagree.
43804
+ */
43805
+ function ruleSectionOf(subject) {
43806
+ if (ruleMatchesSection(subject, "system")) return "system";
43807
+ if (ruleMatchesSection(subject, "audio")) return "audio";
43808
+ if (ruleMatchesSection(subject, "occupancy")) return "occupancy";
43809
+ return "detection";
43810
+ }
43811
+ /**
43812
+ * The occupancy schema's own defaults, in one place.
43813
+ *
43814
+ * Both the WIDGET (what an untouched sub-field shows) and the CREATION PRESET
43815
+ * (what "New occupancy rule" seeds) have to author exactly these numbers, and
43816
+ * two copies of a default is how a preset starts writing a rule that does not
43817
+ * match what the editor then displays. Mirrors `NcOccupancyConditionSchema`'s
43818
+ * `.default(...)` clauses, and the spec asserts the mirror.
43819
+ */
43820
+ var NC_OCCUPANCY_DEFAULTS = {
43821
+ op: "became-occupied",
43822
+ count: 1,
43823
+ sustainSeconds: 15
43824
+ };
43825
+ /**
43826
+ * The seeded sound condition.
43827
+ *
43828
+ * Carries a level floor rather than the bare defaults: with neither a level nor
43829
+ * a label the engine returns false for every window (`matchesAudio`), so a
43830
+ * preset without one would hand the operator a rule that can never fire. The
43831
+ * offered level is the same one the widget writes, so switching to SOUNDS
43832
+ * (label mode — the one that fires on the first labelled frame, D157) is one
43833
+ * tap on the mode picker.
43834
+ *
43835
+ * The seed cannot be a label rule instead: a labels-only condition with no
43836
+ * labels chosen yet is fail-closed too, and it would hand the operator the same
43837
+ * dead rule from the other side.
43838
+ */
43839
+ var NC_AUDIO_SEED = {
43840
+ hitPercent: NC_AUDIO_DEFAULTS.hitPercent,
43841
+ samplingSeconds: NC_AUDIO_DEFAULTS.samplingSeconds,
43842
+ dbThreshold: -55
43843
+ };
43844
+ function ruleSeedForSection(section) {
43845
+ switch (section) {
43846
+ case "system": return {
43847
+ delivery: NC_SYSTEM_DELIVERY,
43848
+ conditions: {}
43849
+ };
43850
+ case "audio": return {
43851
+ delivery: "immediate",
43852
+ conditions: { audio: NC_AUDIO_SEED }
43853
+ };
43854
+ case "occupancy": return {
43855
+ delivery: "device-event",
43856
+ conditions: { occupancy: NC_OCCUPANCY_DEFAULTS }
43857
+ };
43858
+ default: return {
43859
+ delivery: "immediate",
43860
+ conditions: {}
43861
+ };
43862
+ }
43863
+ }
43864
+ /** The trigger a "New rule" in this section should open on. */
43865
+ function defaultDeliveryForSection(section) {
43866
+ return ruleSeedForSection(section).delivery;
43867
+ }
43868
+ var NC_RULE_EDITOR_SECTION_ORDER = [
43869
+ "rule",
43870
+ "devices",
43871
+ "conditions",
43872
+ "notification",
43873
+ "actions",
43874
+ "advanced"
43875
+ ];
43876
+ /** The English fallback name of each section, before the kind renames one. */
43877
+ var EDITOR_SECTION_LABELS = {
43878
+ rule: "Rule",
43879
+ devices: "Cameras",
43880
+ conditions: "Conditions",
43881
+ notification: "Notification",
43882
+ actions: "Actions",
43883
+ advanced: "Advanced"
43884
+ };
43885
+ /**
43886
+ * The sections a rule of THIS kind actually shows.
43887
+ *
43888
+ * Two rules, both of them about a control that would decide nothing:
43889
+ *
43890
+ * - a `system-event` rule has no device scope — `conditions.devices` is not
43891
+ * evaluated for it and the input builder refuses to write it — so the
43892
+ * Cameras section is DROPPED rather than shown and ignored;
43893
+ * - a sound or occupancy rule is NAMED on its conditions section. Neither has
43894
+ * a trigger of its own, so that section is the only place either feature
43895
+ * exists, and a heading that never mentions sound is exactly why an operator
43896
+ * with a working audio engine reported there was no way to create an audio
43897
+ * rule.
43898
+ *
43899
+ * The NOTIFICATION section always survives: priority and the template still
43900
+ * matter even for a kind that carries no picture, and dropping the section to
43901
+ * hide the media controls would take the template with it. Whether the media
43902
+ * half renders is `carriesMedia` / `carriesSubjectCrop`, asked separately.
43903
+ */
43904
+ function ruleEditorSectionsForKind(kind) {
43905
+ const spec = ruleKindSpec(kind);
43906
+ const sections = [];
43907
+ for (const key of NC_RULE_EDITOR_SECTION_ORDER) {
43908
+ if (key === "devices" && kind === "system") continue;
43909
+ sections.push({
43910
+ key,
43911
+ label: key === "conditions" ? spec.label : EDITOR_SECTION_LABELS[key]
43912
+ });
43913
+ }
43914
+ return sections;
43915
+ }
43916
+ //#endregion
43563
43917
  //#region src/notification/schedule.ts
43564
43918
  var WEEKDAY_TO_DAY = {
43565
43919
  Sun: 0,
@@ -46772,6 +47126,7 @@ exports.NC_AUDIO_HIT_PERCENT_MAX = NC_AUDIO_HIT_PERCENT_MAX;
46772
47126
  exports.NC_AUDIO_HIT_PERCENT_MIN = NC_AUDIO_HIT_PERCENT_MIN;
46773
47127
  exports.NC_AUDIO_SAMPLING_MAX_SEC = NC_AUDIO_SAMPLING_MAX_SEC;
46774
47128
  exports.NC_AUDIO_SAMPLING_MIN_SEC = NC_AUDIO_SAMPLING_MIN_SEC;
47129
+ exports.NC_AUDIO_SEED = NC_AUDIO_SEED;
46775
47130
  exports.NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NC_AUTHORABLE_SYSTEM_EVENT_KINDS;
46776
47131
  exports.NC_BASE_CONDITION_KEYS = NC_BASE_CONDITION_KEYS;
46777
47132
  exports.NC_CONDITION_CATALOG = NC_CONDITION_CATALOG;
@@ -46783,7 +47138,12 @@ exports.NC_DEFAULT_SNOOZE_MINUTES = NC_DEFAULT_SNOOZE_MINUTES;
46783
47138
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
46784
47139
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
46785
47140
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
47141
+ exports.NC_OCCUPANCY_DEFAULTS = NC_OCCUPANCY_DEFAULTS;
47142
+ exports.NC_RULE_EDITOR_SECTION_ORDER = NC_RULE_EDITOR_SECTION_ORDER;
47143
+ exports.NC_RULE_KIND_SPECS = NC_RULE_KIND_SPECS;
47144
+ exports.NC_RULE_SECTIONS = NC_RULE_SECTIONS;
46786
47145
  exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
47146
+ exports.NC_SYSTEM_DELIVERY = NC_SYSTEM_DELIVERY;
46787
47147
  exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
46788
47148
  exports.NC_TAXONOMY = NC_TAXONOMY;
46789
47149
  exports.NativeCropBboxSchema = NativeCropBboxSchema;
@@ -47271,6 +47631,8 @@ exports.compileExpression = compileExpression;
47271
47631
  exports.compileExpressionSafe = compileExpressionSafe;
47272
47632
  exports.composeSwitchedOff = composeSwitchedOff;
47273
47633
  exports.conditionDepth = conditionDepth;
47634
+ exports.conditionExclusionReason = conditionExclusionReason;
47635
+ exports.conditionVisibleForKind = conditionVisibleForKind;
47274
47636
  exports.connectionTestCapability = connectionTestCapability;
47275
47637
  exports.connectivityCapability = connectivityCapability;
47276
47638
  exports.consumablesCapability = consumablesCapability;
@@ -47300,6 +47662,7 @@ exports.dayNightCapability = dayNightCapability;
47300
47662
  exports.declarationOwnerNodeId = declarationOwnerNodeId;
47301
47663
  exports.decodeVectorBase64 = decodeVectorBase64;
47302
47664
  exports.decoderCapability = decoderCapability;
47665
+ exports.defaultDeliveryForSection = defaultDeliveryForSection;
47303
47666
  exports.defaultDeviceFor = defaultDeviceFor;
47304
47667
  exports.defineCustomActions = defineCustomActions;
47305
47668
  exports.deriveBatteryPresence = deriveBatteryPresence;
@@ -47322,6 +47685,7 @@ exports.deviceSelectorMatches = deviceSelectorMatches;
47322
47685
  exports.deviceStateCapability = deviceStateCapability;
47323
47686
  exports.deviceStatusCapability = deviceStatusCapability;
47324
47687
  exports.doorbellCapability = doorbellCapability;
47688
+ exports.droppedConditionsForKind = droppedConditionsForKind;
47325
47689
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
47326
47690
  exports.egressTransportFromRequest = egressTransportFromRequest;
47327
47691
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
@@ -47373,6 +47737,7 @@ exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncod
47373
47737
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
47374
47738
  exports.isArrayOutputSchema = isArrayOutputSchema;
47375
47739
  exports.isAudioLabelSelected = isAudioLabelSelected;
47740
+ exports.isAudioRule = isAudioRule;
47376
47741
  exports.isBaseConditionKey = isBaseConditionKey;
47377
47742
  exports.isBatteryPresenceFault = isBatteryPresenceFault;
47378
47743
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
@@ -47384,10 +47749,12 @@ exports.isEvent = require_sleep.isEvent;
47384
47749
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
47385
47750
  exports.isNode = isNode;
47386
47751
  exports.isObjectInput = isObjectInput;
47752
+ exports.isOccupancyRule = isOccupancyRule;
47387
47753
  exports.isRestoredCap = isRestoredCap;
47388
47754
  exports.isSameAddonId = isSameAddonId;
47389
47755
  exports.isScheduleActive = isScheduleActive;
47390
47756
  exports.isSoftwareDecode = require_canonical_hash.isSoftwareDecode;
47757
+ exports.isSystemDelivery = isSystemDelivery;
47391
47758
  exports.isVoidInput = isVoidInput;
47392
47759
  exports.jobKindSchema = jobKindSchema;
47393
47760
  exports.kebabToCamel = kebabToCamel;
@@ -47451,6 +47818,7 @@ exports.parseJsonObject = require_sleep.parseJsonObject;
47451
47818
  exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
47452
47819
  exports.parseProcStatus = parseProcStatus;
47453
47820
  exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
47821
+ exports.parseRuleSection = parseRuleSection;
47454
47822
  exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
47455
47823
  exports.patchAudio = patchAudio;
47456
47824
  exports.petFeederCapability = petFeederCapability;
@@ -47510,6 +47878,13 @@ exports.resolveScrubThumbnailGeometry = resolveScrubThumbnailGeometry;
47510
47878
  exports.resolveVariantModelId = resolveVariantModelId;
47511
47879
  exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
47512
47880
  exports.roleSpec = roleSpec;
47881
+ exports.ruleEditorSectionsForKind = ruleEditorSectionsForKind;
47882
+ exports.ruleKindOf = ruleKindOf;
47883
+ exports.ruleKindSpec = ruleKindSpec;
47884
+ exports.ruleMatchesSection = ruleMatchesSection;
47885
+ exports.ruleSection = ruleSection;
47886
+ exports.ruleSectionOf = ruleSectionOf;
47887
+ exports.ruleSeedForSection = ruleSeedForSection;
47513
47888
  exports.runInferenceStep = runInferenceStep;
47514
47889
  exports.runtimeDevices = runtimeDevices;
47515
47890
  exports.runtimeStatePolicyFor = runtimeStatePolicyFor;