Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/dashboard/src/instrumentation-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import "server-only";

import { getEnvBoolean } from "@hexclave/shared/dist/utils/env";

export async function startRemoteDevelopmentEnvironmentLifecycleIfNeeded(): Promise<void> {
if (!getEnvBoolean("NEXT_PUBLIC_STACK_IS_REMOTE_DEVELOPMENT_ENVIRONMENT")) {
return;
}

const { startRemoteDevelopmentEnvironmentLifecycle } = await import("./lib/remote-development-environment/manager");
startRemoteDevelopmentEnvironmentLifecycle();
Comment on lines +10 to +11
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The startRemoteDevelopmentEnvironmentLifecycle() call is fire-and-forget: if the imported function returns a rejected Promise, it will become an unhandled rejection that is silently swallowed. Since startRemoteDevelopmentEnvironmentLifecycleIfNeeded is itself async, awaiting the inner call ensures any error propagates up to the register() caller.

Suggested change
const { startRemoteDevelopmentEnvironmentLifecycle } = await import("./lib/remote-development-environment/manager");
startRemoteDevelopmentEnvironmentLifecycle();
const { startRemoteDevelopmentEnvironmentLifecycle } = await import("./lib/remote-development-environment/manager");
await startRemoteDevelopmentEnvironmentLifecycle();
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/src/instrumentation-node.ts
Line: 10-11

Comment:
The `startRemoteDevelopmentEnvironmentLifecycle()` call is fire-and-forget: if the imported function returns a rejected Promise, it will become an unhandled rejection that is silently swallowed. Since `startRemoteDevelopmentEnvironmentLifecycleIfNeeded` is itself `async`, awaiting the inner call ensures any error propagates up to the `register()` caller.

```suggestion
  const { startRemoteDevelopmentEnvironmentLifecycle } = await import("./lib/remote-development-environment/manager");
  await startRemoteDevelopmentEnvironmentLifecycle();
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked this: startRemoteDevelopmentEnvironmentLifecycle() currently returns void, so applying the suggested await fails lint with @typescript-eslint/await-thenable. I kept the call synchronous and addressed the actionable nearby instrumentation feedback in 23191f8.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct. startRemoteDevelopmentEnvironmentLifecycle() has a void return type, so awaiting it would violate @typescript-eslint/await-thenable. The synchronous call is the right approach here — the function only sets up a timer via ensureShutdownTimer() and logs, with no async work to propagate. My original comment was incorrect. Thanks for checking.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

Comment thread
vercel[bot] marked this conversation as resolved.
}
76 changes: 35 additions & 41 deletions apps/dashboard/src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,52 @@
import * as Sentry from "@sentry/nextjs";
import { getEnvBoolean, getEnvVariable, getNextRuntime, getNodeEnvironment } from "@hexclave/shared/dist/utils/env";
import { getEnvBoolean, getEnvVariable, getNodeEnvironment } from "@hexclave/shared/dist/utils/env";
import { sentryBaseConfig } from "@hexclave/shared/dist/utils/sentry";
import { nicify } from "@hexclave/shared/dist/utils/strings";
import "./polyfills";

async function startRemoteDevelopmentEnvironmentLifecycleIfNeeded(): Promise<void> {
if (getNextRuntime() !== "nodejs" || getEnvVariable("NEXT_PUBLIC_STACK_IS_REMOTE_DEVELOPMENT_ENVIRONMENT", "") !== "true") {
export async function register(): Promise<void> {
if (process.env.NEXT_RUNTIME !== "nodejs" && process.env.NEXT_RUNTIME !== "edge") {
return;
}

const { startRemoteDevelopmentEnvironmentLifecycle } = await import("./lib/remote-development-environment/manager");
startRemoteDevelopmentEnvironmentLifecycle();
}

export async function register() {
if (getNextRuntime() === "nodejs") {
if (process.env.NEXT_RUNTIME === "nodejs") {
if (getEnvBoolean("NEXT_PUBLIC_STACK_IS_REMOTE_DEVELOPMENT_ENVIRONMENT")) {
globalThis.process.title = `Hexclave — Development Server (port ${getEnvVariable("PORT", "?")})`;
} else {
globalThis.process.title = `stack-dashboard:${getEnvVariable("NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX", "81")} (node/nextjs)`;
}
const { startRemoteDevelopmentEnvironmentLifecycleIfNeeded } = await import("./instrumentation-node");
await startRemoteDevelopmentEnvironmentLifecycleIfNeeded();
}

if (getNextRuntime() === "nodejs" || getNextRuntime() === "edge") {
Sentry.init({
...sentryBaseConfig,

dsn: getEnvVariable("NEXT_PUBLIC_SENTRY_DSN", ""),

enabled: getNodeEnvironment() !== "development" && !getEnvVariable("CI", ""),

// Add exception metadata to the event
beforeSend(event, hint) {
const error = hint.originalException;
let nicified;
try {
nicified = nicify(error, { maxDepth: 8 });
} catch (e) {
nicified = `Error occurred during nicification: ${e}`;
}
if (error instanceof Error) {
event.extra = {
...event.extra,
cause: error.cause,
errorProps: {
...error,
},
nicifiedError: nicified,
};
}
return event;
},
});

}
Sentry.init({
...sentryBaseConfig,

dsn: getEnvVariable("NEXT_PUBLIC_SENTRY_DSN", ""),

enabled: getNodeEnvironment() !== "development" && !getEnvVariable("CI", ""),

beforeSend(event, hint) {
const error = hint.originalException;
let nicified;
try {
nicified = nicify(error, { maxDepth: 8 });
} catch (nicifyError: unknown) {
// Only nicify errors are converted to metadata; the original event still reports.
const nicifyErrorMessage = nicifyError instanceof Error ? nicifyError.message : String(nicifyError);
nicified = `Error occurred during nicification: ${nicifyErrorMessage}`;
}
if (error instanceof Error) {
event.extra = {
...event.extra,
cause: error.cause,
errorProps: {
...error,
},
nicifiedError: nicified,
};
}
return event;
},
});
}
19 changes: 15 additions & 4 deletions docs/src/stack.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { StackServerApp } from '@hexclave/next';
import "server-only";

const placeholderPrefix = "#";
const replaceMePlaceholder = "REPLACE_ME";

function envOrUndefined(value: string | undefined): string | undefined {
const normalized = value?.trim().replace(/^["']|["']$/g, "");
if (normalized == null || normalized === "" || normalized === replaceMePlaceholder || normalized.startsWith(placeholderPrefix) || normalized.startsWith("$(")) {
return undefined;
}
return normalized;
}

// Explicitly configure Stack Auth for docs app
export const stackServerApp = new StackServerApp({
tokenStore: "nextjs-cookie",
projectId: process.env.NEXT_PUBLIC_HEXCLAVE_PROJECT_ID ?? process.env.NEXT_PUBLIC_STACK_PROJECT_ID,
publishableClientKey: process.env.NEXT_PUBLIC_HEXCLAVE_PUBLISHABLE_CLIENT_KEY ?? process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY,
secretServerKey: process.env.HEXCLAVE_SECRET_SERVER_KEY ?? process.env.STACK_SECRET_SERVER_KEY,
baseUrl: process.env.NEXT_PUBLIC_HEXCLAVE_API_URL ?? process.env.NEXT_PUBLIC_STACK_API_URL,
projectId: envOrUndefined(process.env.NEXT_PUBLIC_HEXCLAVE_PROJECT_ID) ?? envOrUndefined(process.env.NEXT_PUBLIC_STACK_PROJECT_ID) ?? "internal",
publishableClientKey: envOrUndefined(process.env.NEXT_PUBLIC_HEXCLAVE_PUBLISHABLE_CLIENT_KEY) ?? envOrUndefined(process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY),
secretServerKey: envOrUndefined(process.env.HEXCLAVE_SECRET_SERVER_KEY) ?? envOrUndefined(process.env.STACK_SECRET_SERVER_KEY) ?? "this-secret-server-key-is-for-local-development-only",
baseUrl: envOrUndefined(process.env.NEXT_PUBLIC_HEXCLAVE_API_URL) ?? envOrUndefined(process.env.NEXT_PUBLIC_STACK_API_URL),
analytics: {
replays: {
enabled: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/template/src/providers/stack-context.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import React from "react";
import { createGlobal } from "@stackframe/stack-shared/dist/utils/globals";
import { createGlobal } from "@hexclave/shared/dist/utils/globals";
import type { StackClientApp } from "../lib/stack-app/apps/interfaces/client-app";

type StackContextValue = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import React from "react";
import { createGlobal } from "@stackframe/stack-shared/dist/utils/globals";
import { createGlobal } from "@hexclave/shared/dist/utils/globals";

type TranslationContextValue = {
quetzalKeys: Map<string, string>,
Expand Down
Loading