@loxel.dev/pharos-browser 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -127,8 +127,10 @@ targeting rules — send nothing if no rule reads it.
127
127
 
128
128
  ```typescript
129
129
  // Synchronous, no network call — the value was computed server-side and is
130
- // already here. Your default is returned until the bootstrap lands, and for
131
- // any key Pharos does not know.
130
+ // already here. Your default is returned until the bootstrap lands, for any
131
+ // key Pharos does not know, and for a value whose type disagrees with your
132
+ // default — a flag sent as the string "false" is not a boolean, and reading
133
+ // it through a boolean gate would otherwise turn the gate ON.
132
134
  if (client.flag("new-nav", false)) {
133
135
  renderNewNav();
134
136
  }
@@ -154,6 +156,13 @@ await client.identify({
154
156
  });
155
157
  ```
156
158
 
159
+ The new user's flags arrive with it, so `identify()` fires `change` the same
160
+ way a server push does — a component that mounted while the user was
161
+ anonymous re-renders against the identified bucket without the app arranging
162
+ anything. If the re-bootstrap fails it still rejects (catch it), and the
163
+ client schedules a reconnect rather than leaving live updates dead until a
164
+ page reload.
165
+
157
166
  ### Shut down
158
167
 
159
168
  ```typescript
@@ -318,6 +327,40 @@ you can verify what a recorder would send before turning one on.
318
327
 
319
328
  ## Version history
320
329
 
330
+ - `0.8.0` — **`identify()` tells you the flags changed, and a failed one no
331
+ longer kills live updates** (#788).
332
+
333
+ `identify()` swaps the whole flags map and emitted nothing, so a component
334
+ that rendered against the anonymous bootstrap kept that value for the life
335
+ of its mount while the server evaluated the real user's bucket on every
336
+ request. It now emits `change` with the new payload. If its bootstrap
337
+ rejects it still rejects — an app awaiting it on login has to see that —
338
+ but it now also schedules a reconnect, where before the stream was left
339
+ dead with nothing pending and nothing reported. Reconnects back off
340
+ exponentially (1 s doubling to a 30 s ceiling, reset by a delivered `flags`
341
+ push) instead of re-bootstrapping every second for the length of an outage.
342
+
343
+ `flag(key, default)` now **returns your default when the stored value's
344
+ type disagrees with it**. It was a cast (`as T`), so a flag delivered as the
345
+ string `"false"` read as ON through a boolean gate. `null`/`undefined`
346
+ defaults opt out of the check, since `typeof` describes the absence of a
347
+ value for both.
348
+
349
+ **The replay sensitive-field word list is no longer English-only** (#823).
350
+ It now carries the common Spanish, French, German, Portuguese, Japanese and
351
+ Chinese spellings of the same fields, and normalization folds accents
352
+ instead of deleting them (`contraseña` → `contrasena`, `Straße` →
353
+ `strasse`) — without which every accented entry would have missed by a
354
+ character. A localised app previously got essentially nothing from this
355
+ layer. Annotating `autocomplete` is still the recommended integration step:
356
+ it is language-independent and is checked first.
357
+
358
+ **Why MINOR:** `change` now fires on a path where it did not, a
359
+ type-mismatched flag stops being returned, and a localised app's fields are
360
+ masked where they were recorded — so a pinned `collectRecordedStrings`
361
+ snapshot will differ. Every move is in the safe direction, but a consumer
362
+ sees different behaviour either way.
363
+
321
364
  - `0.7.0` — **a marker on a shadow host now protects its shadow root** (#821),
322
365
  and the verification helper sees what the recorder sees (#822).
323
366
 
@@ -185,14 +185,23 @@ documented in full in
185
185
  - `flag<T>(key, defaultValue): T` — synchronous map lookup, no network call,
186
186
  no evaluation. Returns `defaultValue` for any key not present in the
187
187
  current flags map (unknown key, or a `server_side`-only flag the bootstrap
188
- endpoint never sent).
188
+ endpoint never sent), and for a stored value whose `typeof` disagrees with
189
+ `defaultValue`'s — the string `"false"` is not a boolean and must not read
190
+ as one. A `null`/`undefined` default opts out of that check, because
191
+ `typeof` describes the absence of a value for both and checking against it
192
+ would return the default unconditionally.
189
193
  - `on(event, cb): () => void` — subscribe to `"change"` (a new flags payload
190
194
  was applied — fires exactly once per accepted server push) or `"error"`
191
195
  (stream/bootstrap failure). Returns an unsubscribe function.
192
196
  - `identify(context): Promise<void>` — re-bootstraps as a new context and
193
197
  reopens the stream against the fresh `streamToken`. **This is a full HTTP
194
198
  round trip, not a local re-key** — call it on meaningful identity changes
195
- (login, plan change), not per-render.
199
+ (login, plan change), not per-render. It **fires `"change"`** with the new
200
+ `{version, flags}` once the stream is back up: it replaced the flags map,
201
+ and a consumer that only watched `"change"` would otherwise render the
202
+ previous context's values for the life of its mount. A rejected bootstrap
203
+ **schedules a reconnect** as well as rejecting, so a failure here costs the
204
+ call, not the session's live updates.
196
205
  - `captureException(err, opts?): void` — reports one exception; see
197
206
  "Error reporting" above. `opts` is `{site?: string, attributes?: Record<string, unknown>}`.
198
207
  Never throws; a failed POST is reported via `on("error")`. A no-op after
@@ -295,10 +304,19 @@ own. The one case it cannot recover from is an **expired stream session**
295
304
  (the server holds a 10-minute token TTL, refreshed only at connect): the
296
305
  browser sees repeated errors and keeps retrying the same dead token forever.
297
306
  When `EventSource` settles into its terminal `CLOSED` state, this client
298
- re-bootstraps once (short fixed backoff) to mint a fresh token and reconnect.
299
- It does not attempt a queue or exponential ramp if the server is down, the
300
- next successful `identify()`/reconnect will resync from scratch, and `flag()`
301
- keeps serving the last-known values in the meantime.
307
+ re-bootstraps to mint a fresh token and reconnect. The same schedule covers a
308
+ rejected `identify()`, which is otherwise the one way to end up with no
309
+ stream and no `EventSource` left to report an error.
310
+
311
+ Retries **back off exponentially**: 1 s, doubling to a 30 s ceiling. It is a
312
+ delay cap rather than an attempt cap — giving up entirely would leave a tab
313
+ permanently stale after an outage it survived — and the ramp is reset by a
314
+ **delivered `flags` push**, not by a successful bootstrap, so a stream that
315
+ opens and dies before sending anything keeps backing off instead of flapping
316
+ at the floor delay. Against a Pharos that is present-but-down this is the
317
+ difference between one request per second per open tab and two per minute.
318
+ There is still no queue and no jitter; `flag()` keeps serving the last-known
319
+ values throughout, and the next successful reconnect resyncs from scratch.
302
320
 
303
321
  Only payloads with `version >= current` are applied; stale (older-version)
304
322
  pushes are ignored. An equal-version push (e.g. the replay a fresh stream
@@ -1,3 +1,11 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined")
5
+ return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
1
9
  // src/privacy/mask.ts
2
10
  var MASK_TOKEN = "••••";
3
11
  function maskText(_text) {
@@ -100,11 +108,101 @@ var SENSITIVE_WORDS = [
100
108
  "cvc",
101
109
  "iban",
102
110
  "passport",
103
- "driverslicense"
111
+ "driverslicense",
112
+ "contrasena",
113
+ "contrasenya",
114
+ "usuario",
115
+ "secreto",
116
+ "correo",
117
+ "movil",
118
+ "nombre",
119
+ "apellido",
120
+ "direccion",
121
+ "codigopostal",
122
+ "nacimiento",
123
+ "cumpleanos",
124
+ "dni",
125
+ "seguridadsocial",
126
+ "tarjeta",
127
+ "motdepasse",
128
+ "utilisateur",
129
+ "identifiant",
130
+ "courriel",
131
+ "prenom",
132
+ "nomdefamille",
133
+ "adresse",
134
+ "codepostal",
135
+ "naissance",
136
+ "anniversaire",
137
+ "securitesociale",
138
+ "carteidentite",
139
+ "permisconduire",
140
+ "passwort",
141
+ "kennwort",
142
+ "benutzer",
143
+ "benutzername",
144
+ "vorname",
145
+ "nachname",
146
+ "familienname",
147
+ "anschrift",
148
+ "strasse",
149
+ "postleitzahl",
150
+ "geburt",
151
+ "kreditkarte",
152
+ "kontonummer",
153
+ "steuernummer",
154
+ "sozialversicherung",
155
+ "ausweis",
156
+ "senha",
157
+ "palavrapasse",
158
+ "correio",
159
+ "sobrenome",
160
+ "nomecompleto",
161
+ "endereco",
162
+ "nascimento",
163
+ "cartao",
164
+ "cpf",
165
+ "cnpj",
166
+ "パスワード",
167
+ "メール",
168
+ "電話",
169
+ "氏名",
170
+ "名前",
171
+ "住所",
172
+ "生年月日",
173
+ "誕生日",
174
+ "郵便番号",
175
+ "クレジットカード",
176
+ "マイナンバー",
177
+ "密码",
178
+ "密碼",
179
+ "口令",
180
+ "邮箱",
181
+ "郵箱",
182
+ "邮件",
183
+ "郵件",
184
+ "电话",
185
+ "手机",
186
+ "手機",
187
+ "姓名",
188
+ "地址",
189
+ "生日",
190
+ "出生日期",
191
+ "邮编",
192
+ "身份证",
193
+ "身份證",
194
+ "信用卡",
195
+ "银行卡"
104
196
  ];
105
197
  var NAME_ATTRS = ["name", "id", "aria-label"];
106
- function normalize(value) {
107
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
198
+ function foldDiacritics(value) {
199
+ return value.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").normalize("NFC").replace(/ß/g, "ss");
200
+ }
201
+ function normalForms(value) {
202
+ const folded = foldDiacritics(value);
203
+ const ascii = folded.replace(/[^a-z0-9]/g, "");
204
+ const script = folded.replace(/[^\p{L}\p{N}]/gu, "");
205
+ return ascii === script ? [ascii] : [ascii, script];
108
206
  }
109
207
  function isSensitiveField(el) {
110
208
  if (el.tagName.toLowerCase() === "input") {
@@ -123,9 +221,10 @@ function isSensitiveField(el) {
123
221
  const raw = el.getAttribute(attr);
124
222
  if (!raw)
125
223
  continue;
126
- const normalized = normalize(raw);
127
- if (SENSITIVE_WORDS.some((word) => normalized.includes(word)))
128
- return true;
224
+ for (const normalized of normalForms(raw)) {
225
+ if (SENSITIVE_WORDS.some((word) => normalized.includes(word)))
226
+ return true;
227
+ }
129
228
  }
130
229
  return false;
131
230
  }
@@ -852,4 +951,4 @@ async function clearPersisted(indexedDB = defaultFactory()) {
852
951
  }
853
952
  }
854
953
 
855
- export { MASK_TOKEN, maskText, EXCLUDE_CLASS, MASK_CLASS, shouldExclude, shouldMaskByMarker, isSensitiveField, scrubUrl, scrubAttribute, decide, masksValue, MAX_BUFFER_BYTES, RECORDER_DEFAULTS, resolveConfig, ACTIVATION_EVENT_TAG, attachInteractions, EVENT_TYPE_FULL_SNAPSHOT, EVENT_TYPE_META, beginsWithSnapshot, ReplayBuffer, createDropLedger, MAX_WINDOW_BYTES, encodeWindow, randomSessionId, postEnvelope, envelopeMetaFor, encodeForUpload, createUploadSink, LOCAL_TTL_MS, quotaDroppedWindows, resetQuotaDroppedWindows, isQuotaExceeded, persistWindow, drainPersisted, clearPersisted };
954
+ export { __require, MASK_TOKEN, maskText, EXCLUDE_CLASS, MASK_CLASS, shouldExclude, shouldMaskByMarker, isSensitiveField, scrubUrl, scrubAttribute, decide, masksValue, MAX_BUFFER_BYTES, RECORDER_DEFAULTS, resolveConfig, ACTIVATION_EVENT_TAG, attachInteractions, EVENT_TYPE_FULL_SNAPSHOT, EVENT_TYPE_META, beginsWithSnapshot, ReplayBuffer, createDropLedger, MAX_WINDOW_BYTES, encodeWindow, randomSessionId, postEnvelope, envelopeMetaFor, encodeForUpload, createUploadSink, LOCAL_TTL_MS, quotaDroppedWindows, resetQuotaDroppedWindows, isQuotaExceeded, persistWindow, drainPersisted, clearPersisted };
@@ -26,8 +26,7 @@ import {
26
26
  resolveConfig,
27
27
  scrubAttribute,
28
28
  scrubUrl
29
- } from "./index-yabengpp.js";
30
- import"./index-c3taa3cg.js";
29
+ } from "./index-temc0wzg.js";
31
30
 
32
31
  // src/replay/recorder.ts
33
32
  import { record } from "rrweb";
@@ -986,9 +985,5 @@ function startReplayUpload(opts) {
986
985
  }
987
986
  };
988
987
  }
989
- export {
990
- SESSION_DROPS_KEY,
991
- SESSION_STORAGE_KEY,
992
- resolveSessionId,
993
- startReplayUpload
994
- };
988
+
989
+ export { startRecording, SESSION_STORAGE_KEY, resolveSessionId, SESSION_DROPS_KEY, startReplayUpload };
package/dist/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export declare class PharosBrowserClient {
62
62
  private listeners;
63
63
  private closed;
64
64
  private reconnectTimer;
65
+ private reconnectAttempts;
65
66
  private errorsEnabled;
66
67
  private errorTarget;
67
68
  private replayHandle;
@@ -80,11 +81,22 @@ export declare class PharosBrowserClient {
80
81
  * request): the browser sees this as an error and keeps retrying the same
81
82
  * dead token forever. Heuristic: when the source has settled into CLOSED
82
83
  * (its terminal state — EventSource does not reach CLOSED on a retryable
83
- * drop), re-bootstrap once after a short backoff to mint a fresh token,
84
- * then reconnect the stream. This is intentionally simpleno queue, no
85
- * exponential ramp matching the brief's "don't over-engineer" guidance.
84
+ * drop), re-bootstrap after a backoff to mint a fresh token, then reconnect
85
+ * the stream. Still no queue and no jitter the one thing it is not simple
86
+ * about is the ramp, for the reason on `scheduleReconnect`.
86
87
  */
87
88
  private maybeReconnect;
89
+ /**
90
+ * Arms the single pending reconnect, backing off exponentially from
91
+ * `RECONNECT_BACKOFF_MS` to `MAX_RECONNECT_BACKOFF_MS`.
92
+ *
93
+ * The ramp is not decoration: against a Pharos that is present-but-down
94
+ * every re-bootstrap fails and re-arms this timer, so a fixed delay is a
95
+ * request per second per open tab for the length of the outage — load
96
+ * arriving exactly when the server can least take it. `reconnectAttempts`
97
+ * is reset by a delivered `flags` push, not here.
98
+ */
99
+ private scheduleReconnect;
88
100
  private onWindowError;
89
101
  private onUnhandledRejection;
90
102
  private registerErrorListeners;
@@ -206,14 +218,41 @@ export declare class PharosBrowserClient {
206
218
  startReplay(options?: Omit<ReplayUploadOptions, "endpoint" | "appKey">): Promise<ReplayUploadHandle | null>;
207
219
  /** Stops routing errors into the recorder. Does not stop the recorder itself. */
208
220
  detachReplay(): void;
209
- /** Synchronous flag lookup; falls back to defaultValue for unknown keys. */
221
+ /**
222
+ * Synchronous flag lookup; falls back to defaultValue for an unknown key,
223
+ * and for a value of a different type than the default (issue #788).
224
+ *
225
+ * `as T` was a cast, not a check, so a flag whose value arrived as the
226
+ * STRING `"false"` — truthy — read as ON through a boolean gate, while the
227
+ * dashboard, the payload and the code all looked right. The default carries
228
+ * the type the caller is prepared to handle, so a value that disagrees with
229
+ * it is a misconfiguration the default is the safe answer to.
230
+ *
231
+ * `null`/`undefined` defaults opt out: `typeof` describes the ABSENCE of a
232
+ * value for both, so checking against them would turn `flag(key, null)`
233
+ * into "always null" and stop returning the server's answer at all.
234
+ */
210
235
  flag<T>(key: string, defaultValue: T): T;
211
236
  /** Subscribes to "change" (a new flags payload was applied) or "error". Returns an unsubscribe function. */
212
237
  on(event: PharosEvent, cb: Listener): () => void;
213
238
  private emit;
214
239
  /**
215
240
  * Re-identifies as a new context: closes the current stream, re-bootstraps
216
- * (a full round trip — flags plus a fresh streamToken), and reconnects.
241
+ * (a full round trip — flags plus a fresh streamToken), reconnects, and
242
+ * emits `change`.
243
+ *
244
+ * THE `change` IS NOT OPTIONAL (issue #788). Replacing the flags map is the
245
+ * whole point of this call, and the SSE handler is the only other thing that
246
+ * emits — so without this a component that rendered against the anonymous
247
+ * bootstrap kept that value for the life of its mount, while the server
248
+ * evaluated the real user's bucket on every request. That is not a corner
249
+ * case: it is every authenticated page load that mounts before login.
250
+ *
251
+ * REJECTS, AND STILL SCHEDULES A RECONNECT. An app awaiting this on login
252
+ * has to be able to see it fail, so the error propagates; but it was also
253
+ * the only way to lose the stream permanently — `eventSource` left `null`
254
+ * with nothing pending, since `maybeReconnect` runs only from an
255
+ * `es.onerror` and there is no `es` any more.
217
256
  */
218
257
  identify(context: PharosContext): Promise<void>;
219
258
  /** Closes the stream, stops any pending reconnect, and unregisters auto-capture. Terminal — construct a new client to resume. */
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  MAX_BUFFER_BYTES,
8
8
  MAX_WINDOW_BYTES,
9
9
  RECORDER_DEFAULTS,
10
+ __require,
10
11
  beginsWithSnapshot,
11
12
  clearPersisted,
12
13
  createDropLedger,
@@ -27,10 +28,7 @@ import {
27
28
  scrubUrl,
28
29
  shouldExclude,
29
30
  shouldMaskByMarker
30
- } from "./index-yabengpp.js";
31
- import {
32
- __require
33
- } from "./index-c3taa3cg.js";
31
+ } from "./index-temc0wzg.js";
34
32
 
35
33
  // src/stack.ts
36
34
  var MAX_FRAMES = 50;
@@ -276,6 +274,7 @@ function sanitizeAttrs(attrs) {
276
274
  return out;
277
275
  }
278
276
  var RECONNECT_BACKOFF_MS = 1000;
277
+ var MAX_RECONNECT_BACKOFF_MS = 30000;
279
278
  function buildBootstrapBody(context) {
280
279
  const { contextKey, application, release, sessionId, attributes } = context;
281
280
  const pharos = { contextKey };
@@ -303,6 +302,7 @@ class PharosBrowserClient {
303
302
  };
304
303
  closed = false;
305
304
  reconnectTimer = null;
305
+ reconnectAttempts = 0;
306
306
  errorsEnabled;
307
307
  errorTarget;
308
308
  replayHandle = null;
@@ -363,6 +363,7 @@ class PharosBrowserClient {
363
363
  }
364
364
  this.flags = payload.flags ?? {};
365
365
  this.version = payload.version;
366
+ this.reconnectAttempts = 0;
366
367
  this.emit("change", payload);
367
368
  } catch (err) {
368
369
  this.emit("error", err);
@@ -375,16 +376,21 @@ class PharosBrowserClient {
375
376
  this.eventSource = es;
376
377
  }
377
378
  maybeReconnect(es) {
378
- if (this.closed || this.reconnectTimer)
379
- return;
380
379
  if (es.readyState !== 2)
381
380
  return;
381
+ this.scheduleReconnect();
382
+ }
383
+ scheduleReconnect() {
384
+ if (this.closed || this.reconnectTimer)
385
+ return;
386
+ const delay = Math.min(RECONNECT_BACKOFF_MS * 2 ** this.reconnectAttempts, MAX_RECONNECT_BACKOFF_MS);
387
+ this.reconnectAttempts += 1;
382
388
  this.reconnectTimer = setTimeout(() => {
383
389
  this.reconnectTimer = null;
384
390
  if (this.closed)
385
391
  return;
386
392
  this.identify(this.context).catch((err) => this.emit("error", err));
387
- }, RECONNECT_BACKOFF_MS);
393
+ }, delay);
388
394
  }
389
395
  onWindowError = (event) => {
390
396
  const e = event;
@@ -473,7 +479,7 @@ class PharosBrowserClient {
473
479
  async startReplay(options = {}) {
474
480
  if (this.closed)
475
481
  return null;
476
- const { startReplayUpload } = await import("./wire-p2kvy6dd.js");
482
+ const { startReplayUpload } = await import("./wire-90whxzan.js");
477
483
  if (this.closed)
478
484
  return null;
479
485
  const handle = startReplayUpload({
@@ -496,10 +502,13 @@ class PharosBrowserClient {
496
502
  this.replayErrorListeners = null;
497
503
  }
498
504
  flag(key, defaultValue) {
499
- if (Object.prototype.hasOwnProperty.call(this.flags, key)) {
500
- return this.flags[key];
505
+ if (!Object.prototype.hasOwnProperty.call(this.flags, key))
506
+ return defaultValue;
507
+ const value = this.flags[key];
508
+ if (defaultValue !== null && defaultValue !== undefined && typeof value !== typeof defaultValue) {
509
+ return defaultValue;
501
510
  }
502
- return defaultValue;
511
+ return value;
503
512
  }
504
513
  on(event, cb) {
505
514
  this.listeners[event].add(cb);
@@ -520,9 +529,15 @@ class PharosBrowserClient {
520
529
  this.eventSource.close();
521
530
  this.eventSource = null;
522
531
  }
523
- await this.bootstrap();
532
+ try {
533
+ await this.bootstrap();
534
+ } catch (err) {
535
+ this.scheduleReconnect();
536
+ throw err;
537
+ }
524
538
  if (!this.closed) {
525
539
  this.connectStream();
540
+ this.emit("change", { version: this.version, flags: this.flags });
526
541
  }
527
542
  }
528
543
  close() {
@@ -544,34 +559,34 @@ class PharosBrowserClient {
544
559
  }
545
560
  }
546
561
  export {
547
- ACTIVATION_EVENT_TAG,
548
- EXCLUDE_CLASS,
549
- LOCAL_TTL_MS,
550
- MASK_CLASS,
551
- MASK_TOKEN,
552
- MAX_BUFFER_BYTES,
553
- MAX_WINDOW_BYTES,
554
- PharosBrowserClient,
555
- RECORDER_DEFAULTS,
556
- beginsWithSnapshot,
557
- clearPersisted,
558
- collectRecordedStrings,
559
- createDropLedger,
560
- createUploadSink,
561
- decide,
562
- drainPersisted,
563
- encodeWindow,
564
- envelopeMetaFor,
565
- isSensitiveField,
566
- maskText,
567
- masksValue,
568
- persistWindow,
569
- postEnvelope,
570
- quotaDroppedWindows,
571
- resetQuotaDroppedWindows,
572
- resolveConfig,
573
- scrubAttribute,
574
- scrubUrl,
562
+ shouldMaskByMarker,
575
563
  shouldExclude,
576
- shouldMaskByMarker
564
+ scrubUrl,
565
+ scrubAttribute,
566
+ resolveConfig,
567
+ resetQuotaDroppedWindows,
568
+ quotaDroppedWindows,
569
+ postEnvelope,
570
+ persistWindow,
571
+ masksValue,
572
+ maskText,
573
+ isSensitiveField,
574
+ envelopeMetaFor,
575
+ encodeWindow,
576
+ drainPersisted,
577
+ decide,
578
+ createUploadSink,
579
+ createDropLedger,
580
+ collectRecordedStrings,
581
+ clearPersisted,
582
+ beginsWithSnapshot,
583
+ RECORDER_DEFAULTS,
584
+ PharosBrowserClient,
585
+ MAX_WINDOW_BYTES,
586
+ MAX_BUFFER_BYTES,
587
+ MASK_TOKEN,
588
+ MASK_CLASS,
589
+ LOCAL_TTL_MS,
590
+ EXCLUDE_CLASS,
591
+ ACTIVATION_EVENT_TAG
577
592
  };
@@ -1,8 +1,15 @@
1
- import"../index-c3taa3cg.js";
2
- export {
1
+ import {
3
2
  SESSION_DROPS_KEY,
4
3
  SESSION_STORAGE_KEY,
5
4
  resolveSessionId,
6
5
  startRecording,
7
6
  startReplayUpload
7
+ } from "../index-xs32mrd9.js";
8
+ import"../index-temc0wzg.js";
9
+ export {
10
+ startReplayUpload,
11
+ startRecording,
12
+ resolveSessionId,
13
+ SESSION_STORAGE_KEY,
14
+ SESSION_DROPS_KEY
8
15
  };
@@ -0,0 +1,13 @@
1
+ import {
2
+ SESSION_DROPS_KEY,
3
+ SESSION_STORAGE_KEY,
4
+ resolveSessionId,
5
+ startReplayUpload
6
+ } from "./index-xs32mrd9.js";
7
+ import"./index-temc0wzg.js";
8
+ export {
9
+ startReplayUpload,
10
+ resolveSessionId,
11
+ SESSION_STORAGE_KEY,
12
+ SESSION_DROPS_KEY
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loxel.dev/pharos-browser",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Browser client for Pharos — server-evaluated feature flags, error capture, and session replay.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,8 @@
45
45
  "access": "public"
46
46
  },
47
47
  "scripts": {
48
- "test": "bun test",
48
+ "typecheck": "tsc --noEmit -p tsconfig.json",
49
+ "test": "bun run typecheck && bun test",
49
50
  "build": "bun build src/index.ts src/replay/index.ts --outdir dist --root src --splitting --target browser --format esm --external rrweb && bun run build:types",
50
51
  "build:types": "tsc -p tsconfig.build.json",
51
52
  "prepublishOnly": "bun run build"
@@ -1,9 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined")
5
- return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- export { __require };