reflet-sdk 0.7.3 → 0.7.5

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 (34) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +54 -17
  3. package/dist/desktop.d.ts +24 -0
  4. package/dist/desktop.d.ts.map +1 -0
  5. package/dist/desktop.js +118 -0
  6. package/dist/desktop.js.map +1 -0
  7. package/dist/{devtools-layer-CsIcpepI.js → devtools-layer-o1O55wu1.js} +396 -277
  8. package/dist/devtools-layer-o1O55wu1.js.map +1 -0
  9. package/dist/devtools-next.d.ts.map +1 -1
  10. package/dist/devtools-next.js +1 -1
  11. package/dist/{sessions-panel-BYM0UUgT.js → devtools-styles-TWR5Ps34.js} +2078 -1686
  12. package/dist/devtools-styles-TWR5Ps34.js.map +1 -0
  13. package/dist/devtools-vite.js +2 -1
  14. package/dist/devtools-vite.js.map +1 -1
  15. package/dist/feedback.d.ts +7 -3
  16. package/dist/feedback.d.ts.map +1 -1
  17. package/dist/feedback.js +21 -12
  18. package/dist/feedback.js.map +1 -1
  19. package/dist/{handler-17FmwoSX.js → handler-ndF3gZBx.js} +11 -416
  20. package/dist/handler-ndF3gZBx.js.map +1 -0
  21. package/dist/reflet-proxy-BW6q9xds.js +467 -0
  22. package/dist/reflet-proxy-BW6q9xds.js.map +1 -0
  23. package/dist/{submit-Dl0rm6TV.js → submit-DJIKxRUp.js} +2 -2
  24. package/dist/submit-DJIKxRUp.js.map +1 -0
  25. package/dist/{team-layer-Dvjkh-_y.js → team-layer-_It1_oSo.js} +159 -95
  26. package/dist/team-layer-_It1_oSo.js.map +1 -0
  27. package/dist/team-tools-bridge-C51PpkUI.d.ts +31 -0
  28. package/dist/team-tools-bridge-C51PpkUI.d.ts.map +1 -0
  29. package/package.json +5 -1
  30. package/dist/devtools-layer-CsIcpepI.js.map +0 -1
  31. package/dist/handler-17FmwoSX.js.map +0 -1
  32. package/dist/sessions-panel-BYM0UUgT.js.map +0 -1
  33. package/dist/submit-Dl0rm6TV.js.map +0 -1
  34. package/dist/team-layer-Dvjkh-_y.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as teamAccessStorageKey, t as TEAM_ACTIVATION_PARAM } from "./team-activation-Cv5U7OF1.js";
2
- import { A as Notices, M as reportFailure, _ as teamTransport, a as DevtoolsBar, d as PersonIcon, f as SessionWorkspaceProvider, h as purgeAccount, i as BarButton, k as sessionApiErrorSchema, l as SESSIONS_STYLES, m as useWorkspaceLoader, n as DEVTOOLS_STYLES, p as failureMessage, r as DevtoolsSheet, s as SessionsBarButton, t as SessionsPanel, u as RecordingLayer } from "./sessions-panel-BYM0UUgT.js";
2
+ import { B as reportFailure, C as bridgeTransport, L as sessionApiErrorSchema, R as Notices, S as purgeAccount, T as teamTransport, _ as RecordingLayer, b as failureMessage, d as sessionSheetPlace, f as useSessionNavigation, l as SESSIONS_STYLES, n as DevtoolsSheet, o as SessionsBarButton, r as DevtoolsBar, s as AccountFooter, t as DEVTOOLS_STYLES, u as SessionsPanel, x as useWorkspaceLoader, y as SessionWorkspaceProvider } from "./devtools-styles-TWR5Ps34.js";
3
3
  import { z } from "zod";
4
- import { useEffect, useState } from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useMemo, useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/sessions/team/team-auth.ts
7
7
  const RETURN_CODE_PARAM = "reflet-team-code";
8
8
  const RETURN_ERROR_PARAM = "reflet-team-error";
@@ -181,32 +181,32 @@ async function forgetTeamAccess(options) {
181
181
  }
182
182
  //#endregion
183
183
  //#region src/sessions/team/team-sign-in-card.tsx
184
- function useTeamSignIn(connection) {
185
- const [isLeaving, setIsLeaving] = useState(false);
184
+ function useTeamSignIn(onSignIn) {
185
+ const [isSigningIn, setIsSigningIn] = useState(false);
186
186
  const [failure, setFailure] = useState(null);
187
187
  const signIn = async () => {
188
- setIsLeaving(true);
188
+ setIsSigningIn(true);
189
189
  setFailure(null);
190
190
  try {
191
- window.location.assign(await prepareTeamSignIn(connection));
191
+ await onSignIn();
192
192
  } catch (error) {
193
193
  setFailure(failureMessage(error));
194
- setIsLeaving(false);
194
+ setIsSigningIn(false);
195
195
  }
196
196
  };
197
197
  return {
198
198
  failure,
199
- isLeaving,
199
+ isSigningIn,
200
200
  signIn
201
201
  };
202
202
  }
203
- function SignInActions({ isSigningIn, onHide, onSignIn }) {
203
+ function SignInActions({ canSignIn, onHide, onSignIn }) {
204
204
  return /* @__PURE__ */ jsxs("div", {
205
205
  className: "dt-actions",
206
206
  children: [/* @__PURE__ */ jsx("button", {
207
207
  className: "dt-btn",
208
208
  "data-variant": "primary",
209
- disabled: isSigningIn,
209
+ disabled: !canSignIn,
210
210
  onClick: onSignIn,
211
211
  type: "button",
212
212
  children: "Sign in with Reflet"
@@ -218,9 +218,11 @@ function SignInActions({ isSigningIn, onHide, onSignIn }) {
218
218
  })]
219
219
  });
220
220
  }
221
- function TeamSignInCard({ connection, isFinishing, notice }) {
221
+ function TeamSignInCard({ isFinishing, notice, onSignIn, continuesInBrowser }) {
222
222
  const [isHidden, setIsHidden] = useState(false);
223
- const { failure, isLeaving, signIn } = useTeamSignIn(connection);
223
+ const { failure, isSigningIn, signIn } = useTeamSignIn(onSignIn);
224
+ const isWaitingForBrowser = continuesInBrowser && isSigningIn;
225
+ const isLeavingPage = isSigningIn && !continuesInBrowser;
224
226
  if (isHidden) return null;
225
227
  return /* @__PURE__ */ jsxs("section", {
226
228
  "aria-labelledby": "rf-team-title",
@@ -244,6 +246,11 @@ function TeamSignInCard({ connection, isFinishing, notice }) {
244
246
  role: "status",
245
247
  children: "Finishing sign-in…"
246
248
  }),
249
+ isWaitingForBrowser && /* @__PURE__ */ jsx("p", {
250
+ className: "dt-meta",
251
+ role: "status",
252
+ children: "Finish signing in in your browser."
253
+ }),
247
254
  notice && /* @__PURE__ */ jsx("p", {
248
255
  className: "dt-meta",
249
256
  role: "status",
@@ -255,7 +262,7 @@ function TeamSignInCard({ connection, isFinishing, notice }) {
255
262
  children: failure
256
263
  }),
257
264
  /* @__PURE__ */ jsx(SignInActions, {
258
- isSigningIn: isLeaving || isFinishing,
265
+ canSignIn: !(isLeavingPage || isFinishing),
259
266
  onHide: () => setIsHidden(true),
260
267
  onSignIn: signIn
261
268
  })
@@ -266,22 +273,6 @@ function TeamSignInCard({ connection, isFinishing, notice }) {
266
273
  //#region src/sessions/team/team-layer.tsx
267
274
  const TEAM_TOOLS_LABEL = "Reflet team tools";
268
275
  const REFUSED_SIGN_IN_COPY = "Reflet no longer accepts this sign-in. Sign in again to record.";
269
- function AccountControls({ access, onSignOut, workspace }) {
270
- const [isOpen, setIsOpen] = useState(false);
271
- const user = workspace?.account.userName ?? workspace?.account.userEmail;
272
- const label = user ? `Signed in as ${user} · ${access.organizationName}` : `Signed in · ${access.organizationName}`;
273
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(BarButton, {
274
- label,
275
- onClick: () => setIsOpen(!isOpen),
276
- pressed: isOpen,
277
- children: /* @__PURE__ */ jsx(PersonIcon, {})
278
- }), isOpen && /* @__PURE__ */ jsx("button", {
279
- className: "dt-btn",
280
- onClick: onSignOut,
281
- type: "button",
282
- children: "Sign out"
283
- })] });
284
- }
285
276
  function SessionsBody({ navigation, onStarted, state }) {
286
277
  if (state.kind === "ready") return /* @__PURE__ */ jsx(SessionsPanel, {
287
278
  navigation,
@@ -302,44 +293,40 @@ function SessionsBody({ navigation, onStarted, state }) {
302
293
  children: "Opening your sessions…"
303
294
  });
304
295
  }
305
- function TeamSheet({ navigation, onOpenChange, side, state }) {
296
+ function TeamSheet({ account, navigation, onClose, side }) {
297
+ const { state } = account;
298
+ const place = state.kind === "ready" ? sessionSheetPlace(navigation) : null;
306
299
  return /* @__PURE__ */ jsx(DevtoolsSheet, {
300
+ footer: place === null && /* @__PURE__ */ jsx(AccountFooter, {
301
+ actions: [{
302
+ label: "Sign out",
303
+ onSelect: account.onSignOut
304
+ }],
305
+ organizationName: account.organizationName,
306
+ person: state.kind === "ready" ? state.workspace.identity.user : null
307
+ }),
307
308
  label: TEAM_TOOLS_LABEL,
308
- onClose: () => onOpenChange(false),
309
- onSelectTab: () => onOpenChange(true),
309
+ navigation: {
310
+ onSelectTab: () => void 0,
311
+ place,
312
+ tab: "sessions",
313
+ tabs: ["sessions"]
314
+ },
315
+ onClose,
310
316
  side,
311
- tab: "sessions",
312
- tabs: ["sessions"],
313
317
  children: /* @__PURE__ */ jsx(SessionsBody, {
314
318
  navigation,
315
- onStarted: () => onOpenChange(false),
319
+ onStarted: onClose,
316
320
  state
317
321
  })
318
322
  });
319
323
  }
320
- function useTeamTransport(team) {
321
- const createTransport = () => ({
322
- apiUrl: team.apiUrl,
323
- transport: teamTransport({
324
- apiUrl: team.apiUrl,
325
- token: team.access.token
326
- })
327
- });
328
- const [current, setCurrent] = useState(createTransport);
329
- if (current.apiUrl !== team.apiUrl) {
330
- const next = createTransport();
331
- setCurrent(next);
332
- return next.transport;
333
- }
334
- return current.transport;
335
- }
336
324
  function useTeamWorkspace(team) {
337
325
  const { onSignOut } = team;
338
- const transport = useTeamTransport(team);
339
326
  const state = useWorkspaceLoader({
340
327
  onUnauthorized: () => onSignOut(REFUSED_SIGN_IN_COPY),
341
328
  surface: "app",
342
- transport
329
+ transport: team.transport
343
330
  });
344
331
  const workspace = state.kind === "ready" ? state.workspace : null;
345
332
  const signOut = async () => {
@@ -355,50 +342,54 @@ function useTeamWorkspace(team) {
355
342
  function TeamTools({ team, placement }) {
356
343
  const { signOut, state, workspace } = useTeamWorkspace(team);
357
344
  const [isSheetOpen, setIsSheetOpen] = useState(false);
358
- const [open, setOpen] = useState(null);
345
+ const navigation = useSessionNavigation();
359
346
  return /* @__PURE__ */ jsxs(SessionWorkspaceProvider, {
360
347
  workspace,
361
348
  children: [
362
349
  isSheetOpen && /* @__PURE__ */ jsx(TeamSheet, {
363
- navigation: {
364
- onOpen: setOpen,
365
- open
350
+ account: {
351
+ onSignOut: signOut,
352
+ organizationName: team.organizationName,
353
+ state
366
354
  },
367
- onOpenChange: setIsSheetOpen,
368
- side: placement.side,
369
- state
355
+ navigation,
356
+ onClose: () => setIsSheetOpen(false),
357
+ side: placement.side
370
358
  }),
371
- /* @__PURE__ */ jsxs(DevtoolsBar, {
359
+ /* @__PURE__ */ jsx(DevtoolsBar, {
372
360
  edge: placement.edge,
373
361
  label: TEAM_TOOLS_LABEL,
374
362
  side: placement.side,
375
- children: [/* @__PURE__ */ jsx(SessionsBarButton, {
376
- isOpen: isSheetOpen,
377
- onToggle: () => setIsSheetOpen(!isSheetOpen)
378
- }), /* @__PURE__ */ jsx(AccountControls, {
379
- access: team.access,
380
- onSignOut: signOut,
381
- workspace
382
- })]
363
+ children: /* @__PURE__ */ jsx(SessionsBarButton, { onOpen: () => setIsSheetOpen(true) })
383
364
  }),
384
365
  workspace && /* @__PURE__ */ jsx(RecordingLayer, { onFinished: (target) => {
385
- setOpen(target);
366
+ navigation.go({
367
+ target,
368
+ view: "review"
369
+ });
386
370
  setIsSheetOpen(true);
387
371
  } })
388
372
  ]
389
373
  });
390
374
  }
391
- function placementFor(position) {
392
- return {
393
- edge: position.startsWith("top") ? "top" : "bottom",
394
- side: position.endsWith("right") ? "right" : "left"
395
- };
375
+ function SignedOutAnchor({ children, placement }) {
376
+ return /* @__PURE__ */ jsx("div", {
377
+ className: "dt-team-anchor",
378
+ "data-edge": placement.edge,
379
+ "data-side": placement.side,
380
+ children
381
+ });
396
382
  }
397
- function TeamLayer({ connection, position }) {
383
+ /** A web page signs in by leaving for reflet.app and coming back with a token for its own origin. */
384
+ function BrowserTeamAccount({ connection, placement }) {
398
385
  const { apiUrl, publicKey } = connection;
399
386
  const [access, setAccess] = useState(() => readTeamAccess(publicKey));
400
387
  const [notice, setNotice] = useState(null);
401
388
  const [isFinishing, setIsFinishing] = useState(hasTeamSignInReturn);
389
+ const transport = useMemo(() => access && teamTransport({
390
+ apiUrl,
391
+ token: access.token
392
+ }), [access, apiUrl]);
402
393
  useEffect(() => {
403
394
  finishTeamSignIn({
404
395
  apiUrl,
@@ -416,27 +407,100 @@ function TeamLayer({ connection, position }) {
416
407
  publicKey
417
408
  }).catch((error) => reportFailure("Signing out of Reflet did not finish", error));
418
409
  };
419
- const placement = placementFor(position);
410
+ if (access && transport) return /* @__PURE__ */ jsx(TeamTools, {
411
+ placement,
412
+ team: {
413
+ onSignOut: signOut,
414
+ organizationName: access.organizationName,
415
+ transport
416
+ }
417
+ });
418
+ return /* @__PURE__ */ jsx(SignedOutAnchor, {
419
+ placement,
420
+ children: /* @__PURE__ */ jsx(TeamSignInCard, {
421
+ continuesInBrowser: false,
422
+ isFinishing,
423
+ notice,
424
+ onSignIn: async () => window.location.assign(await prepareTeamSignIn(connection))
425
+ })
426
+ });
427
+ }
428
+ /** A desktop app signs in through the system browser; its privileged process keeps the credential. */
429
+ function BridgeTeamAccount({ bridge, placement }) {
430
+ const [accountState, setAccountState] = useState({ kind: "loading" });
431
+ const [notice, setNotice] = useState(null);
432
+ const transport = useMemo(() => bridgeTransport(bridge), [bridge]);
433
+ useEffect(() => {
434
+ let isCurrent = true;
435
+ bridge.account().then((account) => isCurrent && setAccountState({
436
+ account,
437
+ kind: "loaded"
438
+ }), (error) => {
439
+ if (isCurrent) {
440
+ setAccountState({
441
+ account: null,
442
+ kind: "loaded"
443
+ });
444
+ setNotice(failureMessage(error));
445
+ }
446
+ });
447
+ return () => {
448
+ isCurrent = false;
449
+ };
450
+ }, [bridge]);
451
+ const signIn = async () => {
452
+ const account = await bridge.signIn();
453
+ setAccountState({
454
+ account,
455
+ kind: "loaded"
456
+ });
457
+ setNotice(null);
458
+ };
459
+ const signOut = (reason) => {
460
+ setAccountState({
461
+ account: null,
462
+ kind: "loaded"
463
+ });
464
+ setNotice(reason);
465
+ bridge.signOut().catch((error) => reportFailure("Signing out of Reflet did not finish", error));
466
+ };
467
+ if (accountState.kind === "loading") return null;
468
+ if (accountState.account) return /* @__PURE__ */ jsx(TeamTools, {
469
+ placement,
470
+ team: {
471
+ onSignOut: signOut,
472
+ organizationName: accountState.account.organizationName,
473
+ transport
474
+ }
475
+ });
476
+ return /* @__PURE__ */ jsx(SignedOutAnchor, {
477
+ placement,
478
+ children: /* @__PURE__ */ jsx(TeamSignInCard, {
479
+ continuesInBrowser: true,
480
+ isFinishing: false,
481
+ notice,
482
+ onSignIn: signIn
483
+ })
484
+ });
485
+ }
486
+ function placementFor(position) {
487
+ return {
488
+ edge: position.startsWith("top") ? "top" : "bottom",
489
+ side: position.endsWith("right") ? "right" : "left"
490
+ };
491
+ }
492
+ function TeamLayer(props) {
493
+ const placement = placementFor(props.position);
420
494
  return /* @__PURE__ */ jsxs("div", {
421
495
  className: "dt",
422
496
  children: [
423
497
  /* @__PURE__ */ jsx("style", { children: `${DEVTOOLS_STYLES}${SESSIONS_STYLES}` }),
424
- access ? /* @__PURE__ */ jsx(TeamTools, {
425
- placement,
426
- team: {
427
- access,
428
- apiUrl,
429
- onSignOut: signOut
430
- }
431
- }) : /* @__PURE__ */ jsx("div", {
432
- className: "dt-team-anchor",
433
- "data-edge": placement.edge,
434
- "data-side": placement.side,
435
- children: /* @__PURE__ */ jsx(TeamSignInCard, {
436
- connection,
437
- isFinishing,
438
- notice
439
- })
498
+ "bridge" in props ? /* @__PURE__ */ jsx(BridgeTeamAccount, {
499
+ bridge: props.bridge,
500
+ placement
501
+ }) : /* @__PURE__ */ jsx(BrowserTeamAccount, {
502
+ connection: props.connection,
503
+ placement
440
504
  }),
441
505
  /* @__PURE__ */ jsx(Notices, {})
442
506
  ]
@@ -445,4 +509,4 @@ function TeamLayer({ connection, position }) {
445
509
  //#endregion
446
510
  export { TeamLayer };
447
511
 
448
- //# sourceMappingURL=team-layer-Dvjkh-_y.js.map
512
+ //# sourceMappingURL=team-layer-_It1_oSo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-layer-_It1_oSo.js","names":[],"sources":["../src/sessions/team/team-auth.ts","../src/sessions/team/team-sign-in-card.tsx","../src/sessions/team/team-layer.tsx"],"sourcesContent":["import { z } from \"zod\";\nimport { sessionApiErrorSchema } from \"../protocol\";\nimport { TEAM_ACTIVATION_PARAM, teamAccessStorageKey } from \"./team-activation\";\n\nconst RETURN_CODE_PARAM = \"reflet-team-code\";\nconst RETURN_ERROR_PARAM = \"reflet-team-error\";\nconst RETURN_STATE_PARAM = \"reflet-team-state\";\nconst RETURN_PARAMS = [\n RETURN_CODE_PARAM,\n RETURN_ERROR_PARAM,\n RETURN_STATE_PARAM,\n];\nconst PENDING_SIGN_IN_PREFIX = \"reflet-team-sign-in:\";\nconst PKCE_RANDOM_BYTES = 32;\nconst TRAILING_SLASHES = /\\/+$/;\nconst BASE64_PADDING = /[=]+$/;\n\nconst teamAccessSchema = z.object({\n expiresAt: z.number(),\n organizationName: z.string(),\n token: z.string().startsWith(\"fb_team_\"),\n});\n\nexport type TeamAccess = z.infer<typeof teamAccessSchema>;\n\nconst pendingSignInSchema = z.object({\n codeVerifier: z.string(),\n state: z.string(),\n});\n\ntype PendingSignIn = z.infer<typeof pendingSignInSchema>;\n\nconst SIGN_IN_FAILURE_MESSAGES = {\n cancelled: \"Sign-in was cancelled.\",\n exchange_failed: \"Couldn't finish signing in to Reflet. Try again.\",\n interrupted:\n \"This sign-in was started in another tab or has expired. Sign in again.\",\n storage_blocked:\n \"Your browser blocks storage for this site, so Reflet sign-in can't finish. Allow site data, then try again.\",\n} as const;\n\nexport type TeamSignInFailure = keyof typeof SIGN_IN_FAILURE_MESSAGES;\n\nexport class TeamSignInError extends Error {\n readonly reason: TeamSignInFailure;\n\n constructor(reason: TeamSignInFailure, message?: string) {\n super(message ?? SIGN_IN_FAILURE_MESSAGES[reason]);\n this.name = \"TeamSignInError\";\n this.reason = reason;\n }\n}\n\nfunction base64Url(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...bytes))\n .replaceAll(\"+\", \"-\")\n .replaceAll(\"/\", \"_\")\n .replace(BASE64_PADDING, \"\");\n}\n\nfunction randomBase64Url(): string {\n return base64Url(crypto.getRandomValues(new Uint8Array(PKCE_RANDOM_BYTES)));\n}\n\nasync function pkceChallengeFor(codeVerifier: string): Promise<string> {\n const digest = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(codeVerifier)\n );\n return base64Url(new Uint8Array(digest));\n}\n\nfunction apiEndpoint(apiUrl: string, path: string): string {\n return `${apiUrl.replace(TRAILING_SLASHES, \"\")}${path}`;\n}\n\nfunction parseStoredJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\nfunction storedTeamAccess(publicKey: string): TeamAccess | null {\n try {\n const raw = localStorage.getItem(teamAccessStorageKey(publicKey));\n const parsed = teamAccessSchema.safeParse(\n raw === null ? null : parseStoredJson(raw)\n );\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\nfunction persistTeamAccess(publicKey: string, access: TeamAccess | null): void {\n try {\n if (access) {\n localStorage.setItem(\n teamAccessStorageKey(publicKey),\n JSON.stringify(access)\n );\n } else {\n localStorage.removeItem(teamAccessStorageKey(publicKey));\n }\n } catch {\n // Storage disabled by browser policy: access then only lasts for this page.\n }\n}\n\nexport function readTeamAccess(publicKey: string): TeamAccess | null {\n const access = storedTeamAccess(publicKey);\n if (access && access.expiresAt > Date.now()) {\n return access;\n }\n persistTeamAccess(publicKey, null);\n return null;\n}\n\nfunction pendingSignInKey(publicKey: string): string {\n return `${PENDING_SIGN_IN_PREFIX}${publicKey}`;\n}\n\nfunction takePendingSignIn(publicKey: string): PendingSignIn | null {\n try {\n const key = pendingSignInKey(publicKey);\n const raw = sessionStorage.getItem(key);\n sessionStorage.removeItem(key);\n const parsed = pendingSignInSchema.safeParse(\n raw === null ? null : parseStoredJson(raw)\n );\n return parsed.success ? parsed.data : null;\n } catch {\n return null;\n }\n}\n\nfunction rememberPendingSignIn(publicKey: string, pending: PendingSignIn) {\n try {\n sessionStorage.setItem(\n pendingSignInKey(publicKey),\n JSON.stringify(pending)\n );\n } catch {\n throw new TeamSignInError(\"storage_blocked\");\n }\n}\n\nfunction returnUrl(): string {\n const url = new URL(window.location.href);\n for (const param of RETURN_PARAMS) {\n url.searchParams.delete(param);\n }\n if (!url.searchParams.has(TEAM_ACTIVATION_PARAM)) {\n url.searchParams.set(TEAM_ACTIVATION_PARAM, \"\");\n }\n return url.href;\n}\n\nasync function exchangeCode(options: {\n apiUrl: string;\n code: string;\n codeVerifier: string;\n}): Promise<TeamAccess> {\n const response = await fetch(\n apiEndpoint(options.apiUrl, \"/api/v1/team/token\"),\n {\n body: JSON.stringify({\n code: options.code,\n codeVerifier: options.codeVerifier,\n }),\n headers: { \"Content-Type\": \"application/json\" },\n method: \"POST\",\n }\n );\n if (!response.ok) {\n const failure = sessionApiErrorSchema.safeParse(\n await response.json().catch(() => null)\n );\n throw new TeamSignInError(\n \"exchange_failed\",\n failure.success ? failure.data.error : undefined\n );\n }\n const access = teamAccessSchema.safeParse(\n await response.json().catch(() => null)\n );\n if (!access.success) {\n throw new TeamSignInError(\"exchange_failed\");\n }\n return access.data;\n}\n\nexport async function prepareTeamSignIn(options: {\n appUrl: string;\n publicKey: string;\n}): Promise<string> {\n const pending = { codeVerifier: randomBase64Url(), state: randomBase64Url() };\n rememberPendingSignIn(options.publicKey, pending);\n const consentUrl = new URL(\"/auth/team\", options.appUrl);\n consentUrl.search = new URLSearchParams({\n code_challenge: await pkceChallengeFor(pending.codeVerifier),\n origin: window.location.origin,\n public_key: options.publicKey,\n return_to: returnUrl(),\n state: pending.state,\n }).toString();\n return consentUrl.href;\n}\n\nexport function hasTeamSignInReturn(): boolean {\n return new URL(window.location.href).searchParams.has(RETURN_STATE_PARAM);\n}\n\nasync function completeReturnedSignIn(options: {\n apiUrl: string;\n code: string | null;\n pending: PendingSignIn | null;\n publicKey: string;\n state: string;\n}): Promise<TeamAccess> {\n if (options.pending?.state !== options.state) {\n throw new TeamSignInError(\"interrupted\");\n }\n if (options.code === null) {\n throw new TeamSignInError(\"cancelled\");\n }\n const access = await exchangeCode({\n apiUrl: options.apiUrl,\n code: options.code,\n codeVerifier: options.pending.codeVerifier,\n });\n persistTeamAccess(options.publicKey, access);\n return access;\n}\n\n/** Strips the returned code from the address bar at once, so it is consumed by one caller only. */\nexport function finishTeamSignIn(options: {\n apiUrl: string;\n publicKey: string;\n}): Promise<TeamAccess> | null {\n const url = new URL(window.location.href);\n const state = url.searchParams.get(RETURN_STATE_PARAM);\n if (state === null) {\n return null;\n }\n const code = url.searchParams.get(RETURN_CODE_PARAM);\n for (const param of RETURN_PARAMS) {\n url.searchParams.delete(param);\n }\n window.history.replaceState(window.history.state, \"\", url.href);\n return completeReturnedSignIn({\n apiUrl: options.apiUrl,\n code,\n pending: takePendingSignIn(options.publicKey),\n publicKey: options.publicKey,\n state,\n });\n}\n\nexport async function forgetTeamAccess(options: {\n apiUrl: string;\n publicKey: string;\n}): Promise<void> {\n const access = storedTeamAccess(options.publicKey);\n persistTeamAccess(options.publicKey, null);\n if (!access) {\n return;\n }\n await fetch(apiEndpoint(options.apiUrl, \"/api/v1/team/revoke\"), {\n headers: { Authorization: `Bearer ${access.token}` },\n method: \"POST\",\n });\n}\n","import { useState } from \"react\";\nimport { failureMessage } from \"../ui/session-workspace\";\n\nexport interface TeamConnection {\n apiUrl: string;\n appUrl: string;\n publicKey: string;\n}\n\nfunction useTeamSignIn(onSignIn: () => Promise<void>) {\n const [isSigningIn, setIsSigningIn] = useState(false);\n const [failure, setFailure] = useState<string | null>(null);\n\n const signIn = async () => {\n setIsSigningIn(true);\n setFailure(null);\n try {\n await onSignIn();\n } catch (error) {\n setFailure(failureMessage(error));\n setIsSigningIn(false);\n }\n };\n\n return { failure, isSigningIn, signIn };\n}\n\nfunction SignInActions({\n canSignIn,\n onHide,\n onSignIn,\n}: {\n canSignIn: boolean;\n onHide: () => void;\n onSignIn: () => void;\n}) {\n return (\n <div className=\"dt-actions\">\n <button\n className=\"dt-btn\"\n data-variant=\"primary\"\n disabled={!canSignIn}\n onClick={onSignIn}\n type=\"button\"\n >\n Sign in with Reflet\n </button>\n <button className=\"dt-btn\" onClick={onHide} type=\"button\">\n Hide\n </button>\n </div>\n );\n}\n\nexport function TeamSignInCard({\n isFinishing,\n notice,\n onSignIn,\n continuesInBrowser,\n}: {\n isFinishing: boolean;\n notice: string | null;\n onSignIn: () => Promise<void>;\n continuesInBrowser: boolean;\n}) {\n const [isHidden, setIsHidden] = useState(false);\n const { failure, isSigningIn, signIn } = useTeamSignIn(onSignIn);\n const isWaitingForBrowser = continuesInBrowser && isSigningIn;\n const isLeavingPage = isSigningIn && !continuesInBrowser;\n\n if (isHidden) {\n return null;\n }\n return (\n <section aria-labelledby=\"rf-team-title\" className=\"dt-team-card glass\">\n <h2 className=\"dt-ses-subhead\" id=\"rf-team-title\">\n Reflet team tools\n </h2>\n <p className=\"dt-meta\">\n Sign in with your Reflet account to record a session on this app.\n </p>\n <p className=\"dt-meta\">\n Only members of the app's Reflet organization can sign in.\n </p>\n {isFinishing && (\n <p className=\"dt-meta\" role=\"status\">\n Finishing sign-in…\n </p>\n )}\n {isWaitingForBrowser && (\n <p className=\"dt-meta\" role=\"status\">\n Finish signing in in your browser.\n </p>\n )}\n {notice && (\n <p className=\"dt-meta\" role=\"status\">\n {notice}\n </p>\n )}\n {failure && (\n <p className=\"dt-error\" role=\"alert\">\n {failure}\n </p>\n )}\n <SignInActions\n canSignIn={!(isLeavingPage || isFinishing)}\n onHide={() => setIsHidden(true)}\n onSignIn={signIn}\n />\n </section>\n );\n}\n","import { type ReactNode, useEffect, useMemo, useState } from \"react\";\nimport { AccountFooter } from \"../../devtools/client/account/account-footer\";\nimport {\n DevtoolsBar,\n SessionsBarButton,\n} from \"../../devtools/client/devtools-bar\";\nimport { DevtoolsSheet } from \"../../devtools/client/devtools-sheet\";\nimport { DEVTOOLS_STYLES } from \"../../devtools/client/devtools-styles\";\nimport { Notices, reportFailure } from \"../../devtools/client/notices\";\nimport type { RefletFeedbackProps } from \"../../feedback/types\";\nimport type { SessionTransport } from \"../api/session-api\";\nimport { bridgeTransport, teamTransport } from \"../api/session-transports\";\nimport { purgeAccount } from \"../local/local-sessions\";\nimport { RecordingLayer } from \"../ui/recording/recording-layer\";\nimport {\n failureMessage,\n SessionWorkspaceProvider,\n useWorkspaceLoader,\n type WorkspaceState,\n} from \"../ui/session-workspace\";\nimport {\n type SessionNavigation,\n SessionsPanel,\n sessionSheetPlace,\n useSessionNavigation,\n} from \"../ui/sessions-panel\";\nimport { SESSIONS_STYLES } from \"../ui/sessions-styles\";\nimport {\n finishTeamSignIn,\n forgetTeamAccess,\n hasTeamSignInReturn,\n prepareTeamSignIn,\n readTeamAccess,\n} from \"./team-auth\";\nimport { type TeamConnection, TeamSignInCard } from \"./team-sign-in-card\";\nimport type { TeamToolsAccount, TeamToolsBridge } from \"./team-tools-bridge\";\n\nconst TEAM_TOOLS_LABEL = \"Reflet team tools\";\nconst REFUSED_SIGN_IN_COPY =\n \"Reflet no longer accepts this sign-in. Sign in again to record.\";\n\ninterface TeamPlacement {\n edge: \"bottom\" | \"top\";\n side: \"left\" | \"right\";\n}\n\ninterface SignedInTeam {\n onSignOut: (reason: string | null) => void;\n organizationName: string;\n transport: SessionTransport;\n}\n\nfunction SessionsBody({\n navigation,\n onStarted,\n state,\n}: {\n navigation: SessionNavigation;\n onStarted: () => void;\n state: WorkspaceState;\n}) {\n if (state.kind === \"ready\") {\n return <SessionsPanel navigation={navigation} onStarted={onStarted} />;\n }\n if (state.kind === \"failed\") {\n return (\n <div className=\"dt-empty\" role=\"alert\">\n <p>Sessions did not open: {state.message}</p>\n <button className=\"dt-btn\" onClick={state.retry} type=\"button\">\n Try again\n </button>\n </div>\n );\n }\n return <p className=\"dt-status\">Opening your sessions…</p>;\n}\n\nfunction TeamSheet({\n account,\n navigation,\n onClose,\n side,\n}: {\n account: {\n onSignOut: () => void;\n organizationName: string;\n state: WorkspaceState;\n };\n navigation: SessionNavigation;\n onClose: () => void;\n side: TeamPlacement[\"side\"];\n}) {\n const { state } = account;\n const place = state.kind === \"ready\" ? sessionSheetPlace(navigation) : null;\n return (\n <DevtoolsSheet\n footer={\n place === null && (\n <AccountFooter\n actions={[{ label: \"Sign out\", onSelect: account.onSignOut }]}\n organizationName={account.organizationName}\n person={\n state.kind === \"ready\" ? state.workspace.identity.user : null\n }\n />\n )\n }\n label={TEAM_TOOLS_LABEL}\n navigation={{\n onSelectTab: () => undefined,\n place,\n tab: \"sessions\",\n tabs: [\"sessions\"],\n }}\n onClose={onClose}\n side={side}\n >\n <SessionsBody navigation={navigation} onStarted={onClose} state={state} />\n </DevtoolsSheet>\n );\n}\n\nfunction useTeamWorkspace(team: SignedInTeam) {\n const { onSignOut } = team;\n const state = useWorkspaceLoader({\n onUnauthorized: () => onSignOut(REFUSED_SIGN_IN_COPY),\n surface: \"app\",\n transport: team.transport,\n });\n const workspace = state.kind === \"ready\" ? state.workspace : null;\n\n const signOut = async () => {\n if (workspace) {\n await purgeAccount(workspace.account.key).catch((error: unknown) =>\n reportFailure(\"This device's session data was not removed\", error)\n );\n }\n onSignOut(null);\n };\n\n return { signOut, state, workspace };\n}\n\nfunction TeamTools({\n team,\n placement,\n}: {\n placement: TeamPlacement;\n team: SignedInTeam;\n}) {\n const { signOut, state, workspace } = useTeamWorkspace(team);\n const [isSheetOpen, setIsSheetOpen] = useState(false);\n const navigation = useSessionNavigation();\n\n return (\n <SessionWorkspaceProvider workspace={workspace}>\n {isSheetOpen && (\n <TeamSheet\n account={{\n onSignOut: signOut,\n organizationName: team.organizationName,\n state,\n }}\n navigation={navigation}\n onClose={() => setIsSheetOpen(false)}\n side={placement.side}\n />\n )}\n <DevtoolsBar\n edge={placement.edge}\n label={TEAM_TOOLS_LABEL}\n side={placement.side}\n >\n <SessionsBarButton onOpen={() => setIsSheetOpen(true)} />\n </DevtoolsBar>\n {workspace && (\n <RecordingLayer\n onFinished={(target) => {\n navigation.go({ target, view: \"review\" });\n setIsSheetOpen(true);\n }}\n />\n )}\n </SessionWorkspaceProvider>\n );\n}\n\nfunction SignedOutAnchor({\n children,\n placement,\n}: {\n children: ReactNode;\n placement: TeamPlacement;\n}) {\n return (\n <div\n className=\"dt-team-anchor\"\n data-edge={placement.edge}\n data-side={placement.side}\n >\n {children}\n </div>\n );\n}\n\n/** A web page signs in by leaving for reflet.app and coming back with a token for its own origin. */\nfunction BrowserTeamAccount({\n connection,\n placement,\n}: {\n connection: TeamConnection;\n placement: TeamPlacement;\n}) {\n const { apiUrl, publicKey } = connection;\n const [access, setAccess] = useState(() => readTeamAccess(publicKey));\n const [notice, setNotice] = useState<string | null>(null);\n const [isFinishing, setIsFinishing] = useState(hasTeamSignInReturn);\n const transport = useMemo(\n () => access && teamTransport({ apiUrl, token: access.token }),\n [access, apiUrl]\n );\n useEffect(() => {\n finishTeamSignIn({ apiUrl, publicKey })\n ?.then(\n (signedIn) => {\n setAccess(signedIn);\n setNotice(null);\n },\n (error: unknown) => setNotice(failureMessage(error))\n )\n .finally(() => setIsFinishing(false));\n }, [apiUrl, publicKey]);\n const signOut = (reason: string | null) => {\n setAccess(null);\n setNotice(reason);\n forgetTeamAccess({ apiUrl, publicKey }).catch((error: unknown) =>\n reportFailure(\"Signing out of Reflet did not finish\", error)\n );\n };\n\n if (access && transport) {\n return (\n <TeamTools\n placement={placement}\n team={{\n onSignOut: signOut,\n organizationName: access.organizationName,\n transport,\n }}\n />\n );\n }\n return (\n <SignedOutAnchor placement={placement}>\n <TeamSignInCard\n continuesInBrowser={false}\n isFinishing={isFinishing}\n notice={notice}\n onSignIn={async () =>\n window.location.assign(await prepareTeamSignIn(connection))\n }\n />\n </SignedOutAnchor>\n );\n}\n\ntype BridgeAccountState =\n | { kind: \"loading\" }\n | { account: TeamToolsAccount | null; kind: \"loaded\" };\n\n/** A desktop app signs in through the system browser; its privileged process keeps the credential. */\nfunction BridgeTeamAccount({\n bridge,\n placement,\n}: {\n bridge: TeamToolsBridge;\n placement: TeamPlacement;\n}) {\n const [accountState, setAccountState] = useState<BridgeAccountState>({\n kind: \"loading\",\n });\n const [notice, setNotice] = useState<string | null>(null);\n const transport = useMemo(() => bridgeTransport(bridge), [bridge]);\n useEffect(() => {\n let isCurrent = true;\n bridge.account().then(\n (account) => isCurrent && setAccountState({ account, kind: \"loaded\" }),\n (error: unknown) => {\n if (isCurrent) {\n setAccountState({ account: null, kind: \"loaded\" });\n setNotice(failureMessage(error));\n }\n }\n );\n return () => {\n isCurrent = false;\n };\n }, [bridge]);\n const signIn = async () => {\n const account = await bridge.signIn();\n setAccountState({ account, kind: \"loaded\" });\n setNotice(null);\n };\n const signOut = (reason: string | null) => {\n setAccountState({ account: null, kind: \"loaded\" });\n setNotice(reason);\n bridge\n .signOut()\n .catch((error: unknown) =>\n reportFailure(\"Signing out of Reflet did not finish\", error)\n );\n };\n\n if (accountState.kind === \"loading\") {\n return null;\n }\n if (accountState.account) {\n return (\n <TeamTools\n placement={placement}\n team={{\n onSignOut: signOut,\n organizationName: accountState.account.organizationName,\n transport,\n }}\n />\n );\n }\n return (\n <SignedOutAnchor placement={placement}>\n <TeamSignInCard\n continuesInBrowser\n isFinishing={false}\n notice={notice}\n onSignIn={signIn}\n />\n </SignedOutAnchor>\n );\n}\n\nfunction placementFor(\n position: NonNullable<RefletFeedbackProps[\"position\"]>\n): TeamPlacement {\n return {\n edge: position.startsWith(\"top\") ? \"top\" : \"bottom\",\n side: position.endsWith(\"right\") ? \"right\" : \"left\",\n };\n}\n\nexport function TeamLayer(\n props: { position: NonNullable<RefletFeedbackProps[\"position\"]> } & (\n | { bridge: TeamToolsBridge }\n | { connection: TeamConnection }\n )\n) {\n const placement = placementFor(props.position);\n return (\n <div className=\"dt\">\n <style>{`${DEVTOOLS_STYLES}${SESSIONS_STYLES}`}</style>\n {\"bridge\" in props ? (\n <BridgeTeamAccount bridge={props.bridge} placement={placement} />\n ) : (\n <BrowserTeamAccount\n connection={props.connection}\n placement={placement}\n />\n )}\n <Notices />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ //#region src/sessions/team/team-tools-bridge.d.ts
2
+ interface TeamToolsAccount {
3
+ organizationName: string;
4
+ }
5
+ /** One Reflet API call; `path` is the API path, e.g. `/api/v1/sessions/list`. */
6
+ interface TeamToolsCall {
7
+ body?: string;
8
+ method: "GET" | "POST";
9
+ path: string;
10
+ query?: Record<string, string>;
11
+ }
12
+ interface TeamToolsReply {
13
+ body: string;
14
+ status: number;
15
+ }
16
+ /**
17
+ * Team tools for a desktop app whose pages have no web origin Reflet can bind a
18
+ * sign-in to, such as Electron's `file://`. The app's privileged process
19
+ * implements it with `createTeamToolsHost` from `reflet-sdk/desktop` and keeps
20
+ * the credential; the page only sees calls and replies.
21
+ */
22
+ interface TeamToolsBridge {
23
+ account: () => Promise<TeamToolsAccount | null>;
24
+ request: (call: TeamToolsCall) => Promise<TeamToolsReply>;
25
+ /** Opens Reflet sign-in in the system browser; resolves once the member approves. Calling it again reopens the same sign-in. */
26
+ signIn: () => Promise<TeamToolsAccount>;
27
+ signOut: () => Promise<void>;
28
+ }
29
+ //#endregion
30
+ export { TeamToolsReply as i, TeamToolsBridge as n, TeamToolsCall as r, TeamToolsAccount as t };
31
+ //# sourceMappingURL=team-tools-bridge-C51PpkUI.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-tools-bridge-C51PpkUI.d.ts","names":[],"sources":["../src/sessions/team/team-tools-bridge.ts"],"mappings":";UAAiB;EACf;;;UAIe;EACf;EACA;EACA;EACA,QAAQ;;UAGO;EACf;EACA;;;;;;;;UASe;EACf,eAAe,QAAQ;EACvB,UAAU,MAAM,kBAAkB,QAAQ;;EAE1C,cAAc,QAAQ;EACtB,eAAe"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflet-sdk",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Official Reflet SDK - Collect user feedback, feature requests, and bug reports from your app",
5
5
  "keywords": [
6
6
  "feedback",
@@ -58,6 +58,10 @@
58
58
  "./devtools/vite": {
59
59
  "types": "./dist/devtools-vite.d.ts",
60
60
  "import": "./dist/devtools-vite.js"
61
+ },
62
+ "./desktop": {
63
+ "types": "./dist/desktop.d.ts",
64
+ "import": "./dist/desktop.js"
61
65
  }
62
66
  },
63
67
  "main": "./dist/index.js",