Skip to content

fix: enhance patch-symlinks.cjs to auto-resolve transitive dependencies for Vercel deployment#430

Merged
hotlong merged 5 commits intomainfrom
copilot/fix-module-not-found-error
Mar 21, 2026
Merged

fix: enhance patch-symlinks.cjs to auto-resolve transitive dependencies for Vercel deployment#430
hotlong merged 5 commits intomainfrom
copilot/fix-module-not-found-error

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 21, 2026

Vercel serverless function crashes at boot with ERR_MODULE_NOT_FOUND for transitive dependencies (@objectstack/spec, zod, @objectstack/rest, etc.) because patch-symlinks.cjs only dereferenced direct dependencies' symlinks but didn't resolve the transitive dependencies those packages needed at runtime.

Changes

  • apps/demo/scripts/patch-symlinks.cjs — Enhanced with a new Phase 1 that automatically resolves and copies ALL transitive dependencies before dereferencing symlinks. The script walks each package's pnpm virtual store context (.pnpm/<name>@<ver>/node_modules/) and copies any missing sibling dependency into the top-level node_modules/, repeating until the full transitive closure is present (~364 packages). Phase 2 then dereferences all remaining symlinks as before. Includes a safety limit to prevent runaway iteration in pathological dependency graphs.
  • apps/demo/package.json — Added @objectstack/spec: ^3.2.8 and zod: ^4.3.6 as explicit devDependencies (defense-in-depth)
  • @objectql/types/package.json — Moved @objectstack/spec and zod from devDependenciesdependencies. The compiled JS emits runtime imports due to z.infer<typeof Data.X> patterns (TypeScript preserves typeof value references through import elision)

Why transitive resolution fails

With pnpm strict mode, transitive dependencies live in .pnpm/ and are reachable only via symlinks. The old patch-symlinks.cjs copied package contents but not the .pnpm/ resolution graph, so packages like @objectstack/runtime/dist/index.js could no longer resolve transitive deps like @objectstack/rest, pino, better-auth, mingo, etc. at runtime in the Vercel function. The new Phase 1 eliminates this by walking the pnpm virtual store structure to discover and copy all transitive dependencies automatically.


⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectql Ready Ready Preview, Comment Mar 21, 2026 6:49am
objectql-demo Ready Ready Preview, Comment Mar 21, 2026 6:49am

Request Review

…ep in @objectql/types

The Vercel serverless function failed at runtime with ERR_MODULE_NOT_FOUND
because @objectstack/core could not resolve @objectstack/spec after pnpm
symlinks were dereferenced by patch-symlinks.cjs.

1. apps/demo/package.json: add @objectstack/spec ^3.2.8 as devDependency
   so it is present in node_modules and dereferenced for Vercel bundling.

2. @objectql/types/package.json: move @objectstack/spec and zod from
   devDependencies to dependencies because compiled JS output contains
   runtime imports via z.infer<typeof Data.X> patterns.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectql/sessions/367bb53d-1d41-42c8-b300-0779006e45dc
Copilot AI changed the title [WIP] Fix module not found error for @objectstack/spec fix: add @objectstack/spec to apps/demo deps, promote to runtime dep in @objectql/types Mar 21, 2026
Copilot AI requested a review from hotlong March 21, 2026 06:12
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Mar 21, 2026

@copilot Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /var/task/apps/demo/node_modules/@objectstack/spec/dist/system/index.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:316:9)
at packageResolve (node:internal/modules/esm/resolve:774:81)
at moduleResolve (node:internal/modules/esm/resolve:864:18)
at moduleResolveWithNodePath (node:internal/modules/esm/resolve:996:14)
at defaultResolve (node:internal/modules/esm/resolve:1039:79)
at #cachedDefaultResolve (node:internal/modules/esm/loader:718:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:735:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:764:52)
at #resolve (node:internal/modules/esm/loader:700:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:35) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /var/task/apps/demo/node_modules/@objectstack/spec/dist/system/index.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:316:9)
at packageResolve (node:internal/modules/esm/resolve:774:81)
at moduleResolve (node:internal/modules/esm/resolve:864:18)
at moduleResolveWithNodePath (node:internal/modules/esm/resolve:996:14)
at defaultResolve (node:internal/modules/esm/resolve:1039:79)
at #cachedDefaultResolve (node:internal/modules/esm/loader:718:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:735:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:764:52)
at #resolve (node:internal/modules/esm/loader:700:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:35) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.

