@timeax/service-builder 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -617,11 +617,81 @@ function normalizeRole(value) {
617
617
  return value === "utility" ? "utility" : "base";
618
618
  }
619
619
 
620
- // src/workspace/authorization.ts
621
- function hasPermission(permissions2, key) {
622
- if (!permissions2) return false;
623
- return permissions2[key] === true;
620
+ // src/workspace/permissions.ts
621
+ var BUILDER_PERMISSIONS = {
622
+ WORKSPACE_READ: "workspace.read",
623
+ WORKSPACE_WRITE: "workspace.write",
624
+ BRANCHES_READ: "branches.read",
625
+ BRANCHES_CREATE: "branches.create",
626
+ BRANCHES_WRITE: "branches.write",
627
+ BRANCHES_DELETE: "branches.delete",
628
+ BRANCHES_MERGE: "branches.merge",
629
+ BRANCHES_PUBLISH: "branches.publish",
630
+ BRANCHES_MANAGE: "branches.manage",
631
+ TEMPLATES_READ: "templates.read",
632
+ TEMPLATES_WRITE: "templates.write",
633
+ TEMPLATES_DELETE: "templates.delete",
634
+ TEMPLATES_MANAGE: "templates.manage",
635
+ COMMENTS_READ: "comments.read",
636
+ COMMENTS_WRITE: "comments.write",
637
+ COMMENTS_MODERATE: "comments.moderate",
638
+ POLICIES_READ: "policies.read",
639
+ POLICIES_WRITE: "policies.write",
640
+ POLICIES_MANAGE: "policies.manage",
641
+ PARTICIPANTS_READ: "participants.read",
642
+ PARTICIPANTS_WRITE: "participants.write",
643
+ PARTICIPANTS_MANAGE: "participants.manage",
644
+ SERVICES_READ: "services.read"
645
+ };
646
+ var BUILDER_PERMISSION_KEYS = new Set(Object.values(BUILDER_PERMISSIONS));
647
+ function toBuilderPermissionsMap(permissions2) {
648
+ if (!permissions2) return {};
649
+ const result = {};
650
+ for (const [key, value] of Object.entries(permissions2)) {
651
+ if (!BUILDER_PERMISSION_KEYS.has(key)) continue;
652
+ result[key] = value === true;
653
+ }
654
+ return result;
655
+ }
656
+ function can(map, permission) {
657
+ if (!map) return false;
658
+ return map[permission] === true;
659
+ }
660
+ function canReadWorkspace(map) {
661
+ return can(map, BUILDER_PERMISSIONS.WORKSPACE_READ);
662
+ }
663
+ function canEditBranch(map) {
664
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_WRITE);
665
+ }
666
+ function canCreateBranch(map) {
667
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_CREATE);
668
+ }
669
+ function canMergeBranch(map) {
670
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_MERGE);
671
+ }
672
+ function canPublishBranch(map) {
673
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_PUBLISH);
624
674
  }
