@flighthq/lifecycle 0.5.1-next.1069.554a2ac → 0.5.1-next.1201.9b49dee
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/README.md +2 -2
- package/dist/lifecycle.d.ts +11 -11
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +31 -35
- package/dist/lifecycle.js.map +1 -1
- package/package.json +4 -4
- package/src/lifecycle.test.ts +45 -44
- package/src/lifecycleHost.test.ts +22 -16
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/lifecycle`. The `@fl
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/lifecycle@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/lifecycle@0.5.1-next.1201.9b49dee](https://github.com/flighthq/flight/tree/9b49dee3f22f6b2838c3d237edcc77bca65734f2/packages/lifecycle)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/9b49dee3f22f6b2838c3d237edcc77bca65734f2/LICENSE.md)
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { BackendOperationExplanation, LifecycleOperation, EntityConstruction } from '@flighthq/types/contract';
|
|
2
|
-
import type { AppLaunchKind,
|
|
3
|
-
export declare function attachAppLifecycle(
|
|
2
|
+
import type { AppLaunchKind, HostLifecycleProvider, AppLifecycle, AppLifecycleState } from '@flighthq/types/contract';
|
|
3
|
+
export declare function attachAppLifecycle(hostLifecycle: Readonly<HostLifecycleProvider>, app: AppLifecycle): void;
|
|
4
4
|
export declare function createAppLifecycle(): AppLifecycle;
|
|
5
|
-
export declare function createWebLifecycleBackend():
|
|
5
|
+
export declare function createWebLifecycleBackend(): HostLifecycleProvider;
|
|
6
6
|
export declare function detachAppLifecycle(app: AppLifecycle): void;
|
|
7
7
|
export declare function disposeAppLifecycle(app: AppLifecycle): void;
|
|
8
|
-
export declare function explainLifecycleOperation(
|
|
9
|
-
export declare function getAppLaunchKind(
|
|
10
|
-
export declare function getAppLifecycleState(
|
|
11
|
-
export declare function hasLifecycleOperation(
|
|
8
|
+
export declare function explainLifecycleOperation(hostLifecycle: Readonly<HostLifecycleProvider>, operation: LifecycleOperation): BackendOperationExplanation;
|
|
9
|
+
export declare function getAppLaunchKind(hostLifecycle: Readonly<HostLifecycleProvider>): AppLaunchKind;
|
|
10
|
+
export declare function getAppLifecycleState(hostLifecycle: Readonly<HostLifecycleProvider>): AppLifecycleState;
|
|
11
|
+
export declare function hasLifecycleOperation(hostLifecycle: Readonly<HostLifecycleProvider>, operation: LifecycleOperation): boolean;
|
|
12
12
|
export declare function initializeAppLifecycle(out: EntityConstruction<AppLifecycle>): void;
|
|
13
|
-
export declare function initializeWebLifecycleBackend(out: EntityConstruction<
|
|
14
|
-
export declare function isAppActive(
|
|
15
|
-
export declare function isAppBackground(
|
|
16
|
-
export declare function isAppInactive(
|
|
13
|
+
export declare function initializeWebLifecycleBackend(out: EntityConstruction<HostLifecycleProvider>): void;
|
|
14
|
+
export declare function isAppActive(hostLifecycle: Readonly<HostLifecycleProvider>): boolean;
|
|
15
|
+
export declare function isAppBackground(hostLifecycle: Readonly<HostLifecycleProvider>): boolean;
|
|
16
|
+
export declare function isAppInactive(hostLifecycle: Readonly<HostLifecycleProvider>): boolean;
|
|
17
17
|
export declare function requestAppBack(app: AppLifecycle): boolean;
|
|
18
18
|
//# sourceMappingURL=lifecycle.d.ts.map
|
package/dist/lifecycle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,KAAK,EACV,aAAa,EACb,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EAElB,MAAM,0BAA0B,CAAC;AAYlC,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,GAAG,EAAE,YAAY,GAAG,IAAI,CAqC1G;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAIjD;AAED,wBAAgB,yBAAyB,IAAI,qBAAqB,CAIjE;AAGD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAM1D;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAG3D;AAID,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EAC9C,SAAS,EAAE,kBAAkB,GAC5B,2BAA2B,CAG7B;AAMD,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,aAAa,CAG9F;AAGD,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,iBAAiB,CAEtG;AAGD,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EAC9C,SAAS,EAAE,kBAAkB,GAC5B,OAAO,CAET;AAGD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,CAAC,YAAY,CAAC,GAAG,IAAI,CAQlF;AAmBD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,kBAAkB,CAAC,qBAAqB,CAAC,GAAG,IAAI,CA0ElG;AAGD,wBAAgB,WAAW,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAEnF;AAGD,wBAAgB,eAAe,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAEvF;AAID,wBAAgB,aAAa,CAAC,aAAa,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAErF;AAOD,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAGzD"}
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { createSignal, emitSignal } from '@flighthq/signals/contract';
|
|
3
|
-
// Begins delivering lifecycle changes to `app`'s signals by subscribing to the
|
|
3
|
+
// Begins delivering lifecycle changes to `app`'s signals by subscribing to the supplied provider. On
|
|
4
4
|
// each change it reads the current state and emits onStateChange plus onResume when transitioning
|
|
5
|
-
// to 'active' and onPause when leaving 'active'. The
|
|
6
|
-
// environments that support it; native hosts emit it through their own
|
|
5
|
+
// to 'active' and onPause when leaving 'active'. The Web provider drives onBackButton only in
|
|
6
|
+
// environments that support it; native hosts emit it through their own provider. Idempotent: a prior
|
|
7
7
|
// subscription is torn down first. Pair with detachAppLifecycle/disposeAppLifecycle.
|
|
8
8
|
//
|
|
9
|
-
// onStateChange is "raw, not deduped" — it fires on every
|
|
9
|
+
// onStateChange is "raw, not deduped" — it fires on every provider notification regardless of
|
|
10
10
|
// whether the derived state changed. onResume/onPause are deduped edges: 'active'→non-'active'
|
|
11
11
|
// fires onPause (including the interruption edge 'active'→'inactive'); non-'active'→'active' fires
|
|
12
12
|
// onResume. The 'inactive'→'background' and reverse transitions do not fire onPause/onResume again.
|
|
13
|
-
export function attachAppLifecycle(
|
|
13
|
+
export function attachAppLifecycle(hostLifecycle, app) {
|
|
14
14
|
detachAppLifecycle(app);
|
|
15
|
-
const backend =
|
|
15
|
+
const backend = hostLifecycle;
|
|
16
16
|
let previous = backend.getState();
|
|
17
17
|
const unsubscribeState = backend.subscribe(() => {
|
|
18
18
|
const state = backend.getState();
|
|
@@ -64,37 +64,33 @@ export function detachAppLifecycle(app) {
|
|
|
64
64
|
_subscriptions.delete(app);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
// Releases `app` for garbage collection by detaching its
|
|
67
|
+
// Releases `app` for garbage collection by detaching its provider subscription. The signals remain
|
|
68
68
|
// plain GC-managed memory afterward.
|
|
69
69
|
export function disposeAppLifecycle(app) {
|
|
70
70
|
detachAppLifecycle(app);
|
|
71
71
|
_savedState.delete(app);
|
|
72
72
|
}
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// ★ The sentinel is deliberately not consulted. It answers every operation, so counting it would make
|
|
78
|
-
// this report `true` for everything and say nothing at all.
|
|
79
|
-
export function explainLifecycleOperation(host, operation) {
|
|
80
|
-
const implemented = typeof host.system.lifecycle[operation] === 'function';
|
|
73
|
+
// Reports whether the supplied provider implements `operation`. Optional methods that the provider
|
|
74
|
+
// omits are reported as the sentinel layer so callers can distinguish absence from a real operation.
|
|
75
|
+
export function explainLifecycleOperation(hostLifecycle, operation) {
|
|
76
|
+
const implemented = typeof hostLifecycle[operation] === 'function';
|
|
81
77
|
return { implemented, layer: implemented ? 'host' : 'sentinel', operation };
|
|
82
78
|
}
|
|
83
79
|
// Returns the kind of launch — 'cold' (fresh process) or 'warm' (resumed from background). The web
|
|
84
|
-
//
|
|
85
|
-
// others → 'cold'). Returns 'warm' as a safe fallback when the
|
|
86
|
-
// getLaunchKind (legacy or minimal
|
|
87
|
-
export function getAppLaunchKind(
|
|
88
|
-
const backend =
|
|
80
|
+
// provider approximates this via PerformanceNavigationTiming.type ('back_forward' → 'warm', all
|
|
81
|
+
// others → 'cold'). Returns 'warm' as a safe fallback when the provider does not implement
|
|
82
|
+
// getLaunchKind (legacy or minimal providers that pre-date the optional method).
|
|
83
|
+
export function getAppLaunchKind(hostLifecycle) {
|
|
84
|
+
const backend = hostLifecycle;
|
|
89
85
|
return backend.getLaunchKind !== undefined ? backend.getLaunchKind() : 'warm';
|
|
90
86
|
}
|
|
91
|
-
// Returns the current application lifecycle state from the
|
|
92
|
-
export function getAppLifecycleState(
|
|
93
|
-
return
|
|
87
|
+
// Returns the current application lifecycle state from the supplied provider.
|
|
88
|
+
export function getAppLifecycleState(hostLifecycle) {
|
|
89
|
+
return hostLifecycle.getState();
|
|
94
90
|
}
|
|
95
|
-
// Whether
|
|
96
|
-
export function hasLifecycleOperation(
|
|
97
|
-
return explainLifecycleOperation(
|
|
91
|
+
// Whether the supplied provider implements `operation`.
|
|
92
|
+
export function hasLifecycleOperation(hostLifecycle, operation) {
|
|
93
|
+
return explainLifecycleOperation(hostLifecycle, operation).implemented;
|
|
98
94
|
}
|
|
99
95
|
// Allocates an AppLifecycle event entity with inert signals; call attachAppLifecycle to start delivery.
|
|
100
96
|
export function initializeAppLifecycle(out) {
|
|
@@ -106,7 +102,7 @@ export function initializeAppLifecycle(out) {
|
|
|
106
102
|
out.onSaveState = createSignal();
|
|
107
103
|
out.onRestoreState = createSignal();
|
|
108
104
|
}
|
|
109
|
-
// Builds the default
|
|
105
|
+
// Builds the default Web provider over document visibility, window focus/blur, and pagehide/pageshow
|
|
110
106
|
// events. Produces three states:
|
|
111
107
|
// 'active' — document visible and window focused
|
|
112
108
|
// 'inactive' — document visible but window not focused (app switcher, control-center, other window)
|
|
@@ -120,7 +116,7 @@ export function initializeAppLifecycle(out) {
|
|
|
120
116
|
// Falls back to 'cold' when the Performance Navigation Timing API is unavailable (SSR/jsdom).
|
|
121
117
|
//
|
|
122
118
|
// subscribeMemoryWarning() wires the experimental 'memory-pressure' window event (Chrome origin
|
|
123
|
-
// trial / behind flags). The event detail carries a 'critical' pressure string; this
|
|
119
|
+
// trial / behind flags). The event detail carries a 'critical' pressure string; this provider maps
|
|
124
120
|
// it to 'critical' and fires 'normal' on the subsequent resolution event when present. Falls back to
|
|
125
121
|
// no-op unsubscribe when the event is not supported (no standard API is widely deployed as of 2026).
|
|
126
122
|
export function initializeWebLifecycleBackend(out) {
|
|
@@ -172,7 +168,7 @@ export function initializeWebLifecycleBackend(out) {
|
|
|
172
168
|
};
|
|
173
169
|
out.subscribeMemoryWarning = (listener) => {
|
|
174
170
|
// The 'memory-pressure' window event is an experimental Chrome API (origin trial / behind
|
|
175
|
-
// flags as of 2026). The event carries a detail object with a 'pressure' string. This
|
|
171
|
+
// flags as of 2026). The event carries a detail object with a 'pressure' string. This provider
|
|
176
172
|
// maps 'critical' pressure to AppMemoryPressure 'critical'; a pressure-resolved / 'moderate'
|
|
177
173
|
// event maps to 'moderate'. Falls back to no-op unsubscribe when the event is not supported
|
|
178
174
|
// (no cross-browser API for memory pressure is widely deployed yet).
|
|
@@ -207,17 +203,17 @@ export function initializeWebLifecycleBackend(out) {
|
|
|
207
203
|
};
|
|
208
204
|
}
|
|
209
205
|
// Returns true when the application is in the 'active' state (visible and focused).
|
|
210
|
-
export function isAppActive(
|
|
211
|
-
return
|
|
206
|
+
export function isAppActive(hostLifecycle) {
|
|
207
|
+
return hostLifecycle.getState() === 'active';
|
|
212
208
|
}
|
|
213
209
|
// Returns true when the application is in the 'background' state (hidden/suspended).
|
|
214
|
-
export function isAppBackground(
|
|
215
|
-
return
|
|
210
|
+
export function isAppBackground(hostLifecycle) {
|
|
211
|
+
return hostLifecycle.getState() === 'background';
|
|
216
212
|
}
|
|
217
213
|
// Returns true when the application is in the 'inactive' state (visible but not focused —
|
|
218
214
|
// e.g. app switcher, control-center overlay, incoming call).
|
|
219
|
-
export function isAppInactive(
|
|
220
|
-
return
|
|
215
|
+
export function isAppInactive(hostLifecycle) {
|
|
216
|
+
return hostLifecycle.getState() === 'inactive';
|
|
221
217
|
}
|
|
222
218
|
// Emits app.onBackButton and returns whether the back action may proceed. Returns false when a
|
|
223
219
|
// listener vetoed by calling cancelSignal(app.onBackButton), meaning the listener handled
|
package/dist/lifecycle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/lifecycle",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.1201.9b49dee",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.5.1-next.
|
|
42
|
-
"@flighthq/signals": "0.5.1-next.
|
|
43
|
-
"@flighthq/types": "0.5.1-next.
|
|
41
|
+
"@flighthq/entity": "0.5.1-next.1201.9b49dee",
|
|
42
|
+
"@flighthq/signals": "0.5.1-next.1201.9b49dee",
|
|
43
|
+
"@flighthq/types": "0.5.1-next.1201.9b49dee"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
package/src/lifecycle.test.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { cancelSignal, connectSignal } from '@flighthq/signals/contract';
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
HostLifecycleProvider,
|
|
5
5
|
AppLaunchKind,
|
|
6
6
|
AppLifecycleState,
|
|
7
7
|
AppMemoryPressure,
|
|
8
8
|
Entity,
|
|
9
|
-
LifecycleBackend,
|
|
10
9
|
LifecycleOperation,
|
|
11
10
|
} from '@flighthq/types/contract';
|
|
12
11
|
|
|
@@ -28,14 +27,14 @@ import {
|
|
|
28
27
|
requestAppBack,
|
|
29
28
|
} from './lifecycle';
|
|
30
29
|
|
|
31
|
-
type FakeBackend =
|
|
30
|
+
type FakeBackend = HostLifecycleProvider & {
|
|
32
31
|
state: AppLifecycleState;
|
|
33
32
|
fire: () => void;
|
|
34
33
|
fireMemory: (level: AppMemoryPressure) => void;
|
|
35
34
|
};
|
|
36
35
|
|
|
37
|
-
function hostOf(backend:
|
|
38
|
-
return { system: { lifecycle: backend } } as
|
|
36
|
+
function hostOf(backend: HostLifecycleProvider): { readonly system: { readonly lifecycle: HostLifecycleProvider } } {
|
|
37
|
+
return { system: { lifecycle: backend } } as { readonly system: { readonly lifecycle: HostLifecycleProvider } };
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
function fakeBackend(): FakeBackend {
|
|
@@ -76,7 +75,7 @@ describe('attachAppLifecycle', () => {
|
|
|
76
75
|
let pauses = 0;
|
|
77
76
|
connectSignal(app.onStateChange, () => changes++);
|
|
78
77
|
connectSignal(app.onPause, () => pauses++);
|
|
79
|
-
attachAppLifecycle(host, app);
|
|
78
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
80
79
|
backend.state = 'background';
|
|
81
80
|
backend.fire();
|
|
82
81
|
expect(changes).toBe(1);
|
|
@@ -89,7 +88,7 @@ describe('attachAppLifecycle', () => {
|
|
|
89
88
|
const app = createAppLifecycle();
|
|
90
89
|
let pauses = 0;
|
|
91
90
|
connectSignal(app.onPause, () => pauses++);
|
|
92
|
-
attachAppLifecycle(host, app);
|
|
91
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
93
92
|
backend.state = 'inactive';
|
|
94
93
|
backend.fire();
|
|
95
94
|
expect(pauses).toBe(1);
|
|
@@ -102,7 +101,7 @@ describe('attachAppLifecycle', () => {
|
|
|
102
101
|
const app = createAppLifecycle();
|
|
103
102
|
let pauses = 0;
|
|
104
103
|
connectSignal(app.onPause, () => pauses++);
|
|
105
|
-
attachAppLifecycle(host, app);
|
|
104
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
106
105
|
backend.state = 'background';
|
|
107
106
|
backend.fire();
|
|
108
107
|
// inactive → background should not re-fire onPause (already paused)
|
|
@@ -116,7 +115,7 @@ describe('attachAppLifecycle', () => {
|
|
|
116
115
|
const app = createAppLifecycle();
|
|
117
116
|
let resumes = 0;
|
|
118
117
|
connectSignal(app.onResume, () => resumes++);
|
|
119
|
-
attachAppLifecycle(host, app);
|
|
118
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
120
119
|
backend.state = 'active';
|
|
121
120
|
backend.fire();
|
|
122
121
|
expect(resumes).toBe(1);
|
|
@@ -129,7 +128,7 @@ describe('attachAppLifecycle', () => {
|
|
|
129
128
|
const app = createAppLifecycle();
|
|
130
129
|
let resumes = 0;
|
|
131
130
|
connectSignal(app.onResume, () => resumes++);
|
|
132
|
-
attachAppLifecycle(host, app);
|
|
131
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
133
132
|
backend.state = 'active';
|
|
134
133
|
backend.fire();
|
|
135
134
|
expect(resumes).toBe(1);
|
|
@@ -141,8 +140,8 @@ describe('attachAppLifecycle', () => {
|
|
|
141
140
|
const app = createAppLifecycle();
|
|
142
141
|
let changes = 0;
|
|
143
142
|
connectSignal(app.onStateChange, () => changes++);
|
|
144
|
-
attachAppLifecycle(host, app);
|
|
145
|
-
attachAppLifecycle(host, app);
|
|
143
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
144
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
146
145
|
backend.state = 'background';
|
|
147
146
|
backend.fire();
|
|
148
147
|
// Only one subscription should be active.
|
|
@@ -158,7 +157,7 @@ describe('attachAppLifecycle', () => {
|
|
|
158
157
|
bag['key'] = 'value';
|
|
159
158
|
saved = true;
|
|
160
159
|
});
|
|
161
|
-
attachAppLifecycle(host, app);
|
|
160
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
162
161
|
backend.state = 'background';
|
|
163
162
|
backend.fire();
|
|
164
163
|
expect(saved).toBe(true);
|
|
@@ -175,7 +174,7 @@ describe('attachAppLifecycle', () => {
|
|
|
175
174
|
connectSignal(app.onRestoreState, (state) => {
|
|
176
175
|
restored = state;
|
|
177
176
|
});
|
|
178
|
-
attachAppLifecycle(host, app);
|
|
177
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
179
178
|
// Transition away to trigger save.
|
|
180
179
|
backend.state = 'background';
|
|
181
180
|
backend.fire();
|
|
@@ -192,7 +191,7 @@ describe('attachAppLifecycle', () => {
|
|
|
192
191
|
const app = createAppLifecycle();
|
|
193
192
|
const levels: AppMemoryPressure[] = [];
|
|
194
193
|
connectSignal(app.onMemoryWarning, (level) => levels.push(level));
|
|
195
|
-
attachAppLifecycle(host, app);
|
|
194
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
196
195
|
backend.fireMemory('critical');
|
|
197
196
|
expect(levels).toEqual(['critical']);
|
|
198
197
|
});
|
|
@@ -210,7 +209,7 @@ describe('attachAppLifecycle', () => {
|
|
|
210
209
|
const app = createAppLifecycle();
|
|
211
210
|
let changes = 0;
|
|
212
211
|
connectSignal(app.onStateChange, () => changes++);
|
|
213
|
-
attachAppLifecycle(host, app);
|
|
212
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
214
213
|
const fireCount = 1 + Math.floor(Math.random() * 30);
|
|
215
214
|
for (let i = 0; i < fireCount; i++) {
|
|
216
215
|
backend.state = states[Math.floor(Math.random() * states.length)];
|
|
@@ -234,7 +233,7 @@ describe('attachAppLifecycle', () => {
|
|
|
234
233
|
let resumes = 0;
|
|
235
234
|
connectSignal(app.onPause, () => pauses++);
|
|
236
235
|
connectSignal(app.onResume, () => resumes++);
|
|
237
|
-
attachAppLifecycle(host, app);
|
|
236
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
238
237
|
|
|
239
238
|
// Drive a random storm while computing the expected edge counts from an independent model:
|
|
240
239
|
// a pause edge is every 'active' → non-'active' transition, a resume edge every
|
|
@@ -272,7 +271,7 @@ describe('attachAppLifecycle', () => {
|
|
|
272
271
|
connectSignal(app.onStateChange, () => changes++);
|
|
273
272
|
connectSignal(app.onPause, () => pauses++);
|
|
274
273
|
connectSignal(app.onResume, () => resumes++);
|
|
275
|
-
attachAppLifecycle(host, app);
|
|
274
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
276
275
|
const fireCount = 1 + Math.floor(Math.random() * 30);
|
|
277
276
|
for (let i = 0; i < fireCount; i++) backend.fire();
|
|
278
277
|
expect(changes).toBe(fireCount);
|
|
@@ -297,7 +296,7 @@ describe('attachAppLifecycle', () => {
|
|
|
297
296
|
let resumes = 0;
|
|
298
297
|
connectSignal(app.onPause, () => pauses++);
|
|
299
298
|
connectSignal(app.onResume, () => resumes++);
|
|
300
|
-
attachAppLifecycle(host, app);
|
|
299
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
301
300
|
const fireCount = 1 + Math.floor(Math.random() * 30);
|
|
302
301
|
for (let i = 0; i < fireCount; i++) {
|
|
303
302
|
backend.state = states[Math.floor(Math.random() * states.length)];
|
|
@@ -411,7 +410,7 @@ describe('detachAppLifecycle', () => {
|
|
|
411
410
|
const app = createAppLifecycle();
|
|
412
411
|
let changes = 0;
|
|
413
412
|
connectSignal(app.onStateChange, () => changes++);
|
|
414
|
-
attachAppLifecycle(host, app);
|
|
413
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
415
414
|
detachAppLifecycle(app);
|
|
416
415
|
backend.fire();
|
|
417
416
|
expect(changes).toBe(0);
|
|
@@ -428,7 +427,7 @@ describe('disposeAppLifecycle', () => {
|
|
|
428
427
|
const backend = fakeBackend();
|
|
429
428
|
const host = hostOf(backend);
|
|
430
429
|
const app = createAppLifecycle();
|
|
431
|
-
attachAppLifecycle(host, app);
|
|
430
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
432
431
|
expect(() => disposeAppLifecycle(app)).not.toThrow();
|
|
433
432
|
});
|
|
434
433
|
|
|
@@ -438,7 +437,7 @@ describe('disposeAppLifecycle', () => {
|
|
|
438
437
|
const app = createAppLifecycle();
|
|
439
438
|
let changes = 0;
|
|
440
439
|
connectSignal(app.onStateChange, () => changes++);
|
|
441
|
-
attachAppLifecycle(host, app);
|
|
440
|
+
attachAppLifecycle(host.system.lifecycle, app);
|
|
442
441
|
disposeAppLifecycle(app);
|
|
443
442
|
backend.fire();
|
|
444
443
|
expect(changes).toBe(0);
|
|
@@ -451,7 +450,7 @@ describe('explainLifecycleOperation', () => {
|
|
|
451
450
|
// about a process-wide fallback that answered everything.
|
|
452
451
|
it('reports the host provider as implementing what it provides', () => {
|
|
453
452
|
const host = hostOf(fakeBackend());
|
|
454
|
-
expect(explainLifecycleOperation(host, 'getState')).toEqual({
|
|
453
|
+
expect(explainLifecycleOperation(host.system.lifecycle, 'getState')).toEqual({
|
|
455
454
|
implemented: true,
|
|
456
455
|
layer: 'host',
|
|
457
456
|
operation: 'getState',
|
|
@@ -461,13 +460,13 @@ describe('explainLifecycleOperation', () => {
|
|
|
461
460
|
it('reports an operation the host provider omits as unimplemented', () => {
|
|
462
461
|
const host = hostOf(
|
|
463
462
|
(() => {
|
|
464
|
-
const out = allocateEntity<
|
|
463
|
+
const out = allocateEntity<HostLifecycleProvider>();
|
|
465
464
|
out.getState = () => 'active' as const;
|
|
466
465
|
out.subscribe = () => () => {};
|
|
467
466
|
return finishEntity(out);
|
|
468
467
|
})(),
|
|
469
468
|
);
|
|
470
|
-
expect(explainLifecycleOperation(host, 'getLaunchKind')).toEqual({
|
|
469
|
+
expect(explainLifecycleOperation(host.system.lifecycle, 'getLaunchKind')).toEqual({
|
|
471
470
|
implemented: false,
|
|
472
471
|
layer: 'sentinel',
|
|
473
472
|
operation: 'getLaunchKind',
|
|
@@ -480,14 +479,14 @@ describe('explainLifecycleOperation', () => {
|
|
|
480
479
|
const rich = hostOf(fakeBackend());
|
|
481
480
|
const bare = hostOf(
|
|
482
481
|
(() => {
|
|
483
|
-
const out = allocateEntity<
|
|
482
|
+
const out = allocateEntity<HostLifecycleProvider>();
|
|
484
483
|
out.getState = () => 'active' as const;
|
|
485
484
|
out.subscribe = () => () => {};
|
|
486
485
|
return finishEntity(out);
|
|
487
486
|
})(),
|
|
488
487
|
);
|
|
489
|
-
expect(explainLifecycleOperation(rich, 'subscribeMemoryWarning').implemented).toBe(true);
|
|
490
|
-
expect(explainLifecycleOperation(bare, 'subscribeMemoryWarning').implemented).toBe(false);
|
|
488
|
+
expect(explainLifecycleOperation(rich.system.lifecycle, 'subscribeMemoryWarning').implemented).toBe(true);
|
|
489
|
+
expect(explainLifecycleOperation(bare.system.lifecycle, 'subscribeMemoryWarning').implemented).toBe(false);
|
|
491
490
|
});
|
|
492
491
|
});
|
|
493
492
|
|
|
@@ -495,17 +494,17 @@ describe('getAppLaunchKind', () => {
|
|
|
495
494
|
it("returns 'warm' when backend does not implement getLaunchKind", () => {
|
|
496
495
|
const backend = fakeBackend();
|
|
497
496
|
const host = hostOf(backend);
|
|
498
|
-
expect(getAppLaunchKind(host)).toBe('warm');
|
|
497
|
+
expect(getAppLaunchKind(host.system.lifecycle)).toBe('warm');
|
|
499
498
|
});
|
|
500
499
|
|
|
501
500
|
it('delegates to backend.getLaunchKind when present', () => {
|
|
502
|
-
const out = allocateEntity<
|
|
501
|
+
const out = allocateEntity<HostLifecycleProvider>();
|
|
503
502
|
out.getState = () => 'active' as AppLifecycleState;
|
|
504
503
|
out.subscribe = () => () => {};
|
|
505
504
|
out.getLaunchKind = () => 'cold' as AppLaunchKind;
|
|
506
505
|
const backend = finishEntity(out);
|
|
507
506
|
const host = hostOf(backend);
|
|
508
|
-
expect(getAppLaunchKind(host)).toBe('cold');
|
|
507
|
+
expect(getAppLaunchKind(host.system.lifecycle)).toBe('cold');
|
|
509
508
|
});
|
|
510
509
|
});
|
|
511
510
|
|
|
@@ -514,26 +513,28 @@ describe('getAppLifecycleState', () => {
|
|
|
514
513
|
const backend = fakeBackend();
|
|
515
514
|
backend.state = 'inactive';
|
|
516
515
|
const host = hostOf(backend);
|
|
517
|
-
expect(getAppLifecycleState(host)).toBe('inactive');
|
|
516
|
+
expect(getAppLifecycleState(host.system.lifecycle)).toBe('inactive');
|
|
518
517
|
});
|
|
519
518
|
});
|
|
520
519
|
|
|
521
520
|
describe('hasLifecycleOperation', () => {
|
|
522
521
|
it('agrees with explainLifecycleOperation for the same host', () => {
|
|
523
522
|
const host = hostOf(fakeBackend());
|
|
524
|
-
expect(hasLifecycleOperation(host, 'getState')).toBe(
|
|
523
|
+
expect(hasLifecycleOperation(host.system.lifecycle, 'getState')).toBe(
|
|
524
|
+
explainLifecycleOperation(host.system.lifecycle, 'getState').implemented,
|
|
525
|
+
);
|
|
525
526
|
});
|
|
526
527
|
|
|
527
528
|
it('is false for an operation the host provider omits', () => {
|
|
528
529
|
const host = hostOf(
|
|
529
530
|
(() => {
|
|
530
|
-
const out = allocateEntity<
|
|
531
|
+
const out = allocateEntity<HostLifecycleProvider>();
|
|
531
532
|
out.getState = () => 'active' as const;
|
|
532
533
|
out.subscribe = () => () => {};
|
|
533
534
|
return finishEntity(out);
|
|
534
535
|
})(),
|
|
535
536
|
);
|
|
536
|
-
expect(hasLifecycleOperation(host, 'getLaunchKind')).toBe(false);
|
|
537
|
+
expect(hasLifecycleOperation(host.system.lifecycle, 'getLaunchKind')).toBe(false);
|
|
537
538
|
});
|
|
538
539
|
});
|
|
539
540
|
|
|
@@ -555,8 +556,8 @@ describe('initializeWebLifecycleBackend', () => {
|
|
|
555
556
|
const OPTIONAL_OPERATIONS: readonly LifecycleOperation[] = ['getLaunchKind', 'subscribeMemoryWarning'];
|
|
556
557
|
|
|
557
558
|
// A host implementing only the REQUIRED members — partial support declared by absence.
|
|
558
|
-
function partialBackend():
|
|
559
|
-
const out = allocateEntity<
|
|
559
|
+
function partialBackend(): HostLifecycleProvider {
|
|
560
|
+
const out = allocateEntity<HostLifecycleProvider>();
|
|
560
561
|
out.getState = (() => undefined) as never;
|
|
561
562
|
out.subscribe = (() => undefined) as never;
|
|
562
563
|
return finishEntity(out);
|
|
@@ -567,21 +568,21 @@ describe('isAppActive', () => {
|
|
|
567
568
|
const backend = fakeBackend();
|
|
568
569
|
backend.state = 'active';
|
|
569
570
|
const host = hostOf(backend);
|
|
570
|
-
expect(isAppActive(host)).toBe(true);
|
|
571
|
+
expect(isAppActive(host.system.lifecycle)).toBe(true);
|
|
571
572
|
});
|
|
572
573
|
|
|
573
574
|
it("returns false when state is 'background'", () => {
|
|
574
575
|
const backend = fakeBackend();
|
|
575
576
|
backend.state = 'background';
|
|
576
577
|
const host = hostOf(backend);
|
|
577
|
-
expect(isAppActive(host)).toBe(false);
|
|
578
|
+
expect(isAppActive(host.system.lifecycle)).toBe(false);
|
|
578
579
|
});
|
|
579
580
|
|
|
580
581
|
it("returns false when state is 'inactive'", () => {
|
|
581
582
|
const backend = fakeBackend();
|
|
582
583
|
backend.state = 'inactive';
|
|
583
584
|
const host = hostOf(backend);
|
|
584
|
-
expect(isAppActive(host)).toBe(false);
|
|
585
|
+
expect(isAppActive(host.system.lifecycle)).toBe(false);
|
|
585
586
|
});
|
|
586
587
|
});
|
|
587
588
|
|
|
@@ -590,14 +591,14 @@ describe('isAppBackground', () => {
|
|
|
590
591
|
const backend = fakeBackend();
|
|
591
592
|
backend.state = 'background';
|
|
592
593
|
const host = hostOf(backend);
|
|
593
|
-
expect(isAppBackground(host)).toBe(true);
|
|
594
|
+
expect(isAppBackground(host.system.lifecycle)).toBe(true);
|
|
594
595
|
});
|
|
595
596
|
|
|
596
597
|
it("returns false when state is 'active'", () => {
|
|
597
598
|
const backend = fakeBackend();
|
|
598
599
|
backend.state = 'active';
|
|
599
600
|
const host = hostOf(backend);
|
|
600
|
-
expect(isAppBackground(host)).toBe(false);
|
|
601
|
+
expect(isAppBackground(host.system.lifecycle)).toBe(false);
|
|
601
602
|
});
|
|
602
603
|
});
|
|
603
604
|
describe('isAppInactive', () => {
|
|
@@ -605,14 +606,14 @@ describe('isAppInactive', () => {
|
|
|
605
606
|
const backend = fakeBackend();
|
|
606
607
|
backend.state = 'inactive';
|
|
607
608
|
const host = hostOf(backend);
|
|
608
|
-
expect(isAppInactive(host)).toBe(true);
|
|
609
|
+
expect(isAppInactive(host.system.lifecycle)).toBe(true);
|
|
609
610
|
});
|
|
610
611
|
|
|
611
612
|
it("returns false when state is 'active'", () => {
|
|
612
613
|
const backend = fakeBackend();
|
|
613
614
|
backend.state = 'active';
|
|
614
615
|
const host = hostOf(backend);
|
|
615
|
-
expect(isAppInactive(host)).toBe(false);
|
|
616
|
+
expect(isAppInactive(host.system.lifecycle)).toBe(false);
|
|
616
617
|
});
|
|
617
618
|
});
|
|
618
619
|
|
|
@@ -1,57 +1,63 @@
|
|
|
1
|
-
import type { AppLifecycleState,
|
|
1
|
+
import type { AppLifecycleState, HostLifecycleProvider } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
3
|
import { getAppLaunchKind, getAppLifecycleState, isAppActive, isAppBackground, isAppInactive } from './lifecycle';
|
|
4
4
|
|
|
5
|
-
function hostWith(backend: Partial<
|
|
6
|
-
|
|
5
|
+
function hostWith(backend: Partial<HostLifecycleProvider>): {
|
|
6
|
+
readonly system: { readonly lifecycle: HostLifecycleProvider };
|
|
7
|
+
} {
|
|
8
|
+
return { system: { lifecycle: backend as HostLifecycleProvider } } as {
|
|
9
|
+
readonly system: { readonly lifecycle: HostLifecycleProvider };
|
|
10
|
+
};
|
|
7
11
|
}
|
|
8
12
|
|
|
9
|
-
function stateHost(state: AppLifecycleState):
|
|
13
|
+
function stateHost(state: AppLifecycleState): { readonly system: { readonly lifecycle: HostLifecycleProvider } } {
|
|
10
14
|
return hostWith({ getState: () => state, subscribe: () => () => {} });
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
describe('getAppLaunchKind', () => {
|
|
14
18
|
it('reads the launch kind from the host it is given', () => {
|
|
15
|
-
expect(getAppLaunchKind(hostWith({ getState: () => 'active', getLaunchKind: () => 'warm' }))).toBe(
|
|
19
|
+
expect(getAppLaunchKind(hostWith({ getState: () => 'active', getLaunchKind: () => 'warm' }).system.lifecycle)).toBe(
|
|
20
|
+
'warm',
|
|
21
|
+
);
|
|
16
22
|
});
|
|
17
23
|
|
|
18
24
|
// getLaunchKind is optional on the backend; a provider without it must not throw.
|
|
19
25
|
it('falls back to warm when the provider does not implement it', () => {
|
|
20
|
-
expect(getAppLaunchKind(stateHost('active'))).toBe('warm');
|
|
26
|
+
expect(getAppLaunchKind(stateHost('active').system.lifecycle)).toBe('warm');
|
|
21
27
|
});
|
|
22
28
|
});
|
|
23
29
|
|
|
24
30
|
describe('getAppLifecycleState', () => {
|
|
25
31
|
it('reads the state from the host it is given', () => {
|
|
26
|
-
expect(getAppLifecycleState(stateHost('background'))).toBe('background');
|
|
32
|
+
expect(getAppLifecycleState(stateHost('background').system.lifecycle)).toBe('background');
|
|
27
33
|
});
|
|
28
34
|
|
|
29
35
|
// Two hosts, two answers, no ambient selection in between. This is the property the migration is
|
|
30
36
|
// for: before it, both calls resolved one process-wide backend and the second host was unreachable.
|
|
31
37
|
it('keeps two hosts independent', () => {
|
|
32
|
-
expect(getAppLifecycleState(stateHost('active'))).toBe('active');
|
|
33
|
-
expect(getAppLifecycleState(stateHost('inactive'))).toBe('inactive');
|
|
38
|
+
expect(getAppLifecycleState(stateHost('active').system.lifecycle)).toBe('active');
|
|
39
|
+
expect(getAppLifecycleState(stateHost('inactive').system.lifecycle)).toBe('inactive');
|
|
34
40
|
});
|
|
35
41
|
});
|
|
36
42
|
|
|
37
43
|
describe('isAppActive', () => {
|
|
38
44
|
it('reports active only for the active state', () => {
|
|
39
|
-
expect(isAppActive(stateHost('active'))).toBe(true);
|
|
40
|
-
expect(isAppActive(stateHost('inactive'))).toBe(false);
|
|
41
|
-
expect(isAppActive(stateHost('background'))).toBe(false);
|
|
45
|
+
expect(isAppActive(stateHost('active').system.lifecycle)).toBe(true);
|
|
46
|
+
expect(isAppActive(stateHost('inactive').system.lifecycle)).toBe(false);
|
|
47
|
+
expect(isAppActive(stateHost('background').system.lifecycle)).toBe(false);
|
|
42
48
|
});
|
|
43
49
|
});
|
|
44
50
|
|
|
45
51
|
describe('isAppBackground', () => {
|
|
46
52
|
it('reports background only for the background state', () => {
|
|
47
|
-
expect(isAppBackground(stateHost('background'))).toBe(true);
|
|
48
|
-
expect(isAppBackground(stateHost('active'))).toBe(false);
|
|
53
|
+
expect(isAppBackground(stateHost('background').system.lifecycle)).toBe(true);
|
|
54
|
+
expect(isAppBackground(stateHost('active').system.lifecycle)).toBe(false);
|
|
49
55
|
});
|
|
50
56
|
});
|
|
51
57
|
|
|
52
58
|
describe('isAppInactive', () => {
|
|
53
59
|
it('reports inactive only for the inactive state', () => {
|
|
54
|
-
expect(isAppInactive(stateHost('inactive'))).toBe(true);
|
|
55
|
-
expect(isAppInactive(stateHost('active'))).toBe(false);
|
|
60
|
+
expect(isAppInactive(stateHost('inactive').system.lifecycle)).toBe(true);
|
|
61
|
+
expect(isAppInactive(stateHost('active').system.lifecycle)).toBe(false);
|
|
56
62
|
});
|
|
57
63
|
});
|