@objectstack/spec depends on zod at runtime. After patch-symlinks.cjs
dereferences pnpm symlinks for Vercel bundling, zod must be present as
an explicit dependency in apps/demo/node_modules/.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectql/sessions/e3b65dd9-3cb4-4a2e-88fa-2100507f9646
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 21, 2026

@copilot Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /var/task/apps/demo/node_modules/@objectstack/spec/dist/system/index.mjs
at Object.getPackageJSONURL (node:internal/m...

Added zod: ^4.3.6 as an explicit devDependency in apps/demo/package.json in 997426e. Same root cause — @objectstack/spec depends on zod at runtime, but after patch-symlinks.cjs dereferences pnpm symlinks, transitive dependencies aren't in the resolution path. The explicit declaration ensures zod lands at the top level of node_modules/ for Vercel bundling.

Copilot AI changed the title fix: add @objectstack/spec to apps/demo deps, promote to runtime dep in @objectql/types fix: add @objectstack/spec and zod to apps/demo deps, promote to runtime dep in @objectql/types Mar 21, 2026
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Mar 21, 2026

@copilot 2026-03-21 06:36:29.410 [error] Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@objectstack/rest' imported from /var/task/apps/demo/node_modules/@objectstack/runtime/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:316:9)
at packageResolve (node:internal/modules/esm/resolve:774:81)
at moduleResolve (node:internal/modules/esm/resolve:864:18)
at moduleResolveWithNodePath (node:internal/modules/esm/resolve:996:14)
at defaultResolve (node:internal/modules/esm/resolve:1039:79)
at #cachedDefaultResolve (node:internal/modules/esm/loader:718:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:735:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:764:52)
at #resolve (node:internal/modules/esm/loader:700:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:35) {
code: 'ERR_MODULE_NOT_FOUND'
}
Node.js process exited with exit status: 1. The logs above can help with debugging the issue.

…encies

Instead of manually listing each transitive dep in apps/demo/package.json,
the script now walks each package's pnpm virtual store context
(.pnpm/<name>@<ver>/node_modules/) and copies any missing dependency
into the top-level node_modules/ before dereferencing symlinks.
This handles @objectstack/rest, pino, better-auth, mingo, and all
other transitive deps automatically (~364 packages).

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Agent-Logs-Url: https://github.com/objectstack-ai/objectql/sessions/41d33284-6232-4795-a3e5-60ed24fddd69
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 21, 2026

@copilot 2026-03-21 06:36:29.410 [error] Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@objectstack/rest' imported from /var/task/apps/demo/node_modules/@objectstack/runtime/dist/index.js
at ...

Fixed in fba93ff. Instead of adding deps one at a time, I enhanced patch-symlinks.cjs with a new Phase 1 that automatically resolves ALL transitive dependencies. It walks each package's pnpm virtual store context (.pnpm/<name>@<ver>/node_modules/) and copies any missing sibling dependency into the top-level node_modules/ before dereferencing symlinks. This handles @objectstack/rest, pino, better-auth, mingo, and all ~364 other transitive deps automatically — no more whack-a-mole.

