feat: add @objectos/ui plugin — ObjectQL-backed view metadata service#234
feat: add @objectos/ui plugin — ObjectQL-backed view metadata service#234
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…rvice Implements UIPlugin following the @objectstack/spec examples/metadata-objectql pattern. View definitions are persisted in database via ObjectQL through a sys_view object, with full CRUD (saveView, loadView, listViews, deleteView). - packages/ui: new plugin package with types, plugin, index, tests - objectstack.config.ts: register UIPlugin in service layer - package.json: add @objectos/ui workspace dependency Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new @objectos/ui kernel plugin that registers the ui CoreServiceName and provides an ObjectQL-backed persistence layer for UI view metadata (sys_view), wiring it into the default stack config.
Changes:
- Introduce new workspace package
packages/ui/withUIPlugin, public exports, types, build/test config, and Jest test suite. - Register
UIPlugininobjectstack.config.tsand add@objectos/uito workspace dependencies. - Update lockfile to include the new workspace importer/deps.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds @objectos/ui workspace linking and package dependency graph entries. |
| package.json | Adds @objectos/ui as a workspace dependency. |
| objectstack.config.ts | Registers new UIPlugin() in the kernel plugin list. |
| packages/ui/package.json | Defines the new @objectos/ui package metadata, build, and test scripts. |
| packages/ui/tsconfig.json | TypeScript build configuration for the new package. |
| packages/ui/jest.config.cjs | Jest + ts-jest ESM configuration and coverage collection. |
| packages/ui/src/types.ts | Defines ViewRecord and re-exports spec kernel compliance types. |
| packages/ui/src/plugin.ts | Implements UIPlugin lifecycle, view CRUD, health/manifests, and sys_view schema registration. |
| packages/ui/src/index.ts | Public API exports for plugin and types. |
| packages/ui/test/plugin.test.ts | Unit tests for lifecycle, CRUD behavior, kernel compliance helpers, and error paths. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| // Obtain ObjectQL service for database access | ||
| try { | ||
| this.objectql = context.getService('objectql') ?? context.getService('data'); | ||
| } catch { | ||
| // ObjectQL might not be available yet; will try again in start() | ||
| } |
There was a problem hiding this comment.
The fallback context.getService('data') is effectively unreachable here: if getService('objectql') throws (likely when only the CoreServiceName data exists), the ?? context.getService('data') expression never runs because control jumps to catch. This will leave this.objectql unset even when the data service is available. Split the lookups (or use hasService) so that a missing objectql service still allows resolving data.
| async saveView(viewName: string, objectName: string, definition: Record<string, unknown>): Promise<ViewRecord> { | ||
| this.ensureObjectQL(); | ||
|
|
||
| const record: Omit<ViewRecord, '_id'> = { | ||
| name: viewName, | ||
| object_name: objectName, | ||
| label: (definition as any).label ?? viewName, | ||
| type: (definition as any).type ?? 'grid', | ||
| definition, | ||
| is_default: false, | ||
| is_public: true, | ||
| }; |
There was a problem hiding this comment.
saveView() persists definition as an object, but the registered ObjectQL schema defines definition as Field.textarea(...) (i.e., a string-like field, as used elsewhere for long text). This type mismatch is likely to break inserts/updates against a real ObjectQL backend. Consider serializing the definition (e.g., JSON) on write and parsing on read/list, and reflect that in ViewRecord (or introduce a separate persisted field like definition_json).
| function createMockContext(objectql?: ReturnType<typeof createInMemoryObjectQL>): { | ||
| context: PluginContext; | ||
| kernel: any; | ||
| } { | ||
| const kernel = { | ||
| getService: jest.fn(), | ||
| services: new Map<string, any>(), | ||
| }; | ||
|
|
||
| if (objectql) { | ||
| kernel.services.set('objectql', objectql); | ||
| } | ||
|
|
There was a problem hiding this comment.
Current tests only register the backend under the service name 'objectql', so they won't catch the case where the kernel exposes the ObjectQL engine under the CoreServiceName 'data' (which this plugin also attempts to support). Add a test that initializes the plugin with only a 'data' service registered (and no 'objectql') to ensure the lookup logic and CRUD paths work as intended.
| label: Field.text({ label: 'Label' }), | ||
| type: Field.select(['grid', 'kanban', 'calendar', 'timeline', 'gantt'], { | ||
| label: 'View Type', | ||
| required: true, | ||
| }), | ||
| definition: Field.textarea({ label: 'View Definition', required: true }), | ||
| is_default: Field.boolean({ label: 'Is Default' }), |
There was a problem hiding this comment.
saveView() accepts any definition.type (and ViewRecord.type is string), but registerViewObject() constrains the persisted type field to a fixed Field.select([...]) list. If callers pass a different view type (or omit type and later migrate), writes can fail at the ObjectQL layer. Either validate/normalize type in saveView() to the allowed set, or loosen the schema/type definition so they stay consistent.
Implements the
uiCoreServiceName service following the spec metadata-objectql example. View definitions are persisted in database via ObjectQL through asys_viewobject rather than filesystem.New package:
packages/ui/UIPluginregisters as'ui'service, definessys_viewObjectQL object on start, exposes CRUD:saveView(name, objectName, definition)— upsertloadView(name)— find by namelistViews(objectName)— filtered + sorteddeleteView(name)— by name lookuphealthCheck(),getManifest(),getStartupResult()following cache/workflow patternsdegradedhealth), throws clear errors on CRUD attemptsIntegration
objectstack.config.tsin the services layer@objectos/ui: workspace:*to root dependenciesUsage
Tests
19 tests covering lifecycle, CRUD operations (save/update/load/list/delete/nested definitions), error handling, and kernel compliance.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.