-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathplugin.ts
More file actions
128 lines (110 loc) · 5.02 KB
/
plugin.ts
File metadata and controls
128 lines (110 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { Plugin, PluginContext } from '@objectstack/core';
import { NodeMetadataManager } from './node-metadata-manager.js';
import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';
import type { MetadataPluginConfig } from '@objectstack/spec/kernel';
import { SysMetadataObject } from './objects/sys-metadata.object.js';
export interface MetadataPluginOptions {
rootDir?: string;
watch?: boolean;
config?: Partial<MetadataPluginConfig>;
}
export class MetadataPlugin implements Plugin {
name = 'com.objectstack.metadata';
type = 'standard';
version = '1.0.0';
private manager: NodeMetadataManager;
private options: MetadataPluginOptions;
constructor(options: MetadataPluginOptions = {}) {
this.options = {
watch: true,
...options
};
const rootDir = this.options.rootDir || process.cwd();
this.manager = new NodeMetadataManager({
rootDir,
watch: this.options.watch ?? true,
formats: ['yaml', 'json', 'typescript', 'javascript']
});
// Initialize with default type registry
this.manager.setTypeRegistry(DEFAULT_METADATA_TYPE_REGISTRY);
}
init = async (ctx: PluginContext) => {
ctx.logger.info('Initializing Metadata Manager', {
root: this.options.rootDir || process.cwd(),
watch: this.options.watch
});
// Register Metadata Manager as the primary metadata service provider.
ctx.registerService('metadata', this.manager);
// Register metadata system objects via the manifest service (if available).
// MetadataPlugin may init before ObjectQLPlugin, so wrap in try/catch.
try {
ctx.getService<{ register(m: any): void }>('manifest').register({
id: 'com.objectstack.metadata',
name: 'Metadata',
version: '1.0.0',
type: 'plugin',
namespace: 'sys',
objects: [SysMetadataObject],
});
} catch {
// ObjectQL not loaded yet — objects will be discovered via legacy fallback
}
ctx.logger.info('MetadataPlugin providing metadata service (primary mode)', {
mode: 'file-system',
features: ['watch', 'persistence', 'multi-format', 'query', 'overlay', 'type-registry']
});
}
start = async (ctx: PluginContext) => {
ctx.logger.info('Loading metadata from file system...');
// Use the type registry to discover metadata types (sorted by loadOrder)
const sortedTypes = [...DEFAULT_METADATA_TYPE_REGISTRY]
.sort((a, b) => a.loadOrder - b.loadOrder);
let totalLoaded = 0;
for (const entry of sortedTypes) {
try {
const items = await this.manager.loadMany(entry.type, {
recursive: true
});
if (items.length > 0) {
// Register loaded items in the in-memory registry
for (const item of items) {
const meta = item as any;
if (meta?.name) {
await this.manager.register(entry.type, meta.name, item);
}
}
ctx.logger.info(`Loaded ${items.length} ${entry.type} from file system`);
totalLoaded += items.length;
}
} catch (e: any) {
ctx.logger.debug(`No ${entry.type} metadata found`, { error: e.message });
}
}
ctx.logger.info('Metadata loading complete', {
totalItems: totalLoaded,
registeredTypes: sortedTypes.length,
});
// Bridge database driver from kernel service registry to MetadataManager.
// DriverPlugin registers drivers as 'driver.{name}' services during init().
// This runs AFTER filesystem loading so that system metadata populated via
// register() above is stored in-memory only, without being persisted to the
// database (which would create noisy DB state/history on every cold boot).
try {
const services = ctx.getServices();
for (const [serviceName, service] of services) {
if (serviceName.startsWith('driver.') && service) {
ctx.logger.info('[MetadataPlugin] Bridging driver to MetadataManager for database-backed persistence', {
driverService: serviceName,
});
this.manager.setDatabaseDriver(service);
break; // Use the first discovered driver — typically only one driver is registered per deployment
}
}
} catch (e: any) {
ctx.logger.debug('[MetadataPlugin] No driver service found — database metadata persistence not available', {
error: e.message,
});
}
}
}