@unisim/sdk 0.157.0 → 0.159.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,17 @@ export interface UserCounts {
9
9
  total: number;
10
10
  /** Of those, how many beat in the last two minutes. */
11
11
  live: number;
12
+ /**
13
+ * Of `total`, how many were last seen in a browser / in an installed app.
14
+ *
15
+ * ⚠️ `web + native <= total`, and on a figure that has not turned over since
16
+ * 0.158.0 shipped it is a good deal less: a person whose last beat predates
17
+ * the platform column is in neither bucket. `null` where the split is not
18
+ * available at all — a database without migration 0187, or a product whose
19
+ * total comes from its own account counter (0185).
20
+ */
21
+ web?: number | null;
22
+ native?: number | null;
12
23
  }
13
24
  /** Which figure the line is showing. Not a product code — 'suite' is all of them. */
14
25
  export type CountScope = 'app' | 'suite';
@@ -17,6 +28,15 @@ export interface ProductCounts extends UserCounts {
17
28
  /** The product_code the beat was written under — 'pdf', 'fire_assess', … */
18
29
  product: string;
19
30
  }
31
+ /**
32
+ * What the beat reports itself as, matching migration 0187's CHECK.
33
+ *
34
+ * ⚠️ `null` is a real answer, not a failure: it means "this install cannot say"
35
+ * — a Capacitor bridge that has not come up yet, or an Electron build on an OS
36
+ * this does not recognise. The server keeps whatever platform an earlier beat
37
+ * established rather than erasing it, so a single unknown beat costs nothing.
38
+ */
39
+ export type DevicePlatform = 'web' | 'ios' | 'android' | 'windows' | 'macos' | 'linux';
20
40
  /** ⚠️ **Legacy, and only ever deleted now.** It used to remember whichever
21
41
  * figure a viewer last switched the line to, which meant one tap made every
22
42
  * app on that browser open on its own figure for ever. The line now always
@@ -25,23 +45,80 @@ export interface ProductCounts extends UserCounts {
25
45
  * do not carry a dead key around. */
26
46
  export declare const COUNT_SCOPE_KEY = "unisim:user-count-scope";
27
47
  export declare function getInstallId(): string | null;
28
- /** Report this install as here now. Never throws; resolves whether it landed. */
48
+ /**
49
+ * True in anything the person INSTALLED — a Capacitor container or an Electron
50
+ * desktop build — as opposed to a page they navigated to.
51
+ *
52
+ * This is what decides that the beat goes immediately rather than waiting for
53
+ * an interaction, so it deliberately errs towards "no": a browser wrongly
54
+ * treated as an app would put page loads back in the total, which is the whole
55
+ * thing this release takes out.
56
+ *
57
+ * ⚠️ Electron is read off `navigator.userAgent` and the preload bridge rather
58
+ * than imported from `desktop.ts`, which pulls React in — and this file has
59
+ * none on purpose (Universal AI is Svelte).
60
+ */
61
+ export declare function isInstalledApp(): boolean;
62
+ /**
63
+ * What this install reports itself as to `app_presence_beat`.
64
+ *
65
+ * ⚠️ An installed app that cannot name its OS answers `null`, NOT `'web'`.
66
+ * Migration 0187 keeps the platform an earlier beat established when a beat
67
+ * says nothing, so an unknown is a missed chance to record rather than a wrong
68
+ * record — and a desktop app quietly filed under "web" is the exact figure
69
+ * this feature exists to get right.
70
+ */
71
+ export declare function devicePlatform(): DevicePlatform | null;
72
+ /**
73
+ * What counts as the person doing something. A scroll and a wheel are in the
74
+ * list on purpose: reading a page IS using it, and a reader who never clicks
75
+ * is not the drive-by this gate is meant to drop.
76
+ */
77
+ export declare const INTERACTION_EVENTS: readonly ["pointerdown", "keydown", "touchstart", "wheel", "scroll"];
78
+ /** Whether this page has seen a real interaction yet. Exported for tests and
79
+ * for an app that wants to hold something else back on the same rule. */
80
+ export declare function hasInteracted(): boolean;
81
+ /**
82
+ * Run `fn` on the first interaction — or straight away if one has already
83
+ * happened on this page. Returns a function that cancels the wait.
84
+ *
85
+ * ⚠️ The flag is module-level, so an app that restarts presence (signing in
86
+ * does) does not put the gate back in front of somebody who has been clicking
87
+ * for ten minutes.
88
+ */
89
+ export declare function onFirstInteraction(fn: () => void): () => void;
90
+ /**
91
+ * Report this install as here now. Never throws; resolves whether it landed.
92
+ */
29
93
  export declare function beatPresence(supabase: SupabaseClient, product: ProductCode): Promise<boolean>;
