bluera-knowledge 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +98 -2
- package/commands/sync.md +96 -0
- package/dist/{chunk-ITH6FWQY.js → chunk-2WBITQWZ.js} +24 -3
- package/dist/{chunk-ITH6FWQY.js.map → chunk-2WBITQWZ.js.map} +1 -1
- package/dist/{chunk-CUHYSPRV.js → chunk-565OVW3C.js} +999 -2
- package/dist/chunk-565OVW3C.js.map +1 -0
- package/dist/{chunk-DWAIT2OD.js → chunk-TRDMYKGC.js} +190 -5
- package/dist/chunk-TRDMYKGC.js.map +1 -0
- package/dist/index.js +217 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/workers/background-worker-cli.js +2 -2
- package/package.json +1 -1
- package/src/analysis/adapter-registry.test.ts +211 -0
- package/src/analysis/adapter-registry.ts +155 -0
- package/src/analysis/language-adapter.ts +127 -0
- package/src/analysis/parser-factory.test.ts +79 -1
- package/src/analysis/parser-factory.ts +8 -0
- package/src/analysis/zil/index.ts +34 -0
- package/src/analysis/zil/zil-adapter.test.ts +187 -0
- package/src/analysis/zil/zil-adapter.ts +121 -0
- package/src/analysis/zil/zil-lexer.test.ts +222 -0
- package/src/analysis/zil/zil-lexer.ts +239 -0
- package/src/analysis/zil/zil-parser.test.ts +210 -0
- package/src/analysis/zil/zil-parser.ts +360 -0
- package/src/analysis/zil/zil-special-forms.ts +193 -0
- package/src/cli/commands/sync.test.ts +54 -0
- package/src/cli/commands/sync.ts +264 -0
- package/src/cli/index.ts +1 -0
- package/src/crawl/claude-client.test.ts +56 -0
- package/src/crawl/claude-client.ts +27 -1
- package/src/index.ts +8 -0
- package/src/mcp/commands/index.ts +2 -0
- package/src/mcp/commands/sync.commands.test.ts +283 -0
- package/src/mcp/commands/sync.commands.ts +233 -0
- package/src/mcp/server.ts +9 -1
- package/src/services/gitignore.service.test.ts +157 -0
- package/src/services/gitignore.service.ts +132 -0
- package/src/services/store-definition.service.test.ts +440 -0
- package/src/services/store-definition.service.ts +198 -0
- package/src/services/store.service.test.ts +279 -1
- package/src/services/store.service.ts +101 -4
- package/src/types/index.ts +18 -0
- package/src/types/store-definition.test.ts +492 -0
- package/src/types/store-definition.ts +129 -0
- package/dist/chunk-CUHYSPRV.js.map +0 -1
- package/dist/chunk-DWAIT2OD.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { StoreService } from './store.service.js';
|
|
3
|
-
import {
|
|
3
|
+
import { StoreDefinitionService } from './store-definition.service.js';
|
|
4
|
+
import { rm, mkdtemp, writeFile, readFile, access } from 'node:fs/promises';
|
|
4
5
|
import { tmpdir } from 'node:os';
|
|
5
6
|
import { join } from 'node:path';
|
|
6
7
|
|
|
@@ -538,4 +539,281 @@ describe('StoreService', () => {
|
|
|
538
539
|
await rm(corruptDir, { recursive: true, force: true });
|
|
539
540
|
});
|
|
540
541
|
});
|
|
542
|
+
|
|
543
|
+
describe('store definition auto-update', () => {
|
|
544
|
+
let projectRoot: string;
|
|
545
|
+
let dataDir: string;
|
|
546
|
+
let serviceWithDefs: StoreService;
|
|
547
|
+
let defService: StoreDefinitionService;
|
|
548
|
+
|
|
549
|
+
beforeEach(async () => {
|
|
550
|
+
projectRoot = await mkdtemp(join(tmpdir(), 'store-def-auto-'));
|
|
551
|
+
dataDir = join(projectRoot, '.bluera/bluera-knowledge/data');
|
|
552
|
+
defService = new StoreDefinitionService(projectRoot);
|
|
553
|
+
serviceWithDefs = new StoreService(dataDir, { definitionService: defService });
|
|
554
|
+
await serviceWithDefs.initialize();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
afterEach(async () => {
|
|
558
|
+
await rm(projectRoot, { recursive: true, force: true });
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
describe('create adds definition', () => {
|
|
562
|
+
it('adds file store definition when creating file store', async () => {
|
|
563
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'file-store-'));
|
|
564
|
+
const result = await serviceWithDefs.create({
|
|
565
|
+
name: 'my-docs',
|
|
566
|
+
type: 'file',
|
|
567
|
+
path: storeDir,
|
|
568
|
+
description: 'My documentation',
|
|
569
|
+
tags: ['docs'],
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
expect(result.success).toBe(true);
|
|
573
|
+
|
|
574
|
+
const def = await defService.getByName('my-docs');
|
|
575
|
+
expect(def).toBeDefined();
|
|
576
|
+
expect(def?.type).toBe('file');
|
|
577
|
+
expect(def?.name).toBe('my-docs');
|
|
578
|
+
if (def?.type === 'file') {
|
|
579
|
+
expect(def.path).toBe(storeDir);
|
|
580
|
+
}
|
|
581
|
+
expect(def?.description).toBe('My documentation');
|
|
582
|
+
expect(def?.tags).toEqual(['docs']);
|
|
583
|
+
|
|
584
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('adds repo store definition when creating repo store with path', async () => {
|
|
588
|
+
const repoDir = await mkdtemp(join(tmpdir(), 'repo-store-'));
|
|
589
|
+
const result = await serviceWithDefs.create({
|
|
590
|
+
name: 'my-repo',
|
|
591
|
+
type: 'repo',
|
|
592
|
+
path: repoDir,
|
|
593
|
+
branch: 'main',
|
|
594
|
+
description: 'Example repo',
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
expect(result.success).toBe(true);
|
|
598
|
+
|
|
599
|
+
const def = await defService.getByName('my-repo');
|
|
600
|
+
expect(def).toBeDefined();
|
|
601
|
+
expect(def?.type).toBe('repo');
|
|
602
|
+
if (def?.type === 'repo') {
|
|
603
|
+
expect(def.branch).toBe('main');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
await rm(repoDir, { recursive: true, force: true });
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('adds web store definition when creating web store', async () => {
|
|
610
|
+
const result = await serviceWithDefs.create({
|
|
611
|
+
name: 'my-site',
|
|
612
|
+
type: 'web',
|
|
613
|
+
url: 'https://example.com/docs',
|
|
614
|
+
depth: 2,
|
|
615
|
+
description: 'Example site',
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
expect(result.success).toBe(true);
|
|
619
|
+
|
|
620
|
+
const def = await defService.getByName('my-site');
|
|
621
|
+
expect(def).toBeDefined();
|
|
622
|
+
expect(def?.type).toBe('web');
|
|
623
|
+
if (def?.type === 'web') {
|
|
624
|
+
expect(def.url).toBe('https://example.com/docs');
|
|
625
|
+
expect(def.depth).toBe(2);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('does not add definition when store creation fails', async () => {
|
|
630
|
+
const result = await serviceWithDefs.create({
|
|
631
|
+
name: 'bad-store',
|
|
632
|
+
type: 'file',
|
|
633
|
+
path: '/nonexistent/path',
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
expect(result.success).toBe(false);
|
|
637
|
+
|
|
638
|
+
const def = await defService.getByName('bad-store');
|
|
639
|
+
expect(def).toBeUndefined();
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('does not add definition when skipDefinitionSync is true', async () => {
|
|
643
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'skip-def-'));
|
|
644
|
+
const result = await serviceWithDefs.create(
|
|
645
|
+
{
|
|
646
|
+
name: 'skip-store',
|
|
647
|
+
type: 'file',
|
|
648
|
+
path: storeDir,
|
|
649
|
+
},
|
|
650
|
+
{ skipDefinitionSync: true }
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
expect(result.success).toBe(true);
|
|
654
|
+
|
|
655
|
+
const def = await defService.getByName('skip-store');
|
|
656
|
+
expect(def).toBeUndefined();
|
|
657
|
+
|
|
658
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
describe('delete removes definition', () => {
|
|
663
|
+
it('removes definition when store is deleted', async () => {
|
|
664
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'del-store-'));
|
|
665
|
+
const createResult = await serviceWithDefs.create({
|
|
666
|
+
name: 'to-delete',
|
|
667
|
+
type: 'file',
|
|
668
|
+
path: storeDir,
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
if (!createResult.success) throw new Error('Create failed');
|
|
672
|
+
|
|
673
|
+
// Verify definition exists
|
|
674
|
+
let def = await defService.getByName('to-delete');
|
|
675
|
+
expect(def).toBeDefined();
|
|
676
|
+
|
|
677
|
+
// Delete the store
|
|
678
|
+
const deleteResult = await serviceWithDefs.delete(createResult.data.id);
|
|
679
|
+
expect(deleteResult.success).toBe(true);
|
|
680
|
+
|
|
681
|
+
// Definition should be removed
|
|
682
|
+
def = await defService.getByName('to-delete');
|
|
683
|
+
expect(def).toBeUndefined();
|
|
684
|
+
|
|
685
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('does not remove definition when skipDefinitionSync is true', async () => {
|
|
689
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'del-skip-'));
|
|
690
|
+
const createResult = await serviceWithDefs.create({
|
|
691
|
+
name: 'keep-def',
|
|
692
|
+
type: 'file',
|
|
693
|
+
path: storeDir,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
if (!createResult.success) throw new Error('Create failed');
|
|
697
|
+
|
|
698
|
+
// Delete with skipDefinitionSync
|
|
699
|
+
const deleteResult = await serviceWithDefs.delete(createResult.data.id, {
|
|
700
|
+
skipDefinitionSync: true,
|
|
701
|
+
});
|
|
702
|
+
expect(deleteResult.success).toBe(true);
|
|
703
|
+
|
|
704
|
+
// Definition should still exist
|
|
705
|
+
const def = await defService.getByName('keep-def');
|
|
706
|
+
expect(def).toBeDefined();
|
|
707
|
+
|
|
708
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
describe('update syncs definition', () => {
|
|
713
|
+
it('updates definition when store description is updated', async () => {
|
|
714
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'upd-store-'));
|
|
715
|
+
const createResult = await serviceWithDefs.create({
|
|
716
|
+
name: 'to-update',
|
|
717
|
+
type: 'file',
|
|
718
|
+
path: storeDir,
|
|
719
|
+
description: 'Original description',
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
if (!createResult.success) throw new Error('Create failed');
|
|
723
|
+
|
|
724
|
+
const updateResult = await serviceWithDefs.update(createResult.data.id, {
|
|
725
|
+
description: 'Updated description',
|
|
726
|
+
});
|
|
727
|
+
expect(updateResult.success).toBe(true);
|
|
728
|
+
|
|
729
|
+
const def = await defService.getByName('to-update');
|
|
730
|
+
expect(def?.description).toBe('Updated description');
|
|
731
|
+
|
|
732
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it('updates definition when store tags are updated', async () => {
|
|
736
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'upd-tags-'));
|
|
737
|
+
const createResult = await serviceWithDefs.create({
|
|
738
|
+
name: 'tag-store',
|
|
739
|
+
type: 'file',
|
|
740
|
+
path: storeDir,
|
|
741
|
+
tags: ['old'],
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
if (!createResult.success) throw new Error('Create failed');
|
|
745
|
+
|
|
746
|
+
const updateResult = await serviceWithDefs.update(createResult.data.id, {
|
|
747
|
+
tags: ['new', 'tags'],
|
|
748
|
+
});
|
|
749
|
+
expect(updateResult.success).toBe(true);
|
|
750
|
+
|
|
751
|
+
const def = await defService.getByName('tag-store');
|
|
752
|
+
expect(def?.tags).toEqual(['new', 'tags']);
|
|
753
|
+
|
|
754
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('does not update definition when skipDefinitionSync is true', async () => {
|
|
758
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'upd-skip-'));
|
|
759
|
+
const createResult = await serviceWithDefs.create({
|
|
760
|
+
name: 'skip-update',
|
|
761
|
+
type: 'file',
|
|
762
|
+
path: storeDir,
|
|
763
|
+
description: 'Original',
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
if (!createResult.success) throw new Error('Create failed');
|
|
767
|
+
|
|
768
|
+
const updateResult = await serviceWithDefs.update(
|
|
769
|
+
createResult.data.id,
|
|
770
|
+
{ description: 'Updated' },
|
|
771
|
+
{ skipDefinitionSync: true }
|
|
772
|
+
);
|
|
773
|
+
expect(updateResult.success).toBe(true);
|
|
774
|
+
|
|
775
|
+
const def = await defService.getByName('skip-update');
|
|
776
|
+
expect(def?.description).toBe('Original');
|
|
777
|
+
|
|
778
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
describe('persistence', () => {
|
|
783
|
+
it('persists definition to config file', async () => {
|
|
784
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'persist-def-'));
|
|
785
|
+
await serviceWithDefs.create({
|
|
786
|
+
name: 'persistent-store',
|
|
787
|
+
type: 'file',
|
|
788
|
+
path: storeDir,
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
// Read config file directly
|
|
792
|
+
const configPath = join(projectRoot, '.bluera/bluera-knowledge/stores.config.json');
|
|
793
|
+
const content = await readFile(configPath, 'utf-8');
|
|
794
|
+
const config = JSON.parse(content);
|
|
795
|
+
|
|
796
|
+
expect(config.stores).toHaveLength(1);
|
|
797
|
+
expect(config.stores[0].name).toBe('persistent-store');
|
|
798
|
+
|
|
799
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
describe('without definition service', () => {
|
|
804
|
+
it('works normally without definition service injected', async () => {
|
|
805
|
+
// Use the storeService from outer scope (no definition service)
|
|
806
|
+
const storeDir = await mkdtemp(join(tmpdir(), 'no-def-'));
|
|
807
|
+
const result = await storeService.create({
|
|
808
|
+
name: 'no-def-store',
|
|
809
|
+
type: 'file',
|
|
810
|
+
path: storeDir,
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
expect(result.success).toBe(true);
|
|
814
|
+
|
|
815
|
+
await rm(storeDir, { recursive: true, force: true });
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
});
|
|
541
819
|
});
|
|
@@ -4,8 +4,15 @@ import { join, resolve } from 'node:path';
|
|
|
4
4
|
import { cloneRepository } from '../plugin/git-clone.js';
|
|
5
5
|
import { createStoreId } from '../types/brands.js';
|
|
6
6
|
import { ok, err } from '../types/result.js';
|
|
7
|
+
import type { StoreDefinitionService } from './store-definition.service.js';
|
|
7
8
|
import type { StoreId } from '../types/brands.js';
|
|
8
9
|
import type { Result } from '../types/result.js';
|
|
10
|
+
import type {
|
|
11
|
+
StoreDefinition,
|
|
12
|
+
FileStoreDefinition,
|
|
13
|
+
RepoStoreDefinition,
|
|
14
|
+
WebStoreDefinition,
|
|
15
|
+
} from '../types/store-definition.js';
|
|
9
16
|
import type { Store, FileStore, RepoStore, WebStore, StoreType } from '../types/store.js';
|
|
10
17
|
|
|
11
18
|
/**
|
|
@@ -31,16 +38,28 @@ export interface CreateStoreInput {
|
|
|
31
38
|
depth?: number | undefined;
|
|
32
39
|
}
|
|
33
40
|
|
|
41
|
+
export interface StoreServiceOptions {
|
|
42
|
+
/** Optional definition service for auto-updating git-committable config */
|
|
43
|
+
definitionService?: StoreDefinitionService;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface OperationOptions {
|
|
47
|
+
/** Skip syncing to store definitions (used by stores:sync command) */
|
|
48
|
+
skipDefinitionSync?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
interface StoreRegistry {
|
|
35
52
|
stores: Store[];
|
|
36
53
|
}
|
|
37
54
|
|
|
38
55
|
export class StoreService {
|
|
39
56
|
private readonly dataDir: string;
|
|
57
|
+
private readonly definitionService: StoreDefinitionService | undefined;
|
|
40
58
|
private registry: StoreRegistry = { stores: [] };
|
|
41
59
|
|
|
42
|
-
constructor(dataDir: string) {
|
|
60
|
+
constructor(dataDir: string, options?: StoreServiceOptions) {
|
|
43
61
|
this.dataDir = dataDir;
|
|
62
|
+
this.definitionService = options?.definitionService ?? undefined;
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
async initialize(): Promise<void> {
|
|
@@ -48,7 +67,54 @@ export class StoreService {
|
|
|
48
67
|
await this.loadRegistry();
|
|
49
68
|
}
|
|
50
69
|
|
|
51
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Convert a Store and CreateStoreInput to a StoreDefinition for persistence.
|
|
72
|
+
*/
|
|
73
|
+
private createDefinitionFromStore(store: Store, input: CreateStoreInput): StoreDefinition {
|
|
74
|
+
// Copy tags array to convert from readonly to mutable
|
|
75
|
+
const tags = store.tags !== undefined ? [...store.tags] : undefined;
|
|
76
|
+
const base = {
|
|
77
|
+
name: store.name,
|
|
78
|
+
description: store.description,
|
|
79
|
+
tags,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
switch (store.type) {
|
|
83
|
+
case 'file': {
|
|
84
|
+
const fileStore = store;
|
|
85
|
+
const fileDef: FileStoreDefinition = {
|
|
86
|
+
...base,
|
|
87
|
+
type: 'file',
|
|
88
|
+
// Use original input path if provided (may be relative), otherwise use normalized
|
|
89
|
+
path: input.path ?? fileStore.path,
|
|
90
|
+
};
|
|
91
|
+
return fileDef;
|
|
92
|
+
}
|
|
93
|
+
case 'repo': {
|
|
94
|
+
const repoStore = store;
|
|
95
|
+
const repoDef: RepoStoreDefinition = {
|
|
96
|
+
...base,
|
|
97
|
+
type: 'repo',
|
|
98
|
+
url: repoStore.url ?? '',
|
|
99
|
+
branch: repoStore.branch,
|
|
100
|
+
depth: input.depth,
|
|
101
|
+
};
|
|
102
|
+
return repoDef;
|
|
103
|
+
}
|
|
104
|
+
case 'web': {
|
|
105
|
+
const webStore = store;
|
|
106
|
+
const webDef: WebStoreDefinition = {
|
|
107
|
+
...base,
|
|
108
|
+
type: 'web',
|
|
109
|
+
url: webStore.url,
|
|
110
|
+
depth: webStore.depth,
|
|
111
|
+
};
|
|
112
|
+
return webDef;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async create(input: CreateStoreInput, options?: OperationOptions): Promise<Result<Store>> {
|
|
52
118
|
if (!input.name || input.name.trim() === '') {
|
|
53
119
|
return err(new Error('Store name cannot be empty'));
|
|
54
120
|
}
|
|
@@ -157,6 +223,12 @@ export class StoreService {
|
|
|
157
223
|
this.registry.stores.push(store);
|
|
158
224
|
await this.saveRegistry();
|
|
159
225
|
|
|
226
|
+
// Sync to store definitions if service is available and not skipped
|
|
227
|
+
if (this.definitionService !== undefined && options?.skipDefinitionSync !== true) {
|
|
228
|
+
const definition = this.createDefinitionFromStore(store, input);
|
|
229
|
+
await this.definitionService.addDefinition(definition);
|
|
230
|
+
}
|
|
231
|
+
|
|
160
232
|
return ok(store);
|
|
161
233
|
}
|
|
162
234
|
|
|
@@ -183,7 +255,8 @@ export class StoreService {
|
|
|
183
255
|
|
|
184
256
|
async update(
|
|
185
257
|
id: StoreId,
|
|
186
|
-
updates: Partial<Pick<Store, 'name' | 'description' | 'tags'
|
|
258
|
+
updates: Partial<Pick<Store, 'name' | 'description' | 'tags'>>,
|
|
259
|
+
options?: OperationOptions
|
|
187
260
|
): Promise<Result<Store>> {
|
|
188
261
|
const index = this.registry.stores.findIndex((s) => s.id === id);
|
|
189
262
|
if (index === -1) {
|
|
@@ -205,18 +278,42 @@ export class StoreService {
|
|
|
205
278
|
this.registry.stores[index] = updated;
|
|
206
279
|
await this.saveRegistry();
|
|
207
280
|
|
|
281
|
+
// Sync to store definitions if service is available and not skipped
|
|
282
|
+
if (this.definitionService !== undefined && options?.skipDefinitionSync !== true) {
|
|
283
|
+
const defUpdates: { description?: string; tags?: string[] } = {};
|
|
284
|
+
if (updates.description !== undefined) {
|
|
285
|
+
defUpdates.description = updates.description;
|
|
286
|
+
}
|
|
287
|
+
if (updates.tags !== undefined) {
|
|
288
|
+
// Copy tags array to convert from readonly to mutable
|
|
289
|
+
defUpdates.tags = [...updates.tags];
|
|
290
|
+
}
|
|
291
|
+
await this.definitionService.updateDefinition(store.name, defUpdates);
|
|
292
|
+
}
|
|
293
|
+
|
|
208
294
|
return ok(updated);
|
|
209
295
|
}
|
|
210
296
|
|
|
211
|
-
async delete(id: StoreId): Promise<Result<void>> {
|
|
297
|
+
async delete(id: StoreId, options?: OperationOptions): Promise<Result<void>> {
|
|
212
298
|
const index = this.registry.stores.findIndex((s) => s.id === id);
|
|
213
299
|
if (index === -1) {
|
|
214
300
|
return err(new Error(`Store not found: ${id}`));
|
|
215
301
|
}
|
|
216
302
|
|
|
303
|
+
const store = this.registry.stores[index];
|
|
304
|
+
if (store === undefined) {
|
|
305
|
+
return err(new Error(`Store not found: ${id}`));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const storeName = store.name;
|
|
217
309
|
this.registry.stores.splice(index, 1);
|
|
218
310
|
await this.saveRegistry();
|
|
219
311
|
|
|
312
|
+
// Sync to store definitions if service is available and not skipped
|
|
313
|
+
if (this.definitionService !== undefined && options?.skipDefinitionSync !== true) {
|
|
314
|
+
await this.definitionService.removeDefinition(storeName);
|
|
315
|
+
}
|
|
316
|
+
|
|
220
317
|
return ok(undefined);
|
|
221
318
|
}
|
|
222
319
|
|
package/src/types/index.ts
CHANGED
|
@@ -53,3 +53,21 @@ export {
|
|
|
53
53
|
|
|
54
54
|
// Progress types
|
|
55
55
|
export { type ProgressEvent, type ProgressCallback } from './progress.js';
|
|
56
|
+
|
|
57
|
+
// Store definition types (for git-committable config)
|
|
58
|
+
export {
|
|
59
|
+
type StoreDefinition,
|
|
60
|
+
type FileStoreDefinition,
|
|
61
|
+
type RepoStoreDefinition,
|
|
62
|
+
type WebStoreDefinition,
|
|
63
|
+
type StoreDefinitionsConfig,
|
|
64
|
+
StoreDefinitionSchema,
|
|
65
|
+
FileStoreDefinitionSchema,
|
|
66
|
+
RepoStoreDefinitionSchema,
|
|
67
|
+
WebStoreDefinitionSchema,
|
|
68
|
+
StoreDefinitionsConfigSchema,
|
|
69
|
+
isFileStoreDefinition,
|
|
70
|
+
isRepoStoreDefinition,
|
|
71
|
+
isWebStoreDefinition,
|
|
72
|
+
DEFAULT_STORE_DEFINITIONS_CONFIG,
|
|
73
|
+
} from './store-definition.js';
|