@chipmobilesdk/rn-notification 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +174 -166
- package/README.md +414 -410
- package/package.json +58 -75
- package/skills/sdk-rn-notification/SKILL.md +172 -173
- package/src/channel/migrate.ts +285 -289
- package/src/channel/tone-map.ts +130 -130
- package/src/channel/validate-tone.ts +61 -61
- package/src/contract/channel.ts +192 -188
- package/src/contract/diagnostics.ts +102 -102
- package/src/contract/errors.ts +134 -134
- package/src/contract/index.ts +10 -10
- package/src/contract/interaction.ts +62 -62
- package/src/contract/notification.ts +118 -118
- package/src/contract/permission.ts +55 -60
- package/src/contract/reconcile.ts +88 -88
- package/src/contract/recurrence.ts +129 -129
- package/src/diagnostics/burst.ts +101 -101
- package/src/diagnostics/events.ts +72 -72
- package/src/diagnostics/redact.ts +72 -72
- package/src/diagnostics/snapshot.ts +67 -64
- package/src/engine/adapter.ts +172 -172
- package/src/engine/codec.ts +89 -89
- package/src/engine/notify-kit-engine.ts +706 -707
- package/src/index.ts +208 -208
- package/src/initialize.ts +240 -236
- package/src/interaction/index.ts +146 -146
- package/src/maintenance/cycle.ts +150 -154
- package/src/maintenance/permission-refresh.ts +39 -41
- package/src/maintenance/reanchor.ts +101 -101
- package/src/maintenance/toggle.ts +66 -64
- package/src/maintenance/verify.ts +83 -83
- package/src/maintenance/window-refresh.ts +64 -64
- package/src/permission/index.ts +183 -183
- package/src/permission/state.ts +77 -77
- package/src/push/envelope.ts +165 -165
- package/src/push/fcm-provider.ts +143 -146
- package/src/push/gate.ts +190 -190
- package/src/push/index.ts +33 -29
- package/src/push/instructions.ts +114 -109
- package/src/push/provider-adapter.ts +54 -54
- package/src/push/signout.ts +65 -65
- package/src/reconcile/chunk.ts +53 -53
- package/src/reconcile/diff.ts +142 -145
- package/src/reconcile/duplicates.ts +73 -73
- package/src/reconcile/index.ts +180 -180
- package/src/reconcile/truncate.ts +56 -56
- package/src/recurrence/clock-guard.ts +73 -71
- package/src/recurrence/expand.ts +326 -319
- package/src/recurrence/index.ts +270 -259
- package/src/repeat/expiry.ts +53 -55
- package/src/repeat/index.ts +54 -54
- package/src/repeat/marker.ts +72 -72
- package/src/repeat/place.ts +73 -73
- package/src/schedule/anchor.ts +278 -267
- package/src/schedule/index.ts +174 -181
- package/src/schedule/partition.ts +88 -85
- package/src/schedule/resolve-moment.ts +80 -80
- package/src/schedule/validate-content.ts +111 -111
- package/src/schedule/validate-identifier.ts +126 -126
- package/src/schedule/writer.ts +55 -55
- package/src/state/caps.ts +71 -71
- package/src/state/dedup-ledger.ts +35 -35
- package/src/state/interaction-buffer.ts +73 -73
- package/src/state/mmkv-store.ts +75 -75
- package/src/state/permission-history.ts +41 -41
- package/src/state/record.ts +266 -263
- package/src/state/reset.ts +41 -41
- package/src/state/series-ledger.ts +91 -91
- package/src/state/store.ts +40 -40
- package/src/testing/conformance/index.ts +258 -248
- package/src/testing/fake-engine.ts +462 -468
- package/src/testing/fake-provider.ts +126 -129
- package/src/testing/faults.ts +72 -72
- package/src/testing/index.ts +23 -28
- package/src/testing/measure.ts +337 -337
package/src/contract/channel.ts
CHANGED
|
@@ -1,188 +1,192 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Channel, category, and tone declaration types.
|
|
3
|
-
*
|
|
4
|
-
* A tone is declared once and mapped by the package to each platform's
|
|
5
|
-
* equivalent concepts, so consumer code contains no platform branches.
|
|
6
|
-
*
|
|
7
|
-
* Two tones must never be merged into one: channel configuration belongs to
|
|
8
|
-
* the user, and collapsing two tones collapses two independent user choices.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/** How insistently a notification asks for attention. */
|
|
12
|
-
export type ToneUrgency = 'min' | 'low' | 'default' | 'high' | 'max';
|
|
13
|
-
|
|
14
|
-
export interface ToneSound {
|
|
15
|
-
/** Consumer-supplied resource name, resolved by the platform. */
|
|
16
|
-
readonly name?: string;
|
|
17
|
-
/** When false, the tone is silent. Defaults to true. */
|
|
18
|
-
readonly enabled?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* A repeating alert and the bound on how long it may repeat.
|
|
23
|
-
*
|
|
24
|
-
* The two are one object rather than two fields because an unbounded repeating
|
|
25
|
-
* alert must be *unrepresentable*, not merely discouraged. A boolean `repeat`
|
|
26
|
-
* beside an optional duration would make the dangerous state the easiest one to
|
|
27
|
-
* write, and the dangerous state here is a phone that keeps sounding until its
|
|
28
|
-
* owner comes home to it.
|
|
29
|
-
*/
|
|
30
|
-
export interface ToneRepeatAlert {
|
|
31
|
-
/**
|
|
32
|
-
* How long the alert may keep repeating, measured from delivery.
|
|
33
|
-
*
|
|
34
|
-
* Required. Bounded by REPEAT_ALERT_BOUNDS, which is enforced at declaration
|
|
35
|
-
* time — a type can forbid an absent duration but not a zero one.
|
|
36
|
-
*/
|
|
37
|
-
readonly forMs: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The declarable range for a repeating alert.
|
|
42
|
-
*
|
|
43
|
-
* The floor exists because a repeat shorter than a minute is indistinguishable
|
|
44
|
-
* from a single chime and buys nothing for the complexity it costs. The ceiling
|
|
45
|
-
* exists because an hour is already past the point where a person who has not
|
|
46
|
-
* responded is going to.
|
|
47
|
-
*/
|
|
48
|
-
export const REPEAT_ALERT_BOUNDS = {
|
|
49
|
-
minMs: 60_000,
|
|
50
|
-
maxMs: 3_600_000,
|
|
51
|
-
} as const;
|
|
52
|
-
|
|
53
|
-
export interface ToneDeclaration {
|
|
54
|
-
/** Stable tone identifier, subject to the same charset rules as a notification id. */
|
|
55
|
-
readonly id: string;
|
|
56
|
-
/** Human-readable name shown in system settings. */
|
|
57
|
-
readonly name: string;
|
|
58
|
-
readonly description?: string;
|
|
59
|
-
readonly urgency: ToneUrgency;
|
|
60
|
-
readonly sound?: ToneSound;
|
|
61
|
-
readonly vibration?: boolean;
|
|
62
|
-
readonly badge?: boolean;
|
|
63
|
-
/** Optional grouping in system settings. */
|
|
64
|
-
readonly groupId?: string;
|
|
65
|
-
/**
|
|
66
|
-
* Request the platform's Do Not Disturb bypass. Honoured where the platform
|
|
67
|
-
* permits it and reported honestly where it does not. This is *not* an
|
|
68
|
-
* override of the device's silent mode, which is out of scope.
|
|
69
|
-
*/
|
|
70
|
-
readonly bypassDoNotDisturb?: boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Present when the alert sound should repeat until the user handles the
|
|
73
|
-
* notification, rather than sounding once.
|
|
74
|
-
*
|
|
75
|
-
* Realized per notification rather than as channel configuration, so adding
|
|
76
|
-
* or changing it never obliges a consumer to bump `version`. Honoured where
|
|
77
|
-
* the platform permits it and reported through `repeatUnavailable` where it
|
|
78
|
-
* does not.
|
|
79
|
-
*/
|
|
80
|
-
readonly repeatAlert?: ToneRepeatAlert;
|
|
81
|
-
/**
|
|
82
|
-
* Ask the platform to treat notifications on this tone as time-sensitive
|
|
83
|
-
* alerts. Like `repeatAlert`, realized per notification, so it needs no
|
|
84
|
-
* version bump.
|
|
85
|
-
*/
|
|
86
|
-
readonly timeSensitive?: boolean;
|
|
87
|
-
/**
|
|
88
|
-
* Incremented by the consumer when a field the platform freezes changes:
|
|
89
|
-
* `urgency`, `sound`, `vibration`, `badge`, `groupId`, or
|
|
90
|
-
* `bypassDoNotDisturb`.
|
|
91
|
-
*
|
|
92
|
-
* Android freezes those once a channel exists, so changing one is a
|
|
93
|
-
* migration rather than an assignment, and this version is what makes the
|
|
94
|
-
* migration detectable.
|
|
95
|
-
*
|
|
96
|
-
* `name`, `description`, `repeatAlert`, and `timeSensitive` are deliberately
|
|
97
|
-
* *not* on that list. Android updates a channel's labels in place, and the
|
|
98
|
-
* two alert attributes are realized per notification — so none of them needs
|
|
99
|
-
* a bump. This matters: a bump means a new channel, and a new channel means
|
|
100
|
-
* the user loses every setting they adjusted. Nobody should have to pay that
|
|
101
|
-
* to correct a typo.
|
|
102
|
-
*/
|
|
103
|
-
readonly version: number;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface ChannelGroup {
|
|
107
|
-
readonly id: string;
|
|
108
|
-
readonly name: string;
|
|
109
|
-
readonly description?: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** What a tone resolves to on each platform. Internal to the engine adapter. */
|
|
113
|
-
export interface ResolvedTone {
|
|
114
|
-
readonly toneId: string;
|
|
115
|
-
readonly version: number;
|
|
116
|
-
readonly android: {
|
|
117
|
-
/** Platform channel id, derived from tone id and version so a change creates a new channel. */
|
|
118
|
-
readonly channelId: string;
|
|
119
|
-
/**
|
|
120
|
-
* The channel's user-visible name.
|
|
121
|
-
*
|
|
122
|
-
* Required rather than optional on purpose. Its absence is what let the
|
|
123
|
-
* adapter fall back to the tone id, so users read a technical identifier
|
|
124
|
-
* on the one screen these strings exist to serve. A type that permits no
|
|
125
|
-
* name permits that bug back.
|
|
126
|
-
*/
|
|
127
|
-
readonly name: string;
|
|
128
|
-
/** The channel's user-visible description. */
|
|
129
|
-
readonly description?: string;
|
|
130
|
-
readonly importance: 'none' | 'min' | 'low' | 'default' | 'high';
|
|
131
|
-
readonly sound?: string;
|
|
132
|
-
readonly vibration: boolean;
|
|
133
|
-
readonly badge: boolean;
|
|
134
|
-
readonly bypassDnd: boolean;
|
|
135
|
-
readonly groupId?: string;
|
|
136
|
-
/** Repeat the alert sound until handled. Per notification, not channel configuration. */
|
|
137
|
-
readonly loopSound: boolean;
|
|
138
|
-
/** Mark the notification as an alarm-category alert. Per notification. */
|
|
139
|
-
readonly alarmCategory: boolean;
|
|
140
|
-
/** How long repetition may continue, so the adapter knows when to place the stop marker. */
|
|
141
|
-
readonly repeatForMs?: number;
|
|
142
|
-
};
|
|
143
|
-
readonly ios: {
|
|
144
|
-
readonly categoryId: string;
|
|
145
|
-
readonly interruptionLevel: 'passive' | 'active' | 'timeSensitive' | 'critical';
|
|
146
|
-
readonly sound?: string;
|
|
147
|
-
readonly badge: boolean;
|
|
148
|
-
/**
|
|
149
|
-
* True when a repeating alert was declared and iOS will not honour it.
|
|
150
|
-
*
|
|
151
|
-
* iOS has no looping-sound API, and the one level that overrides the mute
|
|
152
|
-
* switch is entitlement-gated and does not loop either. Recording the
|
|
153
|
-
* refusal as data rather than as a comment is what lets `apply()` report
|
|
154
|
-
* it instead of dropping it silently.
|
|
155
|
-
*/
|
|
156
|
-
readonly repeatRefused: boolean;
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Outcome of applying a channel configuration, including any migration performed.
|
|
162
|
-
*
|
|
163
|
-
* A tone appears in exactly one of `created`, `migrated`, `labelsRefreshed`, and
|
|
164
|
-
* `unchanged`. The `*Unavailable` arrays are orthogonal and may accompany any of
|
|
165
|
-
* them: a platform refusing an attribute says nothing about whether the tone was
|
|
166
|
-
* newly created.
|
|
167
|
-
*/
|
|
168
|
-
export interface ChannelApplyResult {
|
|
169
|
-
readonly created: readonly string[];
|
|
170
|
-
readonly migrated: readonly {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
readonly
|
|
183
|
-
/**
|
|
184
|
-
readonly
|
|
185
|
-
/**
|
|
186
|
-
readonly
|
|
187
|
-
|
|
188
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Channel, category, and tone declaration types.
|
|
3
|
+
*
|
|
4
|
+
* A tone is declared once and mapped by the package to each platform's
|
|
5
|
+
* equivalent concepts, so consumer code contains no platform branches.
|
|
6
|
+
*
|
|
7
|
+
* Two tones must never be merged into one: channel configuration belongs to
|
|
8
|
+
* the user, and collapsing two tones collapses two independent user choices.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** How insistently a notification asks for attention. */
|
|
12
|
+
export type ToneUrgency = 'min' | 'low' | 'default' | 'high' | 'max';
|
|
13
|
+
|
|
14
|
+
export interface ToneSound {
|
|
15
|
+
/** Consumer-supplied resource name, resolved by the platform. */
|
|
16
|
+
readonly name?: string;
|
|
17
|
+
/** When false, the tone is silent. Defaults to true. */
|
|
18
|
+
readonly enabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A repeating alert and the bound on how long it may repeat.
|
|
23
|
+
*
|
|
24
|
+
* The two are one object rather than two fields because an unbounded repeating
|
|
25
|
+
* alert must be *unrepresentable*, not merely discouraged. A boolean `repeat`
|
|
26
|
+
* beside an optional duration would make the dangerous state the easiest one to
|
|
27
|
+
* write, and the dangerous state here is a phone that keeps sounding until its
|
|
28
|
+
* owner comes home to it.
|
|
29
|
+
*/
|
|
30
|
+
export interface ToneRepeatAlert {
|
|
31
|
+
/**
|
|
32
|
+
* How long the alert may keep repeating, measured from delivery.
|
|
33
|
+
*
|
|
34
|
+
* Required. Bounded by REPEAT_ALERT_BOUNDS, which is enforced at declaration
|
|
35
|
+
* time — a type can forbid an absent duration but not a zero one.
|
|
36
|
+
*/
|
|
37
|
+
readonly forMs: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The declarable range for a repeating alert.
|
|
42
|
+
*
|
|
43
|
+
* The floor exists because a repeat shorter than a minute is indistinguishable
|
|
44
|
+
* from a single chime and buys nothing for the complexity it costs. The ceiling
|
|
45
|
+
* exists because an hour is already past the point where a person who has not
|
|
46
|
+
* responded is going to.
|
|
47
|
+
*/
|
|
48
|
+
export const REPEAT_ALERT_BOUNDS = {
|
|
49
|
+
minMs: 60_000,
|
|
50
|
+
maxMs: 3_600_000,
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
export interface ToneDeclaration {
|
|
54
|
+
/** Stable tone identifier, subject to the same charset rules as a notification id. */
|
|
55
|
+
readonly id: string;
|
|
56
|
+
/** Human-readable name shown in system settings. */
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly description?: string;
|
|
59
|
+
readonly urgency: ToneUrgency;
|
|
60
|
+
readonly sound?: ToneSound;
|
|
61
|
+
readonly vibration?: boolean;
|
|
62
|
+
readonly badge?: boolean;
|
|
63
|
+
/** Optional grouping in system settings. */
|
|
64
|
+
readonly groupId?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Request the platform's Do Not Disturb bypass. Honoured where the platform
|
|
67
|
+
* permits it and reported honestly where it does not. This is *not* an
|
|
68
|
+
* override of the device's silent mode, which is out of scope.
|
|
69
|
+
*/
|
|
70
|
+
readonly bypassDoNotDisturb?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Present when the alert sound should repeat until the user handles the
|
|
73
|
+
* notification, rather than sounding once.
|
|
74
|
+
*
|
|
75
|
+
* Realized per notification rather than as channel configuration, so adding
|
|
76
|
+
* or changing it never obliges a consumer to bump `version`. Honoured where
|
|
77
|
+
* the platform permits it and reported through `repeatUnavailable` where it
|
|
78
|
+
* does not.
|
|
79
|
+
*/
|
|
80
|
+
readonly repeatAlert?: ToneRepeatAlert;
|
|
81
|
+
/**
|
|
82
|
+
* Ask the platform to treat notifications on this tone as time-sensitive
|
|
83
|
+
* alerts. Like `repeatAlert`, realized per notification, so it needs no
|
|
84
|
+
* version bump.
|
|
85
|
+
*/
|
|
86
|
+
readonly timeSensitive?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Incremented by the consumer when a field the platform freezes changes:
|
|
89
|
+
* `urgency`, `sound`, `vibration`, `badge`, `groupId`, or
|
|
90
|
+
* `bypassDoNotDisturb`.
|
|
91
|
+
*
|
|
92
|
+
* Android freezes those once a channel exists, so changing one is a
|
|
93
|
+
* migration rather than an assignment, and this version is what makes the
|
|
94
|
+
* migration detectable.
|
|
95
|
+
*
|
|
96
|
+
* `name`, `description`, `repeatAlert`, and `timeSensitive` are deliberately
|
|
97
|
+
* *not* on that list. Android updates a channel's labels in place, and the
|
|
98
|
+
* two alert attributes are realized per notification — so none of them needs
|
|
99
|
+
* a bump. This matters: a bump means a new channel, and a new channel means
|
|
100
|
+
* the user loses every setting they adjusted. Nobody should have to pay that
|
|
101
|
+
* to correct a typo.
|
|
102
|
+
*/
|
|
103
|
+
readonly version: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface ChannelGroup {
|
|
107
|
+
readonly id: string;
|
|
108
|
+
readonly name: string;
|
|
109
|
+
readonly description?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** What a tone resolves to on each platform. Internal to the engine adapter. */
|
|
113
|
+
export interface ResolvedTone {
|
|
114
|
+
readonly toneId: string;
|
|
115
|
+
readonly version: number;
|
|
116
|
+
readonly android: {
|
|
117
|
+
/** Platform channel id, derived from tone id and version so a change creates a new channel. */
|
|
118
|
+
readonly channelId: string;
|
|
119
|
+
/**
|
|
120
|
+
* The channel's user-visible name.
|
|
121
|
+
*
|
|
122
|
+
* Required rather than optional on purpose. Its absence is what let the
|
|
123
|
+
* adapter fall back to the tone id, so users read a technical identifier
|
|
124
|
+
* on the one screen these strings exist to serve. A type that permits no
|
|
125
|
+
* name permits that bug back.
|
|
126
|
+
*/
|
|
127
|
+
readonly name: string;
|
|
128
|
+
/** The channel's user-visible description. */
|
|
129
|
+
readonly description?: string;
|
|
130
|
+
readonly importance: 'none' | 'min' | 'low' | 'default' | 'high';
|
|
131
|
+
readonly sound?: string;
|
|
132
|
+
readonly vibration: boolean;
|
|
133
|
+
readonly badge: boolean;
|
|
134
|
+
readonly bypassDnd: boolean;
|
|
135
|
+
readonly groupId?: string;
|
|
136
|
+
/** Repeat the alert sound until handled. Per notification, not channel configuration. */
|
|
137
|
+
readonly loopSound: boolean;
|
|
138
|
+
/** Mark the notification as an alarm-category alert. Per notification. */
|
|
139
|
+
readonly alarmCategory: boolean;
|
|
140
|
+
/** How long repetition may continue, so the adapter knows when to place the stop marker. */
|
|
141
|
+
readonly repeatForMs?: number;
|
|
142
|
+
};
|
|
143
|
+
readonly ios: {
|
|
144
|
+
readonly categoryId: string;
|
|
145
|
+
readonly interruptionLevel: 'passive' | 'active' | 'timeSensitive' | 'critical';
|
|
146
|
+
readonly sound?: string;
|
|
147
|
+
readonly badge: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* True when a repeating alert was declared and iOS will not honour it.
|
|
150
|
+
*
|
|
151
|
+
* iOS has no looping-sound API, and the one level that overrides the mute
|
|
152
|
+
* switch is entitlement-gated and does not loop either. Recording the
|
|
153
|
+
* refusal as data rather than as a comment is what lets `apply()` report
|
|
154
|
+
* it instead of dropping it silently.
|
|
155
|
+
*/
|
|
156
|
+
readonly repeatRefused: boolean;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Outcome of applying a channel configuration, including any migration performed.
|
|
162
|
+
*
|
|
163
|
+
* A tone appears in exactly one of `created`, `migrated`, `labelsRefreshed`, and
|
|
164
|
+
* `unchanged`. The `*Unavailable` arrays are orthogonal and may accompany any of
|
|
165
|
+
* them: a platform refusing an attribute says nothing about whether the tone was
|
|
166
|
+
* newly created.
|
|
167
|
+
*/
|
|
168
|
+
export interface ChannelApplyResult {
|
|
169
|
+
readonly created: readonly string[];
|
|
170
|
+
readonly migrated: readonly {
|
|
171
|
+
readonly toneId: string;
|
|
172
|
+
readonly from: number;
|
|
173
|
+
readonly to: number;
|
|
174
|
+
}[];
|
|
175
|
+
/**
|
|
176
|
+
* Tones whose name or description was updated in place on a channel that
|
|
177
|
+
* already existed. No channel was recreated and no user setting was reset —
|
|
178
|
+
* the platform itself refuses to overwrite the rest of the configuration,
|
|
179
|
+
* which is what makes a label correction free.
|
|
180
|
+
*/
|
|
181
|
+
readonly labelsRefreshed: readonly string[];
|
|
182
|
+
readonly retired: readonly string[];
|
|
183
|
+
/** Notifications moved to the new channel during migration. */
|
|
184
|
+
readonly reHomed: readonly string[];
|
|
185
|
+
/** Notifications cancelled because they could not be re-homed. */
|
|
186
|
+
readonly cancelled: readonly string[];
|
|
187
|
+
/** Tones whose requested DND bypass the platform refused. */
|
|
188
|
+
readonly bypassUnavailable: readonly string[];
|
|
189
|
+
/** Tones whose requested repeating alert the platform refused. */
|
|
190
|
+
readonly repeatUnavailable: readonly string[];
|
|
191
|
+
readonly unchanged: readonly string[];
|
|
192
|
+
}
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diagnostics and configuration types.
|
|
3
|
-
*
|
|
4
|
-
* The package's stated purpose is to make invisible notification failures
|
|
5
|
-
* diagnosable, so every lifecycle transition emits a structured event with a
|
|
6
|
-
* stable identifier — and none of those events carries content by default.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { PermissionReport, RestrictionReport } from './permission';
|
|
10
|
-
import type { PendingDiscrepancy, MaintenanceResult, ReconcileResult } from './reconcile';
|
|
11
|
-
import type { ToneDeclaration, ChannelGroup } from './channel';
|
|
12
|
-
import type { LookaheadConfig } from './recurrence';
|
|
13
|
-
import type { PendingEntry } from './notification';
|
|
14
|
-
|
|
15
|
-
export type DiagnosticSeverity = 'debug' | 'info' | 'warn' | 'error';
|
|
16
|
-
|
|
17
|
-
export interface DiagnosticEvent {
|
|
18
|
-
/** Stable identifier, safe to switch on. See contracts/errors.md. */
|
|
19
|
-
readonly code: string;
|
|
20
|
-
readonly severity: DiagnosticSeverity;
|
|
21
|
-
readonly message: string;
|
|
22
|
-
readonly at: number;
|
|
23
|
-
/**
|
|
24
|
-
* Structured context. Never contains notification body text, routing payload
|
|
25
|
-
* values, or push tokens unless verbose diagnostics is explicitly enabled.
|
|
26
|
-
*/
|
|
27
|
-
readonly context?: Readonly<Record<string, unknown>>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type EventSink = (event: DiagnosticEvent) => void;
|
|
31
|
-
|
|
32
|
-
/** Health of one persisted collection against its cap. */
|
|
33
|
-
export interface CollectionHealth {
|
|
34
|
-
readonly name: string;
|
|
35
|
-
readonly size: number;
|
|
36
|
-
readonly cap: number;
|
|
37
|
-
readonly oldestEntryAt?: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface StateHealth {
|
|
41
|
-
readonly readable: boolean;
|
|
42
|
-
/** True when the record was discarded and rebuilt, e.g. after corruption. */
|
|
43
|
-
readonly rebuilt: boolean;
|
|
44
|
-
readonly collections: readonly CollectionHealth[];
|
|
45
|
-
readonly approximateBytes: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The readable answer to "why didn't it arrive". */
|
|
49
|
-
export interface StateSnapshot {
|
|
50
|
-
readonly permissions: PermissionReport;
|
|
51
|
-
readonly restrictions: RestrictionReport;
|
|
52
|
-
readonly tones: readonly { readonly id: string; readonly appliedVersion: number }[];
|
|
53
|
-
readonly pendingCount: number;
|
|
54
|
-
readonly pending: readonly PendingEntry[];
|
|
55
|
-
readonly lastReconcile?: ReconcileResult;
|
|
56
|
-
readonly lastMaintenance?: MaintenanceResult;
|
|
57
|
-
readonly discrepancy?: PendingDiscrepancy;
|
|
58
|
-
readonly stateHealth: StateHealth;
|
|
59
|
-
/** Presence only. The token value is never exposed here or anywhere else. */
|
|
60
|
-
readonly pushTokenPresent: boolean;
|
|
61
|
-
readonly capturedAt: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Abnormal notification volume. Reported, never acted upon. */
|
|
65
|
-
export interface BurstThreshold {
|
|
66
|
-
readonly perMinute: number;
|
|
67
|
-
readonly perHour: number;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export const DEFAULT_BURST_THRESHOLD: BurstThreshold = {
|
|
71
|
-
perMinute: 10,
|
|
72
|
-
perHour: 60,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export interface NotificationConfig {
|
|
76
|
-
/** Every tone, category, and channel in one place. */
|
|
77
|
-
readonly tones: readonly ToneDeclaration[];
|
|
78
|
-
readonly channelGroups?: readonly ChannelGroup[];
|
|
79
|
-
readonly lookahead?: Partial<LookaheadConfig>;
|
|
80
|
-
/**
|
|
81
|
-
* Automatic maintenance covers only what the package alone can know:
|
|
82
|
-
* window refresh, expansion, re-anchoring, verification, permission re-read.
|
|
83
|
-
* Full desired-set reconciliation is never automatic. Defaults to true.
|
|
84
|
-
*/
|
|
85
|
-
readonly automaticMaintenance?: boolean;
|
|
86
|
-
readonly burstThreshold?: BurstThreshold;
|
|
87
|
-
/** With no sink supplied the package emits nothing rather than writing to a console. */
|
|
88
|
-
readonly eventSink?: EventSink;
|
|
89
|
-
/**
|
|
90
|
-
* Includes content values in diagnostics. Documented as unsuitable for
|
|
91
|
-
* production, and off by default.
|
|
92
|
-
*/
|
|
93
|
-
readonly verboseDiagnostics?: boolean;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface InitializeResult {
|
|
97
|
-
readonly tonesApplied: number;
|
|
98
|
-
readonly migrationsRun: number;
|
|
99
|
-
readonly stateRebuilt: boolean;
|
|
100
|
-
readonly automaticMaintenance: boolean;
|
|
101
|
-
readonly durationMs: number;
|
|
102
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Diagnostics and configuration types.
|
|
3
|
+
*
|
|
4
|
+
* The package's stated purpose is to make invisible notification failures
|
|
5
|
+
* diagnosable, so every lifecycle transition emits a structured event with a
|
|
6
|
+
* stable identifier — and none of those events carries content by default.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PermissionReport, RestrictionReport } from './permission';
|
|
10
|
+
import type { PendingDiscrepancy, MaintenanceResult, ReconcileResult } from './reconcile';
|
|
11
|
+
import type { ToneDeclaration, ChannelGroup } from './channel';
|
|
12
|
+
import type { LookaheadConfig } from './recurrence';
|
|
13
|
+
import type { PendingEntry } from './notification';
|
|
14
|
+
|
|
15
|
+
export type DiagnosticSeverity = 'debug' | 'info' | 'warn' | 'error';
|
|
16
|
+
|
|
17
|
+
export interface DiagnosticEvent {
|
|
18
|
+
/** Stable identifier, safe to switch on. See contracts/errors.md. */
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly severity: DiagnosticSeverity;
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly at: number;
|
|
23
|
+
/**
|
|
24
|
+
* Structured context. Never contains notification body text, routing payload
|
|
25
|
+
* values, or push tokens unless verbose diagnostics is explicitly enabled.
|
|
26
|
+
*/
|
|
27
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type EventSink = (event: DiagnosticEvent) => void;
|
|
31
|
+
|
|
32
|
+
/** Health of one persisted collection against its cap. */
|
|
33
|
+
export interface CollectionHealth {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly size: number;
|
|
36
|
+
readonly cap: number;
|
|
37
|
+
readonly oldestEntryAt?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface StateHealth {
|
|
41
|
+
readonly readable: boolean;
|
|
42
|
+
/** True when the record was discarded and rebuilt, e.g. after corruption. */
|
|
43
|
+
readonly rebuilt: boolean;
|
|
44
|
+
readonly collections: readonly CollectionHealth[];
|
|
45
|
+
readonly approximateBytes: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The readable answer to "why didn't it arrive". */
|
|
49
|
+
export interface StateSnapshot {
|
|
50
|
+
readonly permissions: PermissionReport;
|
|
51
|
+
readonly restrictions: RestrictionReport;
|
|
52
|
+
readonly tones: readonly { readonly id: string; readonly appliedVersion: number }[];
|
|
53
|
+
readonly pendingCount: number;
|
|
54
|
+
readonly pending: readonly PendingEntry[];
|
|
55
|
+
readonly lastReconcile?: ReconcileResult;
|
|
56
|
+
readonly lastMaintenance?: MaintenanceResult;
|
|
57
|
+
readonly discrepancy?: PendingDiscrepancy;
|
|
58
|
+
readonly stateHealth: StateHealth;
|
|
59
|
+
/** Presence only. The token value is never exposed here or anywhere else. */
|
|
60
|
+
readonly pushTokenPresent: boolean;
|
|
61
|
+
readonly capturedAt: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Abnormal notification volume. Reported, never acted upon. */
|
|
65
|
+
export interface BurstThreshold {
|
|
66
|
+
readonly perMinute: number;
|
|
67
|
+
readonly perHour: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const DEFAULT_BURST_THRESHOLD: BurstThreshold = {
|
|
71
|
+
perMinute: 10,
|
|
72
|
+
perHour: 60,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export interface NotificationConfig {
|
|
76
|
+
/** Every tone, category, and channel in one place. */
|
|
77
|
+
readonly tones: readonly ToneDeclaration[];
|
|
78
|
+
readonly channelGroups?: readonly ChannelGroup[];
|
|
79
|
+
readonly lookahead?: Partial<LookaheadConfig>;
|
|
80
|
+
/**
|
|
81
|
+
* Automatic maintenance covers only what the package alone can know:
|
|
82
|
+
* window refresh, expansion, re-anchoring, verification, permission re-read.
|
|
83
|
+
* Full desired-set reconciliation is never automatic. Defaults to true.
|
|
84
|
+
*/
|
|
85
|
+
readonly automaticMaintenance?: boolean;
|
|
86
|
+
readonly burstThreshold?: BurstThreshold;
|
|
87
|
+
/** With no sink supplied the package emits nothing rather than writing to a console. */
|
|
88
|
+
readonly eventSink?: EventSink;
|
|
89
|
+
/**
|
|
90
|
+
* Includes content values in diagnostics. Documented as unsuitable for
|
|
91
|
+
* production, and off by default.
|
|
92
|
+
*/
|
|
93
|
+
readonly verboseDiagnostics?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface InitializeResult {
|
|
97
|
+
readonly tonesApplied: number;
|
|
98
|
+
readonly migrationsRun: number;
|
|
99
|
+
readonly stateRebuilt: boolean;
|
|
100
|
+
readonly automaticMaintenance: boolean;
|
|
101
|
+
readonly durationMs: number;
|
|
102
|
+
}
|