94
+ export interface PresenceOptions {
95
+ /**
96
+ * Web only: hold the first beat until the person interacts with the page
97
+ * (see {@link INTERACTION_EVENTS}). Default **true** since 0.158.0.
98
+ *
99
+ * ⚠️ Turn it off for a surface nobody touches but everybody sees — a wall
100
+ * display, a kiosk, a screensaver — where "loaded it" really is the whole of
101
+ * the usage. It does NOT need turning off for an installed app: those never
102
+ * wait in the first place.
103
+ */
104
+ requireInteraction?: boolean;
105
+ }
30
106
  /**
31
- * Beat now, then every {@link PRESENCE_BEAT_MS} while the page is visible, and
32
- * straight away when it becomes visible again. Returns a stop function.
107
+ * Beat now — or, on the web, at the first interaction — then every
108
+ * {@link PRESENCE_BEAT_MS} while the page is visible, and straight away when it
109
+ * becomes visible again. Returns a stop function.
33
110
  *
34
111
  * A hidden page does not beat: a backgrounded tab is not someone using the app,
35
112
  * and it drops out of "live" within two minutes on its own.
36
113
  */
37
- export declare function startPresence(supabase: SupabaseClient, product: ProductCode): () => void;
114
+ export declare function startPresence(supabase: SupabaseClient, product: ProductCode, options?: PresenceOptions): () => void;
38
115
  /** The last counts this origin read for `product` (or, with 'suite', for the
39
116
  * whole suite), or null. */
40
117
  export declare function cachedUserCounts(product: ProductCode | 'suite'): UserCounts | null;
41
118
  /**
42
- * Read `{ total, live }` for `product`. Waits (up to 3 s) for this page's
43
- * pending beat so the reader is in the figure. Resolves null on any failure —
44
- * the caller hides its line rather than showing a wrong number.
119
+ * Read `{ total, live, web, native }` for `product`. Waits (up to 3 s) for this
120
+ * page's pending beat so the reader is in the figure. Resolves null on any
121
+ * failure — the caller hides its line rather than showing a wrong number.
45
122
  */
46
123
  export declare function fetchUserCounts(supabase: SupabaseClient, product: ProductCode, scope?: CountScope): Promise<UserCounts | null>;
47
124
  /** The last breakdown this origin read, or null. Same job as
@@ -49,8 +126,9 @@ export declare function fetchUserCounts(supabase: SupabaseClient, product: Produ
49
126
  * swaps it for the fresh one when it lands, rather than opening empty. */
50
127
  export declare function cachedUserCountsByProduct(): ProductCounts[] | null;
