Skip to content

Commit 7a6e543

Browse files
Copilothotlong
andcommitted
docs: update DESIGN_CORE_REFACTOR.md — upstream @objectstack/spec now meets all refactoring prerequisites
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 34e774d commit 7a6e543

1 file changed

Lines changed: 96 additions & 54 deletions

File tree

docs/DESIGN_CORE_REFACTOR.md

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
> **Author:** ObjectStack Architecture Team
44
> **Date:** 2026-02-10
5-
> **Status:** Draft / RFC
5+
> **Updated:** 2026-02-11
6+
> **Status:** Ready for Implementation — Upstream prerequisites met
67
> **Scope:** Decompose `@objectql/core`, align with `@objectstack/objectql` plugin extension model
7-
> **Upstream Repo:** https://github.com/objectstack-ai/spec (v2.0.5)
8+
> **Upstream Repo:** https://github.com/objectstack-ai/spec (v2.0.5+, latest commit `33646a7`)
89
> **Local Repo:** https://github.com/objectstack-ai/objectql (v4.2.0)
910
1011
---
@@ -44,7 +45,7 @@ This document proposes **decomposing `@objectql/core`** from a monolithic runtim
4445
| `@objectql/core` LOC | ~3,500 | ~800 (thin re-exports + plugin orchestrator) |
4546
| Duplication with upstream | High (CRUD, hooks, middleware, repository, protocol) | Near-zero |
4647
| Plugin count (new) | 0 dedicated | 2 new plugins extracted |
47-
| Upstream PRs needed | 0 | 1-2 PRs to `@objectstack/spec` |
48+
| Upstream PRs needed | 0 | ~~1-2 PRs to `@objectstack/spec`~~ → ✅ 0 (all merged) |
4849

4950
---
5051

@@ -421,50 +422,27 @@ export function createObjectQLKernel(options: ObjectQLKernelOptions = {}) {
421422

422423
### 5.1 Overview
423424

424-
To support the local project's features as plugins, the upstream `@objectstack/objectql` engine needs **4 enhancement areas**:
425+
> **🟢 Status (2026-02-11): All upstream prerequisites are now met.**
426+
> Evaluated against upstream commit [`33646a7`](https://github.com/objectstack-ai/spec/commit/33646a782cc5cb076e404a3178e84b8fd5fd7087) (2026-02-11).
425427
426-
| # | Change | Upstream Package | Type | Priority |
427-
|---|--------|-----------------|------|----------|
428-
| 5.1.1 | Query Profiling Hook Extension Point | `@objectstack/objectql` | Feature | 🔴 High |
429-
| 5.1.2 | Optimization Integration Hooks | `@objectstack/core` | Feature | 🟡 Medium |
428+
The following upstream enhancements were identified as prerequisites for this refactoring. All have been implemented in the upstream `@objectstack/spec` repository:
429+
430+
| # | Change | Upstream Package | Type | Status |
431+
|---|--------|-----------------|------|--------|
432+
| 5.1.1 | Query Profiling Hook Extension Point | `@objectstack/objectql` | Feature |**Met** — Middleware API exists; `OperationContext` and `EngineMiddleware` types exported |
433+
| 5.1.2 | Optimization Integration Hooks | `@objectstack/core` | Feature |**Met**`replaceService<T>()` added to `PluginContext` interface and implemented |
430434

431435
> **Removed from scope:**
432436
> - ~~Gateway / Protocol Router Extension Point~~ — The upstream server (`@objectstack/plugin-hono-server`) already provides the API layer; no gateway plugin is needed.
433437
> - ~~AI Service Registry Interface~~ — AI model/prompt management is handled by a separate dedicated project.
434438
435439
### 5.1.1 Query Profiling Hook Extension Point
436440

437-
**Problem:** The local `QueryService` wraps driver CRUD calls with profiling (execution time, rows scanned, index usage). The upstream engine has no extension point for injecting profiling around driver calls.
438-
439-
**Proposed Change (upstream `@objectstack/objectql/engine.ts`):**
441+
**Problem:** The local `QueryService` wraps driver CRUD calls with profiling (execution time, rows scanned, index usage). The upstream engine had no extension point for injecting profiling around driver calls.
440442

441-
```typescript
442-
// Add profiling hooks in the engine lifecycle
443-
export interface QueryProfiler {
444-
beforeExecute(context: {
445-
object: string;
446-
operation: string;
447-
ast?: QueryAST;
448-
}): void;
449-
afterExecute(context: {
450-
object: string;
451-
operation: string;
452-
ast?: QueryAST;
453-
result?: unknown;
454-
executionTimeMs: number;
455-
error?: Error;
456-
}): void;
457-
}
443+
**Resolution: ✅ Use existing middleware — no dedicated `QueryProfiler` interface needed.**
458444

459-
// In ObjectQL class:
460-
private profiler?: QueryProfiler;
461-
462-
registerProfiler(profiler: QueryProfiler) {
463-
this.profiler = profiler;
464-
}
465-
```
466-
467-
**Alternative:** Achieve via middleware (already exists). The middleware chain is sufficient if the local `QueryPlugin` registers a profiling middleware:
445+
The upstream `ObjectQL` engine already provides `registerMiddleware(fn, options)` with onion-model execution, and exports both `OperationContext` and `EngineMiddleware` types for plugin authors. The local `QueryPlugin` can register a profiling middleware directly:
468446

469447
```typescript
470448
// In QueryPlugin.init():
@@ -476,38 +454,52 @@ ql.registerMiddleware(async (opCtx, next) => {
476454
});
477455
```
478456

479-
**Recommendation:** Use existing middleware — **no upstream change needed** for profiling.
457+
**Upstream evidence:**
458+
- `packages/objectql/src/engine.ts``registerMiddleware(fn: EngineMiddleware, options?: { object?: string }): void`
459+
- `packages/objectql/src/index.ts``export type { OperationContext, EngineMiddleware } from './engine.js'`
460+
- Merged via [PR #597](https://github.com/objectstack-ai/spec/pull/597) (2026-02-11)
480461

481462
### 5.1.2 Optimization Integration Hooks
482463

483-
**Problem:** The local `OptimizationsPlugin` wants to replace/enhance kernel internals (metadata registry, hook manager, connection pooling). The upstream kernel doesn't expose these as replaceable services.
464+
**Problem:** The local `OptimizationsPlugin` wants to replace/enhance kernel internals (metadata registry, hook manager, connection pooling). The upstream kernel didn't expose these as replaceable services.
484465

485-
**Proposed Change (upstream `@objectstack/core`):**
466+
**Resolution: ✅ `replaceService<T>()` API added to upstream `PluginContext`.**
486467

487-
Add a **service replacement** API to `PluginContext`:
468+
The upstream `@objectstack/core` now provides a first-class `replaceService` method:
488469

489470
```typescript
490-
// In PluginContext:
471+
// PluginContext interface (packages/core/src/types.ts):
491472
replaceService<T>(name: string, implementation: T): void;
492473
```
493474

494-
**Alternative:** Use **decorator pattern** — the optimization plugin wraps existing services:
475+
This is fully implemented in:
476+
- `ObjectKernel` (`packages/core/src/kernel.ts`) — validates service exists, replaces in both service map and `PluginLoader`
477+
- `ObjectKernelBase` (`packages/core/src/kernel-base.ts`) — supports both `Map` and `IServiceRegistry` backends
478+
- `PluginLoader` (`packages/core/src/plugin-loader.ts`) — `replaceService()` with existence validation
479+
480+
**Usage (in OptimizationsPlugin):**
495481

496482
```typescript
497483
// In OptimizationsPlugin.start():
498484
const existingMetadata = ctx.getService('metadata');
499485
const optimizedMetadata = new OptimizedMetadataRegistry(existingMetadata);
500-
ctx.registerService('metadata', optimizedMetadata); // override
486+
ctx.replaceService('metadata', optimizedMetadata); // first-class API
501487
```
502488

503-
**Recommendation:** The decorator approach works today. An upstream `replaceService` API would be cleaner but is not blocking.
489+
**Upstream evidence:**
490+
- Merged via commit [`b6b411e`](https://github.com/objectstack-ai/spec/commit/b6b411e8db38065ce1e0f95ba9b5b3ce26ab8bad) — "feat(core): add replaceService to PluginContext for optimization integration hooks"
491+
- Validation fix via commit [`a5a5742`](https://github.com/objectstack-ai/spec/commit/a5a5742289d7cf3d0cadb59bcedc2507a0401e63) — "fix: add validation to PluginLoader.replaceService for consistency"
504492

505493
### 5.2 Summary of Upstream PRs
506494

507-
| PR | Target Repo | Target Package | Description | Blocking? |
508-
|----|------------|----------------|-------------|-----------|
509-
| **PR-1** | objectstack-ai/spec | `@objectstack/objectql` | Export `OperationContext` and `EngineMiddleware` types for plugin authors | 🔴 Yes |
510-
| **PR-2** | objectstack-ai/spec | `@objectstack/core` | Add `replaceService` to `PluginContext` interface | 🟡 No (decorator workaround) |
495+
> **🟢 All upstream prerequisites are complete. No blocking PRs remain.**
496+
497+
| PR | Target Repo | Target Package | Description | Status |
498+
|----|------------|----------------|-------------|--------|
499+
| **PR-1** | objectstack-ai/spec | `@objectstack/objectql` | Export `OperationContext` and `EngineMiddleware` types for plugin authors |**Merged** (PR #597) |
500+
| **PR-2** | objectstack-ai/spec | `@objectstack/core` | Add `replaceService` to `PluginContext` interface |**Merged** (commit `b6b411e`) |
501+
502+
Additionally, the upstream has added **16+ formal service contract interfaces** in `@objectstack/spec/contracts` (PRs [#599](https://github.com/objectstack-ai/spec/pull/599), [#600](https://github.com/objectstack-ai/spec/pull/600)), including `IMetadataService`, `IAuthService`, `IAnalyticsService`, `IAIService`, `IRealtimeService`, and more. These contracts further strengthen the plugin boundary for the local refactoring.
511503

512504
> **Removed from scope:**
513505
> - ~~Add `gateway` to `CoreServiceName` enum~~ — Gateway plugin is not needed (upstream server handles API).
@@ -666,7 +658,7 @@ export { QueryService } from '@objectql/plugin-query';
666658

667659
### Phase 3: Core Slimming (Week 3)
668660

669-
- [ ] Submit upstream PRs (§5.2)
661+
- [x] ~~Submit upstream PRs (§5.2)~~ — All upstream prerequisites already merged (see §5)
670662
- [ ] Remove `app.ts` (local ObjectQL class)
671663
- [ ] Remove `protocol.ts` (duplicated)
672664
- [ ] Remove `repository.ts` (duplicated)
@@ -679,8 +671,8 @@ export { QueryService } from '@objectql/plugin-query';
679671

680672
### Phase 4: Upstream Integration (Week 4+)
681673

682-
- [ ] Merge upstream PRs
683-
- [ ] Update `@objectstack/*` dependency versions
674+
- [x] ~~Merge upstream PRs~~ — All prerequisites merged as of 2026-02-11
675+
- [ ] Update `@objectstack/*` dependency versions to include `replaceService` and type exports
684676
- [ ] Remove local workarounds once upstream changes are released
685677
- [ ] Tag v5.0 release
686678

@@ -692,7 +684,7 @@ export { QueryService } from '@objectql/plugin-query';
692684

693685
| Risk | Probability | Impact | Mitigation |
694686
|------|-------------|--------|------------|
695-
| **Upstream rejects PRs** | Medium | High | All changes have local workarounds (decorators, service registration) |
687+
| ~~**Upstream rejects PRs**~~ | ~~Medium~~ | ~~High~~ | **Eliminated** — All upstream PRs merged as of 2026-02-11 |
696688
| **API breaking changes** | Medium | High | Phase approach with deprecated re-exports; v5.0 major release for removals |
697689
| **Hook behavior difference** | Low | Medium | Comprehensive hook integration tests; compare local vs upstream execution order |
698690
| **Performance regression** | Low | Medium | Benchmark before/after; `OptimizationsPlugin` preserves all existing optimizations |
@@ -826,8 +818,58 @@ export type { DriverInterface, StateMachineConfig, ... } // spec types
826818
| **FQN** | Fully Qualified Name — namespaced identifier for objects (e.g., `com.acme.crm.user`) |
827819
| **QueryAST** | Abstract Syntax Tree for queries (from `@objectstack/spec/data`) |
828820
| **HookContext** | Context object passed to lifecycle hooks (from `@objectstack/spec/data`) |
829-
| **OperationContext** | Context for middleware chain (operation, AST, result) |
821+
| **OperationContext** | Context for middleware chain (operation, AST, result) — exported from `@objectstack/objectql` |
822+
| **EngineMiddleware** | Onion-model middleware type for data operations — exported from `@objectstack/objectql` |
830823
| **CoreServiceName** | Enum of recognized kernel services (metadata, data, analytics, auth, etc.) |
824+
| **replaceService** | `PluginContext` method for swapping kernel service implementations (added upstream for §5.1.2) |
825+
826+
### 10.5 Upstream Compliance Verification (2026-02-11)
827+
828+
Evaluated against upstream `@objectstack/spec` commit [`33646a7`](https://github.com/objectstack-ai/spec/commit/33646a782cc5cb076e404a3178e84b8fd5fd7087).
829+
830+
#### Exports Verification (`@objectstack/objectql`)
831+
832+
The following types and classes are confirmed exported from `packages/objectql/src/index.ts`:
833+
834+
| Export | Type | Required By |
835+
|--------|------|-------------|
836+
| `ObjectQL` | Class | §4.4 (re-export as canonical engine) |
837+
| `ObjectRepository` | Class | §4.4 (re-export) |
838+
| `ScopedContext` | Class | §4.4 (re-export) |
839+
| `SchemaRegistry` | Class | §4.4 (re-export) |
840+
| `ObjectQLPlugin` | Class | §4.3 (kernel bootstrap) |
841+
| `ObjectStackProtocolImplementation` | Class | §4.4 (re-export) |
842+
| `MetadataFacade` | Class | §4.4 (re-export) |
843+
| `computeFQN`, `parseFQN` | Function | §4.4 (re-export) |
844+
| `RESERVED_NAMESPACES`, `DEFAULT_OWNER_PRIORITY`, `DEFAULT_EXTENDER_PRIORITY` | Constant | §4.4 (re-export) |
845+
| `ObjectContributor` | Type | §4.4 (re-export) |
846+
| `ObjectQLHostContext` | Type | §4.4 (re-export) |
847+
| `HookHandler`, `HookEntry` | Type | §4.4 (re-export) |
848+
| `OperationContext` | Type | §5.1.1 (query profiling middleware) |
849+
| `EngineMiddleware` | Type | §5.1.1 (query profiling middleware) |
850+
851+
#### `PluginContext` API Verification (`@objectstack/core`)
852+
853+
The `PluginContext` interface in `packages/core/src/types.ts` exposes:
854+
855+
| Method | Signature | Required By |
856+
|--------|-----------|-------------|
857+
| `registerService` | `(name: string, service: any): void` | Standard plugin registration |
858+
| `getService` | `<T>(name: string): T` | Service discovery |
859+
| `replaceService` | `<T>(name: string, implementation: T): void` | §5.1.2 (optimization hooks) |
860+
| `getServices` | `(): Map<string, any>` | Service enumeration |
861+
| `hook` | `(name: string, handler: Function): void` | Hook registration |
862+
| `trigger` | `(name: string, ...args: any[]): Promise<void>` | Hook triggering |
863+
| `logger` | `Logger` | Logging |
864+
| `getKernel` | `(): ObjectKernel` | Advanced use cases |
865+
866+
#### Engine Middleware Verification (`@objectstack/objectql`)
867+
868+
The `ObjectQL` engine in `packages/objectql/src/engine.ts` confirms:
869+
- `registerMiddleware(fn: EngineMiddleware, options?: { object?: string }): void` — onion-model middleware
870+
- `registerHook(event, handler, options)` — priority-based, per-object hooks
871+
- `triggerHooks(event, context)` — hook execution with object matching
872+
- All CRUD operations execute through `executeWithMiddleware()` pipeline
831873

832874
---
833875

0 commit comments

Comments
 (0)