palbase 0.8.1 → 0.9.1

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.
Files changed (72) hide show
  1. package/dist/ai/index.js +2 -2
  2. package/dist/backend/engine.js +2 -2
  3. package/dist/backend/env.js +1 -1
  4. package/dist/backend/index.js +3 -3
  5. package/dist/backend/stack.js +1 -1
  6. package/dist/backend/test.js +2 -2
  7. package/dist/chunks/2DWAHEYE.js +22 -0
  8. package/dist/chunks/{73R33XMD.js → 2GF6TA4C.js} +1 -1
  9. package/dist/chunks/4DDSGFFD.js +95 -0
  10. package/dist/chunks/{7XVI5JLP.js → 5UI3Q5JO.js} +2 -2
  11. package/dist/chunks/{RZ525VLP.js → 6RX5KVMR.js} +2 -2
  12. package/dist/chunks/{6RXVXPHC.js → BKOIWNJP.js} +1 -1
  13. package/dist/chunks/{ZSKMWUIQ.js → CJPHHWRE.js} +2 -2
  14. package/dist/chunks/{NSQAN2ME.js → CNUOWB6T.js} +2 -2
  15. package/dist/chunks/{RYI2OB52.js → I56MGYTD.js} +2 -2
  16. package/dist/chunks/{KJWHR5BP.js → KUFTU2AJ.js} +2 -2
  17. package/dist/chunks/MALOKTVW.js +13 -0
  18. package/dist/chunks/{CA4VJGGG.js → OV2D7HYG.js} +1 -1
  19. package/dist/chunks/PUILCUFX.js +2 -0
  20. package/dist/chunks/{T2Q5CKLL.js → SE3MXMQD.js} +2 -2
  21. package/dist/chunks/{KHHJRNAO.js → WPE6J77U.js} +1 -1
  22. package/dist/chunks/{KYJJJUEU.js → XGWTZGWR.js} +2 -2
  23. package/dist/chunks/{M4XLAPDF.js → XSPSDUZA.js} +2 -2
  24. package/dist/chunks/{MAYLG7WE.js → YSTCOQFT.js} +2 -2
  25. package/dist/chunks/ZHHMA7V4.js +2 -0
  26. package/dist/chunks/{5RTSNQ4X.js → ZRV54HT6.js} +2 -2
  27. package/dist/cli.js +129 -127
  28. package/dist/refuse/ai.js +1 -1
  29. package/dist/refuse/backend.js +1 -1
  30. package/dist/refuse/server.js +1 -1
  31. package/dist/server/index.js +2 -2
  32. package/dist/server/testing.js +2 -2
  33. package/dist/web/index.js +2 -2
  34. package/dist/web/message.js +2 -0
  35. package/dist/web/palbe.js +2 -2
  36. package/dist/web/paywall.js +2 -2
  37. package/dist/web/push-sw.js +2 -0
  38. package/dist/web/ssr.js +2 -2
  39. package/dist/web/vite.js +2 -2
  40. package/package.json +7 -2
  41. package/types/backend/src/services.d.ts +2 -2
  42. package/types/internal/src/modules/ai.d.ts +1 -1
  43. package/types/internal/src/modules/analytics.d.ts +618 -1
  44. package/types/internal/src/modules/auth.d.ts +409 -90
  45. package/types/internal/src/modules/events.d.ts +7 -3
  46. package/types/internal/src/modules/flags.d.ts +380 -14
  47. package/types/internal/src/modules/notify.d.ts +2270 -99
  48. package/types/internal/src/modules/purchases.d.ts +672 -9
  49. package/types/server/src/webhooks.d.ts +1 -1
  50. package/types/web/src/analytics.d.ts +56 -5
  51. package/types/web/src/client.d.ts +26 -4
  52. package/types/web/src/consent.d.ts +218 -0
  53. package/types/web/src/flags.d.ts +75 -0
  54. package/types/web/src/inapp.d.ts +129 -0
  55. package/types/web/src/inbox.d.ts +59 -0
  56. package/types/web/src/index.d.ts +9 -3
  57. package/types/web/src/installation.d.ts +16 -0
  58. package/types/web/src/message/index.d.ts +5 -0
  59. package/types/web/src/message/present.d.ts +20 -0
  60. package/types/web/src/notify.d.ts +67 -0
  61. package/types/web/src/paywall/index.d.ts +4 -4
  62. package/types/web/src/paywall/placement.d.ts +11 -12
  63. package/types/web/src/paywall/prices.d.ts +7 -2
  64. package/types/web/src/purchases.d.ts +32 -0
  65. package/types/web/src/{paywall → screen}/assets.d.ts +6 -6
  66. package/types/web/src/screen/render.d.ts +186 -0
  67. package/types/web/src/{paywall → screen}/resolve.d.ts +56 -8
  68. package/dist/chunks/5M3HV3X4.js +0 -2
  69. package/dist/chunks/Q4XTWEAW.js +0 -92
  70. package/dist/chunks/Y6AQWQBE.js +0 -2
  71. package/types/web/src/paywall/render.d.ts +0 -93
  72. /package/types/web/src/{paywall → screen}/icons.d.ts +0 -0