Copilot AI changed the title fix: add @objectstack/spec and zod to apps/demo deps, promote to runtime dep in @objectql/types fix: enhance patch-symlinks.cjs to auto-resolve transitive dependencies for Vercel deployment Mar 21, 2026
@hotlong hotlong marked this pull request as ready for review March 21, 2026 06:54
Copilot AI review requested due to automatic review settings March 21, 2026 06:54
@hotlong hotlong merged commit 96b4a13 into main Mar 21, 2026
6 checks passed
@hotlong hotlong deleted the copilot/fix-module-not-found-error branch March 21, 2026 06:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves Vercel deploy reliability for apps/demo by ensuring pnpm-managed dependencies are present as real directories (not symlinks) and that runtime-required transitive dependencies are available after the symlink patching step.

Changes:

  • Add a Phase 1 to patch-symlinks.cjs that discovers/copies transitive dependencies from pnpm’s virtual store before dereferencing symlinks.
  • Add @objectstack/spec and zod to apps/demo devDependencies for deploy defense-in-depth.
  • Move @objectstack/spec and zod to dependencies in @objectql/types to ensure runtime availability.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/demo/scripts/patch-symlinks.cjs Adds transitive dependency resolution phase ahead of symlink dereferencing for Vercel bundling.
apps/demo/package.json Declares @objectstack/spec and zod explicitly for demo build/runtime stability on Vercel.
packages/foundation/types/package.json Promotes @objectstack/spec and zod to runtime deps for @objectql/types.
pnpm-lock.yaml Updates lockfile to reflect new dependency declarations.
CHANGELOG.md Documents the Vercel/pnpm symlink patch improvements and dependency declaration changes.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +136 to +156
const processEntry = (depName, entryPath) => {
const targetPath = path.join(nmAbs, depName);
if (fs.existsSync(targetPath)) return; // already present

// Resolve the real path of this pnpm-store entry.
let realDepPath;
try {
const stat = fs.lstatSync(entryPath);
realDepPath = stat.isSymbolicLink()
? fs.realpathSync(entryPath)
: entryPath;
} catch { return; }

// Ensure scope directory exists for scoped packages.
if (depName.includes('/')) {
fs.mkdirSync(path.join(nmAbs, depName.split('/')[0]), { recursive: true });
}

console.log(` + ${depName}`);
fs.cpSync(realDepPath, targetPath, { recursive: true, dereference: true });
totalAdded++;
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

resolveTransitiveDeps() copies packages directly into node_modules via fs.cpSync(). If the copy fails part-way (disk full, permission error, interrupted install), it can leave a partially-copied directory; subsequent runs will skip it due to existsSync(targetPath) and you can end up with a broken/corrupt dependency tree. Consider mirroring derefSymlink()’s safer temp-copy + rename approach (and cleaning up tmp dirs on failure) for each dep copy.

Copilot uses AI. Check for mistakes.
Comment on lines +122 to +128
while (contextQueue.length > 0) {
if (processedContexts.size > MAX_CONTEXTS) {
console.warn(` ⚠ Reached ${MAX_CONTEXTS} context directories — stopping transitive resolution.`);
break;
}

const ctxDir = contextQueue.shift();
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

resolveTransitiveDeps() uses contextQueue.shift() in a loop. Array#shift() is O(n) because it re-indexes the array each time, which can make this phase noticeably slower as the queue grows. Use an index pointer (e.g., for (let i=0; i<queue.length; i++)) or a small deque implementation to keep it O(n).

Suggested change
while (contextQueue.length > 0) {
if (processedContexts.size > MAX_CONTEXTS) {
console.warn(` ⚠ Reached ${MAX_CONTEXTS} context directories — stopping transitive resolution.`);
break;
}
const ctxDir = contextQueue.shift();
// Use index-based iteration to avoid O(n) Array#shift() cost.
for (let i = 0; i < contextQueue.length; i++) {
if (processedContexts.size > MAX_CONTEXTS) {
console.warn(` ⚠ Reached ${MAX_CONTEXTS} context directories — stopping transitive resolution.`);
break;
}
const ctxDir = contextQueue[i];

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants