diff --git a/platform/instrumentation.ts b/platform/instrumentation.ts index edaa838..c0a16ef 100644 --- a/platform/instrumentation.ts +++ b/platform/instrumentation.ts @@ -6,7 +6,6 @@ import relay from '@chatbotkit-dev/relay' import { assertTrustedSigninEnv } from '@/lib/auth.trusted' import { BANNER } from '@/lib/banner' -import { startClock } from '@/lib/clock' import { warnlog } from '@/lib/debug' import { isDevelopment } from '@/lib/env' @@ -20,6 +19,7 @@ export async function register() { // @note a hard stop, not a warning: trusted sign-in on a shared deployment // opens every account, so a process configured that way must not serve a // single request - see lib/auth.trusted.ts + try { assertTrustedSigninEnv() } catch (e) { @@ -38,6 +38,7 @@ export async function register() { // (lib/auth.providers.ts sendVerificationRequest), and RUNAS_USERID // impersonates any account for every session (lib/session.get.js // getSession) + if (isDevelopment && process.env.NODE_ENV === 'production') { warnlog( 'WARNING: TARGET_ENV=development on a production build - sign-in rate limits are disabled, sign-in codes are written to the log' + @@ -50,6 +51,11 @@ export async function register() { // @note the one place the platform has that outlives a request - see // lib/clock.ts. + // @note keep the import inside the runtime guard: the clock's queue + // dependencies require Node APIs that the Edge compiler cannot resolve + + const { startClock } = await import('@/lib/clock') + startClock() // @note a relay that is a process rather than a service runs here, in the diff --git a/platform/instrumentation.utest.js b/platform/instrumentation.utest.js new file mode 100644 index 0000000..be942e3 --- /dev/null +++ b/platform/instrumentation.utest.js @@ -0,0 +1,73 @@ +/** + * @jest-environment node + */ + +jest.mock('@chatbotkit-dev/observability/next/server', () => ({ + onRequestError: jest.fn(), + register: jest.fn(), +})) + +jest.mock('@chatbotkit-dev/relay', () => ({ + __esModule: true, + default: { listen: jest.fn() }, +})) + +jest.mock('@/lib/clock', () => { + // @note importing the clock reaches Node-only request and queue modules + if (process.env.NEXT_RUNTIME === 'edge') { + throw new Error("Module not found: Can't resolve 'crypto'") + } + + return { startClock: jest.fn() } +}) + +describe('instrumentation runtime boundary', () => { + const originalRuntime = process.env.NEXT_RUNTIME + + afterEach(() => { + if (originalRuntime === undefined) { + delete process.env.NEXT_RUNTIME + } else { + process.env.NEXT_RUNTIME = originalRuntime + } + + jest.restoreAllMocks() + }) + + it('registers Edge observability without importing the Node-only clock', async () => { + process.env.NEXT_RUNTIME = 'edge' + + await jest.isolateModulesAsync(async () => { + const { register, onRequestError } = await import('./instrumentation') + const observability = await import( + '@chatbotkit-dev/observability/next/server' + ) + + await register() + + expect(observability.register).toHaveBeenCalledTimes(1) + expect(onRequestError).toBe(observability.onRequestError) + }) + }) + + it('starts the clock and relay when registering the Node runtime', async () => { + process.env.NEXT_RUNTIME = 'nodejs' + + jest.spyOn(console, 'log').mockImplementation(() => {}) + + await jest.isolateModulesAsync(async () => { + const { register } = await import('./instrumentation') + const { startClock } = await import('@/lib/clock') + const { default: relay } = await import('@chatbotkit-dev/relay') + const observability = await import( + '@chatbotkit-dev/observability/next/server' + ) + + await register() + + expect(startClock).toHaveBeenCalledTimes(1) + expect(relay.listen).toHaveBeenCalledTimes(1) + expect(observability.register).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/platform/tailwind.config.js b/platform/tailwind.config.js index 3b8fada..6cc4b02 100644 --- a/platform/tailwind.config.js +++ b/platform/tailwind.config.js @@ -176,7 +176,8 @@ export default { // black: 'rgb(13, 17, 23)', white: '#fdfdfd', - black: '#0a0a0a', + // black: '#0a0a0a', + black: '#121212', gray: { 50: colors.zinc[50], @@ -186,10 +187,15 @@ export default { 400: colors.zinc[400], 500: colors.zinc[500], 600: colors.zinc[600], - 700: colors.zinc[700], - 800: colors.zinc[800], - 900: colors.zinc[900], - 950: colors.zinc[950], + // @note charcoal steps separate the canvas, panels, raised surfaces and borders + // 700: colors.zinc[700], + // 800: colors.zinc[800], + // 900: colors.zinc[900], + // 950: colors.zinc[950], + 700: '#3f3f3f', + 800: '#2e2e2e', + 900: '#222222', + 950: '#181818', }, // charts