@diegotsi/flint-react 1.0.1 → 1.2.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/dist/index.d.ts CHANGED
@@ -1,122 +1,6 @@
1
+ import { FlintUser, Theme, EnvironmentInfo, ConsoleEntry, NetworkEntry, FormErrorEntry, ReportPayload, ReportResult, FlintWidgetProps } from '@diegotsi/flint-core';
2
+ export { Flint, FlintConfig, FlintUser, FlintWidgetProps, Locale, ReportPayload, ReportResult, Severity, Theme, ThemeOverride, flint } from '@diegotsi/flint-core';
1
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- export { flint } from '@flint/core';
3
-
4
- type Severity = "P1" | "P2" | "P3" | "P4";
5
- interface EnvironmentInfo {
6
- browser: string;
7
- os: string;
8
- viewport: string;
9
- screen: string;
10
- language: string;
11
- timezone: string;
12
- online: boolean;
13
- }
14
- interface ConsoleEntry {
15
- level: "log" | "warn" | "error";
16
- args: string;
17
- timestamp: number;
18
- }
19
- interface NetworkEntry {
20
- method: string;
21
- url: string;
22
- status: number;
23
- duration: number;
24
- timestamp: number;
25
- }
26
- type Locale = "pt-BR" | "en-US";
27
- type Theme = "light" | "dark" | ThemeOverride;
28
- interface ThemeOverride {
29
- background?: string;
30
- accent?: string;
31
- text?: string;
32
- border?: string;
33
- }
34
- interface FlintUser {
35
- id: string;
36
- name: string;
37
- email?: string;
38
- }
39
- interface FlintExtraFields {
40
- /** Session replay URL from Datadog RUM, FullStory, LogRocket, etc. Pass a function for lazy evaluation. */
41
- sessionReplay?: string | (() => string);
42
- [key: string]: unknown;
43
- }
44
- interface FlintWidgetProps {
45
- /** Project API key — get this from the flint-server admin API */
46
- projectKey: string;
47
- /** Full URL of the flint-server, e.g. "https://bugs.example.com" */
48
- serverUrl: string;
49
- /** Authenticated user info (optional) */
50
- user?: FlintUser;
51
- /** Arbitrary metadata to attach to every report */
52
- meta?: Record<string, unknown>;
53
- /** Extra fields for developer use (e.g. external session replay URL) */
54
- extraFields?: FlintExtraFields;
55
- /** Label shown on the trigger button. Default: "Reportar bug" */
56
- buttonLabel?: string;
57
- /** Locale for UI strings. Default: "pt-BR" */
58
- locale?: Locale;
59
- /** Visual theme. Default: "light" */
60
- theme?: Theme;
61
- /** Custom z-index for the widget. Default: 9999 */
62
- zIndex?: number;
63
- /** URL of the admin/status page for reporters to track their bugs */
64
- statusPageUrl?: string;
65
- /**
66
- * Datadog site hostname for auto-generating RUM Session Replay deep links.
67
- * When set, the widget auto-detects `window.DD_RUM` and generates a direct
68
- * link to the session replay at the exact moment the bug was reported.
69
- * Examples: "app.datadoghq.com", "app.datadoghq.eu", "app.us5.datadoghq.com"
70
- */
71
- datadogSite?: string;
72
- /** Enable session replay recording. Default: true */
73
- enableReplay?: boolean;
74
- /** Enable screenshot capture. Default: true */
75
- enableScreenshot?: boolean;
76
- /** Enable console log collection. Default: true */
77
- enableConsole?: boolean;
78
- /** Enable network error collection. Default: true */
79
- enableNetwork?: boolean;
80
- /** Enable frustration detection (rage clicks, dead clicks, error loops). Default: false */
81
- enableFrustration?: boolean;
82
- /** Auto-submit bug report on frustration detection. Default: false */
83
- autoReportFrustration?: boolean;
84
- /** Called before report submission. Return false to cancel. */
85
- onBeforeSubmit?: (payload: ReportPayload) => boolean | Promise<boolean>;
86
- /** Called after successful submission */
87
- onSuccess?: (result: ReportResult) => void;
88
- /** Called when submission fails */
89
- onError?: (error: Error) => void;
90
- /** Called when the modal opens */
91
- onOpen?: () => void;
92
- /** Called when the modal closes */
93
- onClose?: () => void;
94
- }
95
- interface ReportPayload {
96
- reporterId: string;
97
- reporterName: string;
98
- reporterEmail?: string;
99
- description: string;
100
- expectedBehavior?: string;
101
- stepsToReproduce?: {
102
- action: string;
103
- result: string;
104
- }[];
105
- externalReplayUrl?: string;
106
- additionalContext?: string;
107
- severity: Severity;
108
- url?: string;
109
- meta?: Record<string, unknown>;
110
- label?: string;
111
- }
112
- interface ReportResult {
113
- id: string;
114
- status: string;
115
- githubIssueUrl?: string | null;
116
- slackMessageUrl?: string | null;
117
- isDuplicate: boolean;
118
- duplicateOfId?: string | null;
119
- }
120
4
 
121
5
  declare type addedNodeMutation = {
122
6
  parentId: number;
@@ -546,6 +430,7 @@ interface Props {
546
430
  getEnvironment: () => EnvironmentInfo;
547
431
  getConsoleLogs: () => ConsoleEntry[];
548
432
  getNetworkErrors: () => NetworkEntry[];
433
+ getFormErrors: () => FormErrorEntry[];
549
434
  getReplayEvents: () => eventWithTime[];
550
435
  getExternalReplayUrl: () => string | undefined;
551
436
  initialSelection?: string;
@@ -555,8 +440,8 @@ interface Props {
555
440
  onSuccess?: (result: ReportResult) => void;
556
441
  onError?: (error: Error) => void;
557
442
  }
558
- declare function FlintModal({ projectKey, serverUrl, user, meta, theme, zIndex, onClose, getEnvironment, getConsoleLogs, getNetworkErrors, getReplayEvents, getExternalReplayUrl, initialSelection, enableScreenshot, statusPageUrl, onBeforeSubmit, onSuccess, onError, }: Props): react_jsx_runtime.JSX.Element;
443
+ declare function FlintModal({ projectKey, serverUrl, user, meta, theme, zIndex, onClose, getEnvironment, getConsoleLogs, getNetworkErrors, getFormErrors, getReplayEvents, getExternalReplayUrl, initialSelection, enableScreenshot, statusPageUrl, onBeforeSubmit, onSuccess, onError, }: Props): react_jsx_runtime.JSX.Element;
559
444
 
560
445
  declare function FlintWidget(props: FlintWidgetProps): react_jsx_runtime.JSX.Element;
561
446
 
562
- export { FlintModal, type FlintUser, FlintWidget, type FlintWidgetProps, type Locale, type ReportPayload, type ReportResult, type Severity, type Theme, type ThemeOverride };
447
+ export { FlintModal, FlintWidget };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as us
3
3
  import { useTranslation } from "react-i18next";
4
4
 
5
5
  // src/api.ts
6
- import { submitReplay, submitReport } from "@flint/core";
6
+ import { submitReplay, submitReport } from "@diegotsi/flint-core";
7
7
 
8
8
  // src/ScreenAnnotator.tsx
9
9
  import { domToCanvas } from "modern-screenshot";
@@ -148,7 +148,7 @@ function ScreenAnnotator({ zIndex, onCapture, onCancel }) {
148
148
  }
149
149
 
150
150
  // src/theme.ts
151
- import { resolveTheme } from "@flint/core";
151
+ import { resolveTheme } from "@diegotsi/flint-core";
152
152
 
153
153
  // src/FlintModal.tsx
154
154
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -206,6 +206,7 @@ function FlintModal({
206
206
  getEnvironment,
207
207
  getConsoleLogs,
208
208
  getNetworkErrors,
209
+ getFormErrors,
209
210
  getReplayEvents,
210
211
  getExternalReplayUrl,
211
212
  initialSelection = "",
@@ -264,7 +265,8 @@ function FlintModal({
264
265
  ...meta,
265
266
  environment: getEnvironment(),
266
267
  consoleLogs: getConsoleLogs(),
267
- networkErrors: getNetworkErrors()
268
+ networkErrors: getNetworkErrors(),
269
+ formErrors: getFormErrors()
268
270
  };
269
271
  if (isText) {
270
272
  collectedMeta.textIssue = {
@@ -1139,20 +1141,24 @@ function CheckIcon({ size = 20 }) {
1139
1141
  }
1140
1142
 
1141
1143
  // src/FlintWidget.tsx
1144
+ import { Flint as Flint2 } from "@diegotsi/flint-core";
1142
1145
  import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1143
1146
  import { I18nextProvider, useTranslation as useTranslation2 } from "react-i18next";
1144
1147
 
1145
1148
  // src/collectors/console.ts
1146
- import { createConsoleCollector } from "@flint/core";
1149
+ import { createConsoleCollector } from "@diegotsi/flint-core";
1147
1150
 
1148
1151
  // src/collectors/environment.ts
1149
- import { collectEnvironment } from "@flint/core";
1152
+ import { collectEnvironment } from "@diegotsi/flint-core";
1153
+
1154
+ // src/collectors/formErrors.ts
1155
+ import { createFormErrorCollector } from "@diegotsi/flint-core";
1150
1156
 
1151
1157
  // src/collectors/frustration.ts
1152
- import { createFrustrationCollector } from "@flint/core";
1158
+ import { createFrustrationCollector } from "@diegotsi/flint-core";
1153
1159
 
1154
1160
  // src/collectors/network.ts
1155
- import { createNetworkCollector } from "@flint/core";
1161
+ import { createNetworkCollector } from "@diegotsi/flint-core";
1156
1162
 
1157
1163
  // src/i18n/index.ts
1158
1164
  import { createInstance } from "i18next";
@@ -1204,9 +1210,9 @@ widgetI18n.use(initReactI18next).init({
1204
1210
  var i18n_default = widgetI18n;
1205
1211
 
1206
1212
  // src/store.ts
1207
- import { getSnapshot, subscribe } from "@flint/core";
1213
+ import { getSnapshot, subscribe } from "@diegotsi/flint-core";
1208
1214
  import { useSyncExternalStore } from "react";
1209
- import { flint as flint2 } from "@flint/core";
1215
+ import { _setFormErrorCollector, Flint, flint } from "@diegotsi/flint-core";
1210
1216
  function useFlintStore() {
1211
1217
  return useSyncExternalStore(subscribe, getSnapshot);
1212
1218
  }
@@ -1236,6 +1242,7 @@ function WidgetContent({
1236
1242
  enableScreenshot = true,
1237
1243
  enableConsole = true,
1238
1244
  enableNetwork = true,
1245
+ enableFormErrors = true,
1239
1246
  enableFrustration = false,
1240
1247
  autoReportFrustration = false,
1241
1248
  onBeforeSubmit,
@@ -1313,31 +1320,42 @@ function WidgetContent({
1313
1320
  setOpen(true);
1314
1321
  onOpen?.();
1315
1322
  };
1323
+ const globalInit = Flint2.isInitialized();
1324
+ const global = Flint2.getInstance();
1316
1325
  const consoleCollector = useRef3(null);
1317
1326
  const networkCollector = useRef3(null);
1318
1327
  const replayEvents = useRef3([]);
1319
1328
  const stopReplay = useRef3(null);
1320
- if (enableConsole && !consoleCollector.current) {
1321
- consoleCollector.current = createConsoleCollector();
1322
- consoleCollector.current.start();
1323
- }
1324
- if (enableNetwork && !networkCollector.current) {
1325
- const flintHost = (() => {
1326
- try {
1327
- return new URL(serverUrl).hostname;
1328
- } catch {
1329
- return "";
1330
- }
1331
- })();
1332
- networkCollector.current = createNetworkCollector(flintHost ? [flintHost] : []);
1333
- networkCollector.current.start();
1334
- }
1329
+ const formErrorCollector = useRef3(null);
1335
1330
  const frustrationCollector = useRef3(null);
1336
- if (enableFrustration && !frustrationCollector.current) {
1337
- frustrationCollector.current = createFrustrationCollector();
1338
- frustrationCollector.current.start();
1331
+ if (!globalInit) {
1332
+ if (enableConsole && !consoleCollector.current) {
1333
+ consoleCollector.current = createConsoleCollector();
1334
+ consoleCollector.current.start();
1335
+ }
1336
+ if (enableNetwork && !networkCollector.current) {
1337
+ const flintHost = (() => {
1338
+ try {
1339
+ return new URL(serverUrl).hostname;
1340
+ } catch {
1341
+ return "";
1342
+ }
1343
+ })();
1344
+ networkCollector.current = createNetworkCollector(flintHost ? [flintHost] : []);
1345
+ networkCollector.current.start();
1346
+ }
1347
+ if (enableFormErrors && !formErrorCollector.current) {
1348
+ formErrorCollector.current = createFormErrorCollector();
1349
+ formErrorCollector.current.start();
1350
+ _setFormErrorCollector(formErrorCollector.current);
1351
+ }
1352
+ if (enableFrustration && !frustrationCollector.current) {
1353
+ frustrationCollector.current = createFrustrationCollector();
1354
+ frustrationCollector.current.start();
1355
+ }
1339
1356
  }
1340
1357
  useEffect3(() => {
1358
+ if (globalInit) return;
1341
1359
  let cancelled = false;
1342
1360
  if (enableReplay) {
1343
1361
  import("rrweb").then(({ record }) => {
@@ -1358,11 +1376,14 @@ function WidgetContent({
1358
1376
  cancelled = true;
1359
1377
  consoleCollector.current?.stop();
1360
1378
  networkCollector.current?.stop();
1379
+ formErrorCollector.current?.stop();
1380
+ _setFormErrorCollector(null);
1361
1381
  frustrationCollector.current?.stop();
1362
1382
  stopReplay.current?.();
1363
1383
  };
1364
- }, [enableReplay]);
1384
+ }, [enableReplay, globalInit]);
1365
1385
  useEffect3(() => {
1386
+ if (globalInit) return;
1366
1387
  if (!enableFrustration || !autoReportFrustration || !frustrationCollector.current) return;
1367
1388
  const unsubscribe = frustrationCollector.current.onFrustration(async (event) => {
1368
1389
  const user2 = resolvedUser;
@@ -1377,13 +1398,14 @@ function WidgetContent({
1377
1398
  environment: collectEnvironment(),
1378
1399
  consoleLogs: consoleCollector.current?.getEntries() ?? [],
1379
1400
  networkErrors: networkCollector.current?.getEntries() ?? [],
1401
+ formErrors: formErrorCollector.current?.getEntries() ?? [],
1380
1402
  frustrationEvent: event
1381
1403
  }
1382
1404
  }).catch(() => {
1383
1405
  });
1384
1406
  });
1385
1407
  return unsubscribe;
1386
- }, [enableFrustration, autoReportFrustration]);
1408
+ }, [enableFrustration, autoReportFrustration, globalInit]);
1387
1409
  const label = buttonLabel ?? t("buttonLabel");
1388
1410
  return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1389
1411
  /* @__PURE__ */ jsxs3(
@@ -1481,9 +1503,10 @@ function WidgetContent({
1481
1503
  pendingSelection.current = "";
1482
1504
  },
1483
1505
  getEnvironment: collectEnvironment,
1484
- getConsoleLogs: () => consoleCollector.current?.getEntries() ?? [],
1485
- getNetworkErrors: () => networkCollector.current?.getEntries() ?? [],
1486
- getReplayEvents: () => [...replayEvents.current],
1506
+ getConsoleLogs: () => globalInit ? global?.console?.getEntries() ?? [] : consoleCollector.current?.getEntries() ?? [],
1507
+ getNetworkErrors: () => globalInit ? global?.network?.getEntries() ?? [] : networkCollector.current?.getEntries() ?? [],
1508
+ getFormErrors: () => globalInit ? global?.formErrors?.getEntries() ?? [] : formErrorCollector.current?.getEntries() ?? [],
1509
+ getReplayEvents: () => globalInit ? Flint2.getReplayEvents() : [...replayEvents.current],
1487
1510
  getExternalReplayUrl,
1488
1511
  initialSelection: pendingSelection.current,
1489
1512
  enableScreenshot,
@@ -1535,8 +1558,9 @@ function SparkIcon2() {
1535
1558
  );
1536
1559
  }
1537
1560
  export {
1561
+ Flint,
1538
1562
  FlintModal,
1539
1563
  FlintWidget,
1540
- flint2 as flint
1564
+ flint
1541
1565
  };
1542
1566
  //# sourceMappingURL=index.js.map