-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmetadata-service.ts
More file actions
488 lines (433 loc) · 17 KB
/
Copy pathmetadata-service.ts
File metadata and controls
488 lines (433 loc) · 17 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
/**
* IMetadataService - Metadata Service Contract
*
* The unified async interface for managing ALL metadata in the ObjectStack platform.
* This is the service contract that the Metadata Plugin implements and
* that all other plugins depend on for metadata operations.
*
* Concrete implementations (SchemaRegistry, Database-backed, etc.)
* should implement this interface.
*
* All methods are async to support database-backed persistence via datasource.
*
* Follows Dependency Inversion Principle - plugins depend on this interface,
* not on concrete metadata storage implementations.
*
* Aligned with CoreServiceName 'metadata' in core-services.zod.ts.
*
* ## Architecture
* ```
* ┌──────────────────────┐
* │ IMetadataService │ ← Contract (this file)
* ├──────────────────────┤
* │ CRUD Operations │ register / get / list / unregister / exists
* │ Query / Search │ query (filter, paginate, sort)
* │ Bulk Operations │ bulkRegister / bulkUnregister
* │ Overlay Management │ getOverlay / saveOverlay / removeOverlay
* │ Watch / Subscribe │ watch / unwatch
* │ Import / Export │ exportMetadata / importMetadata
* │ Validation │ validate
* │ Type Registry │ getRegisteredTypes / getTypeInfo
* │ Dependencies │ getDependencies / getDependents
* └──────────────────────┘
* ```
*/
import type { MetadataQuery, MetadataQueryResult, MetadataValidationResult, MetadataBulkResult, MetadataDependency } from '../kernel/metadata-plugin.zod';
import type { Action } from '../ui/action.zod';
import type { MetadataOverlay } from '../kernel/metadata-customization.zod';
import type { PackagePublishResult, MetadataHistoryQueryOptions, MetadataHistoryQueryResult, MetadataDiffResult } from '../system/metadata-persistence.zod';
/**
* Metadata watch callback signature
*/
export type MetadataWatchCallback = (event: {
type: 'registered' | 'updated' | 'unregistered';
metadataType: string;
name: string;
data?: unknown;
}) => void;
/**
* Metadata watch subscription handle
*/
export interface MetadataWatchHandle {
/** Unsubscribe from watch */
unsubscribe(): void;
}
/**
* Metadata export options
*/
export interface MetadataExportOptions {
/** Filter by metadata types */
types?: string[];
/** Filter by namespaces */
namespaces?: string[];
/** Export format */
format?: 'json' | 'yaml';
}
/**
* Metadata import options
*/
export interface MetadataImportOptions {
/** Conflict resolution strategy */
conflictResolution?: 'skip' | 'overwrite' | 'merge';
/** Validate before import */
validate?: boolean;
/** Dry run (don't actually save) */
dryRun?: boolean;
}
/**
* Metadata import result
*/
export interface MetadataImportResult {
/** Total items processed */
total: number;
/** Successfully imported */
imported: number;
/** Skipped (conflict resolution) */
skipped: number;
/** Failed items */
failed: number;
/** Per-item error details */
errors?: Array<{ type: string; name: string; error: string }>;
}
/**
* Type registry entry info
*/
export interface MetadataTypeInfo {
/** Metadata type identifier */
type: string;
/** Human-readable label */
label: string;
/** Description */
description?: string;
/** File glob patterns */
filePatterns: string[];
/** Supports overlay customization */
supportsOverlay: boolean;
/** Protocol domain */
domain: string;
/**
* Declarative type-level actions (buttons) for this metadata type — the
* merged result of the registry entry's declarative `actions` and any
* plugin-registered actions (`registerMetadataTypeActions`). The Studio
* metadata-admin engine renders these the same way `ObjectView` renders
* an object's actions. Omitted/empty when the type declares none.
*/
actions?: Action[];
}
/**
* Options for the {@link IMetadataService} write path
* (`register` / `unregister` and their bulk forms).
*/
export interface MetadataWriteOptions {
/**
* Announce the write to `subscribe(type, …)` watchers. Default `true`.
*
* A write that changes what readers see must reach the consumers that
* cache it — ObjectQL's SchemaRegistry bridge, the HMR SSE stream —
* otherwise they serve the pre-write definition until the process
* restarts. Announcing is therefore the default, so a new call site is
* correct without knowing this contract exists.
*
* Pass `false` ONLY for bulk ingest whose subscribers either do not exist
* yet (boot-time registration) or re-read the whole set from a different
* signal — those paths MUST announce by other means, as the artifact
* reload path does via the `metadata:reloaded` hook. Silencing a write
* that nothing else announces is exactly how runtime consumers go stale.
*/
notify?: boolean;
}
export interface IMetadataService {
// ==========================================
// Core CRUD Operations
// ==========================================
/**
* Register/save a metadata item by type.
*
* Announces the write to `subscribe(type, …)` watchers unless
* `options.notify === false` — see {@link MetadataWriteOptions.notify}
* before silencing it.
*
* @param type - Metadata type (e.g. 'object', 'view', 'flow')
* @param name - Item name/identifier (snake_case)
* @param data - The metadata definition to register
* @param options - Write options; `{ notify: false }` suppresses the watcher event
*/
register(type: string, name: string, data: unknown, options?: MetadataWriteOptions): Promise<void>;
/**
* Get a metadata item by type and name
* @param type - Metadata type
* @param name - Item name/identifier
* @returns The metadata definition, or undefined if not found
*/
get(type: string, name: string): Promise<unknown | undefined>;
/**
* List all metadata items of a given type
* @param type - Metadata type
* @returns Array of metadata definitions
*/
list(type: string): Promise<unknown[]>;
/**
* Unregister/remove a metadata item by type and name.
*
* Announces the removal to `subscribe(type, …)` watchers as a `deleted`
* event unless `options.notify === false` — see
* {@link MetadataWriteOptions.notify} before silencing it.
*
* @param type - Metadata type
* @param name - Item name/identifier
* @param options - Write options; `{ notify: false }` suppresses the watcher event
*/
unregister(type: string, name: string, options?: MetadataWriteOptions): Promise<void>;
/**
* Check if a metadata item exists
* @param type - Metadata type
* @param name - Item name/identifier
* @returns True if the item exists
*/
exists(type: string, name: string): Promise<boolean>;
/**
* List all names of metadata items of a given type
* @param type - Metadata type
* @returns Array of item names
*/
listNames(type: string): Promise<string[]>;
/**
* Convenience: get an object definition by name
* @param name - Object name (snake_case)
* @returns The object definition, or undefined if not found
*/
getObject(name: string): Promise<unknown | undefined>;
/**
* Convenience: list all object definitions
* @returns Array of object definitions
*/
listObjects(): Promise<unknown[]>;
// ==========================================
// Convenience: UI Metadata
// ==========================================
/**
* Convenience: get a view definition by name
* Equivalent to get('view', name)
*/
getView?(name: string): Promise<unknown | undefined>;
/**
* Convenience: list view definitions, optionally filtered by object
*/
listViews?(object?: string): Promise<unknown[]>;
/**
* Convenience: get a dashboard definition by name
* Equivalent to get('dashboard', name)
*/
getDashboard?(name: string): Promise<unknown | undefined>;
/**
* Convenience: list all dashboard definitions
*/
listDashboards?(): Promise<unknown[]>;
// ==========================================
// Package Management
// ==========================================
/**
* Unregister all metadata items from a specific package
* @param packageName - The package name whose items should be removed
*/
unregisterPackage?(packageName: string): Promise<void>;
/**
* Publish an entire package:
* 1. Validate all draft items (dependency check)
* 2. Snapshot all items in the package
* 3. Increment package version
* 4. Set all items state → active
* @param packageId - The package ID to publish
* @param options - Publish options
* @returns Publish result with version and item count
*/
publishPackage?(packageId: string, options?: {
changeNote?: string;
publishedBy?: string;
validate?: boolean;
}): Promise<PackagePublishResult>;
/**
* Revert entire package to last published state.
* Restores all metadata definitions from their published snapshots.
* @param packageId - The package ID to revert
*/
revertPackage?(packageId: string): Promise<void>;
/**
* Get the published version of any metadata item (for runtime serving).
* Returns published_definition if exists, else current definition.
* @param type - Metadata type
* @param name - Item name/identifier
* @returns The published definition, or current definition if never published
*/
getPublished?(type: string, name: string): Promise<unknown | undefined>;
// ==========================================
// Query / Search
// ==========================================
/**
* Query metadata items with filtering, sorting, and pagination.
* Supports advanced search across all metadata types.
* @param query - Query parameters
* @returns Paginated query result
*/
query?(query: MetadataQuery): Promise<MetadataQueryResult>;
// ==========================================
// Bulk Operations
// ==========================================
/**
* Register multiple metadata items in a single batch.
* More efficient than individual register calls.
* @param items - Array of { type, name, data } to register
* @param options - Bulk operation options
* @returns Bulk operation result with success/failure counts
*/
bulkRegister?(items: Array<{ type: string; name: string; data: unknown }>, options?: { continueOnError?: boolean; validate?: boolean }): Promise<MetadataBulkResult>;
/**
* Unregister multiple metadata items in a single batch.
* @param items - Array of { type, name } to unregister
* @returns Bulk operation result
*/
bulkUnregister?(items: Array<{ type: string; name: string }>): Promise<MetadataBulkResult>;
// ==========================================
// Overlay / Customization Management
// ==========================================
/**
* Get the active overlay for a metadata item.
* Returns the customization delta applied on top of the base definition.
* @param type - Metadata type
* @param name - Item name
* @param scope - Overlay scope ('platform' or 'user')
* @returns The overlay, or undefined if no customization exists
*/
getOverlay?(type: string, name: string, scope?: 'platform' | 'user'): Promise<MetadataOverlay | undefined>;
/**
* Save/update an overlay for a metadata item.
* @param overlay - The overlay to save
*/
saveOverlay?(overlay: MetadataOverlay): Promise<void>;
/**
* Remove an overlay, reverting to the base definition.
* @param type - Metadata type
* @param name - Item name
* @param scope - Overlay scope
*/
removeOverlay?(type: string, name: string, scope?: 'platform' | 'user'): Promise<void>;
/**
* Get the effective (merged) metadata after applying all overlays.
* Resolution order: system ← merge(platform) ← merge(user)
* @param type - Metadata type
* @param name - Item name
* @param context - Optional auth context for user-scoped overlay resolution
* @returns The effective metadata with all overlays applied
*/
getEffective?(type: string, name: string, context?: {
userId?: string;
tenantId?: string;
positions?: string[];
permissions?: string[];
}): Promise<unknown | undefined>;
// ==========================================
// Watch / Subscribe
// ==========================================
/**
* Watch for metadata changes.
* @param type - Metadata type to watch (or '*' for all types)
* @param callback - Callback invoked when metadata changes
* @returns A handle to unsubscribe
*/
watch?(type: string, callback: MetadataWatchCallback): MetadataWatchHandle;
// ==========================================
// Import / Export
// ==========================================
/**
* Export metadata as a portable bundle.
* @param options - Export options (types, namespaces, format)
* @returns Serialized metadata bundle
*/
exportMetadata?(options?: MetadataExportOptions): Promise<unknown>;
/**
* Import metadata from a portable bundle.
* @param data - The metadata bundle to import
* @param options - Import options (conflict resolution, validation)
* @returns Import result with success/failure counts
*/
importMetadata?(data: unknown, options?: MetadataImportOptions): Promise<MetadataImportResult>;
// ==========================================
// Validation
// ==========================================
/**
* Validate a metadata item against its type schema.
* @param type - Metadata type
* @param data - The metadata payload to validate
* @returns Validation result with errors and warnings
*/
validate?(type: string, data: unknown): Promise<MetadataValidationResult>;
// ==========================================
// Type Registry
// ==========================================
/**
* Get all registered metadata types.
* Includes both built-in types and custom types registered by plugins.
* @returns Array of type identifiers
*/
getRegisteredTypes?(): Promise<string[]>;
/**
* Get detailed information about a metadata type.
* @param type - Metadata type identifier
* @returns Type info, or undefined if not registered
*/
getTypeInfo?(type: string): Promise<MetadataTypeInfo | undefined>;
// ==========================================
// Dependency Tracking
// ==========================================
/**
* Get metadata items that this item depends on.
* @param type - Metadata type
* @param name - Item name
* @returns Array of dependencies
*/
getDependencies?(type: string, name: string): Promise<MetadataDependency[]>;
/**
* Get metadata items that depend on this item.
* Used for impact analysis before deletion.
* @param type - Metadata type
* @param name - Item name
* @returns Array of dependent items
*/
getDependents?(type: string, name: string): Promise<MetadataDependency[]>;
// ==========================================
// Version History & Rollback
// ==========================================
/**
* Get version history for a metadata item.
* Returns a timeline of all changes made to the item.
* @param type - Metadata type
* @param name - Item name
* @param options - Query options (limit, offset, filters)
* @returns History query result with version records
*/
getHistory?(type: string, name: string, options?: MetadataHistoryQueryOptions): Promise<MetadataHistoryQueryResult>;
/**
* Rollback a metadata item to a specific version.
* Restores the metadata definition from the history snapshot.
* @param type - Metadata type
* @param name - Item name
* @param version - Target version to rollback to
* @param options - Rollback options
* @returns The restored metadata definition
*/
rollback?(type: string, name: string, version: number, options?: {
changeNote?: string;
recordedBy?: string;
}): Promise<unknown>;
/**
* Compare two versions of a metadata item.
* Returns a diff showing what changed between versions.
* @param type - Metadata type
* @param name - Item name
* @param version1 - First version (older)
* @param version2 - Second version (newer)
* @returns Diff result with changes
*/
diff?(type: string, name: string, version1: number, version2: number): Promise<MetadataDiffResult>;
}