Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…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
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 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'zod' imported from /var/task/apps/demo/node_modules/@objectstack/spec/dist/system/index.mjs |
@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
Added |
|
@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 |
…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
Fixed in fba93ff. Instead of adding deps one at a time, I enhanced |
There was a problem hiding this comment.
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.cjsthat discovers/copies transitive dependencies from pnpm’s virtual store before dereferencing symlinks. - Add
@objectstack/specandzodtoapps/demodevDependencies for deploy defense-in-depth. - Move
@objectstack/specandzodtodependenciesin@objectql/typesto 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
| 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++; |
There was a problem hiding this comment.
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.
| while (contextQueue.length > 0) { | ||
| if (processedContexts.size > MAX_CONTEXTS) { | ||
| console.warn(` ⚠ Reached ${MAX_CONTEXTS} context directories — stopping transitive resolution.`); | ||
| break; | ||
| } | ||
|
|
||
| const ctxDir = contextQueue.shift(); |
There was a problem hiding this comment.
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).
| 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]; |
Vercel serverless function crashes at boot with
ERR_MODULE_NOT_FOUNDfor transitive dependencies (@objectstack/spec,zod,@objectstack/rest, etc.) becausepatch-symlinks.cjsonly 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-levelnode_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.8andzod: ^4.3.6as explicit devDependencies (defense-in-depth)@objectql/types/package.json— Moved@objectstack/specandzodfromdevDependencies→dependencies. The compiled JS emits runtime imports due toz.infer<typeof Data.X>patterns (TypeScript preservestypeofvalue references through import elision)Why transitive resolution fails
With pnpm strict mode, transitive dependencies live in
.pnpm/and are reachable only via symlinks. The oldpatch-symlinks.cjscopied package contents but not the.pnpm/resolution graph, so packages like@objectstack/runtime/dist/index.jscould 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.