diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3825180bb..fa45bea27 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@ version: 2.1
orbs:
snyk: snyk/snyk@2.0.3
- codecov: codecov/codecov@4.0.0
+ codecov: codecov/codecov@4.1.0
workflows:
test-build:
diff --git a/README.md b/README.md
index c388b4abe..e929eabad 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,9 @@ What is Speckle? Check our [](https://app.speckle.systems) ⇒ creating an account
-- [](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click
+- [](https://app.speckle.systems) ⇒ Create an account at app.speckle.systems
+- [](<[https://](https://speckle.guide/dev/server-manualsetup.html)>) ⇒ Deploy on your own infrastructure with Docker Compose
+- [](<[https://](https://speckle.guide/dev/server-setup-k8s.html)>) ⇒ Deploy on your own infrastructure with Kubernetes
## Resources
diff --git a/package.json b/package.json
index 5a491c5bc..9fa9db991 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,8 @@
"dev:kind:helm:ci": "tilt ci --file ./.circleci/deployment/Tiltfile.helm --context kind-speckle-server --timeout 10m",
"dev": "yarn workspaces foreach -pivW -j unlimited run dev",
"dev:no-server": "yarn workspaces foreach --exclude @speckle/server -pivW -j unlimited run dev",
- "dev:minimal": "yarn workspaces foreach -pivW -j unlimited --include '{@speckle/server,@speckle/frontend,@speckle/shared}' run dev",
- "gqlgen": "yarn workspaces foreach -pivW -j unlimited --include '{@speckle/server,@speckle/frontend,@speckle/frontend-2}' run gqlgen",
+ "dev:minimal": "yarn workspaces foreach -pivW -j unlimited --include '{@speckle/server,@speckle/frontend-2}' run dev",
+ "gqlgen": "yarn workspaces foreach -pivW -j unlimited --include '{@speckle/server,@speckle/frontend,@speckle/frontend-2,@speckle/dui3}' run gqlgen",
"dev:server": "yarn workspace @speckle/server dev",
"dev:frontend": "yarn workspace @speckle/frontend dev",
"dev:frontend-2": "yarn workspace @speckle/frontend-2 dev",
@@ -65,35 +65,32 @@
"zx": "^8.1.2"
},
"resolutions": {
- "@babel/traverse": ">=7.23.2",
+ "@aws-sdk/client-sts/fast-xml-parser": ">=4.2.5",
+ "@aws-sdk/client-s3/fast-xml-parser": ">=4.2.5",
+ "@bull-board/express/express": ">=4.19.2",
+ "@datadog/datadog-ci/ws": "^7.5.10",
"@microsoft/api-extractor/semver": "^7.5.4",
"@rushstack/node-core-library/semver": "^7.5.4",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@typescript-eslint/parser": "^7.12.0",
- "typescript-eslint": "^7.12.0",
"@types/react": "file:./packages/frontend-2/type-augmentations/stubs/types__react",
- "axios": ">=1.6.0",
"core-js": "3.22.4",
"core-js-compat/semver": "^7.5.4",
"eslint": "^9.4.0",
"eslint-config-prettier": "^9.1.0",
- "express": ">=4.19.2",
- "fast-xml-parser": ">=4.2.5",
"graphql": "^15.3.0",
"levelup/bl": ">=1.2.3",
"levelup/semver": ">=5.7.2",
+ "mocha/serialize-javascript": ">=6.0.2",
"prettier": "^2.8.7",
- "serialize-javascript": ">=6.0.2",
+ "puppeteer-core/ws": "^8.17.1",
+ "request/tough-cookie": ">=4.1.3",
+ "rollup-plugin-terser/serialize-javascript": ">=6.0.2",
"simple-update-notifier/semver": "^7.5.4",
- "tough-cookie": ">=4.1.3",
"tslib": "^2.3.1",
"typescript": "^5.2.2",
- "undici": "^5.28.4",
- "wait-on": ">=7.2.0",
- "word-wrap": "npm:@aashutoshrathi/word-wrap@^1.2.4",
- "xml2js": ">=0.5.0",
- "puppeteer-core/ws": "^8.17.1",
- "@datadog/datadog-ci/ws": "^7.5.10"
+ "typescript-eslint": "^7.12.0",
+ "wait-on": ">=7.2.0"
},
"config": {
"commitizen": {
diff --git a/packages/frontend-2/components/projects/Dashboard.vue b/packages/frontend-2/components/projects/Dashboard.vue
index 52f4c5a83..e294c1926 100644
--- a/packages/frontend-2/components/projects/Dashboard.vue
+++ b/packages/frontend-2/components/projects/Dashboard.vue
@@ -18,10 +18,6 @@
-
- Test error
-
-
([
}
])
-const route = useRoute()
const { activeUser, isGuest } = useActiveUser()
const { triggerNotification } = useGlobalToast()
const areQueriesLoading = useQueryLoading()
@@ -160,8 +155,6 @@ const { onResult: onUserProjectsUpdate } = useSubscription(
onUserProjectsUpdateSubscription
)
-const showErrorTest = computed(() => route.query.showErrorButton === '1')
-
const projects = computed(() => projectsPanelResult.value?.activeUser?.projects)
const showEmptyState = computed(() => {
const isFiltering =
@@ -340,8 +333,4 @@ const clearSearch = () => {
selectedRoles.value = []
updateSearchImmediately()
}
-
-const testError = () => {
- throw new Error('what duhh hell')
-}
diff --git a/packages/frontend-2/components/viewer/compare-changes/Panel.vue b/packages/frontend-2/components/viewer/compare-changes/Panel.vue
index 725aab0c2..dccf00e71 100644
--- a/packages/frontend-2/components/viewer/compare-changes/Panel.vue
+++ b/packages/frontend-2/components/viewer/compare-changes/Panel.vue
@@ -66,7 +66,7 @@ import { ChevronLeftIcon } from '@heroicons/vue/24/solid'
import { VisualDiffMode } from '@speckle/viewer'
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
import { uniqBy, debounce } from 'lodash-es'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~~/lib/viewer/helpers/sceneExplorer'
import { useMixpanel } from '~~/lib/core/composables/mp'
defineEmits<{
diff --git a/packages/frontend-2/components/viewer/explorer/Explorer.vue b/packages/frontend-2/components/viewer/explorer/Explorer.vue
index db9277dc1..87f04d706 100644
--- a/packages/frontend-2/components/viewer/explorer/Explorer.vue
+++ b/packages/frontend-2/components/viewer/explorer/Explorer.vue
@@ -53,8 +53,7 @@
class="bg-foundation rounded-lg"
>
{
if (!worldTree.value) return []
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
expandLevel.value = -1
- const nodes = []
const rootNodes = worldTree.value._root.children as ExplorerNode[]
+
+ const results: Record = {}
+ const unmatchedNodes: ExplorerNode[] = []
+
for (const node of rootNodes) {
const objectId = ((node.model as Record).id as string)
.split('/')
.reverse()[0] as string
- const resourceItem = resourceItems.value.find((res) => res.objectId === objectId)
+ const resourceItemIdx = resourceItems.value.findIndex(
+ (res) => res.objectId === objectId
+ )
+ const resourceItem =
+ resourceItemIdx !== -1 ? resourceItems.value[resourceItemIdx] : null
+
const raw = node.model?.raw as Record
if (resourceItem?.modelId) {
// Model resource
@@ -140,9 +150,24 @@ const rootNodes = computed(() => {
raw.name = 'Object'
raw.type = 'Single Object'
}
- nodes.push(node.model as ExplorerNode)
+
+ const res = node.model as ExplorerNode
+ if (resourceItem) {
+ ;(results[resourceItemIdx] = results[resourceItemIdx] || []).push(res)
+ } else {
+ unmatchedNodes.push(res)
+ }
}
- return nodes
+ const nodes = [
+ ...flatten(sortBy(Object.entries(results), (i) => i[0]).map((i) => i[1])),
+ ...unmatchedNodes
+ ]
+
+ return nodes.map(
+ (n): TreeItemComponentModel => ({
+ rawNode: markRaw(n)
+ })
+ )
})
diff --git a/packages/frontend-2/components/viewer/explorer/Filters.vue b/packages/frontend-2/components/viewer/explorer/Filters.vue
index f73b27bf9..741610108 100644
--- a/packages/frontend-2/components/viewer/explorer/Filters.vue
+++ b/packages/frontend-2/components/viewer/explorer/Filters.vue
@@ -70,7 +70,7 @@
refreshColorsIfSetOrActiveFilterIsNumeric()
"
>
- {{ filter.key }}
+ {{ getPropertyName(filter.key) }}
@@ -82,11 +82,11 @@
@@ -95,13 +95,13 @@
diff --git a/packages/frontend-2/components/viewer/explorer/StringFilterItem.vue b/packages/frontend-2/components/viewer/explorer/StringFilterItem.vue
index c9c64aa2c..f1b02bd4a 100644
--- a/packages/frontend-2/components/viewer/explorer/StringFilterItem.vue
+++ b/packages/frontend-2/components/viewer/explorer/StringFilterItem.vue
@@ -2,10 +2,11 @@
-
@@ -15,7 +16,7 @@
:style="`background-color: #${color};`"
>
- {{ item.value.split('.').reverse()[0] || item.value || 'No Name' }}
+ {{ item.value || 'No Name' }}
-
+
-
+
-
+
props.treeItem.atomic === true)
-const speckleData = props.treeItem?.raw as SpeckleObject
-const rawSpeckleData = props.treeItem?.raw as SpeckleObject
+const isAtomic = computed(() => props.treeItem.rawNode.atomic === true)
+const rawSpeckleData = computed(() => props.treeItem?.rawNode.raw as SpeckleObject)
+const speckleData = rawSpeckleData
function getNestedModelHeader(name: string): string {
const parts = name.split('/')
@@ -196,7 +203,9 @@ function getNestedModelHeader(name: string): string {
}
const headerAndSubheader = computed(() => {
- const { header, subheader } = getHeaderAndSubheaderForSpeckleObject(rawSpeckleData)
+ const { header, subheader } = getHeaderAndSubheaderForSpeckleObject(
+ rawSpeckleData.value
+ )
return {
header: getNestedModelHeader(header),
subheader
@@ -204,32 +213,45 @@ const headerAndSubheader = computed(() => {
})
const childrenLength = computed(() => {
- if (rawSpeckleData.elements && Array.isArray(rawSpeckleData.elements))
- return rawSpeckleData.elements.length
- if (rawSpeckleData.children && Array.isArray(rawSpeckleData.children))
- return rawSpeckleData.children.length
+ if (rawSpeckleData.value.elements && Array.isArray(rawSpeckleData.value.elements))
+ return rawSpeckleData.value.elements.length
+ if (rawSpeckleData.value.children && Array.isArray(rawSpeckleData.value.children))
+ return rawSpeckleData.value.children.length
return 0
})
const isSingleCollection = computed(() => {
return (
- isNonEmptyObjectArray(speckleData.children) ||
- isNonEmptyObjectArray(speckleData.elements)
+ isNonEmptyObjectArray(speckleData.value.children) ||
+ isNonEmptyObjectArray(speckleData.value.elements)
)
})
const singleCollectionItems = computed(() => {
- const treeItems = props.treeItem.children.filter((child) => !!child.raw?.id) // filter out random tree children (no id means they're not actual objects)
+ const treeItems = props.treeItem.rawNode.children.filter(
+ (child) => !!child.raw?.id && isAllowedType(child)
+ // filter out random tree children (no id means they're not actual objects)
+ )
// Handle the case of a wall, roof or other atomic objects that have nested children
- if (isNonEmptyObjectArray(speckleData.elements) && isAtomic.value) {
+ if (isNonEmptyObjectArray(speckleData.value.elements) && isAtomic.value) {
// We need to filter out children that are not direct descendants of `elements`
// Note: this is a current assumption convention.
- const ids = (speckleData.elements as SpeckleReference[]).map(
+ const ids = (speckleData.value.elements as SpeckleReference[]).map(
(obj) => obj.referencedId
)
- return treeItems.filter((item) => ids.includes(item.raw?.id as string))
+ return treeItems
+ .filter((item) => ids.includes(item.raw?.id as string))
+ .map(
+ (i): TreeItemComponentModel => ({
+ rawNode: i
+ })
+ )
}
- return treeItems
+ return treeItems.map(
+ (i): TreeItemComponentModel => ({
+ rawNode: i
+ })
+ )
})
const itemCount = ref(10)
@@ -242,17 +264,17 @@ const singleCollectionItemsPaginated = computed(() => {
// object { @boat: [obj, obj, obj], @harbour: [obj, obj, obj], etc. }
// @boat and @harbour would ideally be model collections, but, alas, connectors don't have that yet.
const arrayCollections = computed(() => {
- const arr = [] as ExplorerNode[]
+ const arr = [] as TreeItemComponentModel[]
for (const k of Object.keys(rawSpeckleData)) {
if (k === 'children' || k === 'elements' || k.includes('displayValue')) continue
- const val = rawSpeckleData[k] as SpeckleReference[]
+ const val = rawSpeckleData.value[k] as SpeckleReference[]
if (!isNonEmptyObjectArray(val)) continue
const ids = val.map((ref) => ref.referencedId) // NOTE: we're assuming all collections have refs inside; might revisit/to think re edge cases
- const actualRawRefs = props.treeItem.children.filter((node) =>
- ids.includes(node.raw?.id as string)
+ const actualRawRefs = props.treeItem.rawNode.children.filter(
+ (node) => ids.includes(node.raw?.id as string) && isAllowedType(node)
)
if (actualRawRefs.length === 0) continue // bypasses chunks: if the actual object is not part of the tree item's children, it means it's a sublimated type (ie, a chunk). the assumption we're making is that any list of actual atomic objects is not chunked.
@@ -267,7 +289,9 @@ const arrayCollections = computed(() => {
children: actualRawRefs,
expanded: false
}
- arr.push(modelCollectionItem)
+ arr.push({
+ rawNode: modelCollectionItem
+ })
}
return arr
@@ -283,6 +307,9 @@ const isNonEmptyObjectArray = (x: unknown) => isNonEmptyArray(x) && isObject(x[0
const isObject = (x: unknown) =>
typeof x === 'object' && !Array.isArray(x) && x !== null
+const isAllowedType = (node: ExplorerNode) =>
+ !['Objects.Other.DisplayStyle'].includes(node.raw?.speckle_type || '')
+
const unfold = ref(false)
// NOTE: not happy with how unfolding and collapsing panned out :(
@@ -320,7 +347,7 @@ const manualUnfoldToggle = () => {
}
const isSelected = computed(() => {
- return !!objects.value.find((o) => o.id === speckleData.id)
+ return !!objects.value.find((o) => o.id === speckleData.value.id)
})
const setSelection = (e: MouseEvent) => {
@@ -329,19 +356,19 @@ const setSelection = (e: MouseEvent) => {
return
}
if (isSelected.value && e.shiftKey) {
- removeFromSelection(rawSpeckleData)
+ removeFromSelection(rawSpeckleData.value)
return
}
if (!e.shiftKey) clearSelection()
- addToSelection(rawSpeckleData)
+ addToSelection(rawSpeckleData.value)
}
const highlightObject = () => {
- highlightObjects(getTargetObjectIds(rawSpeckleData))
+ highlightObjects(getTargetObjectIds(rawSpeckleData.value))
}
const unhighlightObject = () => {
- unhighlightObjects(getTargetObjectIds(rawSpeckleData))
+ unhighlightObjects(getTargetObjectIds(rawSpeckleData.value))
}
const hiddenObjects = computed(() => filteringState.value?.hiddenObjects)
@@ -349,7 +376,7 @@ const isolatedObjects = computed(() => filteringState.value?.isolatedObjects)
const isHidden = computed(() => {
if (!hiddenObjects.value) return false
- const ids = getTargetObjectIds(rawSpeckleData)
+ const ids = getTargetObjectIds(rawSpeckleData.value)
return containsAll(ids, hiddenObjects.value)
})
@@ -360,12 +387,12 @@ const stateHasIsolatedObjectsInGeneral = computed(() => {
const isIsolated = computed(() => {
if (!isolatedObjects.value) return false
- const ids = getTargetObjectIds(rawSpeckleData)
+ const ids = getTargetObjectIds(rawSpeckleData.value)
return containsAll(ids, isolatedObjects.value)
})
const hideOrShowObject = () => {
- const ids = getTargetObjectIds(rawSpeckleData)
+ const ids = getTargetObjectIds(rawSpeckleData.value)
if (!isHidden.value) {
hideObjects(ids)
return
@@ -375,7 +402,7 @@ const hideOrShowObject = () => {
}
const isolateOrUnisolateObject = () => {
- const ids = getTargetObjectIds(rawSpeckleData)
+ const ids = getTargetObjectIds(rawSpeckleData.value)
if (!isIsolated.value) {
isolateObjects(ids)
return
diff --git a/packages/frontend-2/components/viewer/selection/Object.vue b/packages/frontend-2/components/viewer/selection/Object.vue
index 5fe966e91..aa2dcadfd 100644
--- a/packages/frontend-2/components/viewer/selection/Object.vue
+++ b/packages/frontend-2/components/viewer/selection/Object.vue
@@ -131,7 +131,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { ChevronDownIcon } from '@heroicons/vue/24/solid'
import { ClipboardDocumentIcon } from '@heroicons/vue/24/outline'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~~/lib/viewer/helpers/sceneExplorer'
import { getHeaderAndSubheaderForSpeckleObject } from '~~/lib/object-sidebar/helpers'
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
import { useHighlightedObjectsUtilities } from '~/lib/viewer/composables/ui'
diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts
index be7694819..ac1b09012 100644
--- a/packages/frontend-2/lib/common/generated/gql/graphql.ts
+++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts
@@ -375,62 +375,6 @@ export type AutomationCollection = {
totalCount: Scalars['Int']['output'];
};
-export type AutomationCreateInput = {
- automationId: Scalars['String']['input'];
- automationName: Scalars['String']['input'];
- automationRevisionId: Scalars['String']['input'];
- modelId: Scalars['String']['input'];
- projectId: Scalars['String']['input'];
- webhookId?: InputMaybe;
-};
-
-export type AutomationFunctionRun = {
- __typename?: 'AutomationFunctionRun';
- contextView?: Maybe;
- elapsed: Scalars['Float']['output'];
- functionId: Scalars['String']['output'];
- functionLogo?: Maybe;
- functionName: Scalars['String']['output'];
- id: Scalars['ID']['output'];
- resultVersions: Array;
- /**
- * NOTE: this is the schema for the results field below!
- * Current schema: {
- * version: "1.0.0",
- * values: {
- * objectResults: Record | null
- * visualoverrides: Records | null
- * }[]>
- * blobIds?: string[]
- * }
- * }
- */
- results?: Maybe;
- status: AutomationRunStatus;
- statusMessage?: Maybe;
-};
-
-export type AutomationMutations = {
- __typename?: 'AutomationMutations';
- create: Scalars['Boolean']['output'];
- functionRunStatusReport: Scalars['Boolean']['output'];
-};
-
-
-export type AutomationMutationsCreateArgs = {
- input: AutomationCreateInput;
-};
-
-
-export type AutomationMutationsFunctionRunStatusReportArgs = {
- input: AutomationRunStatusUpdateInput;
-};
-
export type AutomationRevision = {
__typename?: 'AutomationRevision';
functions: Array;
@@ -454,44 +398,8 @@ export type AutomationRevisionFunction = {
export type AutomationRevisionTriggerDefinition = VersionCreatedTriggerDefinition;
-export type AutomationRun = {
- __typename?: 'AutomationRun';
- automationId: Scalars['String']['output'];
- automationName: Scalars['String']['output'];
- createdAt: Scalars['DateTime']['output'];
- functionRuns: Array;
- id: Scalars['ID']['output'];
- /** Resolved from all function run statuses */
- status: AutomationRunStatus;
- updatedAt: Scalars['DateTime']['output'];
- versionId: Scalars['String']['output'];
-};
-
-export enum AutomationRunStatus {
- Failed = 'FAILED',
- Initializing = 'INITIALIZING',
- Running = 'RUNNING',
- Succeeded = 'SUCCEEDED'
-}
-
-export type AutomationRunStatusUpdateInput = {
- automationId: Scalars['String']['input'];
- automationRevisionId: Scalars['String']['input'];
- automationRunId: Scalars['String']['input'];
- functionRuns: Array;
- versionId: Scalars['String']['input'];
-};
-
export type AutomationRunTrigger = VersionCreatedTrigger;
-export type AutomationsStatus = {
- __typename?: 'AutomationsStatus';
- automationRuns: Array;
- id: Scalars['ID']['output'];
- status: AutomationRunStatus;
- statusMessage?: Maybe;
-};
-
export type AvatarUser = {
__typename?: 'AvatarUser';
avatar?: Maybe;
@@ -936,27 +844,6 @@ export type FileUpload = {
userId: Scalars['String']['output'];
};
-export type FunctionRunStatusInput = {
- contextView?: InputMaybe;
- elapsed: Scalars['Float']['input'];
- functionId: Scalars['String']['input'];
- functionLogo?: InputMaybe;
- functionName: Scalars['String']['input'];
- resultVersionIds: Array;
- /**
- * Current schema: {
- * version: "1.0.0",
- * values: {
- * speckleObjects: Record
- * blobIds?: string[]
- * }
- * }
- */
- results?: InputMaybe;
- status: AutomationRunStatus;
- statusMessage?: InputMaybe;
-};
-
export type GendoAiRender = {
__typename?: 'GendoAIRender';
camera?: Maybe;
@@ -1082,7 +969,6 @@ export type LimitedUserTimelineArgs = {
export type Model = {
__typename?: 'Model';
author: LimitedUser;
- automationStatus?: Maybe;
automationsStatus?: Maybe;
/** Return a model tree of children */
childrenTree: Array;
@@ -1215,7 +1101,6 @@ export type Mutation = {
appUpdate: Scalars['Boolean']['output'];
automateFunctionRunStatusReport: Scalars['Boolean']['output'];
automateMutations: AutomateMutations;
- automationMutations: AutomationMutations;
branchCreate: Scalars['String']['output'];
branchDelete: Scalars['Boolean']['output'];
branchUpdate: Scalars['Boolean']['output'];
@@ -1901,14 +1786,6 @@ export type ProjectAutomationUpdateInput = {
name?: InputMaybe;
};
-export type ProjectAutomationsStatusUpdatedMessage = {
- __typename?: 'ProjectAutomationsStatusUpdatedMessage';
- model: Model;
- project: Project;
- status: AutomationsStatus;
- version: Version;
-};
-
export type ProjectAutomationsUpdatedMessage = {
__typename?: 'ProjectAutomationsUpdatedMessage';
automation?: Maybe;
@@ -2875,7 +2752,6 @@ export type Subscription = {
commitDeleted?: Maybe;
/** Subscribe to commit updated event. */
commitUpdated?: Maybe;
- projectAutomationsStatusUpdated: ProjectAutomationsStatusUpdatedMessage;
/** Subscribe to updates to automations in the project */
projectAutomationsUpdated: ProjectAutomationsUpdatedMessage;
/**
@@ -2971,11 +2847,6 @@ export type SubscriptionCommitUpdatedArgs = {
};
-export type SubscriptionProjectAutomationsStatusUpdatedArgs = {
- projectId: Scalars['String']['input'];
-};
-
-
export type SubscriptionProjectAutomationsUpdatedArgs = {
projectId: Scalars['String']['input'];
};
@@ -3317,7 +3188,6 @@ export type UserUpdateInput = {
export type Version = {
__typename?: 'Version';
authorUser?: Maybe;
- automationStatus?: Maybe;
automationsStatus?: Maybe;
/** All comment threads in this version */
commentThreads: CommentCollection;
diff --git a/packages/frontend-2/lib/common/helpers/sceneExplorer.ts b/packages/frontend-2/lib/common/helpers/sceneExplorer.ts
deleted file mode 100644
index 2f2a812c5..000000000
--- a/packages/frontend-2/lib/common/helpers/sceneExplorer.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { type SpeckleObject, type SpeckleReference } from '@speckle/viewer'
-
-// Note: minor typing hacks for less squiggly lines in the explorer.
-// TODO: ask alex re viewer data tree types
-
-export type ExplorerNode = {
- guid?: string
- data?: SpeckleObject
- raw?: SpeckleObject
- atomic?: boolean
- model?: Record & { id?: string }
- children: ExplorerNode[]
-}
-
-export type { SpeckleObject, SpeckleReference }
diff --git a/packages/frontend-2/lib/core/composables/error.ts b/packages/frontend-2/lib/core/composables/error.ts
index 1f97fca50..c7355eaad 100644
--- a/packages/frontend-2/lib/core/composables/error.ts
+++ b/packages/frontend-2/lib/core/composables/error.ts
@@ -1,9 +1,10 @@
import { useScopedState } from '~~/lib/common/composables/scopedState'
import * as Observability from '@speckle/shared/dist/esm/observability/index'
-import type {
- AbstractErrorHandler,
- AbstractErrorHandlerParams,
- AbstractUnhandledErrorHandler
+import {
+ prettify,
+ type AbstractErrorHandler,
+ type AbstractErrorHandlerParams,
+ type AbstractUnhandledErrorHandler
} from '~/lib/core/helpers/observability'
const ENTER_STATE_AT_ERRORS_PER_MIN = 100
@@ -65,7 +66,11 @@ export const useGetErrorLoggingTransports = () => {
export const useLogToErrorLoggingTransports = () => {
const transports = useGetErrorLoggingTransports()
const invokeTransportsWithPayload = (payload: AbstractErrorHandlerParams) => {
- transports.forEach((handler) => handler.onError(payload))
+ transports.forEach((handler) =>
+ handler.onError(payload, {
+ prettifyMessage: (msg) => prettify(payload.otherData || {}, msg)
+ })
+ )
}
return {
diff --git a/packages/frontend-2/lib/core/configs/apollo.ts b/packages/frontend-2/lib/core/configs/apollo.ts
index 997571498..d5deb8d9c 100644
--- a/packages/frontend-2/lib/core/configs/apollo.ts
+++ b/packages/frontend-2/lib/core/configs/apollo.ts
@@ -8,7 +8,7 @@ import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
import { Kind } from 'graphql'
-import type { OperationDefinitionNode } from 'graphql'
+import type { GraphQLError, OperationDefinitionNode } from 'graphql'
import type { CookieRef, NuxtApp } from '#app'
import type { Optional } from '@speckle/shared'
import { useAuthCookie } from '~~/lib/auth/composables/auth'
@@ -22,7 +22,7 @@ import { onError } from '@apollo/client/link/error'
import { useNavigateToLogin, loginRoute } from '~~/lib/common/helpers/route'
import { useAppErrorState } from '~~/lib/core/composables/error'
import { isInvalidAuth } from '~~/lib/common/helpers/graphql'
-import { isBoolean, omit } from 'lodash-es'
+import { isArray, isBoolean, omit } from 'lodash-es'
import { useRequestId } from '~/lib/core/composables/server'
const appName = 'frontend-2'
@@ -337,12 +337,15 @@ function createLink(params: {
? skipLoggingErrors
: skipLoggingErrors?.(res)
if (!isSubTokenMissingError && !shouldSkip) {
- const errMsg = res.networkError?.message || res.graphQLErrors?.[0]?.message
+ const gqlErrors: Array = isArray(res.graphQLErrors)
+ ? res.graphQLErrors
+ : []
+ const errMsg = res.networkError?.message || gqlErrors[0]?.message
logger.error(
{
...omit(res, ['forward', 'response']),
networkErrorMessage: res.networkError?.message,
- gqlErrorMessages: res.graphQLErrors?.map((e) => e.message),
+ gqlErrorMessages: gqlErrors.map((e) => e.message),
errorMessage: errMsg,
graphql: true
},
diff --git a/packages/frontend-2/lib/core/helpers/observability.ts b/packages/frontend-2/lib/core/helpers/observability.ts
index 2a82f0ae9..b2aa8bf58 100644
--- a/packages/frontend-2/lib/core/helpers/observability.ts
+++ b/packages/frontend-2/lib/core/helpers/observability.ts
@@ -19,7 +19,7 @@ import type { Logger } from 'pino'
/**
* Add pino-pretty like formatting
*/
-const prettify = (log: object, msg: string) =>
+export const prettify = (log: object, msg: string) =>
msg.replace(/{([^{}]+)}/g, (match: string, p1: string) => {
const val = get(log, p1)
if (val === undefined) return match
@@ -33,7 +33,7 @@ const prettify = (log: object, msg: string) =>
* Wrap any logger call w/ logic that prettifies the error message like pino-pretty does
* and emits bindings if they are provided
*/
-const log =
+const prettifiedLoggerFactory =
(logger: (...args: unknown[]) => void, bindings?: () => Record) =>
(...vals: unknown[]) => {
const finalVals = vals.slice()
@@ -72,16 +72,16 @@ export function buildFakePinoLogger(
const errLogger = (...args: unknown[]) => {
const { onError } = options || {}
if (onError) onError(...args)
- log(console.error, bindings)(...args)
+ prettifiedLoggerFactory(console.error, bindings)(...args)
}
const logger = {
- debug: log(console.debug, bindings),
- info: log(console.info, bindings),
- warn: log(console.warn, bindings),
+ debug: prettifiedLoggerFactory(console.debug, bindings),
+ info: prettifiedLoggerFactory(console.info, bindings),
+ warn: prettifiedLoggerFactory(console.warn, bindings),
error: errLogger,
fatal: errLogger,
- trace: log(console.trace, bindings),
+ trace: prettifiedLoggerFactory(console.trace, bindings),
silent: noop
} as unknown as ReturnType
@@ -121,13 +121,18 @@ export const formatAppError = (err: SimpleError) => {
}
}
-export type AbstractErrorHandler = (params: {
- args: unknown[]
- firstString: Optional
- firstError: Optional
- otherData: Record
- nonObjectOtherData: unknown[]
-}) => void
+export type AbstractErrorHandler = (
+ params: {
+ args: unknown[]
+ firstString: Optional
+ firstError: Optional
+ otherData: Record
+ nonObjectOtherData: unknown[]
+ },
+ helpers: {
+ prettifyMessage: (msg: string) => string
+ }
+) => void
export type AbstractUnhandledErrorHandler = (params: {
event: ErrorEvent | PromiseRejectionEvent
@@ -175,13 +180,16 @@ export function enableCustomErrorHandling(params: {
{},
...otherDataObjects
) as Record
- onError({
- args,
- firstError,
- firstString,
- otherData: mergedOtherDataObject,
- nonObjectOtherData: otherDataNonObjects
- })
+ onError(
+ {
+ args,
+ firstError,
+ firstString,
+ otherData: mergedOtherDataObject,
+ nonObjectOtherData: otherDataNonObjects
+ },
+ { prettifyMessage: (msg) => prettify(mergedOtherDataObject, msg) }
+ )
}
return log(...args)
diff --git a/packages/frontend-2/lib/object-sidebar/helpers.ts b/packages/frontend-2/lib/object-sidebar/helpers.ts
index c74e4d17e..0847a0c93 100644
--- a/packages/frontend-2/lib/object-sidebar/helpers.ts
+++ b/packages/frontend-2/lib/object-sidebar/helpers.ts
@@ -1,4 +1,4 @@
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
export type HeaderSubheader = {
header: string
diff --git a/packages/frontend-2/lib/viewer/composables/setup.ts b/packages/frontend-2/lib/viewer/composables/setup.ts
index 5bf8cd0d0..654f59533 100644
--- a/packages/frontend-2/lib/viewer/composables/setup.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup.ts
@@ -49,7 +49,7 @@ import { nanoid } from 'nanoid'
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
import type { CommentBubbleModel } from '~~/lib/viewer/composables/commentBubbles'
import { setupUrlHashState } from '~~/lib/viewer/composables/setup/urlHashState'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
import type { Box3 } from 'three'
import { Vector3 } from 'three'
import { writableAsyncComputed } from '~~/lib/common/composables/async'
@@ -111,11 +111,18 @@ export type InjectableViewerState = Readonly<{
* Various values that represent the current Viewer instance state
*/
metadata: {
+ /**
+ * Based on a shallow ref
+ */
worldTree: ComputedRef>
availableFilters: ComputedRef>
views: ComputedRef
filteringState: ComputedRef>
}
+ /**
+ * Whether the Viewer has finished doing the initial object loading
+ */
+ hasDoneInitialLoad: Ref
}
/**
* Loaded/loadable resources
@@ -395,6 +402,7 @@ function setupInitialState(params: UseSetupViewerParams): InitialSetupState {
createViewerDataBuilder({ viewerDebug })
) || { initPromise: Promise.resolve() }
initPromise.then(() => (isInitialized.value = true))
+ const hasDoneInitialLoad = ref(false)
return {
projectId,
@@ -412,7 +420,8 @@ function setupInitialState(params: UseSetupViewerParams): InitialSetupState {
availableFilters: computed(() => undefined),
views: computed(() => []),
filteringState: computed(() => undefined)
- }
+ },
+ hasDoneInitialLoad
} as unknown as InitialSetupState['viewer'])
: {
instance,
@@ -421,7 +430,8 @@ function setupInitialState(params: UseSetupViewerParams): InitialSetupState {
promise: initPromise,
ref: computed(() => isInitialized.value)
},
- metadata: setupViewerMetadata({ viewer: instance })
+ metadata: setupViewerMetadata({ viewer: instance }),
+ hasDoneInitialLoad
},
urlHashState: setupUrlHashState()
}
diff --git a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
index 2d1759984..bd319d124 100644
--- a/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup/postSetup.ts
@@ -82,7 +82,8 @@ function useViewerObjectAutoLoading() {
projectId,
viewer: {
instance: viewer,
- init: { ref: isInitialized }
+ init: { ref: isInitialized },
+ hasDoneInitialLoad
},
resources: {
response: { resourceItems }
@@ -112,21 +113,24 @@ function useViewerObjectAutoLoading() {
uniq(resourceItems.map((i) => i.objectId))
watch(
- () => [resourceItems.value, isInitialized.value],
- async ([newResources, newIsInitialized], oldData) => {
+ () => [resourceItems.value, isInitialized.value, hasDoneInitialLoad.value],
+ async ([newResources, newIsInitialized, newHasDoneInitialLoad], oldData) => {
// Wait till viewer loaded in
if (!newIsInitialized) return
- const [oldResources, oldIsInitialized] = oldData || [[], false]
+ const [oldResources] = oldData || [[], false]
const zoomToObject = !focusedThreadId.value // we want to zoom to the thread instead
// Viewer initialized - load in all resources
- if (newIsInitialized && !oldIsInitialized) {
+ if (!newHasDoneInitialLoad) {
const allObjectIds = getUniqueObjectIds(newResources)
- await Promise.all(
+ const res = await Promise.all(
allObjectIds.map((i) => loadObject(i, false, { zoomToObject }))
)
+ if (res.length) {
+ hasDoneInitialLoad.value = true
+ }
return
}
@@ -139,7 +143,7 @@ function useViewerObjectAutoLoading() {
await Promise.all(removableObjectIds.map((i) => loadObject(i, true)))
await Promise.all(
- addableObjectIds.map((i) => loadObject(i, false, { zoomToObject }))
+ addableObjectIds.map((i) => loadObject(i, false, { zoomToObject: false }))
)
},
{ deep: true, immediate: true }
diff --git a/packages/frontend-2/lib/viewer/composables/setup/selection.ts b/packages/frontend-2/lib/viewer/composables/setup/selection.ts
index 172c4d64e..9459bc033 100644
--- a/packages/frontend-2/lib/viewer/composables/setup/selection.ts
+++ b/packages/frontend-2/lib/viewer/composables/setup/selection.ts
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { MeasurementType } from '@speckle/viewer'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
import { useCameraUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui'
diff --git a/packages/frontend-2/lib/viewer/composables/ui.ts b/packages/frontend-2/lib/viewer/composables/ui.ts
index 3332c42c4..328a0036c 100644
--- a/packages/frontend-2/lib/viewer/composables/ui.ts
+++ b/packages/frontend-2/lib/viewer/composables/ui.ts
@@ -4,7 +4,7 @@ import { CameraController, MeasurementsExtension } from '@speckle/viewer'
import { until } from '@vueuse/shared'
import { difference, isString, uniq } from 'lodash-es'
import { useEmbedState } from '~/lib/viewer/composables/setup/embed'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
import { isNonNullable } from '~~/lib/common/helpers/utils'
import {
useInjectedViewer,
diff --git a/packages/frontend-2/lib/viewer/composables/viewer.ts b/packages/frontend-2/lib/viewer/composables/viewer.ts
index 9cd5424a9..a329ddff4 100644
--- a/packages/frontend-2/lib/viewer/composables/viewer.ts
+++ b/packages/frontend-2/lib/viewer/composables/viewer.ts
@@ -16,7 +16,7 @@ import {
type SelectionEvent,
type TreeNode
} from '@speckle/viewer'
-import type { SpeckleObject } from '~~/lib/common/helpers/sceneExplorer'
+import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
// NOTE: this is a preformance optimisation - this function is hot, and has to do
// potentially large searches if many elements are hidden/isolated. We cache the
diff --git a/packages/frontend-2/lib/viewer/helpers/sceneExplorer.ts b/packages/frontend-2/lib/viewer/helpers/sceneExplorer.ts
new file mode 100644
index 000000000..5102f6d8e
--- /dev/null
+++ b/packages/frontend-2/lib/viewer/helpers/sceneExplorer.ts
@@ -0,0 +1,34 @@
+import type { MaybeNullOrUndefined } from '@speckle/shared'
+import {
+ type NumericPropertyInfo,
+ type PropertyInfo,
+ type SpeckleObject,
+ type SpeckleReference,
+ type StringPropertyInfo
+} from '@speckle/viewer'
+import type { Raw } from 'vue'
+
+export const isStringPropertyInfo = (
+ info: MaybeNullOrUndefined
+): info is StringPropertyInfo => info?.type === 'string'
+export const isNumericPropertyInfo = (
+ info: MaybeNullOrUndefined
+): info is NumericPropertyInfo => info?.type === 'number'
+
+// Note: minor typing hacks for less squiggly lines in the explorer.
+// TODO: ask alex re viewer data tree types
+
+export type ExplorerNode = {
+ guid?: string
+ data?: SpeckleObject
+ raw?: SpeckleObject
+ atomic?: boolean
+ model?: Record & { id?: string }
+ children: ExplorerNode[]
+}
+
+export type TreeItemComponentModel = {
+ rawNode: Raw
+}
+
+export type { SpeckleObject, SpeckleReference }
diff --git a/packages/frontend-2/plugins/001-logger.ts b/packages/frontend-2/plugins/001-logger.ts
index 633368f62..bab646709 100644
--- a/packages/frontend-2/plugins/001-logger.ts
+++ b/packages/frontend-2/plugins/001-logger.ts
@@ -136,6 +136,10 @@ export default defineNuxtPlugin(async (nuxtApp) => {
...collectMainInfo({ isBrowser: true })
})
+ logger = buildFakePinoLogger({
+ consoleBindings: logCsrEmitProps ? collectCoreInfo : undefined
+ })
+
// SEQ Browser integration
if (logClientApiToken?.length && logClientApiEndpoint?.length) {
const seq = await import('seq-logging/browser')
@@ -195,24 +199,15 @@ export default defineNuxtPlugin(async (nuxtApp) => {
})
}
errorHandlers.push(errorLogger)
-
- logger = buildFakePinoLogger({
- consoleBindings: logCsrEmitProps ? collectCoreInfo : undefined
- })
logger.debug('Set up seq ingestion...')
- } else {
- // No seq integration, fallback to basic console logging
- logger = buildFakePinoLogger({
- consoleBindings: logCsrEmitProps ? collectCoreInfo : undefined
- })
}
}
// Register seq transports, if any
if (errorHandlers.length) {
registerErrorTransport({
- onError: (params) => {
- errorHandlers.forEach((handler) => handler(params))
+ onError: (...params) => {
+ errorHandlers.forEach((handler) => handler(...params))
},
onUnhandledError: (event) => {
unhandledErrorHandlers.forEach((handler) => handler(event))
@@ -220,19 +215,19 @@ export default defineNuxtPlugin(async (nuxtApp) => {
})
}
- // Global error handler - handle all transports
+ // Global error handler - handle all transports besides the core pino/console.log logger
const transports = useGetErrorLoggingTransports()
let serverFatalError: Optional = undefined
logger = enableCustomErrorHandling({
logger,
- onError: (params) => {
+ onError: (params, helpers) => {
const { otherData } = params
if (import.meta.server && otherData?.isAppError) {
serverFatalError = params
}
- transports.forEach((handler) => handler.onError(params))
+ transports.forEach((handler) => handler.onError(params, helpers))
}
})
diff --git a/packages/frontend-2/plugins/002-rum.ts b/packages/frontend-2/plugins/002-rum.ts
index 490287ae6..91089625f 100644
--- a/packages/frontend-2/plugins/002-rum.ts
+++ b/packages/frontend-2/plugins/002-rum.ts
@@ -7,6 +7,7 @@ import type { Plugin } from 'nuxt/dist/app/nuxt'
import { isH3Error } from '~/lib/common/helpers/error'
import { useRequestId, useServerRequestId } from '~/lib/core/composables/server'
import { isBrave, isSafari } from '@speckle/shared'
+import { isString } from 'lodash-es'
type PluginNuxtApp = Parameters[0]
@@ -69,15 +70,28 @@ function initRumClient(app: PluginNuxtApp) {
: {}
registerErrorTransport({
- onError: ({ args, firstError, firstString, otherData, nonObjectOtherData }) => {
+ onError: (
+ { args, firstError, firstString, otherData, nonObjectOtherData },
+ { prettifyMessage }
+ ) => {
if (!datadog || !('addError' in datadog)) return
- const error = firstError || firstString || args[0]
+ let error = firstError || firstString || args[0]
+ const mainErrorMessageTemplate = firstString
+ const mainErrorMessage = mainErrorMessageTemplate
+ ? prettifyMessage(mainErrorMessageTemplate)
+ : undefined
+
+ if (isString(error)) {
+ error = prettifyMessage(error)
+ }
+
datadog.addError(error, {
...otherData,
...resolveH3Data(firstError),
extraData: nonObjectOtherData,
- mainErrorMessage: firstString,
+ mainErrorMessageTemplate,
+ mainErrorMessage,
isProperlySentError: true
})
},
diff --git a/packages/frontend/src/graphql/generated/graphql.ts b/packages/frontend/src/graphql/generated/graphql.ts
index 80ae4f93b..cea571cc3 100644
--- a/packages/frontend/src/graphql/generated/graphql.ts
+++ b/packages/frontend/src/graphql/generated/graphql.ts
@@ -5,26 +5,28 @@ export type InputMaybe = Maybe;
export type Exact = { [K in keyof T]: T[K] };
export type MakeOptional = Omit & { [SubKey in K]?: Maybe };
export type MakeMaybe = Omit & { [SubKey in K]: Maybe };
+export type MakeEmpty = { [_ in K]?: never };
+export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
- ID: string;
- String: string;
- Boolean: boolean;
- Int: number;
- Float: number;
+ ID: { input: string; output: string; }
+ String: { input: string; output: string; }
+ Boolean: { input: boolean; output: boolean; }
+ Int: { input: number; output: number; }
+ Float: { input: number; output: number; }
/** The `BigInt` scalar type represents non-fractional signed whole numeric values. */
- BigInt: any;
+ BigInt: { input: any; output: any; }
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
- DateTime: string;
- EmailAddress: any;
+ DateTime: { input: string; output: string; }
+ EmailAddress: { input: any; output: any; }
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
- JSONObject: Record;
+ JSONObject: { input: Record; output: Record; }
};
export type ActiveUserMutations = {
__typename?: 'ActiveUserMutations';
/** Mark onboarding as complete */
- finishOnboarding: Scalars['Boolean'];
+ finishOnboarding: Scalars['Boolean']['output'];
/** Edit a user's profile */
update: User;
};
@@ -36,29 +38,29 @@ export type ActiveUserMutationsUpdateArgs = {
export type Activity = {
__typename?: 'Activity';
- actionType: Scalars['String'];
- id: Scalars['ID'];
- info: Scalars['JSONObject'];
- message: Scalars['String'];
- resourceId: Scalars['String'];
- resourceType: Scalars['String'];
- streamId?: Maybe;
- time: Scalars['DateTime'];
- userId: Scalars['String'];
+ actionType: Scalars['String']['output'];
+ id: Scalars['ID']['output'];
+ info: Scalars['JSONObject']['output'];
+ message: Scalars['String']['output'];
+ resourceId: Scalars['String']['output'];
+ resourceType: Scalars['String']['output'];
+ streamId?: Maybe;
+ time: Scalars['DateTime']['output'];
+ userId: Scalars['String']['output'];
};
export type ActivityCollection = {
__typename?: 'ActivityCollection';
- cursor?: Maybe;
+ cursor?: Maybe;
items?: Maybe>>;
- totalCount: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
};
export type AdminInviteList = {
__typename?: 'AdminInviteList';
- cursor?: Maybe;
+ cursor?: Maybe;
items: Array;
- totalCount: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
};
export type AdminQueries = {
@@ -71,50 +73,50 @@ export type AdminQueries = {
export type AdminQueriesInviteListArgs = {
- cursor?: InputMaybe;
- limit?: Scalars['Int'];
- query?: InputMaybe;
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+ query?: InputMaybe;
};
export type AdminQueriesProjectListArgs = {
- cursor?: InputMaybe;
- limit?: Scalars['Int'];
- orderBy?: InputMaybe;
- query?: InputMaybe;
- visibility?: InputMaybe;
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+ orderBy?: InputMaybe;
+ query?: InputMaybe;
+ visibility?: InputMaybe;
};
export type AdminQueriesUserListArgs = {
- cursor?: InputMaybe;
- limit?: Scalars['Int'];
- query?: InputMaybe;
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
+ query?: InputMaybe;
role?: InputMaybe;
};
export type AdminUserList = {
__typename?: 'AdminUserList';
- cursor?: Maybe;
+ cursor?: Maybe;
items: Array;
- totalCount: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
};
export type AdminUserListItem = {
__typename?: 'AdminUserListItem';
- avatar?: Maybe;
- company?: Maybe;
- email?: Maybe;
- id: Scalars['ID'];
- name: Scalars['String'];
- role?: Maybe;
- verified?: Maybe;
+ avatar?: Maybe;
+ company?: Maybe;
+ email?: Maybe;
+ id: Scalars['ID']['output'];
+ name: Scalars['String']['output'];
+ role?: Maybe;
+ verified?: Maybe;
};
export type AdminUsersListCollection = {
__typename?: 'AdminUsersListCollection';
items: Array;
- totalCount: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
};
/**
@@ -123,185 +125,316 @@ export type AdminUsersListCollection = {
*/
export type AdminUsersListItem = {
__typename?: 'AdminUsersListItem';
- id: Scalars['String'];
+ id: Scalars['String']['output'];
invitedUser?: Maybe;
registeredUser?: Maybe;
};
export type ApiToken = {
__typename?: 'ApiToken';
- createdAt: Scalars['DateTime'];
- id: Scalars['String'];
- lastChars: Scalars['String'];
- lastUsed: Scalars['DateTime'];
- lifespan: Scalars['BigInt'];
- name: Scalars['String'];
- scopes: Array>;
+ createdAt: Scalars['DateTime']['output'];
+ id: Scalars['String']['output'];
+ lastChars: Scalars['String']['output'];
+ lastUsed: Scalars['DateTime']['output'];
+ lifespan: Scalars['BigInt']['output'];
+ name: Scalars['String']['output'];
+ scopes: Array>;
};
export type ApiTokenCreateInput = {
- lifespan?: InputMaybe;
- name: Scalars['String'];
- scopes: Array;
+ lifespan?: InputMaybe;
+ name: Scalars['String']['input'];
+ scopes: Array;
};
export type AppAuthor = {
__typename?: 'AppAuthor';
- avatar?: Maybe;
- id: Scalars['String'];
- name: Scalars['String'];
+ avatar?: Maybe;
+ id: Scalars['String']['output'];
+ name: Scalars['String']['output'];
};
export type AppCreateInput = {
- description: Scalars['String'];
- logo?: InputMaybe;
- name: Scalars['String'];
- public?: InputMaybe;
- redirectUrl: Scalars['String'];
- scopes: Array>;
- termsAndConditionsLink?: InputMaybe;
+ description: Scalars['String']['input'];
+ logo?: InputMaybe;
+ name: Scalars['String']['input'];
+ public?: InputMaybe;
+ redirectUrl: Scalars['String']['input'];
+ scopes: Array>;
+ termsAndConditionsLink?: InputMaybe;
};
export type AppTokenCreateInput = {
- lifespan?: InputMaybe;
+ lifespan?: InputMaybe;
/** Optionally limit the token to only have access to specific resources */
limitResources?: InputMaybe>;
- name: Scalars['String'];
- scopes: Array;
+ name: Scalars['String']['input'];
+ scopes: Array;
};
export type AppUpdateInput = {
- description: Scalars['String'];
- id: Scalars['String'];
- logo?: InputMaybe;
- name: Scalars['String'];
- public?: InputMaybe;
- redirectUrl: Scalars['String'];
- scopes: Array>;
- termsAndConditionsLink?: InputMaybe;
+ description: Scalars['String']['input'];
+ id: Scalars['String']['input'];
+ logo?: InputMaybe;
+ name: Scalars['String']['input'];
+ public?: InputMaybe;
+ redirectUrl: Scalars['String']['input'];
+ scopes: Array>;
+ termsAndConditionsLink?: InputMaybe;
};
export type AuthStrategy = {
__typename?: 'AuthStrategy';
- color?: Maybe;
- icon: Scalars['String'];
- id: Scalars['String'];
- name: Scalars['String'];
- url: Scalars['String'];
+ color?: Maybe;
+ icon: Scalars['String']['output'];
+ id: Scalars['String']['output'];
+ name: Scalars['String']['output'];
+ url: Scalars['String']['output'];
};
-export type AutomationCreateInput = {
- automationId: Scalars['String'];
- automationName: Scalars['String'];
- automationRevisionId: Scalars['String'];
- modelId: Scalars['String'];
- projectId: Scalars['String'];
- webhookId?: InputMaybe;
+export type AutomateAuthCodePayloadTest = {
+ action: Scalars['String']['input'];
+ code: Scalars['String']['input'];
+ userId: Scalars['String']['input'];
};
-export type AutomationFunctionRun = {
- __typename?: 'AutomationFunctionRun';
- contextView?: Maybe;
- elapsed: Scalars['Float'];
- functionId: Scalars['String'];
- functionLogo?: Maybe;
- functionName: Scalars['String'];
- id: Scalars['ID'];
- resultVersions: Array;
- /**
- * NOTE: this is the schema for the results field below!
- * Current schema: {
- * version: "1.0.0",
- * values: {
- * objectResults: Record | null
- * visualoverrides: Records | null
- * }[]>
- * blobIds?: string[]
- * }
- * }
- */
- results?: Maybe;
- status: AutomationRunStatus;
- statusMessage?: Maybe;
-};
-
-export type AutomationMutations = {
- __typename?: 'AutomationMutations';
- create: Scalars['Boolean'];
- functionRunStatusReport: Scalars['Boolean'];
+export type AutomateFunction = {
+ __typename?: 'AutomateFunction';
+ automationCount: Scalars['Int']['output'];
+ /** Only returned if user is a part of this speckle server */
+ creator?: Maybe;
+ description: Scalars['String']['output'];
+ id: Scalars['ID']['output'];
+ isFeatured: Scalars['Boolean']['output'];
+ logo?: Maybe;
+ name: Scalars['String']['output'];
+ releases: AutomateFunctionReleaseCollection;
+ repo: BasicGitRepositoryMetadata;
+ /** SourceAppNames values from @speckle/shared. Empty array means - all of them */
+ supportedSourceApps: Array;
+ tags: Array;
};
-export type AutomationMutationsCreateArgs = {
- input: AutomationCreateInput;
+export type AutomateFunctionReleasesArgs = {
+ cursor?: InputMaybe;
+ filter?: InputMaybe;
+ limit?: InputMaybe;
};
-
-export type AutomationMutationsFunctionRunStatusReportArgs = {
- input: AutomationRunStatusUpdateInput;
+export type AutomateFunctionCollection = {
+ __typename?: 'AutomateFunctionCollection';
+ cursor?: Maybe;
+ items: Array;
+ totalCount: Scalars['Int']['output'];
};
-export type AutomationRun = {
- __typename?: 'AutomationRun';
- automationId: Scalars['String'];
- automationName: Scalars['String'];
- createdAt: Scalars['DateTime'];
- functionRuns: Array;
- id: Scalars['ID'];
- /** Resolved from all function run statuses */
- status: AutomationRunStatus;
- updatedAt: Scalars['DateTime'];
- versionId: Scalars['String'];
+export type AutomateFunctionRelease = {
+ __typename?: 'AutomateFunctionRelease';
+ commitId: Scalars['String']['output'];
+ createdAt: Scalars['DateTime']['output'];
+ function: AutomateFunction;
+ functionId: Scalars['String']['output'];
+ id: Scalars['ID']['output'];
+ inputSchema?: Maybe;
+ versionTag: Scalars['String']['output'];
};
-export enum AutomationRunStatus {
- Failed = 'FAILED',
- Initializing = 'INITIALIZING',
- Running = 'RUNNING',
- Succeeded = 'SUCCEEDED'
+export type AutomateFunctionReleaseCollection = {
+ __typename?: 'AutomateFunctionReleaseCollection';
+ cursor?: Maybe;
+ items: Array;
+ totalCount: Scalars['Int']['output'];
+};
+
+export type AutomateFunctionReleasesFilter = {
+ search?: InputMaybe;
+};
+
+export type AutomateFunctionRun = {
+ __typename?: 'AutomateFunctionRun';
+ contextView?: Maybe;
+ createdAt: Scalars['DateTime']['output'];
+ elapsed: Scalars['Float']['output'];
+ /** Nullable, in case the function is not retrievable due to poor network conditions */
+ function?: Maybe;
+ functionId?: Maybe;
+ functionReleaseId?: Maybe;
+ id: Scalars['ID']['output'];
+ /** AutomateTypes.ResultsSchema type from @speckle/shared */
+ results?: Maybe;
+ status: AutomateRunStatus;
+ statusMessage?: Maybe;
+ updatedAt: Scalars['DateTime']['output'];
+};
+
+export type AutomateFunctionRunStatusReportInput = {
+ contextView?: InputMaybe;
+ functionRunId: Scalars['String']['input'];
+ /** AutomateTypes.ResultsSchema type from @speckle/shared */
+ results?: InputMaybe;
+ status: AutomateRunStatus;
+ statusMessage?: InputMaybe;
+};
+
+export type AutomateFunctionTemplate = {
+ __typename?: 'AutomateFunctionTemplate';
+ id: AutomateFunctionTemplateLanguage;
+ logo: Scalars['String']['output'];
+ title: Scalars['String']['output'];
+ url: Scalars['String']['output'];
+};
+
+export enum AutomateFunctionTemplateLanguage {
+ DotNet = 'DOT_NET',
+ Python = 'PYTHON',
+ Typescript = 'TYPESCRIPT'
}
-export type AutomationRunStatusUpdateInput = {
- automationId: Scalars['String'];
- automationRevisionId: Scalars['String'];
- automationRunId: Scalars['String'];
- functionRuns: Array;
- versionId: Scalars['String'];
+export type AutomateFunctionsFilter = {
+ featuredFunctionsOnly?: InputMaybe;
+ /** By default we skip functions without releases. Set this to true to include them. */
+ functionsWithoutReleases?: InputMaybe;
+ search?: InputMaybe;
};
-export type AutomationsStatus = {
- __typename?: 'AutomationsStatus';
- automationRuns: Array;
- id: Scalars['ID'];
- status: AutomationRunStatus;
- statusMessage?: Maybe;
+export type AutomateMutations = {
+ __typename?: 'AutomateMutations';
+ createFunction: AutomateFunction;
+ updateFunction: AutomateFunction;
+};
+
+
+export type AutomateMutationsCreateFunctionArgs = {
+ input: CreateAutomateFunctionInput;
+};
+
+
+export type AutomateMutationsUpdateFunctionArgs = {
+ input: UpdateAutomateFunctionInput;
+};
+
+export type AutomateRun = {
+ __typename?: 'AutomateRun';
+ automation: Automation;
+ automationId: Scalars['String']['output'];
+ createdAt: Scalars['DateTime']['output'];
+ functionRuns: Array;
+ id: Scalars['ID']['output'];
+ status: AutomateRunStatus;
+ trigger: AutomationRunTrigger;
+ updatedAt: Scalars['DateTime']['output'];
+};
+
+export type AutomateRunCollection = {
+ __typename?: 'AutomateRunCollection';
+ cursor?: Maybe;
+ items: Array;
+ totalCount: Scalars['Int']['output'];
+};
+
+export enum AutomateRunStatus {
+ Canceled = 'CANCELED',
+ Exception = 'EXCEPTION',
+ Failed = 'FAILED',
+ Initializing = 'INITIALIZING',
+ Pending = 'PENDING',
+ Running = 'RUNNING',
+ Succeeded = 'SUCCEEDED',
+ Timeout = 'TIMEOUT'
+}
+
+export enum AutomateRunTriggerType {
+ VersionCreated = 'VERSION_CREATED'
+}
+
+export type Automation = {
+ __typename?: 'Automation';
+ createdAt: Scalars['DateTime']['output'];
+ /** Only accessible to automation owners */
+ creationPublicKeys: Array;
+ currentRevision?: Maybe;
+ enabled: Scalars['Boolean']['output'];
+ id: Scalars['ID']['output'];
+ isTestAutomation: Scalars['Boolean']['output'];
+ name: Scalars['String']['output'];
+ runs: AutomateRunCollection;
+ updatedAt: Scalars['DateTime']['output'];
+};
+
+
+export type AutomationRunsArgs = {
+ cursor?: InputMaybe;
+ limit?: InputMaybe;
+};
+
+export type AutomationCollection = {
+ __typename?: 'AutomationCollection';
+ cursor?: Maybe;
+ items: Array;
+ totalCount: Scalars['Int']['output'];
+};
+
+export type AutomationRevision = {
+ __typename?: 'AutomationRevision';
+ functions: Array;
+ id: Scalars['ID']['output'];
+ triggerDefinitions: Array;
+};
+
+export type AutomationRevisionCreateFunctionInput = {
+ functionId: Scalars['String']['input'];
+ functionReleaseId: Scalars['String']['input'];
+ /** Should be encrypted from the client side */
+ parameters?: InputMaybe;
+};
+
+export type AutomationRevisionFunction = {
+ __typename?: 'AutomationRevisionFunction';
+ /** The secrets in parameters are redacted with six asterisks - ****** */
+ parameters?: Maybe;
+ release: AutomateFunctionRelease;
+};
+
+export type AutomationRevisionTriggerDefinition = VersionCreatedTriggerDefinition;
+
+export type AutomationRunTrigger = VersionCreatedTrigger;
+
+export type AvatarUser = {
+ __typename?: 'AvatarUser';
+ avatar?: Maybe;
+ id: Scalars['ID']['output'];
+ name: Scalars['String']['output'];
+};
+
+export type BasicGitRepositoryMetadata = {
+ __typename?: 'BasicGitRepositoryMetadata';
+ id: Scalars['ID']['output'];
+ name: Scalars['String']['output'];
+ owner: Scalars['String']['output'];
+ url: Scalars['String']['output'];
};
export type BlobMetadata = {
__typename?: 'BlobMetadata';
- createdAt: Scalars['DateTime'];
- fileHash?: Maybe;
- fileName: Scalars['String'];
- fileSize?: Maybe;
- fileType: Scalars['String'];
- id: Scalars['String'];
- streamId: Scalars['String'];
- uploadError?: Maybe;
- uploadStatus: Scalars['Int'];
- userId: Scalars['String'];
+ createdAt: Scalars['DateTime']['output'];
+ fileHash?: Maybe;
+ fileName: Scalars['String']['output'];
+ fileSize?: Maybe;
+ fileType: Scalars['String']['output'];
+ id: Scalars['String']['output'];
+ streamId: Scalars['String']['output'];
+ uploadError?: Maybe;
+ uploadStatus: Scalars['Int']['output'];
+ userId: Scalars['String']['output'];
};
export type BlobMetadataCollection = {
__typename?: 'BlobMetadataCollection';
- cursor?: Maybe;
+ cursor?: Maybe;
items?: Maybe>;
- totalCount: Scalars['Int'];
- totalSize: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
+ totalSize: Scalars['Int']['output'];
};
export type Branch = {
@@ -310,124 +443,124 @@ export type Branch = {
activity?: Maybe;
author?: Maybe;
commits?: Maybe;
- createdAt?: Maybe;
- description?: Maybe;
- id: Scalars['String'];
- name: Scalars['String'];
+ createdAt?: Maybe;
+ description?: Maybe;
+ id: Scalars['String']['output'];
+ name: Scalars['String']['output'];
};
export type BranchActivityArgs = {
- actionType?: InputMaybe;
- after?: InputMaybe;
- before?: InputMaybe;
- cursor?: InputMaybe;
- limit?: Scalars['Int'];
+ actionType?: InputMaybe;
+ after?: InputMaybe;
+ before?: InputMaybe;
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
};
export type BranchCommitsArgs = {
- cursor?: InputMaybe;
- limit?: Scalars['Int'];
+ cursor?: InputMaybe;
+ limit?: Scalars['Int']['input'];
};
export type BranchCollection = {
__typename?: 'BranchCollection';
- cursor?: Maybe;
+ cursor?: Maybe;
items?: Maybe>;
- totalCount: Scalars['Int'];
+ totalCount: Scalars['Int']['output'];
};
export type BranchCreateInput = {
- description?: InputMaybe;
- name: Scalars['String'];
- streamId: Scalars['String'];
+ description?: InputMaybe;
+ name: Scalars['String']['input'];
+ streamId: Scalars['String']['input'];
};
export type BranchDeleteInput = {
- id: Scalars['String'];
- streamId: Scalars['String'];
+ id: Scalars['String']['input'];
+ streamId: Scalars['String']['input'];
};
export type BranchUpdateInput = {
- description?: InputMaybe;
- id: Scalars['String'];
- name?: InputMaybe;
- streamId: Scalars['String'];
+ description?: InputMaybe;
+ id: Scalars['String']['input'];
+ name?: InputMaybe;
+ streamId: Scalars['String']['input'];
};
export type Comment = {
__typename?: 'Comment';
- archived: Scalars['Boolean'];
+ archived: Scalars['Boolean']['output'];
author: LimitedUser;
- authorId: Scalars['String'];
- createdAt: Scalars['DateTime'];
+ authorId: Scalars['String']['output'];
+ createdAt: Scalars['DateTime']['output'];
/**
* Legacy comment viewer data field
* @deprecated Use the new viewerState field instead
*/
- data?: Maybe;
+ data?: Maybe;
/** Whether or not comment is a reply to another comment */
- hasParent: Scalars['Boolean'];
- id: Scalars['String'];
+ hasParent: Scalars['Boolean']['output'];
+ id: Scalars['String']['output'];
/** Parent thread, if there's any */
parent?: Maybe;
/** Plain-text version of the comment text, ideal for previews */
- rawText: Scalars['String'];
+ rawText: Scalars['String']['output'];
/** @deprecated Not actually implemented */
- reactions?: Maybe>>;
+ reactions?: Maybe>>;
/** Gets the replies to this comment. */
replies: CommentCollection;
/** Get authors of replies to this comment */
replyAuthors: CommentReplyAuthorCollection;
/** Resources that this comment targets. Can be a mixture of either one stream, or multiple commits and objects. */
resources: Array;
- screenshot?: Maybe;
+ screenshot?: Maybe;
text: SmartTextEditorValue;
/** The time this comment was last updated. Corresponds also to the latest reply to this comment, if any. */
- updatedAt: Scalars['DateTime'];
+ updatedAt: Scalars['DateTime']['output'];
/** The last time you viewed this comment. Present only if an auth'ed request. Relevant only if a top level commit. */
- viewedAt?: Maybe;
+ viewedAt?: Maybe;
/** Resource identifiers as defined and implemented in the Viewer of the new frontend */
viewerResources: Array;
/** SerializedViewerState */
- viewerState?: Maybe;
+ viewerState?: Maybe;
};
export type CommentRepliesArgs = {
- cursor?: InputMaybe;
- limit?: InputMaybe;
+ cursor?: InputMaybe