675
+ function canDeleteBranch(map) {
676
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_DELETE);
677
+ }
678
+ function canManageBranches(map) {
679
+ return can(map, BUILDER_PERMISSIONS.BRANCHES_MANAGE);
680
+ }
681
+ function canWriteTemplates(map) {
682
+ return can(map, BUILDER_PERMISSIONS.TEMPLATES_WRITE);
683
+ }
684
+ function canManageTemplates(map) {
685
+ return can(map, BUILDER_PERMISSIONS.TEMPLATES_MANAGE);
686
+ }
687
+ function canReadComments(map) {
688
+ return can(map, BUILDER_PERMISSIONS.COMMENTS_READ);
689
+ }
690
+ function canWriteComments(map) {
691
+ return can(map, BUILDER_PERMISSIONS.COMMENTS_WRITE);
692
+ }
693
+
694
+ // src/workspace/authorization.ts
625
695
  function resolveActorAuthorId(actor, authors2) {
626
696
  if (!actor) return "unknown";
627
697
  const fromMeta = actor?.meta && typeof actor.meta.authorId === "string" ? actor.meta.authorId : void 0;
@@ -633,40 +703,52 @@ function deriveWorkspaceAuthorization(args) {
633
703
  const { actor, permissions: permissions2 = null, participants = null, authors: authors2 = null } = args;
634
704
  const authorId = resolveActorAuthorId(actor, authors2);
635
705
  const participant = (participants ?? []).find((row) => row.authorId === authorId) ?? null;
636
- const canReadWorkspace = hasPermission(permissions2, "workspace.read");
637
- const canEditBranchContent = hasPermission(permissions2, "workspace.write") && participant?.canWrite === true;
638
- const canCreateBranch = canEditBranchContent && hasPermission(permissions2, "branches.create");
639
- const canMergeBranches = canEditBranchContent && hasPermission(permissions2, "branches.merge");
640
- const canManageBranches = canCreateBranch || canMergeBranches || canEditBranchContent;
641
- const canWriteTemplates = canEditBranchContent && hasPermission(permissions2, "templates.write");
642
- const canCommentRead = canReadWorkspace && hasPermission(permissions2, "comments.read");
643
- const canCommentWrite = canReadWorkspace && hasPermission(permissions2, "comments.write");
706
+ const permissionMap = toBuilderPermissionsMap(permissions2);
707
+ const readWorkspace = canReadWorkspace(permissionMap);
708
+ const editBranch = canEditBranch(permissionMap);
709
+ const createBranch = canCreateBranch(permissionMap);
710
+ const mergeBranches = canMergeBranch(permissionMap);
711
+ const publishBranches = canPublishBranch(permissionMap);
712
+ const deleteBranches = canDeleteBranch(permissionMap);
713
+ const manageBranches = canManageBranches(permissionMap);
714
+ const writeTemplates = canWriteTemplates(permissionMap);
715
+ const manageTemplates = canManageTemplates(permissionMap);
716
+ const commentRead = canReadComments(permissionMap);
717
+ const commentWrite = canWriteComments(permissionMap);
644
718
  return {
645
719
  authorId,
646
720
  participant,
647
- canReadWorkspace,
648
- canEditBranchContent,
649
- canCreateBranch,
650
- canMergeBranches,
651
- canManageBranches,
652
- canWriteTemplates,
653
- canCommentRead,
654
- canCommentWrite
721
+ permissions: permissionMap,
722
+ canReadWorkspace: readWorkspace,
723
+ canEditBranchContent: editBranch,
724
+ canCreateBranch: createBranch,
725
+ canMergeBranches: mergeBranches,
726
+ canPublishBranches: publishBranches,
727
+ canDeleteBranches: deleteBranches,
728
+ canManageBranches: manageBranches,
729
+ canWriteTemplates: writeTemplates,
730
+ canManageTemplates: manageTemplates,
731
+ canCommentRead: readWorkspace && commentRead,
732
+ canCommentWrite: readWorkspace && commentWrite
655
733
  };
656
734
  }
657
735
  function getAuthorizationDecision(auth, action) {
658
736
  switch (action) {
659
737
  case "branch-content-edit":
660
738
  case "snapshot-write":
661
- return auth.canEditBranchContent ? { ok: true } : { ok: false, reason: "Editing is disabled. You need workspace write access and branch write access." };
739
+ return auth.canEditBranchContent ? { ok: true } : { ok: false, reason: "Editing is disabled. You need branches.write." };
662
740
  case "template-write":
663
- return auth.canWriteTemplates ? { ok: true } : { ok: false, reason: "Template editing requires templates.write and branch edit access." };
741
+ return auth.canWriteTemplates ? { ok: true } : { ok: false, reason: "Template editing requires templates.write." };
664
742
  case "branch-create":
665
- return auth.canCreateBranch ? { ok: true } : { ok: false, reason: "Branch creation requires branches.create and branch edit access." };
743
+ return auth.canCreateBranch ? { ok: true } : { ok: false, reason: "Branch creation requires branches.create." };
666
744
  case "branch-merge":
667
- return auth.canMergeBranches ? { ok: true } : { ok: false, reason: "Branch merge requires branches.merge and branch edit access." };
745
+ return auth.canMergeBranches ? { ok: true } : { ok: false, reason: "Branch merge requires branches.merge." };
746
+ case "branch-publish":
747
+ return auth.canPublishBranches ? { ok: true } : { ok: false, reason: "Branch publish requires branches.publish." };
748
+ case "branch-delete":
749
+ return auth.canDeleteBranches ? { ok: true } : { ok: false, reason: "Branch deletion requires branches.delete." };
668
750
  case "branch-manage":
669
- return auth.canManageBranches ? { ok: true } : { ok: false, reason: "Branch management requires branch edit access." };
751
+ return auth.canManageBranches ? { ok: true } : { ok: false, reason: "Branch management requires branches.manage." };
670
752
  case "comment-read":
671
753
  return auth.canCommentRead ? { ok: true } : { ok: false, reason: "Comment read access is disabled for this actor." };
672
754
  case "comment-write":
@@ -3404,6 +3486,7 @@ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3404
3486
  function AppToolbar({
3405
3487
  bootActionReason,
3406
3488
  editDisabledReason,
3489
+ publishDisabledReason,
3407
3490
  commentDisabledReason,
3408
3491
  errors,
3409
3492
  bottomPanelOpen,
@@ -3454,7 +3537,7 @@ function AppToolbar({
3454
3537
  "aria-label": "Publish commit",
3455
3538
  onClick: onPublish,
3456
3539
  disabled: pendingAction === "publish" || !canPublish,
3457
- title: (bootActionReason && !canPublish ? bootActionReason : void 0) ?? (!canPublish ? editDisabledReason : void 0) ?? "Publish commit",
3540
+ title: (bootActionReason && !canPublish ? bootActionReason : void 0) ?? (!canPublish ? publishDisabledReason : void 0) ?? "Publish commit",
3458
3541
  children: /* @__PURE__ */ jsx13(LuGitCommitHorizontal, {})
3459
3542
  }
3460
3543
  )
@@ -6067,7 +6150,9 @@ function CanvasPanel({
6067
6150
  );
6068
6151
  const canEditBranchContent = auth.canEditBranchContent;
6069
6152
  const canCommentWrite = auth.canCommentWrite;
6153
+ const canPublishBranch2 = auth.canPublishBranches;
6070
6154
  const editDecision = getAuthorizationDecision(auth, "branch-content-edit");
6155
+ const publishDecision = getAuthorizationDecision(auth, "branch-publish");
6071
6156
  const commentWriteDecision = getAuthorizationDecision(auth, "comment-write");
6072
6157
  const emitPermissionDenied = useCallback7(
6073
6158
  (reason, action, meta) => {
@@ -6217,7 +6302,7 @@ function CanvasPanel({
6217
6302
  }, [canvas.api, ws.snapshot]);
6218
6303
  const hasChanges = currentSnapshotHash !== persistedBaselineHash;
6219
6304
  const canSave = hasChanges && canEditBranchContent;
6220
- const canPublish = !!ws.snapshot.draft && !hasChanges && canEditBranchContent;
6305
+ const canPublish = !!ws.snapshot.draft && !hasChanges && canPublishBranch2;
6221
6306
  const importProps = async (nextProps) => {
6222
6307
  if (!canEditBranchContent) {
6223
6308
  emitPermissionDenied(editDecision.reason ?? "Editing is disabled for this actor.", "import-props");
@@ -6492,6 +6577,7 @@ function CanvasPanel({
6492
6577
  {
6493
6578
  bootActionReason,
6494
6579
  editDisabledReason: !canEditBranchContent ? editDecision.reason ?? "Editing is disabled for this actor." : void 0,
6580
+ publishDisabledReason: !canPublishBranch2 ? publishDecision.reason ?? "Publishing is disabled for this actor." : void 0,
6495
6581
  commentDisabledReason: !canCommentWrite ? commentWriteDecision.reason ?? "Comment write access is disabled for this actor." : void 0,
6496
6582
  errors,
6497
6583
  showErrorBadges,
@@ -6538,8 +6624,8 @@ function CanvasPanel({
6538
6624
  }
6539
6625
  },
6540
6626
  onPublish: async () => {
6541
- if (!canEditBranchContent) {
6542
- emitPermissionDenied(editDecision.reason ?? "Editing is disabled for this actor.", "snapshot-publish");
6627
+ if (!canPublishBranch2) {
6628
+ emitPermissionDenied(publishDecision.reason ?? "Publishing is disabled for this actor.", "snapshot-publish");
6543
6629
  return;
6544
6630
  }
6545
6631
  if (!canPublish) return;
@@ -7215,15 +7301,6 @@ import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
7215
7301
  function normalizeKey(input) {
7216
7302
  return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7217
7303
  }
7218
- function getActorAuthorId(ws) {
7219
- const actorMetaAuthorId = ws.actor?.meta && typeof ws.actor.meta.authorId === "string" ? ws.actor.meta.authorId : void 0;
7220
- if (actorMetaAuthorId) return actorMetaAuthorId;
7221
- if ((ws.authors.data ?? []).some((author) => author.id === ws.actor.id)) return ws.actor.id;
7222
- return ws.actor.id;
7223
- }
7224
- function resolveBranchParticipant(authorId, participants) {
7225
- return (participants ?? []).find((participant) => participant.authorId === authorId);
7226
- }
7227
7304
  function ActionIconButton({
7228
7305
  icon,
7229
7306
  label,
@@ -7262,25 +7339,34 @@ var Drafts = ({ trigger }) => {
7262
7339
  const [createName, setCreateName] = useState11("");
7263
7340
  const [createFromId, setCreateFromId] = useState11(ws.branches.currentId);
7264
7341
  const [actionKey, setActionKey] = useState11(null);
7265
- const workspaceRead = ws.permissions.data?.["workspace.read"] ?? false;
7266
- const canCreateBranch = ws.permissions.data?.["branches.create"] ?? false;
7267
- const canMergeBranches = ws.permissions.data?.["branches.merge"] ?? false;
7268
- const canWriteWorkspace = ws.permissions.data?.["workspace.write"] ?? false;
7342
+ const auth = useMemo11(
7343
+ () => deriveWorkspaceAuthorization({
7344
+ actor: ws.actor,
7345
+ permissions: ws.permissions?.data,
7346
+ participants: ws.participants?.data,
7347
+ authors: ws.authors?.data
7348
+ }),
7349
+ [ws.actor, ws.authors?.data, ws.participants?.data, ws.permissions?.data]
7350
+ );
7351
+ const workspaceRead = auth.canReadWorkspace;
7352
+ const branchesReadable = auth.permissions["branches.read"] === true;
7353
+ const canCreateBranch2 = auth.canCreateBranch;
7354
+ const canMergeBranches = auth.canMergeBranches;
7355
+ const canManageBranches2 = auth.canManageBranches;
7356
+ const canDeleteBranches = auth.canDeleteBranches;
7357
+ const canWriteBranchContent = auth.canEditBranchContent;
7269
7358
  const currentBranch = ws.branches.data?.find((branch) => branch.id === ws.branches.currentId);
7270
- const actorAuthorId = getActorAuthorId(ws);
7271
7359
  const branchItems = useMemo11(() => {
7272
7360
  return (ws.branches.data ?? []).map((branch) => {
7273
- const participant = branch.id === ws.branches.currentId ? resolveBranchParticipant(actorAuthorId, ws.participants.data) : null;
7274
- const canRead = branch.id === ws.branches.currentId ? true : workspaceRead;
7275
- const canWrite = branch.id === ws.branches.currentId ? participant?.canWrite ?? false : canWriteWorkspace;
7361
+ const canRead = branchesReadable && workspaceRead;
7362
+ const canWrite = canWriteBranchContent;
7276
7363
  return {
7277
7364
  branch,
7278
- participant,
7279
7365
  canRead,
7280
7366
  canWrite
7281
7367
  };
7282
7368
  });
7283
- }, [actorAuthorId, canWriteWorkspace, workspaceRead, ws.branches.currentId, ws.branches.data, ws.participants.data]);
7369
+ }, [branchesReadable, canWriteBranchContent, workspaceRead, ws.branches.data]);
7284
7370
  const visibleBranches = useMemo11(() => {
7285
7371
  const lower = query.trim().toLowerCase();
7286
7372
  return branchItems.filter((item) => {
@@ -7376,15 +7462,15 @@ var Drafts = ({ trigger }) => {
7376
7462
  const createPermission = useMemo11(() => {
7377
7463
  if (!ws.boot.isReady) return { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." };
7378
7464
  if (!workspaceRead) return { allowed: false, reason: "You do not have workspace access." };
7379
- if (!canCreateBranch) return { allowed: false, reason: "Branch creation is disabled by workspace permissions." };
7465
+ if (!canCreateBranch2) return { allowed: false, reason: "Branch creation is disabled by workspace permissions." };
7380
7466
  if (!createFromId) return { allowed: false, reason: "Choose a source branch first." };
7381
7467
  return { allowed: true };
7382
- }, [bootActionReason, canCreateBranch, createFromId, workspaceRead, ws.boot.isReady]);
7468
+ }, [bootActionReason, canCreateBranch2, createFromId, workspaceRead, ws.boot.isReady]);
7383
7469
  const branchActionMenu = (item) => {
7384
7470
  const currentId = ws.branches.currentId;
7385
7471
  const switchPermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : item.branch.id === currentId ? { allowed: false, reason: "This branch is already active." } : { allowed: true };
7386
- const setMainPermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : item.branch.isMain ? { allowed: false, reason: "This branch is already the main branch." } : !canWriteWorkspace ? { allowed: false, reason: "Setting the main branch requires workspace write access." } : !item.canWrite ? { allowed: false, reason: "You need write access to this branch to make it main." } : { allowed: true };
7387
- const deletePermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : item.branch.isMain ? { allowed: false, reason: "Set another branch as main before deleting this branch." } : !canWriteWorkspace ? { allowed: false, reason: "Deleting branches requires workspace write access." } : !item.canWrite ? { allowed: false, reason: "You need write access to delete this branch." } : { allowed: true };
7472
+ const setMainPermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : item.branch.isMain ? { allowed: false, reason: "This branch is already the main branch." } : !canManageBranches2 ? { allowed: false, reason: "Setting the main branch requires branches.manage." } : !item.canWrite ? { allowed: false, reason: "You need write access to this branch to make it main." } : { allowed: true };
7473
+ const deletePermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : item.branch.isMain ? { allowed: false, reason: "Set another branch as main before deleting this branch." } : !canDeleteBranches ? { allowed: false, reason: "Deleting branches requires branches.delete." } : !item.canWrite ? { allowed: false, reason: "You need write access to delete this branch." } : { allowed: true };
7388
7474
  const mergeTargets = visibleBranches.filter((target) => target.branch.id !== item.branch.id).map((target) => {
7389
7475
  const mergePermission = !ws.boot.isReady ? { allowed: false, reason: bootActionReason ?? "Workspace data is still loading." } : !canMergeBranches ? { allowed: false, reason: "Merging is disabled by workspace permissions." } : !item.canWrite ? { allowed: false, reason: "You need write access to the source branch to merge it." } : !target.canWrite ? { allowed: false, reason: "You need write access to the target branch to merge into it." } : { allowed: true };
7390
7476
  return {
@@ -9082,6 +9168,13 @@ import * as React22 from "react";
9082
9168
  import { jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
9083
9169
  function Node({ node, matched, isEditing, onCommit, onCancel }) {
9084
9170
  const spanRef = React22.useRef(null);
9171
+ React22.useEffect(() => {
9172
+ const span = spanRef.current;
9173
+ if (!span || isEditing) return;
9174
+ if (span.textContent !== node.title) {
9175
+ span.textContent = node.title;
9176
+ }
9177
+ }, [isEditing, node.title]);
9085
9178
  React22.useEffect(() => {
9086
9179
  if (isEditing && spanRef.current) {
9087
9180
  spanRef.current.focus();
@@ -9093,18 +9186,18 @@ function Node({ node, matched, isEditing, onCommit, onCancel }) {
9093
9186
  selection?.addRange(range);
9094
9187
  }
9095
9188
  }, [isEditing]);
9189
+ const commitFromDom = React22.useCallback(() => {
9190
+ if (!isEditing || !onCommit || !spanRef.current) return;
9191
+ onCommit(spanRef.current.innerText || "");
9192
+ }, [isEditing, onCommit]);
9096
9193
  const handleBlur = () => {
9097
- const edited = spanRef.current.innerText;
9098
- spanRef.current.innerText = node.title;
9099
- if (isEditing && onCommit && spanRef.current) {
9100
- onCommit(edited);
9101
- }
9194
+ commitFromDom();
9102
9195
  };
9103
9196
  const handleKeyDown = (e) => {
9104
9197
  if (!isEditing) return;
9105
9198
  if (e.key === "Enter") {
9106
9199
  e.preventDefault();
9107
- onCommit?.(spanRef.current?.innerText || "");
9200
+ spanRef.current?.blur();
9108
9201
  } else if (e.key === "Escape") {
9109
9202
  e.preventDefault();
9110
9203
  onCancel?.();
@@ -11553,7 +11646,7 @@ function RenderIf({ data, emptyMessage = null, children, when }) {
11553
11646
  // src/panels/right/partials/global/add-service.tsx
11554
11647
  import { useCanvas as useCanvas7, useWorkspace as useWorkspace12 } from "@timeax/digital-service-engine/workspace";
11555
11648
  import { InputField as InputField5 } from "@timeax/form-palette";
11556
- import { useCallback as useCallback17, useEffect as useEffect19, useMemo as useMemo25, useState as useState26 } from "react";
11649
+ import { useCallback as useCallback18, useEffect as useEffect19, useMemo as useMemo25, useState as useState26 } from "react";
11557
11650
  import { BsPlus } from "react-icons/bs";
11558
11651
  import { FaFolderOpen } from "react-icons/fa";
11559
11652
  import { FiFilter } from "react-icons/fi";
@@ -11735,7 +11828,7 @@ function AddServicePopover({
11735
11828
  }
11736
11829
  return ids;
11737
11830
  }, [canvas.api.editor, contextFilterEnabled, liveSnapshot, policies2, services2]);
11738
- const setCatalogMode = useCallback17(
11831
+ const setCatalogMode = useCallback18(
11739
11832
  (mode) => {
11740
11833
  const editor = canvas.api.editor;
11741
11834
  editor.ensureCatalog?.();
@@ -14296,7 +14389,7 @@ var wireframe_tags_widget_default = WireframeTagsWidget;
14296
14389
  // src/panels/right/tabs/wireframe.tsx
14297
14390
  import { useOrderFlow, Wrapper } from "@timeax/digital-service-engine/react";
14298
14391
  import { useCanvas as useCanvas16, useWorkspace as useWorkspace14 } from "@timeax/digital-service-engine/workspace";
14299
- import { useCallback as useCallback18, useMemo as useMemo33 } from "react";
14392
+ import { useCallback as useCallback19, useMemo as useMemo33 } from "react";
14300
14393
  import { jsx as jsx71, jsxs as jsxs51 } from "react/jsx-runtime";
14301
14394
  var CHECKBOX_SINGLE_EXTRA_PROPS = Object.freeze({ single: true });
14302
14395
  function Wireframe() {
@@ -14357,7 +14450,7 @@ function Wireframe() {
14357
14450
  }
14358
14451
  return map;
14359
14452
  }, [canvas.props, canvas.selectionInfo, canvas.activeId]);
14360
- const select = useCallback18(
14453
+ const select = useCallback19(
14361
14454
  (id) => {
14362
14455
  canvas.setActive(id);
14363
14456
  canvas.api.select([id]);
@@ -14499,7 +14592,7 @@ var right_default = RightPanel;
14499
14592
  // src/workspace/fallback-editor-modal.tsx
14500
14593
  import { useCanvas as useCanvas18, useWorkspace as useWorkspace15 } from "@timeax/digital-service-engine/workspace";
14501
14594
  import cloneDeep4 from "lodash/cloneDeep";
14502
- import { Suspense, lazy, createContext as createContext4, useCallback as useCallback19, useContext as useContext4, useEffect as useEffect21, useMemo as useMemo34, useState as useState33 } from "react";
14595
+ import { Suspense, lazy, createContext as createContext4, useCallback as useCallback20, useContext as useContext4, useEffect as useEffect21, useMemo as useMemo34, useState as useState33 } from "react";
14503
14596
  import { createPortal as createPortal4 } from "react-dom";
14504
14597
  import { FiX as FiX4 } from "react-icons/fi";
14505
14598
  import { jsx as jsx73, jsxs as jsxs53 } from "react/jsx-runtime";
@@ -14522,11 +14615,11 @@ function FallbackEditorModalProvider({ children }) {
14522
14615
  const [launch, setLaunch] = useState33(null);
14523
14616
  const [sessionId, setSessionId] = useState33(0);
14524
14617
  const [surfaceError, setSurfaceError] = useState33(null);
14525
- const close = useCallback19(() => {
14618
+ const close = useCallback20(() => {
14526
14619
  setLaunch(null);
14527
14620
  setSurfaceError(null);
14528
14621
  }, []);
14529
- const openForNode = useCallback19((input) => {
14622
+ const openForNode = useCallback20((input) => {
14530
14623
  setSurfaceError(null);
14531
14624
  setSessionId((current) => current + 1);
14532
14625
  setLaunch({
@@ -14537,7 +14630,7 @@ function FallbackEditorModalProvider({ children }) {
14537
14630
  nodeLabel: input.nodeLabel ?? input.nodeId
14538
14631
  });
14539
14632
  }, []);
14540
- const openForService = useCallback19((input) => {
14633
+ const openForService = useCallback20((input) => {
14541
14634
  setSurfaceError(null);
14542
14635
  setSessionId((current) => current + 1);
14543
14636
  setLaunch({
@@ -14546,7 +14639,7 @@ function FallbackEditorModalProvider({ children }) {
14546
14639
  serviceName: input.serviceName
14547
14640
  });
14548
14641
  }, []);
14549
- const persistProps = useCallback19(
14642
+ const persistProps = useCallback20(
14550
14643
  (label, mutate) => {
14551
14644
  const editorAny = canvas.api.editor;
14552
14645
  if (typeof editorAny.transact !== "function" || typeof editorAny.replaceProps !== "function") {
@@ -14560,7 +14653,7 @@ function FallbackEditorModalProvider({ children }) {
14560
14653
  },
14561
14654
  [canvas.api.builder, canvas.api.editor, canvas.props]
14562
14655
  );
14563
- const handleSave = useCallback19(
14656
+ const handleSave = useCallback20(
14564
14657
  async (nextFallbacks) => {
14565
14658
  setSurfaceError(null);
14566
14659
  try {
@@ -14577,7 +14670,7 @@ function FallbackEditorModalProvider({ children }) {
14577
14670
  },
14578
14671
  [close, persistProps]
14579
14672
  );
14580
- const handleSettingsChange = useCallback19(
14673
+ const handleSettingsChange = useCallback20(
14581
14674
  async (nextSettings) => {
14582
14675
  setSurfaceError(null);
14583
14676
  try {
@@ -14690,7 +14783,7 @@ function useFallbackEditorModal() {
14690
14783
 
14691
14784
  // src/workspace/bottom-panel/index.tsx
14692
14785
  import { useCanvas as useCanvas21, useWorkspace as useWorkspace16 } from "@timeax/digital-service-engine/workspace";
14693
- import { useCallback as useCallback20, useEffect as useEffect24, useMemo as useMemo36, useRef as useRef12, useState as useState36 } from "react";
14786
+ import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo36, useRef as useRef12, useState as useState36 } from "react";
14694
14787
  import { FiEye as FiEye3, FiEyeOff as FiEyeOff2, FiSearch, FiTerminal as FiTerminal2, FiX as FiX6 } from "react-icons/fi";
14695
14788
  import { LuGripHorizontal, LuLayers3 } from "react-icons/lu";
14696
14789
  import { MdOutlineSync } from "react-icons/md";
@@ -16521,7 +16614,7 @@ function BottomConsolePanel({ controller, errors, activeServices, allServices, o
16521
16614
  window.removeEventListener("pointercancel", onPointerUp);
16522
16615
  };
16523
16616
  }, [draggingPanel, panelPosition]);
16524
- const setCatalogMode = useCallback20(
16617
+ const setCatalogMode = useCallback21(
16525
16618
  (mode) => {
16526
16619
  const editor = canvas.api.editor;
16527
16620
  editor.ensureCatalog?.();
@@ -16529,27 +16622,27 @@ function BottomConsolePanel({ controller, errors, activeServices, allServices, o
16529
16622
  },
16530
16623
  [canvas.api.editor]
16531
16624
  );
16532
- const handleSelectCatalogService = useCallback20(
16625
+ const handleSelectCatalogService = useCallback21(
16533
16626
  (id) => {
16534
16627
  setSelectedAllServiceId(id);
16535
16628
  canvas.api.editor.setSelectedCatalogService?.(id ?? void 0);
16536
16629
  },
16537
16630
  [canvas.api.editor]
16538
16631
  );
16539
- const handleSelectCatalogGroup = useCallback20(
16632
+ const handleSelectCatalogGroup = useCallback21(
16540
16633
  (groupId) => {
16541
16634
  canvas.api.editor.setActiveCatalogNode?.(groupId ?? void 0);
16542
16635
  },
16543
16636
  [canvas.api.editor]
16544
16637
  );
16545
- const handleCreateRootGroup = useCallback20(
16638
+ const handleCreateRootGroup = useCallback21(
16546
16639
  (label) => {
16547
16640
  if (!label.trim()) return;
16548
16641
  canvas.api.editor.createCatalogGroup?.({ label: label.trim() });
16549
16642
  },
16550
16643
  [canvas.api.editor]
16551
16644
  );
16552
- const handleCreateChildGroup = useCallback20(
16645
+ const handleCreateChildGroup = useCallback21(
16553
16646
  (label) => {
16554
16647
  if (!selectedCatalogGroupId) return;
16555
16648
  const editor = canvas.api.editor;
@@ -16558,7 +16651,7 @@ function BottomConsolePanel({ controller, errors, activeServices, allServices, o
16558
16651
  },
16559
16652
  [canvas.api.editor, selectedCatalogGroupId]
16560
16653
  );
16561
- const handleRenameGroup = useCallback20(
16654
+ const handleRenameGroup = useCallback21(
16562
16655
  (label) => {
16563
16656
  if (!selectedCatalogGroupId) return;
16564
16657
  const group = catalogGroups.find((item) => item.id === selectedCatalogGroupId);
@@ -16568,15 +16661,15 @@ function BottomConsolePanel({ controller, errors, activeServices, allServices, o
16568
16661
  },
16569
16662
  [canvas.api.editor, catalogGroups, selectedCatalogGroupId]
16570
16663
  );
16571
- const handleDeleteGroup = useCallback20(() => {
16664
+ const handleDeleteGroup = useCallback21(() => {
16572
16665
  if (!selectedCatalogGroupId) return;
16573
16666
  canvas.api.editor.removeCatalogNode?.(selectedCatalogGroupId, { cascade: true });
16574
16667
  }, [canvas.api.editor, selectedCatalogGroupId]);
16575
- const handleAssignServices = useCallback20(() => {
16668
+ const handleAssignServices = useCallback21(() => {
16576
16669
  if (!selectedCatalogGroupId) return;
16577
16670
  setServicePickerOpen(true);
16578
16671
  }, [selectedCatalogGroupId]);
16579
- const handleConfirmAssignServices = useCallback20(
16672
+ const handleConfirmAssignServices = useCallback21(
16580
16673
  (serviceIds) => {
16581
16674
  if (!selectedCatalogGroupId || !serviceIds.length) return;
16582
16675
  canvas.api.editor.assignServicesToCatalogGroup?.(selectedCatalogGroupId, serviceIds, "append");
@@ -17247,41 +17340,77 @@ var participantsByBranch = {
17247
17340
  var permissions = {
17248
17341
  "actor:davy": {
17249
17342
  "workspace.read": true,
17250
- "workspace.write": true,
17343
+ "workspace.write": false,
17344
+ "branches.read": true,
17251
17345
  "branches.create": true,
17346
+ "branches.write": false,
17347
+ "branches.delete": true,
17252
17348
  "branches.merge": true,
17349
+ "branches.publish": true,
17350
+ "branches.manage": true,
17253
17351
  "templates.read": true,
17254
17352
  "templates.write": true,
17353
+ "templates.delete": true,
17354
+ "templates.manage": true,
17255
17355
  "comments.read": true,
17256
17356
  "comments.write": true,
17357
+ "comments.moderate": true,
17257
17358
  "policies.read": true,
17258
17359
  "policies.write": true,
17360
+ "policies.manage": true,
17361
+ "participants.read": true,
17362
+ "participants.write": true,
17363
+ "participants.manage": true,
17259
17364
  "services.read": true
17260
17365
  },
17261
17366
  "actor:bot": {
17262
17367
  "workspace.read": true,
17263
17368
  "workspace.write": false,
17369
+ "branches.read": true,
17264
17370
  "branches.create": false,
17371
+ "branches.write": false,
17372
+ "branches.delete": false,
17265
17373
  "branches.merge": false,
17374
+ "branches.publish": false,
17375
+ "branches.manage": false,
17266
17376
  "templates.read": true,
17267
17377
  "templates.write": false,
17378
+ "templates.delete": false,
17379
+ "templates.manage": false,
17268
17380
  "comments.read": true,
17269
17381
  "comments.write": true,
17382
+ "comments.moderate": false,
17270
17383
  "policies.read": false,
17271
17384
  "policies.write": false,
17385
+ "policies.manage": false,
17386
+ "participants.read": false,
17387
+ "participants.write": false,
17388
+ "participants.manage": false,
17272
17389
  "services.read": true
17273
17390
  },
17274
17391
  "actor:readonly": {
17275
17392
  "workspace.read": true,
17276
17393
  "workspace.write": false,
17394
+ "branches.read": true,
17277
17395
  "branches.create": false,
17396
+ "branches.write": false,
17397
+ "branches.delete": false,
17278
17398
  "branches.merge": false,
17399
+ "branches.publish": false,
17400
+ "branches.manage": false,
17279
17401
  "templates.read": true,
17280
17402
  "templates.write": false,
17403
+ "templates.delete": false,
17404
+ "templates.manage": false,
17281
17405
  "comments.read": true,
17282
17406
  "comments.write": false,
17407
+ "comments.moderate": false,
17283
17408
  "policies.read": false,
17284
17409
  "policies.write": false,
17410
+ "policies.manage": false,
17411
+ "participants.read": false,
17412
+ "participants.write": false,
17413
+ "participants.manage": false,
17285
17414
  "services.read": true
17286
17415
  }
17287
17416
  };
@@ -17623,7 +17752,7 @@ var workspaceBackend = {
17623
17752
  return baseBackend.branches.merge(workspaceId, sourceId, targetId);
17624
17753
  },
17625
17754
  delete: async (workspaceId, branchId) => {
17626
- const check = await authorize("branch-manage", branchId);
17755
+ const check = await authorize("branch-delete", branchId);
17627
17756
  if (!check.ok) return check;
17628
17757
  return baseBackend.branches.delete(workspaceId, branchId);
17629
17758
  }
@@ -17677,7 +17806,7 @@ var workspaceBackend = {
17677
17806
  },
17678
17807
  publish: async (params) => {
17679
17808
  if (params.actorId !== workspaceActor.id) return forbidden("Snapshot mutation denied for unknown actor.", { actorId: params.actorId });
17680
- const check = await authorize("snapshot-write", void 0);
17809
+ const check = await authorize("branch-publish", void 0);
17681
17810
  if (!check.ok) return check;
17682
17811
  return baseBackend.snapshots.publish(params);
17683
17812
  },