@spooky-sync/core 0.0.1-canary.281 → 0.0.1-canary.283

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as WebPushSupport, A as RunOptions, At as SealedQuery, B as SyncHealthStatus, Bt as decodeTokenClaims, C as QueryTimeToLive, Ct as AuthEventSystem, D as RecordVersionArray, Dt as RemoteDatabaseService, E as ReconnectConfig, Et as createAuthEventSystem, F as StorageHealth, Ft as ImpersonationTimerHost, G as WebPushDevice, H as UpdateOptions, Ht as renewDelayMs, I as StorageHealthStatus, It as ImpersonationTimers, J as WebPushInfo, K as WebPushError, L as StoreType, Lt as ORIGIN_TOKEN_KEY, M as Sp00kyConfig, Mt as EventSystem, N as Sp00kyQueryResult, Nt as IMPERSONATION_ACCESS, O as RecordVersionDiff, Ot as Logger$1, P as Sp00kyQueryResultPromise, Pt as ImpersonationInfo, Q as WebPushSubscribeOptions, R as SyncHealth, Rt as SESSION_POLL_MS, S as QueryStatusCallback, St as SignOutHook, T as QueryUpdateCallback, Tt as AuthEventTypes, U as PUSH_ENDPOINT_KEY, Ut as sameImpersonation, V as TimingPhase, Vt as impersonationFromToken, W as WebPushConfig, X as WebPushMessageInput, Y as WebPushMessage, Z as WebPushModule, _ as QueryConfig, _t as ActiveImpersonation, a as Level, at as detectWebPushSupport, b as QueryState, bt as SIGN_OUT_HOOK_TIMEOUT_MS, c as MutationCallback, ct as PUSH_PAYLOAD_VERSION, d as PersistenceClient, dt as PushOp, et as WebPushSyncOptions, f as PhaseStat, ft as PushPayload, g as QueryAuthorityCallback, gt as isPushPayload, h as PreloadRefresh, ht as WebPushClientMessage, i as InlineRow, it as WebPushUpdateOptions, j as ServerViewMeta, jt as EventDefinition, k as RegistrationTimings, kt as LocalStore, l as MutationEvent, lt as PushNotification, m as PreloadOptions, mt as PushUrgency, n as DebounceOptions, nt as WebPushUnsubscribeOptions, o as MATERIALIZATION_SAMPLE_WINDOW, ot as BridgeSignOutMessage, p as PinoTransmit, pt as PushPayloadKind, q as WebPushErrorCode, r as EventSubscriptionOptions, rt as WebPushUnsupportedReason, s as MembershipOutcome, st as BridgeTokenMessage, t as ConnectionState, tt as WebPushSyncStatus, u as MutationEventType, ut as PushNotificationAction, v as QueryConfigRecord, vt as AuthService, w as QueryTimings, wt as AuthEventTypeMap, x as QueryStatus, xt as SignOutContext, y as QueryHash, yt as ImpersonationCandidate, z as SyncHealthConfig, zt as TokenClaims } from "./types.js";
1
+ import { $ as WebPushSubscribeOptions, A as RunOptions, At as LocalStore, B as SyncHealthStatus, Bt as TokenClaims, C as QueryTimeToLive, Ct as SignOutHook, D as RecordVersionArray, Dt as createAuthEventSystem, E as ReconnectConfig, Et as AuthEventTypes, F as StorageHealth, Ft as ImpersonationInfo, G as WebPushConfig, H as UpdateOptions, Ht as impersonationFromToken, I as StorageHealthStatus, It as ImpersonationTimerHost, J as WebPushErrorCode, K as WebPushDevice, L as StoreType, Lt as ImpersonationTimers, M as Sp00kyConfig, Mt as EventDefinition, N as Sp00kyQueryResult, Nt as EventSystem, O as RecordVersionDiff, Ot as RemoteDatabaseService, P as Sp00kyQueryResultPromise, Pt as IMPERSONATION_ACCESS, Q as WebPushModule, R as SyncHealth, Rt as ORIGIN_TOKEN_KEY, S as QueryStatusCallback, St as SignOutContext, T as QueryUpdateCallback, Tt as AuthEventTypeMap, U as PUSH_ENDPOINT_KEY, Ut as renewDelayMs, V as TimingPhase, Vt as decodeTokenClaims, W as PushDeviceKind, Wt as sameImpersonation, X as WebPushMessage, Y as WebPushInfo, Z as WebPushMessageInput, _ as QueryConfig, _t as isPushPayload, a as Level, at as WebPushUpdateOptions, b as QueryState, bt as ImpersonationCandidate, c as MutationCallback, ct as BridgeTokenMessage, d as PersistenceClient, dt as PushNotificationAction, et as WebPushSupport, f as PhaseStat, ft as PushOp, g as QueryAuthorityCallback, gt as WebPushClientMessage, h as PreloadRefresh, ht as PushUrgency, i as InlineRow, it as WebPushUnsupportedReason, j as ServerViewMeta, jt as SealedQuery, k as RegistrationTimings, kt as Logger$1, l as MutationEvent, lt as PUSH_PAYLOAD_VERSION, m as PreloadOptions, mt as PushPayloadKind, n as DebounceOptions, nt as WebPushSyncStatus, o as MATERIALIZATION_SAMPLE_WINDOW, ot as detectWebPushSupport, p as PinoTransmit, pt as PushPayload, q as WebPushError, r as EventSubscriptionOptions, rt as WebPushUnsubscribeOptions, s as MembershipOutcome, st as BridgeSignOutMessage, t as ConnectionState, tt as WebPushSyncOptions, u as MutationEventType, ut as PushNotification, v as QueryConfigRecord, vt as ActiveImpersonation, w as QueryTimings, wt as AuthEventSystem, x as QueryStatus, xt as SIGN_OUT_HOOK_TIMEOUT_MS, y as QueryHash, yt as AuthService, z as SyncHealthConfig, zt as SESSION_POLL_MS } from "./types.js";
2
2
  import { BackendNames, BackendRoutes, BucketNames, FinalQuery, GetTable, QueryBuilder, QueryOptions, QueryPlan, RecordId, RoutePayload, SchemaStructure, TableModel, TableNames } from "@spooky-sync/query-builder";
