orcheo-studio 0.23.1 → 0.24.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.
Files changed (73) hide show
  1. package/.bumpversion.cfg +1 -1
  2. package/dist/assets/{arc-CqnxfCHp.js → arc-DAv23sxp.js} +1 -1
  3. package/dist/assets/{architectureDiagram-VXUJARFQ-UGPIhA1j.js → architectureDiagram-VXUJARFQ-D_gUzQPQ.js} +1 -1
  4. package/dist/assets/{blockDiagram-VD42YOAC-Cn6wka40.js → blockDiagram-VD42YOAC-2_hx47qT.js} +1 -1
  5. package/dist/assets/{c4Diagram-YG6GDRKO-BvPRNdur.js → c4Diagram-YG6GDRKO-dehdDKxT.js} +1 -1
  6. package/dist/assets/channel-C3cH7p5A.js +1 -0
  7. package/dist/assets/{chunk-4BX2VUAB-F_o1AmTw.js → chunk-4BX2VUAB-CtLOuP9F.js} +1 -1
  8. package/dist/assets/{chunk-55IACEB6-Cl075es2.js → chunk-55IACEB6-CwinXV4-.js} +1 -1
  9. package/dist/assets/{chunk-B4BG7PRW-DcErQhy9.js → chunk-B4BG7PRW-BI7iPFRv.js} +1 -1
  10. package/dist/assets/{chunk-DI55MBZ5-CFxCndOp.js → chunk-DI55MBZ5-CNYNrYjW.js} +1 -1
  11. package/dist/assets/{chunk-FMBD7UC4-XOcF0fgy.js → chunk-FMBD7UC4-BFVTHaU-.js} +1 -1
  12. package/dist/assets/{chunk-QN33PNHL-CjJH_Bvj.js → chunk-QN33PNHL-xWy7sSv1.js} +1 -1
  13. package/dist/assets/{chunk-QZHKN3VN-nLb-Go8u.js → chunk-QZHKN3VN-Ds_R1QaP.js} +1 -1
  14. package/dist/assets/{chunk-TZMSLE5B-L07ovmT0.js → chunk-TZMSLE5B-D8g2OOzb.js} +1 -1
  15. package/dist/assets/classDiagram-2ON5EDUG-o6x5AEsV.js +1 -0
  16. package/dist/assets/classDiagram-v2-WZHVMYZB-o6x5AEsV.js +1 -0
  17. package/dist/assets/clone-CnJR7M_G.js +1 -0
  18. package/dist/assets/{cose-bilkent-S5V4N54A-B6IcYcMk.js → cose-bilkent-S5V4N54A-DnkMvL-f.js} +1 -1
  19. package/dist/assets/{dagre-6UL2VRFP-CN7ADhef.js → dagre-6UL2VRFP-dpKYJ1gN.js} +1 -1
  20. package/dist/assets/{diagram-PSM6KHXK-3chqLyzk.js → diagram-PSM6KHXK-BnFoR8YQ.js} +1 -1
  21. package/dist/assets/{diagram-QEK2KX5R-MrbKZWaG.js → diagram-QEK2KX5R-Brg1AGVu.js} +1 -1
  22. package/dist/assets/{diagram-S2PKOQOG-Dr7BijIx.js → diagram-S2PKOQOG-Dhmom7Q4.js} +1 -1
  23. package/dist/assets/{erDiagram-Q2GNP2WA-Ck-v9a6N.js → erDiagram-Q2GNP2WA-BTez8fDL.js} +1 -1
  24. package/dist/assets/{flowDiagram-NV44I4VS-D662eMD1.js → flowDiagram-NV44I4VS-CVXmtc0D.js} +1 -1
  25. package/dist/assets/{ganttDiagram-JELNMOA3-c2mXK47M.js → ganttDiagram-JELNMOA3-CHbw3AtF.js} +1 -1
  26. package/dist/assets/{gitGraphDiagram-V2S2FVAM-DUMhfhsS.js → gitGraphDiagram-V2S2FVAM-CaPZ1SUE.js} +1 -1
  27. package/dist/assets/{graph-DkWlmVun.js → graph-DjLsaDIL.js} +1 -1
  28. package/dist/assets/index-DSthAh3a.css +1 -0
  29. package/dist/assets/{index-kH6EkBsZ.js → index-DlptXU2u.js} +192 -187
  30. package/dist/assets/{infoDiagram-HS3SLOUP-OTL7qvkP.js → infoDiagram-HS3SLOUP-D_9DW9VU.js} +1 -1
  31. package/dist/assets/{isUndefined-BmZvH1PX.js → isUndefined-BkijP54r.js} +1 -1
  32. package/dist/assets/{journeyDiagram-XKPGCS4Q-BYzqE5D8.js → journeyDiagram-XKPGCS4Q-xxRWc5wn.js} +1 -1
  33. package/dist/assets/{kanban-definition-3W4ZIXB7-NtzQ4y9p.js → kanban-definition-3W4ZIXB7-BCI2NbpB.js} +1 -1
  34. package/dist/assets/{layout-CwhyLMkP.js → layout-M840MAqW.js} +1 -1
  35. package/dist/assets/{linear-Cirf2V1x.js → linear--_gMRK53.js} +1 -1
  36. package/dist/assets/{min-C81vLJYh.js → min-D06s3WKk.js} +1 -1
  37. package/dist/assets/{mindmap-definition-VGOIOE7T-TDnLQIKZ.js → mindmap-definition-VGOIOE7T-B1-ALKFj.js} +1 -1
  38. package/dist/assets/{pieDiagram-ADFJNKIX-d0-JhjNZ.js → pieDiagram-ADFJNKIX-76kjoNcf.js} +1 -1
  39. package/dist/assets/{quadrantDiagram-AYHSOK5B-wCoihp8a.js → quadrantDiagram-AYHSOK5B-O_d3GTQl.js} +1 -1
  40. package/dist/assets/{requirementDiagram-UZGBJVZJ-BqiCIjcI.js → requirementDiagram-UZGBJVZJ-CasSIIPr.js} +1 -1
  41. package/dist/assets/{sankeyDiagram-TZEHDZUN-BPH0Pd1D.js → sankeyDiagram-TZEHDZUN-CL_kG7jD.js} +1 -1
  42. package/dist/assets/{sequenceDiagram-WL72ISMW-B8FfJraH.js → sequenceDiagram-WL72ISMW-RYnztN0k.js} +1 -1
  43. package/dist/assets/{stateDiagram-FKZM4ZOC-ZQR3Q9_8.js → stateDiagram-FKZM4ZOC-NHFLaLiz.js} +1 -1
  44. package/dist/assets/stateDiagram-v2-4FDKWEC3-3y5AUrwb.js +1 -0
  45. package/dist/assets/{timeline-definition-IT6M3QCI-D-arYxX5.js → timeline-definition-IT6M3QCI-BH1cZoua.js} +1 -1
  46. package/dist/assets/{treemap-GDKQZRPO-Dw5BHp_B.js → treemap-GDKQZRPO-QAQDPzuO.js} +1 -1
  47. package/dist/assets/{xychartDiagram-PRI3JC2R-Dt1zH6V2.js → xychartDiagram-PRI3JC2R-C3M4ZzCP.js} +1 -1
  48. package/dist/index.html +2 -2
  49. package/package.json +2 -74
  50. package/src/features/auth/lib/auth-api.ts +21 -3
  51. package/src/features/workflow/data/templates/candidate-badges.ts +11 -1
  52. package/src/features/workflow/data/workflow-types.ts +3 -0
  53. package/src/features/workflow/lib/workflow-storage-api.ts +15 -0
  54. package/src/features/workflow/lib/workflow-storage-helpers.ts +46 -0
  55. package/src/features/workflow/lib/workflow-storage.ts +17 -0
  56. package/src/features/workflow/lib/workflow-storage.types.ts +16 -0
  57. package/src/features/workflow/pages/workflow/hooks/use-workflow-saver.ts +3 -2
  58. package/src/features/workflow/pages/workflow-gallery/candidate-updates.ts +107 -0
  59. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +29 -4
  60. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +29 -25
  61. package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +187 -6
  62. package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +17 -9
  63. package/src/features/workflow/pages/workflow-gallery.tsx +2 -0
  64. package/src/lib/auth-fetch.ts +81 -15
  65. package/vite.config.ts +0 -7
  66. package/dist/assets/channel-FjxqoZWk.js +0 -1
  67. package/dist/assets/classDiagram-2ON5EDUG-8JYNY9Ic.js +0 -1
  68. package/dist/assets/classDiagram-v2-WZHVMYZB-8JYNY9Ic.js +0 -1
  69. package/dist/assets/clone-NPVQC0Uc.js +0 -1
  70. package/dist/assets/index-BVY_PYJU.css +0 -1
  71. package/dist/assets/stateDiagram-v2-4FDKWEC3-uDYn1wPF.js +0 -1
  72. package/src/features/workflow/components/layouts/workflow-page-layout.tsx +0 -69
  73. package/src/features/workflow/data/templates/template-owner.ts +0 -7