@@ -37,7 +37,7 @@ export declare const DEFAULT_MAX_WEBHOOK_BYTES: number;
37
37
  * The event, once its signature and time are checked; a WebhookSignatureError
38
38
  * otherwise. A PalbaseEvent: a switch on its type narrows its data to what
39
39
  * that type's contract says. An event of a type no contract declares — one a
40
- * newer platform sends, or one whose module declares none yet (user.deleted)
40
+ * newer platform sends, or one whose module declares none yet (secret.changed)
41
41
  * — is none of the switch's cases at run time and reaches its default: take
42
42
  * it there (a 2xx), never throw, or the platform delivers it again and again.
43
43
  * verifyWebhook<T>() reads such an event's data as T.
@@ -1,5 +1,22 @@
1
1
  import type { Transport } from '../../internal/src/transport.js';
2
+ import type { Consent } from './consent.js';
3
+ import { type Installation } from './installation.js';
2
4
  import type { SessionManager } from './session.js';
5
+ /**
6
+ * What is known of the person: the signed-in user, or with no one signed in
7
+ * this browser's anonymous id — whose properties become the user's once they
8
+ * sign in. Kept with the analytics key: nothing is sent while it is not granted.
9
+ */
10
+ export interface People {
11
+ /** Sets each property, replacing what was set. */
12
+ set(properties: Record<string, unknown>): Promise<void>;
13
+ /** Sets each property not set already: where the person first came from. */
14
+ setOnce(properties: Record<string, unknown>): Promise<void>;
15
+ /** Removes these properties. */
16
+ unset(keys: readonly string[]): Promise<void>;
17
+ /** Adds each number to what the property holds (none is 0). */
18
+ add(numbers: Record<string, number>): Promise<void>;
19
+ }
3
20
  export interface AnalyticsOptions {
4
21
  /** A $pageview for the first page and each navigation after it (history and back/forward). Default true. */
5
22
  pageViews?: boolean;
@@ -18,33 +35,55 @@ export interface AnalyticsTiming {
18
35
  now?: () => number;
19
36
  }
20
37
  export declare class Analytics {
38
+ /** The person's events, identify and properties: what says who they are. */
21
39
  private readonly api;
22
40
  private readonly closingApi;
41
+ /** Timings: who they are of goes with them only while analytics is allowed — the user's token included. */
42
+ private readonly perfApi;
43
+ private readonly closingPerfApi;
23
44
  private readonly flushMs;
24
45
  private readonly flushAt;
25
46
  private readonly now;
26
47
  private readonly anonKey;
27
48
  private readonly sessionKey;
49
+ private readonly onceKey;
28
50
  private events;
29
51
  private samples;
30
52
  private timer;
31
53
  private memoryAnon;
32
54
  private memorySession;
55
+ /** What captureOnce sent in the session it names. */
56
+ private memoryOnce;
33
57
  private appVersion;
34
58
  private stopAuto;
35
59
  private readonly session;
60
+ private readonly consent;
36
61
  /** Someone signed in: their identify goes with the next send, never on the sign-in itself. */
37
62
  private identifyPending;
38
- constructor(transport: Transport, session: SessionManager, origin: string, timing?: AnalyticsTiming);
63
+ private readonly listeners;
64
+ /** The person's properties. */
65
+ readonly people: People;
66
+ /**
67
+ * transport sends with the signed-in user's token; keyOnly with the
68
+ * publishable key alone — what goes while analytics is not allowed.
69
+ */
70
+ constructor(transport: Transport, keyOnly: Transport, session: SessionManager, consent: Consent, installation: Installation, origin: string, timing?: AnalyticsTiming);
39
71
  /** Records an event: a name (a letter or $, then letters, digits and _.:-$; 64 at most) and its properties. */
40
72
  capture(event: string, properties?: Record<string, unknown>): void;
73
+ private readOnce;
74
+ private writeOnce;
75
+ /**
76
+ * Told of each event the app captures, here on the device, whatever the person chose of analytics: what an in-app
77
+ * message's trigger is matched with. Nothing leaves the device for it. Returns what stops it.
78
+ */
79
+ onEvent(listener: (event: string, properties?: Record<string, unknown>) => void): () => void;
41
80
  /**
42
81
  * Records a page view: the page's address (the current one unless given),
43
82
  * its title, where the visitor came from. What the SDK reads on its own
44
- * goes without query and fragment — the page's address, and the browser's
45
- * document.referrer, which on a same-site arrival is the whole previous URL
46
- * (a reset link's token, a sign-in callback's code). What the app passes
47
- * is sent as it passed it.
83
+ * goes as its route (routeOf): no query, no fragment, no id or token in its
84
+ * path — the page's address, and the browser's document.referrer, which on
85
+ * a same-site arrival is the whole previous URL (a reset link's token, a
86
+ * sign-in callback's code). What the app passes is sent as it passed it.
48
87
  */
49
88
  page(page?: {
50
89
  url?: string;
@@ -68,6 +107,8 @@ export declare class Analytics {
68
107
  * request timings (each on unless turned off). Returns what stops them.
69
108
  */
70
109
  start(opts?: AnalyticsOptions): () => void;
110
+ private granted;
111
+ private person;
71
112
  private hold;
72
113
  private sample;
73
114
  private soon;
@@ -85,3 +126,13 @@ export declare class Analytics {
85
126
  * caused by input. Returns what stops the observers (and reports what stands).
86
127
  */
87
128
  export declare function observeWebVitals(record: (name: 'LCP' | 'CLS' | 'INP' | 'FCP' | 'TTFB', value: number) => void): () => void;
129
+ /**
130
+ * A path as its route — what is kept, not what is struck out. A segment
131
+ * stays only when it reads as a word of the route: lowercase after its
132
+ * first letter, two digits at most ("v1", "oauth2"), each part between - and
133
+ * _ sixteen letters at most; it is kept lowercased, as the module keeps it.
134
+ * Anything else — a number, an id, an email, a token, an encoded value — is
135
+ * :id, and a segment's parameters (;token=…) go. A short lowercase word that
136
+ * is in fact a secret reads as a route's: secrets stay out of paths.
137
+ */
138
+ export declare function routeOf(path: string): string;
@@ -4,8 +4,12 @@ import { type RetryPolicy } from '../../internal/src/retry.js';
4
4
  import { type RefreshMode, type SessionStore } from './session.js';
5
5
  import { RealtimeSocket } from './socket.js';
6
6
  import { Analytics, type AnalyticsOptions } from './analytics.js';
7
+ import { Consent, type ConsentOptions } from './consent.js';
8
+ import { Installation } from './installation.js';
9
+ import { Notify } from './notify.js';
7
10
  import { Purchases } from './purchases.js';
8
11
  import { type ChatToolCall } from './chat.js';
12
+ import { Flags } from './flags.js';
9
13
  export interface ClientOptions {
10
14
  /** The project's address: https://<ref>.<domain> */
11
15
  url: string;
@@ -30,13 +34,23 @@ export interface ClientOptions {
30
34
  /**
31
35
  * Starts what the browser tells of itself, as the app's analytics: page
32
36
  * views, web vitals and request timings (true for all of them, or say which).
33
- * Off unless given; events the app captures (`analytics.capture`) go either way.
37
+ * Off unless given; events the app captures (`analytics.capture`) go either
38
+ * way — each only while its consent key is granted (`consent`).
34
39
  */
35
40
  analytics?: boolean | AnalyticsOptions;
41
+ /**
42
+ * What the person allowed, as the app starts it: each key's starting value
43
+ * until the person chooses (`start: { analytics: 'granted', performance:
44
+ * 'denied' }` — where the law asks first, denied), the version of the
45
+ * consent text the app shows, and the app's own keys. Analytics collects
46
+ * nothing for a key with neither a starting value nor a choice. The
47
+ * person's choices are pb.consent's (set, needsAsking, onChange).
48
+ */
49
+ consent?: ConsentOptions;
36
50
  /**
37
51
  * The app's version, as its users see it ("2.4.1"): said with a paywall's
38
- * request, for a placement's targeting rules that read it. Nothing is said
39
- * without one.
52
+ * request and a flags read, for a placement's targeting rules and a flag's
53
+ * conditions that read it. Nothing is said without one.
40
54
  */
41
55
  appVersion?: string;
42
56
  /**
@@ -67,10 +81,18 @@ export interface PalbaseClient {
67
81
  socket: RealtimeSocket;
68
82
  /** The transport every module call goes through (credentials, refresh, retries). */
69
83
  transport: Transport;
70
- /** The app's analytics: events it captures, page views, and — once started — the browser's own measures. */
84
+ /** The app's analytics: events it captures, the person's properties, page views, and — once started — the browser's own measures. */
71
85
  analytics: Analytics;
86
+ /** What the person allowed, key by key: their choices, kept here and sent to the project, the keys a screen lists, the keys to ask about. */
87
+ consent: Consent;
88
+ /** This browser's install of the app, as the project knows it: its installation id. */
89
+ installation: Installation;
72
90
  /** What the signed-in customer has (live, onCustomerInfo), the paywall's catalog, and buying on the web. */
73
91
  purchases: Purchases;
92
+ /** The project's notifications as this app takes them: this browser's pushes (Web Push). */
93
+ notify: Notify;
94
+ /** The project's feature flags and remote configuration, resolved for this app and its reader, kept current as they change. */
95
+ flags: Flags;
74
96
  /** The language every request says from now on (ClientOptions.language); null for the browser's own. */
75
97
  setLanguage(language: string | readonly string[] | null): void;
76
98
  /**
@@ -0,0 +1,218 @@
1
+ import { auth as authModule } from '../../internal/src/modules/index.js';
2
+ import { type Installation } from './installation.js';
3
+ import { type SessionManager } from './session.js';
4
+ import type { RealtimeSocket } from './socket.js';
5
+ type Api = ReturnType<typeof authModule.client>;
6
+ /** A key's state, as the person chose it or the app starts it. */
7
+ export type ConsentState = 'granted' | 'denied';
8
+ /** Where the person is, as far as a consent screen cares: gdpr asks before anything is collected, us collects until refused. */
9
+ export type ConsentRegion = authModule.ConsentRegion;
10
+ /** Told each key whose state changed: the person's choice, a refusal from the project, a sign-in or sign-out. */
11
+ export type ConsentListener = (key: string, state: ConsentState | undefined) => void;
12
+ /** A key a consent screen lists. */
13
+ export interface ConsentKeyInfo {
14
+ key: string;
15
+ /** What it is about; custom is the app's own key. */
16
+ category: authModule.ConsentKey['category'] | 'custom';
17
+ /** Its words by language (en, tr); the app's own keys have none — the app words them. */
18
+ title: Record<string, string>;
19
+ description: Record<string, string>;
20
+ /** What the US laws call selling or sharing: Global Privacy Control refuses it, and a refusal is not asked again for twelve months. */
21
+ ads: boolean;
22
+ }
23
+ export interface ConsentOptions {
24
+ /**
25
+ * What the app starts with, key by key, until the person chooses — what is
26
+ * collected before anyone was asked. Where the law asks first (the EU, the
27
+ * UK: region() says) a key starts denied. A key the SDK collects for with
28
+ * no starting value and no choice collects nothing: give `analytics`
29
+ * (events, page views, person properties) and `performance` (web vitals,
30
+ * request timings) when the app uses them. Starting values never leave the
31
+ * browser: only the person's own choices are sent.
32
+ */
33
+ start?: Readonly<Record<string, ConsentState>>;
34
+ /**
35
+ * The version of the consent text the app shows ("2026-10"): sent with
36
+ * each choice made under it, and a choice made under another is to be
37
+ * asked again (needsAsking).
38
+ */
39
+ textVersion?: string;
40
+ /** The app's own keys, custom.<name> (a-z, 0-9 and _, 48 at most): listed by keys() after the platform's. */
41
+ customKeys?: readonly string[];
42
+ }
43
+ export interface ConsentDeps {
44
+ /** The module's calls as this user — their token, or no call at all: never another user's who signed in meanwhile. */
45
+ userApi: (user: string) => Api;
46
+ /** The same with the publishable key alone: the device's own choices. */
47
+ deviceApi: Api;
48
+ session: SessionManager;
49
+ socket: RealtimeSocket;
50
+ ref: string;
51
+ installation: Installation;
52
+ origin: string;
53
+ options?: ConsentOptions;
54
+ now?: () => number;
55
+ }
56
+ export declare class Consent {
57
+ private readonly userApi;
58
+ private readonly deviceApi;
59
+ private readonly session;
60
+ private readonly socket;
61
+ private readonly ref;
62
+ private readonly installation;
63
+ private readonly start;
64
+ private readonly textVersion;
65
+ private readonly customKeys;
66
+ private readonly storeKey;
67
+ private readonly now;
68
+ private kept;
69
+ private listeners;
70
+ /** The app uses consent: the person's account is followed, and what is unsent is sent. */
71
+ private engaged;
72
+ private platformKeys;
73
+ private watch;
74
+ private sending;
75
+ private sendAgain;
76
+ private retryTimer;
77
+ private retries;
78
+ /** Whose choices the project last answered in this page (a user's id, null for the device's); undefined: none yet. */
79
+ private readFor;
80
+ /** A refused choice stands denied here because its re-read failed: the project's state is read with the next attempt. */
81
+ private stale;
82
+ /** This page's last write stamp. */
83
+ private lastW;
84
+ /** This page's last choice time. */
85
+ private lastChosen;
86
+ /** The browser's Global Privacy Control, as the page was loaded with it. */
87
+ private readonly gpc;
88
+ constructor(deps: ConsentDeps);
89
+ /**
90
+ * The keys a consent screen lists: the platform's, in the order a screen
91
+ * shows them, with their words — read from the project once — then the
92
+ * app's own (customKeys).
93
+ */
94
+ keys(): Promise<ConsentKeyInfo[]>;
95
+ /**
96
+ * The key's state: the person's choice, else the app's starting value;
97
+ * undefined when there is neither. Denied while what was kept is not known
98
+ * (unreadable, or the project refused it) until the project's answer is taken.
99
+ */
100
+ state(key: string): ConsentState | undefined;
101
+ /**
102
+ * The project's state is still to be read: what is kept is not known
103
+ * (unreadable, a refused link), stands denied only for want of its answer
104
+ * (a refused choice), or is someone else's.
105
+ */
106
+ private owesRead;
107
+ /** Who the choices here are of now: the signed-in user, or this install. */
108
+ private holder;
109
+ /** When the key was last refused — here, on another of the person's devices, or by the project on their word. */
110
+ lastDeniedAt(key: string): Date | undefined;
111
+ /** Records the person's choice of one key: it stands at once and is sent to the project. */
112
+ set(key: string, state: ConsentState): void;
113
+ /** Records the person's choices of several keys, made together (one consent screen). */
114
+ setMany(choices: Readonly<Record<string, ConsentState>>): void;
115
+ /** Listens to every key whose state changes; returns the unsubscribe. */
116
+ onChange(listener: ConsentListener): () => void;
117
+ /** Where the person is, as the project tells from their address: what the app's starting values follow. */
118
+ region(): Promise<ConsentRegion>;
119
+ /**
120
+ * The keys of `keys` (every key, unless given) the app is to ask the
121
+ * person about: those never chosen, and those chosen on the app's screen
122
+ * under another textVersion than the app's — but in the US not an ads key
123
+ * the person refused within twelve months (CCPA §1798.135(c)(4)).
124
+ */
125
+ needsAsking(keys?: readonly string[]): Promise<string[]>;
126
+ private engage;
127
+ private subject;
128
+ private secret;
129
+ private platform;
130
+ private read;
131
+ /**
132
+ * The project's answer of whose choices: the browser takes it — but a
133
+ * choice made here and not yet sent is newer than any answer, and stands.
134
+ * An answer for someone other than who is signed in now is dropped.
135
+ * recorded: choices the answer is the project's record of (just sent):
136
+ * their copies here give way to it, at whatever time the project kept them
137
+ * (a device clock ahead of the project's is not later).
138
+ */
139
+ private adopt;
140
+ /**
141
+ * The project refused a choice sent: nothing it did not record may stand
142
+ * here. Its state is read again and taken; when that fails too, the keys go
143
+ * denied — never the refused grant — and are read again with the next attempt.
144
+ */
145
+ private refused;
146
+ /**
147
+ * A choice made here: it stands now, and goes after the ones before it —
148
+ * an older one is not dropped for it: refused, the newer one leaves the
149
+ * older standing.
150
+ */
151
+ private queue;
152
+ /** Sends what is not sent yet: one run at a time; a change meanwhile is sent after it. */
153
+ private send;
154
+ /** Sends what is unsent of the device and of who is signed in; false when it could not be sent now. */
155
+ private sendUnsent;
156
+ /** Sends whose unsent choices (a user's, or null: the device's); true when any went. */
157
+ private sendOf;
158
+ /** The next call's choices: whose, made the same way (source, text version), fifty at most. */
159
+ private nextGroup;
160
+ private retryLater;
161
+ private signedIn;
162
+ /**
163
+ * Signed out: the device goes back to the app's starting values — every
164
+ * key it holds a choice of is unset, as the device's — and the next person
165
+ * here is asked anew. A choice the signed-out person made that has not
166
+ * been sent waits for them.
167
+ */
168
+ private signedOut;
169
+ private follow;
170
+ private unfollow;
171
+ private held;
172
+ /**
173
+ * A change told: read at once — not after a read still on its way, which
174
+ * may be slow: answers can come in any order, a key's older one never
175
+ * replaces its newer. A read that fails is made again with the next attempt.
176
+ */
177
+ private changed;
178
+ /**
179
+ * While the browser sends Global Privacy Control, an ads key stands denied
180
+ * for the person here — unless they granted it in the app after this
181
+ * browser recorded GPC's refusal for them: a choice they make after it is
182
+ * theirs. A reset, another tab's, a sign-in does not undo it.
183
+ */
184
+ private gpcRefuses;
185
+ /**
186
+ * GPC's refusal goes to the project (source gpc) for every ads key it
187
+ * refuses here: once per person, and again whenever what stands for them
188
+ * is not a refusal (a reset, another tab's, came after it).
189
+ */
190
+ private refuseByGpc;
191
+ private states;
192
+ private tell;
193
+ /** What the store holds: the choices; empty — never written, or cleared; or unreadable — what it holds is not choices. */
194
+ private load;
195
+ /**
196
+ * Another tab wrote the choices: they stand here too, with what this page
197
+ * has not sent yet. Cleared or unreadable there, this page's choices stand
198
+ * — a refusal is never undone by a read that came back empty — and are
199
+ * written back.
200
+ */
201
+ private reload;
202
+ /**
203
+ * Writes what this page holds merged with what another tab wrote meanwhile
204
+ * — a key's later write stands, whichever tab made it — and tells what the
205
+ * merge changed here.
206
+ */
207
+ private commit;
208
+ private write;
209
+ /**
210
+ * When a choice is made: now, but later than this page's last — two
211
+ * choices of a page never share a time (a reset, and the Global Privacy
212
+ * Control refusal after it, are one after the other, not a tie).
213
+ */
214
+ private chosenAt;
215
+ /** The next mark stamp: later than any this browser wrote that this page has seen. */
216
+ private stamp;
217
+ }
218
+ export {};
@@ -0,0 +1,75 @@
1
+ import { flags as flagsModule } from '../../internal/src/modules/index.js';
2
+ import { type Transport } from '../../internal/src/transport.js';
3
+ import { type Installation } from './installation.js';
4
+ import type { SessionManager } from './session.js';
5
+ import type { RealtimeSocket } from './socket.js';
6
+ /** What reports an experiment's exposure once a session: the client's analytics (its captureOnce is internal, so not named by type here). */
7
+ interface ExposureSink {
8
+ captureOnce(key: string, event: string, properties: Record<string, unknown>): void;
9
+ }
10
+ /** Where a value came from: the flag, the user's own, or an experiment's variant. */
11
+ export type FlagSource = 'system' | 'override' | 'experiment';
12
+ /** Told the keys whose values changed. */
13
+ export type FlagsListener = (keys: string[]) => void;
14
+ /** The project's flags as this reader sees them. */
15
+ export declare class Flags {
16
+ /** Every client operation of the flags contract, through the session's transport. */
17
+ readonly api: ReturnType<typeof flagsModule.client>;
18
+ private session;
19
+ private socket;
20
+ private installation;
21
+ private ref;
22
+ private analytics;
23
+ /** The app's version, when it gave one: what a condition on versions reads. */
24
+ private appVersion;
25
+ private storageKey;
26
+ private held;
27
+ private listeners;
28
+ private started;
29
+ private leave;
30
+ /** The read running, and one asked for while it runs: read after it, once however often it was asked. */
31
+ private running;
32
+ private queued;
33
+ /** A reader just came (or the page opened): their first read is a snapshot, asked with the ETag of what is held. */
34
+ private fresh;
35
+ constructor(transport: Transport, session: SessionManager, socket: RealtimeSocket, installation: Installation, ref: string, origin: string, analytics: ExposureSink, appVersion?: string);
36
+ /** A flag's value, any JSON; undefined when there is none (yet). */
37
+ value(key: string): unknown;
38
+ /** A bool flag's value, or fallback when there is none or it is not a bool. */
39
+ bool(key: string, fallback: boolean): boolean;
40
+ /** A string flag's value, or fallback. */
41
+ string(key: string, fallback: string): string;
42
+ /** A number flag's value, or fallback. */
43
+ number(key: string, fallback: number): number;
44
+ /** An object flag's value, as the app's own type (not checked), or fallback. */
45
+ json<T>(key: string, fallback: T): T;
46
+ /** Every flag's value by key. Reading them here reports no experiment's reading: read a flag by its key for that. */
47
+ all(): Record<string, unknown>;
48
+ /** Where a value came from. */
49
+ source(key: string): FlagSource | undefined;
50
+ /** The version the values held are current as of. */
51
+ get version(): string | undefined;
52
+ /**
53
+ * Listens to the values: told the keys that changed — a change on the server, the values read for another reader
54
+ * (the last one's go first). The first listener starts keeping the values current. Returns what stops it.
55
+ */
56
+ onChange(listener: FlagsListener): () => void;
57
+ /**
58
+ * Brings the values up to date now: what changed since the version held, or a snapshot when there is none or the
59
+ * delta no longer reaches. The first call (or the first read, or listener) also starts keeping them current.
60
+ */
61
+ refresh(): Promise<void>;
62
+ /** The values held, when they are this reader's: read without waiting, and with no network, for another's never. */
63
+ private mine;
64
+ private start;
65
+ private follow;
66
+ private join;
67
+ private changed;
68
+ private read;
69
+ private fetch;
70
+ /** What the reads say of the app: the web, its version, and this install — proven with its secret while no one is signed in, whom audiences and experiments then ask about. */
71
+ private headers;
72
+ private apply;
73
+ private tell;
74
+ }
75
+ export {};
@@ -0,0 +1,129 @@
1
+ import { type Transport } from '../../internal/src/transport.js';
2
+ import { type Installation } from './installation.js';
3
+ import type { SessionManager } from './session.js';
4
+ import type { Purchases } from './purchases.js';
5
+ import { type MessageCloseReason } from './message/present.js';
6
+ import type { MessageHandlers } from './screen/render.js';
7
+ import type { Appearance } from './screen/resolve.js';
8
+ /** What an offer's button is taken with when the app does not take it: the customer's purchases (pb.purchases). */
9
+ type OfferTaker = Pick<Purchases, 'offers' | 'claimOffer' | 'checkout'>;
10
+ /** The event a session's start is, as a campaign's trigger names it. */
11
+ export declare const SESSION_START = "session_start";
12
+ export interface InAppOptions {
13
+ /** The browser's language unless given. */
14
+ locale?: string;
15
+ /** The system's unless given. */
16
+ appearance?: Appearance;
17
+ /** The element messages are shown in: the page's body unless given. */
18
+ root?: HTMLElement;
19
+ /** A link of the app's a button opens (deep_link); without it the page goes to it. */
20
+ onDeepLink?(link: string): void;
21
+ /** An https address a button opens; without it the browser opens it in a new tab. */
22
+ onLink?(url: string): void;
23
+ /** The app's own code a button names. */
24
+ onCustom?(name: string, value?: string): void;
25
+ /** The paywall of a placement a button shows; without it its button is not drawn. */
26
+ onShowPaywall?(placement: string): void | Promise<void>;
27
+ /**
28
+ * One of the catalog's offers a button takes (offer), unless the SDK takes it: a grant claimed, else Stripe
29
+ * Checkout of its first web plan with the offer, back to this page.
30
+ */
31
+ onOffer?(offer: string): void | Promise<void>;
32
+ /** An offer the SDK could not take (no one signed in, a guest, the offer no longer theirs, the network). Without one it is thrown, and the message stays. */
33
+ onOfferError?(error: unknown, offer: string): void;
34
+ /** The push ask's button: the browser's prompt and this browser subscribed (pb.notify.push.subscribe), unless the app does it. */
35
+ onRequestPush?(): void | Promise<void>;
36
+ /** Told as a campaign shows, and as it closes. */
37
+ onShow?(campaignId: string): void;
38
+ onClose?(campaignId: string, reason: MessageCloseReason): void;
39
+ labels?: MessageHandlers['labels'];
40
+ }
41
+ type Globals = {
42
+ document?: Document;
43
+ location?: {
44
+ href: string;
45
+ assign(url: string): void;
46
+ };
47
+ navigator?: {
48
+ language?: string;
49
+ };
50
+ matchMedia?: (q: string) => MediaQueryList;
51
+ localStorage?: Storage;
52
+ };
53
+ /** The project's in-app campaigns, shown in this page. */
54
+ export declare class InApp {
55
+ private api;
56
+ private session;
57
+ private installation;
58
+ private g;
59
+ private now;
60
+ private requestPush;
61
+ private analyticsAllowed;
62
+ private purchases;
63
+ /** Each load's turn: an answer for whoever was here before is not kept. */
64
+ private asked;
65
+ /** Whose the page's campaigns are: one more as someone else comes. */
66
+ private person;
67
+ private sessionKey;
68
+ private pendingKey;
69
+ private opts;
70
+ private messages;
71
+ private limits;
72
+ private shown;
73
+ private loaded;
74
+ private memorySession;
75
+ private memoryPending;
76
+ private showing;
77
+ private stopWatching;
78
+ /** Events are taken one after another: an event's show is asked before the next one is looked at. */
79
+ private queue;
80
+ constructor(transport: Transport, session: SessionManager, installation: Installation, origin: string, requestPush: () => Promise<unknown>, analyticsAllowed: () => boolean, purchases: OfferTaker, g?: Globals, now?: () => number);
81
+ /** Whether it was started: campaigns are shown only then. */
82
+ get started(): boolean;
83
+ /**
84
+ * Starts showing the person's campaigns in this page: asked for now (a session's start shows those it triggers),
85
+ * and again as each session starts. Once is enough; again changes the options.
86
+ */
87
+ start(opts?: InAppOptions): void;
88
+ /** Stops showing campaigns: one on screen closes. */
89
+ stop(): void;
90
+ /** The person's campaigns asked for again (they are as each session starts). */
91
+ refresh(): Promise<void>;
92
+ /**
93
+ * An event of the app's, as a campaign's trigger names it: shows the first campaign it triggers that its caps
94
+ * let, and counts a conversion of one shown that it is. Events the app captures come here by themselves.
95
+ */
96
+ trigger(event: string, properties?: Record<string, unknown>): void;
97
+ /** Waits for the events given so far to be taken (a test's, an app's before it navigates). */
98
+ settled(): Promise<void>;
99
+ private enqueue;
100
+ private take;
101
+ private show;
102
+ /** Whether a campaign showing now would pass a cap, as far as this page knows: the server's answer is the word. */
103
+ private capped;
104
+ /** Asks the server to show a campaign and, shown, shows it: false when a cap of the person's other devices held it. */
105
+ private open;
106
+ /**
107
+ * An offer's button as the SDK takes it: the customer's offer of that key — a grant claimed; a discount or a trial
108
+ * bought with, on its first web plan, through Stripe Checkout, which comes back to this page. One that is not
109
+ * theirs to take on the web is refused here (offer_not_open), as the module refuses it. What could not be taken
110
+ * is the app's to hear when it listens; thrown otherwise.
111
+ */
112
+ private takeOffer;
113
+ /** Reports the conversions an event is: of campaigns shown to whoever is here now, within their hours. */
114
+ private converted;
115
+ private forget;
116
+ private load;
117
+ /** Whose the page's campaigns are: the person signed in, or this browser's install. */
118
+ private subject;
119
+ private appearance;
120
+ /** Marks the session in use now: true when a new one starts (the first, or one after half an hour away). */
121
+ private touch;
122
+ private sessionId;
123
+ private readSession;
124
+ private writeSession;
125
+ private pending;
126
+ private keepPending;
127
+ private pend;
128
+ }
129
+ export {};
@@ -0,0 +1,59 @@
1
+ import { notify as notifyModule } from '../../internal/src/modules/index.js';
2
+ import type { Transport } from '../../internal/src/transport.js';
3
+ import type { SessionManager } from './session.js';
4
+ import type { Appearance } from './screen/resolve.js';
5
+ export type InboxItem = notifyModule.InboxItem;
6
+ export type InboxPage = notifyModule.InboxPage;
7
+ export interface InboxListOptions {
8
+ /** 1 to 500; the server's page unless given. */
9
+ limit?: number;
10
+ /** The previous page's next_cursor. */
11
+ cursor?: string;
12
+ /** The language to write it in: the one the person chose unless given. */
13
+ locale?: string;
14
+ }
15
+ export interface InboxViewOptions {
16
+ /** The language to write it in: the one the person chose unless given; a card's words in the browser's. */
17
+ locale?: string;
18
+ /** The system's unless given. */
19
+ appearance?: Appearance;
20
+ /** How many it shows: 50 unless given. */
21
+ limit?: number;
22
+ /** A message's link, as a tap opens it: an https address in a new tab and an app's link in this page unless given. */
23
+ onOpen?(item: InboxItem, link: string): void;
24
+ /** A designed card's own buttons. */
25
+ onCustom?(name: string, value?: string): void;
26
+ /** Told of the count of unread messages as it changes: what a badge shows. */
27
+ onUnread?(count: number): void;
28
+ /** Its words: an empty inbox, a delete button's name. */
29
+ labels?: {
30
+ empty?: string;
31
+ delete?: string;
32
+ };
33
+ }
34
+ export interface InboxView {
35
+ /** Asks for the inbox again and draws it. */
36
+ refresh(): Promise<void>;
37
+ /** The messages drawn now. */
38
+ readonly items: readonly InboxItem[];
39
+ /** How many of the whole inbox are unread. */
40
+ readonly unread: number;
41
+ /** Removes it from the page. */
42
+ destroy(): void;
43
+ }
44
+ /** The signed-in person's inbox. */
45
+ export declare class Inbox {
46
+ private api;
47
+ private session;
48
+ constructor(transport: Transport, session: SessionManager);
49
+ /** A page of the inbox, newest first, with how many of it all are unread. Someone signed in only. */
50
+ list(opts?: InboxListOptions): Promise<InboxPage>;
51
+ /** How many messages are unread. */
52
+ unread(): Promise<number>;
53
+ /** Marks messages read — some by id, or all — on every device of the person's: how many became read. */
54
+ markRead(ids: readonly string[] | 'all'): Promise<number>;
55
+ /** Deletes a message, from every device of the person's. */
56
+ remove(id: string): Promise<void>;
57
+ /** Draws the inbox into a container: a ready list, kept current. */
58
+ mount(container: HTMLElement, opts?: InboxViewOptions): InboxView;
59
+ }