3
3
  import * as surrealdb0 from "surrealdb";
4
4
  import { Duration, RecordId as RecordId$1, Surreal } from "surrealdb";
@@ -2135,4 +2135,4 @@ declare function textToHtml(text: string): string;
2135
2135
  */
2136
2136
 
2137
2137
  //#endregion
2138
- export { ActiveImpersonation, AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BLURHASH_IMAGE_EXTENSIONS, type BlobCacheStats, type BlobEntry, type BlobKey, type BlobReadOptions, type BlobUrlLease, type BlurhashEncodeOptions, type BlurhashSetting, type BridgeSignOutMessage, type BridgeTokenMessage, BucketHandle, BucketPutOptions, BucketPutResult, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DEFAULT_LOCAL_OP_TIMEOUT_MS, DebounceOptions, EventSubscriptionOptions, type FailedMutationRow as FailedMutation, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagOverride, type FeatureFlagSnapshot, IMPERSONATION_ACCESS, ImpersonationCandidate, ImpersonationInfo, ImpersonationTimerHost, ImpersonationTimers, InlineRow, Level, LocalOpTimeoutError, MATERIALIZATION_SAMPLE_WINDOW, MembershipOutcome, MutationCallback, MutationEvent, MutationEventType, ORIGIN_TOKEN_KEY, PUSH_ENDPOINT_KEY, PUSH_PAYLOAD_VERSION, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, type PushNotification, type PushNotificationAction, type PushOp, type PushPayload, type PushPayloadKind, type PushUrgency, QueryAuthorityCallback, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, SESSION_POLL_MS, SIGN_OUT_HOOK_TIMEOUT_MS, ServerViewMeta, SignOutContext, SignOutHook, Sp00kyClient, Sp00kyClientDeps, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, TokenClaims, UpdateOptions, type WebPushClientMessage, type WebPushConfig, type WebPushDevice, WebPushError, type WebPushErrorCode, type WebPushInfo, type WebPushMessage, type WebPushMessageInput, WebPushModule, type WebPushSubscribeOptions, type WebPushSupport, type WebPushSyncOptions, type WebPushSyncStatus, type WebPushUnsubscribeOptions, type WebPushUnsupportedReason, type WebPushUpdateOptions, blurhashSidecarPath, bucketContentToBlob, createAuthEventSystem, cursorColorFromName, decode as decodeBlurhash, decodeTokenClaims, detectWebPushSupport, encode as encodeBlurhash, encodeImageToBlurhash, fileToUint8Array, impersonationFromToken, isBlurhashValid, isImagePath, isPushPayload, renewDelayMs, sameImpersonation, semverGt, textToHtml };
2138
+ export { ActiveImpersonation, AppReleaseHandle, AppReleaseModule, type AppReleaseOptions, type AppReleaseSnapshot, AuthEventSystem, AuthEventTypeMap, AuthEventTypes, AuthService, BLURHASH_IMAGE_EXTENSIONS, type BlobCacheStats, type BlobEntry, type BlobKey, type BlobReadOptions, type BlobUrlLease, type BlurhashEncodeOptions, type BlurhashSetting, type BridgeSignOutMessage, type BridgeTokenMessage, BucketHandle, BucketPutOptions, BucketPutResult, CURSOR_COLORS, ConnectionState, CrdtField, CrdtManager, DEFAULT_LOCAL_OP_TIMEOUT_MS, DebounceOptions, EventSubscriptionOptions, type FailedMutationRow as FailedMutation, FeatureFlagHandle, FeatureFlagModule, type FeatureFlagOptions, type FeatureFlagOverride, type FeatureFlagSnapshot, IMPERSONATION_ACCESS, ImpersonationCandidate, ImpersonationInfo, ImpersonationTimerHost, ImpersonationTimers, InlineRow, Level, LocalOpTimeoutError, MATERIALIZATION_SAMPLE_WINDOW, MembershipOutcome, MutationCallback, MutationEvent, MutationEventType, ORIGIN_TOKEN_KEY, PUSH_ENDPOINT_KEY, PUSH_PAYLOAD_VERSION, PersistenceClient, PhaseStat, PinoTransmit, PreloadOptions, PreloadRefresh, type PushDeviceKind, type PushNotification, type PushNotificationAction, type PushOp, type PushPayload, type PushPayloadKind, type PushUrgency, QueryAuthorityCallback, QueryConfig, QueryConfigRecord, QueryHash, QueryState, QueryStatus, QueryStatusCallback, QueryTimeToLive, QueryTimings, QueryUpdateCallback, ReconnectConfig, RecordVersionArray, RecordVersionDiff, RegistrationTimings, RunOptions, SESSION_POLL_MS, SIGN_OUT_HOOK_TIMEOUT_MS, ServerViewMeta, SignOutContext, SignOutHook, Sp00kyClient, Sp00kyClientDeps, Sp00kyConfig, Sp00kyQueryResult, Sp00kyQueryResultPromise, StorageHealth, StorageHealthStatus, StoreType, SyncHealth, SyncHealthConfig, SyncHealthStatus, TimingPhase, TokenClaims, UpdateOptions, type WebPushClientMessage, type WebPushConfig, type WebPushDevice, WebPushError, type WebPushErrorCode, type WebPushInfo, type WebPushMessage, type WebPushMessageInput, WebPushModule, type WebPushSubscribeOptions, type WebPushSupport, type WebPushSyncOptions, type WebPushSyncStatus, type WebPushUnsubscribeOptions, type WebPushUnsupportedReason, type WebPushUpdateOptions, blurhashSidecarPath, bucketContentToBlob, createAuthEventSystem, cursorColorFromName, decode as decodeBlurhash, decodeTokenClaims, detectWebPushSupport, encode as encodeBlurhash, encodeImageToBlurhash, fileToUint8Array, impersonationFromToken, isBlurhashValid, isImagePath, isPushPayload, renewDelayMs, sameImpersonation, semverGt, textToHtml };
package/dist/index.js CHANGED
@@ -705,6 +705,10 @@ function toDevice(row, thisEndpoint) {
705
705
  return {
706
706
  id: recordIdString(row.id),
707
707
  endpoint: String(row.endpoint ?? ""),
708
+ kind: row.kind === "apns" || row.kind === "fcm" ? row.kind : "web",
709
+ platform: row.platform ?? void 0,
710
+ appId: row.app_id ?? void 0,
711
+ environment: row.environment ?? void 0,
708
712
  kid: typeof row.kid === "string" ? row.kid : "",
709
713
  label: row.label ?? void 0,
710
714
  userAgent: row.user_agent ?? void 0,
@@ -874,10 +878,12 @@ var WebPushModule = class {
874
878
  info(options = {}) {
875
879
  if (!this.infoCache || options.refresh) {
876
880
  const pending = this.call("RETURN fn::push::info()").then((r) => {
881
+ const providers = Array.isArray(r?.providers) ? r.providers.filter((p) => p === "web" || p === "apns" || p === "fcm") : void 0;
877
882
  const info = dropUndefined({
878
883
  enabled: r?.enabled === true && typeof r.publicKey === "string",
879
884
  publicKey: typeof r?.publicKey === "string" ? r.publicKey : void 0,
880
- kid: typeof r?.kid === "string" ? r.kid : void 0
885
+ kid: typeof r?.kid === "string" ? r.kid : void 0,
886
+ providers
881
887
  });
882
888
  this.lastInfo = info;
883
889
  return info;
@@ -1718,8 +1724,8 @@ function selfAllowlistedVariant(flag, userId) {
1718
1724
 
1719
1725
  //#endregion
1720
1726
  //#region src/modules/devtools/index.ts
1721
- const CORE_VERSION = "0.0.1-canary.281";
1722
- const WASM_VERSION = "0.0.1-canary.281";
1727
+ const CORE_VERSION = "0.0.1-canary.283";
1728
+ const WASM_VERSION = "0.0.1-canary.283";
1723
1729
  const SURREAL_VERSION = "3.0.3";
1724
1730
  var DevToolsService = class DevToolsService {
1725
1731
  eventsHistory = [];
@@ -13752,7 +13758,7 @@ function createTabsCoordinator(config, s, host) {
13752
13758
  return new TabsCoordinator({
13753
13759
  tabId: s.tabId,
13754
13760
  fingerprint: computeTabsFingerprint({
13755
- coreVersion: "0.0.1-canary.281",
13761
+ coreVersion: "0.0.1-canary.283",
13756
13762
  schemaHash: hash53(config.schemaSurql),
13757
13763
  endpoint: config.database.endpoint ?? "",
13758
13764
  namespace: config.database.namespace,
package/dist/types.d.ts CHANGED
@@ -989,12 +989,28 @@ interface WebPushInfo {
989
989
  publicKey?: string;
990
990
  /** Id of that key. */
991
991
  kid?: string;
992
+ /**
993
+ * Every kind of device the project reaches: `web` once a VAPID key is
994
+ * published, `apns` / `fcm` when `push.apns` / `push.fcm` are configured.
995
+ */
996
+ providers?: PushDeviceKind[];
992
997
  }
998
+ /** How a device is reached: a browser (Web Push), APNs or FCM. */
999
+ type PushDeviceKind = 'web' | 'apns' | 'fcm';
993
1000
  interface WebPushDevice {
994
1001
  /** `_00_push_subscription:<id>` */
995
1002
  id: string;
1003
+ /** Web: the push service URL. Native: `<kind>:<token>`. */
996
1004
  endpoint: string;
997
- /** Key id the device subscribed under. */
1005
+ /** Rows the Flutter / native side registered are `apns` or `fcm`. */
1006
+ kind: PushDeviceKind;
1007
+ /** `web`, `ios`, `android` or `macos`. */
1008
+ platform?: string;
1009
+ /** Native: bundle id / package name. */
1010
+ appId?: string;
1011
+ /** APNs: `sandbox` or `production`. */
1012
+ environment?: string;
1013
+ /** Key id a web device subscribed under (empty for native ones). */
998
1014
  kid: string;
999
1015
  label?: string;
1000
1016
  userAgent?: string;
@@ -1988,4 +2004,4 @@ interface DebounceOptions {
1988
2004
  delay?: number;
1989
2005
  }
1990
2006
  //#endregion
1991
- export { WebPushSupport as $, RunOptions as A, SealedQuery as At, SyncHealthStatus as B, decodeTokenClaims as Bt, QueryTimeToLive as C, AuthEventSystem as Ct, RecordVersionArray as D, RemoteDatabaseService as Dt, ReconnectConfig as E, createAuthEventSystem as Et, StorageHealth as F, ImpersonationTimerHost as Ft, WebPushDevice as G, UpdateOptions as H, renewDelayMs as Ht, StorageHealthStatus as I, ImpersonationTimers as It, WebPushInfo as J, WebPushError as K, StoreType as L, ORIGIN_TOKEN_KEY as Lt, Sp00kyConfig as M, EventSystem as Mt, Sp00kyQueryResult as N, IMPERSONATION_ACCESS as Nt, RecordVersionDiff as O, Logger$1 as Ot, Sp00kyQueryResultPromise as P, ImpersonationInfo as Pt, WebPushSubscribeOptions as Q, SyncHealth as R, SESSION_POLL_MS as Rt, QueryStatusCallback as S, SignOutHook as St, QueryUpdateCallback as T, AuthEventTypes as Tt, PUSH_ENDPOINT_KEY as U, sameImpersonation as Ut, TimingPhase as V, impersonationFromToken as Vt, WebPushConfig as W, WebPushMessageInput as X, WebPushMessage as Y, WebPushModule as Z, QueryConfig as _, ActiveImpersonation as _t, Level$1 as a, detectWebPushSupport as at, QueryState as b, SIGN_OUT_HOOK_TIMEOUT_MS as bt, MutationCallback as c, PUSH_PAYLOAD_VERSION as ct, PersistenceClient as d, PushOp as dt, WebPushSyncOptions as et, PhaseStat as f, PushPayload as ft, QueryAuthorityCallback as g, isPushPayload as gt, PreloadRefresh as h, WebPushClientMessage as ht, InlineRow as i, WebPushUpdateOptions as it, ServerViewMeta as j, EventDefinition as jt, RegistrationTimings as k, LocalStore as kt, MutationEvent as l, PushNotification as lt, PreloadOptions as m, PushUrgency as mt, DebounceOptions as n, WebPushUnsubscribeOptions as nt, MATERIALIZATION_SAMPLE_WINDOW as o, BridgeSignOutMessage as ot, PinoTransmit as p, PushPayloadKind as pt, WebPushErrorCode as q, EventSubscriptionOptions as r, WebPushUnsupportedReason as rt, MembershipOutcome as s, BridgeTokenMessage as st, ConnectionState as t, WebPushSyncStatus as tt, MutationEventType as u, PushNotificationAction as ut, QueryConfigRecord as v, AuthService as vt, QueryTimings as w, AuthEventTypeMap as wt, QueryStatus as x, SignOutContext as xt, QueryHash as y, ImpersonationCandidate as yt, SyncHealthConfig as z, TokenClaims as zt };
2007
+ export { WebPushSubscribeOptions as $, RunOptions as A, LocalStore as At, SyncHealthStatus as B, TokenClaims as Bt, QueryTimeToLive as C, SignOutHook as Ct, RecordVersionArray as D, createAuthEventSystem as Dt, ReconnectConfig as E, AuthEventTypes as Et, StorageHealth as F, ImpersonationInfo as Ft, WebPushConfig as G, UpdateOptions as H, impersonationFromToken as Ht, StorageHealthStatus as I, ImpersonationTimerHost as It, WebPushErrorCode as J, WebPushDevice as K, StoreType as L, ImpersonationTimers as Lt, Sp00kyConfig as M, EventDefinition as Mt, Sp00kyQueryResult as N, EventSystem as Nt, RecordVersionDiff as O, RemoteDatabaseService as Ot, Sp00kyQueryResultPromise as P, IMPERSONATION_ACCESS as Pt, WebPushModule as Q, SyncHealth as R, ORIGIN_TOKEN_KEY as Rt, QueryStatusCallback as S, SignOutContext as St, QueryUpdateCallback as T, AuthEventTypeMap as Tt, PUSH_ENDPOINT_KEY as U, renewDelayMs as Ut, TimingPhase as V, decodeTokenClaims as Vt, PushDeviceKind as W, sameImpersonation as Wt, WebPushMessage as X, WebPushInfo as Y, WebPushMessageInput as Z, QueryConfig as _, isPushPayload as _t, Level$1 as a, WebPushUpdateOptions as at, QueryState as b, ImpersonationCandidate as bt, MutationCallback as c, BridgeTokenMessage as ct, PersistenceClient as d, PushNotificationAction as dt, WebPushSupport as et, PhaseStat as f, PushOp as ft, QueryAuthorityCallback as g, WebPushClientMessage as gt, PreloadRefresh as h, PushUrgency as ht, InlineRow as i, WebPushUnsupportedReason as it, ServerViewMeta as j, SealedQuery as jt, RegistrationTimings as k, Logger$1 as kt, MutationEvent as l, PUSH_PAYLOAD_VERSION as lt, PreloadOptions as m, PushPayloadKind as mt, DebounceOptions as n, WebPushSyncStatus as nt, MATERIALIZATION_SAMPLE_WINDOW as o, detectWebPushSupport as ot, PinoTransmit as p, PushPayload as pt, WebPushError as q, EventSubscriptionOptions as r, WebPushUnsubscribeOptions as rt, MembershipOutcome as s, BridgeSignOutMessage as st, ConnectionState as t, WebPushSyncOptions as tt, MutationEventType as u, PushNotification as ut, QueryConfigRecord as v, ActiveImpersonation as vt, QueryTimings as w, AuthEventSystem as wt, QueryStatus as x, SIGN_OUT_HOOK_TIMEOUT_MS as xt, QueryHash as y, AuthService as yt, SyncHealthConfig as z, SESSION_POLL_MS as zt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spooky-sync/core",
3
- "version": "0.0.1-canary.281",
3
+ "version": "0.0.1-canary.283",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -75,8 +75,8 @@
75
75
  }
76
76
  },
77
77
  "dependencies": {
78
- "@spooky-sync/query-builder": "0.0.1-canary.281",
79
- "@spooky-sync/ssp-wasm": "0.0.1-canary.281",
78
+ "@spooky-sync/query-builder": "0.0.1-canary.283",
79
+ "@spooky-sync/ssp-wasm": "0.0.1-canary.283",
80
80
  "@sqlite.org/sqlite-wasm": "3.53.0-build1",
81
81
  "@surrealdb/wasm": "^3.0.3",
82
82
  "blurhash": "^2.0.5",
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ export {
25
25
  type WebPushUnsupportedReason,
26
26
  type WebPushInfo,
27
27
  type WebPushDevice,
28
+ type PushDeviceKind,
28
29
  type WebPushSubscribeOptions,
29
30
  type WebPushUnsubscribeOptions,
30
31
  type WebPushSyncOptions,
@@ -37,6 +37,10 @@ interface Row {
37
37
  failures?: number;
38
38
  current?: boolean;
39
39
  disabled_at?: Date;
40
+ kind?: string;
41
+ platform?: string;
42
+ app_id?: string;
43
+ environment?: string;
40
44
  }
41
45
 
42
46
  function harness(
@@ -780,6 +784,29 @@ describe('WebPushModule devices and direct messages', () => {
780
784
  await expect(h.mod.test()).rejects.toMatchObject({ code: 'server' });
781
785
  });
782
786
 
787
+ it('lists native devices (APNs / FCM) with their kind, never as this browser', async () => {
788
+ const h = harness({ permission: 'granted' });
789
+ h.server.info = { enabled: true, publicKey: KEY, kid: 'kid1', providers: ['web', 'apns', 'bogus'] };
790
+ h.server.rows.push({
791
+ id: new RecordId('_00_push_subscription', 'phone'),
792
+ endpoint: 'apns:ab12',
793
+ kind: 'apns',
794
+ platform: 'ios',
795
+ app_id: 'im.app',
796
+ environment: 'sandbox',
797
+ kid: '',
798
+ current: true,
799
+ });
800
+ expect((await h.mod.info({ refresh: true })).providers).toEqual(['web', 'apns']);
801
+ await h.mod.subscribe({ label: 'Laptop' });
802
+ const devices = await h.mod.devices();
803
+ const phone = devices.find((d) => d.endpoint === 'apns:ab12')!;
804
+ expect(phone).toMatchObject({ kind: 'apns', platform: 'ios', appId: 'im.app', environment: 'sandbox', thisDevice: false });
805
+ expect(devices.find((d) => d.thisDevice)?.kind).toBe('web');
806
+ // A native row never makes the browser resubscribe.
807
+ expect(await h.mod.sync()).toBe('ok');
808
+ });
809
+
783
810
  it('caches info until refreshed; a failed read is not cached', async () => {
784
811
  const h = harness();
785
812
  expect(await h.mod.info()).toEqual({ enabled: true, publicKey: KEY, kid: 'kid1' });
@@ -77,13 +77,30 @@ export interface WebPushInfo {
77
77
  publicKey?: string;
78
78
  /** Id of that key. */
79
79
  kid?: string;
80
+ /**
81
+ * Every kind of device the project reaches: `web` once a VAPID key is
82
+ * published, `apns` / `fcm` when `push.apns` / `push.fcm` are configured.
83
+ */
84
+ providers?: PushDeviceKind[];
80
85
  }
81
86
 
87
+ /** How a device is reached: a browser (Web Push), APNs or FCM. */
88
+ export type PushDeviceKind = 'web' | 'apns' | 'fcm';
89
+
82
90
  export interface WebPushDevice {
83
91
  /** `_00_push_subscription:<id>` */
84
92
  id: string;
93
+ /** Web: the push service URL. Native: `<kind>:<token>`. */
85
94
  endpoint: string;
86
- /** Key id the device subscribed under. */
95
+ /** Rows the Flutter / native side registered are `apns` or `fcm`. */
96
+ kind: PushDeviceKind;
97
+ /** `web`, `ios`, `android` or `macos`. */
98
+ platform?: string;
99
+ /** Native: bundle id / package name. */
100
+ appId?: string;
101
+ /** APNs: `sandbox` or `production`. */
102
+ environment?: string;
103
+ /** Key id a web device subscribed under (empty for native ones). */
87
104
  kid: string;
88
105
  label?: string;
89
106
  userAgent?: string;
@@ -260,6 +277,10 @@ interface StoredEndpoint {
260
277
  interface DeviceRow {
261
278
  id?: unknown;
262
279
  endpoint?: string;
280
+ kind?: string | null;
281
+ platform?: string | null;
282
+ app_id?: string | null;
283
+ environment?: string | null;
263
284
  kid?: string;
264
285
  label?: string | null;
265
286
  user_agent?: string | null;
@@ -309,6 +330,10 @@ function toDevice(row: DeviceRow, thisEndpoint: string | null): WebPushDevice {
309
330
  return {
310
331
  id: recordIdString(row.id),
311
332
  endpoint: String(row.endpoint ?? ''),
333
+ kind: row.kind === 'apns' || row.kind === 'fcm' ? row.kind : 'web',
334
+ platform: row.platform ?? undefined,
335
+ appId: row.app_id ?? undefined,
336
+ environment: row.environment ?? undefined,
312
337
  kid: typeof row.kid === 'string' ? row.kid : '',
313
338
  label: row.label ?? undefined,
314
339
  userAgent: row.user_agent ?? undefined,
@@ -489,11 +514,16 @@ export class WebPushModule {
489
514
  enabled?: boolean;
490
515
  publicKey?: string | null;
491
516
  kid?: string | null;
517
+ providers?: unknown;
492
518
  } | null>('RETURN fn::push::info()').then((r) => {
519
+ const providers = Array.isArray(r?.providers)
520
+ ? (r.providers.filter((p) => p === 'web' || p === 'apns' || p === 'fcm') as PushDeviceKind[])
521
+ : undefined;
493
522
  const info: WebPushInfo = dropUndefined({
494
523
  enabled: r?.enabled === true && typeof r.publicKey === 'string',
495
524
  publicKey: typeof r?.publicKey === 'string' ? r.publicKey : undefined,
496
525
  kid: typeof r?.kid === 'string' ? r.kid : undefined,
526
+ providers,
497
527
  });
498
528
  this.lastInfo = info;
499
529
  return info;