@flighthq/lifecycle 0.5.1-next.815.cc08b93 → 0.5.1-next.903.f434bc2
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/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/lifecycle.d.ts +3 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +117 -111
- package/dist/lifecycle.js.map +1 -1
- package/package.json +4 -4
- package/src/lifecycle.test.ts +59 -37
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.903.f434bc2](https://github.com/flighthq/flight/tree/f434bc233778d75aeea61995df3a8cfaec459a85/packages/lifecycle)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/f434bc233778d75aeea61995df3a8cfaec459a85/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/contract.js
CHANGED
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BackendOperationExplanation, LifecycleOperation } from '@flighthq/types/contract';
|
|
1
|
+
import type { BackendOperationExplanation, LifecycleOperation, EntityConstruction } from '@flighthq/types/contract';
|
|
2
2
|
import type { AppLaunchKind, HasSystemLifecycle, AppLifecycle, AppLifecycleState, LifecycleBackend } from '@flighthq/types/contract';
|
|
3
3
|
export declare function attachAppLifecycle(host: HasSystemLifecycle, app: AppLifecycle): void;
|
|
4
4
|
export declare function createAppLifecycle(): AppLifecycle;
|
|
@@ -9,6 +9,8 @@ export declare function explainLifecycleOperation(host: HasSystemLifecycle, oper
|
|
|
9
9
|
export declare function getAppLaunchKind(host: HasSystemLifecycle): AppLaunchKind;
|
|
10
10
|
export declare function getAppLifecycleState(host: HasSystemLifecycle): AppLifecycleState;
|
|
11
11
|
export declare function hasLifecycleOperation(host: HasSystemLifecycle, operation: LifecycleOperation): boolean;
|
|
12
|
+
export declare function initializeAppLifecycle(out: EntityConstruction<AppLifecycle>): void;
|
|
13
|
+
export declare function initializeWebLifecycleBackend(out: EntityConstruction<LifecycleBackend>): void;
|
|
12
14
|
export declare function isAppActive(host: HasSystemLifecycle): boolean;
|
|
13
15
|
export declare function isAppBackground(host: HasSystemLifecycle): boolean;
|
|
14
16
|
export declare function isAppInactive(host: HasSystemLifecycle): boolean;
|
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,MAAM,0BAA0B,CAAC;
|
|
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,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EAEjB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAYlC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,YAAY,GAAG,IAAI,CAqCpF;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAIjD;AAED,wBAAgB,yBAAyB,IAAI,gBAAgB,CAI5D;AAGD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAM1D;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAG3D;AAQD,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,kBAAkB,EACxB,SAAS,EAAE,kBAAkB,GAC5B,2BAA2B,CAG7B;AAMD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAGxE;AAGD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAEhF;AAGD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,EAAE,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAEtG;AAGD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,CAAC,YAAY,CAAC,GAAG,IAAI,CAQlF;AAmBD,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,GAAG,IAAI,CA0E7F;AAGD,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE7D;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAEjE;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE/D;AAOD,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAGzD"}
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { createSignal, emitSignal } from '@flighthq/signals/contract';
|
|
3
3
|
// Begins delivering lifecycle changes to `app`'s signals by subscribing to the active backend. On
|
|
4
4
|
// each change it reads the current state and emits onStateChange plus onResume when transitioning
|
|
@@ -46,119 +46,15 @@ export function attachAppLifecycle(host, app) {
|
|
|
46
46
|
unsubscribeMemory?.();
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
// Allocates an AppLifecycle event entity with inert signals; call attachAppLifecycle to start delivery.
|
|
50
49
|
export function createAppLifecycle() {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
onPause: createSignal(),
|
|
55
|
-
onBackButton: createSignal(),
|
|
56
|
-
onMemoryWarning: createSignal(),
|
|
57
|
-
onSaveState: createSignal(),
|
|
58
|
-
onRestoreState: createSignal(),
|
|
59
|
-
});
|
|
50
|
+
const out = allocateEntity();
|
|
51
|
+
initializeAppLifecycle(out);
|
|
52
|
+
return finishEntity(out);
|
|
60
53
|
}
|
|
61
|
-
// Builds the default web backend over document visibility, window focus/blur, and pagehide/pageshow
|
|
62
|
-
// events. Produces three states:
|
|
63
|
-
// 'active' — document visible and window focused
|
|
64
|
-
// 'inactive' — document visible but window not focused (app switcher, control-center, other window)
|
|
65
|
-
// 'background' — document.hidden (tab hidden or page unloading)
|
|
66
|
-
// Degrades to state 'active' and a no-op subscription where document/window are absent (SSR/jsdom).
|
|
67
|
-
//
|
|
68
|
-
// getLaunchKind() approximates cold vs. warm using PerformanceNavigationTiming.type:
|
|
69
|
-
// 'back_forward' → 'warm' — page was restored from the bfcache (JS heap was frozen and thawed;
|
|
70
|
-
// the closest web equivalent of a process warm-resume)
|
|
71
|
-
// all others → 'cold' — 'navigate', 'reload', 'prerender' are each a fresh page lifecycle
|
|
72
|
-
// Falls back to 'cold' when the Performance Navigation Timing API is unavailable (SSR/jsdom).
|
|
73
|
-
//
|
|
74
|
-
// subscribeMemoryWarning() wires the experimental 'memory-pressure' window event (Chrome origin
|
|
75
|
-
// trial / behind flags). The event detail carries a 'critical' pressure string; this backend maps
|
|
76
|
-
// it to 'critical' and fires 'normal' on the subsequent resolution event when present. Falls back to
|
|
77
|
-
// no-op unsubscribe when the event is not supported (no standard API is widely deployed as of 2026).
|
|
78
54
|
export function createWebLifecycleBackend() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (typeof document === 'undefined')
|
|
83
|
-
return 'active';
|
|
84
|
-
if (document.hidden)
|
|
85
|
-
return 'background';
|
|
86
|
-
return _windowFocused ? 'active' : 'inactive';
|
|
87
|
-
},
|
|
88
|
-
subscribe(listener) {
|
|
89
|
-
if (typeof document === 'undefined' || typeof window === 'undefined')
|
|
90
|
-
return () => { };
|
|
91
|
-
// Initialize focus state at subscribe time.
|
|
92
|
-
_windowFocused = document.hasFocus();
|
|
93
|
-
const onFocus = () => {
|
|
94
|
-
_windowFocused = true;
|
|
95
|
-
listener();
|
|
96
|
-
};
|
|
97
|
-
const onBlur = () => {
|
|
98
|
-
_windowFocused = false;
|
|
99
|
-
listener();
|
|
100
|
-
};
|
|
101
|
-
document.addEventListener('visibilitychange', listener);
|
|
102
|
-
window.addEventListener('pagehide', listener);
|
|
103
|
-
window.addEventListener('pageshow', listener);
|
|
104
|
-
window.addEventListener('focus', onFocus);
|
|
105
|
-
window.addEventListener('blur', onBlur);
|
|
106
|
-
return () => {
|
|
107
|
-
document.removeEventListener('visibilitychange', listener);
|
|
108
|
-
window.removeEventListener('pagehide', listener);
|
|
109
|
-
window.removeEventListener('pageshow', listener);
|
|
110
|
-
window.removeEventListener('focus', onFocus);
|
|
111
|
-
window.removeEventListener('blur', onBlur);
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
getLaunchKind() {
|
|
115
|
-
// PerformanceNavigationTiming.type === 'back_forward' indicates the page was restored from the
|
|
116
|
-
// browser's back/forward cache (bfcache). The JS heap was frozen and thawed without a fresh
|
|
117
|
-
// process start — the closest analog to a mobile warm-resume. All other navigation types
|
|
118
|
-
// ('navigate', 'reload', 'prerender') are a fresh page lifecycle, i.e. cold.
|
|
119
|
-
if (typeof performance === 'undefined')
|
|
120
|
-
return 'cold';
|
|
121
|
-
const entries = performance.getEntriesByType('navigation');
|
|
122
|
-
if (entries.length > 0 && entries[0].type === 'back_forward')
|
|
123
|
-
return 'warm';
|
|
124
|
-
return 'cold';
|
|
125
|
-
},
|
|
126
|
-
subscribeMemoryWarning(listener) {
|
|
127
|
-
// The 'memory-pressure' window event is an experimental Chrome API (origin trial / behind
|
|
128
|
-
// flags as of 2026). The event carries a detail object with a 'pressure' string. This backend
|
|
129
|
-
// maps 'critical' pressure to AppMemoryPressure 'critical'; a pressure-resolved / 'moderate'
|
|
130
|
-
// event maps to 'moderate'. Falls back to no-op unsubscribe when the event is not supported
|
|
131
|
-
// (no cross-browser API for memory pressure is widely deployed yet).
|
|
132
|
-
if (typeof window === 'undefined')
|
|
133
|
-
return () => { };
|
|
134
|
-
// Feature-detect: attempt to register and immediately remove a passive listener to check
|
|
135
|
-
// whether the event is supported. If addEventListener silently ignores the event type there
|
|
136
|
-
// is nothing we can do — we just return no-op without pretending to be subscribed.
|
|
137
|
-
const onPressure = (e) => {
|
|
138
|
-
const detail = e.detail;
|
|
139
|
-
const pressure = detail?.pressure;
|
|
140
|
-
if (pressure === 'critical') {
|
|
141
|
-
listener('critical');
|
|
142
|
-
}
|
|
143
|
-
else if (pressure === 'moderate') {
|
|
144
|
-
listener('moderate');
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
// Unknown pressure level — treat as moderate rather than silently dropping.
|
|
148
|
-
listener('moderate');
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
const onPressureRelieved = () => {
|
|
152
|
-
listener('normal');
|
|
153
|
-
};
|
|
154
|
-
window.addEventListener('memory-pressure', onPressure);
|
|
155
|
-
window.addEventListener('memory-pressure-relieved', onPressureRelieved);
|
|
156
|
-
return () => {
|
|
157
|
-
window.removeEventListener('memory-pressure', onPressure);
|
|
158
|
-
window.removeEventListener('memory-pressure-relieved', onPressureRelieved);
|
|
159
|
-
};
|
|
160
|
-
},
|
|
161
|
-
});
|
|
55
|
+
const out = allocateEntity();
|
|
56
|
+
initializeWebLifecycleBackend(out);
|
|
57
|
+
return finishEntity(out);
|
|
162
58
|
}
|
|
163
59
|
// Stops delivery to `app` and forgets its subscription. Safe to call when not attached.
|
|
164
60
|
export function detachAppLifecycle(app) {
|
|
@@ -200,6 +96,116 @@ export function getAppLifecycleState(host) {
|
|
|
200
96
|
export function hasLifecycleOperation(host, operation) {
|
|
201
97
|
return explainLifecycleOperation(host, operation).implemented;
|
|
202
98
|
}
|
|
99
|
+
// Allocates an AppLifecycle event entity with inert signals; call attachAppLifecycle to start delivery.
|
|
100
|
+
export function initializeAppLifecycle(out) {
|
|
101
|
+
out.onStateChange = createSignal();
|
|
102
|
+
out.onResume = createSignal();
|
|
103
|
+
out.onPause = createSignal();
|
|
104
|
+
out.onBackButton = createSignal();
|
|
105
|
+
out.onMemoryWarning = createSignal();
|
|
106
|
+
out.onSaveState = createSignal();
|
|
107
|
+
out.onRestoreState = createSignal();
|
|
108
|
+
}
|
|
109
|
+
// Builds the default web backend over document visibility, window focus/blur, and pagehide/pageshow
|
|
110
|
+
// events. Produces three states:
|
|
111
|
+
// 'active' — document visible and window focused
|
|
112
|
+
// 'inactive' — document visible but window not focused (app switcher, control-center, other window)
|
|
113
|
+
// 'background' — document.hidden (tab hidden or page unloading)
|
|
114
|
+
// Degrades to state 'active' and a no-op subscription where document/window are absent (SSR/jsdom).
|
|
115
|
+
//
|
|
116
|
+
// getLaunchKind() approximates cold vs. warm using PerformanceNavigationTiming.type:
|
|
117
|
+
// 'back_forward' → 'warm' — page was restored from the bfcache (JS heap was frozen and thawed;
|
|
118
|
+
// the closest web equivalent of a process warm-resume)
|
|
119
|
+
// all others → 'cold' — 'navigate', 'reload', 'prerender' are each a fresh page lifecycle
|
|
120
|
+
// Falls back to 'cold' when the Performance Navigation Timing API is unavailable (SSR/jsdom).
|
|
121
|
+
//
|
|
122
|
+
// subscribeMemoryWarning() wires the experimental 'memory-pressure' window event (Chrome origin
|
|
123
|
+
// trial / behind flags). The event detail carries a 'critical' pressure string; this backend maps
|
|
124
|
+
// it to 'critical' and fires 'normal' on the subsequent resolution event when present. Falls back to
|
|
125
|
+
// no-op unsubscribe when the event is not supported (no standard API is widely deployed as of 2026).
|
|
126
|
+
export function initializeWebLifecycleBackend(out) {
|
|
127
|
+
let _windowFocused = typeof document !== 'undefined';
|
|
128
|
+
out.getState = () => {
|
|
129
|
+
if (typeof document === 'undefined')
|
|
130
|
+
return 'active';
|
|
131
|
+
if (document.hidden)
|
|
132
|
+
return 'background';
|
|
133
|
+
return _windowFocused ? 'active' : 'inactive';
|
|
134
|
+
};
|
|
135
|
+
out.subscribe = (listener) => {
|
|
136
|
+
if (typeof document === 'undefined' || typeof window === 'undefined')
|
|
137
|
+
return () => { };
|
|
138
|
+
// Initialize focus state at subscribe time.
|
|
139
|
+
_windowFocused = document.hasFocus();
|
|
140
|
+
const onFocus = () => {
|
|
141
|
+
_windowFocused = true;
|
|
142
|
+
listener();
|
|
143
|
+
};
|
|
144
|
+
const onBlur = () => {
|
|
145
|
+
_windowFocused = false;
|
|
146
|
+
listener();
|
|
147
|
+
};
|
|
148
|
+
document.addEventListener('visibilitychange', listener);
|
|
149
|
+
window.addEventListener('pagehide', listener);
|
|
150
|
+
window.addEventListener('pageshow', listener);
|
|
151
|
+
window.addEventListener('focus', onFocus);
|
|
152
|
+
window.addEventListener('blur', onBlur);
|
|
153
|
+
return () => {
|
|
154
|
+
document.removeEventListener('visibilitychange', listener);
|
|
155
|
+
window.removeEventListener('pagehide', listener);
|
|
156
|
+
window.removeEventListener('pageshow', listener);
|
|
157
|
+
window.removeEventListener('focus', onFocus);
|
|
158
|
+
window.removeEventListener('blur', onBlur);
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
out.getLaunchKind = () => {
|
|
162
|
+
// PerformanceNavigationTiming.type === 'back_forward' indicates the page was restored from the
|
|
163
|
+
// browser's back/forward cache (bfcache). The JS heap was frozen and thawed without a fresh
|
|
164
|
+
// process start — the closest analog to a mobile warm-resume. All other navigation types
|
|
165
|
+
// ('navigate', 'reload', 'prerender') are a fresh page lifecycle, i.e. cold.
|
|
166
|
+
if (typeof performance === 'undefined')
|
|
167
|
+
return 'cold';
|
|
168
|
+
const entries = performance.getEntriesByType('navigation');
|
|
169
|
+
if (entries.length > 0 && entries[0].type === 'back_forward')
|
|
170
|
+
return 'warm';
|
|
171
|
+
return 'cold';
|
|
172
|
+
};
|
|
173
|
+
out.subscribeMemoryWarning = (listener) => {
|
|
174
|
+
// 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 backend
|
|
176
|
+
// maps 'critical' pressure to AppMemoryPressure 'critical'; a pressure-resolved / 'moderate'
|
|
177
|
+
// event maps to 'moderate'. Falls back to no-op unsubscribe when the event is not supported
|
|
178
|
+
// (no cross-browser API for memory pressure is widely deployed yet).
|
|
179
|
+
if (typeof window === 'undefined')
|
|
180
|
+
return () => { };
|
|
181
|
+
// Feature-detect: attempt to register and immediately remove a passive listener to check
|
|
182
|
+
// whether the event is supported. If addEventListener silently ignores the event type there
|
|
183
|
+
// is nothing we can do — we just return no-op without pretending to be subscribed.
|
|
184
|
+
const onPressure = (e) => {
|
|
185
|
+
const detail = e.detail;
|
|
186
|
+
const pressure = detail?.pressure;
|
|
187
|
+
if (pressure === 'critical') {
|
|
188
|
+
listener('critical');
|
|
189
|
+
}
|
|
190
|
+
else if (pressure === 'moderate') {
|
|
191
|
+
listener('moderate');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// Unknown pressure level — treat as moderate rather than silently dropping.
|
|
195
|
+
listener('moderate');
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const onPressureRelieved = () => {
|
|
199
|
+
listener('normal');
|
|
200
|
+
};
|
|
201
|
+
window.addEventListener('memory-pressure', onPressure);
|
|
202
|
+
window.addEventListener('memory-pressure-relieved', onPressureRelieved);
|
|
203
|
+
return () => {
|
|
204
|
+
window.removeEventListener('memory-pressure', onPressure);
|
|
205
|
+
window.removeEventListener('memory-pressure-relieved', onPressureRelieved);
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
}
|
|
203
209
|
// Returns true when the application is in the 'active' state (visible and focused).
|
|
204
210
|
export function isAppActive(host) {
|
|
205
211
|
return host.system.lifecycle.getState() === 'active';
|
package/dist/lifecycle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,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.903.f434bc2",
|
|
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.903.f434bc2",
|
|
42
|
+
"@flighthq/signals": "0.5.1-next.903.f434bc2",
|
|
43
|
+
"@flighthq/types": "0.5.1-next.903.f434bc2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
package/src/lifecycle.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { allocateEntity, finishEntity } from '@flighthq/entity/contract';
|
|
2
2
|
import { cancelSignal, connectSignal } from '@flighthq/signals/contract';
|
|
3
3
|
import type {
|
|
4
4
|
HasSystemLifecycle,
|
|
5
|
+
AppLaunchKind,
|
|
5
6
|
AppLifecycleState,
|
|
6
7
|
AppMemoryPressure,
|
|
7
8
|
Entity,
|
|
@@ -38,30 +39,30 @@ function hostOf(backend: LifecycleBackend): HasSystemLifecycle {
|
|
|
38
39
|
function fakeBackend(): FakeBackend {
|
|
39
40
|
let stateListener: (() => void) | null = null;
|
|
40
41
|
let memoryListener: ((level: AppMemoryPressure) => void) | null = null;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
42
|
+
const out = allocateEntity<FakeBackend>();
|
|
43
|
+
out.state = 'active' as AppLifecycleState;
|
|
44
|
+
out.getState = () => {
|
|
45
|
+
return out.state;
|
|
46
|
+
};
|
|
47
|
+
out.subscribe = (l: () => void) => {
|
|
48
|
+
stateListener = l;
|
|
49
|
+
return () => {
|
|
50
|
+
stateListener = null;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
out.subscribeMemoryWarning = (l: (level: AppMemoryPressure) => void) => {
|
|
54
|
+
memoryListener = l;
|
|
55
|
+
return () => {
|
|
56
|
+
memoryListener = null;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
out.fire = () => {
|
|
60
|
+
stateListener?.();
|
|
61
|
+
};
|
|
62
|
+
out.fireMemory = (level: AppMemoryPressure) => {
|
|
63
|
+
memoryListener?.(level);
|
|
64
|
+
};
|
|
65
|
+
return finishEntity(out);
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
describe('attachAppLifecycle', () => {
|
|
@@ -456,7 +457,14 @@ describe('explainLifecycleOperation', () => {
|
|
|
456
457
|
});
|
|
457
458
|
|
|
458
459
|
it('reports an operation the host provider omits as unimplemented', () => {
|
|
459
|
-
const host = hostOf(
|
|
460
|
+
const host = hostOf(
|
|
461
|
+
(() => {
|
|
462
|
+
const out = allocateEntity<LifecycleBackend>();
|
|
463
|
+
out.getState = () => 'active' as const;
|
|
464
|
+
out.subscribe = () => () => {};
|
|
465
|
+
return finishEntity(out);
|
|
466
|
+
})(),
|
|
467
|
+
);
|
|
460
468
|
expect(explainLifecycleOperation(host, 'getLaunchKind')).toEqual({
|
|
461
469
|
implemented: false,
|
|
462
470
|
layer: 'sentinel',
|
|
@@ -468,7 +476,14 @@ describe('explainLifecycleOperation', () => {
|
|
|
468
476
|
// process-wide backend answered for every caller.
|
|
469
477
|
it('answers per host rather than process-wide', () => {
|
|
470
478
|
const rich = hostOf(fakeBackend());
|
|
471
|
-
const bare = hostOf(
|
|
479
|
+
const bare = hostOf(
|
|
480
|
+
(() => {
|
|
481
|
+
const out = allocateEntity<LifecycleBackend>();
|
|
482
|
+
out.getState = () => 'active' as const;
|
|
483
|
+
out.subscribe = () => () => {};
|
|
484
|
+
return finishEntity(out);
|
|
485
|
+
})(),
|
|
486
|
+
);
|
|
472
487
|
expect(explainLifecycleOperation(rich, 'subscribeMemoryWarning').implemented).toBe(true);
|
|
473
488
|
expect(explainLifecycleOperation(bare, 'subscribeMemoryWarning').implemented).toBe(false);
|
|
474
489
|
});
|
|
@@ -482,11 +497,11 @@ describe('getAppLaunchKind', () => {
|
|
|
482
497
|
});
|
|
483
498
|
|
|
484
499
|
it('delegates to backend.getLaunchKind when present', () => {
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
500
|
+
const out = allocateEntity<LifecycleBackend>();
|
|
501
|
+
out.getState = () => 'active' as AppLifecycleState;
|
|
502
|
+
out.subscribe = () => () => {};
|
|
503
|
+
out.getLaunchKind = () => 'cold' as AppLaunchKind;
|
|
504
|
+
const backend = finishEntity(out);
|
|
490
505
|
const host = hostOf(backend);
|
|
491
506
|
expect(getAppLaunchKind(host)).toBe('cold');
|
|
492
507
|
});
|
|
@@ -508,7 +523,14 @@ describe('hasLifecycleOperation', () => {
|
|
|
508
523
|
});
|
|
509
524
|
|
|
510
525
|
it('is false for an operation the host provider omits', () => {
|
|
511
|
-
const host = hostOf(
|
|
526
|
+
const host = hostOf(
|
|
527
|
+
(() => {
|
|
528
|
+
const out = allocateEntity<LifecycleBackend>();
|
|
529
|
+
out.getState = () => 'active' as const;
|
|
530
|
+
out.subscribe = () => () => {};
|
|
531
|
+
return finishEntity(out);
|
|
532
|
+
})(),
|
|
533
|
+
);
|
|
512
534
|
expect(hasLifecycleOperation(host, 'getLaunchKind')).toBe(false);
|
|
513
535
|
});
|
|
514
536
|
});
|
|
@@ -559,10 +581,10 @@ const OPTIONAL_OPERATIONS: readonly LifecycleOperation[] = ['getLaunchKind', 'su
|
|
|
559
581
|
|
|
560
582
|
// A host implementing only the REQUIRED members — partial support declared by absence.
|
|
561
583
|
function partialBackend(): LifecycleBackend {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
584
|
+
const out = allocateEntity<LifecycleBackend>();
|
|
585
|
+
out.getState = (() => undefined) as never;
|
|
586
|
+
out.subscribe = (() => undefined) as never;
|
|
587
|
+
return finishEntity(out);
|
|
566
588
|
}
|
|
567
589
|
|
|
568
590
|
describe('isAppInactive', () => {
|