Skip to content

feat: add plugin-setup and services to Studio deployment#1063

Merged
hotlong merged 2 commits intomainfrom
claude/evaluate-plugins-and-services
Apr 2, 2026
Merged

feat: add plugin-setup and services to Studio deployment#1063
hotlong merged 2 commits intomainfrom
claude/evaluate-plugins-and-services

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Apr 2, 2026

Studio Vercel deployment was missing plugin-setup, preventing access to the Setup UI. This adds essential plugins and services for comprehensive testing and feature parity between local and production environments.

Changes

Dependencies (apps/studio/package.json)

  • Added @objectstack/plugin-setup - Setup App for platform administration
  • Added @objectstack/service-automation - Workflow/automation engine
  • Added @objectstack/service-analytics - Analytics and reporting

Vercel Kernel (apps/studio/server/index.ts)

  • Registered SetupPlugin, AutomationServicePlugin, AnalyticsServicePlugin
  • Maintains registration order: Auth/Security/Audit → Services → Setup

MSW/Browser Kernel (apps/studio/src/mocks/createKernel.ts)

  • Added FeedServicePlugin, MetadataPlugin, AIServicePlugin for parity
  • Added AutomationServicePlugin, AnalyticsServicePlugin, SetupPlugin
  • Ensures MSW mode matches server mode capabilities

Browser Polyfills (apps/studio/mocks/node-polyfills.ts)

  • Added randomUUID export (required by service-ai crypto usage)
  • Added renameSync export (required by driver-memory)

Impact

Bundle size increases from ~735 KB to ~3,085 KB (839 KB gzipped) due to additional services. Acceptable trade-off for full testing environment with Setup UI, automation workflows, and analytics capabilities.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 2, 2026

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

Project Deployment Actions Updated (UTC)
objectstack-play Ready Ready Preview, Comment Apr 2, 2026 10:27am
spec Ready Ready Preview, Comment Apr 2, 2026 10:27am

Request Review

@github-actions github-actions bot added dependencies Pull requests that update a dependency file size/s labels Apr 2, 2026
@hotlong hotlong marked this pull request as ready for review April 2, 2026 10:28
Copilot AI review requested due to automatic review settings April 2, 2026 10:28
@hotlong hotlong merged commit a06e184 into main Apr 2, 2026
14 of 16 checks passed
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

This PR updates the Studio Vercel + MSW/browser kernels to include the Setup plugin and additional platform services, aiming to improve feature parity between local and deployed environments so the Setup UI is available in production-like deployments.

Changes:

  • Add Studio dependencies for @objectstack/plugin-setup, @objectstack/service-automation, and @objectstack/service-analytics (and lockfile updates).
  • Register Setup + Automation + Analytics plugins in the Vercel server kernel.
  • Expand the MSW/browser kernel to register additional services/plugins for parity, and extend browser node polyfills used by Vite aliases.

Reviewed changes

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

Show a summary per file
File Description
pnpm-lock.yaml Adds workspace links for the newly added Studio dependencies.
apps/studio/package.json Adds Setup + Automation + Analytics packages to Studio dependencies.
apps/studio/server/index.ts Registers Automation/Analytics/Setup plugins in the Vercel kernel bootstrap.
apps/studio/src/mocks/createKernel.ts Registers additional services/plugins in the MSW/browser kernel factory for parity.
apps/studio/mocks/node-polyfills.ts Adds missing renameSync and randomUUID exports used by browser builds.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines 131 to +135
await kernel.use(new MetadataPlugin({ watch: false }));
await kernel.use(new AIServicePlugin());
await kernel.use(new AutomationServicePlugin());
await kernel.use(new AnalyticsServicePlugin());
await kernel.use(new SetupPlugin());
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

SetupPlugin is registered after plugins that contribute to the setupNav service (e.g., AIServicePlugin, and on the server also Auth/Security/Audit). Because SetupPlugin only registers setupNav during its own init, those earlier plugins won't be able to contribute navigation items, resulting in an empty/incomplete Setup App. Register SetupPlugin before any plugins that call ctx.getService('setupNav') (typically immediately after ObjectQLPlugin / manifest is available, and before Auth/Security/Audit/AI/etc).

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +90
await kernel.use(new FeedServicePlugin());
await kernel.use(new MetadataPlugin({ watch: false }));
await kernel.use(new AIServicePlugin());
await kernel.use(new AutomationServicePlugin());
await kernel.use(new AnalyticsServicePlugin());
await kernel.use(new SetupPlugin());
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

