Skip to content

Commit dede86f

Browse files
Copilothotlong
andcommitted
fix: resolve build errors in metadata package and spec protocol types
- Add 'memory:' to MetadataLoaderContract protocol enum - Fix MetadataManagerConfig/MetadataLoaderContract to use z.input for constructor-friendly types - Add watchOptions to system MetadataManagerConfigSchema - Fix loader protocol values to use colon suffix convention - Fix Date to ISO string for modifiedAt/timestamp fields - Fix test mock protocol values, imports, and missing replaceService mock Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 3143fdd commit dede86f

9 files changed

Lines changed: 32 additions & 23 deletions

packages/metadata/src/loaders/filesystem-loader.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type { MetadataSerializer } from '../serializers/serializer-interface.js'
2626
export class FilesystemLoader implements MetadataLoader {
2727
readonly contract: MetadataLoaderContract = {
2828
name: 'filesystem',
29-
protocol: 'file',
29+
protocol: 'file:',
3030
capabilities: {
3131
read: true,
3232
write: true,
@@ -224,7 +224,7 @@ export class FilesystemLoader implements MetadataLoader {
224224

225225
return {
226226
size: stats.size,
227-
modifiedAt: stats.mtime,
227+
modifiedAt: stats.mtime.toISOString(),
228228
etag,
229229
format,
230230
path: filePath,

packages/metadata/src/loaders/memory-loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { MetadataLoader } from './loader-interface.js';
2020
export class MemoryLoader implements MetadataLoader {
2121
readonly contract: MetadataLoaderContract = {
2222
name: 'memory',
23-
protocol: 'memory',
23+
protocol: 'memory:',
2424
capabilities: {
2525
read: true,
2626
write: true,

packages/metadata/src/loaders/remote-loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { MetadataLoader } from './loader-interface.js';
2020
export class RemoteLoader implements MetadataLoader {
2121
readonly contract: MetadataLoaderContract = {
2222
name: 'remote',
23-
protocol: 'http',
23+
protocol: 'http:',
2424
capabilities: {
2525
read: true,
2626
write: true,

packages/metadata/src/metadata.test.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
22

33
import { describe, it, expect, vi, beforeEach } from 'vitest';
4-
import { MetadataManager, type MetadataManagerOptions } from './metadata-manager';
4+
import { MetadataManager } from './metadata-manager';
55
import { MemoryLoader } from './loaders/memory-loader';
66
import type { MetadataLoader } from './loaders/loader-interface';
77

@@ -109,7 +109,7 @@ describe('MetadataManager', () => {
109109

110110
it('should throw when no writable loader is available', async () => {
111111
const readOnlyLoader: MetadataLoader = {
112-
contract: { name: 'readonly', protocol: 'test', capabilities: { read: true, write: false, watch: false, list: true } },
112+
contract: { name: 'readonly', protocol: 'memory:' as const, capabilities: { read: true, write: false, watch: false, list: true } },
113113
load: vi.fn().mockResolvedValue({ data: null }),
114114
loadMany: vi.fn().mockResolvedValue([]),
115115
exists: vi.fn().mockResolvedValue(false),
@@ -163,15 +163,15 @@ describe('MetadataManager', () => {
163163

164164
it('should deduplicate across loaders', async () => {
165165
const loader1: MetadataLoader = {
166-
contract: { name: 'l1', protocol: 'test', capabilities: { read: true, write: false, watch: false, list: true } },
166+
contract: { name: 'l1', protocol: 'memory:' as const, capabilities: { read: true, write: false, watch: false, list: true } },
167167
load: vi.fn().mockResolvedValue({ data: null }),
168168
loadMany: vi.fn().mockResolvedValue([]),
169169
exists: vi.fn().mockResolvedValue(false),
170170
stat: vi.fn().mockResolvedValue(null),
171171
list: vi.fn().mockResolvedValue(['account', 'contact']),
172172
};
173173
const loader2: MetadataLoader = {
174-
contract: { name: 'l2', protocol: 'test', capabilities: { read: true, write: false, watch: false, list: true } },
174+
contract: { name: 'l2', protocol: 'memory:' as const, capabilities: { read: true, write: false, watch: false, list: true } },
175175
load: vi.fn().mockResolvedValue({ data: null }),
176176
loadMany: vi.fn().mockResolvedValue([]),
177177
exists: vi.fn().mockResolvedValue(false),
@@ -269,7 +269,7 @@ describe('MemoryLoader', () => {
269269

270270
it('should have correct contract', () => {
271271
expect(loader.contract.name).toBe('memory');
272-
expect(loader.contract.protocol).toBe('memory');
272+
expect(loader.contract.protocol).toBe('memory:');
273273
expect(loader.contract.capabilities.read).toBe(true);
274274
expect(loader.contract.capabilities.write).toBe(true);
275275
});
@@ -356,15 +356,15 @@ describe('MetadataPlugin', () => {
356356
}));
357357

358358
it('should have correct plugin metadata', async () => {
359-
const { MetadataPlugin } = await import('./plugin');
359+
const { MetadataPlugin } = await import('./plugin.js');
360360
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });
361361
expect(plugin.name).toBe('com.objectstack.metadata');
362362
expect(plugin.version).toBe('1.0.0');
363363
expect(plugin.type).toBe('standard');
364364
});
365365

366366
it('should call init and register metadata service', async () => {
367-
const { MetadataPlugin } = await import('./plugin');
367+
const { MetadataPlugin } = await import('./plugin.js');
368368
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });
369369

370370
const ctx = createMockPluginContext();
@@ -374,7 +374,7 @@ describe('MetadataPlugin', () => {
374374
});
375375

376376
it('should call start and attempt to load metadata types', async () => {
377-
const { MetadataPlugin } = await import('./plugin');
377+
const { MetadataPlugin } = await import('./plugin.js');
378378
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });
379379

380380
const ctx = createMockPluginContext();
@@ -390,7 +390,7 @@ describe('MetadataPlugin', () => {
390390

391391
function createMockLoader(name: string, data: any, shouldFail = false): MetadataLoader {
392392
return {
393-
contract: { name, protocol: 'test', capabilities: { read: true, write: false, watch: false, list: true } },
393+
contract: { name, protocol: 'memory:' as const, capabilities: { read: true, write: false, watch: false, list: true } },
394394
load: shouldFail
395395
? vi.fn().mockRejectedValue(new Error('loader failed'))
396396
: vi.fn().mockResolvedValue({ data }),
@@ -403,7 +403,7 @@ function createMockLoader(name: string, data: any, shouldFail = false): Metadata
403403

404404
function createMockLoaderMany(name: string, items: any[], shouldFail = false): MetadataLoader {
405405
return {
406-
contract: { name, protocol: 'test', capabilities: { read: true, write: false, watch: false, list: true } },
406+
contract: { name, protocol: 'memory:' as const, capabilities: { read: true, write: false, watch: false, list: true } },
407407
load: vi.fn().mockResolvedValue({ data: null }),
408408
loadMany: shouldFail
409409
? vi.fn().mockRejectedValue(new Error('loader failed'))
@@ -417,6 +417,7 @@ function createMockLoaderMany(name: string, items: any[], shouldFail = false): M
417417
function createMockPluginContext() {
418418
return {
419419
registerService: vi.fn(),
420+
replaceService: vi.fn(),
420421
getService: vi.fn().mockReturnValue(null),
421422
getServices: vi.fn().mockReturnValue(new Map()),
422423
hook: vi.fn(),

packages/metadata/src/node-metadata-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export class NodeMetadataManager extends MetadataManager {
118118
name,
119119
path: filePath,
120120
data,
121-
timestamp: new Date(),
121+
timestamp: new Date().toISOString(),
122122
};
123123

124124
this.notifyWatchers(type, event);

packages/spec/src/kernel/metadata-loader.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ describe('MetadataLoaderProtocol', () => {
386386
});
387387

388388
it('should accept all valid protocols', () => {
389-
const protocols = ['file:', 'http:', 's3:', 'datasource:'];
389+
const protocols = ['file:', 'http:', 's3:', 'datasource:', 'memory:'];
390390
protocols.forEach((protocol) => {
391391
expect(() => MetadataLoaderContractSchema.parse({
392392
name: 'test', protocol, capabilities: {}, supportedFormats: ['json'],

packages/spec/src/kernel/metadata-loader.zod.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export const MetadataLoaderContractSchema = z.object({
374374
/**
375375
* Protocol handled by this loader (e.g. 'file:', 'http:', 's3:', 'datasource:')
376376
*/
377-
protocol: z.enum(['file:', 'http:', 's3:', 'datasource:']).describe('Protocol identifier'),
377+
protocol: z.enum(['file:', 'http:', 's3:', 'datasource:', 'memory:']).describe('Protocol identifier'),
378378

379379
/**
380380
* Detailed capabilities
@@ -498,6 +498,6 @@ export type MetadataLoadResult = z.infer<typeof MetadataLoadResultSchema>;
498498
export type MetadataSaveResult = z.infer<typeof MetadataSaveResultSchema>;
499499
export type MetadataWatchEvent = z.infer<typeof MetadataWatchEventSchema>;
500500
export type MetadataCollectionInfo = z.infer<typeof MetadataCollectionInfoSchema>;
501-
export type MetadataLoaderContract = z.infer<typeof MetadataLoaderContractSchema>;
502-
export type MetadataManagerConfig = z.infer<typeof MetadataManagerConfigSchema>;
501+
export type MetadataLoaderContract = z.input<typeof MetadataLoaderContractSchema>;
502+
export type MetadataManagerConfig = z.input<typeof MetadataManagerConfigSchema>;
503503
export type MetadataFallbackStrategy = z.infer<typeof MetadataFallbackStrategySchema>;

packages/spec/src/system/metadata-persistence.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ describe('MetadataLoaderContractSchema', () => {
229229
});
230230

231231
it('should accept all valid protocols', () => {
232-
const protocols = ['file:', 'http:', 's3:', 'datasource:'];
232+
const protocols = ['file:', 'http:', 's3:', 'datasource:', 'memory:'];
233233
protocols.forEach((protocol) => {
234234
expect(() => MetadataLoaderContractSchema.parse({
235235
name: 'test', protocol, capabilities: {},

packages/spec/src/system/metadata-persistence.zod.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export const MetadataStatsSchema = z.object({
131131
*/
132132
export const MetadataLoaderContractSchema = z.object({
133133
name: z.string(),
134-
protocol: z.enum(['file:', 'http:', 's3:', 'datasource:']).describe('Loader protocol identifier'),
134+
protocol: z.enum(['file:', 'http:', 's3:', 'datasource:', 'memory:']).describe('Loader protocol identifier'),
135135
description: z.string().optional(),
136136
supportedFormats: z.array(z.string()).optional(),
137137
supportsWatch: z.boolean().optional(),
@@ -311,11 +311,19 @@ export const MetadataManagerConfigSchema = z.object({
311311
* Cache configuration
312312
*/
313313
cache: z.boolean().optional().describe('Enable metadata caching'),
314+
315+
/**
316+
* Watch options
317+
*/
318+
watchOptions: z.object({
319+
ignored: z.array(z.string()).optional().describe('Patterns to ignore'),
320+
persistent: z.boolean().default(true).describe('Keep process running'),
321+
}).optional().describe('File watcher options'),
314322
});
315323

316324
export type MetadataFormat = z.infer<typeof MetadataFormatSchema>;
317325
export type MetadataStats = z.infer<typeof MetadataStatsSchema>;
318-
export type MetadataLoaderContract = z.infer<typeof MetadataLoaderContractSchema>;
326+
export type MetadataLoaderContract = z.input<typeof MetadataLoaderContractSchema>;
319327
export type MetadataLoadOptions = z.infer<typeof MetadataLoadOptionsSchema>;
320328
export type MetadataLoadResult = z.infer<typeof MetadataLoadResultSchema>;
321329
export type MetadataSaveOptions = z.infer<typeof MetadataSaveOptionsSchema>;
@@ -324,6 +332,6 @@ export type MetadataWatchEvent = z.infer<typeof MetadataWatchEventSchema>;
324332
export type MetadataCollectionInfo = z.infer<typeof MetadataCollectionInfoSchema>;
325333
export type MetadataExportOptions = z.infer<typeof MetadataExportOptionsSchema>;
326334
export type MetadataImportOptions = z.infer<typeof MetadataImportOptionsSchema>;
327-
export type MetadataManagerConfig = z.infer<typeof MetadataManagerConfigSchema>;
335+
export type MetadataManagerConfig = z.input<typeof MetadataManagerConfigSchema>;
328336
export type MetadataFallbackStrategy = z.infer<typeof MetadataFallbackStrategySchema>;
329337
export type MetadataSource = z.infer<typeof MetadataSourceSchema>;

0 commit comments

Comments
 (0)