@chipmobilesdk/rn-notification 0.4.0 → 0.5.0
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 +257 -166
- package/README.md +420 -410
- package/package.json +61 -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 +142 -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 +81 -72
- package/src/diagnostics/redact.ts +55 -72
- package/src/diagnostics/snapshot.ts +100 -64
- package/src/engine/adapter.ts +172 -172
- package/src/engine/codec.ts +89 -89
- package/src/engine/notify-kit-engine.ts +715 -707
- package/src/index.ts +208 -208
- package/src/initialize.ts +247 -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 +150 -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 +294 -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 +101 -73
- package/src/state/mmkv-store.ts +92 -75
- package/src/state/permission-history.ts +41 -41
- package/src/state/record.ts +270 -263
- package/src/state/reset.ts +41 -41
- package/src/state/series-ledger.ts +91 -91
- package/src/state/store.ts +43 -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
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core notification request types.
|
|
3
|
-
*
|
|
4
|
-
* Nothing here carries business meaning. The package accepts fully-described
|
|
5
|
-
* requests and is responsible only for delivering them at the right moment.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* How a delivery moment is pinned to the passage of time.
|
|
10
|
-
*
|
|
11
|
-
* - `wallClock` — the local clock time named. An 08:00 reminder stays 08:00
|
|
12
|
-
* after the user crosses a time zone. This is the default, because reminders
|
|
13
|
-
* are wall-clock things and a caller who does not choose should get what
|
|
14
|
-
* their users expect.
|
|
15
|
-
* - `absolute` — one fixed instant worldwide, unaffected by zone changes.
|
|
16
|
-
*/
|
|
17
|
-
export type AnchoringMode = 'wallClock' | 'absolute';
|
|
18
|
-
|
|
19
|
-
export const DEFAULT_ANCHORING_MODE: AnchoringMode = 'wallClock';
|
|
20
|
-
|
|
21
|
-
/** When a notification should be presented, and how that moment is anchored. */
|
|
22
|
-
export interface DeliveryMoment {
|
|
23
|
-
/** Epoch milliseconds. Interpreted per {@link DeliveryMoment.anchoring}. */
|
|
24
|
-
readonly at: number;
|
|
25
|
-
/** Defaults to `wallClock` when omitted. */
|
|
26
|
-
readonly anchoring?: AnchoringMode;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Resolved display strings. The package performs no translation, formatting,
|
|
31
|
-
* or locale resolution — only the consuming app knows its content model.
|
|
32
|
-
*/
|
|
33
|
-
export interface NotificationContent {
|
|
34
|
-
readonly title: string;
|
|
35
|
-
readonly body: string;
|
|
36
|
-
/** Optional short text shown in collapsed form on some platforms. */
|
|
37
|
-
readonly subtitle?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Structured data carried to the interaction handler, unchanged. */
|
|
41
|
-
export type RoutingPayload = Readonly<Record<string, unknown>>;
|
|
42
|
-
|
|
43
|
-
/** A button on a notification. */
|
|
44
|
-
export interface Action {
|
|
45
|
-
/** Stable action identifier, subject to the same charset rules as a notification id. */
|
|
46
|
-
readonly id: string;
|
|
47
|
-
readonly title: string;
|
|
48
|
-
/**
|
|
49
|
-
* `foreground` brings the app up; `background` is handled without opening it,
|
|
50
|
-
* inside the platform's bounded execution window.
|
|
51
|
-
*/
|
|
52
|
-
readonly mode: 'foreground' | 'background';
|
|
53
|
-
/** When present, the action accepts short typed input. */
|
|
54
|
-
readonly input?: {
|
|
55
|
-
readonly placeholder?: string;
|
|
56
|
-
readonly submitLabel?: string;
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** The complete, self-contained description of one notification. */
|
|
61
|
-
export interface NotificationRequest {
|
|
62
|
-
/**
|
|
63
|
-
* Caller-supplied, stable, derived from the caller's own data.
|
|
64
|
-
* The package never generates, normalizes, or hashes it.
|
|
65
|
-
*/
|
|
66
|
-
readonly id: string;
|
|
67
|
-
readonly content: NotificationContent;
|
|
68
|
-
/** Must reference a tone declared in the package configuration. */
|
|
69
|
-
readonly tone: string;
|
|
70
|
-
readonly moment: DeliveryMoment;
|
|
71
|
-
readonly routing?: RoutingPayload;
|
|
72
|
-
readonly actions?: readonly Action[];
|
|
73
|
-
/** Opaque partition label. Never interpreted by the package. */
|
|
74
|
-
readonly ownerTag?: string;
|
|
75
|
-
/** Opaque cancellation group. Never interpreted by the package. */
|
|
76
|
-
readonly groupTag?: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** One entry the platform is currently holding, in enough detail to diff against intent. */
|
|
80
|
-
export interface PendingEntry {
|
|
81
|
-
readonly id: string;
|
|
82
|
-
/** The instant the platform will actually fire, after anchoring resolution. */
|
|
83
|
-
readonly resolvedAt: number;
|
|
84
|
-
readonly anchoring: AnchoringMode;
|
|
85
|
-
/**
|
|
86
|
-
* The IANA zone this entry was last anchored against.
|
|
87
|
-
* `undefined` for absolute-instant entries, which are never anchored.
|
|
88
|
-
*
|
|
89
|
-
* Exists so the one case re-anchoring cannot fix — a zone change while the
|
|
90
|
-
* app never ran — is detectable rather than invisible.
|
|
91
|
-
*/
|
|
92
|
-
readonly anchoringGeneration?: string;
|
|
93
|
-
readonly content: NotificationContent;
|
|
94
|
-
readonly tone: string;
|
|
95
|
-
readonly routing?: RoutingPayload;
|
|
96
|
-
readonly actions?: readonly Action[];
|
|
97
|
-
readonly ownerTag?: string;
|
|
98
|
-
readonly groupTag?: string;
|
|
99
|
-
/** Present when this entry was expanded from a recurrence series. */
|
|
100
|
-
readonly seriesId?: string;
|
|
101
|
-
/** True when scheduled without exact-alarm capability; delivery may be late. */
|
|
102
|
-
readonly mayBeDelayed: boolean;
|
|
103
|
-
/** True when created by a push instruction rather than by app code. */
|
|
104
|
-
readonly pushCreated: boolean;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Outcome of a single schedule call. */
|
|
108
|
-
export interface ScheduleResult {
|
|
109
|
-
readonly id: string;
|
|
110
|
-
readonly resolvedAt: number;
|
|
111
|
-
readonly anchoring: AnchoringMode;
|
|
112
|
-
/** True when an existing notification under this identifier was replaced. */
|
|
113
|
-
readonly replaced: boolean;
|
|
114
|
-
/** True when the moment had just passed and was delivered inside the tolerance window. */
|
|
115
|
-
readonly deliveredImmediately: boolean;
|
|
116
|
-
/** True when scheduled in approximate mode for want of exact-alarm capability. */
|
|
117
|
-
readonly mayBeDelayed: boolean;
|
|
118
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Core notification request types.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here carries business meaning. The package accepts fully-described
|
|
5
|
+
* requests and is responsible only for delivering them at the right moment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* How a delivery moment is pinned to the passage of time.
|
|
10
|
+
*
|
|
11
|
+
* - `wallClock` — the local clock time named. An 08:00 reminder stays 08:00
|
|
12
|
+
* after the user crosses a time zone. This is the default, because reminders
|
|
13
|
+
* are wall-clock things and a caller who does not choose should get what
|
|
14
|
+
* their users expect.
|
|
15
|
+
* - `absolute` — one fixed instant worldwide, unaffected by zone changes.
|
|
16
|
+
*/
|
|
17
|
+
export type AnchoringMode = 'wallClock' | 'absolute';
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_ANCHORING_MODE: AnchoringMode = 'wallClock';
|
|
20
|
+
|
|
21
|
+
/** When a notification should be presented, and how that moment is anchored. */
|
|
22
|
+
export interface DeliveryMoment {
|
|
23
|
+
/** Epoch milliseconds. Interpreted per {@link DeliveryMoment.anchoring}. */
|
|
24
|
+
readonly at: number;
|
|
25
|
+
/** Defaults to `wallClock` when omitted. */
|
|
26
|
+
readonly anchoring?: AnchoringMode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolved display strings. The package performs no translation, formatting,
|
|
31
|
+
* or locale resolution — only the consuming app knows its content model.
|
|
32
|
+
*/
|
|
33
|
+
export interface NotificationContent {
|
|
34
|
+
readonly title: string;
|
|
35
|
+
readonly body: string;
|
|
36
|
+
/** Optional short text shown in collapsed form on some platforms. */
|
|
37
|
+
readonly subtitle?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Structured data carried to the interaction handler, unchanged. */
|
|
41
|
+
export type RoutingPayload = Readonly<Record<string, unknown>>;
|
|
42
|
+
|
|
43
|
+
/** A button on a notification. */
|
|
44
|
+
export interface Action {
|
|
45
|
+
/** Stable action identifier, subject to the same charset rules as a notification id. */
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly title: string;
|
|
48
|
+
/**
|
|
49
|
+
* `foreground` brings the app up; `background` is handled without opening it,
|
|
50
|
+
* inside the platform's bounded execution window.
|
|
51
|
+
*/
|
|
52
|
+
readonly mode: 'foreground' | 'background';
|
|
53
|
+
/** When present, the action accepts short typed input. */
|
|
54
|
+
readonly input?: {
|
|
55
|
+
readonly placeholder?: string;
|
|
56
|
+
readonly submitLabel?: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The complete, self-contained description of one notification. */
|
|
61
|
+
export interface NotificationRequest {
|
|
62
|
+
/**
|
|
63
|
+
* Caller-supplied, stable, derived from the caller's own data.
|
|
64
|
+
* The package never generates, normalizes, or hashes it.
|
|
65
|
+
*/
|
|
66
|
+
readonly id: string;
|
|
67
|
+
readonly content: NotificationContent;
|
|
68
|
+
/** Must reference a tone declared in the package configuration. */
|
|
69
|
+
readonly tone: string;
|
|
70
|
+
readonly moment: DeliveryMoment;
|
|
71
|
+
readonly routing?: RoutingPayload;
|
|
72
|
+
readonly actions?: readonly Action[];
|
|
73
|
+
/** Opaque partition label. Never interpreted by the package. */
|
|
74
|
+
readonly ownerTag?: string;
|
|
75
|
+
/** Opaque cancellation group. Never interpreted by the package. */
|
|
76
|
+
readonly groupTag?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One entry the platform is currently holding, in enough detail to diff against intent. */
|
|
80
|
+
export interface PendingEntry {
|
|
81
|
+
readonly id: string;
|
|
82
|
+
/** The instant the platform will actually fire, after anchoring resolution. */
|
|
83
|
+
readonly resolvedAt: number;
|
|
84
|
+
readonly anchoring: AnchoringMode;
|
|
85
|
+
/**
|
|
86
|
+
* The IANA zone this entry was last anchored against.
|
|
87
|
+
* `undefined` for absolute-instant entries, which are never anchored.
|
|
88
|
+
*
|
|
89
|
+
* Exists so the one case re-anchoring cannot fix — a zone change while the
|
|
90
|
+
* app never ran — is detectable rather than invisible.
|
|
91
|
+
*/
|
|
92
|
+
readonly anchoringGeneration?: string;
|
|
93
|
+
readonly content: NotificationContent;
|
|
94
|
+
readonly tone: string;
|
|
95
|
+
readonly routing?: RoutingPayload;
|
|
96
|
+
readonly actions?: readonly Action[];
|
|
97
|
+
readonly ownerTag?: string;
|
|
98
|
+
readonly groupTag?: string;
|
|
99
|
+
/** Present when this entry was expanded from a recurrence series. */
|
|
100
|
+
readonly seriesId?: string;
|
|
101
|
+
/** True when scheduled without exact-alarm capability; delivery may be late. */
|
|
102
|
+
readonly mayBeDelayed: boolean;
|
|
103
|
+
/** True when created by a push instruction rather than by app code. */
|
|
104
|
+
readonly pushCreated: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Outcome of a single schedule call. */
|
|
108
|
+
export interface ScheduleResult {
|
|
109
|
+
readonly id: string;
|
|
110
|
+
readonly resolvedAt: number;
|
|
111
|
+
readonly anchoring: AnchoringMode;
|
|
112
|
+
/** True when an existing notification under this identifier was replaced. */
|
|
113
|
+
readonly replaced: boolean;
|
|
114
|
+
/** True when the moment had just passed and was delivered inside the tolerance window. */
|
|
115
|
+
readonly deliveredImmediately: boolean;
|
|
116
|
+
/** True when scheduled in approximate mode for want of exact-alarm capability. */
|
|
117
|
+
readonly mayBeDelayed: boolean;
|
|
118
|
+
}
|
|
@@ -1,60 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Permission types.
|
|
3
|
-
*
|
|
4
|
-
* Display permission and exact-alarm permission are different things serving
|
|
5
|
-
* different purposes, so they are requested and reported independently. State
|
|
6
|
-
* is always readable without triggering a dialog.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type PermissionKind = 'display' | 'exactAlarm';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Six states, never collapsed into one another.
|
|
13
|
-
*
|
|
14
|
-
* - `notAsked` — distinguishable from `denied`, which is the whole point of
|
|
15
|
-
* being able to read state without prompting.
|
|
16
|
-
* - `provisional` — iOS quiet delivery. Neither granted nor denied.
|
|
17
|
-
* - `notRequired` — the running platform version has no such concept. Never
|
|
18
|
-
* reported as `denied`, which would imply a refusal that never happened.
|
|
19
|
-
*/
|
|
20
|
-
export type PermissionState =
|
|
21
|
-
| '
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
readonly
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
readonly
|
|
55
|
-
|
|
56
|
-
* True when the platform grants this only through system settings rather
|
|
57
|
-
* than an in-app dialog.
|
|
58
|
-
*/
|
|
59
|
-
readonly settingsOnly: boolean;
|
|
60
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Permission types.
|
|
3
|
+
*
|
|
4
|
+
* Display permission and exact-alarm permission are different things serving
|
|
5
|
+
* different purposes, so they are requested and reported independently. State
|
|
6
|
+
* is always readable without triggering a dialog.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type PermissionKind = 'display' | 'exactAlarm';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Six states, never collapsed into one another.
|
|
13
|
+
*
|
|
14
|
+
* - `notAsked` — distinguishable from `denied`, which is the whole point of
|
|
15
|
+
* being able to read state without prompting.
|
|
16
|
+
* - `provisional` — iOS quiet delivery. Neither granted nor denied.
|
|
17
|
+
* - `notRequired` — the running platform version has no such concept. Never
|
|
18
|
+
* reported as `denied`, which would imply a refusal that never happened.
|
|
19
|
+
*/
|
|
20
|
+
export type PermissionState =
|
|
21
|
+
'notAsked' | 'granted' | 'denied' | 'permanentlyDenied' | 'provisional' | 'notRequired';
|
|
22
|
+
|
|
23
|
+
export interface PermissionReport {
|
|
24
|
+
readonly display: PermissionState;
|
|
25
|
+
readonly exactAlarm: PermissionState;
|
|
26
|
+
readonly readAt: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Whether a manufacturer-level restriction is in force, where it can be known. */
|
|
30
|
+
export type RestrictionStatus = 'unrestricted' | 'restricted' | 'unknown';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Manufacturer background restrictions, reported for information only.
|
|
34
|
+
*
|
|
35
|
+
* `unknown` is a first-class answer: some vendors stop apps in ways with no
|
|
36
|
+
* standard detection API, and guessing would be worse than admitting it.
|
|
37
|
+
* The package never changes any of these settings on the user's behalf.
|
|
38
|
+
*/
|
|
39
|
+
export interface RestrictionReport {
|
|
40
|
+
readonly batteryOptimization: RestrictionStatus;
|
|
41
|
+
readonly autoStart: RestrictionStatus;
|
|
42
|
+
readonly manufacturer?: string;
|
|
43
|
+
readonly readAt: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Whether a permission request can even be presented on this platform version. */
|
|
47
|
+
export interface PermissionCapability {
|
|
48
|
+
readonly kind: PermissionKind;
|
|
49
|
+
readonly supported: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* True when the platform grants this only through system settings rather
|
|
52
|
+
* than an in-app dialog.
|
|
53
|
+
*/
|
|
54
|
+
readonly settingsOnly: boolean;
|
|
55
|
+
}
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reconciliation types.
|
|
3
|
-
*
|
|
4
|
-
* A caller hands over the complete set its data currently implies; the package
|
|
5
|
-
* computes the minimal operation set that makes the device match. Running it
|
|
6
|
-
* twice with unchanged input performs zero platform operations — that
|
|
7
|
-
* idempotence is the package's central testable promise.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { NotificationRequest } from './notification';
|
|
11
|
-
|
|
12
|
-
export interface ReconcileInput {
|
|
13
|
-
/**
|
|
14
|
-
* Bounds what may be cancelled. Notifications outside this domain are never
|
|
15
|
-
* touched, including those scheduled by other parts of the app or by other
|
|
16
|
-
* libraries.
|
|
17
|
-
*/
|
|
18
|
-
readonly domain: string;
|
|
19
|
-
readonly desired: readonly NotificationRequest[];
|
|
20
|
-
/**
|
|
21
|
-
* When false (the default), push-created notifications are left alone rather
|
|
22
|
-
* than cancelled for being absent from a locally-computed desired set.
|
|
23
|
-
*/
|
|
24
|
-
readonly includePushCreated?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Why a desired entry was refused rather than applied. */
|
|
28
|
-
export interface RejectedEntry {
|
|
29
|
-
readonly id: string;
|
|
30
|
-
readonly code: string;
|
|
31
|
-
readonly reason: string;
|
|
32
|
-
/** How many times this identifier appeared in the desired set. */
|
|
33
|
-
readonly occurrences: number;
|
|
34
|
-
/**
|
|
35
|
-
* True when a notification already scheduled under this identifier was left
|
|
36
|
-
* in place. Stated explicitly so a rejection is never misread as a
|
|
37
|
-
* cancellation.
|
|
38
|
-
*/
|
|
39
|
-
readonly existingLeftInPlace: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** What was dropped for exceeding the platform's pending ceiling. */
|
|
43
|
-
export interface TruncationReport {
|
|
44
|
-
readonly ceiling: number;
|
|
45
|
-
readonly droppedCount: number;
|
|
46
|
-
readonly droppedIds: readonly string[];
|
|
47
|
-
/**
|
|
48
|
-
* Dropped entries are not lost — they are re-evaluated at each window
|
|
49
|
-
* refresh as nearer entries deliver.
|
|
50
|
-
*/
|
|
51
|
-
readonly reEvaluatedOnNextRefresh: true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ReconcileResult {
|
|
55
|
-
readonly domain: string;
|
|
56
|
-
readonly created: readonly string[];
|
|
57
|
-
readonly replaced: readonly string[];
|
|
58
|
-
readonly cancelled: readonly string[];
|
|
59
|
-
readonly unchanged: readonly string[];
|
|
60
|
-
readonly rejected: readonly RejectedEntry[];
|
|
61
|
-
readonly truncated?: TruncationReport;
|
|
62
|
-
/** Total platform operations issued. Zero on a no-change pass. */
|
|
63
|
-
readonly operationCount: number;
|
|
64
|
-
readonly durationMs: number;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** A discrepancy between what the package expects and what the platform holds. */
|
|
68
|
-
export interface PendingDiscrepancy {
|
|
69
|
-
/** Expected from the series ledger or last reconciliation, but absent. */
|
|
70
|
-
readonly missing: readonly string[];
|
|
71
|
-
/** Present on the platform but not recognized by the package. */
|
|
72
|
-
readonly unrecognized: readonly string[];
|
|
73
|
-
readonly detectedAt: number;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Outcome of one automatic maintenance cycle. */
|
|
77
|
-
export interface MaintenanceResult {
|
|
78
|
-
readonly ranAt: number;
|
|
79
|
-
readonly windowRefreshed: boolean;
|
|
80
|
-
readonly occurrencesScheduled: number;
|
|
81
|
-
readonly reAnchored: number;
|
|
82
|
-
readonly timeZoneChanged: boolean;
|
|
83
|
-
readonly permissionChanged: boolean;
|
|
84
|
-
/** Detected and reported only — never repaired. */
|
|
85
|
-
readonly discrepancy?: PendingDiscrepancy;
|
|
86
|
-
readonly operationCount: number;
|
|
87
|
-
readonly durationMs: number;
|
|
88
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Reconciliation types.
|
|
3
|
+
*
|
|
4
|
+
* A caller hands over the complete set its data currently implies; the package
|
|
5
|
+
* computes the minimal operation set that makes the device match. Running it
|
|
6
|
+
* twice with unchanged input performs zero platform operations — that
|
|
7
|
+
* idempotence is the package's central testable promise.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { NotificationRequest } from './notification';
|
|
11
|
+
|
|
12
|
+
export interface ReconcileInput {
|
|
13
|
+
/**
|
|
14
|
+
* Bounds what may be cancelled. Notifications outside this domain are never
|
|
15
|
+
* touched, including those scheduled by other parts of the app or by other
|
|
16
|
+
* libraries.
|
|
17
|
+
*/
|
|
18
|
+
readonly domain: string;
|
|
19
|
+
readonly desired: readonly NotificationRequest[];
|
|
20
|
+
/**
|
|
21
|
+
* When false (the default), push-created notifications are left alone rather
|
|
22
|
+
* than cancelled for being absent from a locally-computed desired set.
|
|
23
|
+
*/
|
|
24
|
+
readonly includePushCreated?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Why a desired entry was refused rather than applied. */
|
|
28
|
+
export interface RejectedEntry {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly code: string;
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
/** How many times this identifier appeared in the desired set. */
|
|
33
|
+
readonly occurrences: number;
|
|
34
|
+
/**
|
|
35
|
+
* True when a notification already scheduled under this identifier was left
|
|
36
|
+
* in place. Stated explicitly so a rejection is never misread as a
|
|
37
|
+
* cancellation.
|
|
38
|
+
*/
|
|
39
|
+
readonly existingLeftInPlace: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What was dropped for exceeding the platform's pending ceiling. */
|
|
43
|
+
export interface TruncationReport {
|
|
44
|
+
readonly ceiling: number;
|
|
45
|
+
readonly droppedCount: number;
|
|
46
|
+
readonly droppedIds: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* Dropped entries are not lost — they are re-evaluated at each window
|
|
49
|
+
* refresh as nearer entries deliver.
|
|
50
|
+
*/
|
|
51
|
+
readonly reEvaluatedOnNextRefresh: true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ReconcileResult {
|
|
55
|
+
readonly domain: string;
|
|
56
|
+
readonly created: readonly string[];
|
|
57
|
+
readonly replaced: readonly string[];
|
|
58
|
+
readonly cancelled: readonly string[];
|
|
59
|
+
readonly unchanged: readonly string[];
|
|
60
|
+
readonly rejected: readonly RejectedEntry[];
|
|
61
|
+
readonly truncated?: TruncationReport;
|
|
62
|
+
/** Total platform operations issued. Zero on a no-change pass. */
|
|
63
|
+
readonly operationCount: number;
|
|
64
|
+
readonly durationMs: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A discrepancy between what the package expects and what the platform holds. */
|
|
68
|
+
export interface PendingDiscrepancy {
|
|
69
|
+
/** Expected from the series ledger or last reconciliation, but absent. */
|
|
70
|
+
readonly missing: readonly string[];
|
|
71
|
+
/** Present on the platform but not recognized by the package. */
|
|
72
|
+
readonly unrecognized: readonly string[];
|
|
73
|
+
readonly detectedAt: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Outcome of one automatic maintenance cycle. */
|
|
77
|
+
export interface MaintenanceResult {
|
|
78
|
+
readonly ranAt: number;
|
|
79
|
+
readonly windowRefreshed: boolean;
|
|
80
|
+
readonly occurrencesScheduled: number;
|
|
81
|
+
readonly reAnchored: number;
|
|
82
|
+
readonly timeZoneChanged: boolean;
|
|
83
|
+
readonly permissionChanged: boolean;
|
|
84
|
+
/** Detected and reported only — never repaired. */
|
|
85
|
+
readonly discrepancy?: PendingDiscrepancy;
|
|
86
|
+
readonly operationCount: number;
|
|
87
|
+
readonly durationMs: number;
|
|
88
|
+
}
|