51
128
  /**
52
- * `{ product, total, live }` for every product anybody has ever opened, biggest
53
- * first — the breakdown behind the suite figure (migration 0184).
129
+ * `{ product, total, live, web, native }` for every product anybody has ever
130
+ * opened, biggest first — the breakdown behind the suite figure (migration
131
+ * 0184).
54
132
  *
55
133
  * Resolves null on any failure, exactly like {@link fetchUserCounts}, so a
56
134
  * caller shows nothing rather than a wrong number. ⚠️ **That includes an app
@@ -62,12 +140,45 @@ export declare function cachedUserCountsByProduct(): ProductCounts[] | null;
62
140
  * ⚠️ The rows do NOT sum to `fetchUserCounts(_, _, 'suite').total`: an account
63
141
  * using four apps is four rows here and one person there. Never add them up to
64
142
  * produce a total — the suite figure is the total.
143
+ *
144
+ * ⚠️ `web`/`native` are null on a row whose total comes from that app's own
145
+ * account counter (migration 0185, the Bipolar apps): the counter has no
146
+ * platform in it, so a split beside it would describe a different, smaller
147
+ * population than the total next to it.
65
148
  */
66
149
  export declare function fetchUserCountsByProduct(supabase: SupabaseClient): Promise<ProductCounts[] | null>;
150
+ /** How many Universal IDs exist, and how many are in use right now (migration
151
+ * 0188). Anonymous guest sessions are not IDs and are not in either figure. */
152
+ export interface UniversalIdCounts {
153
+ created: number;
154
+ live: number;
155
+ }
156
+ /** The last Universal ID counts this origin read, or null. */
157
+ export declare function cachedUniversalIdCounts(): UniversalIdCounts | null;
158
+ /**
159
+ * `{ created, live }` for Universal IDs — the row at the top of the breakdown
160
+ * (James, 2026-09-27). Resolves null on any failure, including a database
161
+ * without migration 0188, so the popup leaves the row out rather than showing
162
+ * a wrong number.
163
+ */
164
+ export declare function fetchUniversalIdCounts(supabase: SupabaseClient): Promise<UniversalIdCounts | null>;
165
+ /** "52 created", "2 live" — the Universal IDs row's figures, as separate
166
+ * phrases for the same reason as {@link userCountParts}. */
167
+ export declare function universalIdParts(language: string | null | undefined, counts: UniversalIdCounts): string[];
67
168
  /**
68
169
  * "There are 1,234 total users (5 live)" — or, for the 'suite' scope, "There
69
170
  * are a total of 1,234 users across all UNI·SIM apps (5 live)" — in `language`,
70
171
  * with the numbers grouped the way that language groups them.
71
172
  */
72
173
  export declare function formatUserCounts(language: string | null | undefined, counts: UserCounts, scope?: CountScope): string;
174
+ /**
175
+ * The figures behind one row of the breakdown, as separate phrases — "1,234
176
+ * users", "900 web", "334 native", "5 live" — so the caller can lay them out
177
+ * and let them WRAP rather than measuring a single string (James, 2026-09-21:
178
+ * "show web | native | live if there's enough space").
179
+ *
180
+ * The split is left out entirely when it is not available, rather than shown as
181
+ * "0 web · 0 native": see {@link UserCounts.web}.
182
+ */
183
+ export declare function userCountParts(language: string | null | undefined, counts: UserCounts): string[];
73
184
  //# sourceMappingURL=presence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,yDAAyD;AACzD,eAAO,MAAM,cAAc,sBAAsB,CAAA;AAEjD,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,QAAS,CAAA;AAEtC,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;CACb;AAED,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,CAAA;AAExC,+EAA+E;AAC/E,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;sCAKsC;AACtC,eAAO,MAAM,eAAe,4BAA4B,CAAA;AA4BxD,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAY5C;AAeD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAe7F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI,CAYxF;AAED;6BAC6B;AAC7B,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,IAAI,CASlF;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,WAAW,EACpB,KAAK,GAAE,UAAkB,GACxB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuB5B;AAED;;2EAE2E;AAC3E,wBAAgB,yBAAyB,IAAI,aAAa,EAAE,GAAG,IAAI,CAQlE;AAOD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAwBjC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,MAAM,EAAE,UAAU,EAClB,KAAK,GAAE,UAAkB,GACxB,MAAM,CAUR"}
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"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"}