chore(fe2): upgrade to nuxt 4 (#5306)
* actual npm update * migrating plugin * fix hydration (todo redis) * fix dashboard title * linting fixes * fix ssr dev logs * fix shared build * more linting fixes * more lint fixes * preview dockerfile fix * fix max stack trace issue
This commit is contained in:
committed by
GitHub
parent
c353966993
commit
9d9a456b28
@@ -58,7 +58,7 @@ const { validateCheckoutSession } = useBillingActions()
|
||||
const { finalizeWizard } = useWorkspacesWizard()
|
||||
const workspaceTitle = ref<string>('')
|
||||
|
||||
useHeadSafe({
|
||||
useHead({
|
||||
title: workspaceTitle
|
||||
})
|
||||
const hasFinalized = ref(false)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Redis } from 'ioredis'
|
||||
import type pino from 'pino'
|
||||
|
||||
export const createRedis = async (params: { logger: pino.Logger }) => {
|
||||
const { logger } = params
|
||||
// invoke composables sync first
|
||||
const { redisUrl } = useRuntimeConfig()
|
||||
|
||||
// doesnt work as a static import for some reason, maybe client build is picking it up
|
||||
const { default: Redis } = await import('ioredis')
|
||||
const { logger } = params
|
||||
if (!redisUrl?.length) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import path from 'path'
|
||||
import type { ApolloClientOptions } from '@apollo/client/core'
|
||||
import { addPluginTemplate, defineNuxtModule } from '@nuxt/kit'
|
||||
import type { MaybeAsync } from '@speckle/shared'
|
||||
import type { NuxtApp } from '#app'
|
||||
import { defineNuxtModule, addTemplate, createResolver, addPlugin } from 'nuxt/kit'
|
||||
|
||||
/**
|
||||
* Config resolver default exported function expected type
|
||||
@@ -27,20 +26,32 @@ export default defineNuxtModule<ApolloModuleOptions>({
|
||||
name: 'apollo-module',
|
||||
configKey: 'apollo',
|
||||
compatibility: {
|
||||
nuxt: '>= 3.0.0 || 3.0.0-rc.13'
|
||||
nuxt: '>= 3.0.0 || 3.0.0-rc.13 || >= 4.0.0'
|
||||
}
|
||||
},
|
||||
hooks: {},
|
||||
setup(moduleOptions) {
|
||||
const resolver = createResolver(import.meta.url)
|
||||
|
||||
if (!moduleOptions.configResolvers?.default) {
|
||||
throw new Error('No apollo client config resolvers registered!')
|
||||
}
|
||||
|
||||
addPluginTemplate({
|
||||
src: path.resolve(__dirname, './templates/plugin.js'),
|
||||
options: {
|
||||
configResolvers: moduleOptions.configResolvers
|
||||
}
|
||||
const imports = Object.entries(moduleOptions.configResolvers)
|
||||
.map(([key, path]) => `import ${key}Resolver from '${path}'`)
|
||||
.join('\n')
|
||||
const resolverMap = `const resolvers = {
|
||||
${Object.keys(moduleOptions.configResolvers)
|
||||
.map((key) => `${key}: ${key}Resolver`)
|
||||
.join(',\n')}
|
||||
}`
|
||||
|
||||
const templateContents = `${imports}\n${resolverMap}\nexport default resolvers`
|
||||
addTemplate({
|
||||
filename: 'apollo-config-resolvers.mjs',
|
||||
getContents: () => templateContents
|
||||
})
|
||||
|
||||
addPlugin(resolver.resolve('./templates/plugin'))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<% for (const [key, path] of Object.entries(options.configResolvers)) { %>
|
||||
import <%= key %>ConfigResolver from '<%= path %>';
|
||||
<% } %>
|
||||
|
||||
import { ApolloClient } from '@apollo/client/core'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
import { ApolloClients, provideApolloClient } from '@vue/apollo-composable'
|
||||
import {markRaw, toRaw} from 'vue'
|
||||
|
||||
export default defineNuxtPlugin(async (nuxt) => {
|
||||
// in dev mode, load better messages
|
||||
if (import.meta.dev) {
|
||||
const devSettings = await import('@apollo/client/dev')
|
||||
devSettings.loadDevMessages()
|
||||
}
|
||||
|
||||
// Load all configs
|
||||
const keyedConfigs = {};
|
||||
<% for (const key of Object.keys(options.configResolvers)) { %>
|
||||
keyedConfigs['<%= key %>'] = await Promise.resolve(<%= key %>ConfigResolver(nuxt));
|
||||
<% } %>
|
||||
|
||||
if (!keyedConfigs.default) {
|
||||
throw new Error("Couldn't successfully resolve config for default config!")
|
||||
}
|
||||
|
||||
if (process.client) {
|
||||
// Restore cached data from SSR
|
||||
for (const [key, config] of Object.entries(keyedConfigs)) {
|
||||
/** @type {import('@apollo/client').InMemoryCache} */
|
||||
const cache = config.cache;
|
||||
const restorable = window.__NUXT__?.apollo?.[key] || null
|
||||
|
||||
if (restorable) {
|
||||
// Cache is proxified by Vue, gotta undo all that or all hell breaks loose
|
||||
cache.restore(markRaw(toRaw(restorable)));
|
||||
config.cache = cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init clients
|
||||
let defaultClient,
|
||||
keyedClients = {};
|
||||
for (const [key, config] of Object.entries(keyedConfigs)) {
|
||||
const client = new ApolloClient({
|
||||
...config,
|
||||
...(process.server ? {ssrMode: true} : {ssrForceFetchDelay: 100}),
|
||||
connectToDevTools: !!process.dev
|
||||
});
|
||||
if (key === 'default') {
|
||||
defaultClient = client;
|
||||
if (process.client && process.dev) {
|
||||
window.__APOLLO_CLIENT__ = client;
|
||||
}
|
||||
} else {
|
||||
keyedClients[key] = client;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure server side serializes state on render
|
||||
if (process.server) {
|
||||
const ApolloSSR = await import('@vue/apollo-ssr')
|
||||
nuxt.hook('app:rendered', () => {
|
||||
nuxt.ssrContext.payload.apollo = ApolloSSR.getStates({
|
||||
default: defaultClient,
|
||||
...keyedClients
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// For composable api
|
||||
const providedClients = {
|
||||
default: defaultClient,
|
||||
...keyedClients
|
||||
};
|
||||
nuxt.vueApp.provide(ApolloClients, providedClients)
|
||||
|
||||
// For global access through $apollo
|
||||
nuxt.provide("apollo", providedClients)
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import configResolvers from '#build/apollo-config-resolvers.mjs'
|
||||
import { ApolloClient, type ApolloClientOptions } from '@apollo/client/core'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
import { ApolloClients } from '@vue/apollo-composable'
|
||||
import { markRaw, toRaw } from 'vue'
|
||||
|
||||
export default defineNuxtPlugin(async (nuxt) => {
|
||||
// in dev mode, load better messages
|
||||
if (import.meta.dev) {
|
||||
const devSettings = await import('@apollo/client/dev')
|
||||
devSettings.loadDevMessages()
|
||||
}
|
||||
|
||||
// Load all configs
|
||||
const keyedConfigs: Record<string, ApolloClientOptions<unknown>> = {}
|
||||
for (const key of Object.keys(configResolvers)) {
|
||||
keyedConfigs[key] = await Promise.resolve(configResolvers[key](nuxt))
|
||||
}
|
||||
|
||||
if (!keyedConfigs.default) {
|
||||
throw new Error("Couldn't successfully resolve config for default config!")
|
||||
}
|
||||
|
||||
if (import.meta.client) {
|
||||
// Restore cached data from SSR
|
||||
for (const [key, config] of Object.entries(keyedConfigs)) {
|
||||
const cache = config.cache
|
||||
const restorable = nuxt.payload.apollo?.[key] || null
|
||||
|
||||
if (restorable) {
|
||||
// Cache is proxified by Vue, gotta undo all that or all hell breaks loose
|
||||
cache.restore(markRaw(toRaw(restorable)))
|
||||
config.cache = cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init clients
|
||||
let defaultClient: ApolloClient<unknown>,
|
||||
keyedClients: Record<string, ApolloClient<unknown>> = {}
|
||||
for (const [key, config] of Object.entries(keyedConfigs)) {
|
||||
const client = new ApolloClient({
|
||||
...config,
|
||||
...(import.meta.server ? { ssrMode: true } : { ssrForceFetchDelay: 100 }),
|
||||
connectToDevTools: !!import.meta.dev
|
||||
})
|
||||
if (key === 'default') {
|
||||
defaultClient = client
|
||||
if (import.meta.client && import.meta.dev) {
|
||||
window.__APOLLO_CLIENT__ = client
|
||||
}
|
||||
} else {
|
||||
keyedClients[key] = client
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure server side serializes state on render
|
||||
if (import.meta.server) {
|
||||
const ApolloSSR = await import('@vue/apollo-ssr')
|
||||
nuxt.hook('app:rendered', () => {
|
||||
nuxt.ssrContext!.payload.apollo = ApolloSSR.getStates({
|
||||
default: defaultClient,
|
||||
...keyedClients
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// For composable api
|
||||
const providedClients: Record<string, ApolloClient<unknown>> = {
|
||||
default: defaultClient!,
|
||||
...keyedClients
|
||||
}
|
||||
nuxt.vueApp.provide(ApolloClients, providedClients)
|
||||
|
||||
// For global access through $apollo
|
||||
nuxt.provide('apollo', providedClients)
|
||||
})
|
||||
@@ -115,7 +115,7 @@ export function useStateSerialization() {
|
||||
position: state.ui.camera.position.value.toArray(),
|
||||
target: state.ui.camera.target.value.toArray(),
|
||||
isOrthoProjection: state.ui.camera.isOrthoProjection.value,
|
||||
zoom: (get(camControls, '_zoom') as number) || 1 // kinda hacky, _zoom is a protected prop
|
||||
zoom: (get(camControls, '_zoom') as unknown as number) || 1 // kinda hacky, _zoom is a protected prop
|
||||
},
|
||||
viewMode: state.ui.viewMode.value,
|
||||
sectionBox: state.ui.sectionBox.value ? box : null,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { join } from 'path'
|
||||
import { withoutLeadingSlash } from 'ufo'
|
||||
import { sanitizeFilePath } from 'mlly'
|
||||
import { filename } from 'pathe/utils'
|
||||
import * as Environment from '@speckle/shared/environment'
|
||||
import { defineNuxtConfig } from 'nuxt/config'
|
||||
|
||||
// Copied out from nuxt vite-builder source to correctly build output chunk/entry/asset/etc file names
|
||||
const withoutLeadingSlash = (path: string) => path.replace(/^\//, '')
|
||||
const buildOutputFileName = (chunkName: string) =>
|
||||
withoutLeadingSlash(
|
||||
join('/_nuxt/', `${sanitizeFilePath(filename(chunkName))}.[hash].js`)
|
||||
@@ -26,6 +27,8 @@ const hydrationMismatchReportingEnabled = ['1', 'true', true, 1].includes(
|
||||
HYDRATION_MISMATCH_REPORTING
|
||||
)
|
||||
|
||||
const external = ['ioredis', 'crypto', 'jsdom']
|
||||
|
||||
// https://v3.nuxtjs.org/api/configuration/nuxt.config
|
||||
export default defineNuxtConfig({
|
||||
...(buildSourceMaps ? { sourcemap: true } : {}),
|
||||
@@ -35,7 +38,9 @@ export default defineNuxtConfig({
|
||||
strict: true,
|
||||
tsConfig: {
|
||||
compilerOptions: {
|
||||
moduleResolution: 'bundler'
|
||||
moduleResolution: 'bundler',
|
||||
// TODO: More correct, but requires a lot of (minor) changes
|
||||
noUncheckedIndexedAccess: false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -101,9 +106,13 @@ export default defineNuxtConfig({
|
||||
: {})
|
||||
},
|
||||
|
||||
ssr: {
|
||||
external
|
||||
},
|
||||
|
||||
optimizeDeps: {
|
||||
// Should only be ran on serverside anyway. W/o this it tries to transpile it unsuccessfully
|
||||
exclude: ['jsdom']
|
||||
exclude: external
|
||||
},
|
||||
|
||||
vue: {
|
||||
@@ -148,7 +157,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
// Leave imports as is, they're server-side only
|
||||
external: ['jsdom']
|
||||
external: ['jsdom', 'crypto']
|
||||
}
|
||||
// // optionally disable minification for debugging
|
||||
// minify: false,
|
||||
@@ -239,14 +248,14 @@ export default defineNuxtConfig({
|
||||
to: '/workspaces/actions/create',
|
||||
statusCode: 301
|
||||
}
|
||||
},
|
||||
'/projects/:id/models/:modelId': {
|
||||
ssr: true // TODO: Should experiment w/ false, but this breaks SSR script injection like for RUM
|
||||
}
|
||||
},
|
||||
|
||||
nitro: {
|
||||
compressPublicAssets: true
|
||||
compressPublicAssets: true,
|
||||
externals: {
|
||||
external
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
@@ -270,6 +279,8 @@ export default defineNuxtConfig({
|
||||
'graphql/utilities/getOperationAST'
|
||||
]
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
prometheus: {
|
||||
verbose: false
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"dev:app": "concurrently \"nuxt dev\" \"yarn gqlgen:watch\"",
|
||||
"dev": "yarn dev:app",
|
||||
"preview": "nuxt preview",
|
||||
"analyze": "nuxt analyze",
|
||||
"analyze": "NODE_OPTIONS=--max-old-space-size=8192 nuxt analyze",
|
||||
"lint:js": "eslint .",
|
||||
"lint:tsc": "vue-tsc --noEmit",
|
||||
"lint:prettier": "prettier --config ../../.prettierrc --ignore-path ../../.prettierignore --check .",
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.13.8",
|
||||
"@artmizu/nuxt-prometheus": "^2.2.1",
|
||||
"@artmizu/nuxt-prometheus": "^2.5.2",
|
||||
"@datadog/browser-rum": "^5.11.0",
|
||||
"@headlessui/vue": "npm:@speckle/headlessui-vue@1.7.23-alpha.0",
|
||||
"@heroicons/vue": "^2.0.12",
|
||||
@@ -63,7 +63,7 @@
|
||||
"dayjs": "^1.11.7",
|
||||
"dompurify": "^3.0.4",
|
||||
"graphql": "^16.6.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"ioredis": "^5.7.0",
|
||||
"js-cookie": "^3.0.1",
|
||||
"jsdom": "^22.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -126,7 +126,7 @@
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-vuejs-accessibility": "^2.3.0",
|
||||
"mixpanel": "^0.18.0",
|
||||
"nuxt": "^3.15.0",
|
||||
"nuxt": "^4.0.3",
|
||||
"pino-pretty": "^10.0.1",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-custom-properties": "^12.1.9",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { collectLongTrace } from '@speckle/shared'
|
||||
import type { LogType } from 'consola'
|
||||
import dayjs from 'dayjs'
|
||||
import { get, omit } from 'lodash-es'
|
||||
import { omit } from 'lodash-es'
|
||||
import type { SetRequired } from 'type-fest'
|
||||
import { useReadUserId } from '~/lib/auth/composables/activeUser'
|
||||
import {
|
||||
@@ -25,25 +25,6 @@ import {
|
||||
|
||||
const simpleStripHtml = (str: string) => str.replace(/<[^>]*>?/gm, '')
|
||||
|
||||
// i dunno why but importing this returns undefined in server build, it makes no sense to me why it would be stripped out
|
||||
// but the solution is to duplicate this here
|
||||
const consolaLogLevels = {
|
||||
silent: Number.NEGATIVE_INFINITY,
|
||||
fatal: 0,
|
||||
error: 0,
|
||||
warn: 1,
|
||||
log: 2,
|
||||
info: 3,
|
||||
success: 3,
|
||||
fail: 3,
|
||||
ready: 3,
|
||||
start: 3,
|
||||
box: 3,
|
||||
debug: 4,
|
||||
trace: 5,
|
||||
verbose: Number.POSITIVE_INFINITY
|
||||
}
|
||||
|
||||
/**
|
||||
* - Setting up Pino logger in SSR, basic console.log fallback in CSR
|
||||
* - Also sets up ability to add extra transport for other observability tools
|
||||
@@ -100,7 +81,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
buildLogger,
|
||||
enableDynamicBindings,
|
||||
serializeRequest,
|
||||
prettifiedLoggerFactory
|
||||
prettifiedLoggerFactory,
|
||||
initSsrDevLogs
|
||||
} = await import('~/server/lib/core/helpers/observability')
|
||||
logger = enableDynamicBindings(buildLogger(logLevel, logPretty).child({}), () => ({
|
||||
...collectMainInfo({ isBrowser: false }),
|
||||
@@ -121,18 +103,10 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
|
||||
// Send to consola for SSR log streaming in dev mode
|
||||
if (import.meta.dev) {
|
||||
const { consola } = await import('consola')
|
||||
const ssrDevLogs = await initSsrDevLogs({ logLevel })
|
||||
const consola = ssrDevLogs.consola
|
||||
|
||||
// (consola exports are sometimes being stripped from build for some reason, hence the extra checks)
|
||||
if (consola) {
|
||||
// remove print to stdout, pino already handles all that
|
||||
consola.setReporters(
|
||||
consola.options.reporters.filter(
|
||||
(r) => get(r, 'constructor.name') !== 'FancyReporter'
|
||||
)
|
||||
)
|
||||
consola.level = consolaLogLevels[logLevel] || 0
|
||||
|
||||
const unhandledHandler: AbstractUnhandledErrorHandler = ({
|
||||
error,
|
||||
message,
|
||||
|
||||
@@ -119,7 +119,7 @@ const getOrInitInternalCache = async (params: {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
const val = vals[i]
|
||||
if (!val) continue
|
||||
if (!val || !key) continue
|
||||
|
||||
keyVals[key] = JSON.parse(val)
|
||||
}
|
||||
@@ -151,7 +151,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
nuxtApp.ssrContext!.payload.appCache = cacheToSend
|
||||
})
|
||||
} else if (import.meta.client) {
|
||||
const restorable = window.__NUXT__?.appCache as Optional<Record<string, unknown>>
|
||||
const restorable = nuxtApp.payload?.appCache
|
||||
if (restorable) {
|
||||
await internalCache.setMultiple(restorable)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { IncomingMessage } from 'node:http'
|
||||
import { get } from 'lodash-es'
|
||||
import type { Logger } from 'pino'
|
||||
import type express from 'express'
|
||||
import { prettifiedLoggerFactory } from '~/lib/core/helpers/observability'
|
||||
import { prettifiedLoggerFactory, prettify } from '~/lib/core/helpers/observability'
|
||||
import type { ConsolaInstance, LogType } from 'consola'
|
||||
|
||||
const redactedReqHeaders = ['authorization', 'cookie']
|
||||
|
||||
@@ -67,4 +68,53 @@ export const getRequestPath = (req: IncomingMessage | express.Request) => {
|
||||
return path?.length ? path : null
|
||||
}
|
||||
|
||||
export { prettifiedLoggerFactory }
|
||||
// i dunno why but importing this returns undefined in server build, it makes no sense to me why it would be stripped out
|
||||
// but the solution is to duplicate this here
|
||||
const consolaLogLevels = {
|
||||
silent: Number.NEGATIVE_INFINITY,
|
||||
fatal: 0,
|
||||
error: 0,
|
||||
warn: 1,
|
||||
log: 2,
|
||||
info: 3,
|
||||
success: 3,
|
||||
fail: 3,
|
||||
ready: 3,
|
||||
start: 3,
|
||||
box: 3,
|
||||
debug: 4,
|
||||
trace: 5,
|
||||
verbose: Number.POSITIVE_INFINITY
|
||||
}
|
||||
|
||||
interface DevLogsServerContext {
|
||||
consola?: ConsolaInstance
|
||||
}
|
||||
|
||||
export const initSsrDevLogs = async (params: { logLevel: LogType }) => {
|
||||
const { getContext } = await import('unctx')
|
||||
const { AsyncLocalStorage } = await import('node:async_hooks')
|
||||
|
||||
const asyncContext = getContext<DevLogsServerContext>('nuxt-dev-logs', {
|
||||
asyncContext: true,
|
||||
AsyncLocalStorage
|
||||
})
|
||||
|
||||
const ctx = asyncContext.tryUse()
|
||||
if (ctx?.consola) {
|
||||
// Fix up
|
||||
// remove print to stdout, pino already handles all that
|
||||
ctx.consola.setReporters(
|
||||
ctx.consola.options.reporters.filter(
|
||||
(r) => get(r, 'constructor.name') !== 'FancyReporter'
|
||||
)
|
||||
)
|
||||
ctx.consola.level = consolaLogLevels[params.logLevel] || 0
|
||||
}
|
||||
|
||||
return {
|
||||
consola: ctx?.consola
|
||||
}
|
||||
}
|
||||
|
||||
export { prettifiedLoggerFactory, prettify }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// <reference types="../../type-augmentations/server.d.ts" />
|
||||
import { defineEventHandler, fromNodeMiddleware } from 'h3'
|
||||
import type { IncomingMessage, ServerResponse, IncomingHttpHeaders } from 'http'
|
||||
import pino from 'pino'
|
||||
@@ -103,7 +104,7 @@ export const LoggingMiddleware = pinoHttp({
|
||||
headers: Record<string, string>
|
||||
}
|
||||
}
|
||||
const realRaw = get(res, 'raw.raw') as typeof res.raw
|
||||
const realRaw = get(res, 'raw.raw') as unknown as typeof res.raw
|
||||
const isRequestCompleted = !!realRaw.writableEnded
|
||||
const isRequestAborted = !isRequestCompleted
|
||||
const statusCode = res.statusCode || res.raw.statusCode || realRaw.statusCode
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { getContext } from 'unctx'
|
||||
import { consola, type ConsolaInstance } from 'consola'
|
||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||
|
||||
interface DevLogsServerContext {
|
||||
consola: ConsolaInstance
|
||||
}
|
||||
|
||||
const asyncContext = getContext<DevLogsServerContext>('nuxt-dev-logs', {
|
||||
asyncContext: true,
|
||||
AsyncLocalStorage
|
||||
})
|
||||
|
||||
/**
|
||||
* Importing `consola` from a nuxt plugin scope will give us a different instance. We have to pass through the nitro version
|
||||
* through an async context.
|
||||
*/
|
||||
export default defineNitroPlugin((nitroApp) => {
|
||||
if (!import.meta.dev) return
|
||||
|
||||
const handler = nitroApp.h3App.handler
|
||||
nitroApp.h3App.handler = (event) => {
|
||||
return asyncContext.callAsync({ consola }, () => handler(event))
|
||||
}
|
||||
})
|
||||
@@ -15,6 +15,10 @@ declare module '#app' {
|
||||
|
||||
interface NuxtPayload {
|
||||
serverFatalError?: import('~~/lib/core/helpers/observability').AbstractLoggerHandlerParams
|
||||
apollo?: {
|
||||
[clientKey: string]: Record<string, unknown>
|
||||
}
|
||||
appCache?: Record<string, unknown> | undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
__APOLLO_CLIENT__?: import('@apollo/client/core').ApolloClient<unknown>
|
||||
|
||||
DD_RUM?:
|
||||
| Pick<import('@datadog/browser-rum').RumGlobal, 'onReady'>
|
||||
| import('@datadog/browser-rum').RumGlobal
|
||||
|
||||
@@ -55,14 +55,12 @@ export const loggingExpressMiddleware = pinoHttp({
|
||||
})
|
||||
|
||||
const getRequestPath = (req: IncomingMessage | Request) => {
|
||||
const path = ((get(req, 'originalUrl') || get(req, 'url') || '') as string).split(
|
||||
'?'
|
||||
)[0]
|
||||
const path = (get(req, 'originalUrl') || get(req, 'url') || '').split('?')[0]
|
||||
return path?.length ? path : null
|
||||
}
|
||||
|
||||
const getRequestParameters = (req: IncomingMessage | Request) => {
|
||||
const maybeUrl = (get(req, 'originalUrl') as string) || get(req, 'url') || ''
|
||||
const maybeUrl = get(req, 'originalUrl') || get(req, 'url') || ''
|
||||
const url = parse(maybeUrl, true)
|
||||
return url.query || {}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ const isPayload = (payload: unknown): payload is AuthCodePayload =>
|
||||
has(payload, 'code') &&
|
||||
has(payload, 'userId') &&
|
||||
has(payload, 'action') &&
|
||||
Object.values(AuthCodePayloadAction).includes(get(payload, 'action'))
|
||||
Object.values(AuthCodePayloadAction).includes(
|
||||
get(payload, 'action') as unknown as AuthCodePayloadAction
|
||||
)
|
||||
)
|
||||
|
||||
export const createStoredAuthCodeFactory =
|
||||
|
||||
@@ -113,7 +113,12 @@ export const processNewFileStreamFactory = (): NewFileStreamProcessor => {
|
||||
|
||||
file.on('error', (err: unknown) => {
|
||||
registerUploadResult(
|
||||
markUploadError(deleteObject, streamId, blobId, get(err, 'message'))
|
||||
markUploadError(
|
||||
deleteObject,
|
||||
streamId,
|
||||
blobId,
|
||||
get(err, 'message') as unknown as string
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1655,7 +1655,13 @@ describe('Comments @comments', () => {
|
||||
expect(data?.comments?.items?.length || 0).to.eq(1)
|
||||
expect(errors?.length || 0).to.eq(0)
|
||||
|
||||
const textNode = get(data, 'comments.items[0].text.doc.content[0].content[0]')
|
||||
const textNode = get(
|
||||
data,
|
||||
'comments.items[0].text.doc.content[0].content[0]'
|
||||
) as unknown as {
|
||||
text: string
|
||||
marks: Array<{ type: string; attrs: Record<string, unknown> }>
|
||||
}
|
||||
expect(textNode.text).to.eq(item.text)
|
||||
expect(textNode.marks).to.deep.equalInAnyOrder([
|
||||
{
|
||||
@@ -1716,7 +1722,13 @@ describe('Comments @comments', () => {
|
||||
expect(data?.comments?.items?.length || 0).to.eq(1)
|
||||
expect(errors?.length || 0).to.eq(0)
|
||||
|
||||
const textNodes = get(data, 'comments.items[0].text.doc.content[0].content')
|
||||
const textNodes = get(
|
||||
data,
|
||||
'comments.items[0].text.doc.content[0].content'
|
||||
) as unknown as Array<{
|
||||
text: string
|
||||
marks: Array<{ type: string; attrs: Record<string, unknown> }>
|
||||
}>
|
||||
expect(textNodes.length).to.eq(textParts.length)
|
||||
|
||||
range(textParts.length).forEach((i) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
import { ModelNotFoundError } from '@/modules/core/errors/model'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import { FileImportJobNotFoundError } from '@/modules/fileuploads/helpers/errors'
|
||||
import { get } from 'lodash-es'
|
||||
import { get, isString } from 'lodash-es'
|
||||
|
||||
export const registerUploadCompleteAndStartFileImportFactory = (deps: {
|
||||
registerCompletedUpload: RegisterCompletedUpload
|
||||
@@ -55,10 +55,10 @@ export const registerUploadCompleteAndStartFileImportFactory = (deps: {
|
||||
projectId: storedBlob.streamId //backwards compatibility
|
||||
}
|
||||
} catch (error) {
|
||||
const message = get(error, 'message')
|
||||
const message = get(error, 'message') as unknown as string | undefined
|
||||
if (
|
||||
message &&
|
||||
typeof message === 'string' &&
|
||||
isString(message) &&
|
||||
message.includes(
|
||||
'duplicate key value violates unique constraint "file_uploads_pkey"'
|
||||
)
|
||||
|
||||
@@ -407,6 +407,6 @@ const setUpProjectReplication = async ({
|
||||
|
||||
const sanitizeError = (err: unknown): unknown => {
|
||||
if (!err) return err
|
||||
if (get(err, 'where').includes('password='))
|
||||
if ((get(err, 'where') as unknown as string).includes('password='))
|
||||
return { ...err, where: '[REDACTED AS IT CONTAINS CONNECTION STRING]' }
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export const mockStoreHelpers = (store: IMockStore) => {
|
||||
* for the existence of a field in the mock store.
|
||||
*/
|
||||
const hasField = (type: string, key: string, field: string) => {
|
||||
const internalStore = get(store, 'store') as {
|
||||
const internalStore = get(store, 'store') as unknown as {
|
||||
[type: string]: {
|
||||
[key: string]: {
|
||||
[field: string]: unknown
|
||||
|
||||
@@ -145,7 +145,7 @@ export const LoggingExpressMiddleware = HttpLogger({
|
||||
headers: Record<string, string>
|
||||
}
|
||||
}
|
||||
const serverRes = get(res, 'raw.raw') as ServerResponse
|
||||
const serverRes = get(res, 'raw.raw') as unknown as ServerResponse
|
||||
const auth = serverRes.req.context
|
||||
const statusCode = res.statusCode || res.raw.statusCode || serverRes.statusCode
|
||||
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { trim, isNumber } from '#lodash'
|
||||
import type { JSONContent } from '@tiptap/core'
|
||||
import type { Optional } from '../../core/helpers/utilityTypes.js'
|
||||
|
||||
// TODO: had to copy out of tiptap/core, because of a build issue w/ a type-only import from CJS
|
||||
type JSONContent = {
|
||||
[key: string]: any
|
||||
type?: string | undefined
|
||||
attrs?: Record<string, any> | undefined
|
||||
content?: JSONContent[]
|
||||
marks?: {
|
||||
type: string
|
||||
attrs?: Record<string, any>
|
||||
[key: string]: any
|
||||
}[]
|
||||
text?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to match URLs that can appear anywhere in a string, not perfect, but crafting a perfect
|
||||
* URL regex is quite complex and we only need this for legacy comments
|
||||
|
||||
Reference in New Issue
Block a user