{#if title}
@@ -16,7 +18,7 @@
{@html body}
{:else}
-
+
{@html body}
@@ -24,6 +26,23 @@
diff --git a/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte b/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte
deleted file mode 100644
index 9ad41ad6528..00000000000
--- a/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
- Update {user.email}'s role for {app.name}.
-
-
diff --git a/packages/builder/src/stores/builder/roles.js b/packages/builder/src/stores/builder/roles.js
index ac395aa232d..fd3581f1d47 100644
--- a/packages/builder/src/stores/builder/roles.js
+++ b/packages/builder/src/stores/builder/roles.js
@@ -1,16 +1,34 @@
-import { writable } from "svelte/store"
+import { derived, writable, get } from "svelte/store"
import { API } from "api"
import { RoleUtils } from "@budibase/frontend-core"
export function createRolesStore() {
- const { subscribe, update, set } = writable([])
+ const store = writable([])
+ const enriched = derived(store, $store => {
+ return $store.map(role => ({
+ ...role,
+
+ // Ensure we have new metadata for all roles
+ uiMetadata: {
+ displayName: role.uiMetadata?.displayName || role.name,
+ color:
+ role.uiMetadata?.color || "var(--spectrum-global-color-magenta-400)",
+ description: role.uiMetadata?.description || "Custom role",
+ },
+ }))
+ })
function setRoles(roles) {
- set(
+ store.set(
roles.sort((a, b) => {
const priorityA = RoleUtils.getRolePriority(a._id)
const priorityB = RoleUtils.getRolePriority(b._id)
- return priorityA > priorityB ? -1 : 1
+ if (priorityA !== priorityB) {
+ return priorityA > priorityB ? -1 : 1
+ }
+ const nameA = a.uiMetadata?.displayName || a.name
+ const nameB = b.uiMetadata?.displayName || b.name
+ return nameA < nameB ? -1 : 1
})
)
}
@@ -29,17 +47,43 @@ export function createRolesStore() {
roleId: role?._id,
roleRev: role?._rev,
})
- update(state => state.filter(existing => existing._id !== role._id))
+ await actions.fetch()
},
save: async role => {
const savedRole = await API.saveRole(role)
await actions.fetch()
return savedRole
},
+ replace: (roleId, role) => {
+ // Handles external updates of roles
+ if (!roleId) {
+ return
+ }
+
+ // Handle deletion
+ if (!role) {
+ store.update(state => state.filter(x => x._id !== roleId))
+ return
+ }
+
+ // Add new role
+ const index = get(store).findIndex(x => x._id === role._id)
+ if (index === -1) {
+ store.update(state => [...state, role])
+ }
+
+ // Update existing role
+ else if (role) {
+ store.update(state => {
+ state[index] = role
+ return [...state]
+ })
+ }
+ },
}
return {
- subscribe,
+ subscribe: enriched.subscribe,
...actions,
}
}
diff --git a/packages/builder/src/stores/builder/websocket.js b/packages/builder/src/stores/builder/websocket.js
index 7df5ab9adba..8a0d83abc10 100644
--- a/packages/builder/src/stores/builder/websocket.js
+++ b/packages/builder/src/stores/builder/websocket.js
@@ -9,6 +9,7 @@ import {
snippets,
datasources,
tables,
+ roles,
} from "stores/builder"
import { get } from "svelte/store"
import { auth, appsStore } from "stores/portal"
@@ -56,12 +57,18 @@ export const createBuilderWebsocket = appId => {
datasources.replaceDatasource(id, datasource)
})
+ // Role events
+ socket.onOther(BuilderSocketEvent.RoleChange, ({ id, role }) => {
+ roles.replace(id, role)
+ })
+
// Design section events
socket.onOther(BuilderSocketEvent.ScreenChange, ({ id, screen }) => {
screenStore.replace(id, screen)
})
+
+ // App events
socket.onOther(BuilderSocketEvent.AppMetadataChange, ({ metadata }) => {
- //Sync app metadata across the stores
appStore.syncMetadata(metadata)
themeStore.syncMetadata(metadata)
navigationStore.syncMetadata(metadata)
@@ -79,7 +86,7 @@ export const createBuilderWebsocket = appId => {
}
)
- // Automations
+ // Automation events
socket.onOther(BuilderSocketEvent.AutomationChange, ({ id, automation }) => {
automationStore.actions.replace(id, automation)
})
diff --git a/packages/client/src/components/devtools/DevToolsHeader.svelte b/packages/client/src/components/devtools/DevToolsHeader.svelte
index 55b705e7175..eca085a88a6 100644
--- a/packages/client/src/components/devtools/DevToolsHeader.svelte
+++ b/packages/client/src/components/devtools/DevToolsHeader.svelte
@@ -1,28 +1,29 @@
diff --git a/packages/frontend-core/src/components/grid/cells/DataCell.svelte b/packages/frontend-core/src/components/grid/cells/DataCell.svelte
index 5d5f06872d0..cd6b61af800 100644
--- a/packages/frontend-core/src/components/grid/cells/DataCell.svelte
+++ b/packages/frontend-core/src/components/grid/cells/DataCell.svelte
@@ -66,7 +66,7 @@
focus: () => api?.focus?.(),
blur: () => api?.blur?.(),
isActive: () => api?.isActive?.() ?? false,
- onKeyDown: (...params) => api?.onKeyDown(...params),
+ onKeyDown: (...params) => api?.onKeyDown?.(...params),
isReadonly: () => readonly,
getType: () => column.schema.type,
getValue: () => row[column.name],
diff --git a/packages/frontend-core/src/components/grid/cells/RoleCell.svelte b/packages/frontend-core/src/components/grid/cells/RoleCell.svelte
new file mode 100644
index 00000000000..82d1e26aa7d
--- /dev/null
+++ b/packages/frontend-core/src/components/grid/cells/RoleCell.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+ {role?.uiMetadata?.displayName || role?.name || "Unknown role"}
+
+
+
+
diff --git a/packages/frontend-core/src/components/grid/lib/renderers.js b/packages/frontend-core/src/components/grid/lib/renderers.js
index f9e26d920a8..70700f9417d 100644
--- a/packages/frontend-core/src/components/grid/lib/renderers.js
+++ b/packages/frontend-core/src/components/grid/lib/renderers.js
@@ -16,6 +16,7 @@ import AttachmentSingleCell from "../cells/AttachmentSingleCell.svelte"
import BBReferenceCell from "../cells/BBReferenceCell.svelte"
import SignatureCell from "../cells/SignatureCell.svelte"
import BBReferenceSingleCell from "../cells/BBReferenceSingleCell.svelte"
+import RoleCell from "../cells/RoleCell.svelte"
const TypeComponentMap = {
[FieldType.STRING]: TextCell,
@@ -35,6 +36,9 @@ const TypeComponentMap = {
[FieldType.JSON]: JSONCell,
[FieldType.BB_REFERENCE]: BBReferenceCell,
[FieldType.BB_REFERENCE_SINGLE]: BBReferenceSingleCell,
+
+ // Custom types for UI only
+ role: RoleCell,
}
export const getCellRenderer = column => {
return (
diff --git a/packages/frontend-core/src/utils/roles.js b/packages/frontend-core/src/utils/roles.js
index 1ae9d3ac142..913d452e7cd 100644
--- a/packages/frontend-core/src/utils/roles.js
+++ b/packages/frontend-core/src/utils/roles.js
@@ -7,20 +7,7 @@ const RolePriorities = {
[Roles.BASIC]: 2,
[Roles.PUBLIC]: 1,
}
-const RoleColours = {
- [Roles.ADMIN]: "var(--spectrum-global-color-static-red-400)",
- [Roles.CREATOR]: "var(--spectrum-global-color-static-magenta-600)",
- [Roles.POWER]: "var(--spectrum-global-color-static-orange-400)",
- [Roles.BASIC]: "var(--spectrum-global-color-static-green-400)",
- [Roles.PUBLIC]: "var(--spectrum-global-color-static-blue-400)",
-}
export const getRolePriority = role => {
return RolePriorities[role] ?? 0
}
-
-export const getRoleColour = roleId => {
- return (
- RoleColours[roleId] ?? "var(--spectrum-global-color-static-magenta-400)"
- )
-}
diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts
index f26b4bae690..10477119837 100644
--- a/packages/server/src/api/controllers/role.ts
+++ b/packages/server/src/api/controllers/role.ts
@@ -18,9 +18,11 @@ import {
UserCtx,
UserMetadata,
DocumentType,
+ PermissionLevel,
} from "@budibase/types"
import { RoleColor, sdk as sharedSdk, helpers } from "@budibase/shared-core"
import sdk from "../../sdk"
+import { builderSocket } from "../../websockets"
const UpdateRolesOptions = {
CREATED: "created",
@@ -34,11 +36,11 @@ async function removeRoleFromOthers(roleId: string) {
let changed = false
if (Array.isArray(role.inherits)) {
const newInherits = role.inherits.filter(
- id => !roles.compareRoleIds(id, roleId)
+ id => !roles.roleIDsAreEqual(id, roleId)
)
changed = role.inherits.length !== newInherits.length
role.inherits = newInherits
- } else if (role.inherits && roles.compareRoleIds(role.inherits, roleId)) {
+ } else if (role.inherits && roles.roleIDsAreEqual(role.inherits, roleId)) {
role.inherits = roles.BUILTIN_ROLE_IDS.PUBLIC
changed = true
}
@@ -124,6 +126,17 @@ export async function save(ctx: UserCtx
) {
ctx.throw(400, "Cannot change custom role name")
}
+ // custom roles should always inherit basic - if they don't inherit anything else
+ if (!inherits && roles.validInherits(allRoles, dbRole?.inherits)) {
+ inherits = dbRole?.inherits
+ } else if (!roles.validInherits(allRoles, inherits)) {
+ inherits = [roles.BUILTIN_ROLE_IDS.BASIC]
+ }
+ // assume write permission level for newly created roles
+ if (isCreate && !permissionId) {
+ permissionId = PermissionLevel.WRITE
+ }
+
const role = new roles.Role(_id, name, permissionId, {
displayName: uiMetadata?.displayName || name,
description: uiMetadata?.description || "Custom role",
@@ -177,6 +190,7 @@ export async function save(ctx: UserCtx) {
},
})
}
+ builderSocket?.emitRoleUpdate(ctx, role)
}
export async function destroy(ctx: UserCtx) {
@@ -216,6 +230,7 @@ export async function destroy(ctx: UserCtx) {
ctx.message = `Role ${ctx.params.roleId} deleted successfully`
ctx.status = 200
+ builderSocket?.emitRoleDeletion(ctx, role)
}
export async function accessible(ctx: UserCtx) {
@@ -223,35 +238,23 @@ export async function accessible(ctx: UserCtx) {
if (!roleId) {
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
}
+ // If a custom role is provided in the header, filter out higher level roles
+ const roleHeader = ctx.header[Header.PREVIEW_ROLE]
+ if (Array.isArray(roleHeader)) {
+ ctx.throw(400, `Too many roles specified in ${Header.PREVIEW_ROLE} header`)
+ }
+ const isBuilder = ctx.user && sharedSdk.users.isAdminOrBuilder(ctx.user)
let roleIds: string[] = []
- if (ctx.user && sharedSdk.users.isAdminOrBuilder(ctx.user)) {
+ if (!roleHeader && isBuilder) {
const appId = context.getAppId()
if (appId) {
roleIds = await roles.getAllRoleIds(appId)
}
+ } else if (isBuilder && roleHeader) {
+ roleIds = await roles.getUserRoleIdHierarchy(roleHeader)
} else {
roleIds = await roles.getUserRoleIdHierarchy(roleId!)
}
- // If a custom role is provided in the header, filter out higher level roles
- const roleHeader = ctx.header?.[Header.PREVIEW_ROLE] as string
- if (roleHeader && !Object.keys(roles.BUILTIN_ROLE_IDS).includes(roleHeader)) {
- const role = await roles.getRole(roleHeader)
- const inherits = role?.inherits
- const orderedRoles = roleIds.reverse()
- let filteredRoles = [roleHeader]
- for (let role of orderedRoles) {
- filteredRoles = [role, ...filteredRoles]
- if (
- (Array.isArray(inherits) && inherits.includes(role)) ||
- role === inherits
- ) {
- break
- }
- }
- filteredRoles.pop()
- roleIds = [roleHeader, ...filteredRoles]
- }
-
ctx.body = roleIds.map(roleId => roles.getExternalRoleID(roleId))
}
diff --git a/packages/server/src/api/routes/tests/role.spec.ts b/packages/server/src/api/routes/tests/role.spec.ts
index 56682953427..adb83ca7932 100644
--- a/packages/server/src/api/routes/tests/role.spec.ts
+++ b/packages/server/src/api/routes/tests/role.spec.ts
@@ -38,6 +38,26 @@ describe("/roles", () => {
_id: dbCore.prefixRoleID(res._id!),
})
})
+
+ it("handle a role with invalid inherits", async () => {
+ const role = basicRole()
+ role.inherits = ["not_real", "some_other_not_real"]
+
+ const res = await config.api.roles.save(role, {
+ status: 200,
+ })
+ expect(res.inherits).toEqual([BUILTIN_ROLE_IDS.BASIC])
+ })
+
+ it("handle a role with no inherits", async () => {
+ const role = basicRole()
+ role.inherits = []
+
+ const res = await config.api.roles.save(role, {
+ status: 200,
+ })
+ expect(res.inherits).toEqual([BUILTIN_ROLE_IDS.BASIC])
+ })
})
describe("update", () => {
@@ -149,6 +169,17 @@ describe("/roles", () => {
{ status: 400, body: { message: LOOP_ERROR } }
)
})
+
+ it("handle updating a role, without its inherits", async () => {
+ const res = await config.api.roles.save({
+ ...basicRole(),
+ inherits: [BUILTIN_ROLE_IDS.ADMIN],
+ })
+ // remove the roles so that it will default back to DB roles, then save again
+ delete res.inherits
+ const updatedRes = await config.api.roles.save(res)
+ expect(updatedRes.inherits).toEqual([BUILTIN_ROLE_IDS.ADMIN])
+ })
})
describe("fetch", () => {
@@ -298,6 +329,23 @@ describe("/roles", () => {
}
)
})
+
+ it("should fetch preview role correctly even without basic specified", async () => {
+ const role = await config.api.roles.save(basicRole())
+ // have to forcefully delete the inherits from DB - technically can't
+ // happen anymore - but good test case
+ await dbCore.getDB(config.appId!).put({
+ ...role,
+ _id: dbCore.prefixRoleID(role._id!),
+ inherits: [],
+ })
+ await config.withHeaders({ "x-budibase-role": role.name }, async () => {
+ const res = await config.api.roles.accessible({
+ status: 200,
+ })
+ expect(res).toEqual([role.name])
+ })
+ })
})
describe("accessible - multi-inheritance", () => {
diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts
index 5dfe3d2a44b..894710ca272 100644
--- a/packages/server/src/api/routes/tests/screen.spec.ts
+++ b/packages/server/src/api/routes/tests/screen.spec.ts
@@ -86,7 +86,6 @@ describe("/screens", () => {
status: 200,
}
)
- // basic and role1 screen
expect(res.screens.length).toEqual(screenIds.length)
expect(res.screens.map(s => s._id).sort()).toEqual(screenIds.sort())
})
@@ -107,6 +106,25 @@ describe("/screens", () => {
screen2._id!,
])
})
+
+ it("should be able to fetch basic and screen 1 with role1 in role header", async () => {
+ await config.withHeaders(
+ {
+ "x-budibase-role": role1._id!,
+ },
+ async () => {
+ const res = await config.api.application.getDefinition(
+ config.prodAppId!,
+ {
+ status: 200,
+ }
+ )
+ const screenIds = [screen._id!, screen1._id!]
+ expect(res.screens.length).toEqual(screenIds.length)
+ expect(res.screens.map(s => s._id).sort()).toEqual(screenIds.sort())
+ }
+ )
+ })
})
describe("save", () => {
diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts
index d6163772980..a8ef8bb2512 100644
--- a/packages/server/src/middleware/currentapp.ts
+++ b/packages/server/src/middleware/currentapp.ts
@@ -56,22 +56,9 @@ export default async (ctx: UserCtx, next: any) => {
ctx.request &&
(ctx.request.headers[constants.Header.PREVIEW_ROLE] as string)
if (isBuilder && isDevApp && roleHeader) {
- // Ensure the role is valid by ensuring a definition exists
- try {
- if (roleHeader) {
- const role = await roles.getRole(roleHeader)
- if (role) {
- roleId = roleHeader
-
- // Delete admin and builder flags so that the specified role is honoured
- ctx.user = users.removePortalUserPermissions(
- ctx.user
- ) as ContextUser
- }
- }
- } catch (error) {
- // Swallow error and do nothing
- }
+ roleId = roleHeader
+ // Delete admin and builder flags so that the specified role is honoured
+ ctx.user = users.removePortalUserPermissions(ctx.user) as ContextUser
}
}
diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts
index b38cc6484fa..b63d6d1a78b 100644
--- a/packages/server/src/tests/utilities/structures.ts
+++ b/packages/server/src/tests/utilities/structures.ts
@@ -8,31 +8,31 @@ import {
} from "../../automations"
import {
AIOperationEnum,
+ AutoFieldSubType,
Automation,
AutomationActionStepId,
+ AutomationEventType,
AutomationResults,
AutomationStatus,
AutomationStep,
AutomationStepType,
AutomationTrigger,
AutomationTriggerStepId,
+ BBReferenceFieldSubType,
+ CreateViewRequest,
Datasource,
+ FieldSchema,
FieldType,
+ INTERNAL_TABLE_SOURCE_ID,
+ JsonFieldSubType,
+ LoopStepType,
+ Query,
+ Role,
SourceName,
Table,
- INTERNAL_TABLE_SOURCE_ID,
TableSourceType,
- Query,
Webhook,
WebhookActionType,
- AutomationEventType,
- LoopStepType,
- FieldSchema,
- BBReferenceFieldSubType,
- JsonFieldSubType,
- AutoFieldSubType,
- Role,
- CreateViewRequest,
} from "@budibase/types"
import { LoopInput } from "../../definitions/automations"
import { merge } from "lodash"
@@ -439,7 +439,7 @@ export function updateRowAutomationWithFilters(
appId: string,
tableId: string
): Automation {
- const automation: Automation = {
+ return {
name: "updateRowWithFilters",
type: "automation",
appId,
@@ -472,7 +472,6 @@ export function updateRowAutomationWithFilters(
},
},
}
- return automation
}
export function basicAutomationResults(
diff --git a/packages/server/src/websockets/builder.ts b/packages/server/src/websockets/builder.ts
index 702a9410956..cf92d68ef3b 100644
--- a/packages/server/src/websockets/builder.ts
+++ b/packages/server/src/websockets/builder.ts
@@ -11,6 +11,7 @@ import {
Screen,
App,
Automation,
+ Role,
} from "@budibase/types"
import { gridSocket } from "./index"
import { clearLock, updateLock } from "../utilities/redis"
@@ -100,6 +101,20 @@ export default class BuilderSocket extends BaseSocket {
})
}
+ emitRoleUpdate(ctx: any, role: Role) {
+ this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.RoleChange, {
+ id: role._id,
+ role,
+ })
+ }
+
+ emitRoleDeletion(ctx: any, role: Role) {
+ this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.RoleChange, {
+ id: role._id,
+ role: null,
+ })
+ }
+
emitTableUpdate(ctx: any, table: Table, options?: EmitOptions) {
if (table.sourceId == null || table.sourceId === "") {
throw new Error("Table sourceId is not set")
diff --git a/packages/shared-core/src/constants/index.ts b/packages/shared-core/src/constants/index.ts
index 6c49625937f..11bb79c8d3d 100644
--- a/packages/shared-core/src/constants/index.ts
+++ b/packages/shared-core/src/constants/index.ts
@@ -97,6 +97,7 @@ export enum BuilderSocketEvent {
SelectResource = "SelectResource",
AppPublishChange = "AppPublishChange",
AutomationChange = "AutomationChange",
+ RoleChange = "RoleChange",
}
export const SocketSessionTTL = 60
diff --git a/yarn.lock b/yarn.lock
index 1198e98ad6b..5dfef16e0e1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2343,6 +2343,18 @@
enabled "2.0.x"
kuler "^2.0.0"
+"@dagrejs/dagre@1.1.4":
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/@dagrejs/dagre/-/dagre-1.1.4.tgz#66f9c0e2b558308f2c268f60e2c28f22ee17e339"
+ integrity sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==
+ dependencies:
+ "@dagrejs/graphlib" "2.2.4"
+
+"@dagrejs/graphlib@2.2.4":
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.4.tgz#d77bfa9ff49e2307c0c6e6b8b26b5dd3c05816c4"
+ integrity sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==
+
"@datadog/native-appsec@7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-7.0.0.tgz#a380174dd49aef2d9bb613a0ec8ead6dc7822095"
@@ -5093,6 +5105,11 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-4.3.1.tgz#d333fa41909f691c8750b5c15ad9ba029df2248e"
integrity sha512-rX6Iasu9BsFMVgEN0vGRPm9dmSxva+IK/uqQAa9HM0lliwqUiFrJxrFXHHpiAgNuux/U4srEJwbSpGzfF+CegQ==
+"@svelte-put/shortcut@^3.1.0":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@svelte-put/shortcut/-/shortcut-3.1.1.tgz#aba4d7407024d5cff38727e12925c8f81e877079"
+ integrity sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==
+
"@sveltejs/vite-plugin-svelte@1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.4.0.tgz#412a735de489ca731d0c780c2b410f45dd95b392"
@@ -5451,6 +5468,45 @@
dependencies:
"@types/node" "*"
+"@types/d3-color@*":
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
+ integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
+
+"@types/d3-drag@^3.0.7":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
+ integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-interpolate@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
+ integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
+ dependencies:
+ "@types/d3-color" "*"
+
+"@types/d3-selection@*", "@types/d3-selection@^3.0.10":
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe"
+ integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==
+
+"@types/d3-transition@^3.0.8":
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f"
+ integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==
+ dependencies:
+ "@types/d3-selection" "*"
+
+"@types/d3-zoom@^3.0.8":
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
+ integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
+ dependencies:
+ "@types/d3-interpolate" "*"
+ "@types/d3-selection" "*"
+
"@types/debug@*":
version "4.1.7"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
@@ -6578,6 +6634,28 @@
loupe "^3.1.1"
tinyrainbow "^1.2.0"
+"@xyflow/svelte@^0.1.18":
+ version "0.1.18"
+ resolved "https://registry.yarnpkg.com/@xyflow/svelte/-/svelte-0.1.18.tgz#ba2f9f72adc64ff6f71a5ad03cf759af8d7c9748"
+ integrity sha512-P2td3XcvMk36pnhyRUAXtmwfd7sv1KAHVF29YZUNndYlgxG98vwj1UoyyuXwCHIiyu82GgowaTppHCNPXsvNSg==
+ dependencies:
+ "@svelte-put/shortcut" "^3.1.0"
+ "@xyflow/system" "0.0.41"
+ classcat "^5.0.4"
+
+"@xyflow/system@0.0.41":
+ version "0.0.41"
+ resolved "https://registry.yarnpkg.com/@xyflow/system/-/system-0.0.41.tgz#6c314b2bbca594aec4d7cdb56efb003be6727d21"
+ integrity sha512-XAjs8AUA0YMfYD91cT6pLGALwbsPS64s2WBHyULqL1m0gTqXqaUSLK1P7qA/Q8HecN0RFbqlM2tPO8bmZXP0YQ==
+ dependencies:
+ "@types/d3-drag" "^3.0.7"
+ "@types/d3-selection" "^3.0.10"
+ "@types/d3-transition" "^3.0.8"
+ "@types/d3-zoom" "^3.0.8"
+ d3-drag "^3.0.0"
+ d3-selection "^3.0.0"
+ d3-zoom "^3.0.0"
+
"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
@@ -8244,6 +8322,11 @@ cjs-module-lexer@^1.0.0, cjs-module-lexer@^1.2.2:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==
+classcat@^5.0.4:
+ version "5.0.5"
+ resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77"
+ integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==
+
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@@ -9159,6 +9242,68 @@ curlconverter@3.21.0:
string.prototype.startswith "^1.0.0"
yamljs "^0.3.0"
+"d3-color@1 - 3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
+ integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
+
+"d3-dispatch@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
+ integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
+
+"d3-drag@2 - 3", d3-drag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
+ integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-selection "3"
+
+"d3-ease@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+ integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+"d3-interpolate@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+ integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+ dependencies:
+ d3-color "1 - 3"
+
+"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
+ integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
+
+"d3-timer@1 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+ integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
+"d3-transition@2 - 3":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
+ integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
+ dependencies:
+ d3-color "1 - 3"
+ d3-dispatch "1 - 3"
+ d3-ease "1 - 3"
+ d3-interpolate "1 - 3"
+ d3-timer "1 - 3"
+
+d3-zoom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
+ integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
+ dependencies:
+ d3-dispatch "1 - 3"
+ d3-drag "2 - 3"
+ d3-interpolate "1 - 3"
+ d3-selection "2 - 3"
+ d3-transition "2 - 3"
+
dargs@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"