SetupPlugin is registered last, after AIServicePlugin / other services. Since contributing plugins attempt to call ctx.getService('setupNav') during their init, and setupNav is only registered by SetupPlugin.init, those contributions will be skipped and the Setup App will have no navigation. Register SetupPlugin before the plugins that contribute (at least before AIServicePlugin, and ideally before any service/plugin that adds Setup navigation).

Suggested change
await kernel.use(new FeedServicePlugin());
await kernel.use(new MetadataPlugin({ watch: false }));
await kernel.use(new AIServicePlugin());
await kernel.use(new AutomationServicePlugin());
await kernel.use(new AnalyticsServicePlugin());
await kernel.use(new SetupPlugin());
// SetupPlugin must be registered BEFORE services that contribute to Setup navigation
await kernel.use(new SetupPlugin());
await kernel.use(new FeedServicePlugin());
await kernel.use(new MetadataPlugin({ watch: false }));
await kernel.use(new AIServicePlugin());
await kernel.use(new AutomationServicePlugin());
await kernel.use(new AnalyticsServicePlugin());

Copilot uses AI. Check for mistakes.
export const rename = async () => {};
export const renameSync = () => {};
export const createHash = () => ({ update: () => ({ digest: () => '' }) });
export const randomUUID = () => '00000000-0000-0000-0000-000000000000';
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

randomUUID polyfill currently returns a constant all-zero UUID. service-ai uses this to generate conversation/message IDs; returning a constant will cause ID collisions and can break any code that assumes IDs are unique (e.g., inserts keyed by id, UI lists keyed by id). Prefer delegating to globalThis.crypto.randomUUID() when available, or generate a simple unique/monotonic UUID fallback (e.g., counter + timestamp) so IDs remain stable but non-colliding in the browser build.

Suggested change
export const randomUUID = () => '00000000-0000-0000-0000-000000000000';
// randomUUID polyfill – prefer native crypto.randomUUID, then crypto.getRandomValues, then a monotonic fallback
let __objectstackUuidCounter = 0;
const __objectstackMonotonicUUID = (): string => {
const now = Date.now();
__objectstackUuidCounter = (__objectstackUuidCounter + 1) & 0xfffff; // wrap at 20 bits
const counter = __objectstackUuidCounter;
const random = Math.floor(Math.random() * 0xffffffff);
const timeHex = now.toString(16).padStart(12, '0');
const counterHex = counter.toString(16).padStart(5, '0');
const randomHex = random.toString(16).padStart(8, '0');
// Format to look like a UUID, but uniqueness is based on time + counter + randomness
return (
timeHex.slice(0, 8) + '-' +
timeHex.slice(8, 12) + '-' +
'4' + counterHex.slice(0, 3) + '-' + // version-like nibble
'a' + counterHex.slice(3, 5) + '-' + // variant-like nibble
randomHex + timeHex.slice(0, 4)
);
};
export const randomUUID = (): string => {
try {
const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined;
const cryptoObj = g && g.crypto;
if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {
return cryptoObj.randomUUID();
}
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
const bytes = new Uint8Array(16);
cryptoObj.getRandomValues(bytes);
// Per RFC 4122: set version to 4 and variant to RFC 4122
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const byteToHex: string[] = [];
for (let i = 0; i < 256; i++) {
byteToHex[i] = (i + 0x100).toString(16).slice(1);
}
return (
byteToHex[bytes[0]] +
byteToHex[bytes[1]] +
byteToHex[bytes[2]] +
byteToHex[bytes[3]] +
'-' +
byteToHex[bytes[4]] +
byteToHex[bytes[5]] +
'-' +
byteToHex[bytes[6]] +
byteToHex[bytes[7]] +
'-' +
byteToHex[bytes[8]] +
byteToHex[bytes[9]] +
'-' +
byteToHex[bytes[10]] +
byteToHex[bytes[11]] +
byteToHex[bytes[12]] +
byteToHex[bytes[13]] +
byteToHex[bytes[14]] +
byteToHex[bytes[15]]
);
}
} catch {
// Ignore and fall through to monotonic fallback
}
return __objectstackMonotonicUUID();
};

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

dependencies Pull requests that update a dependency file size/s

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants