Skip to content

Commit 4e4ea4a

Browse files
committed
refactor: update ObjectQL bridge class to integrate MetadataRegistry and legacy config
1 parent 1004247 commit 4e4ea4a

2 files changed

Lines changed: 160 additions & 6 deletions

File tree

Lines changed: 157 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,159 @@
11
/**
2-
* @deprecated This file is dead code. Delete it.
3-
* ObjectQL class now comes from @objectstack/objectql.
4-
* Re-exported via index.ts for backward compatibility.
2+
* ObjectQL Bridge Class
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* Extends the upstream @objectstack/objectql.ObjectQL engine with:
6+
* - Legacy constructor config (datasources map)
7+
* - MetadataRegistry integration (for ObjectLoader filesystem loading)
8+
*
9+
* This allows existing consumers to keep using:
10+
* const app = new ObjectQL({ datasources: { default: driver } });
11+
* const loader = new ObjectLoader(app.metadata);
12+
* await loader.load(dir);
13+
* await app.init();
514
*/
6-
export { ObjectQL } from '@objectstack/objectql';
15+
16+
import { ObjectQL as UpstreamObjectQL, SchemaRegistry } from '@objectstack/objectql';
17+
import type { ServiceObject } from '@objectstack/spec/data';
18+
import type { DriverInterface } from '@objectstack/core';
19+
import { MetadataRegistry } from '@objectql/types';
20+
import type { Driver } from '@objectql/types';
21+
22+
// Runtime-safe accessor for compat methods that exist in @objectstack/objectql@3.0.1
23+
// but may not be visible to TypeScript due to module resolution variance.
24+
type UpstreamCompat = UpstreamObjectQL & {
25+
registerObject(schema: ServiceObject, packageId?: string, namespace?: string): string;
26+
getObject(name: string): ServiceObject | undefined;
27+
getConfigs(): Record<string, ServiceObject>;
28+
removePackage(packageId: string): void;
29+
};
30+
31+
/**
32+
* Legacy config shape accepted by the ObjectQL bridge constructor.
33+
*/
34+
export interface ObjectQLConfig {
35+
datasources?: Record<string, Driver | DriverInterface>;
36+
[key: string]: unknown;
37+
}
38+
39+
/**
40+
* ObjectQL — drop-in replacement that bridges the upstream engine
41+
* with the @objectql/types MetadataRegistry used by ObjectLoader.
42+
*/
43+
export class ObjectQL extends UpstreamObjectQL {
44+
/**
45+
* Filesystem metadata registry populated by ObjectLoader.
46+
* After calling loader.load(), call app.init() to sync these
47+
* entries into the upstream SchemaRegistry & driver layer.
48+
*/
49+
readonly metadata = new MetadataRegistry();
50+
51+
/** Typed self-reference for compat methods */
52+
private get compat(): UpstreamCompat { return this as unknown as UpstreamCompat; }
53+
54+
constructor(config: ObjectQLConfig = {}) {
55+
// Upstream constructor only accepts hostContext
56+
super();
57+
58+
// Register drivers from legacy datasources config
59+
if (config.datasources) {
60+
for (const [name, driver] of Object.entries(config.datasources)) {
61+
if (!(driver as any).name) {
62+
(driver as any).name = name;
63+
}
64+
// Cast: local Driver interface is structurally compatible with upstream DriverInterface
65+
this.registerDriver(driver as DriverInterface, name === 'default');
66+
}
67+
}
68+
}
69+
70+
/**
71+
* Initialize the engine.
72+
*
73+
* Before calling the upstream init (which connects drivers and syncs schemas),
74+
* bridge all objects loaded via ObjectLoader into the upstream SchemaRegistry.
75+
*/
76+
async init(): Promise<void> {
77+
this.syncMetadataToRegistry();
78+
return super.init();
79+
}
80+
81+
/**
82+
* Sync all filesystem-loaded metadata into the upstream SchemaRegistry.
83+
* Called automatically by init(), but can also be called manually.
84+
*/
85+
private syncMetadataToRegistry(): void {
86+
// Bridge filesystem-loaded objects → upstream SchemaRegistry
87+
const objects = this.metadata.list<any>('object');
88+
for (const obj of objects) {
89+
if (obj && obj.name) {
90+
// Only register if not already in SchemaRegistry
91+
if (!SchemaRegistry.getObject(obj.name)) {
92+
this.compat.registerObject(obj as ServiceObject, '__filesystem__');
93+
}
94+
}
95+
}
96+
97+
// Bridge filesystem-loaded hooks → upstream hook system
98+
const hooks = this.metadata.list<any>('hook');
99+
for (const hookEntry of hooks) {
100+
if (hookEntry && typeof hookEntry === 'object') {
101+
const objectName = (hookEntry as any).id || (hookEntry as any).objectName;
102+
for (const [event, handler] of Object.entries(hookEntry)) {
103+
if (typeof handler === 'function' && event !== 'id' && event !== 'objectName') {
104+
this.registerHook(event, handler as any, { object: objectName });
105+
}
106+
}
107+
}
108+
}
109+
}
110+
111+
/**
112+
* Get an object definition by name.
113+
*
114+
* Checks the upstream SchemaRegistry first, then falls back to the
115+
* local MetadataRegistry for objects loaded via ObjectLoader but
116+
* not yet synced (i.e., init() hasn't been called yet).
117+
*/
118+
getObject(name: string): ServiceObject | undefined {
119+
// Check upstream SchemaRegistry
120+
const upstream = SchemaRegistry.getObject(name);
121+
if (upstream) return upstream;
122+
// Fallback: check local MetadataRegistry (pre-init)
123+
return this.metadata.get<ServiceObject>('object', name);
124+
}
125+
126+
/**
127+
* Get all registered object configs as a name→config map.
128+
*
129+
* Merges results from the upstream SchemaRegistry with the
130+
* local MetadataRegistry (for pre-init objects).
131+
*/
132+
getConfigs(): Record<string, ServiceObject> {
133+
const result: Record<string, ServiceObject> = {};
134+
// Get upstream objects from SchemaRegistry
135+
const upstreamObjects = SchemaRegistry.getAllObjects();
136+
for (const obj of upstreamObjects) {
137+
if (obj.name) {
138+
result[obj.name] = obj;
139+
}
140+
}
141+
// Merge local MetadataRegistry entries not yet synced upstream
142+
const localObjects = this.metadata.list<any>('object');
143+
for (const obj of localObjects) {
144+
if (obj && obj.name && !result[obj.name]) {
145+
result[obj.name] = obj;
146+
}
147+
}
148+
return result;
149+
}
150+
151+
/**
152+
* Remove all hooks, actions, and objects contributed by a package.
153+
* Also cleans up the local MetadataRegistry.
154+
*/
155+
removePackage(packageId: string): void {
156+
this.compat.removePackage(packageId);
157+
this.metadata.unregisterPackage(packageId);
158+
}
159+
}

packages/foundation/core/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
// ── Convenience factory ──
1010
export { createObjectQLKernel, type ObjectQLKernelOptions } from './kernel-factory';
1111

12-
// ── Re-export upstream engine (replaces local app.ts + repository.ts) ──
13-
export { ObjectQL, ObjectRepository, ScopedContext, SchemaRegistry } from '@objectstack/objectql';
12+
// ── Re-export bridge engine (extends upstream with MetadataRegistry + legacy config) ──
13+
export { ObjectQL, type ObjectQLConfig } from './app';
14+
export { ObjectRepository, ScopedContext, SchemaRegistry } from '@objectstack/objectql';
1415
export type { HookHandler, HookEntry, OperationContext, EngineMiddleware, ObjectQLHostContext } from '@objectstack/objectql';
1516

1617
// ── Plugin orchestration ──

0 commit comments

Comments
 (0)