Skip to content

Commit 13dbcf2

Browse files
os-zhuangclaude
andauthored
refactor(objectql): extract metadata-protocol + add lean ./core entry (ADR-0076 Step 1) (#2415)
* refactor(objectql): extract metadata management into @objectstack/metadata-protocol (ADR-0076 Step 1) protocol.ts (ObjectStackProtocol impl — sys_metadata CRUD, draft/publish, locks, package ownership, diagnostics) plus its sys-metadata-repository, metadata-diagnostics, seed-loader and build-probes helpers were metadata-domain code living inside @objectstack/objectql for historical reasons. They now live in a new @objectstack/metadata-protocol package. - protocol no longer references the concrete ObjectQL class; it is typed against an injected MetadataHostEngine interface (engine still injected at runtime). - Dependency is one-way (objectql -> metadata-protocol); no cycle. - Non-breaking: objectql re-exports every previously public symbol (ObjectStackProtocolImplementation, SysMetadataRepository, SysMetadataEngine, SeedLoaderService, runBuildProbes, ...), so existing imports keep working. - metadata-protocol added to the fixed version group. Verified: full turbo build green (incl. DTS) across the chain; objectql suite 57 files / 718 tests pass (incl. real-engine protocol integration tests); runtime + rest + metadata downstream (25 pkgs) build clean. This is Step 1 of ADR-0076. A later step turns the protocol into a capability plugin so objectql itself stops depending on it (lean engine by construction). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(objectql): add lean `@objectstack/objectql/core` entry + D2 boundary ratchet (ADR-0076) A thin embedder (e.g. the objectbase gateway) can import `@objectstack/objectql/core` to get the engine/registry/hooks/validation surface with NO kernel plugin, kernel factory, or metadata protocol — so `@objectstack/metadata-protocol` (and its 268KB) never enters its bundle/graph. - New src/core.ts lean barrel; objectql now builds two entries (index + core) via a local tsup config; package.json gains the `./core` export. - New core-boundary ratchet test walks core.ts's full import closure and fails if anything reachable imports @objectstack/metadata-protocol / ./plugin / ./kernel-factory. Verified: build emits dist/core.{mjs,js,d.ts} with 0 references to metadata-protocol (vs 5 in index); runtime smoke confirms ObjectQL is exported and ObjectQLPlugin / ObjectStackProtocolImplementation are not; objectql suite 58 files / 719 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(metadata-protocol): add self-contained public-surface smoke `@objectstack/metadata-protocol` had a `test` script but no test files, so `vitest run` exited non-zero and failed CI's Test Core (`turbo run test`). Adds a smoke that imports the package and asserts its public exports + pure helpers without needing a data engine (protocol behavior is covered e2e by the objectql suite). Full `turbo run test` now green (126/126 tasks). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d9fe95f commit 13dbcf2

48 files changed

Lines changed: 469 additions & 40 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@objectstack/metadata-core",
2222
"@objectstack/metadata-fs",
2323
"@objectstack/objectql",
24+
"@objectstack/metadata-protocol",
2425
"@objectstack/observability",
2526
"@objectstack/formula",
2627
"@objectstack/lint",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/objectql': minor
3+
'@objectstack/metadata-protocol': minor
4+
---
5+
6+
Extract metadata management into `@objectstack/metadata-protocol` (ADR-0076)
7+
8+
`protocol.ts` (the `ObjectStackProtocol` implementation — sys_metadata CRUD, draft/publish, locks, package ownership, diagnostics) plus its `sys-metadata-repository`, `metadata-diagnostics`, `seed-loader`, and `build-probes` helpers were metadata-domain code that lived inside `@objectstack/objectql` for historical reasons. They now live in a dedicated **`@objectstack/metadata-protocol`** package.
9+
10+
The protocol no longer depends on the concrete `ObjectQL` class — it is typed against an injected `MetadataHostEngine` interface (the engine is still injected at runtime). Dependency direction is now one-way (`objectql → metadata-protocol`); there is no cycle.
11+
12+
**Non-breaking**: `@objectstack/objectql` re-exports every previously public symbol (`ObjectStackProtocolImplementation`, `SysMetadataRepository`, `SysMetadataEngine`, `SeedLoaderService`, `runBuildProbes`, …), so existing imports keep working.
13+
14+
This is Step 1 of ADR-0076. A later step turns the protocol into a capability plugin so `objectql` itself stops depending on it (making the engine lean by construction).
15+
16+
Also adds a lean **`@objectstack/objectql/core`** entry — the engine/registry/hooks/validation surface only, with no kernel plugin or metadata protocol — so a thin embedder can import just the engine and never pull `@objectstack/metadata-protocol` into its bundle. A boundary ratchet test guards the entry.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# @objectstack/metadata-protocol
2+
3+
The ObjectStack metadata-management protocol, extracted from `@objectstack/objectql` per [ADR-0076](../../docs/adr/0076-objectql-core-tiering.md).
4+
5+
Implements `ObjectStackProtocol`: `sys_metadata` CRUD, draft/publish lifecycle, write locks, package ownership/installation, metadata diagnostics, seed application, and build probes.
6+
7+
It uses a data engine purely as storage + schema registry, injected at runtime via the `MetadataHostEngine` interface — so this package does **not** depend on `@objectstack/objectql`.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "@objectstack/metadata-protocol",
3+
"version": "11.0.0",
4+
"license": "Apache-2.0",
5+
"description": "ObjectStack metadata management protocol: sys_metadata CRUD, draft/publish, locks, package ownership, diagnostics (ADR-0076).",
6+
"type": "module",
7+
"main": "dist/index.js",
8+
"types": "dist/index.d.ts",
9+
"exports": {
10+
".": {
11+
"types": "./dist/index.d.ts",
12+
"import": "./dist/index.js",
13+
"require": "./dist/index.cjs"
14+
}
15+
},
16+
"files": [
17+
"dist",
18+
"README.md"
19+
],
20+
"scripts": {
21+
"build": "tsup",
22+
"dev": "tsc --watch",
23+
"clean": "rm -rf dist",
24+
"test": "vitest run",
25+
"test:watch": "vitest"
26+
},
27+
"keywords": [
28+
"objectstack",
29+
"metadata",
30+
"protocol",
31+
"sys-metadata"
32+
],
33+
"dependencies": {
34+
"@objectstack/core": "workspace:*",
35+
"@objectstack/formula": "workspace:*",
36+
"@objectstack/metadata-core": "workspace:*",
37+
"@objectstack/spec": "workspace:*",
38+
"@objectstack/types": "workspace:*",
39+
"zod": "^4.4.3"
40+
},
41+
"devDependencies": {
42+
"@types/node": "^26.0.0",
43+
"tsup": "^8.5.1",
44+
"typescript": "^6.0.3",
45+
"vitest": "^4.1.9"
46+
},
47+
"author": "ObjectStack",
48+
"repository": {
49+
"type": "git",
50+
"url": "https://github.com/objectstack-ai/framework.git",
51+
"directory": "packages/metadata-protocol"
52+
},
53+
"homepage": "https://objectstack.ai/docs",
54+
"bugs": "https://github.com/objectstack-ai/framework/issues",
55+
"publishConfig": {
56+
"access": "public"
57+
},
58+
"engines": {
59+
"node": ">=18.0.0"
60+
}
61+
}
File renamed without changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import type { IDataEngine } from '@objectstack/core';
4+
5+
/**
6+
* The engine surface the metadata protocol needs from its host (ADR-0076).
7+
*
8+
* The protocol uses the data engine purely as storage + a schema registry; it
9+
* is injected a concrete engine at runtime (today `@objectstack/objectql`'s
10+
* `ObjectQL`). Typing against this interface — instead of the concrete class —
11+
* is what lets `@objectstack/metadata-protocol` avoid a dependency on
12+
* `@objectstack/objectql` (which would otherwise form a cycle, since the
13+
* ObjectQL plugin constructs the protocol).
14+
*/
15+
export interface MetadataHostEngine extends IDataEngine {
16+
/** Schema registry (listItems/getItem/registerItem/getObject/registerObject/installPackage/...). */
17+
registry: any;
18+
/** DDL: create/sync the physical table for an object schema. */
19+
syncObjectSchema(...args: any[]): Promise<any>;
20+
/** DDL: drop the physical table for an object schema. */
21+
dropObjectSchema(...args: any[]): Promise<any>;
22+
// Protocol accesses additional engine members structurally; keep it permissive
23+
// for this relocation (behavior unchanged — the concrete engine is injected).
24+
[key: string]: any;
25+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
//
3+
// Self-contained smoke for @objectstack/metadata-protocol: the package must
4+
// import cleanly and expose its public surface without needing a data engine.
5+
// (Protocol behavior is exercised end-to-end by the @objectstack/objectql suite,
6+
// which injects a real engine.)
7+
8+
import { describe, it, expect } from 'vitest';
9+
import {
10+
ObjectStackProtocolImplementation,
11+
ConcurrentUpdateError,
12+
normalizeViewMetadata,
13+
SysMetadataRepository,
14+
resetEnvWritableMetadataTypes,
15+
computeMetadataDiagnostics,
16+
computeViewReferenceDiagnostics,
17+
decorateMetadataItem,
18+
SeedLoaderService,
19+
runBuildProbes,
20+
} from './index.js';
21+
22+
describe('@objectstack/metadata-protocol public surface', () => {
23+
it('exposes the protocol, repository, seed-loader and probe entry points', () => {
24+
expect(typeof ObjectStackProtocolImplementation).toBe('function');
25+
expect(typeof SysMetadataRepository).toBe('function');
26+
expect(typeof SeedLoaderService).toBe('function');
27+
expect(typeof runBuildProbes).toBe('function');
28+
expect(ConcurrentUpdateError.prototype).toBeInstanceOf(Error);
29+
});
30+
31+
it('exposes pure metadata helpers', () => {
32+
expect(typeof normalizeViewMetadata).toBe('function');
33+
expect(typeof computeMetadataDiagnostics).toBe('function');
34+
expect(typeof computeViewReferenceDiagnostics).toBe('function');
35+
expect(typeof decorateMetadataItem).toBe('function');
36+
expect(typeof resetEnvWritableMetadataTypes).toBe('function');
37+
});
38+
39+
it('normalizeViewMetadata passes a non-view item through without throwing', () => {
40+
const item = { name: 'acct', label: 'Account' };
41+
expect(() => normalizeViewMetadata('object', item, 'acct')).not.toThrow();
42+
});
43+
44+
it('resetEnvWritableMetadataTypes is callable (state reset, no engine needed)', () => {
45+
expect(() => resetEnvWritableMetadataTypes()).not.toThrow();
46+
});
47+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
export { ObjectStackProtocolImplementation, ConcurrentUpdateError, normalizeViewMetadata } from './protocol.js';
4+
5+
export { SysMetadataRepository, resetEnvWritableMetadataTypes } from './sys-metadata-repository.js';
6+
export type {
7+
SysMetadataEngine,
8+
SysMetadataRepositoryOptions,
9+
OverlayState,
10+
ExtendedOperation,
11+
} from './sys-metadata-repository.js';
12+
13+
export {
14+
computeMetadataDiagnostics,
15+
computeViewReferenceDiagnostics,
16+
decorateMetadataItem,
17+
decorateMetadataItems,
18+
} from './metadata-diagnostics.js';
19+
export type { MetadataDiagnostics } from './metadata-diagnostics.js';
20+
21+
export type { MetadataHostEngine } from './host-engine.js';
22+
23+
export { SeedLoaderService } from './seed-loader.js';
24+
export { runBuildProbes } from './build-probes.js';
25+
export type {
26+
RuntimeBuildIssue,
27+
BuildProbeReport,
28+
RunBuildProbesOptions,
29+
ProbeEngine,
30+
ProbeAnalytics,
31+
} from './build-probes.js';
File renamed without changes.

packages/objectql/src/protocol.ts renamed to packages/metadata-protocol/src/protocol.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { ObjectStackProtocol } from '@objectstack/spec/api';
44
import { IDataEngine } from '@objectstack/core';
55
import { readEnvWithDeprecation } from '@objectstack/types';
6-
import type { ObjectQL } from './engine.js';
6+
import type { MetadataHostEngine } from './host-engine.js';
77
import { SysMetadataRepository, type SysMetadataEngine } from './sys-metadata-repository.js';
88
import { ConflictError } from '@objectstack/metadata-core';
99
import type {
@@ -667,7 +667,7 @@ function detectDestructiveObjectChanges(prev: any, next: any): Array<{
667667
}
668668

669669
export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
670-
private engine: ObjectQL;
670+
private engine: MetadataHostEngine;
671671
private getServicesRegistry?: () => Map<string, any>;
672672
private getFeedService?: () => IFeedService | undefined;
673673
/**
@@ -694,7 +694,7 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
694694
getFeedService?: () => IFeedService | undefined,
695695
environmentId?: string,
696696
) {
697-
this.engine = engine as ObjectQL;
697+
this.engine = engine as MetadataHostEngine;
698698
this.getServicesRegistry = getServicesRegistry;
699699
this.getFeedService = getFeedService;
700700
this.environmentId = environmentId;

0 commit comments

Comments
 (0)