@@ -1,6 +1,14 @@
1
1
  import { useRef, useState } from "react";
2
2
  import type { KeyboardEvent, MouseEvent, SyntheticEvent } from "react";
3
- import { MoreHorizontal, Send, Star, UserMinus, UserPlus } from "lucide-react";
3
+ import {
4
+ Loader2,
5
+ MoreHorizontal,
6
+ RefreshCcw,
7
+ Send,
8
+ Star,
9
+ UserMinus,
10
+ UserPlus,
11
+ } from "lucide-react";
4
12
  import { toast } from "@/hooks/use-toast";
5
13
  import { Button } from "@/design-system/ui/button";
6
14
  import {
@@ -9,6 +17,14 @@ import {
9
17
  CardFooter,
10
18
  CardHeader,
11
19
  } from "@/design-system/ui/card";
20
+ import {
21
+ Dialog,
22
+ DialogContent,
23
+ DialogDescription,
24
+ DialogFooter,
25
+ DialogHeader,
26
+ DialogTitle,
27
+ } from "@/design-system/ui/dialog";
12
28
  import {
13
29
  DropdownMenu,
14
30
  DropdownMenuContent,
@@ -16,15 +32,23 @@ import {
16
32
  DropdownMenuSeparator,
17
33
  DropdownMenuTrigger,
18
34
  } from "@/design-system/ui/dropdown-menu";
35
+ import {
36
+ Tooltip,
37
+ TooltipContent,
38
+ TooltipProvider,
39
+ TooltipTrigger,
40
+ } from "@/design-system/ui/tooltip";
19
41
  import { cn } from "@/lib/utils";
20
42
  import { resolveAvatarUrl } from "@/assets/avatars";
21
43
  import { ConfirmDeleteWorkflowDialog } from "@features/workflow/components/dialogs/confirm-delete-workflow-dialog";
22
44
  import {
45
+ getCandidateBadgeByCandidateId,
23
46
  getCandidateBadgeByHandle,
24
47
  getCandidateBadgeDefinition,
25
48
  } from "@features/workflow/data/templates/candidate-badges";
26
49
  import { type Workflow } from "@features/workflow/data/workflow-data";
27
50
  import { getWorkflowRouteRef } from "@features/workflow/lib/workflow-storage-helpers";
51
+ import { getCandidateUpdateAvailability } from "./candidate-updates";
28
52
  import { WORKFLOW_GALLERY_CARD_ASPECT_CLASSNAME } from "./workflow-card-size";
29
53
 
30
54
  const getWorkflowTemplateAvatar = (workflow: Workflow): string | undefined => {
@@ -49,6 +73,10 @@ interface WorkflowCardProps {
49
73
  workflowId: string,
50
74
  workflowName: string,
51
75
  ) => Promise<void> | void;
76
+ onUpdateCandidateWorkflow: (
77
+ workflowId: string,
78
+ candidateId: string,
79
+ ) => Promise<void> | void;
52
80
  }
53
81
 
54
82
  export const WorkflowCard = ({
@@ -60,6 +88,7 @@ export const WorkflowCard = ({
60
88
  onUseTemplate,
61
89
  onExportWorkflow,
62
90
  onDeleteWorkflow,
91
+ onUpdateCandidateWorkflow,
63
92
  }: WorkflowCardProps) => {
64
93
  const workflowRouteRef = getWorkflowRouteRef(workflow);
65
94
  const isClickable = !isTemplate;
@@ -70,14 +99,25 @@ export const WorkflowCard = ({
70
99
  const templateId = !isTemplate
71
100
  ? workflow.versions?.at(-1)?.templateId
72
101
  : undefined;
102
+ const candidateSource = !isTemplate
103
+ ? workflow.versions?.at(-1)?.candidateSource
104
+ : undefined;
73
105
  const templateBadge = !isTemplate
74
- ? (templateId
75
- ? getCandidateBadgeDefinition(templateId)
76
- : workflow.handle
77
- ? getCandidateBadgeByHandle(workflow.handle)
78
- : undefined)
106
+ ? ((candidateSource?.candidateId
107
+ ? getCandidateBadgeByCandidateId(candidateSource.candidateId)
108
+ : undefined) ??
109
+ (candidateSource?.candidateHandle
110
+ ? getCandidateBadgeByHandle(candidateSource.candidateHandle)
111
+ : undefined) ??
112
+ (templateId ? getCandidateBadgeDefinition(templateId) : undefined) ??
113
+ (workflow.handle
114
+ ? getCandidateBadgeByHandle(workflow.handle)
115
+ : undefined))
79
116
  : undefined;
80
117
  const displayBadge = candidateBadge ?? templateBadge;
118
+ const candidateUpdate = !isTemplate
119
+ ? getCandidateUpdateAvailability(workflow, templateBadge)
120
+ : undefined;
81
121
  const headerLabel = isTemplate ? "Candidate" : workspaceLabel;
82
122
  const rawHandle = workflow.handle ?? workflow.id;
83
123
  const workflowSlug = isTemplate
@@ -85,6 +125,9 @@ export const WorkflowCard = ({
85
125
  : rawHandle;
86
126
  const displayHandle =
87
127
  !isTemplate && teamSlug ? `${teamSlug}/${workflowSlug}` : workflowSlug;
128
+ const versionToDisplay = isTemplate
129
+ ? (candidateBadge?.version ?? null)
130
+ : (workflow.versions?.at(-1)?.candidateSource?.candidateVersion ?? null);
88
131
  const avatarUrl = resolveAvatarUrl(
89
132
  workflow.avatarEmoji ?? getWorkflowTemplateAvatar(workflow),
90
133
  workflow.id,
@@ -94,6 +137,8 @@ export const WorkflowCard = ({
94
137
  const [isMenuOpen, setIsMenuOpen] = useState(false);
95
138
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
96
139
  const [isDeletePending, setIsDeletePending] = useState(false);
140
+ const [isUpdateDialogOpen, setIsUpdateDialogOpen] = useState(false);
141
+ const [isUpdatePending, setIsUpdatePending] = useState(false);
97
142
 
98
143
  const suppressCardOpen = () => {
99
144
  suppressCardOpenRef.current = true;
@@ -149,6 +194,20 @@ export const WorkflowCard = ({
149
194
  }
150
195
  };
151
196
 
197
+ const handleConfirmUpdate = async () => {
198
+ if (!candidateUpdate) {
199
+ return;
200
+ }
201
+
202
+ setIsUpdatePending(true);
203
+ try {
204
+ await onUpdateCandidateWorkflow(workflow.id, candidateUpdate.candidateId);
205
+ setIsUpdateDialogOpen(false);
206
+ } finally {
207
+ setIsUpdatePending(false);
208
+ }
209
+ };
210
+
152
211
  return (
153
212
  <>
154
213
  <Card
@@ -266,6 +325,45 @@ export const WorkflowCard = ({
266
325
  />
267
326
  </div>
268
327
 
328
+ {candidateUpdate ? (
329
+ <TooltipProvider delayDuration={200}>
330
+ <Tooltip>
331
+ <TooltipTrigger asChild>
332
+ <Button
333
+ variant="outline"
334
+ size="icon"
335
+ className="absolute right-10 top-0 h-8 w-8 border-emerald-500/40 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-300 dark:hover:bg-emerald-900/60"
336
+ aria-label={`Update ${workflow.name} to candidate version ${candidateUpdate.latestVersion}`}
337
+ data-card-action="true"
338
+ onClick={(event) => {
339
+ stopPropagation(event);
340
+ setIsUpdateDialogOpen(true);
341
+ }}
342
+ onPointerDown={stopPropagation}
343
+ >
344
+ <RefreshCcw className="h-3.5 w-3.5" />
345
+ </Button>
346
+ </TooltipTrigger>
347
+ <TooltipContent side="top" className="max-w-[280px]">
348
+ <div className="space-y-2 text-xs">
349
+ <div className="font-semibold text-foreground">
350
+ Candidate version {candidateUpdate.currentVersion} to{" "}
351
+ {candidateUpdate.latestVersion}
352
+ </div>
353
+ {candidateUpdate.latestSummary ? (
354
+ <p>{candidateUpdate.latestSummary}</p>
355
+ ) : null}
356
+ {candidateUpdate.firstMigration ? (
357
+ <p className="text-amber-600 dark:text-amber-300">
358
+ {candidateUpdate.firstMigration}
359
+ </p>
360
+ ) : null}
361
+ </div>
362
+ </TooltipContent>
363
+ </Tooltip>
364
+ </TooltipProvider>
365
+ ) : null}
366
+
269
367
  {!isTemplate ? (
270
368
  <Button
271
369
  variant="ghost"
@@ -295,6 +393,12 @@ export const WorkflowCard = ({
295
393
  @{displayHandle}
296
394
  </div>
297
395
 
396
+ {versionToDisplay ? (
397
+ <div className="mt-0.5 shrink-0 font-mono text-[9px] tracking-[0.04em] text-muted-foreground/60">
398
+ v{versionToDisplay}
399
+ </div>
400
+ ) : null}
401
+
298
402
  <div className="mt-3 flex min-h-0 w-full flex-1 flex-col items-center justify-start">
299
403
  <div className="h-px w-8 rounded-full bg-border/80" />
300
404
 
@@ -343,6 +447,83 @@ export const WorkflowCard = ({
343
447
  onConfirm={handleConfirmDelete}
344
448
  />
345
449
  ) : null}
450
+
451
+ {candidateUpdate ? (
452
+ <Dialog
453
+ open={isUpdateDialogOpen}
454
+ onOpenChange={(open) => {
455
+ if (!isUpdatePending) {
456
+ setIsUpdateDialogOpen(open);
457
+ }
458
+ }}
459
+ >
460
+ <DialogContent className="max-w-xl">
461
+ <DialogHeader>
462
+ <DialogTitle>Update candidate version</DialogTitle>
463
+ <DialogDescription>
464
+ Review release notes before updating {workflow.name} from{" "}
465
+ {candidateUpdate.currentVersion} to{" "}
466
+ {candidateUpdate.latestVersion}.
467
+ </DialogDescription>
468
+ </DialogHeader>
469
+
470
+ {candidateUpdate.isMajor ? (
471
+ <div className="rounded-md border border-amber-500/50 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:bg-amber-950/30 dark:text-amber-200">
472
+ This is a major candidate update. Review migration notes before
473
+ applying it to this colleague.
474
+ </div>
475
+ ) : null}
476
+
477
+ <div className="max-h-[320px] space-y-3 overflow-y-auto pr-1">
478
+ {candidateUpdate.crossedUpdates.length > 0 ? (
479
+ candidateUpdate.crossedUpdates.map((note) => (
480
+ <div
481
+ key={note.version}
482
+ className="rounded-md border border-border/70 p-3 text-sm"
483
+ >
484
+ <div className="font-semibold">
485
+ Candidate version {note.version}
486
+ </div>
487
+ <p className="mt-1 text-muted-foreground">{note.summary}</p>
488
+ {note.migration ? (
489
+ <p className="mt-2 text-amber-700 dark:text-amber-300">
490
+ {note.migration}
491
+ </p>
492
+ ) : null}
493
+ </div>
494
+ ))
495
+ ) : (
496
+ <p className="text-sm text-muted-foreground">
497
+ No release notes were provided for the versions being crossed.
498
+ </p>
499
+ )}
500
+ </div>
501
+
502
+ <DialogFooter>
503
+ <Button
504
+ type="button"
505
+ variant="outline"
506
+ onClick={() => setIsUpdateDialogOpen(false)}
507
+ disabled={isUpdatePending}
508
+ >
509
+ Cancel
510
+ </Button>
511
+ <Button
512
+ type="button"
513
+ onClick={handleConfirmUpdate}
514
+ disabled={isUpdatePending}
515
+ >
516
+ {isUpdatePending ? (
517
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
518
+ ) : (
519
+ <RefreshCcw className="mr-2 h-4 w-4" />
520
+ )}
521
+ Update
522
+ </Button>
523
+ </DialogFooter>
524
+ </DialogContent>
525
+ </Dialog>
526
+ ) : null}
346
527
  </>
347
528
  );
348
529
  };
@@ -39,6 +39,10 @@ interface WorkflowGalleryTabsProps {
39
39
  workflowId: string,
40
40
  workflowName: string,
41
41
  ) => Promise<void> | void;
42
+ onUpdateCandidateWorkflow: (
43
+ workflowId: string,
44
+ candidateId: string,
45
+ ) => Promise<void> | void;
42
46
  onDeleteTeam?: (teamId: string) => void;
43
47
  }
44
48
 
@@ -58,6 +62,7 @@ export const WorkflowGalleryTabs = ({
58
62
  onUseTemplate,
59
63
  onExportWorkflow,
60
64
  onDeleteWorkflow,
65
+ onUpdateCandidateWorkflow,
61
66
  onDeleteTeam,
62
67
  }: WorkflowGalleryTabsProps) => {
63
68
  const [isUploadOpen, setIsUploadOpen] = useState(false);
@@ -80,6 +85,7 @@ export const WorkflowGalleryTabs = ({
80
85
  onUseTemplate={onUseTemplate}
81
86
  onExportWorkflow={onExportWorkflow}
82
87
  onDeleteWorkflow={onDeleteWorkflow}
88
+ onUpdateCandidateWorkflow={onUpdateCandidateWorkflow}
83
89
  />
84
90
  ))}
85
91
  </div>
@@ -114,15 +120,17 @@ export const WorkflowGalleryTabs = ({
114
120
  return (
115
121
  <div className="flex flex-col gap-1 pb-6">
116
122
  {independents.length > 0 ? renderGrid(independents) : null}
117
- {[...byGroup.entries()].map(([slug, items]) => (
118
- <TeamSection
119
- key={slug}
120
- name={formatCandidateGroupName(slug)}
121
- count={items.length}
122
- >
123
- {renderGrid(items)}
124
- </TeamSection>
125
- ))}
123
+ {[...byGroup.entries()]
124
+ .sort(([left], [right]) => left.localeCompare(right))
125
+ .map(([slug, items]) => (
126
+ <TeamSection
127
+ key={slug}
128
+ name={formatCandidateGroupName(slug)}
129
+ count={items.length}
130
+ >
131
+ {renderGrid(items)}
132
+ </TeamSection>
133
+ ))}
126
134
  </div>
127
135
  );
128
136
  };
@@ -62,6 +62,7 @@ export default function WorkflowGallery() {
62
62
  handleImportStarterPack,
63
63
  handleExportWorkflow,
64
64
  handleDeleteWorkflow,
65
+ handleUpdateCandidateWorkflow,
65
66
  handleOpenWorkflow,
66
67
  onboardTarget,
67
68
  confirmOnboardTeam,
@@ -102,6 +103,7 @@ export default function WorkflowGallery() {
102
103
  onUseTemplate={handleUseTemplate}
103
104
  onExportWorkflow={handleExportWorkflow}
104
105
  onDeleteWorkflow={handleDeleteWorkflow}
106
+ onUpdateCandidateWorkflow={handleUpdateCandidateWorkflow}
105
107
  onDeleteTeam={handleDeleteTeam}
106
108
  />
107
109
  </div>
@@ -1,35 +1,101 @@
1
1
  import {
2
2
  getAccessToken,
3
+ getAuthTokens,
3
4
  getDevAuthSessionHeaderValue,
4
5
  } from "@features/auth/lib/auth-session";
6
+ import { refreshSession } from "@features/auth/lib/auth-api";
5
7
  import { getWorkspaceSelectionHeaders } from "./workspace-session";
6
8
 
7
- export const authFetch = async (
8
- input: RequestInfo | URL,
9
- init: RequestInit = {},
10
- options: { includeWorkspaceHeaders?: boolean } = {},
11
- ): Promise<Response> => {
12
- const headers = new Headers(init.headers ?? {});
9
+ const buildAuthHeaders = (
10
+ headers: Headers,
11
+ shouldAttachAuth: boolean,
12
+ ): string | null => {
13
13
  const token = getAccessToken();
14
- if (token && !headers.has("Authorization")) {
14
+ if (token && shouldAttachAuth) {
15
15
  headers.set("Authorization", `Bearer ${token}`);
16
16
  }
17
17
  const devSession = getDevAuthSessionHeaderValue();
18
18
  if (devSession && !headers.has("X-Orcheo-Dev-Session")) {
19
19
  headers.set("X-Orcheo-Dev-Session", devSession);
20
20
  }
21
- if (options.includeWorkspaceHeaders ?? true) {
22
- for (const [name, value] of Object.entries(
23
- getWorkspaceSelectionHeaders(),
24
- )) {
25
- if (!headers.has(name)) {
26
- headers.set(name, value);
27
- }
21
+ return shouldAttachAuth ? token : null;
22
+ };
23
+
24
+ const attachWorkspaceHeaders = (headers: Headers): void => {
25
+ for (const [name, value] of Object.entries(getWorkspaceSelectionHeaders())) {
26
+ if (!headers.has(name)) {
27
+ headers.set(name, value);
28
28
  }
29
29
  }
30
- return globalThis.fetch(input, {
30
+ };
31
+
32
+ const fetchWithHeaders = (
33
+ input: RequestInfo | URL,
34
+ init: RequestInit,
35
+ headers: Headers,
36
+ ): Promise<Response> =>
37
+ globalThis.fetch(input, {
31
38
  ...init,
32
39
  credentials: init.credentials ?? "include",
33
40
  headers,
34
41
  });
42
+
43
+ const buildRequestHeaders = (
44
+ init: RequestInit,
45
+ shouldAttachAuth: boolean,
46
+ includeWorkspaceHeaders: boolean,
47
+ headers = new Headers(init.headers ?? {}),
48
+ ): { headers: Headers; accessToken: string | null } => {
49
+ const accessToken = buildAuthHeaders(headers, shouldAttachAuth);
50
+ if (includeWorkspaceHeaders) {
51
+ attachWorkspaceHeaders(headers);
52
+ }
53
+ return { headers, accessToken };
54
+ };
55
+
56
+ export const authFetch = async (
57
+ input: RequestInfo | URL,
58
+ init: RequestInit = {},
59
+ options: { includeWorkspaceHeaders?: boolean } = {},
60
+ ): Promise<Response> => {
61
+ const originalHeaders = new Headers(init.headers ?? {});
62
+ const shouldAttachAuth = !originalHeaders.has("Authorization");
63
+ // getAccessToken includes the shared 60-second expiry skew, so nearly expired
64
+ // tokens are treated as missing and refreshed before protected requests.
65
+ if (shouldAttachAuth && !getAccessToken() && getAuthTokens()?.refreshToken) {
66
+ await refreshSession();
67
+ }
68
+
69
+ const includeWorkspaceHeaders = options.includeWorkspaceHeaders ?? true;
70
+ const { headers, accessToken: requestAccessToken } = buildRequestHeaders(
71
+ init,
72
+ shouldAttachAuth,
73
+ includeWorkspaceHeaders,
74
+ originalHeaders,
75
+ );
76
+ const response = await fetchWithHeaders(input, init, headers);
77
+ if (response.status !== 401 || !shouldAttachAuth) {
78
+ return response;
79
+ }
80
+
81
+ const currentAccessToken = getAccessToken();
82
+ if (currentAccessToken && currentAccessToken !== requestAccessToken) {
83
+ const { headers: retryHeaders } = buildRequestHeaders(
84
+ init,
85
+ shouldAttachAuth,
86
+ includeWorkspaceHeaders,
87
+ );
88
+ return fetchWithHeaders(input, init, retryHeaders);
89
+ }
90
+
91
+ if (!getAuthTokens()?.refreshToken || !(await refreshSession())) {
92
+ return response;
93
+ }
94
+
95
+ const { headers: retryHeaders } = buildRequestHeaders(
96
+ init,
97
+ shouldAttachAuth,
98
+ includeWorkspaceHeaders,
99
+ );
100
+ return fetchWithHeaders(input, init, retryHeaders);
35
101
  };
package/vite.config.ts CHANGED
@@ -86,14 +86,7 @@ export default defineConfig({
86
86
  'lodash/defaultsDeep',
87
87
  'lodash/flatten',
88
88
  // Other CJS packages
89
- 'react-split',
90
- 'dom-helpers',
91
89
  'dayjs',
92
- 'invariant',
93
- 'copy-to-clipboard',
94
- 'moment',
95
- 'moment-timezone',
96
- 'papaparse',
97
90
  // zustand and related
98
91
  'zustand',
99
92
  'zustand/traditional',
@@ -1 +0,0 @@
1
- import{ap as o,aq as n}from"./index-kH6EkBsZ.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-DcErQhy9.js";import{_ as i}from"./index-kH6EkBsZ.js";import"./chunk-FMBD7UC4-XOcF0fgy.js";import"./chunk-55IACEB6-Cl075es2.js";import"./chunk-QN33PNHL-CjJH_Bvj.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-DcErQhy9.js";import{_ as i}from"./index-kH6EkBsZ.js";import"./chunk-FMBD7UC4-XOcF0fgy.js";import"./chunk-55IACEB6-Cl075es2.js";import"./chunk-QN33PNHL-CjJH_Bvj.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
@@ -1 +0,0 @@
1
- import{ay as r}from"./index-kH6EkBsZ.js";var a=4;function n(o){return r(o,a)}export{n as c};