diff --git a/.claude/skills/indexing-diagnostics/SKILL.md b/.claude/skills/indexing-diagnostics/SKILL.md index fbc814da78..1a8b1bd92a 100644 --- a/.claude/skills/indexing-diagnostics/SKILL.md +++ b/.claude/skills/indexing-diagnostics/SKILL.md @@ -1288,7 +1288,7 @@ Path A is faster, doesn't require server config changes, and works against stagi ### Path A — direct token mint (the common case) -The user runs `mise run claude-prerender-token []`. The script (`packages/realm-server/scripts/claude-prerender-token.ts`, executed via `ts-node --transpileOnly` from inside `packages/realm-server`) mints a `boxel-session` JWT the same way the indexer does — same claims, same HS256/1d, same `JSON.stringify({ : })` map shape that lands in `localStorage['boxel-session']`. Faithful CLI port of `buildCreatePrerenderAuth` (`packages/realm-server/prerender/auth.ts`). +The user runs `mise run claude-prerender-token []`. The script (`packages/realm-server/scripts/claude-prerender-token.ts`, executed via `node` from inside `packages/realm-server`) mints a `boxel-session` JWT the same way the indexer does — same claims, same HS256/1d, same `JSON.stringify({ : })` map shape that lands in `localStorage['boxel-session']`. Faithful CLI port of `buildCreatePrerenderAuth` (`packages/realm-server/prerender/auth.ts`). **The CLI is intentionally minimal: just `` and an optional `` positional.** If `` is omitted the script reads from `process.stdin` — interactively that's a masked TTY paste prompt (each char echoes as `*`, paste-friendly, no shell history); piped/redirected stdin is read in full and trimmed. Optional flags: `--user ` (required only for system realms), `--permissions `, `--output `, `--no-output`. That's it. The token is realm-scoped, not card-scoped — Claude builds the `/render` URL itself for whichever card it's investigating. diff --git a/.eslintrc.js b/.eslintrc.js index 0d12a3c49b..1f871c456c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -53,8 +53,8 @@ module.exports = { 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], - // Keep new code "erasable" so it can run under Node's - // `--experimental-strip-types` without ts-node. + // Keep new code "erasable" so Node can run it via + // `--experimental-strip-types` (type-only syntax that vanishes when stripped). '@typescript-eslint/parameter-properties': [ 'error', { prefer: 'class-property' }, diff --git a/.github/workflows/boxel-cli-publish.yml b/.github/workflows/boxel-cli-publish.yml index 05c5603266..a7165b3982 100644 --- a/.github/workflows/boxel-cli-publish.yml +++ b/.github/workflows/boxel-cli-publish.yml @@ -125,7 +125,7 @@ jobs: PR_BODY: ${{ steps.pr.outputs.body }} run: | set -euo pipefail - RESULT=$(NODE_NO_WARNINGS=1 pnpm exec ts-node --transpileOnly scripts/compute-release.ts) + RESULT=$(NODE_NO_WARNINGS=1 pnpm exec node scripts/compute-release.ts) echo "Compute result: $RESULT" { echo "json<<__REL_EOF__" @@ -205,7 +205,7 @@ jobs: RELEASE_BODY_FILE="$BODY_FILE" \ CHANGELOG_FRAGMENT_FILE="$FRAGMENT_FILE" \ NODE_NO_WARNINGS=1 pnpm --filter @cardstack/boxel-cli exec \ - ts-node --transpileOnly scripts/generate-release-notes.ts + node scripts/generate-release-notes.ts echo "has=true" >> "$GITHUB_OUTPUT" echo "body_file=$BODY_FILE" >> "$GITHUB_OUTPUT" @@ -441,7 +441,7 @@ jobs: RELEASE_BODY_FILE="$BODY_FILE" \ CHANGELOG_FRAGMENT_FILE="$FRAGMENT_FILE" \ NODE_NO_WARNINGS=1 pnpm --filter @cardstack/boxel-cli exec \ - ts-node --transpileOnly scripts/generate-release-notes.ts + node scripts/generate-release-notes.ts FRAG_FILE="$FRAGMENT_FILE" node -e ' const fs = require("fs"); const path = "packages/boxel-cli/CHANGELOG.md"; diff --git a/.github/workflows/ci-software-factory.yaml b/.github/workflows/ci-software-factory.yaml index 6526207b5f..cf2a6bbab5 100644 --- a/.github/workflows/ci-software-factory.yaml +++ b/.github/workflows/ci-software-factory.yaml @@ -75,7 +75,10 @@ jobs: shopt -s dotglob cp -a .test-web-assets-artifact/. ./ - name: Install Playwright Browsers - run: pnpm exec playwright install + # --with-deps also apt-installs the system libraries Playwright needs; + # without it CI fails with "Host system is missing dependencies to run + # browsers". + run: pnpm exec playwright install --with-deps working-directory: packages/software-factory - name: Run Node tests if: ${{ matrix.shard.index == 1 }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 70dd2a5a48..42235bf37f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -595,7 +595,7 @@ jobs: cp -a .test-web-assets-artifact/. ./ - name: Compute shard test modules id: shard_modules - run: echo "modules=$(node scripts/shard-test-modules.js ${{ matrix.shardIndex }} ${{ matrix.shardTotal }})" >> "$GITHUB_OUTPUT" + run: echo "modules=$(node scripts/shard-test-modules.cjs ${{ matrix.shardIndex }} ${{ matrix.shardTotal }})" >> "$GITHUB_OUTPUT" working-directory: packages/realm-server # Warm the test images from the GHCR mirror so the services # below start from a local image instead of an unauthenticated Docker Hub diff --git a/eslint/erasable-syntax-selectors.cjs b/eslint/erasable-syntax-selectors.cjs index 31150a86f9..eed739e0a6 100644 --- a/eslint/erasable-syntax-selectors.cjs +++ b/eslint/erasable-syntax-selectors.cjs @@ -3,7 +3,7 @@ // Selectors for TypeScript syntax that Node cannot run via // `--experimental-strip-types`, which only handles "erasable" TS — // syntax that vanishes once type annotations are stripped. New code -// must avoid these so it can run under Node natively, without ts-node. +// must avoid these so it can run under Node natively. // // Shared by the root `.eslintrc.js`, `packages/ai-bot/.eslintrc.js` // (which is `root: true`), and `packages/catalog/.eslintrc.cjs` (which diff --git a/mise-tasks/claude-prerender-token b/mise-tasks/claude-prerender-token index 9f4c8f51d0..4c3675fd53 100755 --- a/mise-tasks/claude-prerender-token +++ b/mise-tasks/claude-prerender-token @@ -2,4 +2,4 @@ #MISE description="Mint a realm-scoped boxel-session JWT (the indexer's auth shape) and write to /tmp/claude-prerender.json for Claude to drive a live browser render" #MISE dir="packages/realm-server" -exec ts-node --transpileOnly ./scripts/claude-prerender-token.ts "$@" +exec node ./scripts/claude-prerender-token.ts "$@" diff --git a/mise-tasks/infra/clear-modules-cache b/mise-tasks/infra/clear-modules-cache index b963a1e3f0..adcdce7006 100755 --- a/mise-tasks/infra/clear-modules-cache +++ b/mise-tasks/infra/clear-modules-cache @@ -6,4 +6,4 @@ echo "Clearing modules cache..." PGPORT="${PGPORT:-5435}" PGDATABASE="${PGDATABASE:-boxel}" \ NODE_NO_WARNINGS=1 \ - ts-node --transpileOnly scripts/clear-modules-cache.ts + node scripts/clear-modules-cache.ts diff --git a/mise-tasks/infra/start-admin b/mise-tasks/infra/start-admin index 09ed70ce8c..82927f2761 100755 --- a/mise-tasks/infra/start-admin +++ b/mise-tasks/infra/start-admin @@ -2,4 +2,4 @@ #MISE description="Start the Matrix admin console (port 8080)" #MISE dir="packages/matrix" -exec ts-node --transpileOnly ./scripts/admin-console +exec node ./scripts/admin-console.ts diff --git a/mise-tasks/infra/stop-synapse b/mise-tasks/infra/stop-synapse index d8cf9a5b54..bc5b392066 100755 --- a/mise-tasks/infra/stop-synapse +++ b/mise-tasks/infra/stop-synapse @@ -2,4 +2,4 @@ #MISE description="Stop the Matrix Synapse server" #MISE dir="packages/matrix" -exec ts-node --transpileOnly ./scripts/synapse.ts stop +exec node ./scripts/synapse.ts stop diff --git a/mise-tasks/lib/dev-common.sh b/mise-tasks/lib/dev-common.sh index 2ebe8c7da9..25af829d90 100644 --- a/mise-tasks/lib/dev-common.sh +++ b/mise-tasks/lib/dev-common.sh @@ -230,7 +230,7 @@ _kill_tree_walk() { # Sweep orphaned dev-stack processes scoped to this checkout. mise's task # supervisor reparents long-running task scripts to init, and the wrapper -# layers above ts-node (pnpm, npm exec, run-p, vite, start-server-and-test) +# layers above the node entrypoints (pnpm, npm exec, run-p, vite, start-server-and-test) # routinely exit without relaying SIGTERM to their grandchildren — so a # tree-walk from a known pid can't reach all of them via PPID. SIGTERM first # for anything that listens, brief grace, then SIGKILL. @@ -245,11 +245,15 @@ _kill_tree_walk() { # # The patterns: # - mise-tasks/services/* — the bash service entrypoints -# - node_modules.*--transpileOnly (worker|main|prerender) — ts-node -# grandchildren that actually hold the realm/worker/prerender ports -# (4201/4202, 4210/4211, 4221/4222). Wrappers that just invoke ts-node -# don't `exec` it, so killing the wrapper alone leaves the ts-node -# grandchild reparented to init with its port still bound. +# - node .ts (main / worker-manager / prerender/*-server) — the +# native-Node grandchildren that actually hold the realm/worker/prerender +# ports (4201/4202, 4210/4211, 4221/4222). They run with +# cwd=packages/realm-server and relative argv, so $REPO_ROOT can't anchor +# the match; the entry names plus a distinctive flag keep it +# Boxel-specific, and the early-return guard above prevents +# sibling-checkout collisions. Wrappers that just invoke node don't +# `exec` it, so killing the wrapper alone leaves the node grandchild +# reparented to init with its port still bound. # - scripts/vite-serve.js — the host start wrapper that spawns the # actual vite child. Can't anchor to $REPO_ROOT because pnpm invokes # it as `node scripts/vite-serve.js` (relative argv, cwd-relative), @@ -287,7 +291,7 @@ sweep_orphaned_services() { return 0 fi REPO_ROOT_RE="$(printf '%s' "$REPO_ROOT" | sed -E 's/[][\\.*^$+?(){}|]/\\&/g')" - TSNODE_RE="${REPO_ROOT_RE}/packages/realm-server/node_modules.*--transpileOnly (worker|main|prerender)" + REALM_NODE_RE="(main\.ts .*--realmsRootPath|worker-manager\.ts .*--allPriorityCount|prerender/(prerender|manager)-server\.ts)" VITE_SERVE_RE="scripts/vite-serve\.js" VITE_BIN_RE="${REPO_ROOT_RE}/packages/host/.*vite/bin/vite\.js" SAT_RE="${REPO_ROOT_RE}/.*node_modules/.*start-server-and-test/src/bin/start\.js" @@ -296,7 +300,7 @@ sweep_orphaned_services() { for sig in TERM KILL; do pkill -"$sig" -f "${REPO_ROOT_RE}/mise-tasks/services/" 2>/dev/null || true - pkill -"$sig" -f "$TSNODE_RE" 2>/dev/null || true + pkill -"$sig" -f "$REALM_NODE_RE" 2>/dev/null || true pkill -"$sig" -f "$VITE_SERVE_RE" 2>/dev/null || true pkill -"$sig" -f "$VITE_BIN_RE" 2>/dev/null || true pkill -"$sig" -f "$SAT_RE" 2>/dev/null || true diff --git a/mise-tasks/services/ai-bot b/mise-tasks/services/ai-bot index 545a116787..5eb9402abb 100755 --- a/mise-tasks/services/ai-bot +++ b/mise-tasks/services/ai-bot @@ -6,4 +6,4 @@ MATRIX_URL="${MATRIX_URL_VAL}" \ NODE_NO_WARNINGS=1 \ PGPORT="${PGPORT}" \ PGDATABASE="${PGDATABASE}" \ - exec ts-node --transpileOnly main + exec node main.ts diff --git a/mise-tasks/services/bot-runner b/mise-tasks/services/bot-runner index 8a715f0122..4b312a4378 100755 --- a/mise-tasks/services/bot-runner +++ b/mise-tasks/services/bot-runner @@ -6,4 +6,4 @@ MATRIX_URL="${MATRIX_URL_VAL}" \ NODE_NO_WARNINGS=1 \ PGPORT="${PGPORT}" \ PGDATABASE="${PGDATABASE}" \ - exec ts-node --transpileOnly main + exec node main.ts diff --git a/mise-tasks/services/prerender b/mise-tasks/services/prerender index 9909fed477..88de792f19 100755 --- a/mise-tasks/services/prerender +++ b/mise-tasks/services/prerender @@ -44,7 +44,7 @@ fi # host is actually browser-ready. if [ -z "$ENV_MODE" ]; then HOST_URL="${BOXEL_HOST_URL:-$DEFAULT_HOST_URL}" \ - ts-node --transpileOnly ./scripts/wait-for-host-standby.ts || exit $? + node ./scripts/wait-for-host-standby.ts || exit $? fi # In local HTTPS dev (vite on https://localhost:4200 with HTTP/2), @@ -61,8 +61,8 @@ NODE_ENV=development \ NODE_NO_WARNINGS=1 \ BOXEL_HOST_URL="${BOXEL_HOST_URL:-$DEFAULT_HOST_URL}" \ PRERENDER_STANDBY_TIMEOUT_MS="${PRERENDER_STANDBY_TIMEOUT_MS:-}" \ - ts-node \ - --transpileOnly prerender/prerender-server \ + node \ + prerender/prerender-server.ts \ --port="${PRERENDER_PORT}" \ --count="${PRERENDER_COUNT:-1}" \ 2>&1 | "$LOG_TEE" prerender diff --git a/mise-tasks/services/prerender-mgr b/mise-tasks/services/prerender-mgr index a3de4293ca..364a59c988 100755 --- a/mise-tasks/services/prerender-mgr +++ b/mise-tasks/services/prerender-mgr @@ -23,8 +23,8 @@ NODE_ENV=development \ NODE_NO_WARNINGS=1 \ PRERENDER_MANAGER_VERBOSE_LOGS=false \ PRERENDER_MULTIPLEX="${PRERENDER_MULTIPLEX:-1}" \ - ts-node \ - --transpileOnly prerender/manager-server \ + node \ + prerender/manager-server.ts \ --port=${PRERENDER_MANAGER_PORT:-$PRERENDER_MGR_PORT} \ --exit-on-signal \ 2>&1 | "$LOG_TEE" prerender-manager diff --git a/mise-tasks/services/realm-server b/mise-tasks/services/realm-server index eeb7567433..61ca190697 100755 --- a/mise-tasks/services/realm-server +++ b/mise-tasks/services/realm-server @@ -23,9 +23,9 @@ cleanup_icons_server() { fi } # EXIT runs cleanup unconditionally. INT/TERM additionally exit 0 so that the -# 143-on-Ctrl-C from the ts-node pipeline below isn't reported by mise / +# 143-on-Ctrl-C from the node pipeline below isn't reported by mise / # run-p as "task failed" — see mise-tasks/services/worker for the broader -# rationale. Real crashes still propagate (ts-node faulting on its own +# rationale. Real crashes still propagate (node faulting on its own # leaves the trap unfired, so pipefail's 143-aware exit code wins). # The signal handler clears the EXIT trap before exiting so cleanup doesn't # run twice in succession. @@ -44,7 +44,7 @@ if [ "${SKIP_CATALOG:-}" != "true" ]; then fi if [ -z "$MATRIX_REGISTRATION_SHARED_SECRET" ]; then - MATRIX_REGISTRATION_SHARED_SECRET=$(ts-node --transpileOnly "$SCRIPTS_DIR/matrix-registration-secret.ts") + MATRIX_REGISTRATION_SHARED_SECRET=$(node "$SCRIPTS_DIR/matrix-registration-secret.ts") export MATRIX_REGISTRATION_SHARED_SECRET fi @@ -93,8 +93,7 @@ LOW_CREDIT_THRESHOLD="${LOW_CREDIT_THRESHOLD:-2000}" \ MATRIX_URL="${MATRIX_URL_VAL}" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ ENABLE_FILE_WATCHER=true \ - ts-node \ - --transpileOnly main \ + node main.ts \ --port="${REALM_PORT}" \ --matrixURL="${MATRIX_URL_VAL}" \ --realmsRootPath="${REALMS_ROOT}" \ diff --git a/mise-tasks/services/realm-server-base b/mise-tasks/services/realm-server-base index b35ac208fa..2b34b382cd 100755 --- a/mise-tasks/services/realm-server-base +++ b/mise-tasks/services/realm-server-base @@ -9,7 +9,7 @@ trap 'exit 0' INT TERM if [ -z "$MATRIX_REGISTRATION_SHARED_SECRET" ]; then - MATRIX_REGISTRATION_SHARED_SECRET=$(ts-node --transpileOnly ./scripts/matrix-registration-secret.ts) + MATRIX_REGISTRATION_SHARED_SECRET=$(node ./scripts/matrix-registration-secret.ts) export MATRIX_REGISTRATION_SHARED_SECRET fi @@ -22,8 +22,7 @@ NODE_ENV=development \ GRAFANA_SECRET="shhh! it's a secret" \ MATRIX_URL="${MATRIX_URL_VAL}" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ - ts-node \ - --transpileOnly main \ + node main.ts \ --port=4201 \ --matrixURL="${MATRIX_URL_VAL}" \ --realmsRootPath='./realms/localhost_4201_base' \ diff --git a/mise-tasks/services/test-realms b/mise-tasks/services/test-realms index 830edb6c62..2000a46f83 100755 --- a/mise-tasks/services/test-realms +++ b/mise-tasks/services/test-realms @@ -55,7 +55,7 @@ fi pnpm --dir=../skills-realm skills:setup if [ -z "$MATRIX_REGISTRATION_SHARED_SECRET" ]; then - MATRIX_REGISTRATION_SHARED_SECRET=$(ts-node --transpileOnly "$SCRIPTS_DIR/matrix-registration-secret.ts") + MATRIX_REGISTRATION_SHARED_SECRET=$(node "$SCRIPTS_DIR/matrix-registration-secret.ts") export MATRIX_REGISTRATION_SHARED_SECRET fi @@ -68,8 +68,7 @@ NODE_ENV=test \ GRAFANA_SECRET="shhh! it's a secret" \ MATRIX_URL="${MATRIX_URL_VAL}" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ - ts-node \ - --transpileOnly main \ + node main.ts \ --port="${TEST_PORT}" \ --matrixURL="${MATRIX_URL_VAL}" \ --realmsRootPath="${REALMS_TEST_ROOT}" \ diff --git a/mise-tasks/services/worker b/mise-tasks/services/worker index 25ec3c7936..e364c4de11 100755 --- a/mise-tasks/services/worker +++ b/mise-tasks/services/worker @@ -13,7 +13,7 @@ set -o pipefail # SIGTERM this script during Ctrl-C; without this trap the pipefail-aware # pipe below exits 143, mise reports it as "task failed", and run-p prints # `ERROR: "start:worker-development" exited with 143` — pure shutdown -# noise. Real crashes (ts-node faulting on its own) still propagate, since +# noise. Real crashes (node faulting on its own) still propagate, since # the trap only fires when *we* receive the signal. trap 'exit 0' INT TERM @@ -40,8 +40,7 @@ NODE_ENV=development \ REALM_SERVER_MATRIX_USERNAME=realm_server \ LOW_CREDIT_THRESHOLD=2000 \ OPENROUTER_REALM_URL="${REALM_BASE_URL}/openrouter/" \ - ts-node \ - --transpileOnly worker-manager \ + node worker-manager.ts \ --allPriorityCount="${WORKER_ALL_PRIORITY_COUNT:-1}" \ --highPriorityCount="${WORKER_HIGH_PRIORITY_COUNT:-0}" \ --port="${WORKER_PORT}" \ diff --git a/mise-tasks/services/worker-base b/mise-tasks/services/worker-base index 5bdb2ed5a5..47961a9667 100755 --- a/mise-tasks/services/worker-base +++ b/mise-tasks/services/worker-base @@ -16,8 +16,7 @@ NODE_ENV=development \ REALM_SECRET_SEED="shhh! it's a secret" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ LOW_CREDIT_THRESHOLD=2000 \ - ts-node \ - --transpileOnly worker-manager \ + node worker-manager.ts \ --port=4213 \ --matrixURL="${MATRIX_URL_VAL}" \ --prerendererUrl="${PRERENDER_MGR_URL}" \ diff --git a/mise-tasks/services/worker-test b/mise-tasks/services/worker-test index d54de08330..9443d4a81b 100755 --- a/mise-tasks/services/worker-test +++ b/mise-tasks/services/worker-test @@ -28,8 +28,7 @@ NODE_ENV=test \ REALM_SECRET_SEED="shhh! it's a secret" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ LOW_CREDIT_THRESHOLD=2000 \ - ts-node \ - --transpileOnly worker-manager \ + node worker-manager.ts \ --port="${WORKER_TEST_PORT}" \ --matrixURL="${MATRIX_URL_VAL}" \ --prerendererUrl="${PRERENDER_MGR_URL}" \ diff --git a/packages/ai-bot/.eslintrc.js b/packages/ai-bot/.eslintrc.cjs similarity index 100% rename from packages/ai-bot/.eslintrc.js rename to packages/ai-bot/.eslintrc.cjs diff --git a/packages/ai-bot/README.md b/packages/ai-bot/README.md index c4dda479b2..9d2b582596 100644 --- a/packages/ai-bot/README.md +++ b/packages/ai-bot/README.md @@ -37,8 +37,8 @@ Start the server with `pnpm start` or `pnpm start-dev` for live reload. ### Async graphs (Clinic Bubbleprof) -- Run Bubbleprof attached to ts-node: - - `AI_BOT_PROF=1 DISABLE_MATRIX_JS_LOGGING=1 LOG_LEVELS="ai-bot=debug" pnpm dlx clinic bubbleprof --dest .clinic/ai-bot-bp -- node --require ts-node/register/transpile-only main.ts` +- Run Bubbleprof attached to the node process: + - `AI_BOT_PROF=1 DISABLE_MATRIX_JS_LOGGING=1 LOG_LEVELS="ai-bot=debug" pnpm dlx clinic bubbleprof --dest .clinic/ai-bot-bp -- node main.ts` - Drive one interaction; stop with Ctrl+C twice (~200–500ms apart) or send `SIGTERM` from another terminal. - Open the report: - `pnpm dlx clinic open .clinic/ai-bot-bp` diff --git a/packages/ai-bot/lib/code-patch-correctness.ts b/packages/ai-bot/lib/code-patch-correctness.ts index 3daa1699cc..7693ad9c22 100644 --- a/packages/ai-bot/lib/code-patch-correctness.ts +++ b/packages/ai-bot/lib/code-patch-correctness.ts @@ -1,5 +1,5 @@ import type { MatrixClient } from 'matrix-js-sdk'; -import type { RoomMessageEventContent } from 'matrix-js-sdk/lib/@types/events'; +import type { RoomMessageEventContent } from 'matrix-js-sdk/lib/@types/events.js'; import { uuidv4 } from '@cardstack/runtime-common'; import type { PendingCodePatchCorrectnessCheck } from '@cardstack/runtime-common/ai/types'; diff --git a/packages/ai-bot/lib/debug.ts b/packages/ai-bot/lib/debug.ts index 3bd511dae0..f3e5ddd3ef 100644 --- a/packages/ai-bot/lib/debug.ts +++ b/packages/ai-bot/lib/debug.ts @@ -1,7 +1,7 @@ import { setTitle } from './set-title.ts'; import type OpenAI from 'openai'; -import * as Sentry from '@sentry/node'; +import { errorReporter } from './sentry.ts'; import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/base/matrix-event'; import { getPromptParts, @@ -78,7 +78,7 @@ To patch a card:\n customMessage, ); } catch (error) { - Sentry.captureException(error, { + errorReporter.captureException(error, { extra: { roomId: roomId, userId: userId, @@ -133,7 +133,7 @@ To patch a card:\n ); } } catch (error) { - Sentry.captureException(error, { + errorReporter.captureException(error, { extra: { roomId: roomId, userId: userId, diff --git a/packages/ai-bot/lib/responder.ts b/packages/ai-bot/lib/responder.ts index cfa0c0366f..111c59cea8 100644 --- a/packages/ai-bot/lib/responder.ts +++ b/packages/ai-bot/lib/responder.ts @@ -2,10 +2,10 @@ import { logger } from '@cardstack/runtime-common'; import { APP_BOXEL_CODE_PATCH_CORRECTNESS_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; import { isCommandOrCodePatchResult } from '@cardstack/runtime-common/ai'; -import * as Sentry from '@sentry/node'; +import { errorReporter } from './sentry.ts'; import type { OpenAIError } from 'openai/error'; -import throttle from 'lodash/throttle'; -import type { ISendEventResponse } from 'matrix-js-sdk/lib/matrix'; +import { throttle } from 'lodash-es'; +import type { ISendEventResponse } from 'matrix-js-sdk/lib/matrix.js'; import type { ChatCompletionMessageFunctionToolCall } from 'openai/resources/chat/completions'; import type { FunctionToolCall } from '@cardstack/runtime-common/helpers/ai'; import type OpenAI from 'openai'; @@ -188,7 +188,7 @@ export class Responder { if (this.responseState.isStreamingFinished) { return; } - Sentry.captureException(error, { + errorReporter.captureException(error, { extra: { roomId: this.matrixResponsePublisher.roomId, agentId: this.matrixResponsePublisher.agentId, diff --git a/packages/ai-bot/lib/sentry.ts b/packages/ai-bot/lib/sentry.ts new file mode 100644 index 0000000000..3b9dc55a2b --- /dev/null +++ b/packages/ai-bot/lib/sentry.ts @@ -0,0 +1,13 @@ +import * as Sentry from '@sentry/node'; + +// Indirection over Sentry so tests can replace `captureException`. Under native +// ESM, `import * as Sentry` is a sealed module namespace whose bindings are +// read-only, so tests can't stub `Sentry.captureException` directly. Calling +// through this mutable object lets a test swap the method and restore it. +export const errorReporter = { + captureException( + ...args: Parameters + ): ReturnType { + return Sentry.captureException(...args); + }, +}; diff --git a/packages/ai-bot/main.ts b/packages/ai-bot/main.ts index 353cf05cbb..7cbddf4a63 100644 --- a/packages/ai-bot/main.ts +++ b/packages/ai-bot/main.ts @@ -2,7 +2,10 @@ import './instrument.ts'; import './setup-logger.ts'; // This should be first import type { MatrixEvent } from 'matrix-js-sdk'; import { RoomMemberEvent, RoomEvent, createClient } from 'matrix-js-sdk'; -import { SlidingSync, type MSC3575List } from 'matrix-js-sdk/lib/sliding-sync'; +import { + SlidingSync, + type MSC3575List, +} from 'matrix-js-sdk/lib/sliding-sync.js'; import OpenAI from 'openai'; import { logger, @@ -47,11 +50,12 @@ import { APIUserAbortError } from 'openai/error'; import type { OpenAIError } from 'openai/error'; import type { ChatCompletionStream } from 'openai/lib/ChatCompletionStream'; import { acquireRoomLock, releaseRoomLock } from './lib/queries.ts'; -import { DebugLogger } from 'matrix-js-sdk/lib/logger'; +import { DebugLogger } from 'matrix-js-sdk/lib/logger.js'; import { setupSignalHandlers } from './lib/signal-handlers.ts'; import { isShuttingDown, setActiveGenerations } from './lib/shutdown.ts'; import type { MatrixClient } from 'matrix-js-sdk'; -import { debug } from 'debug'; +import createDebug from 'debug'; +const { debug } = createDebug; import { profEnabled, profTime, profNote } from './lib/profiler.ts'; import { publishCodePatchCorrectnessMessage } from './lib/code-patch-correctness.ts'; import { diff --git a/packages/ai-bot/package.json b/packages/ai-bot/package.json index 7f85659e6f..6e6a42b90b 100644 --- a/packages/ai-bot/package.json +++ b/packages/ai-bot/package.json @@ -1,22 +1,22 @@ { "name": "@cardstack/ai-bot", + "type": "module", "dependencies": { "@cardstack/billing": "workspace:*", "@cardstack/postgres": "workspace:*", "@cardstack/runtime-common": "workspace:*", "@sentry/node": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/node": "catalog:", "@types/stream-chain": "catalog:", "@types/stream-json": "catalog:", "debug": "^4.4.3", - "lodash": "catalog:", + "lodash-es": "catalog:", "matrix-js-sdk": "catalog:", "openai": "catalog:", "qunit": "catalog:", "stream-chain": "catalog:", "stream-json": "catalog:", - "ts-node": "^10.9.2", "typescript": "catalog:" }, "devDependencies": { @@ -39,8 +39,8 @@ "lint:js": "eslint . --report-unused-disable-directives --cache", "lint:js:fix": "eslint . --report-unused-disable-directives --fix", "lint:types": "ember-tsc --noEmit", - "start": "NODE_NO_WARNINGS=1 ts-node --transpileOnly main", - "test": "NODE_NO_WARNINGS=1 qunit --require ts-node/register/transpile-only tests/index.ts", - "get-chat": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/get_chat.ts" + "start": "NODE_NO_WARNINGS=1 node main.ts", + "test": "NODE_NO_WARNINGS=1 node tests/index.ts", + "get-chat": "NODE_NO_WARNINGS=1 node scripts/get_chat.ts" } } diff --git a/packages/ai-bot/scripts/start-development.sh b/packages/ai-bot/scripts/start-development.sh index ac1e8ad8da..eafd368c9f 100644 --- a/packages/ai-bot/scripts/start-development.sh +++ b/packages/ai-bot/scripts/start-development.sh @@ -32,4 +32,4 @@ else export PGDATABASE="${PGDATABASE:-boxel}" fi -exec ts-node --transpileOnly main +exec node main.ts diff --git a/packages/ai-bot/tests/chat-titling-test.ts b/packages/ai-bot/tests/chat-titling-test.ts index 4b76b97986..6385fec665 100644 --- a/packages/ai-bot/tests/chat-titling-test.ts +++ b/packages/ai-bot/tests/chat-titling-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { getLatestResultMessage, setTitle, diff --git a/packages/ai-bot/tests/code-patch-correctness-test.ts b/packages/ai-bot/tests/code-patch-correctness-test.ts index fbea6e8306..3cc5d90be5 100644 --- a/packages/ai-bot/tests/code-patch-correctness-test.ts +++ b/packages/ai-bot/tests/code-patch-correctness-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { publishCodePatchCorrectnessMessage } from '../lib/code-patch-correctness.ts'; import { FakeMatrixClient } from './helpers/fake-matrix-client.ts'; diff --git a/packages/ai-bot/tests/credit-tracking-test.ts b/packages/ai-bot/tests/credit-tracking-test.ts index d463728da7..2ed2385272 100644 --- a/packages/ai-bot/tests/credit-tracking-test.ts +++ b/packages/ai-bot/tests/credit-tracking-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import FakeTimers from '@sinonjs/fake-timers'; import type { DBAdapter } from '@cardstack/runtime-common'; import { diff --git a/packages/ai-bot/tests/history-construction-test.ts b/packages/ai-bot/tests/history-construction-test.ts index 75409b7e4c..a439145475 100644 --- a/packages/ai-bot/tests/history-construction-test.ts +++ b/packages/ai-bot/tests/history-construction-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { APP_BOXEL_COMMAND_RESULT_EVENT_TYPE, APP_BOXEL_COMMAND_RESULT_REL_TYPE, diff --git a/packages/ai-bot/tests/index.ts b/packages/ai-bot/tests/index.ts index 698197141d..e31d569d96 100644 --- a/packages/ai-bot/tests/index.ts +++ b/packages/ai-bot/tests/index.ts @@ -1,3 +1,5 @@ +import './qunit-bootstrap.ts'; // configures QUnit before any test registers +import QUnit from 'qunit'; import '../setup-logger.ts'; // This should be first import './response-parsing-test.ts'; import './history-construction-test.ts'; @@ -10,3 +12,5 @@ import './modality-test.ts'; import './locking-test.ts'; import './interrupt-test.ts'; import './credit-tracking-test.ts'; + +QUnit.start(); diff --git a/packages/ai-bot/tests/interrupt-test.ts b/packages/ai-bot/tests/interrupt-test.ts index d14c67322c..73ee0d18ae 100644 --- a/packages/ai-bot/tests/interrupt-test.ts +++ b/packages/ai-bot/tests/interrupt-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { Responder } from '../lib/responder.ts'; import { FakeMatrixClient } from './helpers/fake-matrix-client.ts'; import FakeTimers from '@sinonjs/fake-timers'; import type { ChatCompletionSnapshot } from 'openai/lib/ChatCompletionStream'; -import * as Sentry from '@sentry/node'; +import { errorReporter } from '../lib/sentry.ts'; import { OpenAIError } from 'openai'; function snapshotWithContent(content: string): ChatCompletionSnapshot { @@ -217,8 +218,8 @@ module('Responder Cancellation', (hooks) => { test('onError does not report to Sentry after cancellation', async (assert) => { let sentryCalls: any[] = []; - let originalCaptureException = Sentry.captureException; - (Sentry as any).captureException = (...args: any[]) => { + let originalCaptureException = errorReporter.captureException; + errorReporter.captureException = (...args: any[]) => { sentryCalls.push(args); return ''; }; @@ -246,7 +247,7 @@ module('Responder Cancellation', (hooks) => { 'Sentry should NOT be called after finalize — prevents false alarms from cancellation', ); } finally { - (Sentry as any).captureException = originalCaptureException; + errorReporter.captureException = originalCaptureException; } }); diff --git a/packages/ai-bot/tests/locking-test.ts b/packages/ai-bot/tests/locking-test.ts index 8ba9d25d50..ae1ec87013 100644 --- a/packages/ai-bot/tests/locking-test.ts +++ b/packages/ai-bot/tests/locking-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { PgAdapter } from '@cardstack/postgres'; import { acquireRoomLock, releaseRoomLock } from '../lib/queries.ts'; import { Client } from 'pg'; diff --git a/packages/ai-bot/tests/matrix-util-test.ts b/packages/ai-bot/tests/matrix-util-test.ts index a0d5a8a432..218fbb911d 100644 --- a/packages/ai-bot/tests/matrix-util-test.ts +++ b/packages/ai-bot/tests/matrix-util-test.ts @@ -1,4 +1,5 @@ -import { test } from 'qunit'; +import QUnit from 'qunit'; +const { test } = QUnit; import { FakeMatrixClient } from './helpers/fake-matrix-client.ts'; import type { Method } from 'matrix-js-sdk'; import type { diff --git a/packages/ai-bot/tests/modality-test.ts b/packages/ai-bot/tests/modality-test.ts index db21bae182..04bb4e335d 100644 --- a/packages/ai-bot/tests/modality-test.ts +++ b/packages/ai-bot/tests/modality-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { requiredModality, modalityLabel, diff --git a/packages/ai-bot/tests/prompt-construction-test.ts b/packages/ai-bot/tests/prompt-construction-test.ts index 59b084a922..ce24a81599 100644 --- a/packages/ai-bot/tests/prompt-construction-test.ts +++ b/packages/ai-bot/tests/prompt-construction-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { getPatchTool } from '@cardstack/runtime-common/helpers/ai'; import type { ChatCompletionMessageFunctionToolCall } from 'openai/resources/chat/completions'; import { @@ -25,7 +26,8 @@ import type { } from 'https://cardstack.com/base/matrix-event'; import { EventStatus } from 'matrix-js-sdk'; import type { CardDef } from 'https://cardstack.com/base/card-api'; -import { readFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { readFileSync } = fsExtra; import * as path from 'path'; import { FakeMatrixClient } from './helpers/fake-matrix-client.ts'; import { @@ -1932,7 +1934,7 @@ Attached Files (files with newer versions don't show their content): test('should include instructions in system prompt for skill cards', async () => { const rawEvents = readFileSync( - path.join(__dirname, 'resources/chats/added-skill.json'), + path.join(import.meta.dirname, 'resources/chats/added-skill.json'), 'utf-8', ); const eventList: DiscreteMatrixEvent[] = JSON.parse( @@ -2011,7 +2013,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/added-skill-and-attached-card.json', ), 'utf-8', @@ -2129,7 +2131,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/added-two-skills-removed-one-skill.json', ), 'utf-8', @@ -2205,7 +2207,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/added-two-skills-removed-two-skills.json', ), 'utf-8', @@ -2232,7 +2234,7 @@ Attached Files (files with newer versions don't show their content): // handle that. const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/skill-card-no-id.json'), + path.join(import.meta.dirname, 'resources/chats/skill-card-no-id.json'), 'utf-8', ), ); @@ -2293,7 +2295,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/two-messages-with-same-skill-card.json', ), 'utf-8', @@ -2341,7 +2343,10 @@ Attached Files (files with newer versions don't show their content): test('if tool calls are required, ensure they are set', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/forced-function-call.json'), + path.join( + import.meta.dirname, + 'resources/chats/forced-function-call.json', + ), 'utf-8', ), ); @@ -2717,7 +2722,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/required-tools-multiple-messages.json', ), 'utf-8', @@ -2743,7 +2748,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/required-tools-multiple-messages.json', ), 'utf-8', @@ -2762,7 +2767,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/required-tool-call-in-last-message.json', ), 'utf-8', @@ -2786,7 +2791,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/connect-tool-calls-to-results.json', ), 'utf-8', @@ -2836,7 +2841,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/invoke-submode-swith-command.json', ), 'utf-8', @@ -2960,7 +2965,10 @@ Attached Files (files with newer versions don't show their content): test('Does not respond to first tool call result when two tool calls were made', async function () { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/two-tool-calls-one-result.json'), + path.join( + import.meta.dirname, + 'resources/chats/two-tool-calls-one-result.json', + ), 'utf-8', ), ); @@ -3002,7 +3010,10 @@ Attached Files (files with newer versions don't show their content): test('Responds to second tool call result when two tool calls were made', async function () { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/two-tool-calls-two-results.json'), + path.join( + import.meta.dirname, + 'resources/chats/two-tool-calls-two-results.json', + ), 'utf-8', ), ); @@ -3080,7 +3091,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/enabled-skill-with-commands.json', ), 'utf-8', @@ -3224,7 +3235,7 @@ Attached Files (files with newer versions don't show their content): const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/disabled-skill-with-commands.json', ), 'utf-8', @@ -3256,7 +3267,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/updated-skill-command-definitions.json', ), 'utf-8', @@ -3394,7 +3405,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/two-code-blocks-two-results.json', ), 'utf-8', @@ -3428,7 +3439,10 @@ Current date and time: 2025-06-11T11:43:00.533Z // m.replace messages, relying on server side aggregation const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/server-side-aggregations.json'), + path.join( + import.meta.dirname, + 'resources/chats/server-side-aggregations.json', + ), 'utf-8', ), ); @@ -3533,7 +3547,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('Responds to successful completion of lone code patch', async function () { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/one-code-block-one-success.json'), + path.join( + import.meta.dirname, + 'resources/chats/one-code-block-one-success.json', + ), 'utf-8', ), ); @@ -3558,7 +3575,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('Does not respond to first code patch result when two patches were proposed', async function () { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/two-code-blocks-one-result.json'), + path.join( + import.meta.dirname, + 'resources/chats/two-code-blocks-one-result.json', + ), 'utf-8', ), ); @@ -3578,7 +3598,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/two-code-blocks-two-results.json', ), 'utf-8', @@ -3605,7 +3625,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/code-block-and-command-one-result.json', ), 'utf-8', @@ -3627,7 +3647,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/code-block-and-command-two-results-a.json', ), 'utf-8', @@ -3694,7 +3714,7 @@ Current date and time: 2025-06-11T11:43:00.533Z const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/code-block-and-command-two-results-b.json', ), 'utf-8', @@ -3760,7 +3780,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('Responds to failure of lone code patch', async function () { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/one-code-block-one-failure.json'), + path.join( + import.meta.dirname, + 'resources/chats/one-code-block-one-failure.json', + ), 'utf-8', ), ); @@ -3785,7 +3808,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('context message is placed before last user message when just one user message', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/user-message-last-single.json'), + path.join( + import.meta.dirname, + 'resources/chats/user-message-last-single.json', + ), 'utf-8', ), ); @@ -3803,7 +3829,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('context message is placed before last user message when multiple user messages', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/user-message-last-multiple.json'), + path.join( + import.meta.dirname, + 'resources/chats/user-message-last-multiple.json', + ), 'utf-8', ), ); @@ -3823,7 +3852,7 @@ Current date and time: 2025-06-11T11:43:00.533Z test('context message is placed after the last tool call if the last message is a tool call', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/tool-call-last.json'), + path.join(import.meta.dirname, 'resources/chats/tool-call-last.json'), 'utf-8', ), ); @@ -3847,7 +3876,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('context message is placed after the last user message if the last message is an assistant message', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/assistant-message-last.json'), + path.join( + import.meta.dirname, + 'resources/chats/assistant-message-last.json', + ), 'utf-8', ), ); @@ -3866,7 +3898,10 @@ Current date and time: 2025-06-11T11:43:00.533Z test('context message contains the current date and time', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/user-message-last-multiple.json'), + path.join( + import.meta.dirname, + 'resources/chats/user-message-last-multiple.json', + ), 'utf-8', ), ); @@ -3889,7 +3924,7 @@ Current date and time: 2025-06-11T11:43:00.533Z test('tool call messages include attached files when command result does', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/read-gts-file.json'), + path.join(import.meta.dirname, 'resources/chats/read-gts-file.json'), 'utf-8', ), ); @@ -3928,7 +3963,7 @@ Attached Files (files with newer versions don't show their content): test('tool call messages include attached cards when command result does', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/read-card.json'), + path.join(import.meta.dirname, 'resources/chats/read-card.json'), 'utf-8', ), ); @@ -3973,7 +4008,7 @@ Attached Cards (cards with newer versions don't show their content): test('getPromptParts collects patched files from code patch result attachments', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/patched-gts.json'), + path.join(import.meta.dirname, 'resources/chats/patched-gts.json'), 'utf-8', ), ); @@ -6540,7 +6575,7 @@ module('set model in prompt', (hooks) => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( path.join( - __dirname, + import.meta.dirname, 'resources/chats/required-tool-call-in-last-message.json', ), 'utf-8', @@ -6558,7 +6593,7 @@ module('set model in prompt', (hooks) => { test('use latest active llm', async () => { const eventList: DiscreteMatrixEvent[] = JSON.parse( readFileSync( - path.join(__dirname, 'resources/chats/set-active-llm.json'), + path.join(import.meta.dirname, 'resources/chats/set-active-llm.json'), 'utf-8', ), ); diff --git a/packages/ai-bot/tests/qunit-bootstrap.ts b/packages/ai-bot/tests/qunit-bootstrap.ts new file mode 100644 index 0000000000..6f938d42c4 --- /dev/null +++ b/packages/ai-bot/tests/qunit-bootstrap.ts @@ -0,0 +1,13 @@ +// Configures QUnit to run under native Node (`node tests/index.ts`). The qunit +// CLI used to provide these three things; running the suite directly under node +// means wiring them up ourselves: +// - autostart off, so `index.ts` can register every test before starting +// - the TAP reporter the CLI emitted by default +// - a failure-based process exit code +import QUnit from 'qunit'; + +QUnit.config.autostart = false; +(QUnit as any).reporters.tap.init(QUnit); // QUnit 2.x API missing from @types/qunit +(QUnit as any).on('runEnd', (data: { testCounts: { failed: number } }) => { + process.exitCode = data.testCounts.failed > 0 ? 1 : 0; +}); diff --git a/packages/ai-bot/tests/responding-test.ts b/packages/ai-bot/tests/responding-test.ts index 984b6a3506..0b9ac7e6e4 100644 --- a/packages/ai-bot/tests/responding-test.ts +++ b/packages/ai-bot/tests/responding-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { Responder } from '../lib/responder.ts'; import { DEFAULT_EVENT_SIZE_MAX } from '../lib/matrix/response-publisher.ts'; import FakeTimers from '@sinonjs/fake-timers'; @@ -14,7 +15,7 @@ import { import type OpenAI from 'openai'; import { FakeMatrixClient } from './helpers/fake-matrix-client.ts'; import { OpenAIError } from 'openai'; -import * as Sentry from '@sentry/node'; +import { errorReporter } from '../lib/sentry.ts'; function snapshotWithContent(content: string): ChatCompletionSnapshot { return { @@ -1135,8 +1136,8 @@ module('Responding', (hooks) => { test('onError does not report to Sentry when streaming is already finished', async () => { let sentryCalls: any[] = []; - let originalCaptureException = Sentry.captureException; - (Sentry as any).captureException = (...args: any[]) => { + let originalCaptureException = errorReporter.captureException; + errorReporter.captureException = (...args: any[]) => { sentryCalls.push(args); return ''; }; @@ -1168,14 +1169,14 @@ module('Responding', (hooks) => { 'No error events should be sent to Matrix', ); } finally { - (Sentry as any).captureException = originalCaptureException; + errorReporter.captureException = originalCaptureException; } }); test('onError reports to Sentry when streaming is not finished', async () => { let sentryCalls: any[] = []; - let originalCaptureException = Sentry.captureException; - (Sentry as any).captureException = (...args: any[]) => { + let originalCaptureException = errorReporter.captureException; + errorReporter.captureException = (...args: any[]) => { sentryCalls.push(args); return ''; }; @@ -1192,7 +1193,7 @@ module('Responding', (hooks) => { 'Sentry should be called when streaming is not finished', ); } finally { - (Sentry as any).captureException = originalCaptureException; + errorReporter.captureException = originalCaptureException; } }); diff --git a/packages/ai-bot/tests/response-parsing-test.ts b/packages/ai-bot/tests/response-parsing-test.ts index 82b74ab94f..8c9fc872ea 100644 --- a/packages/ai-bot/tests/response-parsing-test.ts +++ b/packages/ai-bot/tests/response-parsing-test.ts @@ -1,4 +1,5 @@ -import { module, test, assert } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, assert } = QUnit; import { cleanContent } from '@cardstack/runtime-common/ai'; module('cleanContent', () => { diff --git a/packages/ai-bot/tsconfig.json b/packages/ai-bot/tsconfig.json index 9d1b5b4ebf..27d530eb29 100644 --- a/packages/ai-bot/tsconfig.json +++ b/packages/ai-bot/tsconfig.json @@ -26,7 +26,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"] }, diff --git a/packages/base/card-api.gts b/packages/base/card-api.gts index e9f6eae177..48f07c8ca7 100644 --- a/packages/base/card-api.gts +++ b/packages/base/card-api.gts @@ -1,6 +1,6 @@ import Modifier from 'ember-modifier'; import GlimmerComponent from '@glimmer/component'; -import { isEqual } from 'lodash'; +import { isEqual } from 'lodash-es'; import { WatchedArray, rawArrayValues } from './watched-array'; import { BoxelInput, CopyButton } from '@cardstack/boxel-ui/components'; import { diff --git a/packages/base/card-serialization.ts b/packages/base/card-serialization.ts index 529f7bb6cd..9af323e45d 100644 --- a/packages/base/card-serialization.ts +++ b/packages/base/card-serialization.ts @@ -21,7 +21,7 @@ import type { ResourceID, VirtualNetwork } from '@cardstack/runtime-common'; // --- Runtime Imports --- -import { isEqual, merge } from 'lodash'; +import { isEqual, merge } from 'lodash-es'; import { assertIsSerializerName, CardResourceType, diff --git a/packages/base/color-field/components/color-picker-field.gts b/packages/base/color-field/components/color-picker-field.gts index 6f4a980d2d..2ed67371cc 100644 --- a/packages/base/color-field/components/color-picker-field.gts +++ b/packages/base/color-field/components/color-picker-field.gts @@ -2,7 +2,7 @@ import Component from '@glimmer/component'; import type Owner from '@ember/owner'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; -import { debounce } from 'lodash'; +import { debounce } from 'lodash-es'; import { eq, or, not } from '@cardstack/boxel-ui/helpers'; import { ColorPicker } from '@cardstack/boxel-ui/components'; diff --git a/packages/base/default-templates/card-info.gts b/packages/base/default-templates/card-info.gts index f44597f65d..2fedb47cdb 100644 --- a/packages/base/default-templates/card-info.gts +++ b/packages/base/default-templates/card-info.gts @@ -22,7 +22,7 @@ import { import { and, cn, eq } from '@cardstack/boxel-ui/helpers'; import { ChevronRight } from '@cardstack/boxel-ui/icons'; -import { startCase } from 'lodash'; +import { startCase } from 'lodash-es'; import { chooseFile, diff --git a/packages/base/default-templates/field-edit.gts b/packages/base/default-templates/field-edit.gts index 2887881321..0bdbaec7ce 100644 --- a/packages/base/default-templates/field-edit.gts +++ b/packages/base/default-templates/field-edit.gts @@ -2,7 +2,7 @@ import GlimmerComponent from '@glimmer/component'; import type { FieldDef } from '../card-api'; import { FieldContainer } from '@cardstack/boxel-ui/components'; import { eq } from '@cardstack/boxel-ui/helpers'; -import { startCase } from 'lodash'; +import { startCase } from 'lodash-es'; import { getField } from '@cardstack/runtime-common'; export default class FieldDefEditTemplate extends GlimmerComponent<{ diff --git a/packages/base/default-templates/isolated-and-edit.gts b/packages/base/default-templates/isolated-and-edit.gts index ab4d3761fb..e2c1bba486 100644 --- a/packages/base/default-templates/isolated-and-edit.gts +++ b/packages/base/default-templates/isolated-and-edit.gts @@ -2,7 +2,7 @@ import GlimmerComponent from '@glimmer/component'; import type { CardDef, FieldsTypeFor, Format } from '../card-api'; import { FieldContainer, Header } from '@cardstack/boxel-ui/components'; import { cn, eq } from '@cardstack/boxel-ui/helpers'; -import { startCase } from 'lodash'; +import { startCase } from 'lodash-es'; import { getFieldIcon, getField, diff --git a/packages/base/enum.gts b/packages/base/enum.gts index 0dcbbfe8ee..4230d47cd0 100644 --- a/packages/base/enum.gts +++ b/packages/base/enum.gts @@ -1,5 +1,5 @@ import GlimmerComponent from '@glimmer/component'; -import { isEqual } from 'lodash'; +import { isEqual } from 'lodash-es'; import { BoxelSelect } from '@cardstack/boxel-ui/components'; import { getField } from '@cardstack/runtime-common'; import { resolveFieldConfiguration } from './field-support'; diff --git a/packages/base/field-component.gts b/packages/base/field-component.gts index 8282b6da07..32b5cdd1cd 100644 --- a/packages/base/field-component.gts +++ b/packages/base/field-component.gts @@ -34,7 +34,7 @@ import { sanitizeHtmlSafe, } from '@cardstack/boxel-ui/helpers'; import Modifier from 'ember-modifier'; -import { isEqual, flatMap } from 'lodash'; +import { isEqual, flatMap } from 'lodash-es'; import { initSharedState } from './shared-state'; import { and, cn, eq, not } from '@cardstack/boxel-ui/helpers'; import { consume, provide } from 'ember-provide-consume-context'; diff --git a/packages/base/field-support.ts b/packages/base/field-support.ts index 8c8ef2d165..e719fac11b 100644 --- a/packages/base/field-support.ts +++ b/packages/base/field-support.ts @@ -23,7 +23,7 @@ import { } from './card-serialization'; import { initSharedState } from './shared-state'; import { rawArrayValues } from './watched-array'; -import { flatMap } from 'lodash'; +import { flatMap } from 'lodash-es'; import { TrackedMap, TrackedWeakMap } from 'tracked-built-ins'; import type { ConfigurationInput, FieldConfiguration } from './card-api'; diff --git a/packages/base/package.json b/packages/base/package.json index 875e69e23a..0bf39c593a 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -12,7 +12,7 @@ "@glimmer/tracking": "^1.0.4", "@glint/template": "catalog:", "@types/flat": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "awesome-phonenumber": "catalog:", "concurrently": "catalog:", "ember-cli-htmlbars": "^6.3.0", @@ -29,7 +29,8 @@ }, "peerDependencies": { "ember-provide-consume-context": "^0.7.0", - "ember-source": "catalog:" + "ember-source": "catalog:", + "lodash-es": "catalog:" }, "scripts": { "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", diff --git a/packages/base/realm-config.gts b/packages/base/realm-config.gts index 7fd32d3575..7fa1b99313 100644 --- a/packages/base/realm-config.gts +++ b/packages/base/realm-config.gts @@ -29,7 +29,7 @@ import FileSettingsIcon from '@cardstack/boxel-icons/file-settings'; import LinkIcon from '@cardstack/boxel-icons/link'; import { action } from '@ember/object'; import type Owner from '@ember/owner'; -import { startCase } from 'lodash'; +import { startCase } from 'lodash-es'; import type { FieldsTypeFor } from './card-api'; class RoutingRuleAtom extends Component { diff --git a/packages/billing/.eslintrc.js b/packages/billing/.eslintrc.cjs similarity index 100% rename from packages/billing/.eslintrc.js rename to packages/billing/.eslintrc.cjs diff --git a/packages/billing/package.json b/packages/billing/package.json index 972b16d6a8..5151136150 100644 --- a/packages/billing/package.json +++ b/packages/billing/package.json @@ -2,6 +2,13 @@ "name": "@cardstack/billing", "version": "0.0.0", "license": "MIT", + "type": "module", + "exports": { + "./package.json": "./package.json", + "./stripe-webhook-handlers": "./stripe-webhook-handlers/index.ts", + "./stripe-webhook-handlers/*": "./stripe-webhook-handlers/*.ts", + "./*": "./*.ts" + }, "dependencies": { "@cardstack/postgres": "workspace:*", "@cardstack/runtime-common": "workspace:*", diff --git a/packages/billing/stripe-webhook-handlers/checkout-session-completed.ts b/packages/billing/stripe-webhook-handlers/checkout-session-completed.ts index 58e7b535ea..bb04a34986 100644 --- a/packages/billing/stripe-webhook-handlers/checkout-session-completed.ts +++ b/packages/billing/stripe-webhook-handlers/checkout-session-completed.ts @@ -8,7 +8,7 @@ import { markStripeEventAsProcessed, updateUserStripeCustomerEmail, } from '../billing-queries.ts'; -import type { StripeCheckoutSessionCompletedWebhookEvent } from '.'; +import type { StripeCheckoutSessionCompletedWebhookEvent } from './index.ts'; import type { PgAdapter } from '@cardstack/postgres'; import { TransactionManager } from '@cardstack/postgres'; diff --git a/packages/billing/stripe-webhook-handlers/payment-succeeded.ts b/packages/billing/stripe-webhook-handlers/payment-succeeded.ts index 726d596f57..ed4ec579c0 100644 --- a/packages/billing/stripe-webhook-handlers/payment-succeeded.ts +++ b/packages/billing/stripe-webhook-handlers/payment-succeeded.ts @@ -21,7 +21,7 @@ import { sumUpCreditsLedger, updateSubscription as updateSubscriptionQuery, } from '../billing-queries.ts'; -import type { StripeInvoicePaymentSucceededWebhookEvent } from '.'; +import type { StripeInvoicePaymentSucceededWebhookEvent } from './index.ts'; import type { PgAdapter } from '@cardstack/postgres'; import { TransactionManager } from '@cardstack/postgres'; import { ProrationCalculator } from '../proration-calculator.ts'; diff --git a/packages/billing/stripe-webhook-handlers/subscription-deleted.ts b/packages/billing/stripe-webhook-handlers/subscription-deleted.ts index 11f540a89d..f8fd209626 100644 --- a/packages/billing/stripe-webhook-handlers/subscription-deleted.ts +++ b/packages/billing/stripe-webhook-handlers/subscription-deleted.ts @@ -1,6 +1,6 @@ import type { DBAdapter } from '@cardstack/runtime-common'; import { reportError } from '@cardstack/runtime-common'; -import type { StripeSubscriptionDeletedWebhookEvent } from '.'; +import type { StripeSubscriptionDeletedWebhookEvent } from './index.ts'; import { insertStripeEvent, updateSubscription, diff --git a/packages/billing/tsconfig.json b/packages/billing/tsconfig.json index 5516357016..ab079dac84 100644 --- a/packages/billing/tsconfig.json +++ b/packages/billing/tsconfig.json @@ -26,7 +26,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"], "*": ["types/*"] diff --git a/packages/bot-runner/package.json b/packages/bot-runner/package.json index 4de1394192..b087a05758 100644 --- a/packages/bot-runner/package.json +++ b/packages/bot-runner/package.json @@ -1,11 +1,11 @@ { "name": "@cardstack/bot-runner", + "type": "module", "dependencies": { "@cardstack/postgres": "workspace:*", "@cardstack/runtime-common": "workspace:*", "@sentry/node": "catalog:", "matrix-js-sdk": "catalog:", - "ts-node": "^10.9.2", "typescript": "catalog:" }, "devDependencies": { @@ -16,8 +16,8 @@ "qunit": "catalog:" }, "scripts": { - "start": "NODE_NO_WARNINGS=1 ts-node --transpileOnly main", - "test": "NODE_NO_WARNINGS=1 qunit --require ts-node/register/transpile-only tests/index.ts", + "start": "NODE_NO_WARNINGS=1 node main.ts", + "test": "NODE_NO_WARNINGS=1 node tests/index.ts", "lint": "ember-tsc --noEmit" } } diff --git a/packages/bot-runner/tests/bot-runner-test.ts b/packages/bot-runner/tests/bot-runner-test.ts index 1f25c911a2..164c327a5c 100644 --- a/packages/bot-runner/tests/bot-runner-test.ts +++ b/packages/bot-runner/tests/bot-runner-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { DBAdapter, ExecuteOptions, diff --git a/packages/bot-runner/tests/command-runner-test.ts b/packages/bot-runner/tests/command-runner-test.ts index 249fbe820e..cd7f55a310 100644 --- a/packages/bot-runner/tests/command-runner-test.ts +++ b/packages/bot-runner/tests/command-runner-test.ts @@ -1,4 +1,5 @@ -import { module, test, skip } from 'qunit'; +import QUnit from 'qunit'; +const { module, test, skip } = QUnit; import type { DBAdapter, ExecuteOptions, diff --git a/packages/bot-runner/tests/create-listing-pr-handler-test.ts b/packages/bot-runner/tests/create-listing-pr-handler-test.ts index d75543ef33..a4d9a0ccba 100644 --- a/packages/bot-runner/tests/create-listing-pr-handler-test.ts +++ b/packages/bot-runner/tests/create-listing-pr-handler-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { GitHubClient } from '../lib/github.ts'; import { CreateListingPRHandler, diff --git a/packages/bot-runner/tests/index.ts b/packages/bot-runner/tests/index.ts index cc9767aba1..187ec6d4ed 100644 --- a/packages/bot-runner/tests/index.ts +++ b/packages/bot-runner/tests/index.ts @@ -1,5 +1,9 @@ +import './qunit-bootstrap.ts'; // configures QUnit before any test registers +import QUnit from 'qunit'; import '../setup-logger.ts'; import './bot-runner-test.ts'; import './command-runner-test.ts'; import './create-listing-pr-handler-test.ts'; import './lint-runner-test.ts'; + +QUnit.start(); diff --git a/packages/bot-runner/tests/lint-runner-test.ts b/packages/bot-runner/tests/lint-runner-test.ts index 70ca2553cb..753108460e 100644 --- a/packages/bot-runner/tests/lint-runner-test.ts +++ b/packages/bot-runner/tests/lint-runner-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import * as os from 'node:os'; import * as path from 'node:path'; import { @@ -86,10 +87,7 @@ module('lint-runner | parseTscOutput', () => { test('also works when prefix is passed with a trailing slash', (assert) => { let output = '../catalog/__submissions-temp__/x/foo.gts(1,1): error TS1: msg'; - let errors = parseTscOutput( - output, - '../catalog/__submissions-temp__/x/', - ); + let errors = parseTscOutput(output, '../catalog/__submissions-temp__/x/'); assert.strictEqual(errors.length, 1); assert.ok(errors[0].startsWith('foo.gts '), errors[0]); }); @@ -171,10 +169,7 @@ module('lint-runner | parseTemplateLintOutput', () => { test('handles empty messages array', (assert) => { let json = JSON.stringify({ '__submissions-temp__/abc/foo.hbs': [] }); - assert.deepEqual( - parseTemplateLintOutput(json, tempDir, catalogDir), - [], - ); + assert.deepEqual(parseTemplateLintOutput(json, tempDir, catalogDir), []); }); test('handles multiple files', (assert) => { diff --git a/packages/bot-runner/tests/qunit-bootstrap.ts b/packages/bot-runner/tests/qunit-bootstrap.ts new file mode 100644 index 0000000000..6f938d42c4 --- /dev/null +++ b/packages/bot-runner/tests/qunit-bootstrap.ts @@ -0,0 +1,13 @@ +// Configures QUnit to run under native Node (`node tests/index.ts`). The qunit +// CLI used to provide these three things; running the suite directly under node +// means wiring them up ourselves: +// - autostart off, so `index.ts` can register every test before starting +// - the TAP reporter the CLI emitted by default +// - a failure-based process exit code +import QUnit from 'qunit'; + +QUnit.config.autostart = false; +(QUnit as any).reporters.tap.init(QUnit); // QUnit 2.x API missing from @types/qunit +(QUnit as any).on('runEnd', (data: { testCounts: { failed: number } }) => { + process.exitCode = data.testCounts.failed > 0 ? 1 : 0; +}); diff --git a/packages/bot-runner/tsconfig.json b/packages/bot-runner/tsconfig.json index eda8b5f44e..f33fc1dae9 100644 --- a/packages/bot-runner/tsconfig.json +++ b/packages/bot-runner/tsconfig.json @@ -26,7 +26,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"], "@cardstack/runtime-common": ["../runtime-common"], diff --git a/packages/boxel-cli/README.md b/packages/boxel-cli/README.md index b47ad3f81f..7438dcb8ab 100644 --- a/packages/boxel-cli/README.md +++ b/packages/boxel-cli/README.md @@ -101,7 +101,7 @@ boxel --help boxel sync . ``` -The linked command automatically uses `dist/index.js` if built, or falls back to running TypeScript source via `ts-node`. +The linked command runs `dist/index.js`, so build the CLI first (`pnpm --filter @cardstack/boxel-cli build`). The TypeScript source isn't run directly. To unlink: diff --git a/packages/boxel-cli/bin/boxel.js b/packages/boxel-cli/bin/boxel.js index 12ecddf070..30b46a2698 100755 --- a/packages/boxel-cli/bin/boxel.js +++ b/packages/boxel-cli/bin/boxel.js @@ -67,13 +67,18 @@ function mkcertRootCAPath() { maybeReExecWithMkcertCA(); -// Use the built dist version if available, otherwise fall back to ts-node +// Use the built dist version if available, otherwise run the TS source directly. const distEntry = path.resolve(__dirname, '..', 'dist', 'index.js'); if (fs.existsSync(distEntry)) { require(distEntry); } else { - // Development fallback: run from TypeScript source via ts-node - require('ts-node').register({ transpileOnly: true }); - require('../src/index.ts'); + // The CLI ships as a built CJS bundle (the TypeScript source uses + // CommonJS-only globals like __dirname and isn't runnable as native ESM). + // Build it first. + console.error( + 'boxel-cli: dist/index.js not found. Build the CLI first:\n' + + ' pnpm --filter @cardstack/boxel-cli build', + ); + process.exit(1); } diff --git a/packages/boxel-cli/package.json b/packages/boxel-cli/package.json index 74d5523f9f..170c8d77de 100644 --- a/packages/boxel-cli/package.json +++ b/packages/boxel-cli/package.json @@ -4,6 +4,14 @@ "license": "MIT", "description": "CLI tools for Boxel workspace management", "main": "./dist/index.js", + "exports": { + ".": "./dist/index.js", + "./api": { + "types": "./api.ts", + "default": "./dist/api.js" + }, + "./package.json": "./package.json" + }, "bin": { "boxel": "./bin/boxel.js" }, @@ -64,19 +72,19 @@ "eslint-plugin-prettier": "catalog:", "prettier": "catalog:", "semver": "^7.7.0", - "ts-node": "^10.9.1", "vite": "catalog:", "vitest": "catalog:" }, "scripts": { - "build": "pnpm clean && pnpm build:types && pnpm build:test-harness && pnpm build:realms && NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build.ts", - "build:plugin": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build-plugin.ts", - "build:realms": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build-realms.ts", - "build:skills": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build-skills.ts", - "build:test-harness": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build-test-harness.ts", - "build:types": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/build-types.ts", + "build": "pnpm clean && pnpm build:types && pnpm build:test-harness && pnpm build:realms && NODE_NO_WARNINGS=1 node scripts/build.ts", + "build:api": "NODE_NO_WARNINGS=1 node scripts/build.ts --api-only", + "build:plugin": "NODE_NO_WARNINGS=1 node scripts/build-plugin.ts", + "build:realms": "NODE_NO_WARNINGS=1 node scripts/build-realms.ts", + "build:skills": "NODE_NO_WARNINGS=1 node scripts/build-skills.ts", + "build:test-harness": "NODE_NO_WARNINGS=1 node scripts/build-test-harness.ts", + "build:types": "NODE_NO_WARNINGS=1 node scripts/build-types.ts", "clean": "rm -rf dist/* bundled-types bundled-test-harness bundled-realms", - "start": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/index.ts", + "start": "NODE_NO_WARNINGS=1 node dist/index.js", "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", "lint:js": "eslint . --report-unused-disable-directives --cache", diff --git a/packages/boxel-cli/scripts/build-plugin.ts b/packages/boxel-cli/scripts/build-plugin.ts index e9f745f3cd..80637f113f 100644 --- a/packages/boxel-cli/scripts/build-plugin.ts +++ b/packages/boxel-cli/scripts/build-plugin.ts @@ -12,7 +12,7 @@ import { readFileSync, writeFileSync } from 'fs'; import { resolve } from 'path'; import { buildBoxelProgram } from '../src/build-program.ts'; -const PLUGIN_DIR = resolve(__dirname, '..', 'plugin'); +const PLUGIN_DIR = resolve(import.meta.dirname, '..', 'plugin'); interface SkillSpec { /** Skill folder name under plugin/skills/ */ diff --git a/packages/boxel-cli/scripts/build-realms.ts b/packages/boxel-cli/scripts/build-realms.ts index d4c1d158f6..0fcdbe6a8a 100644 --- a/packages/boxel-cli/scripts/build-realms.ts +++ b/packages/boxel-cli/scripts/build-realms.ts @@ -22,7 +22,7 @@ import { cpSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs'; import { basename, join, resolve } from 'node:path'; -const PACKAGE_ROOT = resolve(__dirname, '..'); +const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); const MONOREPO_PACKAGES = resolve(PACKAGE_ROOT, '..'); const OUT_DIR = join(PACKAGE_ROOT, 'bundled-realms'); diff --git a/packages/boxel-cli/scripts/build-skills.ts b/packages/boxel-cli/scripts/build-skills.ts index fd5aaafb88..a8ac7d5fc1 100644 --- a/packages/boxel-cli/scripts/build-skills.ts +++ b/packages/boxel-cli/scripts/build-skills.ts @@ -38,10 +38,13 @@ import { format, resolveConfig } from 'prettier'; const BOXEL_SKILLS_VERSION = 'v0.0.22'; const BOXEL_SKILLS_REPO_URL = 'https://github.com/cardstack/boxel-skills.git'; -const PLUGIN_DIR = resolve(__dirname, '..', 'plugin'); +const PLUGIN_DIR = resolve(import.meta.dirname, '..', 'plugin'); const PLUGIN_README_PATH = resolve(PLUGIN_DIR, 'README.md'); -const CACHE_DIR = resolve(__dirname, '..', '.boxel-skills-cache'); -const MANIFEST_PATH = resolve(__dirname, '.boxel-skills-manifest.json'); +const CACHE_DIR = resolve(import.meta.dirname, '..', '.boxel-skills-cache'); +const MANIFEST_PATH = resolve( + import.meta.dirname, + '.boxel-skills-manifest.json', +); const README_BEGIN_MARKER = ''; @@ -459,7 +462,7 @@ function writeManifest(plan: EmissionPlan): void { }; writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + '\n'); console.log( - `wrote ${relative(resolve(__dirname, '..'), MANIFEST_PATH)} (${manifest.skills.length} skills)`, + `wrote ${relative(resolve(import.meta.dirname, '..'), MANIFEST_PATH)} (${manifest.skills.length} skills)`, ); } @@ -554,7 +557,7 @@ async function main(): Promise { } // Run main only when invoked directly, not when imported from tests. -if (require.main === module) { +if (import.meta.main) { main().catch((err) => { console.error(err); process.exit(1); diff --git a/packages/boxel-cli/scripts/build-test-harness.ts b/packages/boxel-cli/scripts/build-test-harness.ts index 3ffdb66bc8..e4b85af71e 100644 --- a/packages/boxel-cli/scripts/build-test-harness.ts +++ b/packages/boxel-cli/scripts/build-test-harness.ts @@ -21,7 +21,7 @@ import { cpSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs'; import { basename, join, resolve } from 'node:path'; -const PACKAGE_ROOT = resolve(__dirname, '..'); +const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); const MONOREPO_PACKAGES = resolve(PACKAGE_ROOT, '..'); const HOST_DIST = join(MONOREPO_PACKAGES, 'host', 'dist'); diff --git a/packages/boxel-cli/scripts/build-types.ts b/packages/boxel-cli/scripts/build-types.ts index 485ce86d1c..d08a36c243 100644 --- a/packages/boxel-cli/scripts/build-types.ts +++ b/packages/boxel-cli/scripts/build-types.ts @@ -66,7 +66,7 @@ import { dirname, isAbsolute, join, relative, resolve } from 'node:path'; import { Preprocessor } from 'content-tag'; import * as ts from 'typescript'; -const PACKAGE_ROOT = resolve(__dirname, '..'); +const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); const MONOREPO_PACKAGES = resolve(PACKAGE_ROOT, '..'); // --------------------------------------------------------------------------- diff --git a/packages/boxel-cli/scripts/build.ts b/packages/boxel-cli/scripts/build.ts index 9f951dff2c..caa15b5730 100644 --- a/packages/boxel-cli/scripts/build.ts +++ b/packages/boxel-cli/scripts/build.ts @@ -57,24 +57,6 @@ async function buildCLI() { console.log('Making CLI file executable...'); chmodSync('dist/index.js', 0o755); - // content-tag (pulled in via runtime-common's transpile pipeline) - // loads its wasm with `readFileSync(`${__dirname}/content_tag_bg.wasm`)` - // from `pkg/node/`. After esbuild bundles content-tag into - // `dist/index.js`, `__dirname` becomes the boxel-cli dist/ dir, - // so the wasm has to live next to index.js — otherwise `boxel test` - // hits ENOENT on the first transpile. - let wasmSrc = join( - __dirname, - '..', - 'node_modules', - 'content-tag', - 'pkg', - 'node', - 'content_tag_bg.wasm', - ); - copyFileSync(wasmSrc, 'dist/content_tag_bg.wasm'); - console.log('Copied content_tag_bg.wasm into dist/'); - console.log('Build complete!'); // Log bundle size @@ -89,4 +71,57 @@ async function buildCLI() { } } -buildCLI(); +// The public `@cardstack/boxel-cli/api` surface, bundled to a single CJS file +// so cross-package consumers (software-factory) load a normal built module +// rather than raw `.ts` source — the latter resolves under some loaders +// (vitest, plain node) but not others (Playwright's worker loader), which +// failed with "does not provide an export named 'BoxelCLIClient'". esbuild's +// CJS output keeps named exports detectable by Node's cjs-module-lexer, so ESM +// consumers can still `import { BoxelCLIClient }`. +async function buildAPI() { + mkdirSync('dist', { recursive: true }); + console.log('Building api...'); + try { + await build({ + ...commonConfig, + entryPoints: ['api.ts'], + outfile: 'dist/api.js', + }); + console.log('Built dist/api.js'); + } catch (error) { + console.error('API build failed:', error); + process.exit(1); + } +} + +// content-tag (pulled in via runtime-common's transpile pipeline) loads its +// wasm with `readFileSync(`${import.meta.dirname}/content_tag_bg.wasm`)` from +// `pkg/node/`. After esbuild bundles content-tag into `dist/index.js` or +// `dist/api.js`, `import.meta.dirname` becomes the boxel-cli `dist/` dir, so +// the wasm has to live next to whichever entry was built — otherwise the first +// transpile hits ENOENT. Both `build` and `build:api` consumers need it. +function copyContentTagWasm() { + let wasmSrc = join( + import.meta.dirname, + '..', + 'node_modules', + 'content-tag', + 'pkg', + 'node', + 'content_tag_bg.wasm', + ); + copyFileSync(wasmSrc, 'dist/content_tag_bg.wasm'); + console.log('Copied content_tag_bg.wasm into dist/'); +} + +async function main() { + // `build:api` passes --api-only to rebuild just the API surface quickly + // (consumers' test pipelines do this); a full `build` produces both. + if (!process.argv.includes('--api-only')) { + await buildCLI(); + } + await buildAPI(); + copyContentTagWasm(); +} + +main(); diff --git a/packages/boxel-cli/scripts/compute-release.ts b/packages/boxel-cli/scripts/compute-release.ts index 10f4338bd0..7196354338 100644 --- a/packages/boxel-cli/scripts/compute-release.ts +++ b/packages/boxel-cli/scripts/compute-release.ts @@ -319,7 +319,7 @@ function main(): void { } // CJS guard: only run main() when invoked as a script, not when imported -// by the vitest test file. `require.main === module` is the standard idiom. -if (require.main === module) { +// by the vitest test file. `import.meta.main` is the standard idiom. +if (import.meta.main) { main(); } diff --git a/packages/boxel-cli/scripts/generate-release-notes.ts b/packages/boxel-cli/scripts/generate-release-notes.ts index 366f9d05af..dac7132c12 100644 --- a/packages/boxel-cli/scripts/generate-release-notes.ts +++ b/packages/boxel-cli/scripts/generate-release-notes.ts @@ -261,6 +261,6 @@ function main(): void { ); } -if (require.main === module) { +if (import.meta.main) { main(); } diff --git a/packages/boxel-cli/src/lib/find-package-root.ts b/packages/boxel-cli/src/lib/find-package-root.ts index e8b1415a65..9c2d530eac 100644 --- a/packages/boxel-cli/src/lib/find-package-root.ts +++ b/packages/boxel-cli/src/lib/find-package-root.ts @@ -4,9 +4,9 @@ import { dirname, join } from 'node:path'; /** * Walk up from `__dirname` until we find the `@cardstack/boxel-cli` * package.json. The single-file esbuild bundle places `__dirname` at - * `boxel-cli/dist`; the ts-node fallback places it inside `src/...`. - * Anchoring to the package.json keeps every downstream path stable - * regardless of which entry mode is active. + * `boxel-cli/dist`; running from source (e.g. under vitest) places it + * inside `src/...`. Anchoring to the package.json keeps every downstream + * path stable regardless of which entry mode is active. */ export function findBoxelCliRoot(startDir: string): string { let dir = startDir; diff --git a/packages/boxel-cli/tests/fixtures/fake-watcher.cjs b/packages/boxel-cli/tests/fixtures/fake-watcher.cjs index ae34d323c6..497c028a21 100644 --- a/packages/boxel-cli/tests/fixtures/fake-watcher.cjs +++ b/packages/boxel-cli/tests/fixtures/fake-watcher.cjs @@ -4,16 +4,19 @@ // exiting cleanly — mimicking what a real `realm watch start` does on // shutdown. -require('ts-node').register({ transpileOnly: true }); +// Node (>=22.18 / 24) strips TypeScript types natively, so the registry .ts +// loads via a plain require with no extra loader. const path = require('path'); -const registry = require(path.resolve( - __dirname, - '..', - '..', - 'src', - 'lib', - 'watch-process-registry.ts', -)); +const registry = require( + path.resolve( + __dirname, + '..', + '..', + 'src', + 'lib', + 'watch-process-registry.ts', + ), +); async function main() { const workspace = process.env.WATCHER_WORKSPACE || '/tmp/fake-watcher'; diff --git a/packages/boxel-cli/tsconfig.json b/packages/boxel-cli/tsconfig.json index 05d3f8476d..c7b3e458f7 100644 --- a/packages/boxel-cli/tsconfig.json +++ b/packages/boxel-cli/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "es2022", - "module": "nodenext", - "moduleResolution": "nodenext", + "module": "esnext", + "moduleResolution": "bundler", "allowImportingTsExtensions": true, "strict": true, "noEmit": true, diff --git a/packages/boxel-ui/addon/package.json b/packages/boxel-ui/addon/package.json index edacc1757d..87be581ca9 100644 --- a/packages/boxel-ui/addon/package.json +++ b/packages/boxel-ui/addon/package.json @@ -63,7 +63,7 @@ "ember-velcro": "^2.1.3", "file-loader": "catalog:", "focus-trap": "catalog:", - "lodash": "catalog:", + "lodash-es": "catalog:", "pluralize": "catalog:", "tracked-built-ins": "catalog:", "typescript": "catalog:" @@ -82,7 +82,7 @@ "@rollup/plugin-babel": "catalog:", "@tsconfig/ember": "3.0.1", "@types/dompurify": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/pluralize": "catalog:", "@typescript-eslint/eslint-plugin": "catalog:", "@typescript-eslint/parser": "catalog:", diff --git a/packages/boxel-ui/addon/src/components/drag-and-drop/usage.gts b/packages/boxel-ui/addon/src/components/drag-and-drop/usage.gts index a7f9297fee..228cefec8e 100644 --- a/packages/boxel-ui/addon/src/components/drag-and-drop/usage.gts +++ b/packages/boxel-ui/addon/src/components/drag-and-drop/usage.gts @@ -7,7 +7,7 @@ import { type CSSVariableInfo, cssVariable, } from 'ember-freestyle/decorators/css-variable'; -import { get } from 'lodash'; +import { get } from 'lodash-es'; import Pill from '../pill/index.gts'; import DndKanbanBoard, { DndColumn } from './index.gts'; diff --git a/packages/boxel-ui/addon/src/components/multi-select/usage.gts b/packages/boxel-ui/addon/src/components/multi-select/usage.gts index e00479eec1..9125d964d1 100644 --- a/packages/boxel-ui/addon/src/components/multi-select/usage.gts +++ b/packages/boxel-ui/addon/src/components/multi-select/usage.gts @@ -8,7 +8,7 @@ import { cssVariable, } from 'ember-freestyle/decorators/css-variable'; import type { Select } from 'ember-power-select/components/power-select'; -import { includes } from 'lodash'; +import { includes } from 'lodash-es'; import pluralize from 'pluralize'; import cn from '../../helpers/cn.ts'; diff --git a/packages/boxel-ui/addon/src/helpers/pick.ts b/packages/boxel-ui/addon/src/helpers/pick.ts index 06bc4ba575..67d81fe814 100644 --- a/packages/boxel-ui/addon/src/helpers/pick.ts +++ b/packages/boxel-ui/addon/src/helpers/pick.ts @@ -1,4 +1,4 @@ -import { get } from 'lodash'; +import { get } from 'lodash-es'; export default function pick( path: string, diff --git a/packages/catalog/package.json b/packages/catalog/package.json index b41c6d0553..ad6413952d 100644 --- a/packages/catalog/package.json +++ b/packages/catalog/package.json @@ -13,7 +13,7 @@ "@glint/ember-tsc": "catalog:", "@typescript-eslint/eslint-plugin": "catalog:", "@typescript-eslint/parser": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/qunit": "catalog:", "@universal-ember/test-support": "catalog:", "concurrently": "catalog:", @@ -28,7 +28,7 @@ "eslint-plugin-import": "catalog:", "eslint-plugin-n": "catalog:", "eslint-plugin-prettier": "catalog:", - "lodash": "catalog:", + "lodash-es": "catalog:", "prettier": "catalog:", "prettier-plugin-ember-template-tag": "catalog:", "qunit": "catalog:", diff --git a/packages/catalog/tsconfig.json b/packages/catalog/tsconfig.json index 10ea6c1ddc..f24264165f 100644 --- a/packages/catalog/tsconfig.json +++ b/packages/catalog/tsconfig.json @@ -26,39 +26,21 @@ "skipLibCheck": true, "paths": { "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", "../base/*" ], - "@cardstack/catalog/*": [ - "contents/*" - ], - "@cardstack/host/*": [ - "../host/app/*" - ], - "@cardstack/host/tests/*": [ - "../host/tests/*" - ], - "@cardstack/openrouter/*": [ - "../openrouter-realm/*" - ], - "@cardstack/boxel-host/commands/*": [ - "../host/app/commands/*" - ], - "@cardstack/boxel-ui": [ - "../boxel-ui/addon" - ], - "@cardstack/boxel-ui/*": [ - "../boxel-ui/addon/*" - ], - "*": [ - "../host/types/*" - ] + "@cardstack/catalog/*": ["contents/*"], + "@cardstack/host/*": ["../host/app/*"], + "@cardstack/host/tests/*": ["../host/tests/*"], + "@cardstack/openrouter/*": ["../openrouter-realm/*"], + "@cardstack/boxel-host/commands/*": ["../host/app/commands/*"], + "@cardstack/boxel-ui": ["../boxel-ui/addon"], + "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"], + "*": ["../host/types/*"] }, "types": ["@cardstack/local-types", "qunit", "qunit-dom"] }, - "include": [ - "../host/types/**/*", - "**/*.d.ts", - "**/*.ts", - "**/*.gts" - ] + "include": ["../host/types/**/*", "**/*.d.ts", "**/*.ts", "**/*.gts"] } diff --git a/packages/experiments-realm/components/search-input.gts b/packages/experiments-realm/components/search-input.gts index b9396ac669..a63c74a681 100644 --- a/packages/experiments-realm/components/search-input.gts +++ b/packages/experiments-realm/components/search-input.gts @@ -1,5 +1,5 @@ import Component from '@glimmer/component'; -import { debounce } from 'lodash'; +import { debounce } from 'lodash-es'; import { action } from '@ember/object'; import { on } from '@ember/modifier'; diff --git a/packages/experiments-realm/experiments_fields_preview.gts b/packages/experiments-realm/experiments_fields_preview.gts index 4a74842275..ad31704728 100644 --- a/packages/experiments-realm/experiments_fields_preview.gts +++ b/packages/experiments-realm/experiments_fields_preview.gts @@ -22,7 +22,7 @@ import { Component } from 'https://cardstack.com/base/card-api'; import { FieldContainer } from '@cardstack/boxel-ui/components'; import { eq } from '@cardstack/boxel-ui/helpers'; import { getField } from '@cardstack/runtime-common'; -import { startCase } from 'lodash'; +import { startCase } from 'lodash-es'; export class ExperimentsFieldsPreview extends CardDef { @field url = contains(UrlField); diff --git a/packages/experiments-realm/llm-model-environment/environment.gts b/packages/experiments-realm/llm-model-environment/environment.gts index d6ab13bf6c..c637408647 100644 --- a/packages/experiments-realm/llm-model-environment/environment.gts +++ b/packages/experiments-realm/llm-model-environment/environment.gts @@ -23,7 +23,7 @@ import { import { Component } from 'https://cardstack.com/base/card-api'; import StringField from 'https://cardstack.com/base/string'; import { Skill } from 'https://cardstack.com/base/skill'; -import { includes, uniqBy } from 'lodash'; +import { includes, uniqBy } from 'lodash-es'; import SettingsIcon from '@cardstack/boxel-icons/settings'; import ZapIcon from '@cardstack/boxel-icons/zap'; diff --git a/packages/experiments-realm/package.json b/packages/experiments-realm/package.json index 48903a065b..45736a777d 100644 --- a/packages/experiments-realm/package.json +++ b/packages/experiments-realm/package.json @@ -12,7 +12,8 @@ "@types/qunit": "catalog:", "@universal-ember/test-support": "catalog:", "@cardstack/view-transitions": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", + "lodash-es": "catalog:", "ember-animated": "catalog:", "chess.js": "catalog:", "concurrently": "catalog:", diff --git a/packages/experiments-realm/tsconfig.json b/packages/experiments-realm/tsconfig.json index 48a215fb58..76d2fd3d87 100644 --- a/packages/experiments-realm/tsconfig.json +++ b/packages/experiments-realm/tsconfig.json @@ -23,18 +23,14 @@ "experimentalDecorators": true, "paths": { "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", "../base/*" ], - "@cardstack/host/tests/*": [ - "../host/tests/*" - ] + "@cardstack/host/tests/*": ["../host/tests/*"] }, - "types": [ - "@cardstack/local-types" - ] + "types": ["@cardstack/local-types"] }, - "include": [ - "**/*.ts", - "**/*.gts" - ] + "include": ["**/*.ts", "**/*.gts"] } diff --git a/packages/host/app/components/ai-assistant/message/index.gts b/packages/host/app/components/ai-assistant/message/index.gts index 532f14df15..21aad7c2cb 100644 --- a/packages/host/app/components/ai-assistant/message/index.gts +++ b/packages/host/app/components/ai-assistant/message/index.gts @@ -10,7 +10,7 @@ import Component from '@glimmer/component'; import { task } from 'ember-concurrency'; import perform from 'ember-concurrency/helpers/perform'; import Modifier from 'ember-modifier'; -import throttle from 'lodash/throttle'; +import { throttle } from 'lodash-es'; import { Alert } from '@cardstack/boxel-ui/components'; import { cn } from '@cardstack/boxel-ui/helpers'; diff --git a/packages/host/app/components/matrix/room.gts b/packages/host/app/components/matrix/room.gts index 9658112b29..0fd8b5b494 100644 --- a/packages/host/app/components/matrix/room.gts +++ b/packages/host/app/components/matrix/room.gts @@ -21,7 +21,7 @@ import { import perform from 'ember-concurrency/helpers/perform'; import { consume } from 'ember-provide-consume-context'; import { resource, use } from 'ember-resources'; -import max from 'lodash/max'; +import { max } from 'lodash-es'; import pluralize from 'pluralize'; diff --git a/packages/host/app/components/operator-mode/code-editor.gts b/packages/host/app/components/operator-mode/code-editor.gts index 7f39d05234..aff1f4b2fd 100644 --- a/packages/host/app/components/operator-mode/code-editor.gts +++ b/packages/host/app/components/operator-mode/code-editor.gts @@ -16,7 +16,7 @@ import { import perform from 'ember-concurrency/helpers/perform'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { Position } from 'monaco-editor'; diff --git a/packages/host/app/components/operator-mode/code-submode.gts b/packages/host/app/components/operator-mode/code-submode.gts index 7f09d01bac..07a3270d5f 100644 --- a/packages/host/app/components/operator-mode/code-submode.gts +++ b/packages/host/app/components/operator-mode/code-submode.gts @@ -19,7 +19,7 @@ import { consume, provide } from 'ember-provide-consume-context'; import { use, resource } from 'ember-resources'; import window from 'ember-window-mock'; -import startCase from 'lodash/startCase'; +import { startCase } from 'lodash-es'; import { TrackedObject } from 'tracked-built-ins'; import { diff --git a/packages/host/app/components/operator-mode/detail-panel.gts b/packages/host/app/components/operator-mode/detail-panel.gts index 319d2f8c96..7991123bbc 100644 --- a/packages/host/app/components/operator-mode/detail-panel.gts +++ b/packages/host/app/components/operator-mode/detail-panel.gts @@ -9,7 +9,7 @@ import Component from '@glimmer/component'; import Package from '@cardstack/boxel-icons/package'; import { use, resource } from 'ember-resources'; -import startCase from 'lodash/startCase'; +import { startCase } from 'lodash-es'; import { TrackedObject } from 'tracked-built-ins'; diff --git a/packages/host/app/components/operator-mode/interact-submode.gts b/packages/host/app/components/operator-mode/interact-submode.gts index b317baea5c..670017bc44 100644 --- a/packages/host/app/components/operator-mode/interact-submode.gts +++ b/packages/host/app/components/operator-mode/interact-submode.gts @@ -13,7 +13,7 @@ import perform from 'ember-concurrency/helpers/perform'; import onKeyMod from 'ember-keyboard/modifiers/on-key'; import { consume } from 'ember-provide-consume-context'; -import get from 'lodash/get'; +import { get } from 'lodash-es'; import { TrackedWeakMap, TrackedSet } from 'tracked-built-ins'; import { cn, gt, MenuItem, MenuDivider } from '@cardstack/boxel-ui/helpers'; diff --git a/packages/host/app/components/operator-mode/overlays.gts b/packages/host/app/components/operator-mode/overlays.gts index 1143d89fc0..ce110f4303 100644 --- a/packages/host/app/components/operator-mode/overlays.gts +++ b/packages/host/app/components/operator-mode/overlays.gts @@ -10,7 +10,7 @@ import { tracked } from '@glimmer/tracking'; import { dropTask } from 'ember-concurrency'; import { velcro } from 'ember-velcro'; -import { isEqual, omit } from 'lodash'; +import { isEqual, omit } from 'lodash-es'; import { localId as localIdSymbol, rri } from '@cardstack/runtime-common'; diff --git a/packages/host/app/helpers/subscribe-to-realms.ts b/packages/host/app/helpers/subscribe-to-realms.ts index 5401ae1712..70c517ee31 100644 --- a/packages/host/app/helpers/subscribe-to-realms.ts +++ b/packages/host/app/helpers/subscribe-to-realms.ts @@ -1,6 +1,6 @@ import Helper from '@ember/component/helper'; -import { isEqual } from 'lodash'; +import { isEqual } from 'lodash-es'; import { subscribeToRealm } from '@cardstack/runtime-common'; diff --git a/packages/host/app/lib/browser-queue.ts b/packages/host/app/lib/browser-queue.ts index 4a55c734ab..56c17919e7 100644 --- a/packages/host/app/lib/browser-queue.ts +++ b/packages/host/app/lib/browser-queue.ts @@ -1,4 +1,4 @@ -import debounce from 'lodash/debounce'; +import { debounce } from 'lodash-es'; import { type QueueRunner, diff --git a/packages/host/app/lib/externals.ts b/packages/host/app/lib/externals.ts index dbbd2e274c..993d77e8f0 100644 --- a/packages/host/app/lib/externals.ts +++ b/packages/host/app/lib/externals.ts @@ -43,7 +43,7 @@ import * as emberProvideConsumeContextContextConsumer from 'ember-provide-consum import * as emberProvideConsumeContextContextProvider from 'ember-provide-consume-context/components/context-provider'; import * as emberResources from 'ember-resources'; import * as flat from 'flat'; -import * as lodash from 'lodash'; +import * as lodash from 'lodash-es'; import * as matrixJsSDK from 'matrix-js-sdk'; import * as rsvp from 'rsvp'; import * as superFastMD5 from 'super-fast-md5'; @@ -180,7 +180,7 @@ export function shimExternals(virtualNetwork: VirtualNetwork) { }); virtualNetwork.shimModule('flat', flat); virtualNetwork.shimModule('@floating-ui/dom', floatingUiDom); - virtualNetwork.shimModule('lodash', lodash); + virtualNetwork.shimModule('lodash-es', lodash); virtualNetwork.shimModule('matrix-js-sdk', matrixJsSDK); virtualNetwork.shimModule('rsvp', rsvp); virtualNetwork.shimModule('super-fast-md5', superFastMD5); @@ -199,7 +199,7 @@ export function shimExternals(virtualNetwork: VirtualNetwork) { }); virtualNetwork.shimAsyncModule({ id: '@cardstack/runtime-common/marked-sync', - resolve: () => import('@cardstack/runtime-common/marked-sync.ts'), + resolve: () => import('@cardstack/runtime-common/marked-sync'), }); shimModulesForLiveTests(virtualNetwork); diff --git a/packages/host/app/lib/matrix-utils.ts b/packages/host/app/lib/matrix-utils.ts index 939510a2d9..4c9d45f588 100644 --- a/packages/host/app/lib/matrix-utils.ts +++ b/packages/host/app/lib/matrix-utils.ts @@ -1,4 +1,4 @@ -import difference from 'lodash/difference'; +import { difference } from 'lodash-es'; export interface MatrixError { data: { diff --git a/packages/host/app/lib/utils.ts b/packages/host/app/lib/utils.ts index 10684fb0fc..7faee7a011 100644 --- a/packages/host/app/lib/utils.ts +++ b/packages/host/app/lib/utils.ts @@ -1,4 +1,4 @@ -import deburr from 'lodash/deburr'; +import { deburr } from 'lodash-es'; import { realmURL, diff --git a/packages/host/app/resources/card-collection.ts b/packages/host/app/resources/card-collection.ts index 0dc3ef5bbe..85785f8da7 100644 --- a/packages/host/app/resources/card-collection.ts +++ b/packages/host/app/resources/card-collection.ts @@ -8,7 +8,7 @@ import { restartableTask } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { TrackedArray } from 'tracked-built-ins'; diff --git a/packages/host/app/resources/file-tree-from-index.ts b/packages/host/app/resources/file-tree-from-index.ts index c8b111bdba..6022d4638a 100644 --- a/packages/host/app/resources/file-tree-from-index.ts +++ b/packages/host/app/resources/file-tree-from-index.ts @@ -6,7 +6,7 @@ import { cached } from '@glimmer/tracking'; import { restartableTask } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { TrackedArray } from 'tracked-built-ins'; import { diff --git a/packages/host/app/resources/prerendered-search.ts b/packages/host/app/resources/prerendered-search.ts index 6756c036db..f71f43f5c4 100644 --- a/packages/host/app/resources/prerendered-search.ts +++ b/packages/host/app/resources/prerendered-search.ts @@ -8,7 +8,7 @@ import { tracked } from '@glimmer/tracking'; import { restartableTask } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { TrackedArray } from 'tracked-built-ins'; import { diff --git a/packages/host/app/resources/room.ts b/packages/host/app/resources/room.ts index 891d5964bd..a199eb1920 100644 --- a/packages/host/app/resources/room.ts +++ b/packages/host/app/resources/room.ts @@ -6,7 +6,7 @@ import { tracked, cached } from '@glimmer/tracking'; import { restartableTask, timeout } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import difference from 'lodash/difference'; +import { difference } from 'lodash-es'; import { TrackedMap } from 'tracked-built-ins'; diff --git a/packages/host/app/resources/search-entries.ts b/packages/host/app/resources/search-entries.ts index ae1a8b1fb7..f4d9d958b6 100644 --- a/packages/host/app/resources/search-entries.ts +++ b/packages/host/app/resources/search-entries.ts @@ -7,7 +7,7 @@ import { tracked } from '@glimmer/tracking'; import { didCancel, restartableTask } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { TrackedArray } from 'tracked-built-ins'; import { diff --git a/packages/host/app/resources/search.ts b/packages/host/app/resources/search.ts index 58240dff09..e2b75a721c 100644 --- a/packages/host/app/resources/search.ts +++ b/packages/host/app/resources/search.ts @@ -8,8 +8,8 @@ import { tracked, cached } from '@glimmer/tracking'; import { didCancel, restartableTask, task } from 'ember-concurrency'; import { Resource } from 'ember-modify-based-class-resource'; -import difference from 'lodash/difference'; -import isEqual from 'lodash/isEqual'; +import { difference } from 'lodash-es'; +import { isEqual } from 'lodash-es'; import { TrackedArray } from 'tracked-built-ins'; import type { diff --git a/packages/host/app/routes/module.ts b/packages/host/app/routes/module.ts index efec42d123..1e17996ddc 100644 --- a/packages/host/app/routes/module.ts +++ b/packages/host/app/routes/module.ts @@ -8,7 +8,7 @@ import { isTesting } from '@embroider/macros'; import { parse } from 'date-fns'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { type Definition, diff --git a/packages/host/app/routes/render/meta.ts b/packages/host/app/routes/render/meta.ts index 27be2364f2..35b68dc6f1 100644 --- a/packages/host/app/routes/render/meta.ts +++ b/packages/host/app/routes/render/meta.ts @@ -3,7 +3,7 @@ import type Transition from '@ember/routing/transition'; import { service } from '@ember/service'; -import { isEqual } from 'lodash'; +import { isEqual } from 'lodash-es'; import type { CodeRef } from '@cardstack/runtime-common'; import { diff --git a/packages/host/app/services/matrix-service.ts b/packages/host/app/services/matrix-service.ts index 1a59b35410..3d34b8c62d 100644 --- a/packages/host/app/services/matrix-service.ts +++ b/packages/host/app/services/matrix-service.ts @@ -9,7 +9,7 @@ import { cached, tracked } from '@glimmer/tracking'; import { dropTask, task, timeout } from 'ember-concurrency'; import window from 'ember-window-mock'; -import { cloneDeep } from 'lodash'; +import { cloneDeep } from 'lodash-es'; import { Filter } from 'matrix-js-sdk'; import { diff --git a/packages/host/app/services/monaco-service.ts b/packages/host/app/services/monaco-service.ts index 7cb287d17a..22684e891d 100644 --- a/packages/host/app/services/monaco-service.ts +++ b/packages/host/app/services/monaco-service.ts @@ -8,7 +8,7 @@ import { tracked } from '@glimmer/tracking'; import { task } from 'ember-concurrency'; -import merge from 'lodash/merge'; +import { merge } from 'lodash-es'; // The `?worker&url` suffix is a vite feature that builds each worker into a // standalone script outside the main bundle and gives us back its URL. We diff --git a/packages/host/app/services/store.ts b/packages/host/app/services/store.ts index 28780745d1..6a649e8f23 100644 --- a/packages/host/app/services/store.ts +++ b/packages/host/app/services/store.ts @@ -13,9 +13,9 @@ import { isTesting } from '@embroider/macros'; import { formatDistanceToNow } from 'date-fns'; import { task } from 'ember-concurrency'; -import cloneDeep from 'lodash/cloneDeep'; -import isEqual from 'lodash/isEqual'; -import merge from 'lodash/merge'; +import { cloneDeep } from 'lodash-es'; +import { isEqual } from 'lodash-es'; +import { merge } from 'lodash-es'; import { TrackedObject, TrackedMap } from 'tracked-built-ins'; diff --git a/packages/host/app/utils/file-def-attributes-extractor.ts b/packages/host/app/utils/file-def-attributes-extractor.ts index ef2daf7571..1557be43d3 100644 --- a/packages/host/app/utils/file-def-attributes-extractor.ts +++ b/packages/host/app/utils/file-def-attributes-extractor.ts @@ -1,4 +1,4 @@ -import { isEqual } from 'lodash'; +import { isEqual } from 'lodash-es'; import { baseRef, diff --git a/packages/host/config/environment.js b/packages/host/config/environment.js index f116695c89..654fdc14d6 100644 --- a/packages/host/config/environment.js +++ b/packages/host/config/environment.js @@ -241,13 +241,18 @@ function getLatestSchemaFile() { path.join(__dirname, '..', '..', 'postgres', 'migrations'), ); let migrations = fs.readdirSync(migrationsDir); + // Only timestamped migration files — ignores non-migration entries in the dir + // such as `package.json` (pins the dir to type:commonjs) and `.eslintrc.js`. let lastMigration = migrations - .filter((f) => f !== '.eslintrc.js') + .filter((f) => /^\d+_/.test(f)) .sort() .pop(); const schemaDir = path.join(__dirname, 'schema'); let files = fs.readdirSync(schemaDir); - let latestSchemaFile = files.sort().pop(); + let latestSchemaFile = files + .filter((f) => /^\d+_schema\.sql$/.test(f)) + .sort() + .pop(); if ( lastMigration.replace(/_.*/, '') !== latestSchemaFile.replace(/_.*/, '') && ['development', 'test'].includes(process.env.EMBER_ENV) diff --git a/packages/host/package.json b/packages/host/package.json index da9a5d294f..4934773f86 100644 --- a/packages/host/package.json +++ b/packages/host/package.json @@ -81,7 +81,7 @@ "@types/flat": "catalog:", "@types/htmlbars-inline-precompile": "catalog:", "@types/indefinite": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/matrix-js-sdk": "catalog:", "@types/ms": "catalog:", "@types/pluralize": "catalog:", @@ -159,7 +159,7 @@ "ignore": "catalog:", "indefinite": "catalog:", "katex": "catalog:", - "lodash": "catalog:", + "lodash-es": "catalog:", "loglevel": "catalog:", "matrix-js-sdk": "catalog:", "mermaid": "catalog:", diff --git a/packages/host/tests/helpers/indexer.ts b/packages/host/tests/helpers/indexer.ts index 2358922723..880f272347 100644 --- a/packages/host/tests/helpers/indexer.ts +++ b/packages/host/tests/helpers/indexer.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import type { LooseCardResource, DBAdapter } from '@cardstack/runtime-common'; import { diff --git a/packages/host/tests/integration/realm-indexing-test.gts b/packages/host/tests/integration/realm-indexing-test.gts index 7a5f7642df..2e1b861ebd 100644 --- a/packages/host/tests/integration/realm-indexing-test.gts +++ b/packages/host/tests/integration/realm-indexing-test.gts @@ -4795,7 +4795,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/ember-css-url', 'https://packages/ember-modifier', 'https://packages/ember-provide-consume-context', - 'https://packages/lodash', + 'https://packages/lodash-es', 'https://packages/super-fast-md5', 'https://packages/tracked-built-ins', // Sort the expected list so the assertion is robust against @@ -4968,7 +4968,7 @@ module(`Integration | realm indexing`, function (hooks) { 'https://packages/ember-modifier', 'https://packages/ember-provide-consume-context', 'https://packages/ember-resources', - 'https://packages/lodash', + 'https://packages/lodash-es', 'https://packages/super-fast-md5', 'https://packages/tracked-built-ins', // See note on iconsBase ordering above. diff --git a/packages/host/tsconfig.json b/packages/host/tsconfig.json index 3dcfaa3492..4b23b4dbb6 100644 --- a/packages/host/tsconfig.json +++ b/packages/host/tsconfig.json @@ -28,7 +28,12 @@ "@cardstack/host/tests/*": ["tests/*"], "@cardstack/host/*": ["app/*"], "@cardstack/boxel-host/commands/*": ["app/commands/*"], - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/openrouter/*": ["../openrouter-realm/*"], "*": ["types/*"] }, diff --git a/packages/matrix/package.json b/packages/matrix/package.json index 2e51a4151a..fda3e34e8b 100644 --- a/packages/matrix/package.json +++ b/packages/matrix/package.json @@ -2,6 +2,7 @@ "name": "@cardstack/matrix", "version": "1.0.0", "license": "MIT", + "type": "module", "devDependencies": { "@aws-crypto/sha256-js": "catalog:", "@cardstack/runtime-common": "workspace:*", @@ -17,35 +18,34 @@ "pg": "catalog:", "start-server-and-test": "catalog:", "tmp": "catalog:", - "ts-node": "^10.9.1", "typescript": "catalog:", "uuid": "catalog:", "yaml": "catalog:" }, "scripts": { "start:synapse": "./scripts/start-synapse.sh", - "stop:synapse": "ts-node --transpileOnly ./scripts/synapse.ts stop", + "stop:synapse": "node ./scripts/synapse.ts stop", "assert-synapse-running": "./scripts/assert-synapse-running.sh", - "start:smtp": "ts-node --transpileOnly ./scripts/smtp.ts start", - "stop:smtp": "ts-node --transpileOnly ./scripts/smtp.ts stop", + "start:smtp": "node ./scripts/smtp.ts start", + "stop:smtp": "node ./scripts/smtp.ts stop", "assert-smtp-running": "if [ \"`docker ps -f name='boxel-smtp' --format '{{.Names}}'`\" = 'boxel-smtp' ]; then echo 'SMTP is already running'; else pnpm run start:smtp; fi", "start:host-pre-built": "cd ../host && pnpm start --path ./dist", - "start:admin": "ts-node --transpileOnly ./scripts/admin-console", + "start:admin": "node ./scripts/admin-console.ts", "stop:admin": "docker stop synapse-admin && docker rm synapse-admin", - "register-bot-user": "MATRIX_USERNAME=aibot MATRIX_PASSWORD=pass ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-submission-bot": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-realm-user-using-api": "MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-realm-user-using-api.ts", - "realm-user-password": "ts-node --transpileOnly ./scripts/reveal-realm-user-password.ts", - "setup-submission-bot": "MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/setup-submission-bot.ts", - "register-test-user": "MATRIX_USERNAME=user MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-skills-writer": "MATRIX_USERNAME=skills_writer MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-homepage-writer": "MATRIX_USERNAME=homepage_writer MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-realm-users": "ts-node --transpileOnly ./scripts/register-matrix-users.ts realms-only", - "migrate-account-data-http-to-https": "ts-node --transpileOnly ./scripts/migrate-account-data-http-to-https.ts", - "migrate-account-data-https-to-http": "ts-node --transpileOnly ./scripts/migrate-account-data-http-to-https.ts --reverse", - "register-test-admin": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=admin MATRIX_PASSWORD=password ts-node --transpileOnly ./scripts/register-test-user.ts", - "register-test-admin-and-token": "pnpm register-test-admin && ts-node --transpileOnly ./scripts/register-test-token.ts", - "register-all": "ts-node --transpileOnly ./scripts/register-matrix-users.ts all", + "register-bot-user": "MATRIX_USERNAME=aibot MATRIX_PASSWORD=pass node ./scripts/register-test-user.ts", + "register-submission-bot": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password node ./scripts/register-test-user.ts", + "register-realm-user-using-api": "MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password node ./scripts/register-realm-user-using-api.ts", + "realm-user-password": "node ./scripts/reveal-realm-user-password.ts", + "setup-submission-bot": "MATRIX_USERNAME=submissionbot MATRIX_PASSWORD=password node ./scripts/setup-submission-bot.ts", + "register-test-user": "MATRIX_USERNAME=user MATRIX_PASSWORD=password node ./scripts/register-test-user.ts", + "register-skills-writer": "MATRIX_USERNAME=skills_writer MATRIX_PASSWORD=password node ./scripts/register-test-user.ts", + "register-homepage-writer": "MATRIX_USERNAME=homepage_writer MATRIX_PASSWORD=password node ./scripts/register-test-user.ts", + "register-realm-users": "node ./scripts/register-matrix-users.ts realms-only", + "migrate-account-data-http-to-https": "node ./scripts/migrate-account-data-http-to-https.ts", + "migrate-account-data-https-to-http": "node ./scripts/migrate-account-data-http-to-https.ts --reverse", + "register-test-admin": "MATRIX_IS_ADMIN=TRUE MATRIX_USERNAME=admin MATRIX_PASSWORD=password node ./scripts/register-test-user.ts", + "register-test-admin-and-token": "pnpm register-test-admin && node ./scripts/register-test-token.ts", + "register-all": "node ./scripts/register-matrix-users.ts all", "test": "./scripts/test.sh", "test:cleanup": "./scripts/remove-test-dbs.sh", "test:group": "./scripts/test.sh", diff --git a/packages/matrix/scripts/assert-synapse-running.sh b/packages/matrix/scripts/assert-synapse-running.sh index 06750b0f7f..9d6a3ced9d 100755 --- a/packages/matrix/scripts/assert-synapse-running.sh +++ b/packages/matrix/scripts/assert-synapse-running.sh @@ -25,7 +25,7 @@ if [ "$RUNNING" = "$CONTAINER_NAME" ]; then if [ -n "$BOXEL_ENVIRONMENT" ]; then HOST_PORT=$(docker port "$CONTAINER_NAME" 8008/tcp 2>/dev/null | head -1 | awk -F: '{print $NF}') if [ -n "$HOST_PORT" ]; then - pnpm exec ts-node --transpileOnly -e "import { registerSynapseWithTraefik } from './support/environment-config'; registerSynapseWithTraefik($HOST_PORT);" + node --input-type=module -e "import { registerSynapseWithTraefik } from './support/environment-config.ts'; registerSynapseWithTraefik($HOST_PORT);" fi fi else diff --git a/packages/matrix/scripts/migrate-realm-user.sh b/packages/matrix/scripts/migrate-realm-user.sh index d460e2a000..646670d5d5 100755 --- a/packages/matrix/scripts/migrate-realm-user.sh +++ b/packages/matrix/scripts/migrate-realm-user.sh @@ -10,4 +10,4 @@ if [ -z "$1" ]; then exit -1 fi -ts-node --transpileOnly ./scripts/migrate-realm-user $1 +node ./scripts/migrate-realm-user.ts $1 diff --git a/packages/matrix/scripts/migrate-realm-users.sh b/packages/matrix/scripts/migrate-realm-users.sh index 02f2efbea3..47d919a050 100755 --- a/packages/matrix/scripts/migrate-realm-users.sh +++ b/packages/matrix/scripts/migrate-realm-users.sh @@ -3,12 +3,12 @@ : ${REALM_SECRET_SEED:="shhh! it's a secret"} export REALM_SECRET_SEED -ts-node --transpileOnly ./scripts/migrate-realm-user @realm-server:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @node-test_realm-server:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @base_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @boxel_homepage_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @submission_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @experiments_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @software_factory_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @node-test_realm:localhost -ts-node --transpileOnly ./scripts/migrate-realm-user @test_realm:localhost +node ./scripts/migrate-realm-user.ts @realm-server:localhost +node ./scripts/migrate-realm-user.ts @node-test_realm-server:localhost +node ./scripts/migrate-realm-user.ts @base_realm:localhost +node ./scripts/migrate-realm-user.ts @boxel_homepage_realm:localhost +node ./scripts/migrate-realm-user.ts @submission_realm:localhost +node ./scripts/migrate-realm-user.ts @experiments_realm:localhost +node ./scripts/migrate-realm-user.ts @software_factory_realm:localhost +node ./scripts/migrate-realm-user.ts @node-test_realm:localhost +node ./scripts/migrate-realm-user.ts @test_realm:localhost diff --git a/packages/matrix/scripts/register-github-webhook-for-submission-realm.ts b/packages/matrix/scripts/register-github-webhook-for-submission-realm.ts index 3c63f14197..036071f875 100755 --- a/packages/matrix/scripts/register-github-webhook-for-submission-realm.ts +++ b/packages/matrix/scripts/register-github-webhook-for-submission-realm.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env -S node import { registerRealmUser } from './register-realm-user-using-api.ts'; import { realmPassword } from '../helpers/realm-credentials.ts'; @@ -274,7 +274,7 @@ async function main() { console.log('='.repeat(70)); } -if (require.main === module) { +if (import.meta.main) { main() .then(() => { console.log('✓ GitHub webhook registration complete'); diff --git a/packages/matrix/scripts/register-realm-user-using-api.ts b/packages/matrix/scripts/register-realm-user-using-api.ts index ed5abc0090..d629842f4b 100644 --- a/packages/matrix/scripts/register-realm-user-using-api.ts +++ b/packages/matrix/scripts/register-realm-user-using-api.ts @@ -123,7 +123,7 @@ export async function registerRealmUser(): Promise<{ return { jwt, userId }; } -if (require.main === module) { +if (import.meta.main) { registerRealmUser() .then(({ userId }) => { console.log(`Registered realm user ${userId}`); diff --git a/packages/matrix/scripts/remove-test-dbs.sh b/packages/matrix/scripts/remove-test-dbs.sh index 43028801ab..128ca019c3 100755 --- a/packages/matrix/scripts/remove-test-dbs.sh +++ b/packages/matrix/scripts/remove-test-dbs.sh @@ -1,11 +1,11 @@ #!/bin/sh # forcibly stop any isolated realm processes -isolated_realm_processes=$(ps -ef | grep ts-node | grep '\-\-port=4205' | awk '{print $2}') +isolated_realm_processes=$(ps -ef | grep node | grep '\-\-port=4205' | awk '{print $2}') for pid in $isolated_realm_processes; do kill -9 $pid done -isolated_realm_processes=$(ps -ef | grep ts-node | grep '\-\-port=4212' | awk '{print $2}') +isolated_realm_processes=$(ps -ef | grep node | grep '\-\-port=4212' | awk '{print $2}') for pid in $isolated_realm_processes; do kill -9 $pid done diff --git a/packages/matrix/scripts/reveal-realm-user-password.ts b/packages/matrix/scripts/reveal-realm-user-password.ts index b092b655ff..2662a60e34 100755 --- a/packages/matrix/scripts/reveal-realm-user-password.ts +++ b/packages/matrix/scripts/reveal-realm-user-password.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env ts-node +#!/usr/bin/env -S node import { realmPassword } from '../helpers/realm-credentials.ts'; diff --git a/packages/matrix/scripts/start-synapse.sh b/packages/matrix/scripts/start-synapse.sh index aeadba1367..9e7b02748f 100755 --- a/packages/matrix/scripts/start-synapse.sh +++ b/packages/matrix/scripts/start-synapse.sh @@ -13,4 +13,4 @@ fi mkdir -p "${SYNAPSE_DATA_DIR}/db" export SYNAPSE_DATA_DIR -exec ts-node --transpileOnly ./scripts/synapse.ts start +exec node ./scripts/synapse.ts start diff --git a/packages/matrix/support/docker.ts b/packages/matrix/support/docker.ts index c3b832a9c1..4691e1a538 100644 --- a/packages/matrix/support/docker.ts +++ b/packages/matrix/support/docker.ts @@ -1,6 +1,6 @@ import * as os from 'os'; import * as childProcess from 'child_process'; -import * as fse from 'fs-extra'; +import fse from 'fs-extra'; function imageExistsLocally(image: string): Promise { return new Promise((resolve) => { diff --git a/packages/matrix/support/environment-config.ts b/packages/matrix/support/environment-config.ts index 760f808474..5ebdc60712 100644 --- a/packages/matrix/support/environment-config.ts +++ b/packages/matrix/support/environment-config.ts @@ -24,7 +24,14 @@ function traefikDynamicDir(): string { } catch { // Traefik not running — fall back to repo-relative path } - _traefikDir = resolve(__dirname, '..', '..', '..', 'traefik', 'dynamic'); + _traefikDir = resolve( + import.meta.dirname, + '..', + '..', + '..', + 'traefik', + 'dynamic', + ); return _traefikDir; } diff --git a/packages/matrix/support/isolated-realm-server.ts b/packages/matrix/support/isolated-realm-server.ts index 571fb95766..c82a84e5ac 100644 --- a/packages/matrix/support/isolated-realm-server.ts +++ b/packages/matrix/support/isolated-realm-server.ts @@ -2,7 +2,8 @@ import { spawn, type ChildProcess } from 'child_process'; import { resolve, join } from 'path'; // @ts-expect-error no types import { dirSync, setGracefulCleanup } from 'tmp'; -import { ensureDirSync, copySync, readFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync, copySync, readFileSync } = fsExtra; import { Pool } from 'pg'; import { createServer as createNetServer, type AddressInfo } from 'net'; import type { SynapseInstance } from './synapse/index.ts'; @@ -22,14 +23,16 @@ setGracefulCleanup(); // `writable` patch, and the hand-rolled `proxyAsset` forwarder). const testRealmCards = resolve( - join(__dirname, '..', '..', 'test-realm-cards', 'contents'), + join(import.meta.dirname, '..', '..', 'test-realm-cards', 'contents'), +); +const realmServerDir = resolve( + join(import.meta.dirname, '..', '..', 'realm-server'), ); -const realmServerDir = resolve(join(__dirname, '..', '..', 'realm-server')); const skillsRealmDir = resolve( - join(__dirname, '..', '..', 'skills-realm', 'contents'), + join(import.meta.dirname, '..', '..', 'skills-realm', 'contents'), ); -const baseRealmDir = resolve(join(__dirname, '..', '..', 'base')); -const matrixDir = resolve(join(__dirname, '..')); +const baseRealmDir = resolve(join(import.meta.dirname, '..', '..', 'base')); +const matrixDir = resolve(join(import.meta.dirname, '..')); export const appURL = 'https://localhost:4205/test'; const DEFAULT_PRERENDER_PORT = 4231; @@ -288,13 +291,9 @@ export async function startPrerenderServer( PRERENDER_PAGE_POOL_MIN: process.env.PRERENDER_PAGE_POOL_MIN ?? '4', PRERENDER_PAGE_POOL_MAX: process.env.PRERENDER_PAGE_POOL_MAX ?? '8', }; - let prerenderArgs = [ - '--transpileOnly', - 'prerender/prerender-server', - `--port=${port}`, - ]; + let prerenderArgs = ['prerender/prerender-server.ts', `--port=${port}`]; - let child = spawn('ts-node', prerenderArgs, { + let child = spawn('node', prerenderArgs, { cwd: realmServerDir, stdio: ['pipe', 'pipe', 'pipe'], env, @@ -405,8 +404,7 @@ export async function startServer({ process.env.LOW_CREDIT_THRESHOLD = '2000'; let workerArgs = [ - `--transpileOnly`, - 'worker-manager', + 'worker-manager.ts', `--port=${workerManagerPort}`, `--matrixURL='${matrixURL}'`, `--prerendererUrl='${prerenderURL}'`, @@ -424,7 +422,7 @@ export async function startServer({ `--toUrl='https://localhost:4205/base/'`, ]); - let workerManager = spawn('ts-node', workerArgs, { + let workerManager = spawn('node', workerArgs, { cwd: realmServerDir, stdio: ['pipe', 'pipe', 'pipe', 'ipc'], env: { @@ -515,8 +513,7 @@ export async function startServer({ } let serverArgs = [ - `--transpileOnly`, - 'main', + 'main.ts', `--port=4205`, `--matrixURL='${matrixURL}'`, `--realmsRootPath='${dir.name}'`, @@ -544,7 +541,7 @@ export async function startServer({ console.log(`realm server database: ${testDBName}`); - realmServer = spawn('ts-node', serverArgs, { + realmServer = spawn('node', serverArgs, { cwd: realmServerDir, stdio: ['pipe', 'pipe', 'pipe', 'ipc'], env: { diff --git a/packages/matrix/support/synapse/index.ts b/packages/matrix/support/synapse/index.ts index 2a2813852e..48755ebf20 100644 --- a/packages/matrix/support/synapse/index.ts +++ b/packages/matrix/support/synapse/index.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as os from 'os'; import * as crypto from 'crypto'; import * as net from 'net'; -import * as fse from 'fs-extra'; +import fse from 'fs-extra'; import { request } from '@playwright/test'; import { dockerCreateNetwork, @@ -24,7 +24,7 @@ export const SYNAPSE_IP_ADDRESS = '172.20.0.5'; export const SYNAPSE_PORT = 8008; const registrationSecretFile = path.resolve( - path.join(__dirname, '..', '..', 'registration_secret.txt'), + path.join(import.meta.dirname, '..', '..', 'registration_secret.txt'), ); interface SynapseConfig { @@ -94,7 +94,7 @@ export async function cfgDirFromTemplate( port?: number; }, ): Promise { - const templateDir = path.join(__dirname, template); + const templateDir = path.join(import.meta.dirname, template); const stats = await fse.stat(templateDir); if (!stats?.isDirectory) { @@ -215,7 +215,7 @@ export async function synapseStart( '-v', `${synCfg.configDir}:/data`, '-v', - `${path.join(__dirname, 'templates')}:/custom/templates/`, + `${path.join(import.meta.dirname, 'templates')}:/custom/templates/`, ]; if (useDynamicHostPort) { // In dynamic-host-port mode multiple harnesses may run concurrently, so diff --git a/packages/matrix/tsconfig.json b/packages/matrix/tsconfig.json index 3e9a6a4fde..9296f90469 100644 --- a/packages/matrix/tsconfig.json +++ b/packages/matrix/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es2020", "allowJs": true, - "moduleResolution": "node", + "moduleResolution": "nodenext", "allowImportingTsExtensions": true, "allowSyntheticDefaultImports": true, "noImplicitAny": true, @@ -19,12 +19,18 @@ "inlineSourceMap": true, "inlineSources": true, "baseUrl": ".", - "module": "CommonJS", + "module": "nodenext", "esModuleInterop": true, + "skipLibCheck": true, "experimentalDecorators": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"], "*": ["types/*"] diff --git a/packages/postgres/.eslintrc.js b/packages/postgres/.eslintrc.cjs similarity index 100% rename from packages/postgres/.eslintrc.js rename to packages/postgres/.eslintrc.cjs diff --git a/packages/postgres/Dockerfile b/packages/postgres/Dockerfile index 4b442434eb..d78f2e064c 100644 --- a/packages/postgres/Dockerfile +++ b/packages/postgres/Dockerfile @@ -20,4 +20,4 @@ RUN CI=1 pnpm install -r --offline WORKDIR /boxel/packages/postgres -CMD ./node_modules/.bin/ts-node --transpileOnly ./scripts/fix-migration-names.ts && ./node_modules/.bin/node-pg-migrate --check-order false --migrations-table migrations up && sleep infinity +CMD node ./scripts/fix-migration-names.ts && ./node_modules/.bin/node-pg-migrate --check-order false --migrations-table migrations up && sleep infinity diff --git a/packages/postgres/migrations/package.json b/packages/postgres/migrations/package.json new file mode 100644 index 0000000000..5bbefffbab --- /dev/null +++ b/packages/postgres/migrations/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/packages/postgres/package.json b/packages/postgres/package.json index 4e55d8c97d..d27148be11 100644 --- a/packages/postgres/package.json +++ b/packages/postgres/package.json @@ -2,6 +2,12 @@ "name": "@cardstack/postgres", "version": "0.0.0", "license": "MIT", + "type": "module", + "exports": { + ".": "./index.ts", + "./package.json": "./package.json", + "./*": "./*.ts" + }, "dependencies": { "@cardstack/runtime-common": "workspace:*", "@sentry/node": "catalog:", @@ -9,8 +15,7 @@ "@types/pg": "catalog:", "fs-extra": "catalog:", "node-pg-migrate": "catalog:", - "pg": "catalog:", - "ts-node": "^10.9.1" + "pg": "catalog:" }, "devDependencies": { "@cardstack/local-types": "workspace:*", @@ -21,14 +26,14 @@ "scripts": { "start:pg": "./scripts/start-pg.sh", "stop:pg": "./scripts/stop-pg.sh", - "migrate": "PGDATABASE=boxel ./scripts/ensure-db-exists.sh && PGPORT=5435 PGDATABASE=boxel PGUSER=postgres ts-node --transpileOnly ./scripts/fix-migration-names.ts && PGPORT=5435 PGDATABASE=boxel PGUSER=postgres node-pg-migrate --migrations-table migrations --no-check-order --verbose=false", + "migrate": "PGDATABASE=boxel ./scripts/ensure-db-exists.sh && PGPORT=5435 PGDATABASE=boxel PGUSER=postgres node ./scripts/fix-migration-names.ts && PGPORT=5435 PGDATABASE=boxel PGUSER=postgres node-pg-migrate --migrations-table migrations --no-check-order --ignore-pattern '.*\\.eslintrc\\.js|package\\.json' --verbose=false", "make-schema": "./scripts/schema-dump.sh", "drop-db": "docker exec boxel-pg dropdb --if-exists -U postgres -w", "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", "lint:js": "eslint . --report-unused-disable-directives --cache", "lint:js:fix": "eslint . --report-unused-disable-directives --fix", - "lint:migrations": "node ./scripts/lint-migrations.js", + "lint:migrations": "node ./scripts/lint-migrations.cjs", "lint:types": "ember-tsc --noEmit" } } diff --git a/packages/postgres/pg-adapter.ts b/packages/postgres/pg-adapter.ts index 07ca49b40f..c91704f95d 100644 --- a/packages/postgres/pg-adapter.ts +++ b/packages/postgres/pg-adapter.ts @@ -9,12 +9,20 @@ import { param, } from '@cardstack/runtime-common'; import { createHash } from 'crypto'; -import migrate from 'node-pg-migrate'; +import nodePgMigrate, { type RunnerOption } from 'node-pg-migrate'; import { join } from 'path'; import { Pool, Client, type Notification } from 'pg'; import { postgresConfig } from './pg-config.ts'; -import migrationNameFixes from './scripts/migration-name-fixes.js'; +import migrationNameFixes from './scripts/migration-name-fixes.cjs'; + +// node-pg-migrate is CJS and exposes the runner as its default export. Under +// native ESM the default import binds the module's namespace object, so the +// callable lives on `.default`; the package's types declare the default as the +// runner, so cast to its call signature. +const migrate = ((nodePgMigrate as any).default ?? nodePgMigrate) as ( + options: RunnerOption, +) => Promise; // Hash a realm URL to a stable signed int64 (as a string, because JS numbers // can't represent the full int64 range). Used as a pg advisory lock key: @@ -658,8 +666,11 @@ export class PgAdapter implements DBAdapter { port: config.port, }, count: Infinity, - dir: join(__dirname, 'migrations'), - ignorePattern: '.*\\.eslintrc\\.js', + dir: join(import.meta.dirname, 'migrations'), + // Ignore the eslint config and the `package.json` that pins this dir + // to `type:commonjs` (the CJS migration files use `exports.up`); both + // sit in the migrations dir but aren't migrations. + ignorePattern: '.*\\.eslintrc\\.js|package\\.json', log: enableLogging ? (...args) => log.info(...args) : () => undefined, }); await this.fixupEnvironmentModePermissions(config); diff --git a/packages/postgres/scripts/convert-to-sqlite.ts b/packages/postgres/scripts/convert-to-sqlite.ts index b6016c8c34..ea6f9a23f4 100755 --- a/packages/postgres/scripts/convert-to-sqlite.ts +++ b/packages/postgres/scripts/convert-to-sqlite.ts @@ -1,5 +1,5 @@ /* eslint-env node */ -import { readFileSync, readdirSync, writeFileSync } from 'fs-extra'; +import { readFileSync, readdirSync, writeFileSync } from 'fs'; import { resolve, join } from 'path'; import { parse, @@ -16,9 +16,9 @@ import { // much more simplistic than postgres. const args = process.argv; -const migrationsDir = resolve(join(__dirname, '..', 'migrations')); +const migrationsDir = resolve(join(import.meta.dirname, '..', 'migrations')); const sqliteSchemaDir = resolve( - join(__dirname, '..', '..', 'host', 'config', 'schema'), + join(import.meta.dirname, '..', '..', 'host', 'config', 'schema'), ); const INDENT = ' '; const SQLITE_PK_COLUMN_MAPPING: Record> = { diff --git a/packages/postgres/scripts/fix-migration-names.ts b/packages/postgres/scripts/fix-migration-names.ts index 0ee566d446..efc7fc82f2 100755 --- a/packages/postgres/scripts/fix-migration-names.ts +++ b/packages/postgres/scripts/fix-migration-names.ts @@ -1,7 +1,7 @@ /* eslint-env node */ import { Client } from 'pg'; -import migrationNameFixes from './migration-name-fixes.js'; +import migrationNameFixes from './migration-name-fixes.cjs'; type MigrationNameFixes = { migrationRenames: Array<[string, string]>; diff --git a/packages/postgres/scripts/lint-migrations.js b/packages/postgres/scripts/lint-migrations.cjs similarity index 100% rename from packages/postgres/scripts/lint-migrations.js rename to packages/postgres/scripts/lint-migrations.cjs diff --git a/packages/postgres/scripts/migration-name-fixes.js b/packages/postgres/scripts/migration-name-fixes.cjs similarity index 100% rename from packages/postgres/scripts/migration-name-fixes.js rename to packages/postgres/scripts/migration-name-fixes.cjs diff --git a/packages/postgres/scripts/schema-dump.sh b/packages/postgres/scripts/schema-dump.sh index 75d5d4d648..e947ea3e59 100755 --- a/packages/postgres/scripts/schema-dump.sh +++ b/packages/postgres/scripts/schema-dump.sh @@ -38,5 +38,5 @@ docker exec boxel-pg pg_dump \ --no-acl \ boxel >$tmpFile -ts-node --transpileOnly ./scripts/convert-to-sqlite.ts $tmpFile +node ./scripts/convert-to-sqlite.ts $tmpFile rm $tmpFile diff --git a/packages/postgres/tsconfig.json b/packages/postgres/tsconfig.json index 9bd38a1942..19868bcffc 100644 --- a/packages/postgres/tsconfig.json +++ b/packages/postgres/tsconfig.json @@ -25,7 +25,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"], "*": ["types/*"] diff --git a/packages/realm-server/.eslintrc.js b/packages/realm-server/.eslintrc.cjs similarity index 100% rename from packages/realm-server/.eslintrc.js rename to packages/realm-server/.eslintrc.cjs diff --git a/packages/realm-server/handlers/create-realm.ts b/packages/realm-server/handlers/create-realm.ts index ea3d7b3ee9..6809570b54 100644 --- a/packages/realm-server/handlers/create-realm.ts +++ b/packages/realm-server/handlers/create-realm.ts @@ -1,6 +1,7 @@ import type Koa from 'koa'; import { resolve, join } from 'path'; -import { ensureDirSync, writeJSONSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync, writeJSONSync } = fsExtra; import * as Sentry from '@sentry/node'; import type { DBAdapter, diff --git a/packages/realm-server/handlers/handle-download-realm.ts b/packages/realm-server/handlers/handle-download-realm.ts index 47575bbaf7..8c598bd728 100644 --- a/packages/realm-server/handlers/handle-download-realm.ts +++ b/packages/realm-server/handlers/handle-download-realm.ts @@ -12,7 +12,8 @@ import { AuthenticationError } from '@cardstack/runtime-common/router'; import { parseRealmsParam } from '@cardstack/runtime-common/search-utils'; import { verifyURLSignature } from '@cardstack/runtime-common/url-signature'; import archiver from 'archiver'; -import { existsSync, statSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync, statSync } = fsExtra; import { join, relative, resolve, sep, isAbsolute } from 'path'; import type { CreateRoutesArgs } from '../routes.ts'; import { retrieveTokenClaim } from '../utils/jwt.ts'; diff --git a/packages/realm-server/handlers/handle-indexing-dashboard.ts b/packages/realm-server/handlers/handle-indexing-dashboard.ts index 0391f8e93f..a4d7d9f2cb 100644 --- a/packages/realm-server/handlers/handle-indexing-dashboard.ts +++ b/packages/realm-server/handlers/handle-indexing-dashboard.ts @@ -1,7 +1,12 @@ import { readFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; import type { RealmIndexingState } from '../indexing-event-sink.ts'; +// `require` doesn't exist in ESM scope; recreate it to resolve the bundled +// morphdom asset path. +const require = createRequire(import.meta.url); + // morphdom's UMD build, inlined into the dashboard so the auto-refresh can // patch the live DOM in place instead of reloading the page. Read once at // module load; the dashboard is a local-only debug surface. diff --git a/packages/realm-server/handlers/handle-post-deployment.ts b/packages/realm-server/handlers/handle-post-deployment.ts index a139307f69..03ef927ddf 100644 --- a/packages/realm-server/handlers/handle-post-deployment.ts +++ b/packages/realm-server/handlers/handle-post-deployment.ts @@ -8,10 +8,7 @@ import { setContextResponse, } from '../middleware/index.ts'; import type { CreateRoutesArgs } from '../routes.ts'; -import { - compareCurrentBoxelUIChecksum, - writeCurrentBoxelUIChecksum, -} from '../lib/boxel-ui-change-checker.ts'; +import { boxelUIChecker } from '../lib/boxel-ui-change-checker.ts'; import { getFullReindexRealmUrls } from '../lib/full-reindex-realm-urls.ts'; export default function handlePostDeployment({ @@ -39,7 +36,7 @@ export default function handlePostDeployment({ await definitionLookup.clearAllDefinitions(); let boxelUiChangeCheckerResult = - await compareCurrentBoxelUIChecksum(assetsURL); + await boxelUIChecker.compareCurrentBoxelUIChecksum(assetsURL); if ( boxelUiChangeCheckerResult.currentChecksum !== @@ -57,7 +54,9 @@ export default function handlePostDeployment({ }, }); - writeCurrentBoxelUIChecksum(boxelUiChangeCheckerResult.currentChecksum); + boxelUIChecker.writeCurrentBoxelUIChecksum( + boxelUiChangeCheckerResult.currentChecksum, + ); } await setContextResponse( diff --git a/packages/realm-server/handlers/handle-publish-realm.ts b/packages/realm-server/handlers/handle-publish-realm.ts index aa16dbfd93..549003c1ec 100644 --- a/packages/realm-server/handlers/handle-publish-realm.ts +++ b/packages/realm-server/handlers/handle-publish-realm.ts @@ -18,14 +18,15 @@ import { import { getPublishedRealmDomainOverrides } from '@cardstack/runtime-common/constants'; import { join } from 'path'; -import { +import fsExtra from 'fs-extra'; +const { copySync, readJsonSync, writeJsonSync, removeSync, existsSync, moveSync, -} from 'fs-extra'; +} = fsExtra; import { fetchRequestFromContext, diff --git a/packages/realm-server/handlers/handle-realm-auth.ts b/packages/realm-server/handlers/handle-realm-auth.ts index 2374e65cd1..885d771278 100644 --- a/packages/realm-server/handlers/handle-realm-auth.ts +++ b/packages/realm-server/handlers/handle-realm-auth.ts @@ -11,7 +11,7 @@ import { upsertSessionRoom, type Expression, } from '@cardstack/runtime-common'; -import type { RealmServerTokenClaim } from 'utils/jwt'; +import type { RealmServerTokenClaim } from '../utils/jwt.ts'; import { getUserByMatrixUserId } from '@cardstack/billing/billing-queries'; import { createJWT } from '../jwt.ts'; import { diff --git a/packages/realm-server/handlers/realm-destruction-utils.ts b/packages/realm-server/handlers/realm-destruction-utils.ts index 889c01f147..af7d38d0db 100644 --- a/packages/realm-server/handlers/realm-destruction-utils.ts +++ b/packages/realm-server/handlers/realm-destruction-utils.ts @@ -6,7 +6,8 @@ import { param, separatedByCommas, } from '@cardstack/runtime-common'; -import { pathExistsSync, readdirSync, removeSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { pathExistsSync, readdirSync, removeSync } = fsExtra; import { join, relative } from 'path'; // Walk a realm's on-disk directory and return every file's path relative diff --git a/packages/realm-server/handlers/serve-index.ts b/packages/realm-server/handlers/serve-index.ts index 719effaf05..a484baeb93 100644 --- a/packages/realm-server/handlers/serve-index.ts +++ b/packages/realm-server/handlers/serve-index.ts @@ -1,6 +1,6 @@ import type Koa from 'koa'; import { JSDOM } from 'jsdom'; -import merge from 'lodash/merge'; +import { merge } from 'lodash-es'; import type { DBAdapter, Realm } from '@cardstack/runtime-common'; import { hasExtension, diff --git a/packages/realm-server/lib/boxel-ui-change-checker.ts b/packages/realm-server/lib/boxel-ui-change-checker.ts index dbfeaf5a98..7479456445 100644 --- a/packages/realm-server/lib/boxel-ui-change-checker.ts +++ b/packages/realm-server/lib/boxel-ui-change-checker.ts @@ -53,3 +53,12 @@ export async function compareCurrentBoxelUIChecksum(distURL: URL) { export function writeCurrentBoxelUIChecksum(checksum: string) { fs.writeFileSync(fileChecksumPath, checksum); } + +// Grouped on a mutable object so tests can stub these under native ESM — +// named exports are read-only live bindings and can't be stubbed directly. +// Consumers (e.g. the post-deployment handler) call through `boxelUIChecker` +// so a `sinon.stub(boxelUIChecker, …)` takes effect. +export const boxelUIChecker = { + compareCurrentBoxelUIChecksum, + writeCurrentBoxelUIChecksum, +}; diff --git a/packages/realm-server/lib/dev-service-registry.ts b/packages/realm-server/lib/dev-service-registry.ts index 0abd4d5e14..f386a51164 100644 --- a/packages/realm-server/lib/dev-service-registry.ts +++ b/packages/realm-server/lib/dev-service-registry.ts @@ -31,7 +31,14 @@ function traefikDynamicDir(): string { } catch { // Traefik not running — fall back to repo-relative path } - _traefikDir = resolve(__dirname, '..', '..', '..', 'traefik', 'dynamic'); + _traefikDir = resolve( + import.meta.dirname, + '..', + '..', + '..', + 'traefik', + 'dynamic', + ); return _traefikDir; } diff --git a/packages/realm-server/lib/realm-registry-backfill.ts b/packages/realm-server/lib/realm-registry-backfill.ts index e553b5eeaa..84f8abc8a0 100644 --- a/packages/realm-server/lib/realm-registry-backfill.ts +++ b/packages/realm-server/lib/realm-registry-backfill.ts @@ -11,7 +11,7 @@ import { type DBAdapter, } from '@cardstack/runtime-common'; import type { PgAdapter } from '@cardstack/postgres'; -import { isEnvironmentMode } from './dev-service-registry'; +import { isEnvironmentMode } from './dev-service-registry.ts'; const log = logger('realm-server:registry-backfill'); diff --git a/packages/realm-server/lib/realm-routing.ts b/packages/realm-server/lib/realm-routing.ts index ec2aaf657d..a9848654b4 100644 --- a/packages/realm-server/lib/realm-routing.ts +++ b/packages/realm-server/lib/realm-routing.ts @@ -1,5 +1,6 @@ import { join } from 'path'; -import { existsSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync } = fsExtra; import type { DBAdapter, Realm } from '@cardstack/runtime-common'; import { executableExtensions, diff --git a/packages/realm-server/lib/wtfnode-on-signal.ts b/packages/realm-server/lib/wtfnode-on-signal.ts index 865f8a772e..a1b88bb111 100644 --- a/packages/realm-server/lib/wtfnode-on-signal.ts +++ b/packages/realm-server/lib/wtfnode-on-signal.ts @@ -5,8 +5,11 @@ // // Import this for side effects at the top of each node entry point. +import { createRequire } from 'module'; + if (process.env.BOXEL_WTFNODE === '1') { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // `require` doesn't exist in ESM scope; recreate it for this lazy, opt-in load. + const require = createRequire(import.meta.url); const wtfnode = require('wtfnode'); const tag = (process.argv[1] || 'node').split('/').pop() + `(pid=${process.pid})`; diff --git a/packages/realm-server/node-realm.ts b/packages/realm-server/node-realm.ts index 11de9151b9..a3884601e8 100644 --- a/packages/realm-server/node-realm.ts +++ b/packages/realm-server/node-realm.ts @@ -19,7 +19,8 @@ import type { ServerResponse } from 'http'; import sane, { type Watcher } from 'sane'; import type { ReadStream } from 'fs-extra'; -import { +import fsExtra from 'fs-extra'; +const { readdirSync, existsSync, writeFileSync, @@ -28,7 +29,7 @@ import { ensureFileSync, createReadStream, removeSync, -} from 'fs-extra'; +} = fsExtra; import { join } from 'path'; import { Duplex } from 'node:stream'; import type { diff --git a/packages/realm-server/package.json b/packages/realm-server/package.json index a5486df756..4ccc2d57a3 100644 --- a/packages/realm-server/package.json +++ b/packages/realm-server/package.json @@ -2,7 +2,7 @@ "name": "@cardstack/realm-server", "version": "0.0.0", "license": "MIT", - "type": "commonjs", + "type": "module", "devDependencies": { "@aws-crypto/sha256-js": "catalog:", "@babel/parser": "catalog:", @@ -29,7 +29,7 @@ "@types/koa-compose": "catalog:", "@types/koa__cors": "catalog:", "@types/koa__router": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/mime-types": "catalog:", "@types/ms": "catalog:", "@types/node": "catalog:", @@ -65,7 +65,7 @@ "koa": "catalog:", "koa-compose": "catalog:", "koa-proxies": "catalog:", - "lodash": "catalog:", + "lodash-es": "catalog:", "loglevel": "catalog:", "mime-types": "catalog:", "npm-run-all": "catalog:", @@ -84,7 +84,6 @@ "supertest": "catalog:", "testem": "catalog:", "tmp": "catalog:", - "ts-node": "^10.9.1", "typescript": "catalog:", "undici": "catalog:", "uuid": "catalog:", @@ -97,9 +96,9 @@ "scripts": { "test": "./tests/scripts/run-qunit-with-test-pg.sh", "test-module": "./tests/scripts/run-qunit-with-test-pg.sh --module ${TEST_MODULE}", - "bench:realm": "LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_REALM_LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_PRERENDER_LOG_LEVELS='*=warn,prerenderer-chrome=none' NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-realm/bench.ts", - "bench:realm:check": "LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_REALM_LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_PRERENDER_LOG_LEVELS='*=warn,prerenderer-chrome=none' NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-realm/check.ts", - "migrate": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/run-migrations.ts", + "bench:realm": "LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_REALM_LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_PRERENDER_LOG_LEVELS='*=warn,prerenderer-chrome=none' NODE_NO_WARNINGS=1 node scripts/bench-realm/bench.ts", + "bench:realm:check": "LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_REALM_LOG_LEVELS='*=warn,prerenderer-chrome=none' TEST_HARNESS_PRERENDER_LOG_LEVELS='*=warn,prerenderer-chrome=none' NODE_NO_WARNINGS=1 node scripts/bench-realm/check.ts", + "migrate": "NODE_NO_WARNINGS=1 node scripts/run-migrations.ts", "start:matrix": "./scripts/start-matrix.sh", "start:smtp": "cd ../matrix && pnpm assert-smtp-running", "start:icons": "mise run services:icons", @@ -114,7 +113,7 @@ "setup:software-factory-in-deployment": "mkdir -p /persistent/software-factory && rsync --dry-run --itemize-changes --checksum --recursive --delete ../software-factory/realm/. /persistent/software-factory/ && rsync --checksum --recursive --delete ../software-factory/realm/. /persistent/software-factory/", "setup:boxel-homepage-in-deployment": "mkdir -p /persistent/boxel-homepage", "setup:openrouter-in-deployment": "mkdir -p /persistent/openrouter /persistent/openrouter/OpenRouterModel && rsync --dry-run --itemize-changes --checksum --recursive --delete --exclude 'OpenRouterModel/' ../openrouter-realm/. /persistent/openrouter/ && rsync --checksum --recursive --delete --exclude 'OpenRouterModel/' ../openrouter-realm/. /persistent/openrouter/", - "start": "PGPORT=5435 NODE_NO_WARNINGS=1 ts-node --transpileOnly main", + "start": "PGPORT=5435 NODE_NO_WARNINGS=1 node main.ts", "start:base": "mise run services:realm-server-base -- --workerManagerPort=4213", "start:test-realms": "mise run services:test-realms -- --workerManagerPort=4211", "start:all": "mise run dev", @@ -140,11 +139,11 @@ "lint:types": "ember-tsc --noEmit", "full-reset": "./scripts/full-reset.sh", "full-reindex": "./scripts/full-reindex.sh", - "clear-modules-cache": "NODE_NO_WARNINGS=1 PGDATABASE=${PGDATABASE:-boxel} PGPORT=${PGPORT:-5435} ts-node --transpileOnly scripts/clear-modules-cache.ts", - "codemod:context-search": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/codemod/context-search/run.ts", + "clear-modules-cache": "NODE_NO_WARNINGS=1 PGDATABASE=${PGDATABASE:-boxel} PGPORT=${PGPORT:-5435} node scripts/clear-modules-cache.ts", + "codemod:context-search": "NODE_NO_WARNINGS=1 node scripts/codemod/context-search/run.ts", "check-user-pg-connections": "./scripts/check-user-pg-connections.sh", - "sync-stripe-products": "NODE_NO_WARNINGS=1 PGDATABASE=boxel PGPORT=5435 ts-node --transpileOnly scripts/sync-stripe-products.ts", - "sync-openrouter-models": "NODE_NO_WARNINGS=1 PGDATABASE=boxel PGPORT=5435 ts-node --transpileOnly scripts/sync-openrouter-models.ts", + "sync-stripe-products": "NODE_NO_WARNINGS=1 PGDATABASE=boxel PGPORT=5435 node scripts/sync-stripe-products.ts", + "sync-openrouter-models": "NODE_NO_WARNINGS=1 PGDATABASE=boxel PGPORT=5435 node scripts/sync-openrouter-models.ts", "stripe": "docker run --rm --add-host=host.docker.internal:host-gateway -it stripe/stripe-cli:latest" }, "dependencies": { diff --git a/packages/realm-server/routes.ts b/packages/realm-server/routes.ts index ab5567bb62..589e2d250b 100644 --- a/packages/realm-server/routes.ts +++ b/packages/realm-server/routes.ts @@ -408,7 +408,7 @@ function handleGitHubPRRequestLazy(args: CreateRoutesArgs) { return async function (ctxt: Koa.Context, next: Koa.Next) { if (!handler) { handler = ( - createRequire(__filename)( + createRequire(import.meta.filename)( './handlers/handle-github-pr', ) as typeof import('./handlers/handle-github-pr.ts') ).default(args); diff --git a/packages/realm-server/scripts/bench-realm/bench.ts b/packages/realm-server/scripts/bench-realm/bench.ts index abd9bdd86b..072b2086da 100644 --- a/packages/realm-server/scripts/bench-realm/bench.ts +++ b/packages/realm-server/scripts/bench-realm/bench.ts @@ -50,7 +50,7 @@ import { realmSnapshotDir } from './paths.ts'; // which is correct — a different host produced the cached rows. function hostDistFingerprint(): string { let indexPath = pathResolve( - __dirname, + import.meta.dirname, '..', '..', '..', @@ -82,7 +82,11 @@ export const DEFAULT_WARMUP = 5; // the bench's instance JSONs `adoptsFrom`. No runtime fileFilter — the // glob is materialized in the snapshot itself, so the bench mounts the // realm with the default copy-everything semantics. -const benchSourceRealmDir = pathResolve(__dirname, 'fixtures', 'source-realm'); +const benchSourceRealmDir = pathResolve( + import.meta.dirname, + 'fixtures', + 'source-realm', +); export interface Scenario { name: string; @@ -358,7 +362,7 @@ async function main(): Promise { } } -if (require.main === module) { +if (import.meta.main) { main().catch((err) => { console.error(err); process.exit(1); diff --git a/packages/realm-server/scripts/bench-realm/paths.ts b/packages/realm-server/scripts/bench-realm/paths.ts index 1d32a62663..712bb83bf8 100644 --- a/packages/realm-server/scripts/bench-realm/paths.ts +++ b/packages/realm-server/scripts/bench-realm/paths.ts @@ -1,6 +1,6 @@ import { resolve } from 'node:path'; -export const benchRoot = __dirname; +export const benchRoot = import.meta.dirname; export const fixturesDir = resolve(benchRoot, 'fixtures'); export const realmSnapshotDir = resolve(fixturesDir, 'realm-snapshot'); export const baselinePath = resolve(benchRoot, 'baseline.json'); diff --git a/packages/realm-server/scripts/codemod/context-search/run.ts b/packages/realm-server/scripts/codemod/context-search/run.ts index ac87e703ee..2cf51b4e9c 100644 --- a/packages/realm-server/scripts/codemod/context-search/run.ts +++ b/packages/realm-server/scripts/codemod/context-search/run.ts @@ -3,14 +3,14 @@ // usages it can transform mechanically, and reports the ones it can't so they // can be migrated by hand. // -// ts-node --transpileOnly scripts/codemod/context-search/run.ts [--write] +// node scripts/codemod/context-search/run.ts [--write] import { readFileSync, writeFileSync, statSync, readdirSync } from 'fs'; import { join, resolve } from 'path'; import * as prettier from 'prettier'; -import { transformContextSearch } from './transform'; +import { transformContextSearch } from './transform.ts'; // Format the migrated source through the repo's prettier config (with // prettier-plugin-ember-template-tag for `.gts`) so the structural edits land as @@ -54,7 +54,7 @@ async function main(): Promise { let paths = args.filter((a) => !a.startsWith('--')); if (paths.length === 0) { console.error( - 'usage: ts-node scripts/codemod/context-search/run.ts … [--write]', + 'usage: node scripts/codemod/context-search/run.ts … [--write]', ); process.exit(2); } diff --git a/packages/realm-server/scripts/junit-reporter.js b/packages/realm-server/scripts/junit-reporter.cjs similarity index 100% rename from packages/realm-server/scripts/junit-reporter.js rename to packages/realm-server/scripts/junit-reporter.cjs diff --git a/packages/realm-server/scripts/measure-test-file.sh b/packages/realm-server/scripts/measure-test-file.sh index bfaa1253a9..c4aba253ba 100755 --- a/packages/realm-server/scripts/measure-test-file.sh +++ b/packages/realm-server/scripts/measure-test-file.sh @@ -74,7 +74,7 @@ for i in $(seq 1 "$RUNS"); do STRIPE_API_KEY=stripe-api-key \ MATRIX_REGISTRATION_SHARED_SECRET="${MATRIX_REGISTRATION_SHARED_SECRET:-fake}" \ TEST_FILES="$TEST_FILES_ARG" \ - "$QUNIT" --require ts-node/register/transpile-only tests/index.ts \ + node tests/index.ts \ >/dev/null 2>"$timing" qunit_status=$? set -e diff --git a/packages/realm-server/scripts/run-test-modules.js b/packages/realm-server/scripts/run-test-modules.js deleted file mode 100644 index 9d3d224686..0000000000 --- a/packages/realm-server/scripts/run-test-modules.js +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env node -/* eslint-env node */ -'use strict'; - -const { spawnSync } = require('node:child_process'); // eslint-disable-line @typescript-eslint/no-var-requires - -function buildModuleFilter(modulesToMatch) { - const escaped = modulesToMatch - .map((moduleName) => escapeRegex(moduleName)) - .join('|'); - const pattern = `^(?:${escaped})(?:\\s>\\s|:)`; - return `/${pattern}/`; -} - -function escapeRegex(value) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\//g, '\\/'); -} - -const rawModules = process.env.TEST_MODULES ?? ''; -const cleanedRaw = rawModules.trim(); - -if (!cleanedRaw) { - console.error('TEST_MODULES must be set.'); - process.exit(1); -} - -const modules = cleanedRaw - .split(/[|,]/) - .map((value) => value.trim()) - .filter(Boolean) - .map((value) => value.replace(/^['"]+|['"]+$/g, '')); - -if (modules.length === 0) { - console.error('No module names found in TEST_MODULES.'); - process.exit(1); -} - -const args = ['--require', 'ts-node/register/transpile-only']; - -args.push('--filter', buildModuleFilter(modules)); - -args.push('tests/index.ts'); - -const qunitBin = require.resolve('qunit/bin/qunit.js'); -const result = spawnSync(process.execPath, [qunitBin, ...args], { - stdio: 'inherit', - env: process.env, -}); - -if (typeof result.status === 'number') { - process.exit(result.status); -} - -if (result.error) { - console.error(result.error); -} - -process.exit(1); diff --git a/packages/realm-server/scripts/shard-test-modules.js b/packages/realm-server/scripts/shard-test-modules.cjs similarity index 89% rename from packages/realm-server/scripts/shard-test-modules.js rename to packages/realm-server/scripts/shard-test-modules.cjs index 7eaeb1124c..8950f4c1cf 100644 --- a/packages/realm-server/scripts/shard-test-modules.js +++ b/packages/realm-server/scripts/shard-test-modules.cjs @@ -6,7 +6,7 @@ // to the requested shard (1-based). Files are sorted alphabetically and // distributed round-robin so every shard gets a roughly equal share. // -// Usage: node shard-test-modules.js +// Usage: node shard-test-modules.cjs // Output: module names joined by "|", suitable for TEST_MODULES. const fs = require('node:fs'); // eslint-disable-line @typescript-eslint/no-var-requires @@ -17,7 +17,7 @@ const totalShards = parseInt(process.argv[3], 10); if (!shard || !totalShards || shard < 1 || shard > totalShards) { console.error( - `Usage: shard-test-modules.js (got shard=${process.argv[2]}, totalShards=${process.argv[3]})`, + `Usage: shard-test-modules.cjs (got shard=${process.argv[2]}, totalShards=${process.argv[3]})`, ); process.exit(1); } diff --git a/packages/realm-server/scripts/start-prerender-manager.sh b/packages/realm-server/scripts/start-prerender-manager.sh index 962bc14e06..bf53e344cf 100755 --- a/packages/realm-server/scripts/start-prerender-manager.sh +++ b/packages/realm-server/scripts/start-prerender-manager.sh @@ -2,19 +2,18 @@ # Start the prerender manager in staging -# Run from the realm-server package directory so ts-node finds the package's -# tsconfig. Previously the CMD wrapped pnpm --filter, which set CWD for us; +# Run from the realm-server package directory so node resolves the package's +# node_modules. Previously the CMD wrapped pnpm --filter, which set CWD for us; # now PID 1 execs into this script directly so we set it ourselves. The PATH -# prepend gives us the local ts-node binary that `pnpm --filter` used to put +# prepend gives us the package's local node_modules/.bin that `pnpm --filter` used to put # on PATH automatically. cd "$(cd "$(dirname "$0")" && pwd)/.." PATH="./node_modules/.bin:$PATH" export PATH -echo "[start-prerender-manager] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-prerender-manager] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_ENV=production \ NODE_NO_WARNINGS=1 \ - exec ts-node \ - --transpileOnly prerender/manager-server \ + exec node prerender/manager-server.ts \ --port=${PRERENDER_MANAGER_PORT:-4222} diff --git a/packages/realm-server/scripts/start-prerender-production.sh b/packages/realm-server/scripts/start-prerender-production.sh index 48c0ffe200..8ed09013f5 100755 --- a/packages/realm-server/scripts/start-prerender-production.sh +++ b/packages/realm-server/scripts/start-prerender-production.sh @@ -3,20 +3,19 @@ # Start the prerender server in production # Expects REALM_SECRET_SEED to be set in the environment -# Run from the realm-server package directory so ts-node finds the package's -# tsconfig. Previously the CMD wrapped pnpm --filter, which set CWD for us; +# Run from the realm-server package directory so node resolves the package's +# node_modules. Previously the CMD wrapped pnpm --filter, which set CWD for us; # now PID 1 execs into this script directly so we set it ourselves. The PATH -# prepend gives us the local ts-node binary that `pnpm --filter` used to put +# prepend gives us the package's local node_modules/.bin that `pnpm --filter` used to put # on PATH automatically. cd "$(cd "$(dirname "$0")" && pwd)/.." PATH="./node_modules/.bin:$PATH" export PATH -echo "[start-prerender-production] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-prerender-production] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_ENV=production \ NODE_NO_WARNINGS=1 \ BOXEL_HOST_URL=https://app.boxel.ai \ - exec ts-node \ - --transpileOnly prerender/prerender-server \ + exec node prerender/prerender-server.ts \ --port=${PRERENDER_PORT:-4221} diff --git a/packages/realm-server/scripts/start-prerender-staging.sh b/packages/realm-server/scripts/start-prerender-staging.sh index c93d724a62..5f17f3ef4e 100755 --- a/packages/realm-server/scripts/start-prerender-staging.sh +++ b/packages/realm-server/scripts/start-prerender-staging.sh @@ -3,20 +3,19 @@ # Start the prerender server in staging # Expects REALM_SECRET_SEED to be set in the environment -# Run from the realm-server package directory so ts-node finds the package's -# tsconfig. Previously the CMD wrapped pnpm --filter, which set CWD for us; +# Run from the realm-server package directory so node resolves the package's +# node_modules. Previously the CMD wrapped pnpm --filter, which set CWD for us; # now PID 1 execs into this script directly so we set it ourselves. The PATH -# prepend gives us the local ts-node binary that `pnpm --filter` used to put +# prepend gives us the package's local node_modules/.bin that `pnpm --filter` used to put # on PATH automatically. cd "$(cd "$(dirname "$0")" && pwd)/.." PATH="./node_modules/.bin:$PATH" export PATH -echo "[start-prerender-staging] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-prerender-staging] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_ENV=production \ NODE_NO_WARNINGS=1 \ BOXEL_HOST_URL=https://realms-staging.stack.cards \ - exec ts-node \ - --transpileOnly prerender/prerender-server \ + exec node prerender/prerender-server.ts \ --port=${PRERENDER_PORT:-4221} diff --git a/packages/realm-server/scripts/start-production.sh b/packages/realm-server/scripts/start-production.sh index 4fb9296138..1d98efc1ae 100755 --- a/packages/realm-server/scripts/start-production.sh +++ b/packages/realm-server/scripts/start-production.sh @@ -2,10 +2,10 @@ SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" # Run from the realm-server package directory so `pnpm setup:*` resolves the # package's npm scripts (the relative `../base/` paths inside those scripts -# depend on this CWD) and ts-node can find the package's tsconfig. Previously +# depend on this CWD). Previously # the CMD wrapped pnpm --filter, which set CWD for us; now PID 1 execs into -# this script directly so we set it ourselves. The PATH prepend gives us the -# local ts-node binary that `pnpm --filter` used to put on PATH automatically. +# this script directly so we set it ourselves. The PATH prepend gives us the package's +# local node_modules/.bin that `pnpm --filter` used to put on PATH automatically. cd "$SCRIPTS_DIR/.." PATH="./node_modules/.bin:$PATH" export PATH @@ -28,7 +28,7 @@ SOFTWARE_FACTORY_REALM_URL="${RESOLVED_SOFTWARE_FACTORY_REALM_URL:-$DEFAULT_SOFT DEFAULT_BOXEL_HOMEPAGE_REALM_URL='https://app.boxel.ai/boxel-homepage/' BOXEL_HOMEPAGE_REALM_URL="${RESOLVED_BOXEL_HOMEPAGE_REALM_URL:-$DEFAULT_BOXEL_HOMEPAGE_REALM_URL}" -echo "[start-production] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-production] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_NO_WARNINGS=1 \ LOW_CREDIT_THRESHOLD=2000 \ @@ -38,8 +38,7 @@ NODE_NO_WARNINGS=1 \ REALM_SERVER_MATRIX_USERNAME=realm_server \ PUBLISHED_REALM_BOXEL_SPACE_DOMAIN='boxel.space' \ PUBLISHED_REALM_BOXEL_SITE_DOMAIN='boxel.site' \ - exec ts-node \ - --transpileOnly main \ + exec node main.ts \ --port=3000 \ --matrixURL='https://matrix.boxel.ai' \ --realmsRootPath='/persistent/realms' \ diff --git a/packages/realm-server/scripts/start-staging.sh b/packages/realm-server/scripts/start-staging.sh index e12eb23666..e4ae93808a 100755 --- a/packages/realm-server/scripts/start-staging.sh +++ b/packages/realm-server/scripts/start-staging.sh @@ -2,10 +2,10 @@ SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" # Run from the realm-server package directory so `pnpm setup:*` resolves the # package's npm scripts (the relative `../base/` paths inside those scripts -# depend on this CWD) and ts-node can find the package's tsconfig. Previously +# depend on this CWD). Previously # the CMD wrapped pnpm --filter, which set CWD for us; now PID 1 execs into -# this script directly so we set it ourselves. The PATH prepend gives us the -# local ts-node binary that `pnpm --filter` used to put on PATH automatically. +# this script directly so we set it ourselves. The PATH prepend gives us the package's +# local node_modules/.bin that `pnpm --filter` used to put on PATH automatically. cd "$SCRIPTS_DIR/.." PATH="./node_modules/.bin:$PATH" export PATH @@ -28,7 +28,7 @@ SOFTWARE_FACTORY_REALM_URL="${RESOLVED_SOFTWARE_FACTORY_REALM_URL:-$DEFAULT_SOFT DEFAULT_BOXEL_HOMEPAGE_REALM_URL='https://realms-staging.stack.cards/boxel-homepage/' BOXEL_HOMEPAGE_REALM_URL="${RESOLVED_BOXEL_HOMEPAGE_REALM_URL:-$DEFAULT_BOXEL_HOMEPAGE_REALM_URL}" -echo "[start-staging] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-staging] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_NO_WARNINGS=1 \ LOW_CREDIT_THRESHOLD=2000 \ @@ -38,8 +38,7 @@ NODE_NO_WARNINGS=1 \ REALM_SERVER_MATRIX_USERNAME=realm_server \ PUBLISHED_REALM_BOXEL_SPACE_DOMAIN='staging.boxel.dev' \ PUBLISHED_REALM_BOXEL_SITE_DOMAIN='staging.boxel.build' \ - exec ts-node \ - --transpileOnly main \ + exec node main.ts \ --port=3000 \ --matrixURL='https://matrix-staging.stack.cards' \ --realmsRootPath='/persistent/realms' \ diff --git a/packages/realm-server/scripts/start-worker-production.sh b/packages/realm-server/scripts/start-worker-production.sh index 0e1503f011..d5e6d006f4 100755 --- a/packages/realm-server/scripts/start-worker-production.sh +++ b/packages/realm-server/scripts/start-worker-production.sh @@ -1,9 +1,9 @@ #! /bin/sh -# Run from the realm-server package directory so ts-node finds the package's -# tsconfig. Previously the CMD wrapped pnpm --filter, which set CWD for us; +# Run from the realm-server package directory so node resolves the package's +# node_modules. Previously the CMD wrapped pnpm --filter, which set CWD for us; # now PID 1 execs into this script directly so we set it ourselves. The PATH -# prepend gives us the local ts-node binary that `pnpm --filter` used to put +# prepend gives us the package's local node_modules/.bin that `pnpm --filter` used to put # on PATH automatically. cd "$(cd "$(dirname "$0")" && pwd)/.." PATH="./node_modules/.bin:$PATH" @@ -14,15 +14,14 @@ CATALOG_REALM_URL="${RESOLVED_CATALOG_REALM_URL:-$DEFAULT_CATALOG_REALM_URL}" DEFAULT_SOFTWARE_FACTORY_REALM_URL='https://app.boxel.ai/software-factory/' SOFTWARE_FACTORY_REALM_URL="${RESOLVED_SOFTWARE_FACTORY_REALM_URL:-$DEFAULT_SOFTWARE_FACTORY_REALM_URL}" -echo "[start-worker-production] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-worker-production] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_NO_WARNINGS=1 \ NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=4096}" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ LOW_CREDIT_THRESHOLD=2000 \ OPENROUTER_REALM_URL='https://app.boxel.ai/openrouter/' \ - exec ts-node \ - --transpileOnly worker-manager \ + exec node worker-manager.ts \ --allPriorityCount="${WORKER_ALL_PRIORITY_COUNT:-1}" \ --highPriorityCount="${WORKER_HIGH_PRIORITY_COUNT:-0}" \ --prerendererUrl='http://boxel-prerender-manager.boxel-production-internal:4222' \ diff --git a/packages/realm-server/scripts/start-worker-staging.sh b/packages/realm-server/scripts/start-worker-staging.sh index 74754dc1f2..3861506d5d 100755 --- a/packages/realm-server/scripts/start-worker-staging.sh +++ b/packages/realm-server/scripts/start-worker-staging.sh @@ -1,9 +1,9 @@ #! /bin/sh -# Run from the realm-server package directory so ts-node finds the package's -# tsconfig. Previously the CMD wrapped pnpm --filter, which set CWD for us; +# Run from the realm-server package directory so node resolves the package's +# node_modules. Previously the CMD wrapped pnpm --filter, which set CWD for us; # now PID 1 execs into this script directly so we set it ourselves. The PATH -# prepend gives us the local ts-node binary that `pnpm --filter` used to put +# prepend gives us the package's local node_modules/.bin that `pnpm --filter` used to put # on PATH automatically. cd "$(cd "$(dirname "$0")" && pwd)/.." PATH="./node_modules/.bin:$PATH" @@ -14,15 +14,14 @@ CATALOG_REALM_URL="${RESOLVED_CATALOG_REALM_URL:-$DEFAULT_CATALOG_REALM_URL}" DEFAULT_SOFTWARE_FACTORY_REALM_URL='https://realms-staging.stack.cards/software-factory/' SOFTWARE_FACTORY_REALM_URL="${RESOLVED_SOFTWARE_FACTORY_REALM_URL:-$DEFAULT_SOFTWARE_FACTORY_REALM_URL}" -echo "[start-worker-staging] pid=$$ ppid=$PPID about to exec ts-node at $(date -Iseconds)" >&2 +echo "[start-worker-staging] pid=$$ ppid=$PPID about to exec node at $(date -Iseconds)" >&2 NODE_NO_WARNINGS=1 \ NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=4096}" \ REALM_SERVER_MATRIX_USERNAME=realm_server \ LOW_CREDIT_THRESHOLD=2000 \ OPENROUTER_REALM_URL='https://realms-staging.stack.cards/openrouter/' \ - exec ts-node \ - --transpileOnly worker-manager \ + exec node worker-manager.ts \ --allPriorityCount="${WORKER_ALL_PRIORITY_COUNT:-1}" \ --highPriorityCount="${WORKER_HIGH_PRIORITY_COUNT:-0}" \ --prerendererUrl='http://boxel-prerender-manager.boxel-staging-internal:4222' \ diff --git a/packages/realm-server/scripts/sync-openrouter-models.ts b/packages/realm-server/scripts/sync-openrouter-models.ts index 506b916a31..5acb7a6f3e 100644 --- a/packages/realm-server/scripts/sync-openrouter-models.ts +++ b/packages/realm-server/scripts/sync-openrouter-models.ts @@ -30,7 +30,7 @@ async function main() { } // When run directly as a script -if (require.main === module) { +if (import.meta.main) { main().catch((err) => { console.error(err); process.exit(1); diff --git a/packages/realm-server/server.ts b/packages/realm-server/server.ts index 8357fc9835..3d5b881487 100644 --- a/packages/realm-server/server.ts +++ b/packages/realm-server/server.ts @@ -14,7 +14,8 @@ import { DEFAULT_CARD_SIZE_LIMIT_BYTES, DEFAULT_FILE_SIZE_LIMIT_BYTES, } from '@cardstack/runtime-common'; -import { ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync } = fsExtra; import { httpLogging, ecsMetadata, diff --git a/packages/realm-server/setup-localhost-resolver.ts b/packages/realm-server/setup-localhost-resolver.ts index 8b061e9021..e95cd592cf 100644 --- a/packages/realm-server/setup-localhost-resolver.ts +++ b/packages/realm-server/setup-localhost-resolver.ts @@ -7,11 +7,14 @@ // NOTE: This runs before logger setup, so we check the env var directly instead // of importing isEnvironmentMode() (which would trigger a logger import). +import { createRequire } from 'module'; + if (process.env.BOXEL_ENVIRONMENT) { try { - // eslint-disable-next-line @typescript-eslint/no-var-requires + // `require` doesn't exist in ESM scope; recreate it so undici stays a lazy, + // optional load (the catch below tolerates it being absent). + const require = createRequire(import.meta.url); const undici = require('undici') as typeof import('undici'); - // eslint-disable-next-line @typescript-eslint/no-var-requires const dns = require('dns'); const agent = new undici.Agent({ diff --git a/packages/realm-server/synapse.ts b/packages/realm-server/synapse.ts index aef86026e4..23f1fd766b 100644 --- a/packages/realm-server/synapse.ts +++ b/packages/realm-server/synapse.ts @@ -1,5 +1,6 @@ /* eslint-env node */ -import { readFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { readFileSync } = fsExtra; import { resolve, join } from 'path'; import { createHmac } from 'crypto'; import yaml from 'yaml'; @@ -15,7 +16,7 @@ function homeserverFile(): string { .replace(/^-|-$/g, ''); let branchFile = resolve( join( - __dirname, + import.meta.dirname, '..', 'matrix', `synapse-data-${slug}`, @@ -27,7 +28,13 @@ function homeserverFile(): string { } } return resolve( - join(__dirname, '..', 'matrix', 'synapse-data', 'homeserver.yaml'), + join( + import.meta.dirname, + '..', + 'matrix', + 'synapse-data', + 'homeserver.yaml', + ), ); } diff --git a/packages/realm-server/tests/async-semaphore-test.ts b/packages/realm-server/tests/async-semaphore-test.ts index 3080dba364..82f622b662 100644 --- a/packages/realm-server/tests/async-semaphore-test.ts +++ b/packages/realm-server/tests/async-semaphore-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { AsyncSemaphore } from '../prerender/async-semaphore.ts'; import { isPrerenderCancellation } from '../prerender/prerender-cancel.ts'; @@ -20,7 +21,7 @@ import { isPrerenderCancellation } from '../prerender/prerender-cancel.ts'; // 4. Edge cases: clamping to 1, no-op resize, resize while empty, // cancelled waiters mixed with grow. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('AsyncSemaphore basic state', function () { test('reports correct counts at construction', function (assert) { let sem = new AsyncSemaphore(3); diff --git a/packages/realm-server/tests/atomic-endpoints-test.ts b/packages/realm-server/tests/atomic-endpoints-test.ts index 80c8a2fe79..39061184da 100644 --- a/packages/realm-server/tests/atomic-endpoints-test.ts +++ b/packages/realm-server/tests/atomic-endpoints-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; @@ -95,7 +96,7 @@ async function readIndexSnapshot( }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module( 'Realm-specific Endpoints: can make request to post /_atomic', function () { diff --git a/packages/realm-server/tests/auth-client-test.ts b/packages/realm-server/tests/auth-client-test.ts index 01d78e9de3..48b08b4037 100644 --- a/packages/realm-server/tests/auth-client-test.ts +++ b/packages/realm-server/tests/auth-client-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import '@cardstack/runtime-common/helpers/code-equality-assertion'; import { @@ -17,7 +18,7 @@ function createJWT( return jwt.sign(payload, 'secret', { expiresIn }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('realm-auth-client', function (assert) { let client: RealmAuthClient; let sessionHandler: (request: Request) => Promise; diff --git a/packages/realm-server/tests/bfm-card-references-test.ts b/packages/realm-server/tests/bfm-card-references-test.ts index 0557049ee5..f3e83b9967 100644 --- a/packages/realm-server/tests/bfm-card-references-test.ts +++ b/packages/realm-server/tests/bfm-card-references-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import bfmCardReferencesTests from '@cardstack/runtime-common/tests/bfm-card-references-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('cardTypeName', function () { test('cardTypeName extracts type from absolute URL', async function (assert) { await runSharedTest(bfmCardReferencesTests, assert, {}); diff --git a/packages/realm-server/tests/billing-test.ts b/packages/realm-server/tests/billing-test.ts index 2fa53d567e..b0958769ec 100644 --- a/packages/realm-server/tests/billing-test.ts +++ b/packages/realm-server/tests/billing-test.ts @@ -6,7 +6,8 @@ import type { User, } from '@cardstack/runtime-common'; import { logger, param, query } from '@cardstack/runtime-common'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { createTestPgAdapter, fetchSubscriptionsByUserId, @@ -101,7 +102,7 @@ function buildDailyCreditGrantTaskArgs(dbAdapter: PgAdapter): TaskArgs { }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('billing', function (hooks) { let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/boxel-domain-availability-test.ts b/packages/realm-server/tests/boxel-domain-availability-test.ts index 43313cf298..0f693bca12 100644 --- a/packages/realm-server/tests/boxel-domain-availability-test.ts +++ b/packages/realm-server/tests/boxel-domain-availability-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { User } from '@cardstack/runtime-common'; @@ -19,11 +20,12 @@ import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; const testRealmURL = new URL('http://127.0.0.1:0/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('boxel domain availability endpoint', function (hooks) { let testRealmServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/card-dependencies-endpoint-test.ts b/packages/realm-server/tests/card-dependencies-endpoint-test.ts index f5f3bf439e..f9af9cc00b 100644 --- a/packages/realm-server/tests/card-dependencies-endpoint-test.ts +++ b/packages/realm-server/tests/card-dependencies-endpoint-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; @@ -12,7 +13,7 @@ import { } from './helpers/index.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | card dependencies requests', function () { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/card-endpoints-test.ts b/packages/realm-server/tests/card-endpoints-test.ts index 066b4e41b1..a9b55ae8c8 100644 --- a/packages/realm-server/tests/card-endpoints-test.ts +++ b/packages/realm-server/tests/card-endpoints-test.ts @@ -1,10 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import supertest from 'supertest'; import { join, basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import type { DirResult } from 'tmp'; -import { existsSync, readJSONSync, statSync, writeFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync, readJSONSync, statSync, writeFileSync } = fsExtra; import type { Realm, Relationship, @@ -94,7 +96,7 @@ function buildPngChunk(type: string, data: Uint8Array): Uint8Array { return chunk; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | card URLs', function (hooks) { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealmHref = realmURL.href; diff --git a/packages/realm-server/tests/card-source-endpoints-test.ts b/packages/realm-server/tests/card-source-endpoints-test.ts index b5076d0a84..f2e1ea5ea2 100644 --- a/packages/realm-server/tests/card-source-endpoints-test.ts +++ b/packages/realm-server/tests/card-source-endpoints-test.ts @@ -1,9 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { join, basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import type { DirResult } from 'tmp'; -import { existsSync, readFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync, readFileSync } = fsExtra; import { cardSrc, compiledCard, @@ -30,9 +32,9 @@ import '@cardstack/runtime-common/helpers/code-equality-assertion'; import stripScopedCSSGlimmerAttributes from '@cardstack/runtime-common/helpers/strip-scoped-css-glimmer-attributes'; import { APP_BOXEL_REALM_EVENT_TYPE } from '@cardstack/runtime-common/matrix-constants'; import type { MatrixEvent } from 'https://cardstack.com/base/matrix-event'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | card source requests', function () { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealmHref = realmURL.href; diff --git a/packages/realm-server/tests/claim-boxel-domain-test.ts b/packages/realm-server/tests/claim-boxel-domain-test.ts index 039c21f8ba..a9b0cf1d15 100644 --- a/packages/realm-server/tests/claim-boxel-domain-test.ts +++ b/packages/realm-server/tests/claim-boxel-domain-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { User } from '@cardstack/runtime-common'; @@ -19,11 +20,12 @@ import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; const testRealmURL = new URL('http://127.0.0.1:0/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('claim boxel claimed domain endpoint', function (hooks) { let testRealmServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/clamp-serialized-error-test.ts b/packages/realm-server/tests/clamp-serialized-error-test.ts index 3e198696b2..541da77228 100644 --- a/packages/realm-server/tests/clamp-serialized-error-test.ts +++ b/packages/realm-server/tests/clamp-serialized-error-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { clampSerializedError, @@ -39,7 +40,7 @@ function isOmittedSentinel(entry: any): boolean { ); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('returns the input unchanged when the doc fits the budget', function (assert) { let input: SerializedError = { message: 'boom', diff --git a/packages/realm-server/tests/codemod-context-search-test.ts b/packages/realm-server/tests/codemod-context-search-test.ts index f770a1e63c..72fee09cf0 100644 --- a/packages/realm-server/tests/codemod-context-search-test.ts +++ b/packages/realm-server/tests/codemod-context-search-test.ts @@ -1,7 +1,8 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; -import { transformContextSearch } from '../scripts/codemod/context-search/transform'; +import { transformContextSearch } from '../scripts/codemod/context-search/transform.ts'; // A clean, mechanically-transformable usage: the `:response` body is a direct // `{{#each}}` over the result array that renders ``, the query @@ -184,7 +185,7 @@ export class Plain extends GlimmerComponent { } `; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('transforms a representative card: renames the component, reshapes the blocks, adds the query getter + import', function (assert) { let { status, output, reasons } = transformContextSearch(TRANSFORMABLE, { filename: 'grid.gts', diff --git a/packages/realm-server/tests/coerce-error-message-test.ts b/packages/realm-server/tests/coerce-error-message-test.ts index 3b7ca26957..aed2210f7f 100644 --- a/packages/realm-server/tests/coerce-error-message-test.ts +++ b/packages/realm-server/tests/coerce-error-message-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import coerceErrorMessageTests from '@cardstack/runtime-common/tests/coerce-error-message-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('coerceErrorMessage', function () { test('returns the existing non-empty message', async function (assert) { await runSharedTest(coerceErrorMessageTests, assert, {}); diff --git a/packages/realm-server/tests/command-parsing-utils-test.ts b/packages/realm-server/tests/command-parsing-utils-test.ts index 918028be21..c82545f4b8 100644 --- a/packages/realm-server/tests/command-parsing-utils-test.ts +++ b/packages/realm-server/tests/command-parsing-utils-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import commandParsingTests from '@cardstack/runtime-common/tests/command-parsing-utils-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('command parsing utils', function () { test('parseBoxelHostCommandSpecifier parses scoped command specifier', async function (assert) { await runSharedTest(commandParsingTests, assert, {}); diff --git a/packages/realm-server/tests/consuming-realm-header-test.ts b/packages/realm-server/tests/consuming-realm-header-test.ts index 250fd3bd8b..1b3f449727 100644 --- a/packages/realm-server/tests/consuming-realm-header-test.ts +++ b/packages/realm-server/tests/consuming-realm-header-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import consumingRealmHeaderTests from '@cardstack/runtime-common/tests/consuming-realm-header-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('sanitizeConsumingRealmHeader', function () { test('accepts a plain http realm URL', async function (assert) { await runSharedTest(consumingRealmHeaderTests, assert, {}); diff --git a/packages/realm-server/tests/cpu-profiler-affinity-gate-test.ts b/packages/realm-server/tests/cpu-profiler-affinity-gate-test.ts index ee37318369..54e44e28c5 100644 --- a/packages/realm-server/tests/cpu-profiler-affinity-gate-test.ts +++ b/packages/realm-server/tests/cpu-profiler-affinity-gate-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { shouldProfileAffinity, getAffinityProfileTargets, diff --git a/packages/realm-server/tests/data-plane-write-lock-test.ts b/packages/realm-server/tests/data-plane-write-lock-test.ts index 6c714ce8c0..719e85e56f 100644 --- a/packages/realm-server/tests/data-plane-write-lock-test.ts +++ b/packages/realm-server/tests/data-plane-write-lock-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; @@ -22,7 +23,7 @@ import '@cardstack/runtime-common/helpers/code-equality-assertion'; // Postgres advisory lock to serialize critical sections that pre-CS-11125 // raced. Without the lock applied to PATCH and /_atomic, the assertions // below detect the lost update / TOCTOU directly. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('CS-11125: data-plane write serialization', function () { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealm: Realm; diff --git a/packages/realm-server/tests/definition-lookup-test.ts b/packages/realm-server/tests/definition-lookup-test.ts index eae3f1f0d5..133e909a1f 100644 --- a/packages/realm-server/tests/definition-lookup-test.ts +++ b/packages/realm-server/tests/definition-lookup-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { CachingDefinitionLookup, @@ -93,7 +94,7 @@ function buildModuleResponse( }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('DefinitionLookup', function (hooks) { let definitionLookup: CachingDefinitionLookup; let realmURL = 'http://127.0.0.1:4450/'; diff --git a/packages/realm-server/tests/delete-boxel-claimed-domain-test.ts b/packages/realm-server/tests/delete-boxel-claimed-domain-test.ts index a870638ab8..caefef5149 100644 --- a/packages/realm-server/tests/delete-boxel-claimed-domain-test.ts +++ b/packages/realm-server/tests/delete-boxel-claimed-domain-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { User } from '@cardstack/runtime-common'; @@ -24,11 +25,12 @@ import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; const testRealmURL = new URL('http://127.0.0.1:0/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('delete boxel claimed domain endpoint', function (hooks) { let testRealmServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/eq-containment-integration-test.ts b/packages/realm-server/tests/eq-containment-integration-test.ts index 02b7767b61..d61238f21e 100644 --- a/packages/realm-server/tests/eq-containment-integration-test.ts +++ b/packages/realm-server/tests/eq-containment-integration-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; @@ -294,7 +295,7 @@ async function seedPolicy( ]); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('eq containment (Postgres integration)', function (hooks) { let dbAdapter: PgAdapter; let engine: IndexQueryEngine; diff --git a/packages/realm-server/tests/fallback-models-test.ts b/packages/realm-server/tests/fallback-models-test.ts index 7e2158dce7..1a90dd048d 100644 --- a/packages/realm-server/tests/fallback-models-test.ts +++ b/packages/realm-server/tests/fallback-models-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import fallbackModelsTests from '@cardstack/runtime-common/tests/fallback-models-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('DEFAULT_FALLBACK_MODELS', function () { test('ships at least one curated model', async function (assert) { await runSharedTest(fallbackModelsTests, assert, {}); diff --git a/packages/realm-server/tests/file-watcher-events-test.ts b/packages/realm-server/tests/file-watcher-events-test.ts index 59dc4687ab..2bf249ffe6 100644 --- a/packages/realm-server/tests/file-watcher-events-test.ts +++ b/packages/realm-server/tests/file-watcher-events-test.ts @@ -1,9 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { join, basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import type { DirResult } from 'tmp'; -import { removeSync, writeJSONSync, writeFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { removeSync, writeJSONSync, writeFileSync } = fsExtra; import type { Realm } from '@cardstack/runtime-common'; import { rri } from '@cardstack/runtime-common'; import { @@ -20,7 +22,7 @@ import type { UpdateRealmEventContent, } from 'https://cardstack.com/base/matrix-event'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('file watcher realm events', function (hooks) { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealm: Realm; diff --git a/packages/realm-server/tests/finalize-child-fatal-failure-test.ts b/packages/realm-server/tests/finalize-child-fatal-failure-test.ts index ee1d892916..82865e0171 100644 --- a/packages/realm-server/tests/finalize-child-fatal-failure-test.ts +++ b/packages/realm-server/tests/finalize-child-fatal-failure-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { @@ -47,7 +48,7 @@ async function fetchReservation( return rows[0]; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('finalizeChildReservationAsFailure', function (hooks) { let adapter: PgAdapter; diff --git a/packages/realm-server/tests/finalize-orphan-reservations-test.ts b/packages/realm-server/tests/finalize-orphan-reservations-test.ts index 7b70a0c937..ced0a797a2 100644 --- a/packages/realm-server/tests/finalize-orphan-reservations-test.ts +++ b/packages/realm-server/tests/finalize-orphan-reservations-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { @@ -57,7 +58,7 @@ async function fetchReservation( return rows[0]; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('finalizeOrphanedReservations', function (hooks) { let adapter: PgAdapter; diff --git a/packages/realm-server/tests/full-index-on-startup-test.ts b/packages/realm-server/tests/full-index-on-startup-test.ts index 70c958a26d..02ab6c943f 100644 --- a/packages/realm-server/tests/full-index-on-startup-test.ts +++ b/packages/realm-server/tests/full-index-on-startup-test.ts @@ -1,8 +1,9 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { resolveFullIndexOnStartup } from '../lib/full-index-on-startup.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('default behavior (env var unset)', function () { test('bootstrap realms full-index on startup', function (assert) { assert.true(resolveFullIndexOnStartup('bootstrap', undefined)); diff --git a/packages/realm-server/tests/full-reindex-test.ts b/packages/realm-server/tests/full-reindex-test.ts index 09b1d7b62f..7fb7db6cb1 100644 --- a/packages/realm-server/tests/full-reindex-test.ts +++ b/packages/realm-server/tests/full-reindex-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { @@ -18,7 +19,7 @@ import { import { upsertPublishedRealmInRegistry } from '../lib/realm-registry-writes.ts'; import { setupDB } from './helpers/index.ts'; -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let dbAdapter: PgAdapter; let queuePublisher: QueuePublisher; diff --git a/packages/realm-server/tests/get-boxel-claimed-domain-test.ts b/packages/realm-server/tests/get-boxel-claimed-domain-test.ts index 7b9128767c..8cb9a3575c 100644 --- a/packages/realm-server/tests/get-boxel-claimed-domain-test.ts +++ b/packages/realm-server/tests/get-boxel-claimed-domain-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { User } from '@cardstack/runtime-common'; @@ -19,11 +20,12 @@ import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; const testRealmURL = new URL('http://127.0.0.1:0/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('get boxel claimed domain endpoint', function (hooks) { let testRealmServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/helpers/index.ts b/packages/realm-server/tests/helpers/index.ts index 27bdfca4d2..e931a80bf3 100644 --- a/packages/realm-server/tests/helpers/index.ts +++ b/packages/realm-server/tests/helpers/index.ts @@ -1,11 +1,12 @@ -import { +import fsExtra from 'fs-extra'; +const { writeFileSync, writeJSONSync, readdirSync, statSync, ensureDirSync, copySync, -} from 'fs-extra'; +} = fsExtra; import { NodeAdapter } from '../../node-realm.ts'; import { dirname, join } from 'path'; import { createHash } from 'crypto'; @@ -46,7 +47,8 @@ import { resetCatalogRealms } from '../../handlers/handle-fetch-catalog-realms.t import { dirSync, setGracefulCleanup, type DirResult } from 'tmp'; import { getLocalConfig as getSynapseConfig } from '../../synapse.ts'; import { RealmServer } from '../../server.ts'; -import { sign as jwtSign } from 'jsonwebtoken'; +import jsonwebtoken from 'jsonwebtoken'; +const { sign: jwtSign } = jsonwebtoken; import { RealmRegistryReconciler, type RealmRegistryRow, @@ -1935,7 +1937,7 @@ function realmEventIsIndex( export type RealmFixtureName = 'blank' | 'simple' | 'realistic'; export function fixtureDir(name: RealmFixtureName): string { - return join(__dirname, '..', 'fixtures', name); + return join(import.meta.dirname, '..', 'fixtures', name); } type InternalPermissionedRealmSetupOptions = { diff --git a/packages/realm-server/tests/host-routing-validation-test.ts b/packages/realm-server/tests/host-routing-validation-test.ts index 7564beaba3..941e3290a3 100644 --- a/packages/realm-server/tests/host-routing-validation-test.ts +++ b/packages/realm-server/tests/host-routing-validation-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import hostRoutingValidationTests from '@cardstack/runtime-common/tests/host-routing-validation-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('validateRoutingPath', function () { test('validateRoutingPath: no warning for empty or whitespace paths', async function (assert) { await runSharedTest(hostRoutingValidationTests, assert, {}); diff --git a/packages/realm-server/tests/http2-keepalive-test.ts b/packages/realm-server/tests/http2-keepalive-test.ts index 96833f4062..e486c97c0e 100644 --- a/packages/realm-server/tests/http2-keepalive-test.ts +++ b/packages/realm-server/tests/http2-keepalive-test.ts @@ -1,10 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { EventEmitter } from 'events'; import type http2 from 'http2'; import { logger } from '@cardstack/runtime-common'; -import { startSessionKeepalive } from '../server'; +import { startSessionKeepalive } from '../server.ts'; // Unit coverage for the HTTP/2 PING keepalive that tears down wedged sessions // so a hung browser fetch rejects (and retries) instead of hanging until a @@ -103,7 +104,7 @@ function wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('a responsive session is pinged and never torn down', async function (assert) { let fake = new FakeSession(); let stopKeepalive = startSessionKeepalive(asSession(fake), log, FAST); diff --git a/packages/realm-server/tests/index.ts b/packages/realm-server/tests/index.ts index 40d7c9366d..5b8ac4a64a 100644 --- a/packages/realm-server/tests/index.ts +++ b/packages/realm-server/tests/index.ts @@ -36,6 +36,21 @@ delete process.env.REALM_SERVER_TLS_KEY_FILE; } import QUnit from 'qunit'; +import { createRequire } from 'module'; + +// `require` doesn't exist in ESM scope; recreate it so the synchronous, +// order-preserving test-file loader and the lazy cleanup requires below keep +// working under native node. +const require = createRequire(import.meta.url); + +// The qunit CLI used to provide the TAP reporter, autostart, and a +// failure-based exit code. Running under `node tests/index.ts` we wire them up +// here; autostart is disabled so every test file registers before we start. +QUnit.config.autostart = false; +(QUnit as any).reporters.tap.init(QUnit); // QUnit 2.x API missing from @types/qunit +(QUnit as any).on('runEnd', (data: { testCounts: { failed: number } }) => { + process.exitCode = data.testCounts.failed > 0 ? 1 : 0; +}); QUnit.config.testTimeout = 60000; const testModules = process.env.TEST_MODULES?.trim(); @@ -59,8 +74,7 @@ if (testModules) { // hardcoded test ports (4444-4471, etc.) bound after a test is aborted by // Ctrl+C or an abnormal exit (but not SIGKILL, which bypasses handlers). async function runTrackedCleanup(): Promise { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const helpers = require('./helpers') as { + const helpers = require('./helpers/index.ts') as { closeTrackedServers?: () => Promise; stopTrackedPrerenderers?: () => Promise; destroyTrackedQueueRunners?: () => Promise; @@ -87,8 +101,7 @@ for (let signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) { } QUnit.done(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const helpers = require('./helpers') as { + const helpers = require('./helpers/index.ts') as { closeTrackedServers?: () => Promise; stopTrackedPrerenderers?: () => Promise; destroyTrackedQueueRunners?: () => Promise; @@ -103,7 +116,6 @@ QUnit.done(() => { await helpers.destroyTrackedQueuePublishers?.(); await helpers.closeTrackedDbAdapters?.(); try { - // eslint-disable-next-line @typescript-eslint/no-var-requires const undici = require('undici') as { getGlobalDispatcher?: () => { close?: () => Promise }; }; @@ -330,9 +342,12 @@ if (testFilesEnv) { } for (const file of filesToLoad) { - require(file); + // Explicit `.ts` — native `require` does no extension search for TypeScript. + require(`${file}.ts`); } +QUnit.start(); + function parseTestFiles(value: string): string[] { return value .split(',') diff --git a/packages/realm-server/tests/indexing-event-sink-test.ts b/packages/realm-server/tests/indexing-event-sink-test.ts index 3727b007df..07cf39d43b 100644 --- a/packages/realm-server/tests/indexing-event-sink-test.ts +++ b/packages/realm-server/tests/indexing-event-sink-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { DBAdapter, PgPrimitive } from '@cardstack/runtime-common'; import { IndexingEventSink } from '../indexing-event-sink.ts'; @@ -44,7 +45,7 @@ function sleep(ms: number): Promise { return new Promise((r) => setTimeout(r, ms)); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('tracks active indexing from start through file visits to finish', function (assert) { let sink = new IndexingEventSink(); diff --git a/packages/realm-server/tests/indexing-test.ts b/packages/realm-server/tests/indexing-test.ts index fee2300802..696a9895f5 100644 --- a/packages/realm-server/tests/indexing-test.ts +++ b/packages/realm-server/tests/indexing-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { internalKeyFor, rri, @@ -459,7 +460,7 @@ function makeTestRealmFileSystem(): Record< }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('indexing (read only)', function (hooks) { let realm: Realm; diff --git a/packages/realm-server/tests/is-json-content-type-test.ts b/packages/realm-server/tests/is-json-content-type-test.ts index b6d696325c..77ec242d95 100644 --- a/packages/realm-server/tests/is-json-content-type-test.ts +++ b/packages/realm-server/tests/is-json-content-type-test.ts @@ -1,8 +1,9 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { isJsonContentType } from '@cardstack/runtime-common'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('accepts application/json and JSON-suffix media types', function (assert) { assert.true(isJsonContentType('application/json')); assert.true(isJsonContentType('text/json')); diff --git a/packages/realm-server/tests/job-scoped-search-cache-test.ts b/packages/realm-server/tests/job-scoped-search-cache-test.ts index 693350c950..08607cd1be 100644 --- a/packages/realm-server/tests/job-scoped-search-cache-test.ts +++ b/packages/realm-server/tests/job-scoped-search-cache-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { query, type Query, type Expression } from '@cardstack/runtime-common'; @@ -30,7 +31,7 @@ function makeDoc(label: string): string { ); } -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let dbAdapter: PgAdapter; setupDB(hooks, { diff --git a/packages/realm-server/tests/jobs-finished-listener-test.ts b/packages/realm-server/tests/jobs-finished-listener-test.ts index dc0a6f2778..72992eac86 100644 --- a/packages/realm-server/tests/jobs-finished-listener-test.ts +++ b/packages/realm-server/tests/jobs-finished-listener-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { query, param } from '@cardstack/runtime-common'; @@ -50,7 +51,7 @@ function waitFor( }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('JobsFinishedListener (sweep dispatch)', function () { test('clears every cached key whose job has finalized', async function (assert) { let cache = new FakeSearchCache(['5.1', '9.1']); diff --git a/packages/realm-server/tests/lazy-mount-test.ts b/packages/realm-server/tests/lazy-mount-test.ts index c160dfc149..cb52855a21 100644 --- a/packages/realm-server/tests/lazy-mount-test.ts +++ b/packages/realm-server/tests/lazy-mount-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { dirSync } from 'tmp'; import type { PgAdapter } from '@cardstack/postgres'; @@ -91,7 +92,7 @@ function buildServer(opts: { }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Phase 3 lazy mount', function (hooks) { let dbAdapter: PgAdapter; let mountCalls: string[]; diff --git a/packages/realm-server/tests/listener-dispatcher-test.ts b/packages/realm-server/tests/listener-dispatcher-test.ts index 2f0a70472e..70d9042b9c 100644 --- a/packages/realm-server/tests/listener-dispatcher-test.ts +++ b/packages/realm-server/tests/listener-dispatcher-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { execFileSync } from 'child_process'; import { mkdtempSync, rmSync, writeFileSync } from 'fs'; @@ -258,7 +259,7 @@ function withEnv( } } -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { hooks.before(function () { tmpCertDir = mkdtempSync(join(tmpdir(), 'realm-listener-test-')); let pair = makeCert(tmpCertDir); diff --git a/packages/realm-server/tests/load-links-batching-test.ts b/packages/realm-server/tests/load-links-batching-test.ts index f7b5e8d7f2..688d6b0e87 100644 --- a/packages/realm-server/tests/load-links-batching-test.ts +++ b/packages/realm-server/tests/load-links-batching-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { rri } from '@cardstack/runtime-common'; import type { @@ -82,7 +83,7 @@ function buildFileSystem(): Record { // cards each linking to 5 targets, the original implementation would have // fired 250 sequential `WHERE i.url = $1` lookups. The new BFS path issues // one batched `WHERE i.url IN (...)` per recursion depth. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('loadLinks batching', function (hooks) { let realm: Realm; diff --git a/packages/realm-server/tests/markdown-fallback-server-isolation-test.ts b/packages/realm-server/tests/markdown-fallback-server-isolation-test.ts index e3e0cb21fd..3419625e15 100644 --- a/packages/realm-server/tests/markdown-fallback-server-isolation-test.ts +++ b/packages/realm-server/tests/markdown-fallback-server-isolation-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import * as fs from 'fs'; import * as path from 'path'; @@ -16,7 +17,7 @@ import * as path from 'path'; // If a future change accidentally pulls turndown into the server graph, this // test fails — even before any networked behavior is exercised. -const REPO_ROOT = path.resolve(__dirname, '..', '..', '..'); +const REPO_ROOT = path.resolve(import.meta.dirname, '..', '..', '..'); // Packages whose source code runs in the realm-server Node.js process. If a // new server-side workspace is added that runs in Node, list it here too. @@ -86,7 +87,7 @@ function findForbiddenImports(file: string): string | null { return null; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('realm-server package.json does not declare turndown deps', function (assert) { let pkg = JSON.parse( fs.readFileSync( diff --git a/packages/realm-server/tests/matches-filter-integration-test.ts b/packages/realm-server/tests/matches-filter-integration-test.ts index da17ef685a..6bdc7016c0 100644 --- a/packages/realm-server/tests/matches-filter-integration-test.ts +++ b/packages/realm-server/tests/matches-filter-integration-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; @@ -86,7 +87,7 @@ async function countBoxelIndexRows(dbAdapter: PgAdapter): Promise { return rows[0].total; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('MatchesFilter (Postgres integration)', function (hooks) { let dbAdapter: PgAdapter; let engine: IndexQueryEngine; diff --git a/packages/realm-server/tests/module-cache-coordination-test.ts b/packages/realm-server/tests/module-cache-coordination-test.ts index d6783536aa..347c3bf779 100644 --- a/packages/realm-server/tests/module-cache-coordination-test.ts +++ b/packages/realm-server/tests/module-cache-coordination-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { @@ -152,7 +153,7 @@ function makeLookup( return lookup; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('ModuleCacheCoordinator unit', function (hooks) { let dbAdapter: PgAdapter; setupDB(hooks, { diff --git a/packages/realm-server/tests/module-cache-invalidation-listener-test.ts b/packages/realm-server/tests/module-cache-invalidation-listener-test.ts index 64bd536fc0..e09b2dfdca 100644 --- a/packages/realm-server/tests/module-cache-invalidation-listener-test.ts +++ b/packages/realm-server/tests/module-cache-invalidation-listener-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { @@ -118,7 +119,7 @@ function waitFor( }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('parseModuleCacheInvalidationPayload', function () { test('parses a module payload carrying a single URL', function (assert) { assert.deepEqual( diff --git a/packages/realm-server/tests/module-cache-race-test.ts b/packages/realm-server/tests/module-cache-race-test.ts index 2f07a17105..2bb72cbd81 100644 --- a/packages/realm-server/tests/module-cache-race-test.ts +++ b/packages/realm-server/tests/module-cache-race-test.ts @@ -1,6 +1,8 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; -import { ensureDirSync, writeFileSync, writeJSONSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync, writeFileSync, writeJSONSync } = fsExtra; import sinon from 'sinon'; import { dirSync } from 'tmp'; import type { SuperTest, Test } from 'supertest'; @@ -49,7 +51,7 @@ import { // transforms + scoped-css), so the invalidate lands inside the race // window. The observable assertion is on the subsequent request's // `x-boxel-cache` header — a miss proves A's cache write was discarded. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module( 'Realm.#transpiledModuleCache invalidate-during-transpile race', function (hooks) { diff --git a/packages/realm-server/tests/module-syntax-test.ts b/packages/realm-server/tests/module-syntax-test.ts index 3bed967552..cdca609f09 100644 --- a/packages/realm-server/tests/module-syntax-test.ts +++ b/packages/realm-server/tests/module-syntax-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { ModuleSyntax, gjsToPlaceholderJS, @@ -22,7 +23,7 @@ const virtualNetwork = new VirtualNetwork(); import { basename } from 'path'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('module-syntax', function () { function addField(src: string, addFieldAtIndex?: number) { let mod = new ModuleSyntax( diff --git a/packages/realm-server/tests/network-inflight-tracker-test.ts b/packages/realm-server/tests/network-inflight-tracker-test.ts index f5a17db8fc..b8aa70116a 100644 --- a/packages/realm-server/tests/network-inflight-tracker-test.ts +++ b/packages/realm-server/tests/network-inflight-tracker-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { NetworkInflightTracker } from '../prerender/network-inflight-tracker.ts'; // The tracker is the one render-hang signal that survives a wedged page diff --git a/packages/realm-server/tests/node-realm-test.ts b/packages/realm-server/tests/node-realm-test.ts index 98cbb6e41f..252bf5c0b4 100644 --- a/packages/realm-server/tests/node-realm-test.ts +++ b/packages/realm-server/tests/node-realm-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { @@ -11,7 +12,7 @@ import type { RealmEventContent } from 'https://cardstack.com/base/matrix-event' import { NodeAdapter } from '../node-realm.ts'; import { insertUser, setupDB } from './helpers/index.ts'; -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let dbAdapter: PgAdapter; const realmURL = new URL('http://127.0.0.1:4444/test/'); const staleRoomId = '!room-alice:localhost'; diff --git a/packages/realm-server/tests/normalize-realm-meta-value-test.ts b/packages/realm-server/tests/normalize-realm-meta-value-test.ts index d68baa8d7e..ff2a1d3972 100644 --- a/packages/realm-server/tests/normalize-realm-meta-value-test.ts +++ b/packages/realm-server/tests/normalize-realm-meta-value-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import normalizeRealmMetaValueTests from '@cardstack/runtime-common/tests/normalize-realm-meta-value-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('normalizeRealmMetaValue', function () { test('undefined value normalizes to empty groups', async function (assert) { await runSharedTest(normalizeRealmMetaValueTests, assert, {}); diff --git a/packages/realm-server/tests/openrouter-passthrough-test.ts b/packages/realm-server/tests/openrouter-passthrough-test.ts index e63de0a51e..5142e8fce1 100644 --- a/packages/realm-server/tests/openrouter-passthrough-test.ts +++ b/packages/realm-server/tests/openrouter-passthrough-test.ts @@ -1,11 +1,13 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import sinon from 'sinon'; import type { Test, SuperTest } from 'supertest'; import supertest from 'supertest'; import { basename, join } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; import { setupDB, runTestRealmServer, @@ -25,7 +27,7 @@ import { } from '@cardstack/billing/billing-queries'; import { AllowedProxyDestinations } from '../lib/allowed-proxy-destinations.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module( 'Realm-specific Endpoints | _openrouter/chat/completions', function (hooks) { diff --git a/packages/realm-server/tests/package-shim-handler-test.ts b/packages/realm-server/tests/package-shim-handler-test.ts index 03c145f3e4..23a8186b49 100644 --- a/packages/realm-server/tests/package-shim-handler-test.ts +++ b/packages/realm-server/tests/package-shim-handler-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import packageShimHandlerTests from '@cardstack/runtime-common/tests/package-shim-handler-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Strict named-export check (CS-10860 follow-up)', function () { test('wrapWithStrictNamespace returns existing exports unchanged', async function (assert) { await runSharedTest(packageShimHandlerTests, assert, {}); diff --git a/packages/realm-server/tests/page-pool-cert-verifier-retry-test.ts b/packages/realm-server/tests/page-pool-cert-verifier-retry-test.ts index 8c84510f3c..9137a27672 100644 --- a/packages/realm-server/tests/page-pool-cert-verifier-retry-test.ts +++ b/packages/realm-server/tests/page-pool-cert-verifier-retry-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PagePool } from '../prerender/page-pool.ts'; @@ -117,7 +118,7 @@ function makeBrowserStub(opts: BrowserStubOptions) { return browserManager; } -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let pools: PagePool[] = []; hooks.afterEach(async () => { diff --git a/packages/realm-server/tests/page-pool-eviction-recovery-test.ts b/packages/realm-server/tests/page-pool-eviction-recovery-test.ts index bf103c0230..20422372fe 100644 --- a/packages/realm-server/tests/page-pool-eviction-recovery-test.ts +++ b/packages/realm-server/tests/page-pool-eviction-recovery-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PagePool } from '../prerender/page-pool.ts'; @@ -80,7 +81,7 @@ function makeBrowserStub(control: CloseControl) { }; } -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let pools: PagePool[] = []; hooks.afterEach(async () => { diff --git a/packages/realm-server/tests/page-pool-expansion-test.ts b/packages/realm-server/tests/page-pool-expansion-test.ts index 159ddce2b5..86e9174db8 100644 --- a/packages/realm-server/tests/page-pool-expansion-test.ts +++ b/packages/realm-server/tests/page-pool-expansion-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PagePool } from '../prerender/page-pool.ts'; import { AsyncSemaphore } from '../prerender/async-semaphore.ts'; @@ -105,7 +106,7 @@ async function withEnv( } } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('PagePool dynamic-pool configuration', function (hooks) { let teardown: Array<() => Promise> = []; diff --git a/packages/realm-server/tests/page-pool-priority-test.ts b/packages/realm-server/tests/page-pool-priority-test.ts index 0921dedc0c..3ba87038a3 100644 --- a/packages/realm-server/tests/page-pool-priority-test.ts +++ b/packages/realm-server/tests/page-pool-priority-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { AsyncSemaphore } from '../prerender/async-semaphore.ts'; import { TabQueue } from '../prerender/page-pool.ts'; @@ -16,7 +17,7 @@ import { TabQueue } from '../prerender/page-pool.ts'; // arriving while a system-priority full reindex has saturated the // queue should NOT wait behind every queued background entry. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('AsyncSemaphore priority dequeue', function () { test('higher priority jumps the queue ahead of lower-priority pending work', async function (assert) { let sem = new AsyncSemaphore(1); diff --git a/packages/realm-server/tests/page-pool-standby-refill-test.ts b/packages/realm-server/tests/page-pool-standby-refill-test.ts index 2a6bbf066e..42da12180f 100644 --- a/packages/realm-server/tests/page-pool-standby-refill-test.ts +++ b/packages/realm-server/tests/page-pool-standby-refill-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PagePool } from '../prerender/page-pool.ts'; @@ -90,7 +91,7 @@ function makeManualGate() { }; } -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { let pools: PagePool[] = []; hooks.afterEach(async () => { diff --git a/packages/realm-server/tests/permissions/permission-checker-test.ts b/packages/realm-server/tests/permissions/permission-checker-test.ts index 5b2c6cba20..89dc2de61d 100644 --- a/packages/realm-server/tests/permissions/permission-checker-test.ts +++ b/packages/realm-server/tests/permissions/permission-checker-test.ts @@ -1,7 +1,8 @@ import type { MatrixClient } from '@cardstack/runtime-common/matrix-client'; import RealmPermissionChecker from '@cardstack/runtime-common/realm-permission-checker'; import { basename } from 'path'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; let matrixUserProfile: { displayname: string } | undefined = undefined; let mockMatrixClient = { @@ -10,7 +11,7 @@ let mockMatrixClient = { }, } as MatrixClient; -module(`permissions/${basename(__filename)}`, function () { +module(`permissions/${basename(import.meta.filename)}`, function () { module('world-readable realm', function () { let permissionsChecker = new RealmPermissionChecker( { diff --git a/packages/realm-server/tests/pg-adapter-subscribe-test.ts b/packages/realm-server/tests/pg-adapter-subscribe-test.ts index 555ea858f8..f75773dd49 100644 --- a/packages/realm-server/tests/pg-adapter-subscribe-test.ts +++ b/packages/realm-server/tests/pg-adapter-subscribe-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { query, param } from '@cardstack/runtime-common'; @@ -48,7 +49,7 @@ async function notify( ]); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('PgAdapter.subscribe', function (hooks) { let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/prerender-affinity-activity-test.ts b/packages/realm-server/tests/prerender-affinity-activity-test.ts index 275c643e49..9eabe2c803 100644 --- a/packages/realm-server/tests/prerender-affinity-activity-test.ts +++ b/packages/realm-server/tests/prerender-affinity-activity-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { AffinityActivityTracker } from '../prerender/affinity-activity.ts'; @@ -11,7 +12,7 @@ import { AffinityActivityTracker } from '../prerender/affinity-activity.ts'; // `computeBatchClearCacheGate` for CS-10758 — we test the extracted // helper directly rather than spinning up a full Prerenderer. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { const REALM_A = 'realm:http://localhost:4201/user/alpha/'; const REALM_B = 'realm:http://localhost:4201/user/beta/'; diff --git a/packages/realm-server/tests/prerender-artifact-sink-test.ts b/packages/realm-server/tests/prerender-artifact-sink-test.ts index 587a83b5ee..6f5676be11 100644 --- a/packages/realm-server/tests/prerender-artifact-sink-test.ts +++ b/packages/realm-server/tests/prerender-artifact-sink-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { buildArtifactKey, getMaxSessionBytes, diff --git a/packages/realm-server/tests/prerender-batch-ownership-test.ts b/packages/realm-server/tests/prerender-batch-ownership-test.ts index 35a4bcf224..d701a018d4 100644 --- a/packages/realm-server/tests/prerender-batch-ownership-test.ts +++ b/packages/realm-server/tests/prerender-batch-ownership-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { computeBatchClearCacheGate, @@ -29,7 +30,7 @@ function args( }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module( 'computeBatchClearCacheGate — clearCache batch ownership (CS-10758 step 3)', function () { diff --git a/packages/realm-server/tests/prerender-cancellation-test.ts b/packages/realm-server/tests/prerender-cancellation-test.ts index a6e5bf7dea..8ca6e3ff57 100644 --- a/packages/realm-server/tests/prerender-cancellation-test.ts +++ b/packages/realm-server/tests/prerender-cancellation-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PrerenderCancelledError, @@ -24,7 +25,7 @@ import { AsyncSemaphore } from '../prerender/async-semaphore.ts'; // Holding these invariants down means the manager-level client-abort // tests don't have to re-verify them end to end. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('PrerenderCancelledError shape', function () { test('defaults to queued state with no reason', function (assert) { let err = new PrerenderCancelledError(); diff --git a/packages/realm-server/tests/prerender-deadlock-test.ts b/packages/realm-server/tests/prerender-deadlock-test.ts index ad73ea0956..2cdffc7f84 100644 --- a/packages/realm-server/tests/prerender-deadlock-test.ts +++ b/packages/realm-server/tests/prerender-deadlock-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { PagePool } from '../prerender/page-pool.ts'; import { AsyncSemaphore } from '../prerender/async-semaphore.ts'; @@ -140,7 +141,7 @@ function makeStubPagePool(opts: { return { pool, stub }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('CS-10976: deadlock-safety reservation', function (hooks) { let prevTabMax: string | undefined; diff --git a/packages/realm-server/tests/prerender-diagnostics-persistence-test.ts b/packages/realm-server/tests/prerender-diagnostics-persistence-test.ts index dacd3ac681..1f75adbbb6 100644 --- a/packages/realm-server/tests/prerender-diagnostics-persistence-test.ts +++ b/packages/realm-server/tests/prerender-diagnostics-persistence-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { Prerenderer } from '../prerender/prerenderer.ts'; import { decorateRenderErrorDiagnostics } from '../prerender/prerender-app.ts'; @@ -80,7 +81,7 @@ function buildFakeSuccessVisitResponse(): FakeVisitResponse { }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('render diagnostics persistence — consolidated channel', function () { test('Prerenderer.decorateRenderErrorsWithTimings lifts outer RenderError.diagnostics onto response.meta and leaves the inner SerializedError clean', function (assert) { let response = buildFakeVisitResponseWithTimeoutError(); diff --git a/packages/realm-server/tests/prerender-host-shell-recycle-test.ts b/packages/realm-server/tests/prerender-host-shell-recycle-test.ts index 497fae4495..a07c2572c5 100644 --- a/packages/realm-server/tests/prerender-host-shell-recycle-test.ts +++ b/packages/realm-server/tests/prerender-host-shell-recycle-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { decideHostShellRecycle } from '../prerender/prerender-app.ts'; @@ -6,7 +7,7 @@ import { decideHostShellRecycle } from '../prerender/prerender-app.ts'; // every heartbeat: the manager echoes the current host-shell token, and the // server recycles its browser when that token differs from the one it warmed // against (the host was redeployed). See PRERENDER_HOST_SHELL_HASH_HEADER. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('decideHostShellRecycle', function () { test('no token reported yet → no recycle, baseline unchanged', function (assert) { assert.deepEqual(decideHostShellRecycle(null, undefined), { diff --git a/packages/realm-server/tests/prerender-manager-test.ts b/packages/realm-server/tests/prerender-manager-test.ts index 02a20af85f..25f65b2631 100644 --- a/packages/realm-server/tests/prerender-manager-test.ts +++ b/packages/realm-server/tests/prerender-manager-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import { basename } from 'path'; @@ -17,7 +18,7 @@ import { toAffinityKey } from '../prerender/affinity.ts'; import { Deferred } from '@cardstack/runtime-common'; import { testCreatePrerenderAuth } from './helpers/index.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Prerender manager', function (hooks) { let previousMultiplex: string | undefined; let previousHeartbeatTimeout: string | undefined; diff --git a/packages/realm-server/tests/prerender-proxy-test.ts b/packages/realm-server/tests/prerender-proxy-test.ts index 7c8671603e..af851a79fd 100644 --- a/packages/realm-server/tests/prerender-proxy-test.ts +++ b/packages/realm-server/tests/prerender-proxy-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import Koa from 'koa'; import Router from '@koa/router'; import supertest from 'supertest'; @@ -13,7 +14,7 @@ import { realmSecretSeed } from './helpers/index.ts'; import { buildCreatePrerenderAuth } from '../prerender/auth.ts'; import { verifyJWT } from '../jwt.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('prerender proxy', function () { let createPrerenderAuth = buildCreatePrerenderAuth(realmSecretSeed); diff --git a/packages/realm-server/tests/prerender-server-test.ts b/packages/realm-server/tests/prerender-server-test.ts index 6f6e628692..5cd427b379 100644 --- a/packages/realm-server/tests/prerender-server-test.ts +++ b/packages/realm-server/tests/prerender-server-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import { basename } from 'path'; @@ -23,7 +24,7 @@ import { import { toAffinityKey } from '../prerender/affinity.ts'; import { Deferred } from '@cardstack/runtime-common'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Prerender server', function (hooks) { let request: SuperTest; let prerenderer: Prerenderer; diff --git a/packages/realm-server/tests/prerender-v8-prof-test.ts b/packages/realm-server/tests/prerender-v8-prof-test.ts index 5291825608..2a05e128fe 100644 --- a/packages/realm-server/tests/prerender-v8-prof-test.ts +++ b/packages/realm-server/tests/prerender-v8-prof-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { promises as fs } from 'fs'; import path from 'path'; import { diff --git a/packages/realm-server/tests/prerendering-test.ts b/packages/realm-server/tests/prerendering-test.ts index 5485e775c1..ec775d5bdd 100644 --- a/packages/realm-server/tests/prerendering-test.ts +++ b/packages/realm-server/tests/prerendering-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { RealmPermissions, @@ -226,7 +227,7 @@ function makeStubPagePool(opts: StubPagePoolOptions) { return { pool, contextsCreated, contextsClosed }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('prerender - mutating tests', function (hooks) { let realmURL = 'http://127.0.0.1:4450/test/'; let prerenderServerURL = new URL(realmURL).origin; diff --git a/packages/realm-server/tests/publish-unpublish-realm-test.ts b/packages/realm-server/tests/publish-unpublish-realm-test.ts index 4a058a1d23..a814ed2b75 100644 --- a/packages/realm-server/tests/publish-unpublish-realm-test.ts +++ b/packages/realm-server/tests/publish-unpublish-realm-test.ts @@ -1,8 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test } from 'supertest'; import supertest from 'supertest'; import { v4 as uuidv4 } from 'uuid'; -import { +import fsExtra from 'fs-extra'; +const { existsSync, ensureDirSync, copySync, @@ -10,7 +12,7 @@ import { readJsonSync, writeJsonSync, removeSync, -} from 'fs-extra'; +} = fsExtra; import { basename, join } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; @@ -37,7 +39,7 @@ import { createJWT as createRealmServerJWT } from '../utils/jwt.ts'; const testRealm2URL = 'http://127.0.0.1:4445/test/'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('publish and unpublish realm tests', function (hooks) { let testRealmHttpServer: Server; let testRealm: Realm; diff --git a/packages/realm-server/tests/queries-test.ts b/packages/realm-server/tests/queries-test.ts index d9f2777f59..39b2bf5a0f 100644 --- a/packages/realm-server/tests/queries-test.ts +++ b/packages/realm-server/tests/queries-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { v4 as uuidv4 } from 'uuid'; @@ -12,7 +13,7 @@ import { import { upsertPublishedRealmInRegistry } from '../lib/realm-registry-writes.ts'; import { setupDB } from './helpers/index.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('fetchUserPermissions', function (hooks) { let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/query-matches-filter-test.ts b/packages/realm-server/tests/query-matches-filter-test.ts index 4cdff7912f..61e56a5485 100644 --- a/packages/realm-server/tests/query-matches-filter-test.ts +++ b/packages/realm-server/tests/query-matches-filter-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import queryMatchesFilterTests from '@cardstack/runtime-common/tests/query-matches-filter-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('MatchesFilter', function () { test('isMatchesFilter returns true for a MatchesFilter', async function (assert) { await runSharedTest(queryMatchesFilterTests, assert, {}); diff --git a/packages/realm-server/tests/queue-test.ts b/packages/realm-server/tests/queue-test.ts index a0e66f6056..22d941f776 100644 --- a/packages/realm-server/tests/queue-test.ts +++ b/packages/realm-server/tests/queue-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { createTestPgAdapter, prepareTestDB } from './helpers/index.ts'; import { @@ -33,7 +34,7 @@ import { import queueTests from '@cardstack/runtime-common/tests/queue-test'; import { basename } from 'path'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('queue', function (hooks) { let publisher: QueuePublisher; let runner: QueueRunner; diff --git a/packages/realm-server/tests/realm-advisory-locks-test.ts b/packages/realm-server/tests/realm-advisory-locks-test.ts index 9b7dd207d7..096be55899 100644 --- a/packages/realm-server/tests/realm-advisory-locks-test.ts +++ b/packages/realm-server/tests/realm-advisory-locks-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { hashRealmUrlForAdvisoryLock, @@ -19,7 +20,7 @@ function timeline(events: string[], startedAt: number, eventTimes: number[]) { .join(','); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('hashRealmUrlForAdvisoryLock', function () { test('is deterministic', function (assert) { const url = 'http://localhost:4201/luke/my-realm/'; diff --git a/packages/realm-server/tests/realm-auth-test.ts b/packages/realm-server/tests/realm-auth-test.ts index cd0a3073b6..e063050dab 100644 --- a/packages/realm-server/tests/realm-auth-test.ts +++ b/packages/realm-server/tests/realm-auth-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test as SupertestTest } from 'supertest'; import sinon from 'sinon'; import { basename } from 'path'; @@ -16,7 +17,7 @@ import { createJWT as createRealmServerJWT } from '../utils/jwt.ts'; import { insertSourceRealmInRegistry } from '../lib/realm-registry-writes.ts'; import type { RealmServer } from '../server.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('realm auth handler', function (hooks) { let dbAdapter: PgAdapter; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-cleanup-transaction-test.ts b/packages/realm-server/tests/realm-cleanup-transaction-test.ts index 6bbca75e90..3ad660bf2c 100644 --- a/packages/realm-server/tests/realm-cleanup-transaction-test.ts +++ b/packages/realm-server/tests/realm-cleanup-transaction-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { @@ -19,7 +20,7 @@ import { // registry-row delete AND the permissions delete. Pre-CS-10898 the handler // ran each helper through the shared dbAdapter, so each DELETE committed in // its own auto-tx and a mid-cleanup throw left the realm half-deleted. -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('CS-10898: realm cleanup transactionality', function (hooks) { let dbAdapter: PgAdapter; setupDB(hooks, { diff --git a/packages/realm-server/tests/realm-endpoints-test.ts b/packages/realm-server/tests/realm-endpoints-test.ts index 47e208e351..225fa6245a 100644 --- a/packages/realm-server/tests/realm-endpoints-test.ts +++ b/packages/realm-server/tests/realm-endpoints-test.ts @@ -1,10 +1,12 @@ -import QUnit, { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import supertest from 'supertest'; import { join, resolve, basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync, existsSync, @@ -12,7 +14,7 @@ import { readJSONSync, removeSync, writeFileSync, -} from 'fs-extra'; +} = fsExtra; import { utimesSync } from 'fs'; import type { Realm } from '@cardstack/runtime-common'; import { @@ -72,7 +74,7 @@ import type { const testRealm2URL = new URL('http://127.0.0.1:4445/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints', function (hooks) { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealmHref = realmURL.href; @@ -2008,7 +2010,7 @@ module(basename(__filename), function () { let testRealm: Realm; let virtualNetwork = createVirtualNetwork(); - const basePath = resolve(join(__dirname, '..', '..', 'base')); + const basePath = resolve(join(import.meta.dirname, '..', '..', 'base')); const demoFileSystem: Record = { 'realm.json': readJSONSync(join(fixtureDir('realistic'), 'realm.json')), 'person.gts': readFileSync( diff --git a/packages/realm-server/tests/realm-endpoints/cancel-indexing-job-test.ts b/packages/realm-server/tests/realm-endpoints/cancel-indexing-job-test.ts index d79ecf0513..9e59a58243 100644 --- a/packages/realm-server/tests/realm-endpoints/cancel-indexing-job-test.ts +++ b/packages/realm-server/tests/realm-endpoints/cancel-indexing-job-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { SuperTest, Test } from 'supertest'; import type { @@ -15,7 +16,7 @@ import { } from '../helpers/index.ts'; import type { PgAdapter as TestPgAdapter } from '@cardstack/postgres'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm-specific Endpoints | POST _cancel-indexing-job', function (hooks) { diff --git a/packages/realm-server/tests/realm-endpoints/dependencies-test.ts b/packages/realm-server/tests/realm-endpoints/dependencies-test.ts index 5f2b199cf6..7724bb1712 100644 --- a/packages/realm-server/tests/realm-endpoints/dependencies-test.ts +++ b/packages/realm-server/tests/realm-endpoints/dependencies-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -6,7 +7,7 @@ import { SupportedMimeType } from '@cardstack/runtime-common'; import type { RealmHttpServer as Server } from '../../server.ts'; import { closeServer, setupPermissionedRealmCached } from '../helpers/index.ts'; -module(`realm-endpoints/${basename(__filename)}`, function (hooks) { +module(`realm-endpoints/${basename(import.meta.filename)}`, function (hooks) { let testRealm: Realm; let testRealmHttpServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/directory-test.ts b/packages/realm-server/tests/realm-endpoints/directory-test.ts index a12cdf4909..ed00f210de 100644 --- a/packages/realm-server/tests/realm-endpoints/directory-test.ts +++ b/packages/realm-server/tests/realm-endpoints/directory-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -9,7 +10,7 @@ import { } from '../helpers/index.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | GET directory path', function () { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/indexing-errors-test.ts b/packages/realm-server/tests/realm-endpoints/indexing-errors-test.ts index 961bcd67e4..99fb462757 100644 --- a/packages/realm-server/tests/realm-endpoints/indexing-errors-test.ts +++ b/packages/realm-server/tests/realm-endpoints/indexing-errors-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import supertest from 'supertest'; import type { SuperTest, Test } from 'supertest'; @@ -20,7 +21,7 @@ import { const ownerUserId = '@mango:localhost'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('with a clean realm', function (hooks) { let realmURL = testRealmURLFor('test/'); let request: RealmRequest; diff --git a/packages/realm-server/tests/realm-endpoints/info-test.ts b/packages/realm-server/tests/realm-endpoints/info-test.ts index e2ac6dc5fc..bbbfcc0395 100644 --- a/packages/realm-server/tests/realm-endpoints/info-test.ts +++ b/packages/realm-server/tests/realm-endpoints/info-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { RealmHttpServer as Server } from '../../server.ts'; @@ -13,7 +14,7 @@ import { import '@cardstack/runtime-common/helpers/code-equality-assertion'; import { resetCatalogRealms } from '../../handlers/handle-fetch-catalog-realms.ts'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | QUERY _info', function (hooks) { let realmURL = testRealmURLFor('test/'); let testRealm: Realm; diff --git a/packages/realm-server/tests/realm-endpoints/invalidate-urls-test.ts b/packages/realm-server/tests/realm-endpoints/invalidate-urls-test.ts index 2bcbeb4076..bdb7822096 100644 --- a/packages/realm-server/tests/realm-endpoints/invalidate-urls-test.ts +++ b/packages/realm-server/tests/realm-endpoints/invalidate-urls-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { SuperTest, Test } from 'supertest'; import type { Realm } from '@cardstack/runtime-common'; @@ -6,7 +7,7 @@ import { SupportedMimeType } from '@cardstack/runtime-common'; import { createJWT, setupPermissionedRealmCached } from '../helpers/index.ts'; import type { PgAdapter as TestPgAdapter } from '@cardstack/postgres'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | POST _invalidate', function (hooks) { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/lint-test.ts b/packages/realm-server/tests/realm-endpoints/lint-test.ts index 3a2129db87..984003c47d 100644 --- a/packages/realm-server/tests/realm-endpoints/lint-test.ts +++ b/packages/realm-server/tests/realm-endpoints/lint-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -12,7 +13,7 @@ import { } from '../helpers/prettier-test-utils.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | POST _lint', function (hooks) { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/markdown-test.ts b/packages/realm-server/tests/realm-endpoints/markdown-test.ts index 09548a4457..8090f7cf5c 100644 --- a/packages/realm-server/tests/realm-endpoints/markdown-test.ts +++ b/packages/realm-server/tests/realm-endpoints/markdown-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SuperTest, Test } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -280,7 +281,7 @@ const CACHE_CARD_UPDATED_GTS = ` } `; -module(`realm-endpoints/${basename(__filename)}`, function (hooks) { +module(`realm-endpoints/${basename(import.meta.filename)}`, function (hooks) { let testRealm: Realm; let testRealmHttpServer: Server; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/mtimes-test.ts b/packages/realm-server/tests/realm-endpoints/mtimes-test.ts index e6e5605176..b3c83aa05b 100644 --- a/packages/realm-server/tests/realm-endpoints/mtimes-test.ts +++ b/packages/realm-server/tests/realm-endpoints/mtimes-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -11,7 +12,7 @@ import { } from '../helpers/index.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | GET _mtimes', function (hooks) { let testRealm: Realm; let testRealmPath: string; diff --git a/packages/realm-server/tests/realm-endpoints/permissions-test.ts b/packages/realm-server/tests/realm-endpoints/permissions-test.ts index 11e19ab992..181bdb67f7 100644 --- a/packages/realm-server/tests/realm-endpoints/permissions-test.ts +++ b/packages/realm-server/tests/realm-endpoints/permissions-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -17,7 +18,7 @@ import { import '@cardstack/runtime-common/helpers/code-equality-assertion'; import type { PgAdapter } from '@cardstack/postgres'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | _permissions', function () { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/publishability-test.ts b/packages/realm-server/tests/realm-endpoints/publishability-test.ts index 1f820781a7..e28ec6c421 100644 --- a/packages/realm-server/tests/realm-endpoints/publishability-test.ts +++ b/packages/realm-server/tests/realm-endpoints/publishability-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import supertest from 'supertest'; import type { SuperTest, Test } from 'supertest'; @@ -20,7 +21,7 @@ import { const ownerUserId = '@mango:localhost'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('with a publishable realm', function (hooks) { let realmURL = testRealmURLFor('test/'); let request: RealmRequest; diff --git a/packages/realm-server/tests/realm-endpoints/reindex-test.ts b/packages/realm-server/tests/realm-endpoints/reindex-test.ts index dc3136ff4b..1bee35981d 100644 --- a/packages/realm-server/tests/realm-endpoints/reindex-test.ts +++ b/packages/realm-server/tests/realm-endpoints/reindex-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import { readFileSync, utimesSync, writeFileSync } from 'fs'; import type { SuperTest, Test } from 'supertest'; @@ -95,7 +96,7 @@ const ARTICLE_INSTANCE = JSON.stringify({ }, }); -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm-specific Endpoints | POST _reindex and _full-reindex', function (hooks) { diff --git a/packages/realm-server/tests/realm-endpoints/search-test.ts b/packages/realm-server/tests/realm-endpoints/search-test.ts index f4b9c198c0..8a09526c97 100644 --- a/packages/realm-server/tests/realm-endpoints/search-test.ts +++ b/packages/realm-server/tests/realm-endpoints/search-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import { baseRealm, baseRRI, type Realm, rri } from '@cardstack/runtime-common'; @@ -10,7 +11,7 @@ import { } from '../helpers/index.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | _search', function () { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/realm-endpoints/search-v2-test.ts b/packages/realm-server/tests/realm-endpoints/search-v2-test.ts index b763c94f3d..21b42b030d 100644 --- a/packages/realm-server/tests/realm-endpoints/search-v2-test.ts +++ b/packages/realm-server/tests/realm-endpoints/search-v2-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import { rri, type Realm } from '@cardstack/runtime-common'; @@ -9,7 +10,7 @@ import { } from '../helpers/index.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | _search-v2', function (hooks) { let testRealm: Realm; let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/realm-endpoints/user-test.ts b/packages/realm-server/tests/realm-endpoints/user-test.ts index a302257ab3..f1d66ed573 100644 --- a/packages/realm-server/tests/realm-endpoints/user-test.ts +++ b/packages/realm-server/tests/realm-endpoints/user-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { RealmHttpServer as Server } from '../../server.ts'; @@ -21,7 +22,7 @@ import { } from '@cardstack/billing/billing-queries'; import { resetCatalogRealms } from '../../handlers/handle-fetch-catalog-realms.ts'; -module(`realm-endpoints/${basename(__filename)}`, function () { +module(`realm-endpoints/${basename(import.meta.filename)}`, function () { module('Realm-specific Endpoints | GET _user', function (hooks) { let testRealm: Realm; let testRealmHttpServer: Server; diff --git a/packages/realm-server/tests/realm-file-changes-listener-test.ts b/packages/realm-server/tests/realm-file-changes-listener-test.ts index b616f4941d..e97f417fa5 100644 --- a/packages/realm-server/tests/realm-file-changes-listener-test.ts +++ b/packages/realm-server/tests/realm-file-changes-listener-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { notifyAllFileChanges, type Realm } from '@cardstack/runtime-common'; @@ -53,7 +54,7 @@ function waitFor( }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('parsePayload', function () { test('parses url:path with a port in the url', function (assert) { assert.deepEqual( diff --git a/packages/realm-server/tests/realm-identifiers-test.ts b/packages/realm-server/tests/realm-identifiers-test.ts index 9a95552624..de2eb6717f 100644 --- a/packages/realm-server/tests/realm-identifiers-test.ts +++ b/packages/realm-server/tests/realm-identifiers-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { RealmPaths, VirtualNetwork } from '@cardstack/runtime-common'; import { ri, rri } from '@cardstack/runtime-common'; import type { SingleCardDocument } from '@cardstack/runtime-common'; import { relativizeDocument } from '@cardstack/runtime-common/realm-index-query-engine'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { // Regression test for CS-10498: cards in prefix-mapped realms (like the // openrouter realm) threw TypeError: Invalid URL when served. // diff --git a/packages/realm-server/tests/realm-index-updated-listener-test.ts b/packages/realm-server/tests/realm-index-updated-listener-test.ts index 37ed38a205..0466f6886a 100644 --- a/packages/realm-server/tests/realm-index-updated-listener-test.ts +++ b/packages/realm-server/tests/realm-index-updated-listener-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { Realm } from '@cardstack/runtime-common'; @@ -39,7 +40,7 @@ function waitFor( }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('RealmIndexUpdatedListener (dispatch)', function () { test('handleNotification forwards to the mounted realm', function (assert) { let cleared = 0; diff --git a/packages/realm-server/tests/realm-operations-test.ts b/packages/realm-server/tests/realm-operations-test.ts index d2631ab67d..a65ab67397 100644 --- a/packages/realm-server/tests/realm-operations-test.ts +++ b/packages/realm-server/tests/realm-operations-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { checkDomainAvailability, @@ -53,7 +54,7 @@ function jsonResponse(status: number, body: unknown): Response { }); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('realm-operations', function () { test('publishRealm POSTs normalized URLs and maps the 202 response', async function (assert) { let { client, calls } = makeClient(() => diff --git a/packages/realm-server/tests/realm-registry-backfill-test.ts b/packages/realm-server/tests/realm-registry-backfill-test.ts index 3f5b1f468b..ea70fcc1ae 100644 --- a/packages/realm-server/tests/realm-registry-backfill-test.ts +++ b/packages/realm-server/tests/realm-registry-backfill-test.ts @@ -1,7 +1,9 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import { dirSync, type DirResult } from 'tmp'; -import { ensureDirSync, writeFileSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync, writeFileSync } = fsExtra; import type { PgAdapter } from '@cardstack/postgres'; import { asExpressions, @@ -103,7 +105,7 @@ function seedRealmJson(realmDir: string, payload: Record) { writeFileSync(join(realmDir, 'realm.json'), JSON.stringify(card, null, 2)); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('runRegistryBackfill', function (hooks) { let dbAdapter: PgAdapter; let dir: DirResult; diff --git a/packages/realm-server/tests/realm-registry-reconciler-test.ts b/packages/realm-server/tests/realm-registry-reconciler-test.ts index 8df5348fb6..28b8f4e369 100644 --- a/packages/realm-server/tests/realm-registry-reconciler-test.ts +++ b/packages/realm-server/tests/realm-registry-reconciler-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import type { Realm } from '@cardstack/runtime-common'; @@ -51,7 +52,7 @@ async function deleteRow(dbAdapter: PgAdapter, url: string) { ]); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('RealmRegistryReconciler', function (hooks) { let dbAdapter: PgAdapter; let mountCalls: string[]; diff --git a/packages/realm-server/tests/realm-registry-writes-test.ts b/packages/realm-server/tests/realm-registry-writes-test.ts index d81473c609..082551016a 100644 --- a/packages/realm-server/tests/realm-registry-writes-test.ts +++ b/packages/realm-server/tests/realm-registry-writes-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { asExpressions, insert, query } from '@cardstack/runtime-common'; @@ -71,7 +72,7 @@ async function seedBootstrapRow(dbAdapter: PgAdapter, url: string) { ); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('upsertPublishedRealmInRegistry', function (hooks) { let dbAdapter: PgAdapter; setupDB(hooks, { diff --git a/packages/realm-server/tests/realm-routing-test.ts b/packages/realm-server/tests/realm-routing-test.ts index 293197312b..cfe32570a3 100644 --- a/packages/realm-server/tests/realm-routing-test.ts +++ b/packages/realm-server/tests/realm-routing-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { rri } from '@cardstack/runtime-common'; import type { LooseSingleCardDocument, Realm } from '@cardstack/runtime-common'; @@ -73,7 +74,7 @@ function makeRoutingFixture(): Record< }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm.getHostRoutingMap', function (hooks) { let realmURL = new URL('http://127.0.0.1:4444/routing-unit/'); let testRealm: Realm; diff --git a/packages/realm-server/tests/remote-prerenderer-test.ts b/packages/realm-server/tests/remote-prerenderer-test.ts index 555440e331..afaff3f4b5 100644 --- a/packages/realm-server/tests/remote-prerenderer-test.ts +++ b/packages/realm-server/tests/remote-prerenderer-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createServer } from 'http'; import { createRemotePrerenderer } from '../prerender/remote-prerenderer.ts'; @@ -8,7 +9,7 @@ import { PRERENDER_SERVER_STATUS_HEADER, } from '../prerender/prerender-constants.ts'; -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { hooks.afterEach(function () { delete process.env.PRERENDER_MANAGER_RETRY_ATTEMPTS; delete process.env.PRERENDER_MANAGER_RETRY_DELAY_MS; @@ -449,7 +450,7 @@ module(basename(__filename), function (hooks) { }); }); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('remote prerenderer timeouts', function () { test('does not retry when the client aborts from request timeout', async function (assert) { process.env.PRERENDER_MANAGER_RETRY_ATTEMPTS = '3'; diff --git a/packages/realm-server/tests/render-type-resolution-test.ts b/packages/realm-server/tests/render-type-resolution-test.ts index a3d30d0c89..5202cf1fa2 100644 --- a/packages/realm-server/tests/render-type-resolution-test.ts +++ b/packages/realm-server/tests/render-type-resolution-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { parseUnifiedSearchRequestFromPayload, @@ -22,7 +23,7 @@ const explicitRef: ResolvedCodeRef = { name: 'Contact', }; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('render-type resolution', function () { test('an explicit renderType CodeRef wins', function (assert) { assert.deepEqual( diff --git a/packages/realm-server/tests/request-forward-test.ts b/packages/realm-server/tests/request-forward-test.ts index 4c7fa1b1a3..532724acf3 100644 --- a/packages/realm-server/tests/request-forward-test.ts +++ b/packages/realm-server/tests/request-forward-test.ts @@ -1,11 +1,13 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import sinon from 'sinon'; import type { Test, SuperTest } from 'supertest'; import supertest from 'supertest'; import { basename, join } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import { dirSync, type DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; import { setupDB, runTestRealmServer, @@ -26,7 +28,7 @@ import { } from '@cardstack/billing/billing-queries'; import { AllowedProxyDestinations } from '../lib/allowed-proxy-destinations.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | _request-forward', function (hooks) { let testRealmHttpServer: Server; let testRealm: any; diff --git a/packages/realm-server/tests/resolve-published-realm-url-test.ts b/packages/realm-server/tests/resolve-published-realm-url-test.ts index bf641bf5cf..0cf2a73351 100644 --- a/packages/realm-server/tests/resolve-published-realm-url-test.ts +++ b/packages/realm-server/tests/resolve-published-realm-url-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { deriveRealmName, resolvePublishedRealmUrl, } from '@cardstack/runtime-common'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('resolve-published-realm-url', function () { // deriveRealmName test('deriveRealmName returns the last path segment, lowercased', async function (assert) { diff --git a/packages/realm-server/tests/run-command-task-test.ts b/packages/realm-server/tests/run-command-task-test.ts index d4de108c4a..16c1117eb6 100644 --- a/packages/realm-server/tests/run-command-task-test.ts +++ b/packages/realm-server/tests/run-command-task-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import runCommandTaskTests from '@cardstack/runtime-common/tests/run-command-task-shared-tests'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('run-command task', function () { test('returns error when runAs has no realm permissions', async function (assert) { await runSharedTest(runCommandTaskTests, assert, {}); diff --git a/packages/realm-server/tests/runtime-dependency-tracker-test.ts b/packages/realm-server/tests/runtime-dependency-tracker-test.ts index facbff8ccb..8975ed0712 100644 --- a/packages/realm-server/tests/runtime-dependency-tracker-test.ts +++ b/packages/realm-server/tests/runtime-dependency-tracker-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { Loader, @@ -14,7 +15,7 @@ import { withRuntimeDependencyTrackingContext, } from '@cardstack/runtime-common'; -module(basename(__filename), function (hooks) { +module(basename(import.meta.filename), function (hooks) { hooks.afterEach(() => { endRuntimeDependencyTrackingSession(); resetRuntimeDependencyTracker(); diff --git a/packages/realm-server/tests/runtime-exception-capture-test.ts b/packages/realm-server/tests/runtime-exception-capture-test.ts index 53fd79f31d..1b29f479f5 100644 --- a/packages/realm-server/tests/runtime-exception-capture-test.ts +++ b/packages/realm-server/tests/runtime-exception-capture-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { EventEmitter } from 'events'; import { attachRuntimeExceptionCapture } from '../prerender/runtime-exception-capture.ts'; @@ -124,7 +125,7 @@ function buildExceptionThrownEvent(opts: { }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('records Runtime.exceptionThrown into the recorder', async function (assert) { let client = new FakeCDPClient(); let page = new FakePage(client); diff --git a/packages/realm-server/tests/sanitize-for-jsonb-test.ts b/packages/realm-server/tests/sanitize-for-jsonb-test.ts index cc79b4a07e..ced91212bf 100644 --- a/packages/realm-server/tests/sanitize-for-jsonb-test.ts +++ b/packages/realm-server/tests/sanitize-for-jsonb-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { sanitizeForJsonb } from '@cardstack/runtime-common'; @@ -18,7 +19,7 @@ function hasIllegalCodePoint(value: string): boolean { ); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('returns clean values unchanged', function (assert) { assert.strictEqual(sanitizeForJsonb('plain text'), 'plain text'); assert.strictEqual(sanitizeForJsonb(42 as unknown), 42); diff --git a/packages/realm-server/tests/sanitize-head-html-test.ts b/packages/realm-server/tests/sanitize-head-html-test.ts index 1270b8578c..c31f510b84 100644 --- a/packages/realm-server/tests/sanitize-head-html-test.ts +++ b/packages/realm-server/tests/sanitize-head-html-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { JSDOM } from 'jsdom'; import { @@ -11,7 +12,7 @@ function makeDoc() { return new JSDOM().window.document; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('sanitizeHeadHTML', function () { test('allows title, meta, and link tags', function (assert) { let doc = makeDoc(); diff --git a/packages/realm-server/tests/screenshot-card-test.ts b/packages/realm-server/tests/screenshot-card-test.ts index b64d10805a..a5a079f206 100644 --- a/packages/realm-server/tests/screenshot-card-test.ts +++ b/packages/realm-server/tests/screenshot-card-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import Koa from 'koa'; import Router from '@koa/router'; import supertest from 'supertest'; @@ -19,7 +20,7 @@ import { jwtMiddleware } from '../middleware/index.ts'; import { createJWT } from '../utils/jwt.ts'; import { realmSecretSeed } from './helpers/index.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('/_screenshot-card endpoint', function () { function makeDbAdapter(): DBAdapter { return { diff --git a/packages/realm-server/tests/scripts/create_seeded_db.sh b/packages/realm-server/tests/scripts/create_seeded_db.sh index fa72e80580..fd1cf7d25e 100755 --- a/packages/realm-server/tests/scripts/create_seeded_db.sh +++ b/packages/realm-server/tests/scripts/create_seeded_db.sh @@ -38,6 +38,7 @@ docker exec "$TEST_PG_SEED_CONTAINER" psql -U postgres -d postgres -v ON_ERROR_S pnpm exec node-pg-migrate \ --migrations-table migrations \ --check-order false \ + --ignore-pattern '.*\.eslintrc\.js|package\.json' \ --no-verbose \ up ) diff --git a/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh b/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh index 3796ca8455..8a236da66c 100755 --- a/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh +++ b/packages/realm-server/tests/scripts/run-qunit-with-test-pg.sh @@ -16,7 +16,7 @@ fi JUNIT_REPORTER_ARGS=() if [ -n "${JUNIT_OUTPUT_FILE-}" ]; then - JUNIT_REPORTER_ARGS=(--require "${SCRIPT_DIR}/../../scripts/junit-reporter.js") + JUNIT_REPORTER_ARGS=(--require "${SCRIPT_DIR}/../../scripts/junit-reporter.cjs") fi # Trust mkcert's local CA so test fetches to the dev stack on HTTPS @@ -45,4 +45,4 @@ NODE_DISABLE_COMPILE_CACHE=1 \ PGPORT=55436 \ STRIPE_WEBHOOK_SECRET=stripe-webhook-secret \ STRIPE_API_KEY=stripe-api-key \ -qunit --require ts-node/register/transpile-only ${JUNIT_REPORTER_ARGS[@]+"${JUNIT_REPORTER_ARGS[@]}"} "$@" tests/index.ts +node ${JUNIT_REPORTER_ARGS[@]+"${JUNIT_REPORTER_ARGS[@]}"} tests/index.ts "$@" diff --git a/packages/realm-server/tests/search-compat-test.ts b/packages/realm-server/tests/search-compat-test.ts index a5818620cb..d76cb84ce1 100644 --- a/packages/realm-server/tests/search-compat-test.ts +++ b/packages/realm-server/tests/search-compat-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { searchEntryDocToPrerenderedDoc, @@ -64,7 +65,7 @@ function chainDoc(memberRefs: { module: string; name: string }[]) { return doc; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('prerendered coalescing: renderType pick', function () { test('picks the requested ancestor when its rendering exists', function (assert) { let doc = searchEntryDocToPrerenderedDoc( diff --git a/packages/realm-server/tests/search-entries-engine-test.ts b/packages/realm-server/tests/search-entries-engine-test.ts index c8c0c9d80f..2ffaf9f649 100644 --- a/packages/realm-server/tests/search-entries-engine-test.ts +++ b/packages/realm-server/tests/search-entries-engine-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { baseRRI, @@ -70,7 +71,7 @@ function htmlIdsOf(entry: SearchEntryResource): string[] | undefined { return entry.relationships.html?.data.map((member) => member.id); } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('searchEntries projection engine', function (hooks) { let testRealm: Realm; let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/search-entry-test.ts b/packages/realm-server/tests/search-entry-test.ts index 94752ebcef..d8b5170d61 100644 --- a/packages/realm-server/tests/search-entry-test.ts +++ b/packages/realm-server/tests/search-entry-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { buildHtmlResource, @@ -79,7 +80,7 @@ const universe: RenderingCandidate[] = [ { format: 'head' }, ]; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('search-entry query parser', function () { test('translates the canonical search-entry query', function (assert) { let htmlQuery: HtmlQuery = { diff --git a/packages/realm-server/tests/search-in-flight-key-test.ts b/packages/realm-server/tests/search-in-flight-key-test.ts index 47b33be840..d58f74d1a8 100644 --- a/packages/realm-server/tests/search-in-flight-key-test.ts +++ b/packages/realm-server/tests/search-in-flight-key-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { runSharedTest } from '@cardstack/runtime-common/helpers'; import searchInFlightKeyTests from '@cardstack/runtime-common/tests/search-in-flight-key-test'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('searchInFlightKey', function () { test('same query + opts produce the same key', async function (assert) { await runSharedTest(searchInFlightKeyTests, assert, {}); diff --git a/packages/realm-server/tests/search-prerendered-test.ts b/packages/realm-server/tests/search-prerendered-test.ts index 360ccfaa4b..00c8fc50b0 100644 --- a/packages/realm-server/tests/search-prerendered-test.ts +++ b/packages/realm-server/tests/search-prerendered-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { basename } from 'path'; import type { Realm } from '@cardstack/runtime-common'; @@ -10,7 +11,7 @@ import '@cardstack/runtime-common/helpers/code-equality-assertion'; const missingPrerenderedHtmlFormatMessage = `Must include a 'prerenderedHtmlFormat' parameter with a value of ${PRERENDERED_HTML_FORMATS.join()} to use this endpoint`; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | _search-prerendered', function () { let testRealm: Realm; let request: SuperTest; diff --git a/packages/realm-server/tests/serve-index-test.ts b/packages/realm-server/tests/serve-index-test.ts index 2dfecb6235..d880780032 100644 --- a/packages/realm-server/tests/serve-index-test.ts +++ b/packages/realm-server/tests/serve-index-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createServeIndex } from '../handlers/serve-index.ts'; @@ -32,7 +33,7 @@ function validIndexHTML(): string { )}">`; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('a thrown error in retrieveIndexHTML clears the cache so the next call retries', async function (assert) { let calls = 0; let { retrieveIndexHTML } = createServeIndex( diff --git a/packages/realm-server/tests/server-config-test.ts b/packages/realm-server/tests/server-config-test.ts index 2107762bbc..b248da5a5e 100644 --- a/packages/realm-server/tests/server-config-test.ts +++ b/packages/realm-server/tests/server-config-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createServeIndex } from '../handlers/serve-index.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { test('prefers MATRIX_SERVER_NAME over matrix URL hostname in host config', async function (assert) { let originalMatrixServerName = process.env.MATRIX_SERVER_NAME; diff --git a/packages/realm-server/tests/server-endpoints/authentication-test.ts b/packages/realm-server/tests/server-endpoints/authentication-test.ts index a2d457e86f..3556b4a820 100644 --- a/packages/realm-server/tests/server-endpoints/authentication-test.ts +++ b/packages/realm-server/tests/server-endpoints/authentication-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { Test, SuperTest } from 'supertest'; import type { RealmHttpServer as Server } from '../../server.ts'; @@ -16,7 +17,7 @@ import { getUserByMatrixUserId } from '@cardstack/billing/billing-queries'; import type { PgAdapter } from '@cardstack/postgres'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Realm server authentication', function (hooks) { let request: SuperTest; let dbAdapter: PgAdapter; diff --git a/packages/realm-server/tests/server-endpoints/bot-commands-test.ts b/packages/realm-server/tests/server-endpoints/bot-commands-test.ts index 4f207eae47..f2d97ba08d 100644 --- a/packages/realm-server/tests/server-endpoints/bot-commands-test.ts +++ b/packages/realm-server/tests/server-endpoints/bot-commands-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { param, query, uuidv4 } from '@cardstack/runtime-common'; import { setupServerEndpointsTest } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Realm Server Endpoints', function (hooks) { let context = setupServerEndpointsTest(hooks); diff --git a/packages/realm-server/tests/server-endpoints/bot-registration-test.ts b/packages/realm-server/tests/server-endpoints/bot-registration-test.ts index 4344a2cee2..8ad2af2f3b 100644 --- a/packages/realm-server/tests/server-endpoints/bot-registration-test.ts +++ b/packages/realm-server/tests/server-endpoints/bot-registration-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { param, query, uuidv4 } from '@cardstack/runtime-common'; import { setupServerEndpointsTest } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm Server Endpoints (not specific to one realm)', function (hooks) { diff --git a/packages/realm-server/tests/server-endpoints/delete-realm-test.ts b/packages/realm-server/tests/server-endpoints/delete-realm-test.ts index 52067e97bb..8bcae9ac43 100644 --- a/packages/realm-server/tests/server-endpoints/delete-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/delete-realm-test.ts @@ -1,6 +1,8 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; -import { existsSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync } = fsExtra; import { v4 as uuidv4 } from 'uuid'; import { @@ -15,7 +17,7 @@ import { insertJob, insertUser, realmSecretSeed } from '../helpers/index.ts'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { setupServerEndpointsTest } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function (hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (hooks) { let context = setupServerEndpointsTest(hooks); async function createRealmFor(ownerUserId: string) { diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts index f8519696ce..447b9978df 100644 --- a/packages/realm-server/tests/server-endpoints/download-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; import { mkdirSync, writeFileSync } from 'fs'; import { v4 as uuidv4 } from 'uuid'; @@ -25,7 +26,7 @@ function binaryParser( }); } -module(`server-endpoints/${basename(__filename)}`, function (hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (hooks) { // Use the `simple` fixture so the realm has real card files to assert // the archive contains; the `blank` fixture has no card content. let context = setupServerEndpointsTest(hooks, { fixture: 'simple' }); diff --git a/packages/realm-server/tests/server-endpoints/federated-types-test.ts b/packages/realm-server/tests/server-endpoints/federated-types-test.ts index e3a6956061..00414ea12e 100644 --- a/packages/realm-server/tests/server-endpoints/federated-types-test.ts +++ b/packages/realm-server/tests/server-endpoints/federated-types-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; import { basename, join } from 'path'; @@ -32,7 +33,7 @@ interface FederatedTypesResponse { }; } -module(`server-endpoints/${basename(__filename)}`, function (_hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (_hooks) { module('Realm Server Endpoints | /_federated-types', function (hooks) { let testRealm: Realm; let secondaryRealm: Realm; diff --git a/packages/realm-server/tests/server-endpoints/incoming-webhook-test.ts b/packages/realm-server/tests/server-endpoints/incoming-webhook-test.ts index 3b21b10e54..5f45150486 100644 --- a/packages/realm-server/tests/server-endpoints/incoming-webhook-test.ts +++ b/packages/realm-server/tests/server-endpoints/incoming-webhook-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { param, query, uuidv4 } from '@cardstack/runtime-common'; import { setupServerEndpointsTest } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Incoming Webhook Endpoints', function (hooks) { let context = setupServerEndpointsTest(hooks); diff --git a/packages/realm-server/tests/server-endpoints/index-responses-test.ts b/packages/realm-server/tests/server-endpoints/index-responses-test.ts index ef5c74cd44..130373fb03 100644 --- a/packages/realm-server/tests/server-endpoints/index-responses-test.ts +++ b/packages/realm-server/tests/server-endpoints/index-responses-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { join, basename } from 'path'; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; @@ -24,10 +25,11 @@ import { waitUntil, } from '../helpers/index.ts'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; -import { ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { ensureDirSync } = fsExtra; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm Server Endpoints (not specific to one realm)', function (hooks) { diff --git a/packages/realm-server/tests/server-endpoints/info-test.ts b/packages/realm-server/tests/server-endpoints/info-test.ts index a734726c7b..174deb272a 100644 --- a/packages/realm-server/tests/server-endpoints/info-test.ts +++ b/packages/realm-server/tests/server-endpoints/info-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; import { basename, join } from 'path'; @@ -22,7 +23,7 @@ import { import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import type { RealmHttpServer as Server } from '../../server.ts'; -module(`server-endpoints/${basename(__filename)}`, function (_hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (_hooks) { module('Realm Server Endpoints | /_federated-info', function (hooks) { let testRealm: Realm; let secondaryRealm: Realm; diff --git a/packages/realm-server/tests/server-endpoints/maintenance-endpoints-test.ts b/packages/realm-server/tests/server-endpoints/maintenance-endpoints-test.ts index 70e2037ecd..bce26054ea 100644 --- a/packages/realm-server/tests/server-endpoints/maintenance-endpoints-test.ts +++ b/packages/realm-server/tests/server-endpoints/maintenance-endpoints-test.ts @@ -1,10 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { v4 as uuidv4 } from 'uuid'; import sinon from 'sinon'; import { PgAdapter, PgQueueRunner } from '@cardstack/postgres'; import { sumUpCreditsLedger } from '@cardstack/billing/billing-queries'; -import * as boxelUIChangeChecker from '../../lib/boxel-ui-change-checker.ts'; +import { boxelUIChecker } from '../../lib/boxel-ui-change-checker.ts'; import { fetchRealmPermissions } from '@cardstack/runtime-common'; import { grafanaSecret, @@ -54,7 +55,7 @@ async function ensureMatrixAdminUser(): Promise { }); } -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm Server Endpoints (not specific to one realm)', function (hooks) { @@ -900,13 +901,13 @@ module(`server-endpoints/${basename(__filename)}`, function () { test('post-deployment endpoint triggers full reindex when checksums differ', async function (assert: Assert) { let compareCurrentBoxelUIChecksumStub = sinon - .stub(boxelUIChangeChecker, 'compareCurrentBoxelUIChecksum') + .stub(boxelUIChecker, 'compareCurrentBoxelUIChecksum') .resolves({ previousChecksum: 'old-checksum-123', currentChecksum: 'new-checksum-456', }); let writeCurrentBoxelUIChecksumStub = sinon.stub( - boxelUIChangeChecker, + boxelUIChecker, 'writeCurrentBoxelUIChecksum', ); let registryOnlyRealmURL = `http://localhost:4201/registry-only-${uuidv4()}/`; @@ -1030,13 +1031,13 @@ module(`server-endpoints/${basename(__filename)}`, function () { test('post-deployment endpoint clears modules cache even when checksums match', async function (assert: Assert) { let compareCurrentBoxelUIChecksumStub = sinon - .stub(boxelUIChangeChecker, 'compareCurrentBoxelUIChecksum') + .stub(boxelUIChecker, 'compareCurrentBoxelUIChecksum') .resolves({ previousChecksum: 'same-checksum-789', currentChecksum: 'same-checksum-789', }); let writeCurrentBoxelUIChecksumStub = sinon.stub( - boxelUIChangeChecker, + boxelUIChecker, 'writeCurrentBoxelUIChecksum', ); diff --git a/packages/realm-server/tests/server-endpoints/queue-status-test.ts b/packages/realm-server/tests/server-endpoints/queue-status-test.ts index d76d1ac276..629d55ffe1 100644 --- a/packages/realm-server/tests/server-endpoints/queue-status-test.ts +++ b/packages/realm-server/tests/server-endpoints/queue-status-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { Test, SuperTest } from 'supertest'; import type { PgAdapter } from '@cardstack/postgres'; @@ -6,7 +7,7 @@ import { insertJob, setupPermissionedRealmCached } from '../helpers/index.ts'; import { monitoringAuthToken } from '../../utils/monitoring.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Realm Server Endpoints (not specific to one realm)', function () { module('_queue-status', function (hooks) { let request: SuperTest; diff --git a/packages/realm-server/tests/server-endpoints/realm-lifecycle-test.ts b/packages/realm-server/tests/server-endpoints/realm-lifecycle-test.ts index 21c901a3fa..b0f5604249 100644 --- a/packages/realm-server/tests/server-endpoints/realm-lifecycle-test.ts +++ b/packages/realm-server/tests/server-endpoints/realm-lifecycle-test.ts @@ -1,6 +1,8 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename, join } from 'path'; -import { existsSync, readJSONSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { existsSync, readJSONSync } = fsExtra; import type { Test, SuperTest } from 'supertest'; import { v4 as uuidv4 } from 'uuid'; import type { Query } from '@cardstack/runtime-common/query'; @@ -26,7 +28,7 @@ import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { setupServerEndpointsTest, testRealmURL } from './helpers.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm Server Endpoints (not specific to one realm)', function (hooks) { diff --git a/packages/realm-server/tests/server-endpoints/run-command-endpoint-test.ts b/packages/realm-server/tests/server-endpoints/run-command-endpoint-test.ts index 244abb06f5..57360a8f0b 100644 --- a/packages/realm-server/tests/server-endpoints/run-command-endpoint-test.ts +++ b/packages/realm-server/tests/server-endpoints/run-command-endpoint-test.ts @@ -1,10 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { setupServerEndpointsTest, testRealmURL } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('/_run-command endpoint', function (hooks) { let context = setupServerEndpointsTest(hooks); diff --git a/packages/realm-server/tests/server-endpoints/screenshot-card-endpoint-test.ts b/packages/realm-server/tests/server-endpoints/screenshot-card-endpoint-test.ts index c01969ac5e..cfe28ac17b 100644 --- a/packages/realm-server/tests/server-endpoints/screenshot-card-endpoint-test.ts +++ b/packages/realm-server/tests/server-endpoints/screenshot-card-endpoint-test.ts @@ -1,10 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { setupServerEndpointsTest, testRealmURL } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('/_screenshot-card endpoint', function (hooks) { // Auth / body-validation only — the cardId never has to resolve, so use `blank`. let context = setupServerEndpointsTest(hooks, { fixture: 'blank' }); diff --git a/packages/realm-server/tests/server-endpoints/search-prerendered-test.ts b/packages/realm-server/tests/server-endpoints/search-prerendered-test.ts index 75e94f9ed7..1312eda453 100644 --- a/packages/realm-server/tests/server-endpoints/search-prerendered-test.ts +++ b/packages/realm-server/tests/server-endpoints/search-prerendered-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; import { basename, join } from 'path'; @@ -28,7 +29,7 @@ import { import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import type { RealmHttpServer as Server } from '../../server.ts'; -module(`server-endpoints/${basename(__filename)}`, function (_hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (_hooks) { module( 'Realm Server Endpoints | /_federated-search-prerendered', function (hooks) { diff --git a/packages/realm-server/tests/server-endpoints/search-test.ts b/packages/realm-server/tests/server-endpoints/search-test.ts index 8ea600b5d4..e11c9fb85e 100644 --- a/packages/realm-server/tests/server-endpoints/search-test.ts +++ b/packages/realm-server/tests/server-endpoints/search-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; import { basename, join } from 'path'; @@ -32,7 +33,7 @@ import { import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import type { RealmHttpServer as Server } from '../../server.ts'; -module(`server-endpoints/${basename(__filename)}`, function (_hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (_hooks) { module('Realm Server Endpoints | /_federated-search', function (hooks) { let testRealm: Realm; let secondaryRealm: Realm; diff --git a/packages/realm-server/tests/server-endpoints/search-v2-test.ts b/packages/realm-server/tests/server-endpoints/search-v2-test.ts index 2dcd00299e..7fd5a09e98 100644 --- a/packages/realm-server/tests/server-endpoints/search-v2-test.ts +++ b/packages/realm-server/tests/server-endpoints/search-v2-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import supertest from 'supertest'; import type { Test, SuperTest } from 'supertest'; import { basename, join } from 'path'; @@ -23,7 +24,7 @@ import { import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import type { RealmHttpServer as Server } from '../../server.ts'; -module(`server-endpoints/${basename(__filename)}`, function (_hooks) { +module(`server-endpoints/${basename(import.meta.filename)}`, function (_hooks) { module('Realm Server Endpoints | /_federated-search-v2', function (hooks) { let testRealm: Realm; let secondaryRealm: Realm; diff --git a/packages/realm-server/tests/server-endpoints/stripe-session-test.ts b/packages/realm-server/tests/server-endpoints/stripe-session-test.ts index ee1ae83ca4..97f5b23667 100644 --- a/packages/realm-server/tests/server-endpoints/stripe-session-test.ts +++ b/packages/realm-server/tests/server-endpoints/stripe-session-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { Test, SuperTest } from 'supertest'; import sinon from 'sinon'; @@ -14,7 +15,7 @@ import { import '@cardstack/runtime-common/helpers/code-equality-assertion'; import type { Realm } from '@cardstack/runtime-common'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Realm Server Endpoints (not specific to one realm)', function () { module('stripe session handler', function (hooks) { let createCustomerStub: sinon.SinonStub; diff --git a/packages/realm-server/tests/server-endpoints/stripe-webhook-test.ts b/packages/realm-server/tests/server-endpoints/stripe-webhook-test.ts index 71103cb70d..cd938ee9e4 100644 --- a/packages/realm-server/tests/server-endpoints/stripe-webhook-test.ts +++ b/packages/realm-server/tests/server-endpoints/stripe-webhook-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { Test, SuperTest } from 'supertest'; import type { Realm, User } from '@cardstack/runtime-common'; @@ -25,7 +26,7 @@ import type { } from 'https://cardstack.com/base/matrix-event'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Realm Server Endpoints (not specific to one realm)', function () { module('stripe webhook handler', function (hooks) { let testRealm: Realm; diff --git a/packages/realm-server/tests/server-endpoints/user-and-catalog-test.ts b/packages/realm-server/tests/server-endpoints/user-and-catalog-test.ts index 504744a305..666606717c 100644 --- a/packages/realm-server/tests/server-endpoints/user-and-catalog-test.ts +++ b/packages/realm-server/tests/server-endpoints/user-and-catalog-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { getUserByMatrixUserId } from '@cardstack/billing/billing-queries'; import { param, query } from '@cardstack/runtime-common'; @@ -8,7 +9,7 @@ import { resetCatalogRealms } from '../../handlers/handle-fetch-catalog-realms.t import { setupServerEndpointsTest, testRealmURL } from './helpers.ts'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module( 'Realm Server Endpoints (not specific to one realm)', function (hooks) { diff --git a/packages/realm-server/tests/server-endpoints/webhook-commands-test.ts b/packages/realm-server/tests/server-endpoints/webhook-commands-test.ts index ab61668fd5..1d37e44b91 100644 --- a/packages/realm-server/tests/server-endpoints/webhook-commands-test.ts +++ b/packages/realm-server/tests/server-endpoints/webhook-commands-test.ts @@ -1,11 +1,12 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; import { realmSecretSeed, insertUser } from '../helpers/index.ts'; import { param, query, uuidv4 } from '@cardstack/runtime-common'; import { setupServerEndpointsTest } from './helpers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Webhook Command Endpoints', function (hooks) { let context = setupServerEndpointsTest(hooks); diff --git a/packages/realm-server/tests/server-endpoints/webhook-receiver-test.ts b/packages/realm-server/tests/server-endpoints/webhook-receiver-test.ts index 9ce42993dd..4af17c6b8e 100644 --- a/packages/realm-server/tests/server-endpoints/webhook-receiver-test.ts +++ b/packages/realm-server/tests/server-endpoints/webhook-receiver-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { createHmac } from 'crypto'; import { createJWT as createRealmServerJWT } from '../../utils/jwt.ts'; @@ -10,7 +11,7 @@ import { extractBranchNameFromPayload, } from '../../handlers/webhook-filter-handlers.ts'; -module(`server-endpoints/${basename(__filename)}`, function () { +module(`server-endpoints/${basename(import.meta.filename)}`, function () { module('Webhook Receiver Endpoint', function (hooks) { let context = setupServerEndpointsTest(hooks); diff --git a/packages/realm-server/tests/session-room-queries-test.ts b/packages/realm-server/tests/session-room-queries-test.ts index be5fdff3b5..9bc2d561ca 100644 --- a/packages/realm-server/tests/session-room-queries-test.ts +++ b/packages/realm-server/tests/session-room-queries-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import type { PgAdapter } from '@cardstack/postgres'; import { insertPermissions } from '@cardstack/runtime-common'; @@ -10,7 +11,7 @@ import { } from '@cardstack/runtime-common/db-queries/session-room-queries'; import { setupDB, insertUser } from './helpers/index.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('fetchRealmSessionRooms', function (hooks) { let dbAdapter: PgAdapter; const realmURL = new URL('http://127.0.0.1:4444/test/'); diff --git a/packages/realm-server/tests/skip-query-backed-expansion-test.ts b/packages/realm-server/tests/skip-query-backed-expansion-test.ts index f92c9a0d41..820f432420 100644 --- a/packages/realm-server/tests/skip-query-backed-expansion-test.ts +++ b/packages/realm-server/tests/skip-query-backed-expansion-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { rri } from '@cardstack/runtime-common'; import type { LooseSingleCardDocument, Realm } from '@cardstack/runtime-common'; @@ -79,7 +80,7 @@ function buildFileSystem(): Record { return fs; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('skipQueryBackedExpansion', function (hooks) { let realm: Realm; diff --git a/packages/realm-server/tests/transpile-test.ts b/packages/realm-server/tests/transpile-test.ts index 757aecdf6a..adfea33b8b 100644 --- a/packages/realm-server/tests/transpile-test.ts +++ b/packages/realm-server/tests/transpile-test.ts @@ -1,9 +1,10 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { transpileJS } from '@cardstack/runtime-common/transpile'; import '@cardstack/runtime-common/helpers/code-equality-assertion'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Transpile', function () { test('can rewrite fetch()', async function (assert) { let transpiled = await transpileJS( diff --git a/packages/realm-server/tests/types-endpoint-test.ts b/packages/realm-server/tests/types-endpoint-test.ts index 014777b2fa..938d59f37b 100644 --- a/packages/realm-server/tests/types-endpoint-test.ts +++ b/packages/realm-server/tests/types-endpoint-test.ts @@ -1,9 +1,11 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { Test, SuperTest } from 'supertest'; import { join, basename } from 'path'; import type { RealmHttpServer as Server } from '../server.ts'; import type { DirResult } from 'tmp'; -import { copySync, ensureDirSync } from 'fs-extra'; +import fsExtra from 'fs-extra'; +const { copySync, ensureDirSync } = fsExtra; import type { Realm } from '@cardstack/runtime-common'; import type { QueuePublisher, QueueRunner } from '@cardstack/runtime-common'; import { @@ -24,7 +26,7 @@ import type { PgAdapter } from '@cardstack/postgres'; const testRealm2URL = new URL('http://127.0.0.1:4445/test/'); -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('Realm-specific Endpoints | GET _types', function (hooks) { let realmURL = new URL('http://127.0.0.1:4444/test/'); let testRealm: Realm; diff --git a/packages/realm-server/tests/unified-search-contracts-test.ts b/packages/realm-server/tests/unified-search-contracts-test.ts index 79a983207c..5203d65fcd 100644 --- a/packages/realm-server/tests/unified-search-contracts-test.ts +++ b/packages/realm-server/tests/unified-search-contracts-test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import { buildCssResource, @@ -105,7 +106,7 @@ function css( }; } -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('unified search contracts', function () { // --- predicates --------------------------------------------------------- diff --git a/packages/realm-server/tests/virtual-network-test.ts b/packages/realm-server/tests/virtual-network-test.ts index cd4a1fd402..0b3528315b 100644 --- a/packages/realm-server/tests/virtual-network-test.ts +++ b/packages/realm-server/tests/virtual-network-test.ts @@ -1,10 +1,11 @@ import type { ResponseWithNodeStream } from '@cardstack/runtime-common'; import { VirtualNetwork } from '@cardstack/runtime-common'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { basename } from 'path'; import '../setup-logger.ts'; -module(basename(__filename), function () { +module(basename(import.meta.filename), function () { module('virtual-network', function () { test('will respond with real (not virtual) url when handler makes a redirect', async function (assert) { let virtualNetwork = new VirtualNetwork(); diff --git a/packages/realm-server/tsconfig.json b/packages/realm-server/tsconfig.json index 76b1cf7c17..b9bf5a2f9e 100644 --- a/packages/realm-server/tsconfig.json +++ b/packages/realm-server/tsconfig.json @@ -25,7 +25,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/*"] }, diff --git a/packages/realm-server/utils/jwt.ts b/packages/realm-server/utils/jwt.ts index 4cb6ee6050..99c07b5036 100644 --- a/packages/realm-server/utils/jwt.ts +++ b/packages/realm-server/utils/jwt.ts @@ -2,12 +2,8 @@ import { AuthenticationError, AuthenticationErrorMessages, } from '@cardstack/runtime-common/router'; -import { - JsonWebTokenError, - sign, - TokenExpiredError, - verify, -} from 'jsonwebtoken'; +import jsonwebtoken from 'jsonwebtoken'; +const { JsonWebTokenError, sign, TokenExpiredError, verify } = jsonwebtoken; export interface RealmServerTokenClaim { user: string; diff --git a/packages/realm-server/worker-manager.ts b/packages/realm-server/worker-manager.ts index 41b0c05ce1..f03db7eaf1 100644 --- a/packages/realm-server/worker-manager.ts +++ b/packages/realm-server/worker-manager.ts @@ -12,7 +12,7 @@ import { writeSync } from 'node:fs'; // and throws *again*. Node delivers the throw inside an uncaughtException // handler as the next pending exception, so V8 hot-loops re-reporting it // (uv__run_check → CheckImmediate → InspectorConsoleCall → Error.stack -// formatting via ts-node) at ~100% CPU until the process is SIGKILLed — +// formatting) at ~100% CPU until the process is SIGKILLed — // CS-11084. Swallowing EPIPE at the stream level breaks the loop and // lets normal SIGTERM-driven shutdown finish. const swallowEpipe = (err: NodeJS.ErrnoException) => { @@ -55,7 +55,7 @@ import { } from '@cardstack/runtime-common'; import yargs from 'yargs'; import * as Sentry from '@sentry/node'; -import flattenDeep from 'lodash/flattenDeep'; +import { flattenDeep } from 'lodash-es'; import { spawn, type ChildProcess } from 'child_process'; import pluralize from 'pluralize'; import Koa from 'koa'; @@ -822,10 +822,9 @@ async function startWorker( urlMappings: [URL | string, URL][], ) { let worker = spawn( - 'ts-node', + 'node', [ - '--transpileOnly', - 'worker', + 'worker.ts', `--matrixURL='${matrixURL}'`, `--prerendererUrl=${prerendererUrl}`, `--priority=${priority}`, diff --git a/packages/realm-test-harness/.eslintrc.js b/packages/realm-test-harness/.eslintrc.cjs similarity index 100% rename from packages/realm-test-harness/.eslintrc.js rename to packages/realm-test-harness/.eslintrc.cjs diff --git a/packages/realm-test-harness/package.json b/packages/realm-test-harness/package.json index e90bec7432..0bf6501d1c 100644 --- a/packages/realm-test-harness/package.json +++ b/packages/realm-test-harness/package.json @@ -4,8 +4,14 @@ "version": "1.0.0", "license": "MIT", "description": "Spin up an isolated boxel realm-server stack (postgres + prerender + worker + realm-server) against a fixture realm directory. Used by integration tests, benchmarks, and CLI dev tools that need a hermetic realm.", + "type": "module", "main": "src/index.ts", "types": "src/index.ts", + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json", + "./*": "./src/*.ts" + }, "scripts": { "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", @@ -27,7 +33,6 @@ "jsonwebtoken": "catalog:", "pg": "catalog:", "puppeteer": "^25.0.2", - "ts-node": "^10.9.1", "typescript": "catalog:" }, "volta": { diff --git a/packages/realm-test-harness/src/isolated-realm-stack.ts b/packages/realm-test-harness/src/isolated-realm-stack.ts index 94a1e15114..a756ca88ec 100644 --- a/packages/realm-test-harness/src/isolated-realm-stack.ts +++ b/packages/realm-test-harness/src/isolated-realm-stack.ts @@ -840,8 +840,7 @@ export async function startIsolatedRealmStack({ }; let workerArgs = [ - '--transpileOnly', - 'worker-manager', + 'worker-manager.ts', `--port=${actualWorkerManagerPort}`, `--matrixURL=${context.matrixURL}`, `--prerendererUrl=${prerenderURL}`, @@ -893,7 +892,7 @@ export async function startIsolatedRealmStack({ attempt++; // Release the worker-manager port holder right before the child binds. await workerManagerPortInfo.releaseHolder(); - workerManager = spawn('ts-node', workerArgs, { + workerManager = spawn('node', workerArgs, { cwd: realmServerDir, env, stdio: managedProcessStdio, @@ -962,8 +961,7 @@ export async function startIsolatedRealmStack({ } let serverArgs = [ - '--transpileOnly', - 'main', + 'main.ts', `--port=${actualRealmServerPort}`, `--serverURL=${realmServerURL.href}`, `--matrixURL=${context.matrixURL}`, @@ -1013,7 +1011,7 @@ export async function startIsolatedRealmStack({ // Release the realm-server port holder right before the child binds. await realmServerPortInfo.releaseHolder(); let realmServerSpawnedAt = Date.now(); - let realmServer = spawn('ts-node', serverArgs, { + let realmServer = spawn('node', serverArgs, { cwd: realmServerDir, env, stdio: managedProcessStdio, diff --git a/packages/realm-test-harness/src/logger.ts b/packages/realm-test-harness/src/logger.ts index ee703f936e..ab3cb3eb58 100644 --- a/packages/realm-test-harness/src/logger.ts +++ b/packages/realm-test-harness/src/logger.ts @@ -1,3 +1,5 @@ +import { createRequire } from 'module'; + export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'none'; type AcceptedLogLevel = LogLevel | 'silent'; @@ -21,8 +23,9 @@ type LoggerFactory = { }; // The published package does not ship TypeScript declarations, so we keep the -// typing local here instead of maintaining an ambient .d.ts shim. -// eslint-disable-next-line @typescript-eslint/no-var-requires +// typing local here instead of maintaining an ambient .d.ts shim. It's also +// CJS, so under native ESM we load it through createRequire. +const require = createRequire(import.meta.url); const createLogger = require('@cardstack/logger') as LoggerFactory; export function configureLogger(serializedLogLevels: string): void { diff --git a/packages/realm-test-harness/src/support-services.ts b/packages/realm-test-harness/src/support-services.ts index b3362fc1df..2a6d926e4f 100644 --- a/packages/realm-test-harness/src/support-services.ts +++ b/packages/realm-test-harness/src/support-services.ts @@ -644,8 +644,8 @@ async function attemptStartHarnessPrerenderServer(options: { await portReservation.release(); } let child = spawn( - 'ts-node', - ['--transpileOnly', 'prerender/prerender-server', `--port=${port}`], + 'node', + ['prerender/prerender-server.ts', `--port=${port}`], { cwd: realmServerDir, stdio: ['pipe', 'pipe', 'pipe'], diff --git a/packages/runtime-common/ai/matrix-utils.ts b/packages/runtime-common/ai/matrix-utils.ts index 68f52829da..0d5e0f3d46 100644 --- a/packages/runtime-common/ai/matrix-utils.ts +++ b/packages/runtime-common/ai/matrix-utils.ts @@ -21,7 +21,7 @@ import { import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/base/matrix-event'; import type { MatrixEvent } from 'matrix-js-sdk'; import type { PromptParts } from './types.ts'; -import { encodeUri } from 'matrix-js-sdk/lib/utils'; +import { encodeUri } from 'matrix-js-sdk/lib/utils.js'; import type { SerializedFileDef } from 'https://cardstack.com/base/file-api'; import { isTextBasedContentType } from './modality.ts'; diff --git a/packages/runtime-common/cached-fetch.ts b/packages/runtime-common/cached-fetch.ts index ae622fa5f1..774e524749 100644 --- a/packages/runtime-common/cached-fetch.ts +++ b/packages/runtime-common/cached-fetch.ts @@ -1,4 +1,4 @@ -import merge from 'lodash/merge'; +import { merge } from 'lodash-es'; import { isNode } from './index.ts'; diff --git a/packages/runtime-common/catalog.ts b/packages/runtime-common/catalog.ts index da78af0b9e..3263b0338b 100644 --- a/packages/runtime-common/catalog.ts +++ b/packages/runtime-common/catalog.ts @@ -1,4 +1,6 @@ -import { isEqual, uniqWith, kebabCase } from 'lodash'; +import { isEqual } from 'lodash-es'; +import { uniqWith } from 'lodash-es'; +import { kebabCase } from 'lodash-es'; import { v4 as uuidv4 } from 'uuid'; import type { Spec } from 'https://cardstack.com/base/spec'; import type { CardDef } from 'https://cardstack.com/base/card-api'; diff --git a/packages/runtime-common/commands.ts b/packages/runtime-common/commands.ts index de79ce96c4..e5b86fa0e9 100644 --- a/packages/runtime-common/commands.ts +++ b/packages/runtime-common/commands.ts @@ -10,7 +10,7 @@ import type { CardDefConstructor } from 'https://cardstack.com/base/card-api'; import type { AttributesSchema, CardSchema } from './helpers/ai.ts'; import { generateJsonSchemaForCardType } from './helpers/ai.ts'; import { simpleHash } from './utils.ts'; -import type { EncodedCommandRequest } from '../base/matrix-event'; +import type { EncodedCommandRequest } from '../base/matrix-event.gts'; export interface CommandRequest { id: string; diff --git a/packages/runtime-common/etc/eslint/package.json b/packages/runtime-common/etc/eslint/package.json new file mode 100644 index 0000000000..5bbefffbab --- /dev/null +++ b/packages/runtime-common/etc/eslint/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/packages/runtime-common/expression.ts b/packages/runtime-common/expression.ts index f24bb363eb..c2f0a56d56 100644 --- a/packages/runtime-common/expression.ts +++ b/packages/runtime-common/expression.ts @@ -1,7 +1,7 @@ import type * as JSONTypes from 'json-typescript'; -import isPlainObject from 'lodash/isPlainObject'; +import { isPlainObject } from 'lodash-es'; import stringify from 'safe-stable-stringify'; -import flattenDeep from 'lodash/flattenDeep'; +import { flattenDeep } from 'lodash-es'; import type { CodeRef, DBAdapter, TypeCoercion } from './index.ts'; diff --git a/packages/runtime-common/fetch-node.ts b/packages/runtime-common/fetch-node.ts index 9872b7114c..59568e41b9 100644 --- a/packages/runtime-common/fetch-node.ts +++ b/packages/runtime-common/fetch-node.ts @@ -1,3 +1,9 @@ +import { createRequire } from 'node:module'; + +// Node-only module; resolved via the package `#fetch` condition. Browsers get +// fetch-browser.ts instead, so this CJS bridge never reaches a bundle. +const require = createRequire(import.meta.url); + /** * Creates a fetch implementation that's appropriate for the current environment. * In Node.js, it enhances localhost subdomain resolution using Undici agent. diff --git a/packages/runtime-common/helpers/code-equality-assertion.ts b/packages/runtime-common/helpers/code-equality-assertion.ts index c09cf99074..0d9df2acbc 100644 --- a/packages/runtime-common/helpers/code-equality-assertion.ts +++ b/packages/runtime-common/helpers/code-equality-assertion.ts @@ -10,7 +10,7 @@ import classPropertiesPlugin from '@babel/plugin-syntax-class-properties'; //@ts-ignore unsure where these types live import typescriptPlugin from '@babel/plugin-syntax-typescript'; -import * as QUnit from 'qunit'; +import QUnit from 'qunit'; import { gjsToPlaceholderJS } from '../module-syntax.ts'; declare global { diff --git a/packages/runtime-common/ignore.ts b/packages/runtime-common/ignore.ts new file mode 100644 index 0000000000..34ea84df19 --- /dev/null +++ b/packages/runtime-common/ignore.ts @@ -0,0 +1,11 @@ +// `ignore` ships no `types`/`exports` fields, so its default export resolves +// inconsistently across toolchains: nodenext types it as a non-callable +// namespace, and esbuild (bundling downstream consumers) won't synthesize a +// default at all. Import the namespace and read the CJS default off it — that's +// the callable factory under native Node, esbuild, and Vite alike. +import type { Ignore } from 'ignore'; +import * as ignoreNamespace from 'ignore'; + +export const ignore = ((ignoreNamespace as { default?: unknown }).default ?? + ignoreNamespace) as unknown as (options?: object) => Ignore; +export type { Ignore }; diff --git a/packages/runtime-common/index-query-engine.ts b/packages/runtime-common/index-query-engine.ts index ff5bb4a7ed..c505104fc3 100644 --- a/packages/runtime-common/index-query-engine.ts +++ b/packages/runtime-common/index-query-engine.ts @@ -1,5 +1,5 @@ import type * as JSONTypes from 'json-typescript'; -import flatten from 'lodash/flatten'; +import { flatten } from 'lodash-es'; import stringify from 'safe-stable-stringify'; import type { ResolvedCodeRef } from './index.ts'; import type { RealmResourceIdentifier } from './realm-identifiers.ts'; diff --git a/packages/runtime-common/index-runner.ts b/packages/runtime-common/index-runner.ts index 91faa4ec3b..334c0239d5 100644 --- a/packages/runtime-common/index-runner.ts +++ b/packages/runtime-common/index-runner.ts @@ -1,4 +1,4 @@ -import ignore, { type Ignore } from 'ignore'; +import { ignore, type Ignore } from './ignore.ts'; // Isomorphic UUID — works in both Node and the browser (host tests // instantiate IndexRunner inside a Chrome tab, so Node's built-in // `crypto.randomUUID` is not available). diff --git a/packages/runtime-common/index-runner/card-indexer.ts b/packages/runtime-common/index-runner/card-indexer.ts index 509cd2cae3..4e4ff483d6 100644 --- a/packages/runtime-common/index-runner/card-indexer.ts +++ b/packages/runtime-common/index-runner/card-indexer.ts @@ -1,4 +1,4 @@ -import merge from 'lodash/merge'; +import { merge } from 'lodash-es'; import { jobIdentity, diff --git a/packages/runtime-common/index-runner/discover-invalidations.ts b/packages/runtime-common/index-runner/discover-invalidations.ts index 42bac26343..4cc829e870 100644 --- a/packages/runtime-common/index-runner/discover-invalidations.ts +++ b/packages/runtime-common/index-runner/discover-invalidations.ts @@ -1,4 +1,4 @@ -import ignore, { type Ignore } from 'ignore'; +import { ignore, type Ignore } from '../ignore.ts'; import { jobIdentity, diff --git a/packages/runtime-common/index-writer.ts b/packages/runtime-common/index-writer.ts index 0476b683b5..04694ed667 100644 --- a/packages/runtime-common/index-writer.ts +++ b/packages/runtime-common/index-writer.ts @@ -1,5 +1,5 @@ -import flatten from 'lodash/flatten'; -import flattenDeep from 'lodash/flattenDeep'; +import { flatten } from 'lodash-es'; +import { flattenDeep } from 'lodash-es'; import { type CardResource, type JobInfo, diff --git a/packages/runtime-common/instance-filter-matcher.ts b/packages/runtime-common/instance-filter-matcher.ts index 419f4f1744..9b707d0de8 100644 --- a/packages/runtime-common/instance-filter-matcher.ts +++ b/packages/runtime-common/instance-filter-matcher.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { getField, identifyCard } from './code-ref.ts'; import { diff --git a/packages/runtime-common/jobs/indexing.ts b/packages/runtime-common/jobs/indexing.ts index 6b2634c236..abe0b73dc9 100644 --- a/packages/runtime-common/jobs/indexing.ts +++ b/packages/runtime-common/jobs/indexing.ts @@ -7,7 +7,7 @@ import type { } from '../tasks/indexer.ts'; import type { PgPrimitive } from '../expression.ts'; import { v4 as uuidv4 } from '@lukeed/uuid'; -import isObjectLike from 'lodash/isObjectLike'; +import { isObjectLike } from 'lodash-es'; export const INCREMENTAL_INDEX_JOB_TIMEOUT_SEC = 10 * 60; diff --git a/packages/runtime-common/lint/submission-lint.ts b/packages/runtime-common/lint/submission-lint.ts index c1b66de3d3..52d19ddcca 100644 --- a/packages/runtime-common/lint/submission-lint.ts +++ b/packages/runtime-common/lint/submission-lint.ts @@ -12,7 +12,7 @@ if (typeof (globalThis as any).document !== 'undefined') { const log = logger('submission-lint'); -const REPO_ROOT = path.resolve(__dirname, '..', '..', '..'); +const REPO_ROOT = path.resolve(import.meta.dirname, '..', '..', '..'); const CATALOG_DIR = path.join(REPO_ROOT, 'packages', 'catalog'); const HOST_DIR = path.join(REPO_ROOT, 'packages', 'host'); export const SUBMISSIONS_TEMP_ROOT = path.join( diff --git a/packages/runtime-common/loader.ts b/packages/runtime-common/loader.ts index 8282d0b23b..c1642f7df5 100644 --- a/packages/runtime-common/loader.ts +++ b/packages/runtime-common/loader.ts @@ -4,7 +4,7 @@ import { cachedFetch, type MaybeCachedResponse } from './cached-fetch.ts'; import { executableExtensions, logger } from './index.ts'; import { CardError, iconNotFoundMessage } from './error.ts'; -import flatMap from 'lodash/flatMap'; +import { flatMap } from 'lodash-es'; import { shouldTrackRuntimeModuleGraph, trackRuntimeModuleDependency, diff --git a/packages/runtime-common/merge-relationships.ts b/packages/runtime-common/merge-relationships.ts index 31207b2745..2136af212a 100644 --- a/packages/runtime-common/merge-relationships.ts +++ b/packages/runtime-common/merge-relationships.ts @@ -1,6 +1,6 @@ import type { LooseCardResource, Relationship } from './index.ts'; import { relationshipEntries } from './relationship-utils.ts'; -import mergeWith from 'lodash/mergeWith'; +import { mergeWith } from 'lodash-es'; export function mergeRelationships( relData: LooseCardResource['relationships'], diff --git a/packages/runtime-common/module-syntax.ts b/packages/runtime-common/module-syntax.ts index 42d6f273d9..ff7ee2b29d 100644 --- a/packages/runtime-common/module-syntax.ts +++ b/packages/runtime-common/module-syntax.ts @@ -17,7 +17,7 @@ import type { Options as RemoveOptions } from './remove-field-plugin.ts'; import { removeFieldPlugin } from './remove-field-plugin.ts'; import { ImportUtil } from 'babel-import-util'; import camelCase from 'camelcase'; -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import * as ContentTag from 'content-tag'; import { diff --git a/packages/runtime-common/package.json b/packages/runtime-common/package.json index c0cb2b6f23..f1f8e47891 100644 --- a/packages/runtime-common/package.json +++ b/packages/runtime-common/package.json @@ -2,6 +2,7 @@ "name": "@cardstack/runtime-common", "version": "1.0.0", "license": "MIT", + "type": "module", "imports": { "#lint-task": { "node": "./tasks/lint.ts", @@ -12,6 +13,16 @@ "browser": "./fetch-browser.ts" } }, + "exports": { + ".": "./index.ts", + "./package.json": "./package.json", + "./ai": "./ai/index.ts", + "./amd-transpile": "./amd-transpile/index.ts", + "./helpers": "./helpers/index.ts", + "./serializers": "./serializers/index.ts", + "./tasks": "./tasks/index.ts", + "./*": "./*.ts" + }, "dependencies": { "@aws-crypto/sha256-js": "catalog:", "@babel/generator": "catalog:", @@ -35,7 +46,7 @@ "@types/diff": "catalog:", "@types/eslint": "catalog:", "@types/flat": "catalog:", - "@types/lodash": "catalog:", + "@types/lodash-es": "catalog:", "@types/pluralize": "catalog:", "@types/qs": "catalog:", "@types/uuid": "catalog:", @@ -59,7 +70,7 @@ "ignore": "catalog:", "js-string-escape": "catalog:", "json-typescript": "catalog:", - "lodash": "catalog:", + "lodash-es": "catalog:", "loglevel": "catalog:", "magic-string": "catalog:", "marked": "catalog:", @@ -96,8 +107,7 @@ "monaco-editor": "catalog:", "statuses": "catalog:", "@glint/ember-tsc": "catalog:", - "es-module-lexer": "^1.7.0", - "ts-node": "^10.9.1" + "es-module-lexer": "^1.7.0" }, "peerDependencies": { "@babel/core": "catalog:", @@ -108,10 +118,10 @@ "lint:js": "eslint . ", "lint:js:fix": "eslint . --report-unused-disable-directives --fix", "lint:types": "ember-tsc --noEmit", - "bench:amd:prep": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-amd/prepare-fixtures.ts", - "bench:amd": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-amd/bench.ts", - "bench:amd:check": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-amd/check.ts", - "bench:amd:check:trip-test": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-amd/trip-test.ts", - "bench:amd:trip-drill": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/bench-amd/trip-drill.ts" + "bench:amd:prep": "NODE_NO_WARNINGS=1 node scripts/bench-amd/prepare-fixtures.ts", + "bench:amd": "NODE_NO_WARNINGS=1 node scripts/bench-amd/bench.ts", + "bench:amd:check": "NODE_NO_WARNINGS=1 node scripts/bench-amd/check.ts", + "bench:amd:check:trip-test": "NODE_NO_WARNINGS=1 node scripts/bench-amd/trip-test.ts", + "bench:amd:trip-drill": "NODE_NO_WARNINGS=1 node scripts/bench-amd/trip-drill.ts" } } diff --git a/packages/runtime-common/query.ts b/packages/runtime-common/query.ts index d6cecadb62..613a5691f7 100644 --- a/packages/runtime-common/query.ts +++ b/packages/runtime-common/query.ts @@ -1,4 +1,4 @@ -import isEqual from 'lodash/isEqual'; +import { isEqual } from 'lodash-es'; import { assertJSONValue, assertJSONPrimitive } from './json-validation.ts'; import qs from 'qs'; diff --git a/packages/runtime-common/realm-index-query-engine.ts b/packages/runtime-common/realm-index-query-engine.ts index 9609957402..7f4f2e3a0a 100644 --- a/packages/runtime-common/realm-index-query-engine.ts +++ b/packages/runtime-common/realm-index-query-engine.ts @@ -1,5 +1,5 @@ import { isScopedCSSRequest } from './scoped-css.ts'; -import cloneDeep from 'lodash/cloneDeep'; +import { cloneDeep } from 'lodash-es'; import { SupportedMimeType, isJsonContentType, @@ -25,7 +25,7 @@ import { visitInstanceURLs, maybeRelativeReference, codeRefFromInternalKey, -} from '.'; +} from './index.ts'; import type { Realm } from './realm.ts'; import type { VirtualNetwork } from './virtual-network.ts'; import { FILE_META_RESERVED_KEYS } from './realm.ts'; diff --git a/packages/runtime-common/realm-index-updater.ts b/packages/runtime-common/realm-index-updater.ts index 4f01509d32..3dc4f83695 100644 --- a/packages/runtime-common/realm-index-updater.ts +++ b/packages/runtime-common/realm-index-updater.ts @@ -10,7 +10,7 @@ import { type QueuePublisher, type CopyArgs, type CopyResult, -} from '.'; +} from './index.ts'; import { INCREMENTAL_INDEX_JOB_TIMEOUT_SEC, makeIncrementalArgsWithCallerMetadata, @@ -24,7 +24,7 @@ import type { } from './tasks/indexer.ts'; import type { Realm } from './realm.ts'; import { RealmPaths } from './paths.ts'; -import ignore, { type Ignore } from 'ignore'; +import { ignore, type Ignore } from './ignore.ts'; export class RealmIndexUpdater { #realm: Realm; diff --git a/packages/runtime-common/realm.ts b/packages/runtime-common/realm.ts index 9d8d40e287..663444083e 100644 --- a/packages/runtime-common/realm.ts +++ b/packages/runtime-common/realm.ts @@ -107,11 +107,11 @@ import { } from './index.ts'; import type { FromScratchResult } from './tasks/indexer.ts'; import { isCodeRef, visitModuleDeps } from './code-ref.ts'; -import merge from 'lodash/merge'; -import mergeWith from 'lodash/mergeWith'; -import cloneDeep from 'lodash/cloneDeep'; -import isEqual from 'lodash/isEqual'; -import isPlainObject from 'lodash/isPlainObject'; +import { merge } from 'lodash-es'; +import { mergeWith } from 'lodash-es'; +import { cloneDeep } from 'lodash-es'; +import { isEqual } from 'lodash-es'; +import { isPlainObject } from 'lodash-es'; import { z } from 'zod'; import { inferContentType } from './infer-content-type.ts'; import { diff --git a/packages/runtime-common/scripts/bench-amd/bench.ts b/packages/runtime-common/scripts/bench-amd/bench.ts index 1e9c7dc2e9..0259392a8c 100644 --- a/packages/runtime-common/scripts/bench-amd/bench.ts +++ b/packages/runtime-common/scripts/bench-amd/bench.ts @@ -12,6 +12,7 @@ import { readdirSync, readFileSync } from 'node:fs'; import path from 'node:path'; import { performance } from 'node:perf_hooks'; +import { pathToFileURL } from 'node:url'; import { candidatesDir, fixturesDir } from './paths.ts'; @@ -69,9 +70,8 @@ export async function loadCandidates(): Promise { .sort(); const candidates: Candidate[] = []; for (const f of candidateFiles) { - // ts-node intercepts `.ts` so `require()` returns the compiled module. - // eslint-disable-next-line @typescript-eslint/no-var-requires - const mod = require(path.join(candidatesDir, f)); + // Native Node strips types from the `.ts` candidate on import. + const mod = await import(pathToFileURL(path.join(candidatesDir, f)).href); if (typeof mod.transform === 'function') { candidates.push({ name: mod.name ?? f.replace(/\.ts$/, ''), @@ -229,7 +229,7 @@ async function main() { } } -if (require.main === module) { +if (import.meta.main) { main().catch((err) => { console.error(err); process.exit(1); diff --git a/packages/runtime-common/scripts/bench-amd/paths.ts b/packages/runtime-common/scripts/bench-amd/paths.ts index 5acc8aa4ed..d39de9198b 100644 --- a/packages/runtime-common/scripts/bench-amd/paths.ts +++ b/packages/runtime-common/scripts/bench-amd/paths.ts @@ -3,7 +3,7 @@ // stay in lockstep when the layout changes. import path from 'node:path'; -export const repoRoot = path.resolve(__dirname, '../../../..'); +export const repoRoot = path.resolve(import.meta.dirname, '../../../..'); // Committed, hermetic fixtures. Generated by `pnpm bench:amd:prep`. The // bench gate reads only from this path so that edits to card sources @@ -13,7 +13,7 @@ export const fixturesDir = path.join( 'bench-fixtures/runtime-common/amd-transpile', ); -export const candidatesDir = path.join(__dirname, 'candidates'); +export const candidatesDir = path.join(import.meta.dirname, 'candidates'); // `BENCH_AMD_BASELINE_OVERRIDE` redirects the gate to read a synthetic // baseline from somewhere else on disk. Used by the trip-test to prove @@ -21,4 +21,4 @@ export const candidatesDir = path.join(__dirname, 'candidates'); // `baseline.json`. Default unchanged. export const baselinePath = process.env.BENCH_AMD_BASELINE_OVERRIDE ?? - path.join(__dirname, 'baseline.json'); + path.join(import.meta.dirname, 'baseline.json'); diff --git a/packages/runtime-common/scripts/bench-amd/prepare-fixtures.ts b/packages/runtime-common/scripts/bench-amd/prepare-fixtures.ts index 7d07b2db91..791ad206bf 100644 --- a/packages/runtime-common/scripts/bench-amd/prepare-fixtures.ts +++ b/packages/runtime-common/scripts/bench-amd/prepare-fixtures.ts @@ -35,11 +35,9 @@ const fixtures: { name: string; file: string }[] = [ const ContentTag = await import('content-tag'); (globalThis as any).ContentTagGlobal = ContentTag; - // transpile.ts is in the runtime-common package; ts-node intercepts the - // `.ts` extension via require. - const { transpileJS } = - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('../../transpile') as typeof import('../../transpile.ts'); + // transpile.ts is in the runtime-common package; native Node strips its + // types on import. + const { transpileJS } = await import('../../transpile.ts'); mkdirSync(fixturesDir, { recursive: true }); diff --git a/packages/runtime-common/tasks/lint.ts b/packages/runtime-common/tasks/lint.ts index fb20f0ea9d..91eff2f1b6 100644 --- a/packages/runtime-common/tasks/lint.ts +++ b/packages/runtime-common/tasks/lint.ts @@ -58,9 +58,9 @@ const MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024; // --------------------------------------------------------------------------- // runtime-common lives at /packages/runtime-common; host is its sibling. -const HOST_PKG = resolve(__dirname, '..', '..', 'host'); +const HOST_PKG = resolve(import.meta.dirname, '..', '..', 'host'); const HOST_ESLINTRC = resolve(HOST_PKG, '.eslintrc.js'); -const REPO_ROOT = resolve(__dirname, '..', '..', '..'); +const REPO_ROOT = resolve(import.meta.dirname, '..', '..', '..'); const LINT_ANCHOR = resolve(HOST_PKG, '__realm__'); // pnpm doesn't hoist transitive deps, so parsers/plugins host references by diff --git a/packages/runtime-common/tsconfig.json b/packages/runtime-common/tsconfig.json index 31b1b583fd..e02d04c8e9 100644 --- a/packages/runtime-common/tsconfig.json +++ b/packages/runtime-common/tsconfig.json @@ -24,7 +24,12 @@ "experimentalDecorators": true, "skipLibCheck": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-ui": ["../boxel-ui/addon/declarations"], "@cardstack/boxel-ui/*": ["../boxel-ui/addon/declarations/*"] }, diff --git a/packages/runtime-common/unified-search.ts b/packages/runtime-common/unified-search.ts index cdba6fc4ea..e12e3648a9 100644 --- a/packages/runtime-common/unified-search.ts +++ b/packages/runtime-common/unified-search.ts @@ -1,4 +1,4 @@ -import { isScopedCSSRequest } from './scoped-css'; +import { isScopedCSSRequest } from './scoped-css.ts'; import { cssResourceId, type CardResource, @@ -6,9 +6,9 @@ import { type FileMetaResource, type RenderedHtmlResource, type Saved, -} from './resource-types'; -import type { CodeRef } from './code-ref'; -import type { RealmResourceIdentifier } from './realm-identifiers'; +} from './resource-types.ts'; +import type { CodeRef } from './code-ref.ts'; +import type { RealmResourceIdentifier } from './realm-identifiers.ts'; // Builders for the unified-search resources. The realm-server's result mapper // runs these per row when applying the prefer-HTML resolution policy; keeping diff --git a/packages/runtime-common/url-signature.ts b/packages/runtime-common/url-signature.ts index f0d9e839d8..a8a81d0ba6 100644 --- a/packages/runtime-common/url-signature.ts +++ b/packages/runtime-common/url-signature.ts @@ -7,6 +7,11 @@ * - urlPath is the pathname + search params (without the sig param) */ +// HMAC for the Node-only signing path below. Bare `crypto` resolves to the +// Node builtin under native Node, and to crypto-browserify in the host build +// via its Vite alias (host browsers only reach the Web Crypto path above). +import { createHmac } from 'crypto'; + // Browser implementation using Web Crypto API export async function createURLSignature( token: string, @@ -39,16 +44,11 @@ export async function createURLSignature( // Node.js implementation export function createURLSignatureSync(token: string, url: URL): string { - // Dynamic import to avoid issues in browser - // eslint-disable-next-line @typescript-eslint/no-var-requires - let crypto = require('crypto'); - let urlForSigning = new URL(url.href); urlForSigning.searchParams.delete('sig'); let message = urlForSigning.pathname + urlForSigning.search; - let signature = crypto - .createHmac('sha256', token) + let signature = createHmac('sha256', token) .update(message) .digest('base64url'); diff --git a/packages/runtime-common/worker.ts b/packages/runtime-common/worker.ts index 36067465e1..54c4891baf 100644 --- a/packages/runtime-common/worker.ts +++ b/packages/runtime-common/worker.ts @@ -24,7 +24,7 @@ import { type DBAdapter, type RealmPermissions, CachingDefinitionLookup, -} from '.'; +} from './index.ts'; import { MatrixClient } from './matrix-client.ts'; import * as Tasks from './tasks/index.ts'; import type { WorkerArgs, TaskArgs } from './tasks/index.ts'; diff --git a/packages/software-factory/.eslintrc.js b/packages/software-factory/.eslintrc.cjs similarity index 100% rename from packages/software-factory/.eslintrc.js rename to packages/software-factory/.eslintrc.cjs diff --git a/packages/software-factory/docs/phase-1-plan.md b/packages/software-factory/docs/phase-1-plan.md index babf239dfb..0544ba2ff2 100644 --- a/packages/software-factory/docs/phase-1-plan.md +++ b/packages/software-factory/docs/phase-1-plan.md @@ -443,10 +443,10 @@ The first version should support: Add a script: ```json -"factory:go": "ts-node --transpileOnly src/cli/factory-entrypoint.ts" +"factory:go": "node src/cli/factory-entrypoint.ts" ``` -For software-factory CLI entrypoints, favor `ts-node --transpileOnly` over `tsx`. +For software-factory CLI entrypoints, run the TypeScript directly with `node` (native type stripping) rather than a separate runner like `tsx`. - it matches the execution model already used by `realm-server` - it avoids the decorator/runtime incompatibilities we hit when `tsx` imports `runtime-common` auth code diff --git a/packages/software-factory/package.json b/packages/software-factory/package.json index f8aa4b4f2a..c30d4cf1cf 100644 --- a/packages/software-factory/package.json +++ b/packages/software-factory/package.json @@ -1,18 +1,19 @@ { "name": "@cardstack/software-factory", + "type": "module", "private": true, "version": "1.0.0", "license": "MIT", "description": "Software Factory workspace package", "scripts": { - "boxel:search": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/boxel-search.ts", - "cache:prepare": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/cache-realm.ts", - "factory:go": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/factory-entrypoint.ts", - "smoke:context": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/factory-context-smoke.ts", - "smoke:prompt": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/factory-prompt-smoke.ts", - "smoke:skill": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/factory-skill-smoke.ts", - "smoke:issue-loop": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/issue-loop-smoke.ts", - "smoke:tools": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/factory-tools-smoke.ts", + "boxel:search": "NODE_NO_WARNINGS=1 node scripts/boxel-search.ts", + "cache:prepare": "NODE_NO_WARNINGS=1 node src/cli/cache-realm.ts", + "factory:go": "NODE_NO_WARNINGS=1 node src/cli/factory-entrypoint.ts", + "smoke:context": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/factory-context-smoke.ts", + "smoke:prompt": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/factory-prompt-smoke.ts", + "smoke:skill": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/factory-skill-smoke.ts", + "smoke:issue-loop": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/issue-loop-smoke.ts", + "smoke:tools": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/factory-tools-smoke.ts", "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", "lint:js": "eslint . --report-unused-disable-directives --cache", @@ -20,17 +21,17 @@ "lint:format": "prettier --check .", "lint:format:fix": "prettier --write .", "lint:types": "ember-tsc --noEmit", - "serve:realm": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/serve-realm.ts", - "serve:support": "NODE_NO_WARNINGS=1 ts-node --transpileOnly src/cli/serve-support.ts", - "smoke:factory-scenarios": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/smoke-test-factory-scenarios.ts", - "smoke:test-realm": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/smoke-tests/smoke-test-realm.ts", - "test": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/test.ts", - "test:all": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/test.ts", - "test:node": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/test.ts --node-only", - "test:playwright": "playwright test", - "test:playwright:headed": "playwright test --headed", - "test:playwright:shard": "playwright test --shard", - "test:realm": "NODE_NO_WARNINGS=1 ts-node --transpileOnly scripts/run-realm-tests.ts" + "serve:realm": "NODE_NO_WARNINGS=1 node src/cli/serve-realm.ts", + "serve:support": "NODE_NO_WARNINGS=1 node src/cli/serve-support.ts", + "smoke:factory-scenarios": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/smoke-test-factory-scenarios.ts", + "smoke:test-realm": "NODE_NO_WARNINGS=1 node scripts/smoke-tests/smoke-test-realm.ts", + "test": "NODE_NO_WARNINGS=1 node scripts/test.ts", + "test:all": "NODE_NO_WARNINGS=1 node scripts/test.ts", + "test:node": "pnpm --filter @cardstack/boxel-cli build:api && NODE_NO_WARNINGS=1 node scripts/test.ts --node-only", + "test:playwright": "pnpm --filter @cardstack/boxel-cli build:api && playwright test", + "test:playwright:headed": "pnpm --filter @cardstack/boxel-cli build:api && playwright test --headed", + "test:playwright:shard": "pnpm --filter @cardstack/boxel-cli build:api && playwright test --shard", + "test:realm": "NODE_NO_WARNINGS=1 node scripts/run-realm-tests.ts" }, "devDependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.114", @@ -69,7 +70,6 @@ "qunit": "catalog:", "qunit-dom": "catalog:", "tmp": "catalog:", - "ts-node": "^10.9.1", "typescript": "catalog:", "zod": "^4.0.17", "zod-from-json-schema": "^0.5.2" diff --git a/packages/software-factory/playwright.global-setup.ts b/packages/software-factory/playwright.global-setup.ts index 3f9f88298c..786c442b38 100644 --- a/packages/software-factory/playwright.global-setup.ts +++ b/packages/software-factory/playwright.global-setup.ts @@ -10,8 +10,8 @@ import { type PreparedTemplateMetadata, } from '@cardstack/realm-test-harness'; -const packageRoot = resolve(__dirname); -const tsNodeBin = resolve(packageRoot, 'node_modules', '.bin', 'ts-node'); +const packageRoot = resolve(import.meta.dirname); +const nodeBin = process.execPath; const configuredRealmDir = resolve( packageRoot, process.env.TEST_HARNESS_REALM_DIR ?? 'test-fixtures/darkfactory-adopter', @@ -258,20 +258,16 @@ export default async function globalSetup() { supportLog.debug(`starting serve:support for realm ${realmDir}`); let logs = ''; - let child = spawn( - tsNodeBin, - ['--transpileOnly', 'src/cli/serve-support.ts', realmDir], - { - cwd: packageRoot, - detached: true, - stdio: ['ignore', 'pipe', 'pipe'], - env: { - ...process.env, - NODE_NO_WARNINGS: '1', - TEST_HARNESS_SUPPORT_METADATA_FILE: metadataFile, - }, + let child = spawn(nodeBin, ['src/cli/serve-support.ts', realmDir], { + cwd: packageRoot, + detached: true, + stdio: ['ignore', 'pipe', 'pipe'], + env: { + ...process.env, + NODE_NO_WARNINGS: '1', + TEST_HARNESS_SUPPORT_METADATA_FILE: metadataFile, }, - ); + }); mirrorChildOutput( child, diff --git a/packages/software-factory/scripts/canusetool-fix-verify.ts b/packages/software-factory/scripts/canusetool-fix-verify.ts index 6984220499..750ce79fef 100644 --- a/packages/software-factory/scripts/canusetool-fix-verify.ts +++ b/packages/software-factory/scripts/canusetool-fix-verify.ts @@ -8,7 +8,7 @@ * Faster than the full matrix repro — two probes instead of sixteen. * * Usage: - * pnpm exec ts-node --transpileOnly scripts/canusetool-fix-verify.ts + * pnpm exec node scripts/canusetool-fix-verify.ts */ import { existsSync, mkdtempSync, realpathSync, rmSync } from 'node:fs'; diff --git a/packages/software-factory/scripts/canusetool-repro.ts b/packages/software-factory/scripts/canusetool-repro.ts index 8852950eed..6e78564919 100644 --- a/packages/software-factory/scripts/canusetool-repro.ts +++ b/packages/software-factory/scripts/canusetool-repro.ts @@ -26,7 +26,7 @@ * out-of-workspace Write is rejected. * * Usage: - * pnpm exec ts-node --transpileOnly scripts/canusetool-repro.ts + * pnpm exec node scripts/canusetool-repro.ts * * (Auth: works with whatever `claude login` already set up; no * ANTHROPIC_API_KEY needed.) diff --git a/packages/software-factory/scripts/smoke-tests/smoke-test-factory-scenarios.ts b/packages/software-factory/scripts/smoke-tests/smoke-test-factory-scenarios.ts index b15b1aa375..6b8d12d68b 100644 --- a/packages/software-factory/scripts/smoke-tests/smoke-test-factory-scenarios.ts +++ b/packages/software-factory/scripts/smoke-tests/smoke-test-factory-scenarios.ts @@ -39,7 +39,7 @@ import { logger } from '../../src/logger.ts'; // --------------------------------------------------------------------------- let log = logger('smoke-factory-scenarios'); -let packageRoot = resolve(__dirname, '../..'); +let packageRoot = resolve(import.meta.dirname, '../..'); const DEFAULT_BRIEF_URL = 'http://localhost:4201/software-factory/Wiki/sticky-note'; @@ -237,7 +237,8 @@ export class HelloCard extends CardDef { `; // QUnit test for HelloCard -const HELLO_TEST_GTS = `import { module, test } from 'qunit'; +const HELLO_TEST_GTS = `import QUnit from 'qunit'; +const { module, test } = QUnit; import { setupCardTest } from '@cardstack/host/tests/helpers'; import { renderCard } from '@cardstack/host/tests/helpers/render-component'; import { getService } from '@universal-ember/test-support'; diff --git a/packages/software-factory/scripts/smoke-tests/smoke-test-realm.ts b/packages/software-factory/scripts/smoke-tests/smoke-test-realm.ts index a03c6158b6..805982ed9a 100644 --- a/packages/software-factory/scripts/smoke-tests/smoke-test-realm.ts +++ b/packages/software-factory/scripts/smoke-tests/smoke-test-realm.ts @@ -74,7 +74,8 @@ const HELLO_SPEC_CARD = { }, }; -const HELLO_TEST_GTS = `import { module, test } from 'qunit'; +const HELLO_TEST_GTS = `import QUnit from 'qunit'; +const { module, test } = QUnit; import { setupCardTest } from '@cardstack/host/tests/helpers'; import { renderCard } from '@cardstack/host/tests/helpers/render-component'; import { getService } from '@universal-ember/test-support'; @@ -96,7 +97,8 @@ export function runTests() { } `; -const HELLO_FAILING_TEST_GTS = `import { module, test } from 'qunit'; +const HELLO_FAILING_TEST_GTS = `import QUnit from 'qunit'; +const { module, test } = QUnit; import { setupCardTest } from '@cardstack/host/tests/helpers'; import { renderCard } from '@cardstack/host/tests/helpers/render-component'; import { getService } from '@universal-ember/test-support'; diff --git a/packages/software-factory/scripts/test.ts b/packages/software-factory/scripts/test.ts index 39d5b10f34..2e9f0c6cf4 100644 --- a/packages/software-factory/scripts/test.ts +++ b/packages/software-factory/scripts/test.ts @@ -6,7 +6,7 @@ configureLogger(process.env.LOG_LEVELS || '*=error'); let log = logger('test'); -const packageRoot = resolve(__dirname, '..'); +const packageRoot = resolve(import.meta.dirname, '..'); type TestRunnerOptions = { nodeOnly: boolean; @@ -81,7 +81,7 @@ async function main(): Promise { await runCommand( `LOG_LEVELS=${JSON.stringify( logLevels, - )} NODE_NO_WARNINGS=1 qunit --require ts-node/register/transpile-only tests/index.ts`, + )} NODE_NO_WARNINGS=1 node tests/index.ts`, ); } diff --git a/packages/software-factory/src/cli/serve-realm.ts b/packages/software-factory/src/cli/serve-realm.ts index 1cd5f87502..3190b66b57 100644 --- a/packages/software-factory/src/cli/serve-realm.ts +++ b/packages/software-factory/src/cli/serve-realm.ts @@ -39,7 +39,7 @@ function cardDefinitionsOnly(relativePath: string): boolean { return included; } -const sfSourceRealmDir = resolve(__dirname, '..', '..', 'realm'); +const sfSourceRealmDir = resolve(import.meta.dirname, '..', '..', 'realm'); function parseCliArg(name: string): string | undefined { let prefix = `--${name}=`; diff --git a/packages/software-factory/src/factory-agent/opencode.ts b/packages/software-factory/src/factory-agent/opencode.ts index eb35e2b01c..48c0d4c147 100644 --- a/packages/software-factory/src/factory-agent/opencode.ts +++ b/packages/software-factory/src/factory-agent/opencode.ts @@ -32,9 +32,9 @@ import { } from '@modelcontextprotocol/sdk/types.js'; import type { Config as OpencodeConfig } from '@opencode-ai/sdk'; -// `@opencode-ai/sdk` is ESM-only and the test runner uses ts-node in -// CommonJS mode, so a top-level `import` would fail at module-load -// time on every test that touches this file. Lazy-load via dynamic +// `@opencode-ai/sdk` is ESM-only, so in a CommonJS load context a +// top-level `import` would fail at module-load time on every test that +// touches this file. Lazy-load via dynamic // import inside `run()` so the type imports stay available at compile // time and the runtime cost (one dynamic import per `factory:go`) is // negligible. diff --git a/packages/software-factory/src/factory-issue-loop-wiring.ts b/packages/software-factory/src/factory-issue-loop-wiring.ts index b5cecd92fa..8eb3eb9b00 100644 --- a/packages/software-factory/src/factory-issue-loop-wiring.ts +++ b/packages/software-factory/src/factory-issue-loop-wiring.ts @@ -52,7 +52,7 @@ import { withStdoutRedirected } from './redirect-stdout.ts'; let log = logger('factory-issue-loop-wiring'); -const PACKAGE_ROOT = resolve(__dirname, '..'); +const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); // --------------------------------------------------------------------------- // Types diff --git a/packages/software-factory/src/factory-prompt-loader.ts b/packages/software-factory/src/factory-prompt-loader.ts index fc0afe5a08..cc3d0b7ea3 100644 --- a/packages/software-factory/src/factory-prompt-loader.ts +++ b/packages/software-factory/src/factory-prompt-loader.ts @@ -14,7 +14,7 @@ import type { // Constants // --------------------------------------------------------------------------- -const PROMPTS_DIR = resolve(__dirname, '../prompts'); +const PROMPTS_DIR = resolve(import.meta.dirname, '../prompts'); // --------------------------------------------------------------------------- // PromptLoader diff --git a/packages/software-factory/src/factory-skill-loader.ts b/packages/software-factory/src/factory-skill-loader.ts index fc524267f3..73b996c19c 100644 --- a/packages/software-factory/src/factory-skill-loader.ts +++ b/packages/software-factory/src/factory-skill-loader.ts @@ -14,7 +14,7 @@ const log = logger('factory-skill-loader'); // Constants // --------------------------------------------------------------------------- -const PACKAGE_ROOT = resolve(__dirname, '..'); +const PACKAGE_ROOT = resolve(import.meta.dirname, '..'); const MONOREPO_ROOT = resolve(PACKAGE_ROOT, '../..'); /** * The SDK orchestrator and the new interactive Claude Code path each get diff --git a/packages/software-factory/src/logger.ts b/packages/software-factory/src/logger.ts index 5940be983e..df0296f964 100644 --- a/packages/software-factory/src/logger.ts +++ b/packages/software-factory/src/logger.ts @@ -1,3 +1,5 @@ +import { createRequire } from 'module'; + export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'none'; type AcceptedLogLevel = LogLevel | 'silent'; @@ -21,8 +23,9 @@ type LoggerFactory = { }; // The published package does not ship TypeScript declarations, so we keep the -// typing local here instead of maintaining an ambient .d.ts shim. -// eslint-disable-next-line @typescript-eslint/no-var-requires +// typing local here instead of maintaining an ambient .d.ts shim. It's also +// CJS, so under native ESM we load it through createRequire. +const require = createRequire(import.meta.url); const createLogger = require('@cardstack/logger') as LoggerFactory; export function configureLogger(serializedLogLevels: string): void { diff --git a/packages/software-factory/src/parse-execution.ts b/packages/software-factory/src/parse-execution.ts index e92130a494..1c4688df22 100644 --- a/packages/software-factory/src/parse-execution.ts +++ b/packages/software-factory/src/parse-execution.ts @@ -61,7 +61,7 @@ export const PARSEABLE_JSON_EXTENSION = '.json'; * `packages/software-factory`, alongside `packages/base`, * `packages/host`, and `packages/boxel-ui`. */ -const PACKAGES_PATH = resolve(__dirname, '..', '..'); +const PACKAGES_PATH = resolve(import.meta.dirname, '..', '..'); const BASE_PKG_PATH = join(PACKAGES_PATH, 'base'); const HOST_PKG_PATH = join(PACKAGES_PATH, 'host'); @@ -650,7 +650,7 @@ export async function runGlintCheck( symlinkSync(NODE_MODULES_PATH, join(tempDir, 'node_modules')); let emberTscBin = resolve( - __dirname, + import.meta.dirname, '..', 'node_modules', '.bin', diff --git a/packages/software-factory/tests/eval-execution.test.ts b/packages/software-factory/tests/eval-execution.test.ts index 0a8e84fd91..4057cbe153 100644 --- a/packages/software-factory/tests/eval-execution.test.ts +++ b/packages/software-factory/tests/eval-execution.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { BoxelCLIClient } from '@cardstack/boxel-cli/api'; diff --git a/packages/software-factory/tests/eval-step.test.ts b/packages/software-factory/tests/eval-step.test.ts index 3c19384af4..428b876849 100644 --- a/packages/software-factory/tests/eval-step.test.ts +++ b/packages/software-factory/tests/eval-step.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ValidationStepResult } from '../src/factory-agent/index.ts'; diff --git a/packages/software-factory/tests/factory-agent-claude-code.test.ts b/packages/software-factory/tests/factory-agent-claude-code.test.ts index f97418589a..3ab7b6fecc 100644 --- a/packages/software-factory/tests/factory-agent-claude-code.test.ts +++ b/packages/software-factory/tests/factory-agent-claude-code.test.ts @@ -2,7 +2,8 @@ import { mkdtempSync, rmSync, symlinkSync, unlinkSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { z, type ZodType } from 'zod'; import type { Options } from '@anthropic-ai/claude-agent-sdk'; diff --git a/packages/software-factory/tests/factory-brief.test.ts b/packages/software-factory/tests/factory-brief.test.ts index 20f1c82cd7..523b76fca5 100644 --- a/packages/software-factory/tests/factory-brief.test.ts +++ b/packages/software-factory/tests/factory-brief.test.ts @@ -1,7 +1,8 @@ import { readFileSync } from 'node:fs'; import { createServer } from 'node:http'; import { resolve } from 'node:path'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { SupportedMimeType } from '@cardstack/runtime-common/supported-mime-type'; @@ -12,12 +13,15 @@ import { } from '../src/factory-brief.ts'; const stickyNoteFixture = JSON.parse( - readFileSync(resolve(__dirname, '../realm/Wiki/sticky-note.json'), 'utf8'), + readFileSync( + resolve(import.meta.dirname, '../realm/Wiki/sticky-note.json'), + 'utf8', + ), ) as unknown; const darkfactoryIssueFixture = JSON.parse( readFileSync( resolve( - __dirname, + import.meta.dirname, '../test-fixtures/darkfactory-adopter/Issues/issue-001.json', ), 'utf8', diff --git a/packages/software-factory/tests/factory-context-builder.test.ts b/packages/software-factory/tests/factory-context-builder.test.ts index 0fb80202b3..ea1810f233 100644 --- a/packages/software-factory/tests/factory-context-builder.test.ts +++ b/packages/software-factory/tests/factory-context-builder.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { KnowledgeArticleData, diff --git a/packages/software-factory/tests/factory-entrypoint.integration.test.ts b/packages/software-factory/tests/factory-entrypoint.integration.test.ts index 69243c347b..eb365e5c77 100644 --- a/packages/software-factory/tests/factory-entrypoint.integration.test.ts +++ b/packages/software-factory/tests/factory-entrypoint.integration.test.ts @@ -9,13 +9,14 @@ import { spawn, spawnSync } from 'node:child_process'; import { createServer } from 'node:http'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { SupportedMimeType } from '@cardstack/runtime-common/supported-mime-type'; -const packageRoot = resolve(__dirname, '..'); +const packageRoot = resolve(import.meta.dirname, '..'); const stickyNoteFixture = readFileSync( - resolve(__dirname, '../realm/Wiki/sticky-note.json'), + resolve(import.meta.dirname, '../realm/Wiki/sticky-note.json'), 'utf8', ); diff --git a/packages/software-factory/tests/factory-entrypoint.test.ts b/packages/software-factory/tests/factory-entrypoint.test.ts index 084b3e4140..edbd2edf6a 100644 --- a/packages/software-factory/tests/factory-entrypoint.test.ts +++ b/packages/software-factory/tests/factory-entrypoint.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { SupportedMimeType } from '@cardstack/runtime-common/supported-mime-type'; diff --git a/packages/software-factory/tests/factory-prompt-loader.test.ts b/packages/software-factory/tests/factory-prompt-loader.test.ts index a4160564a2..97b12db597 100644 --- a/packages/software-factory/tests/factory-prompt-loader.test.ts +++ b/packages/software-factory/tests/factory-prompt-loader.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { assembleImplementPrompt, diff --git a/packages/software-factory/tests/factory-skill-loader.test.ts b/packages/software-factory/tests/factory-skill-loader.test.ts index 22f54978fe..03c9a9c137 100644 --- a/packages/software-factory/tests/factory-skill-loader.test.ts +++ b/packages/software-factory/tests/factory-skill-loader.test.ts @@ -2,7 +2,8 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ProjectData, diff --git a/packages/software-factory/tests/factory-target-realm.spec.ts b/packages/software-factory/tests/factory-target-realm.spec.ts index 9421a4e567..0997e8505e 100644 --- a/packages/software-factory/tests/factory-target-realm.spec.ts +++ b/packages/software-factory/tests/factory-target-realm.spec.ts @@ -100,8 +100,6 @@ test('factory:go creates a target realm and bootstraps project artifacts end-to- 'node', [ '--no-warnings', - '--require', - require.resolve('ts-node/register/transpile-only'), resolve(packageRoot, 'src/cli/factory-entrypoint.ts'), '--brief-url', briefUrl, diff --git a/packages/software-factory/tests/factory-target-realm.test.ts b/packages/software-factory/tests/factory-target-realm.test.ts index 3ac0c5eda9..7fafbdddbd 100644 --- a/packages/software-factory/tests/factory-target-realm.test.ts +++ b/packages/software-factory/tests/factory-target-realm.test.ts @@ -1,7 +1,8 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { setProfileManager, diff --git a/packages/software-factory/tests/factory-test-realm.test.ts b/packages/software-factory/tests/factory-test-realm.test.ts index 6691e442d3..a514ee0274 100644 --- a/packages/software-factory/tests/factory-test-realm.test.ts +++ b/packages/software-factory/tests/factory-test-realm.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { rri } from '@cardstack/runtime-common/realm-identifiers'; import { SupportedMimeType } from '@cardstack/runtime-common/supported-mime-type'; diff --git a/packages/software-factory/tests/factory-tool-builder.test.ts b/packages/software-factory/tests/factory-tool-builder.test.ts index 430afd8c1f..ceab5eed6a 100644 --- a/packages/software-factory/tests/factory-tool-builder.test.ts +++ b/packages/software-factory/tests/factory-tool-builder.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ToolResult } from '../src/factory-agent/index.ts'; import { @@ -30,13 +31,9 @@ const TARGET_REALM = 'https://realms.example.test/user/target/'; // for anything that slips past, but cleaning per-test is cheaper and // keeps the OS tmpdir from growing during the run. let pendingWorkspaces: TestWorkspace[] = []; -declare const QUnit: { - testDone: (cb: () => void) => void; -}; let testDoneHookInstalled = false; function installTestDoneHook() { if (testDoneHookInstalled) return; - if (typeof QUnit === 'undefined') return; testDoneHookInstalled = true; QUnit.testDone(() => { let toClean = pendingWorkspaces; diff --git a/packages/software-factory/tests/factory-tool-executor.integration.test.ts b/packages/software-factory/tests/factory-tool-executor.integration.test.ts index ad05cff057..5d11de189f 100644 --- a/packages/software-factory/tests/factory-tool-executor.integration.test.ts +++ b/packages/software-factory/tests/factory-tool-executor.integration.test.ts @@ -1,5 +1,6 @@ import { createServer, type IncomingMessage, type Server } from 'node:http'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { SupportedMimeType } from '@cardstack/runtime-common/supported-mime-type'; diff --git a/packages/software-factory/tests/factory-tool-executor.test.ts b/packages/software-factory/tests/factory-tool-executor.test.ts index 710102c7c4..ed38ee1938 100644 --- a/packages/software-factory/tests/factory-tool-executor.test.ts +++ b/packages/software-factory/tests/factory-tool-executor.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { ToolExecutor, diff --git a/packages/software-factory/tests/factory-tool-registry.test.ts b/packages/software-factory/tests/factory-tool-registry.test.ts index 8910fc6c1c..0de60875fa 100644 --- a/packages/software-factory/tests/factory-tool-registry.test.ts +++ b/packages/software-factory/tests/factory-tool-registry.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { getDefaultToolRegistry, diff --git a/packages/software-factory/tests/factory-tool-schema-adapter.test.ts b/packages/software-factory/tests/factory-tool-schema-adapter.test.ts index 5236bfdbf7..b00b680898 100644 --- a/packages/software-factory/tests/factory-tool-schema-adapter.test.ts +++ b/packages/software-factory/tests/factory-tool-schema-adapter.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { z, type ZodObject } from 'zod'; import { diff --git a/packages/software-factory/tests/find-and-hold-available-port.test.ts b/packages/software-factory/tests/find-and-hold-available-port.test.ts index a1fb9e1654..746ebc46a5 100644 --- a/packages/software-factory/tests/find-and-hold-available-port.test.ts +++ b/packages/software-factory/tests/find-and-hold-available-port.test.ts @@ -1,5 +1,6 @@ import { createServer } from 'node:net'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { findAndHoldAvailablePort } from '@cardstack/realm-test-harness'; diff --git a/packages/software-factory/tests/fixtures.ts b/packages/software-factory/tests/fixtures.ts index 37c31ace6f..303a64de2e 100644 --- a/packages/software-factory/tests/fixtures.ts +++ b/packages/software-factory/tests/fixtures.ts @@ -90,7 +90,7 @@ type SharedRealmHandle = { }; const packageRoot = resolve(process.cwd()); -const tsNodeBin = resolve(packageRoot, 'node_modules', '.bin', 'ts-node'); +const nodeBin = process.execPath; const defaultRealmDir = resolve( packageRoot, process.env.TEST_HARNESS_REALM_DIR ?? 'test-fixtures/darkfactory-adopter', @@ -278,9 +278,8 @@ async function startRealmProcess( }; try { child = spawn( - tsNodeBin, + nodeBin, [ - '--transpileOnly', 'src/cli/serve-realm.ts', realmDir, `--compatRealmServerPort=${testWorkerPortSet.compatRealmServerPort}`, @@ -347,7 +346,7 @@ async function startRealmProcess( // Race the metadata-file poll against an early `'error'` from the // child. Without this, a spawn-level failure (e.g. ENOENT on the - // ts-node binary) leaves waitForMetadataFile polling until its + // node binary) leaves waitForMetadataFile polling until its // 300-second timeout before the startup error surfaces. let earlyError = new Promise((_, reject) => { child!.once('error', reject); diff --git a/packages/software-factory/tests/hold-specific-port.test.ts b/packages/software-factory/tests/hold-specific-port.test.ts index 2fc7e49113..2dd2d003b2 100644 --- a/packages/software-factory/tests/hold-specific-port.test.ts +++ b/packages/software-factory/tests/hold-specific-port.test.ts @@ -1,5 +1,6 @@ import { createServer } from 'node:net'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { findAndHoldAvailablePort, diff --git a/packages/software-factory/tests/index.ts b/packages/software-factory/tests/index.ts index 8fba08587c..0024040813 100644 --- a/packages/software-factory/tests/index.ts +++ b/packages/software-factory/tests/index.ts @@ -1,3 +1,5 @@ +import './qunit-bootstrap.ts'; // configures QUnit before any test registers +import QUnit from 'qunit'; import './factory-brief.test.ts'; import './validation-run-cache.test.ts'; import './factory-prompt-loader.test.ts'; @@ -26,3 +28,5 @@ import './instantiate-step.test.ts'; import './parse-step.test.ts'; import './port-allocator.test.ts'; import './find-and-hold-available-port.test.ts'; + +QUnit.start(); diff --git a/packages/software-factory/tests/instantiate-discovery.test.ts b/packages/software-factory/tests/instantiate-discovery.test.ts index 9cb69e3081..af79e794e1 100644 --- a/packages/software-factory/tests/instantiate-discovery.test.ts +++ b/packages/software-factory/tests/instantiate-discovery.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { BoxelCLIClient } from '@cardstack/boxel-cli/api'; diff --git a/packages/software-factory/tests/instantiate-step.test.ts b/packages/software-factory/tests/instantiate-step.test.ts index 0a501b6979..fedbe6e92f 100644 --- a/packages/software-factory/tests/instantiate-step.test.ts +++ b/packages/software-factory/tests/instantiate-step.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { InstantiateValidationStep, diff --git a/packages/software-factory/tests/issue-loop.test.ts b/packages/software-factory/tests/issue-loop.test.ts index 7b448f4df8..a5968f7a54 100644 --- a/packages/software-factory/tests/issue-loop.test.ts +++ b/packages/software-factory/tests/issue-loop.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { AgentContext, diff --git a/packages/software-factory/tests/issue-scheduler.test.ts b/packages/software-factory/tests/issue-scheduler.test.ts index 3edf8aed59..9eda91cb66 100644 --- a/packages/software-factory/tests/issue-scheduler.test.ts +++ b/packages/software-factory/tests/issue-scheduler.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { BoxelCLIClient } from '@cardstack/boxel-cli/api'; diff --git a/packages/software-factory/tests/lint-step.test.ts b/packages/software-factory/tests/lint-step.test.ts index 40c6ba05cb..5c6eeee6ed 100644 --- a/packages/software-factory/tests/lint-step.test.ts +++ b/packages/software-factory/tests/lint-step.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ValidationStepResult } from '../src/factory-agent/index.ts'; diff --git a/packages/software-factory/tests/parse-step.test.ts b/packages/software-factory/tests/parse-step.test.ts index 478dfcecd3..6a32502027 100644 --- a/packages/software-factory/tests/parse-step.test.ts +++ b/packages/software-factory/tests/parse-step.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ValidationStepResult } from '../src/factory-agent/index.ts'; diff --git a/packages/software-factory/tests/port-allocator.test.ts b/packages/software-factory/tests/port-allocator.test.ts index 51cc10f799..6497db3e24 100644 --- a/packages/software-factory/tests/port-allocator.test.ts +++ b/packages/software-factory/tests/port-allocator.test.ts @@ -1,5 +1,6 @@ import { createServer } from 'node:net'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import { TEST_WORKER_PORT_RANGE_END, diff --git a/packages/software-factory/tests/qunit-bootstrap.ts b/packages/software-factory/tests/qunit-bootstrap.ts new file mode 100644 index 0000000000..6f938d42c4 --- /dev/null +++ b/packages/software-factory/tests/qunit-bootstrap.ts @@ -0,0 +1,13 @@ +// Configures QUnit to run under native Node (`node tests/index.ts`). The qunit +// CLI used to provide these three things; running the suite directly under node +// means wiring them up ourselves: +// - autostart off, so `index.ts` can register every test before starting +// - the TAP reporter the CLI emitted by default +// - a failure-based process exit code +import QUnit from 'qunit'; + +QUnit.config.autostart = false; +(QUnit as any).reporters.tap.init(QUnit); // QUnit 2.x API missing from @types/qunit +(QUnit as any).on('runEnd', (data: { testCounts: { failed: number } }) => { + process.exitCode = data.testCounts.failed > 0 ? 1 : 0; +}); diff --git a/packages/software-factory/tests/retry-blocked-issues.test.ts b/packages/software-factory/tests/retry-blocked-issues.test.ts index ff73d4ad05..7e11d6178f 100644 --- a/packages/software-factory/tests/retry-blocked-issues.test.ts +++ b/packages/software-factory/tests/retry-blocked-issues.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { SchedulableIssue } from '../src/factory-agent/index.ts'; import type { IssueStore } from '../src/issue-scheduler.ts'; diff --git a/packages/software-factory/tests/test-step.test.ts b/packages/software-factory/tests/test-step.test.ts index 2e7e619a43..1fb2177863 100644 --- a/packages/software-factory/tests/test-step.test.ts +++ b/packages/software-factory/tests/test-step.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { LooseSingleCardDocument } from '@cardstack/runtime-common'; diff --git a/packages/software-factory/tests/validation-pipeline.test.ts b/packages/software-factory/tests/validation-pipeline.test.ts index ef357c3e95..8a4c67b8a6 100644 --- a/packages/software-factory/tests/validation-pipeline.test.ts +++ b/packages/software-factory/tests/validation-pipeline.test.ts @@ -1,4 +1,5 @@ -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { ValidationStep, diff --git a/packages/software-factory/tests/validation-run-cache.test.ts b/packages/software-factory/tests/validation-run-cache.test.ts index ef8fd1a7e5..0c9e6d5977 100644 --- a/packages/software-factory/tests/validation-run-cache.test.ts +++ b/packages/software-factory/tests/validation-run-cache.test.ts @@ -2,7 +2,8 @@ import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; -import { module, test } from 'qunit'; +import QUnit from 'qunit'; +const { module, test } = QUnit; import type { BoxelCLIClient } from '@cardstack/boxel-cli/api'; diff --git a/packages/software-factory/tsconfig.json b/packages/software-factory/tsconfig.json index 43a346c3e6..9369a3dbcb 100644 --- a/packages/software-factory/tsconfig.json +++ b/packages/software-factory/tsconfig.json @@ -26,7 +26,12 @@ "skipLibCheck": true, "strict": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"] + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ] }, "types": ["@cardstack/local-types", "node"] }, diff --git a/packages/test-realm-cards/tsconfig.json b/packages/test-realm-cards/tsconfig.json index 917c3ece39..ba7706d102 100644 --- a/packages/test-realm-cards/tsconfig.json +++ b/packages/test-realm-cards/tsconfig.json @@ -22,7 +22,12 @@ "strict": true, "experimentalDecorators": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/boxel-host/commands/*": ["../host/app/commands/*"] }, "types": ["@cardstack/local-types"] diff --git a/packages/vscode-boxel-tools/tsconfig.json b/packages/vscode-boxel-tools/tsconfig.json index df42714344..053b2bf7f9 100644 --- a/packages/vscode-boxel-tools/tsconfig.json +++ b/packages/vscode-boxel-tools/tsconfig.json @@ -13,7 +13,12 @@ "experimentalDecorators": true, "skipLibCheck": true, "paths": { - "https://cardstack.com/base/*": ["../base/*"], + "https://cardstack.com/base/*": [ + "../base/*.gts", + "../base/*.ts", + "../base/*.d.ts", + "../base/*" + ], "@cardstack/runtime-common/*": ["../runtime-common/*"], "babel-plugin-ember-template-compilation": [ "node_modules/babel-plugin-ember-template-compilation/*" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be35c65538..0538daa092 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -213,9 +213,9 @@ catalogs: '@types/koa__router': specifier: ^12.0.0 version: 12.0.5 - '@types/lodash': - specifier: ^4.17.15 - version: 4.17.24 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 '@types/matrix-js-sdk': specifier: ^11.0.1 version: 11.1.0 @@ -510,15 +510,15 @@ catalogs: loader.js: specifier: ^4.7.0 version: 4.7.0 - lodash: + lodash-es: specifier: ^4.17.21 version: 4.18.1 loglevel: specifier: ^1.8.1 version: 1.9.2 magic-string: - specifier: 0.25.9 - version: 0.25.9 + specifier: ^0.30.21 + version: 0.30.21 marked: specifier: ^12.0.1 version: 12.0.2 @@ -808,9 +808,9 @@ importers: '@sentry/node': specifier: 'catalog:' version: 8.55.2 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/node': specifier: 'catalog:' version: 24.12.4 @@ -823,7 +823,7 @@ importers: debug: specifier: ^4.4.3 version: 4.4.3(supports-color@8.1.1) - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 matrix-js-sdk: @@ -841,9 +841,6 @@ importers: stream-json: specifier: 'catalog:' version: 1.9.1 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -890,6 +887,9 @@ importers: ember-source: specifier: 'catalog:' version: 6.10.1(patch_hash=ea945024993105fb6cc4ae5cb5e9ea8e0eff6cd5fe0b0033c43dd0cf9453eb0d)(@glimmer/component@2.1.1)(rsvp@4.8.5) + lodash-es: + specifier: 'catalog:' + version: 4.18.1 devDependencies: '@babel/core': specifier: 'catalog:' @@ -918,9 +918,9 @@ importers: '@types/flat': specifier: 'catalog:' version: 5.0.5 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 awesome-phonenumber: specifier: 'catalog:' version: 7.8.0 @@ -997,9 +997,6 @@ importers: matrix-js-sdk: specifier: 'catalog:' version: 38.3.0(patch_hash=cee0baf579283943dc5a6b48977e8ed40a13fc111b5de9c91814893f9a4989fb) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1116,9 +1113,6 @@ importers: semver: specifier: ^7.7.0 version: 7.8.0 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) vite: specifier: 'catalog:' version: 6.4.2(@types/node@24.12.4)(lightningcss@1.32.0)(terser@5.47.1)(yaml@2.9.0) @@ -1344,7 +1338,7 @@ importers: focus-trap: specifier: 'catalog:' version: 7.8.0 - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 pluralize: @@ -1399,9 +1393,9 @@ importers: '@types/dompurify': specifier: 'catalog:' version: 3.2.0 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/pluralize': specifier: 'catalog:' version: 0.0.30 @@ -1737,9 +1731,9 @@ importers: '@glint/template': specifier: 'catalog:' version: 1.7.7 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/qunit': specifier: 'catalog:' version: 2.19.14 @@ -1788,7 +1782,7 @@ importers: eslint-plugin-prettier: specifier: 'catalog:' version: 5.5.5(@types/eslint@8.56.5)(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.8.3) - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 prettier: @@ -1920,9 +1914,9 @@ importers: '@ember/test-helpers': specifier: 'catalog:' version: 5.4.2(@babel/core@7.29.0)(@glint/template@1.7.7) - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/qunit': specifier: 'catalog:' version: 2.19.14 @@ -1947,6 +1941,9 @@ importers: ember-template-lint: specifier: 'catalog:' version: 7.9.3 + lodash-es: + specifier: 'catalog:' + version: 4.18.1 tracked-built-ins: specifier: ^4.1.2 version: 4.1.2(@babel/core@7.29.0) @@ -2110,9 +2107,9 @@ importers: '@types/indefinite': specifier: 'catalog:' version: 2.3.4 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/matrix-js-sdk': specifier: 'catalog:' version: 11.1.0 @@ -2344,7 +2341,7 @@ importers: katex: specifier: 'catalog:' version: 0.16.45 - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 loglevel: @@ -2500,9 +2497,6 @@ importers: tmp: specifier: 'catalog:' version: 0.2.5 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -2559,9 +2553,6 @@ importers: pg: specifier: 'catalog:' version: 8.20.0 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@25.7.0)(typescript@5.9.3) devDependencies: '@cardstack/local-types': specifier: workspace:* @@ -2666,9 +2657,9 @@ importers: '@types/koa__router': specifier: 'catalog:' version: 12.0.5 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/mime-types': specifier: 'catalog:' version: 2.1.4 @@ -2774,7 +2765,7 @@ importers: koa-proxies: specifier: 'catalog:' version: 0.12.4(koa@2.16.4) - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 loglevel: @@ -2831,9 +2822,6 @@ importers: tmp: specifier: 'catalog:' version: 0.2.5 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -2900,9 +2888,6 @@ importers: puppeteer: specifier: ^25.0.2 version: 25.1.0 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -2975,9 +2960,9 @@ importers: '@types/flat': specifier: 'catalog:' version: 5.0.5 - '@types/lodash': + '@types/lodash-es': specifier: 'catalog:' - version: 4.17.24 + version: 4.17.12 '@types/pluralize': specifier: 'catalog:' version: 0.0.30 @@ -3047,7 +3032,7 @@ importers: json-typescript: specifier: 'catalog:' version: 1.1.2 - lodash: + lodash-es: specifier: 'catalog:' version: 4.18.1 loglevel: @@ -3055,7 +3040,7 @@ importers: version: 1.9.2 magic-string: specifier: 'catalog:' - version: 0.25.9(patch_hash=32dda55b40f0bc860d0a8e93bee54d8005c34ed7ac5faed23ce9f5e5f57eea39) + version: 0.30.21 marked: specifier: 'catalog:' version: 12.0.2 @@ -3156,9 +3141,6 @@ importers: statuses: specifier: 'catalog:' version: 2.0.2 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@25.7.0)(typescript@5.9.3) packages/skills-realm: {} @@ -3272,9 +3254,6 @@ importers: tmp: specifier: 'catalog:' version: 0.2.5 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@24.12.4)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -4303,10 +4282,6 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -5477,9 +5452,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@koa/cors@4.0.0': resolution: {integrity: sha512-Y4RrbvGTlAaa04DBoPBWJqDR5gPj32OOz827ULXfgB1F7piD1MB/zwn8JR2LAnvdILhxUbXbkXGWuNVsFuVFCQ==} engines: {node: '>= 14.0.0'} @@ -6652,18 +6624,6 @@ packages: resolution: {integrity: sha512-IBoECN9o9StxTZSy12eNSPdqiH5VzngD5Qx9YQDfteiXk9XyJhnyRQuBoU/MQCVnqau9fJpgKoA8Sy/0qItFXw==} deprecated: Please use @ember/app-tsconfig or @ember/library-tsconfig instead. These live at https://github.com/ember-cli/tsconfigs - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} @@ -6921,6 +6881,9 @@ packages: '@types/koa__router@12.0.5': resolution: {integrity: sha512-1HeLxuDn4n5it1yZYCSyOYXo++73zT0ffoviXnPxbwbxLbvDFEvWD9ZzpRiIpK4oKR0pi+K+Mk/ZjyROjW3HSw==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} @@ -7586,9 +7549,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} deprecated: This package is no longer supported. - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -8909,9 +8869,6 @@ packages: create-hmac@1.1.7: resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -9382,10 +9339,6 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} - engines: {node: '>=0.3.1'} - diff@5.2.2: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} @@ -12257,9 +12210,6 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -14926,20 +14876,6 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -15255,9 +15191,6 @@ packages: deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -15826,10 +15759,6 @@ packages: resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} engines: {node: '>= 4.0.0'} - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -17204,10 +17133,6 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -18491,11 +18416,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - '@koa/cors@4.0.0': dependencies: vary: 1.1.2 @@ -19817,14 +19737,6 @@ snapshots: '@tsconfig/ember@3.0.1': {} - '@tsconfig/node10@1.0.12': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 @@ -20153,6 +20065,10 @@ snapshots: dependencies: '@types/koa': 2.15.0 + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.24 + '@types/lodash@4.17.24': {} '@types/luxon@3.4.2': {} @@ -20944,8 +20860,6 @@ snapshots: are-we-there-yet@4.0.2: {} - arg@4.1.3: {} - arg@5.0.2: {} argparse@2.0.1: {} @@ -22517,8 +22431,6 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.12 - create-require@1.1.1: {} - crelt@1.0.6: {} cron@3.5.0: @@ -23033,8 +22945,6 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.4: {} - diff@5.2.2: {} diff@7.0.0: {} @@ -27345,8 +27255,6 @@ snapshots: dependencies: semver: 6.3.1 - make-error@1.3.6: {} - makeerror@1.0.12: dependencies: tmpl: 1.0.5 @@ -30472,42 +30380,6 @@ snapshots: ts-dedent@2.2.0: {} - ts-node@10.9.2(@types/node@24.12.4)(typescript@5.9.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 24.12.4 - acorn: 8.16.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - ts-node@10.9.2(@types/node@25.7.0)(typescript@5.9.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 25.7.0 - acorn: 8.16.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -30781,8 +30653,6 @@ snapshots: uuid@9.0.1: {} - v8-compile-cache-lib@3.0.1: {} - validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -31451,8 +31321,6 @@ snapshots: ylru@1.4.0: {} - yn@3.1.1: {} - yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fef2c83dfc..3513795397 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -80,6 +80,7 @@ catalog: "@types/koa__router": ^12.0.0 "@types/line-column": ^1.0.0 "@types/lodash": ^4.17.15 + "@types/lodash-es": ^4.17.12 "@types/matrix-js-sdk": ^11.0.1 "@types/mime-types": ^2.1.1 "@types/ms": ^2.1.0 @@ -183,10 +184,11 @@ catalog: line-column: ^1.0.2 loader.js: ^4.7.0 lodash: ^4.17.21 + lodash-es: ^4.17.21 loglevel: ^1.8.1 lucide-static: ^0.447.0 macro-decorators: ^0.1.2 - magic-string: 0.25.9 + magic-string: ^0.30.21 katex: ^0.16.44 marked: ^12.0.1 marked-alert: ^2.1.2 diff --git a/scripts/esm-codemod/README.md b/scripts/esm-codemod/README.md new file mode 100644 index 0000000000..e8cf87bcde --- /dev/null +++ b/scripts/esm-codemod/README.md @@ -0,0 +1,151 @@ +# CS-11449 — ts-node → native-Node ESM codemod + +Tooling for migrating the **node-run package cluster** off `ts-node` and onto +native Node (≥24) TypeScript execution (type-stripping). Native Node ESM is far +stricter than ts-node / Vite about module resolution, so the swap surfaces a +predictable set of breakages. This directory automates the mechanical ones. + +The host (Vite/Embroider) build **masks** every error below — a green host build +does not mean a package is node-loadable. Verify by actually `import()`-ing the +entry under native node. + +## Usage + +```sh +# Apply all automated rules across the cluster (idempotent): +node scripts/esm-codemod/run.mjs + +# Preview without writing: +node scripts/esm-codemod/run.mjs --dry +``` + +Individual rules can be run on specific files, e.g. +`node scripts/esm-codemod/lodash-to-lodash-es.mjs ...`. + +## The node-run cluster + +Packages Node executes directly (NOT bundled by Vite): +`runtime-common`, `postgres`, `billing`, `realm-server`, `realm-test-harness`, +`ai-bot`, `bot-runner`, `matrix`, `software-factory`. + +## Error taxonomy + +Each error was found by `import()`-ing a service entry under native node and +reading the first failure, then fixing and repeating. Classes marked +**automated** are handled by `run.mjs`; **manual** ones are listed in the next +section. + +| # | Symptom | Cause | Fix | | +| --- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | `ERR_MODULE_NOT_FOUND` resolving a relative import `./foo` | Node does no extension search or directory-index resolution | append `./foo.ts` / `./foo.gts` / `./foo/index.ts`; bare `'.'` → `'./index.ts'` | automated (`add-relative-extensions`) | +| 2 | `ERR_MODULE_NOT_FOUND` resolving a workspace pkg subpath (`@cardstack/billing/x`) | workspace package has no `exports` map → Node looks for `index.js` | add an `exports` map pointing at `.ts` + `"type":"module"` | **manual** | +| 3 | `Cannot find module '.../lodash/merge'` (`Did you mean lodash/merge.js?`) | `lodash` is CJS-only with no exports map | repo-wide switch to `lodash-es` named imports | automated (`lodash-to-lodash-es`) | +| 4 | `does not provide an export named 'X'` from a CJS pkg (`fs-extra`, `debug`, `qunit`, `jsonwebtoken`) | Node can't statically read named exports from these CJS modules | default-import + destructure | automated (`cjs-named-to-default`, line-anchored so it skips imports embedded in test-fixture strings) — extend `CJS_PACKAGES` as new ones surface | +| 5 | `ERR_MODULE_NOT_FOUND` on a CJS pkg deep import (`matrix-js-sdk/lib/x`) | no exports map; ESM won't add `.js` or pick a file over a same-named dir | append `.js` | **manual** (small, package-specific) | +| 6 | `Cannot find module '.../scripts/x.js'` | a stray CJS file was renamed `.cjs` (because the pkg is now `type:module`) but the import still says `.js` | update the import to `.cjs` | **manual** | +| 7 | `ReferenceError: __dirname is not defined` | CJS globals don't exist under ESM | `import.meta.dirname` / `import.meta.filename` | automated (`dirname-to-import-meta`) | +| 8 | wrong path built from `import.meta.url` | `import.meta.url` is a `file://` URL string, not a path | use `import.meta.dirname` | **manual** (rare — was a WIP bug) | +| 9 | `ReferenceError: exports is not defined` when a consumer runs the dep via `ts-node` | once a dep ships an `exports` map Node handles its `.ts` natively, colliding with a still-`ts-node` consumer | the dep + all its node-run consumers must drop ts-node in the same change (big-bang) | **manual** (coordination) | +| 10 | in-source `spawn('ts-node', ['--transpileOnly', 'entry', …])` | child processes also need native node | `spawn('node', ['entry.ts', …])` (or `process.execPath`) | **manual** (few sites, varied shapes) | +| 11 | `ReferenceError: require is not defined in ES module scope` | `require()` / lazy `require()` don't exist in ESM | `const require = createRequire(import.meta.url)` shim, OR static/dynamic import. Native `require()` does NO extension search — `require('./x.ts')` works, `require('./x')` and `require('./dir')` do not | **manual** (small set) | +| 12 | a test's `sinon.stub(NS, 'fn')` / `(NS as any).fn = …` silently no-ops | ES-module namespace objects (`import * as NS`) are sealed; their bindings are read-only | inject the dependency, or stub through a small mutable indirection module the source calls and the test imports — not the namespace | **manual** (per-test; ai-bot Sentry tests now stub `lib/sentry.ts`'s `errorReporter`) | +| 13 | `X is not a function` from `import X from 'some-cjs-pkg'` (e.g. `node-pg-migrate`) | CJS pkg authored with transpiled `exports.default = fn`: native ESM binds `X = module.exports` (the whole namespace) and the callable is on `X.default`; ts-node's `esModuleInterop` unwrapped it | `const fn = (X as any).default ?? X` | **manual** (surfaces at runtime, not load — verify the export shape) | +| 14 | `module is not defined in ES module scope` loading a `.js` config/script (`.eslintrc.js`, helper scripts run via `node`) | the pkg is now `type:module`, so its CJS `.js` files (using `module.exports`/`require`) are parsed as ESM | rename to `.cjs` (ESLint still auto-discovers `.eslintrc.cjs`) and update any `node x.js` / path references | **manual** (find CJS `.js` in each `type:module` pkg) | + +### Invocation sites (automated — `ts-node-to-node`) + +`ts-node --transpileOnly ` → `node .ts` in `package.json` scripts, +`*.sh`, and `mise-tasks`. The extensionless entry must gain `.ts` because Node +does no extension search for the CLI entry point. Handles shell line-continuation +(`exec ts-node \`↵`--transpileOnly main`). Skips (and reports) two forms it can't +safely rewrite: `qunit --require ts-node/register/transpile-only …` and inline +`ts-node … -e/--eval …`. + +## Not automated — do by hand + +These are too package-specific or too coupled for a blind codemod: + +1. **`exports` maps + `"type":"module"`** per workspace package (class #2). Each + map mirrors that package's directory layout. A wildcard + `"./*":["./*.ts","./*/index.ts"]` does **not** work — Node exports won't fall + through on file-not-found, so add an explicit entry per directory-index + subpath. Never set `--preserve-symlinks` (it breaks type-stripping for + pnpm-symlinked workspace deps, which only works because Node resolves to + realpath outside `node_modules` first). +2. **The qunit test-runner bootstrap** (DONE). `qunit --require ts-node/register` + has no node equivalent, so each test package now runs `node tests/index.ts` + with a `tests/qunit-bootstrap.ts` that turns autostart off, inits the TAP + reporter, and sets a failure-based exit code; `index.ts` ends with + `QUnit.start()`. Done for `ai-bot`, `bot-runner`, `software-factory`, and + `realm-server` (whose `index.ts` also needed the class-#11 `createRequire` + shim and `require(`${file}.ts`)` so its sync, order-preserving loader keeps + working; the CI JUnit reporter is `--require`d and was renamed `.cjs`). +3. **Remove the `ts-node` devDependency** (DONE) — dropped from every package now + that all invocations and test runners are converted. +4. **CJS deep imports** needing `.js` (class #5) and **`.cjs` import path fixes** + (class #6) — verify against the actual file on disk. +5. **`require()` shims** (class #11) and **ESM-namespace mock breakage** (class + #12) — the latter is genuine per-test work (e.g. ai-bot's Sentry/locking + tests fail because they reassign a sealed namespace binding). + +## Type-checking (`lint:types`) + +All nine node-cluster packages type-check clean under `ember-tsc` (nodenext). +`"type":"module"` flips nodenext from CJS-mode to ESM-mode resolution, which is +what surfaces the type errors. Decision: stay on **nodenext** (not `bundler`) so +the type layer models the native-node runtime; the friction was bounded. Classes +fixed (see also #11/#13): + +- **Base-realm alias** (class #15): under ESM-mode nodenext the extensionless + `"https://cardstack.com/base/*": ["../base/*"]` mapping stops resolving. Expand + every tsconfig's mapping to `["../base/*.gts","../base/*.ts","../base/*.d.ts","../base/*"]`. + Applies to consumers too (host, catalog, …) — they re-type-check runtime-common's + now-ESM sources, so the cascade lands there as well. +- **`import.meta` rejected (TS1470/TS1343)**: a `type:module` (or import.meta-using) + package must type-check in ESM-mode — set `module`/`moduleResolution` to + `nodenext` and add `skipLibCheck`. Flip ai-bot/bot-runner/software-factory/matrix + to `type:module` (they already run as ESM). +- **CJS interop the nodenext type layer can't model** (runtime is fine): bump the + mispackaged dep when a properly-packaged version exists (magic-string + 0.25→0.30), else re-export once with the right signature (`runtime-common/ignore.ts`) + or cast (`node-pg-migrate` via `RunnerOption`). +- **Types missing from a dep** (`@types/qunit` lacks `.reporters`/`.on`): cast the + call site (`(QUnit as any)`), typing any callback param to avoid noImplicitAny. + +`@types/lodash` was swapped to `@types/lodash-es` alongside the runtime dep. + +## Remaining known gaps + +- **ai-bot**: full suite passes (170/170) — the 5 prior failures were class #12 + (Sentry namespace mocking, fixed via `lib/sentry.ts`) and class #13 + (`node-pg-migrate` default interop in `postgres`). The locking tests need PG + env (`PGHOST`/`PGPORT`/`PGUSER`/`PGPASSWORD`) like the rest of the DB suite. +- **software-factory**: full node suite passes (452/453). The one failure + (port-allocator dual-stack `::` bind) is a macOS-vs-Linux socket-semantics + difference, not a migration regression. Unblocked by the `@cardstack/boxel-cli` + `exports` map, the `@cardstack/logger` createRequire fix (class #11, shared + with `realm-test-harness`), and the `.eslintrc.cjs` rename (class #14). +- **realm-server**: the full suite needs the dev services stack to run; the + bootstrap is validated on standalone unit tests. All known bare/lazy + `require()` sites in the cluster are now shimmed (`createRequire`) or + dual-mode — every remaining `require()` either has a `const require = +createRequire(import.meta.url)` in its file or is guarded by + `typeof require === 'function'`. +- **host**: configured for the migration (nodenext + skipLibCheck + the base-path + fix, same as catalog which type-checks clean); its full `lint:types` is slow, so + CI is the practical confirmation. +- **experiments-realm**: pre-existing `lint:types` debt unrelated to the migration + (`node16` tsconfig, no `skipLibCheck`, ~568 source errors); only its base-path + mapping was touched here. +- **realm-server `testem.js`** is CJS under `type:module` but only loaded by the + (now-unused) testem CLI, so it's dormant rather than broken. + +## Verifying a package loads + +```sh +cd packages/realm-server +node --eval 'import("./main.ts").then(()=>process.exit(0)).catch(e=>{console.error(e);process.exit(1)})' +``` + +Reaching a runtime error (e.g. "REALM_SERVER_SECRET_SEED not set") means the full +module graph linked — the migration succeeded for that entry. diff --git a/scripts/esm-codemod/add-relative-extensions.mjs b/scripts/esm-codemod/add-relative-extensions.mjs new file mode 100644 index 0000000000..0c778049aa --- /dev/null +++ b/scripts/esm-codemod/add-relative-extensions.mjs @@ -0,0 +1,82 @@ +#!/usr/bin/env node +// Adds explicit extensions to relative imports/exports so native Node ESM can +// resolve them (Node does NOT do extension search or directory-index resolution). +// +// import x from './foo' -> './foo.ts' (foo.ts exists) +// import x from './foo' -> './foo.gts' (foo.gts exists) +// import x from './foo' -> './foo/index.ts' (foo/ is a dir with index) +// import x from '.' -> './index.ts' +// export * from './bar' -> './bar.ts' +// +// Leaves alone: specifiers that already have a known extension, bare/package +// specifiers, and relative specifiers that resolve to nothing (reported). +// +// Usage: node add-relative-extensions.mjs ... (edits in place) +import { readFileSync, writeFileSync, existsSync, statSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; + +const KNOWN_EXT = + /\.(ts|tsx|mts|cts|gts|js|mjs|cjs|gjs|json|css|wasm|node|d\.ts)$/; +// Resolution order matters: prefer source over compiled, ts over gts. +const FILE_EXTS = ['.ts', '.gts', '.tsx', '.js', '.mjs', '.cjs', '.gjs']; +const INDEX_EXTS = FILE_EXTS; + +// Matches the specifier in import/export ... from '...' and bare import '...'. +const FROM = + /(\bfrom\s*|\bimport\s*|\bexport\s*\*\s*from\s*)(['"])(\.[^'"]*)\2/g; + +function resolveSpecifier(fromFile, spec) { + const base = resolve(dirname(fromFile), spec); + // Already a file with an extension we trust. + if (KNOWN_EXT.test(spec)) return null; + // Direct file match by appending an extension. + for (const ext of FILE_EXTS) { + if (existsSync(base + ext)) return spec + ext; + } + // Directory index. + if (existsSync(base) && statSync(base).isDirectory()) { + for (const ext of INDEX_EXTS) { + if (existsSync(resolve(base, 'index' + ext))) { + return (spec.endsWith('/') ? spec.slice(0, -1) : spec) + '/index' + ext; + } + } + } + return undefined; // unresolved +} + +export function transform(fromFile, src) { + const unresolved = []; + let changed = false; + const code = src.replace(FROM, (full, kw, q, spec) => { + const out = resolveSpecifier(fromFile, spec); + if (out === null) return full; // already extensioned + if (out === undefined) { + unresolved.push(spec); + return full; + } + changed = true; + return `${kw}${q}${out}${q}`; + }); + return { code, changed, unresolved }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let count = 0; + const allUnresolved = []; + for (const file of process.argv.slice(2)) { + const { code, changed, unresolved } = transform( + file, + readFileSync(file, 'utf8'), + ); + if (changed) { + writeFileSync(file, code); + count++; + } + for (const u of unresolved) allUnresolved.push(`${file}: ${u}`); + } + console.log(`relative-extensions: ${count} file(s) changed`); + if (allUnresolved.length) { + console.log(`\nUNRESOLVED (${allUnresolved.length}):`); + for (const u of allUnresolved) console.log(' ' + u); + } +} diff --git a/scripts/esm-codemod/cjs-named-to-default.mjs b/scripts/esm-codemod/cjs-named-to-default.mjs new file mode 100644 index 0000000000..2d41153b52 --- /dev/null +++ b/scripts/esm-codemod/cjs-named-to-default.mjs @@ -0,0 +1,67 @@ +#!/usr/bin/env node +// Converts named imports from CommonJS packages that Node's static analyzer +// can't read named exports from, into default-import + destructure. +// +// import { ensureDir, copy as cp } from 'fs-extra'; +// becomes +// import fsExtra from 'fs-extra'; +// const { ensureDir, copy: cp } = fsExtra; +// +// Works under both native Node ESM (default export is module.exports) and Vite. +// +// Usage: node cjs-named-to-default.mjs ... (edits in place) +import { readFileSync, writeFileSync } from 'node:fs'; + +// Packages whose named exports Node cannot statically detect. +export const CJS_PACKAGES = { + 'fs-extra': 'fsExtra', + debug: 'createDebug', + qunit: 'QUnit', + jsonwebtoken: 'jsonwebtoken', +}; + +function varName(pkg) { + if (CJS_PACKAGES[pkg]) return CJS_PACKAGES[pkg]; + return pkg.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()); +} + +export function transform(src) { + let changed = false; + let code = src; + for (const pkg of Object.keys(CJS_PACKAGES)) { + // Line-anchored so it only matches a real top-level import statement, never + // an import that appears as test-fixture text inside a string/template + // literal (those are preceded by a quote/backtick, so never start a line). + const re = new RegExp( + `^[ \\t]*import\\s*\\{([^}]*)\\}\\s*from\\s*(['"])${pkg.replace(/[/\\^$*+?.()|[\]{}]/g, '\\$&')}\\2;?[ \\t]*$`, + 'gm', + ); + code = code.replace(re, (_full, names) => { + const local = varName(pkg); + const destructure = names + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + .map((s) => { + const m = s.match(/^(\w+)\s+as\s+(\w+)$/); + return m ? `${m[1]}: ${m[2]}` : s; + }) + .join(', '); + changed = true; + return `import ${local} from '${pkg}';\nconst { ${destructure} } = ${local};`; + }); + } + return { code, changed }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let count = 0; + for (const file of process.argv.slice(2)) { + const { code, changed } = transform(readFileSync(file, 'utf8')); + if (changed) { + writeFileSync(file, code); + count++; + } + } + console.log(`cjs-named-to-default: ${count} file(s) changed`); +} diff --git a/scripts/esm-codemod/dirname-to-import-meta.mjs b/scripts/esm-codemod/dirname-to-import-meta.mjs new file mode 100644 index 0000000000..eb8545a6ab --- /dev/null +++ b/scripts/esm-codemod/dirname-to-import-meta.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +// Replaces CommonJS `__dirname` / `__filename` with their ESM equivalents. +// Node >=20.11 exposes `import.meta.dirname` and `import.meta.filename`; under +// ESM the bare `__dirname` / `__filename` globals are undefined (ReferenceError). +// +// path.resolve(__dirname, '..') -> path.resolve(import.meta.dirname, '..') +// basename(__filename) -> basename(import.meta.filename) +// createRequire(__filename) -> createRequire(import.meta.filename) +// +// Apply ONLY to ESM source (.ts/.mts/.gts). Never run it on .cjs/.js CommonJS +// files, where these globals are legitimately defined. +// +// Usage: node dirname-to-import-meta.mjs ... (edits in place) +import { readFileSync, writeFileSync } from 'node:fs'; + +export function transform(src) { + let changed = false; + let code = src + .replace(/\b__dirname\b/g, () => { + changed = true; + return 'import.meta.dirname'; + }) + .replace(/\b__filename\b/g, () => { + changed = true; + return 'import.meta.filename'; + }); + return { code, changed }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let count = 0; + for (const file of process.argv.slice(2)) { + if (/\.(cjs|js)$/.test(file)) continue; // never touch CommonJS files + const { code, changed } = transform(readFileSync(file, 'utf8')); + if (changed) { + writeFileSync(file, code); + count++; + } + } + console.log(`dirname-to-import-meta: ${count} file(s) changed`); +} diff --git a/scripts/esm-codemod/lodash-to-lodash-es.mjs b/scripts/esm-codemod/lodash-to-lodash-es.mjs new file mode 100644 index 0000000000..ec5613c12b --- /dev/null +++ b/scripts/esm-codemod/lodash-to-lodash-es.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +// Rewrites lodash imports to lodash-es (native ESM, named exports). +// +// import merge from 'lodash/merge' -> import { merge } from 'lodash-es' +// import merge from 'lodash/merge.js' -> import { merge } from 'lodash-es' +// import foo from 'lodash/merge' -> import { merge as foo } from 'lodash-es' +// import { a, b } from 'lodash' -> import { a, b } from 'lodash-es' +// import * as _ from 'lodash' -> import * as _ from 'lodash-es' +// +// Usage: node lodash-to-lodash-es.mjs ... (edits in place) +import { readFileSync, writeFileSync } from 'node:fs'; + +const SUBPATH = + /^(\s*)import\s+(\w+)\s+from\s+['"]lodash\/([\w]+)(?:\.js)?['"];?\s*$/; +const BARE = /from\s+(['"])lodash\1/g; + +export function transform(src) { + let changed = false; + const lines = src.split('\n').map((line) => { + const m = line.match(SUBPATH); + if (m) { + const [, indent, binding, method] = m; + const spec = binding === method ? method : `${method} as ${binding}`; + changed = true; + return `${indent}import { ${spec} } from 'lodash-es';`; + } + if (BARE.test(line)) { + changed = true; + return line.replace(BARE, 'from $1lodash-es$1'); + } + return line; + }); + return { code: lines.join('\n'), changed }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let count = 0; + for (const file of process.argv.slice(2)) { + const { code, changed } = transform(readFileSync(file, 'utf8')); + if (changed) { + writeFileSync(file, code); + count++; + console.log(`rewrote ${file}`); + } + } + console.log(`lodash-es: ${count} file(s) changed`); +} diff --git a/scripts/esm-codemod/run.mjs b/scripts/esm-codemod/run.mjs new file mode 100644 index 0000000000..65660f988f --- /dev/null +++ b/scripts/esm-codemod/run.mjs @@ -0,0 +1,106 @@ +#!/usr/bin/env node +// Orchestrates the CS-11449 ts-node -> native-Node ESM codemod across the +// node-run package cluster. Idempotent: safe to re-run. Pass --dry to preview. +// +// node scripts/esm-codemod/run.mjs [--dry] +// +// What it does (see ./README.md for the full error taxonomy): +// 1. Source rewrites over cluster .ts files: +// - add explicit extensions to relative imports (add-relative-extensions) +// - lodash/X -> named import from lodash-es (lodash-to-lodash-es) +// - named CJS imports -> default + destructure (cjs-named-to-default) +// - __dirname/__filename-> import.meta.dirname/filename (dirname-to-import-meta) +// 2. Invocation rewrites over package.json / *.sh / mise-tasks: +// - ts-node --transpileOnly -> node .ts (ts-node-to-node) +// +// NOT automated (do by hand — see README): per-package `exports` maps + `type`, +// `import.meta.url`-as-path bugs, the qunit test-runner bootstrap, removing the +// `ts-node` devDependency, and in-source `spawn('ts-node', ...)` sites. +import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { transform as addExt } from './add-relative-extensions.mjs'; +import { transform as lodashEs } from './lodash-to-lodash-es.mjs'; +import { transform as cjsNamed } from './cjs-named-to-default.mjs'; +import { transform as dirname } from './dirname-to-import-meta.mjs'; +import { transform as tsNode } from './ts-node-to-node.mjs'; + +const REPO = fileURLToPath(new URL('../..', import.meta.url)); +const DRY = process.argv.includes('--dry'); + +// The node-run cluster: packages Node executes directly (not bundled by Vite). +const CLUSTER = [ + 'packages/runtime-common', + 'packages/postgres', + 'packages/billing', + 'packages/realm-server', + 'packages/realm-test-harness', + 'packages/ai-bot', + 'packages/bot-runner', + 'packages/matrix', + 'packages/software-factory', +]; + +function walk(dir, test, out = []) { + let entries; + try { + entries = readdirSync(dir, { withFileTypes: true }); + } catch { + return out; + } + for (const e of entries) { + if (e.name === 'node_modules' || e.name === 'dist' || e.name === '.git') + continue; + const p = join(dir, e.name); + if (e.isDirectory()) walk(p, test, out); + else if (test(p)) out.push(p); + } + return out; +} + +function applySource(file, fns) { + let src = readFileSync(file, 'utf8'); + let changed = false; + for (const fn of fns) { + const r = fn === addExt ? fn(file, src) : fn(src); + if (r.changed) { + src = r.code; + changed = true; + } + } + if (changed && !DRY) writeFileSync(file, src); + return changed; +} + +let srcChanged = 0; +for (const pkg of CLUSTER) { + const files = walk(join(REPO, pkg), (p) => /\.(ts|mts)$/.test(p)); + for (const f of files) { + if (applySource(f, [addExt, lodashEs, cjsNamed, dirname])) srcChanged++; + } +} +console.log(`source rewrites: ${srcChanged} file(s)${DRY ? ' (dry)' : ''}`); + +let invChanged = 0; +const invFiles = [ + ...CLUSTER.map((p) => join(REPO, p, 'package.json')), + ...walk(join(REPO, 'mise-tasks'), () => true), + ...CLUSTER.flatMap((p) => walk(join(REPO, p), (f) => f.endsWith('.sh'))), +]; +for (const f of invFiles) { + let src; + try { + src = readFileSync(f, 'utf8'); + } catch { + continue; + } + const r = tsNode(src); + if (r.changed) { + if (!DRY) writeFileSync(f, r.code); + invChanged++; + } + if (r.skipped?.length) { + for (const s of r.skipped) console.log(` SKIP ${f}: ${s}`); + } +} +console.log(`invocation rewrites: ${invChanged} file(s)${DRY ? ' (dry)' : ''}`); diff --git a/scripts/esm-codemod/ts-node-to-node.mjs b/scripts/esm-codemod/ts-node-to-node.mjs new file mode 100644 index 0000000000..eb88b15275 --- /dev/null +++ b/scripts/esm-codemod/ts-node-to-node.mjs @@ -0,0 +1,66 @@ +#!/usr/bin/env node +// Rewrites `ts-node --transpileOnly ` invocations to `node .ts` +// in package.json scripts, shell scripts, and mise-tasks. Native Node runs the +// TypeScript entry directly (type-stripping is stable in Node >=23.6); the +// extensionless entry must gain a `.ts` suffix because Node does no extension +// search for the CLI entry point. +// +// ts-node --transpileOnly main -> node main.ts +// ts-node --transpileOnly prerender/server -> node prerender/server.ts +// ts-node --transpileOnly scripts/foo.ts -> node scripts/foo.ts +// NODE_NO_WARNINGS=1 ts-node --transpileOnly x -> NODE_NO_WARNINGS=1 node x.ts +// +// Leaves alone (reported, fix by hand): +// - qunit --require ts-node/register/transpile-only ... (needs a node bootstrap) +// - ts-node ... -e / --eval ... (inline code, no type-strip) +// +// Usage: node ts-node-to-node.mjs ... (edits in place) +import { readFileSync, writeFileSync } from 'node:fs'; + +const KNOWN_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/; + +// `ts-node --transpileOnly ` (entry = next non-flag token). The +// separator class allows shell line-continuations (`\` + newline + indent), +// so multi-line `exec ts-node \\\n --transpileOnly main \\` forms convert too. +// The entry class excludes shell metacharacters `)`, `;`, `&`, `|` as well as +// quotes so a `$(ts-node … entry)` command substitution doesn't capture the +// closing `)` and get a `.ts` appended after it. +const INVOCATION = /\bts-node[\s\\]+--transpileOnly[\s\\]+([^\s"'\\);&|]+)/g; + +export function transform(src) { + const skipped = []; + let changed = false; + let code = src.replace(INVOCATION, (full, entry) => { + if (entry.startsWith('-')) { + skipped.push(full.trim()); + return full; // inline -e / --eval etc. + } + changed = true; + const withExt = KNOWN_EXT.test(entry) ? entry : `${entry}.ts`; + return `node ${withExt}`; + }); + // `qunit --require ts-node/register/transpile-only` needs a hand-written + // bootstrap; flag it rather than silently breaking it. + if (/ts-node\/register\/transpile-only/.test(code)) { + skipped.push('qunit --require ts-node/register/transpile-only'); + } + return { code, changed, skipped }; +} + +if (import.meta.url === `file://${process.argv[1]}`) { + let count = 0; + const allSkipped = []; + for (const file of process.argv.slice(2)) { + const { code, changed, skipped } = transform(readFileSync(file, 'utf8')); + if (changed) { + writeFileSync(file, code); + count++; + } + for (const s of skipped) allSkipped.push(`${file}: ${s}`); + } + console.log(`ts-node-to-node: ${count} file(s) changed`); + if (allSkipped.length) { + console.log(`\nSKIPPED — fix by hand (${allSkipped.length}):`); + for (const s of allSkipped) console.log(' ' + s); + } +}