@burdenoff/fe-libs 2026.1012.6 → 2026.1013.2

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.
@@ -45,8 +45,8 @@ function j(j) {
45
45
  ...p(j.appName),
46
46
  ...j.backButton
47
47
  })), O("deepLinks", () => _()), O("network", () => b(j.network ?? {})), O("runtimeHooks", () => u(j.runtimeHooks ?? {})), j.push?.enabled && j.plugins?.pushNotifications) {
48
- let e = j.plugins.pushNotifications;
49
- O("push", () => S(e, j.push ?? {}));
48
+ let e = j.plugins.pushNotifications, t = j.plugins.localNotifications;
49
+ O("push", () => S(e, j.push ?? {}, t));
50
50
  }
51
51
  return l() && j.splash?.autoHide !== !1 && O("splashSafetyTimer", () => T(j.splash?.maxWaitMs ?? 8e3)), O("pwa", () => w({
52
52
  enabled: !0,
@@ -16,6 +16,8 @@
16
16
  * plugins: {
17
17
  * updater: () => import('@capgo/capacitor-updater'),
18
18
  * pushNotifications: () => import('@capacitor-firebase/messaging'),
19
+ * // Android foreground push display only — see LocalNotificationsLike.
20
+ * localNotifications: () => import('@capacitor/local-notifications'),
19
21
  * },
20
22
  * push: { enabled: true },
21
23
  * });
@@ -51,5 +53,5 @@ export { configureElectronGlobalKey, getDesktopBridge, getElectronGlobalKey, get
51
53
  export { defineCapacitorConfig, isValidAllowNavigationRule, type CapacitorConfigLike, } from './capacitor-config';
52
54
  export { defineWebManifest, type WebManifestIcon, type WebManifestLike, type WebManifestPushConfig, type WebManifestShortcut, } from './web-manifest';
53
55
  export { renderServiceWorker, SERVICE_WORKER_TEMPLATE } from './service-worker';
54
- export type { AuthInterceptOptions, BackButtonHandlers, BeforeInstallPromptEventLike, BootNativeShellOptions, CapacitorListenerHandleLike, CapacitorUpdaterLike, DeepLinkDetail, LegacyElectronBridge, MobileRuntimeHooks, NativeConfig, NativePreviewMode, NativeShellHandle, NativeShellPlatform, NetworkHooks, NetworkStatusDetail, OptionalPluginLoaders, PushActionPerformedLike, PushNotificationSchemaLike, PushNotificationsLike, PushNotificationsLoader, PushOptions, PushPermissionStateLike, PushTokenLike, PwaOptions, SplashOptions, UpdaterLoader, } from './types';
56
+ export type { AuthInterceptOptions, BackButtonHandlers, BeforeInstallPromptEventLike, BootNativeShellOptions, CapacitorListenerHandleLike, CapacitorUpdaterLike, DeepLinkDetail, LegacyElectronBridge, MobileRuntimeHooks, NativeConfig, LocalNotificationsLike, LocalNotificationsLoader, NativePreviewMode, NativeShellHandle, NativeShellPlatform, NetworkHooks, NetworkStatusDetail, OptionalPluginLoaders, PushActionPerformedLike, PushNotificationSchemaLike, PushNotificationsLike, PushNotificationsLoader, PushOptions, PushPermissionStateLike, PushTokenLike, PwaOptions, SplashOptions, UpdaterLoader, } from './types';
55
57
  export { installPushOptIn, resetPushOptInForTests } from './push-optin';
@@ -1,5 +1,5 @@
1
1
  import { PushTokenEventDetail } from '../shared/native/types';
2
- import { PushNotificationsLoader, PushOptions, PushPermissionStateLike } from './types';
2
+ import { LocalNotificationsLoader, PushNotificationsLoader, PushOptions, PushPermissionStateLike } from './types';
3
3
  export declare const PUSH_TOKEN_EVENT = "burdenoff:pushToken";
4
4
  /**
5
5
  * Publish a push token to the rest of the app: `window.__burdenoffPushToken`
@@ -15,7 +15,7 @@ export declare function emitPushToken(value: string, platform: PushTokenEventDet
15
15
  * `window.__burdenoffPushToken`; notification tap → deep-link navigator).
16
16
  * Does NOT prompt for permission — call `requestPushPermission()` for that.
17
17
  */
18
- export declare function installPushNotifications(loader: PushNotificationsLoader, options?: Pick<PushOptions, 'onToken' | 'onNotificationAction' | 'onNotificationReceived'>): Promise<void>;
18
+ export declare function installPushNotifications(loader: PushNotificationsLoader, options?: Pick<PushOptions, 'onToken' | 'onNotificationAction' | 'onNotificationReceived'>, localNotificationsLoader?: LocalNotificationsLoader): Promise<void>;
19
19
  /**
20
20
  * Read current native permission state WITHOUT prompting.
21
21
  *
@@ -22,7 +22,33 @@ async function s() {
22
22
  }
23
23
  return a;
24
24
  }
25
- function c(e) {
25
+ var c = null, l = null;
26
+ async function u(e) {
27
+ if (!e || !t()) return null;
28
+ if (l !== e && (c = null, l = e), c) return c;
29
+ try {
30
+ let t = (await e()).LocalNotifications ?? null;
31
+ c = t ? { plugin: t } : null;
32
+ } catch {
33
+ c = null;
34
+ }
35
+ return c;
36
+ }
37
+ var d = 0;
38
+ async function f(e, t) {
39
+ try {
40
+ let n = await u(e);
41
+ if (!n || (await n.plugin.checkPermissions()).display !== "granted") return;
42
+ await n.plugin.schedule({ notifications: [{
43
+ id: ++d % 2147483647,
44
+ title: t.title ?? "",
45
+ body: t.body ?? "",
46
+ channelId: "default",
47
+ extra: t.data ?? void 0
48
+ }] });
49
+ } catch {}
50
+ }
51
+ function p(e) {
26
52
  let t = e.notification.data;
27
53
  if (!t || typeof t != "object") return null;
28
54
  let n = t.route;
@@ -36,7 +62,7 @@ function c(e) {
36
62
  }
37
63
  return null;
38
64
  }
39
- async function l(t, r = {}) {
65
+ async function m(t, r = {}, c) {
40
66
  o !== t && (a = null, o = t);
41
67
  let l = await s();
42
68
  if (!l) return;
@@ -47,17 +73,21 @@ async function l(t, r = {}) {
47
73
  i(t.token, d), r.onToken?.(t.token, d);
48
74
  }), await u.addListener("notificationReceived", (e) => {
49
75
  let t = e;
50
- r.onNotificationReceived?.(t.notification);
76
+ d === "android" && f(c, t.notification), r.onNotificationReceived?.(t.notification);
51
77
  }), await u.addListener("notificationActionPerformed", (e) => {
52
- let t = e, i = c(t);
78
+ let t = e, i = p(t);
53
79
  i && n(i), r.onNotificationAction?.(t);
80
+ }), d === "android" && await u.createChannel({
81
+ id: "default",
82
+ name: "Notifications",
83
+ importance: 4
54
84
  }), (await u.checkPermissions()).receive === "granted") {
55
85
  let { token: e } = await u.getToken();
56
86
  i(e, d), r.onToken?.(e, d);
57
87
  }
58
88
  } catch {}
59
89
  }
60
- async function u() {
90
+ async function h() {
61
91
  try {
62
92
  let e = await s();
63
93
  return e ? (await e.plugin.checkPermissions()).receive : null;
@@ -65,7 +95,7 @@ async function u() {
65
95
  return null;
66
96
  }
67
97
  }
68
- async function d() {
98
+ async function g() {
69
99
  try {
70
100
  let t = await s();
71
101
  if (!t) return !1;
@@ -78,4 +108,4 @@ async function d() {
78
108
  }
79
109
  }
80
110
  //#endregion
81
- export { r as PUSH_TOKEN_EVENT, u as checkPushPermission, i as emitPushToken, l as installPushNotifications, d as requestPushPermission };
111
+ export { r as PUSH_TOKEN_EVENT, h as checkPushPermission, i as emitPushToken, m as installPushNotifications, g as requestPushPermission };
@@ -194,19 +194,62 @@ export interface PushNotificationsLike {
194
194
  getToken(): Promise<{
195
195
  token: string;
196
196
  }>;
197
+ createChannel(options: {
198
+ id: string;
199
+ name: string;
200
+ importance?: number;
201
+ }): Promise<void>;
197
202
  addListener(eventName: 'tokenReceived' | 'notificationReceived' | 'notificationActionPerformed', listener: (payload: unknown) => void): Promise<CapacitorListenerHandleLike>;
198
203
  }
204
+ /**
205
+ * Structural subset of `@capacitor/local-notifications`' `LocalNotifications`.
206
+ *
207
+ * Only used for ANDROID foreground push display. `notificationReceived`
208
+ * (`PushOptions.onNotificationReceived`) fires ONLY while the app is in the
209
+ * foreground — Android's OS auto-displays a backgrounded/killed app's
210
+ * notification-type message via the system tray directly, bypassing JS
211
+ * entirely, so this listener is never reached for that case. In the
212
+ * foreground, neither platform auto-displays anything: iOS's own
213
+ * `presentationOptions` config handles that side: Android has no such
214
+ * mechanism, and without this, a push received while the app is open is
215
+ * silently never shown — confirmed live: FCM reported successful delivery,
216
+ * nothing appeared, because the app was foregrounded and nothing built a
217
+ * system notification in response.
218
+ */
219
+ export interface LocalNotificationsLike {
220
+ checkPermissions(): Promise<{
221
+ display: PushPermissionStateLike;
222
+ }>;
223
+ schedule(options: {
224
+ notifications: Array<{
225
+ id: number;
226
+ title: string;
227
+ body: string;
228
+ channelId?: string;
229
+ extra?: Record<string, unknown>;
230
+ }>;
231
+ }): Promise<unknown>;
232
+ }
199
233
  export type UpdaterLoader = () => Promise<{
200
234
  CapacitorUpdater: CapacitorUpdaterLike;
201
235
  }>;
202
236
  export type PushNotificationsLoader = () => Promise<{
203
237
  FirebaseMessaging: PushNotificationsLike;
204
238
  }>;
239
+ export type LocalNotificationsLoader = () => Promise<{
240
+ LocalNotifications: LocalNotificationsLike;
241
+ }>;
205
242
  export interface OptionalPluginLoaders {
206
243
  /** `() => import('@capgo/capacitor-updater')` */
207
244
  updater?: UpdaterLoader;
208
245
  /** `() => import('@capacitor-firebase/messaging')` */
209
246
  pushNotifications?: PushNotificationsLoader;
247
+ /**
248
+ * `() => import('@capacitor/local-notifications')` — Android foreground
249
+ * push display only. Optional: a shell that never enables push, or
250
+ * accepts silent foreground pushes, can omit this entirely.
251
+ */
252
+ localNotifications?: LocalNotificationsLoader;
210
253
  }
211
254
  export interface PushOptions {
212
255
  enabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.1012.6",
3
+ "version": "2026.1013.2",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {