@utilarium/overcontext 0.0.4-dev.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.
Files changed (87) hide show
  1. package/LICENSE +65 -0
  2. package/README.md +286 -0
  3. package/dist/api/builder.d.ts +26 -0
  4. package/dist/api/builder.js +24 -0
  5. package/dist/api/builder.js.map +1 -0
  6. package/dist/api/context.d.ts +90 -0
  7. package/dist/api/context.js +94 -0
  8. package/dist/api/context.js.map +1 -0
  9. package/dist/api/index.d.ts +6 -0
  10. package/dist/api/query-builder.d.ts +51 -0
  11. package/dist/api/query-builder.js +95 -0
  12. package/dist/api/query-builder.js.map +1 -0
  13. package/dist/api/query.d.ts +32 -0
  14. package/dist/api/search.d.ts +20 -0
  15. package/dist/api/search.js +112 -0
  16. package/dist/api/search.js.map +1 -0
  17. package/dist/api/slug.d.ts +10 -0
  18. package/dist/api/slug.js +55 -0
  19. package/dist/api/slug.js.map +1 -0
  20. package/dist/cli/builder.d.ts +74 -0
  21. package/dist/cli/builder.js +42 -0
  22. package/dist/cli/builder.js.map +1 -0
  23. package/dist/cli/commands.d.ts +53 -0
  24. package/dist/cli/commands.js +57 -0
  25. package/dist/cli/commands.js.map +1 -0
  26. package/dist/cli/formatters.d.ts +15 -0
  27. package/dist/cli/formatters.js +50 -0
  28. package/dist/cli/formatters.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/discovery/context-root.d.ts +31 -0
  31. package/dist/discovery/context-root.js +48 -0
  32. package/dist/discovery/context-root.js.map +1 -0
  33. package/dist/discovery/hierarchical-provider.d.ts +13 -0
  34. package/dist/discovery/hierarchical-provider.js +102 -0
  35. package/dist/discovery/hierarchical-provider.js.map +1 -0
  36. package/dist/discovery/index.d.ts +18 -0
  37. package/dist/discovery/index.js +47 -0
  38. package/dist/discovery/index.js.map +1 -0
  39. package/dist/discovery/walker.d.ts +36 -0
  40. package/dist/discovery/walker.js +87 -0
  41. package/dist/discovery/walker.js.map +1 -0
  42. package/dist/index.cjs +1763 -0
  43. package/dist/index.cjs.map +1 -0
  44. package/dist/index.d.ts +9 -0
  45. package/dist/index.js +24 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/namespace/index.d.ts +3 -0
  48. package/dist/namespace/multi-context.d.ts +40 -0
  49. package/dist/namespace/multi-context.js +72 -0
  50. package/dist/namespace/multi-context.js.map +1 -0
  51. package/dist/namespace/resolver.d.ts +33 -0
  52. package/dist/namespace/resolver.js +85 -0
  53. package/dist/namespace/resolver.js.map +1 -0
  54. package/dist/namespace/types.d.ts +41 -0
  55. package/dist/overcontext.d.ts +7 -0
  56. package/dist/schema/base.d.ts +29 -0
  57. package/dist/schema/base.js +24 -0
  58. package/dist/schema/base.js.map +1 -0
  59. package/dist/schema/builder.d.ts +28 -0
  60. package/dist/schema/builder.js +39 -0
  61. package/dist/schema/builder.js.map +1 -0
  62. package/dist/schema/index.d.ts +5 -0
  63. package/dist/schema/inference.d.ts +49 -0
  64. package/dist/schema/inference.js +15 -0
  65. package/dist/schema/inference.js.map +1 -0
  66. package/dist/schema/registry.d.ts +74 -0
  67. package/dist/schema/registry.js +117 -0
  68. package/dist/schema/registry.js.map +1 -0
  69. package/dist/schema/validation.d.ts +26 -0
  70. package/dist/schema/validation.js +51 -0
  71. package/dist/schema/validation.js.map +1 -0
  72. package/dist/storage/errors.d.ts +35 -0
  73. package/dist/storage/errors.js +58 -0
  74. package/dist/storage/errors.js.map +1 -0
  75. package/dist/storage/events.d.ts +50 -0
  76. package/dist/storage/filesystem.d.ts +10 -0
  77. package/dist/storage/filesystem.js +284 -0
  78. package/dist/storage/filesystem.js.map +1 -0
  79. package/dist/storage/index.d.ts +6 -0
  80. package/dist/storage/interface.d.ts +109 -0
  81. package/dist/storage/memory.d.ts +7 -0
  82. package/dist/storage/memory.js +128 -0
  83. package/dist/storage/memory.js.map +1 -0
  84. package/dist/storage/observable.d.ts +6 -0
  85. package/dist/storage/observable.js +98 -0
  86. package/dist/storage/observable.js.map +1 -0
  87. package/package.json +85 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sources":["../../src/cli/commands.ts"],"sourcesContent":["import { SchemaMap } from '../schema/registry';\nimport { OvercontextAPI } from '../api/context';\nimport { formatEntities, formatEntity, OutputFormat } from './formatters';\n\nexport interface CommandContext<TSchemas extends SchemaMap = SchemaMap> {\n api: OvercontextAPI<TSchemas>;\n outputFormat: OutputFormat;\n namespace?: string;\n}\n\nexport interface ListCommandOptions {\n type: string;\n search?: string;\n limit?: number;\n fields?: string[];\n}\n\n/**\n * Build a list command handler.\n */\nexport const listCommand = async <TSchemas extends SchemaMap>(\n ctx: CommandContext<TSchemas>,\n options: ListCommandOptions\n): Promise<string> => {\n const result = await ctx.api.search({\n type: options.type,\n namespace: ctx.namespace,\n search: options.search,\n limit: options.limit || 20,\n });\n\n return formatEntities(result.items, {\n format: ctx.outputFormat,\n fields: options.fields,\n });\n};\n\nexport interface GetCommandOptions {\n type: string;\n id: string;\n}\n\n/**\n * Build a get command handler.\n */\nexport const getCommand = async <TSchemas extends SchemaMap>(\n ctx: CommandContext<TSchemas>,\n options: GetCommandOptions\n): Promise<string> => {\n const entity = await ctx.api.get(options.type as keyof TSchemas & string, options.id, ctx.namespace);\n\n if (!entity) {\n throw new Error(`Entity not found: ${options.type}/${options.id}`);\n }\n\n return formatEntity(entity, { format: ctx.outputFormat });\n};\n\nexport interface CreateCommandOptions {\n type: string;\n name: string;\n data?: Record<string, unknown>;\n id?: string;\n}\n\n/**\n * Build a create command handler.\n */\nexport const createCommand = async <TSchemas extends SchemaMap>(\n ctx: CommandContext<TSchemas>,\n options: CreateCommandOptions\n): Promise<string> => {\n const entity = await ctx.api.create(\n options.type as keyof TSchemas & string,\n { name: options.name, ...options.data } as any,\n { id: options.id, namespace: ctx.namespace }\n );\n\n return `Created ${options.type}: ${entity.id}`;\n};\n\nexport interface UpdateCommandOptions {\n type: string;\n id: string;\n data: Record<string, unknown>;\n}\n\n/**\n * Build an update command handler.\n */\nexport const updateCommand = async <TSchemas extends SchemaMap>(\n ctx: CommandContext<TSchemas>,\n options: UpdateCommandOptions\n): Promise<string> => {\n await ctx.api.update(\n options.type as keyof TSchemas & string,\n options.id,\n options.data as any,\n ctx.namespace\n );\n\n return `Updated ${options.type}: ${options.id}`;\n};\n\nexport interface DeleteCommandOptions {\n type: string;\n id: string;\n}\n\n/**\n * Build a delete command handler.\n */\nexport const deleteCommand = async <TSchemas extends SchemaMap>(\n ctx: CommandContext<TSchemas>,\n options: DeleteCommandOptions\n): Promise<string> => {\n const deleted = await ctx.api.delete(options.type, options.id, ctx.namespace);\n\n if (!deleted) {\n throw new Error(`Entity not found: ${options.type}/${options.id}`);\n }\n\n return `Deleted ${options.type}: ${options.id}`;\n};\n"],"names":["listCommand","ctx","options","result","api","search","type","namespace","limit","formatEntities","items","format","outputFormat","fields","getCommand","entity","get","id","Error","formatEntity","createCommand","create","name","data","updateCommand","update","deleteCommand","deleted","delete"],"mappings":";;AAiBA;;AAEC,IACM,MAAMA,WAAAA,GAAc,OACvBC,GAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,MAAMC,SAAS,MAAMF,GAAAA,CAAIG,GAAG,CAACC,MAAM,CAAC;AAChCC,QAAAA,IAAAA,EAAMJ,QAAQI,IAAI;AAClBC,QAAAA,SAAAA,EAAWN,IAAIM,SAAS;AACxBF,QAAAA,MAAAA,EAAQH,QAAQG,MAAM;QACtBG,KAAAA,EAAON,OAAAA,CAAQM,KAAK,IAAI;AAC5B,KAAA,CAAA;IAEA,OAAOC,cAAAA,CAAeN,MAAAA,CAAOO,KAAK,EAAE;AAChCC,QAAAA,MAAAA,EAAQV,IAAIW,YAAY;AACxBC,QAAAA,MAAAA,EAAQX,QAAQW;AACpB,KAAA,CAAA;AACJ;AAOA;;AAEC,IACM,MAAMC,UAAAA,GAAa,OACtBb,GAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,MAAMa,MAAAA,GAAS,MAAMd,GAAAA,CAAIG,GAAG,CAACY,GAAG,CAACd,OAAAA,CAAQI,IAAI,EAA6BJ,OAAAA,CAAQe,EAAE,EAAEhB,IAAIM,SAAS,CAAA;AAEnG,IAAA,IAAI,CAACQ,MAAAA,EAAQ;AACT,QAAA,MAAM,IAAIG,KAAAA,CAAM,CAAC,kBAAkB,EAAEhB,OAAAA,CAAQI,IAAI,CAAC,CAAC,EAAEJ,OAAAA,CAAQe,EAAE,CAAA,CAAE,CAAA;AACrE,IAAA;AAEA,IAAA,OAAOE,aAAaJ,MAAAA,EAAQ;AAAEJ,QAAAA,MAAAA,EAAQV,IAAIW;AAAa,KAAA,CAAA;AAC3D;AASA;;AAEC,IACM,MAAMQ,aAAAA,GAAgB,OACzBnB,GAAAA,EACAC,OAAAA,GAAAA;IAEA,MAAMa,MAAAA,GAAS,MAAMd,GAAAA,CAAIG,GAAG,CAACiB,MAAM,CAC/BnB,OAAAA,CAAQI,IAAI,EACZ;AAAEgB,QAAAA,IAAAA,EAAMpB,QAAQoB,IAAI;AAAE,QAAA,GAAGpB,QAAQqB;KAAK,EACtC;AAAEN,QAAAA,EAAAA,EAAIf,QAAQe,EAAE;AAAEV,QAAAA,SAAAA,EAAWN,IAAIM;AAAU,KAAA,CAAA;IAG/C,OAAO,CAAC,QAAQ,EAAEL,OAAAA,CAAQI,IAAI,CAAC,EAAE,EAAES,MAAAA,CAAOE,EAAE,CAAA,CAAE;AAClD;AAQA;;AAEC,IACM,MAAMO,aAAAA,GAAgB,OACzBvB,GAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,MAAMD,GAAAA,CAAIG,GAAG,CAACqB,MAAM,CAChBvB,OAAAA,CAAQI,IAAI,EACZJ,OAAAA,CAAQe,EAAE,EACVf,OAAAA,CAAQqB,IAAI,EACZtB,IAAIM,SAAS,CAAA;IAGjB,OAAO,CAAC,QAAQ,EAAEL,OAAAA,CAAQI,IAAI,CAAC,EAAE,EAAEJ,OAAAA,CAAQe,EAAE,CAAA,CAAE;AACnD;AAOA;;AAEC,IACM,MAAMS,aAAAA,GAAgB,OACzBzB,GAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,MAAMyB,OAAAA,GAAU,MAAM1B,GAAAA,CAAIG,GAAG,CAACwB,MAAM,CAAC1B,OAAAA,CAAQI,IAAI,EAAEJ,OAAAA,CAAQe,EAAE,EAAEhB,IAAIM,SAAS,CAAA;AAE5E,IAAA,IAAI,CAACoB,OAAAA,EAAS;AACV,QAAA,MAAM,IAAIT,KAAAA,CAAM,CAAC,kBAAkB,EAAEhB,OAAAA,CAAQI,IAAI,CAAC,CAAC,EAAEJ,OAAAA,CAAQe,EAAE,CAAA,CAAE,CAAA;AACrE,IAAA;IAEA,OAAO,CAAC,QAAQ,EAAEf,OAAAA,CAAQI,IAAI,CAAC,EAAE,EAAEJ,OAAAA,CAAQe,EAAE,CAAA,CAAE;AACnD;;;;"}
@@ -0,0 +1,15 @@
1
+ import { BaseEntity } from '../schema/base';
2
+ export type OutputFormat = 'table' | 'json' | 'yaml';
3
+ export interface FormatterOptions {
4
+ format: OutputFormat;
5
+ fields?: string[];
6
+ noHeaders?: boolean;
7
+ }
8
+ /**
9
+ * Format entities for CLI output.
10
+ */
11
+ export declare const formatEntities: <T extends BaseEntity>(entities: T[], options: FormatterOptions) => string;
12
+ /**
13
+ * Format a single entity for display.
14
+ */
15
+ export declare const formatEntity: <T extends BaseEntity>(entity: T, options: Pick<FormatterOptions, "format">) => string;
@@ -0,0 +1,50 @@
1
+ import * as yaml from 'js-yaml';
2
+
3
+ /**
4
+ * Format entities for CLI output.
5
+ */ const formatEntities = (entities, options)=>{
6
+ const { format, fields, noHeaders } = options;
7
+ if (format === 'json') {
8
+ return JSON.stringify(entities, null, 2);
9
+ }
10
+ if (format === 'yaml') {
11
+ return yaml.dump(entities);
12
+ }
13
+ // Table format
14
+ if (entities.length === 0) {
15
+ return 'No entities found.';
16
+ }
17
+ const displayFields = fields || [
18
+ 'id',
19
+ 'name',
20
+ 'type'
21
+ ];
22
+ const rows = [];
23
+ if (!noHeaders) {
24
+ rows.push(displayFields.map((f)=>f.toUpperCase()));
25
+ }
26
+ for (const entity of entities){
27
+ const row = displayFields.map((f)=>{
28
+ const value = entity[f];
29
+ if (value === undefined || value === null) return '-';
30
+ if (typeof value === 'object') return JSON.stringify(value);
31
+ return String(value);
32
+ });
33
+ rows.push(row);
34
+ }
35
+ // Calculate column widths
36
+ const widths = displayFields.map((_, i)=>Math.max(...rows.map((row)=>row[i].length)));
37
+ // Format rows
38
+ return rows.map((row)=>row.map((cell, i)=>cell.padEnd(widths[i])).join(' ')).join('\n');
39
+ };
40
+ /**
41
+ * Format a single entity for display.
42
+ */ const formatEntity = (entity, options)=>{
43
+ if (options.format === 'json') {
44
+ return JSON.stringify(entity, null, 2);
45
+ }
46
+ return yaml.dump(entity);
47
+ };
48
+
49
+ export { formatEntities, formatEntity };
50
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","sources":["../../src/cli/formatters.ts"],"sourcesContent":["import * as yaml from 'js-yaml';\nimport { BaseEntity } from '../schema/base';\n\nexport type OutputFormat = 'table' | 'json' | 'yaml';\n\nexport interface FormatterOptions {\n format: OutputFormat;\n fields?: string[];\n noHeaders?: boolean;\n}\n\n/**\n * Format entities for CLI output.\n */\nexport const formatEntities = <T extends BaseEntity>(\n entities: T[],\n options: FormatterOptions\n): string => {\n const { format, fields, noHeaders } = options;\n\n if (format === 'json') {\n return JSON.stringify(entities, null, 2);\n }\n\n if (format === 'yaml') {\n return yaml.dump(entities);\n }\n\n // Table format\n if (entities.length === 0) {\n return 'No entities found.';\n }\n\n const displayFields = fields || ['id', 'name', 'type'];\n const rows: string[][] = [];\n\n if (!noHeaders) {\n rows.push(displayFields.map(f => f.toUpperCase()));\n }\n\n for (const entity of entities) {\n const row = displayFields.map(f => {\n const value = (entity as Record<string, unknown>)[f];\n if (value === undefined || value === null) return '-';\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n });\n rows.push(row);\n }\n\n // Calculate column widths\n const widths = displayFields.map((_, i) =>\n Math.max(...rows.map(row => row[i].length))\n );\n\n // Format rows\n return rows\n .map(row =>\n row.map((cell, i) => cell.padEnd(widths[i])).join(' ')\n )\n .join('\\n');\n};\n\n/**\n * Format a single entity for display.\n */\nexport const formatEntity = <T extends BaseEntity>(\n entity: T,\n options: Pick<FormatterOptions, 'format'>\n): string => {\n if (options.format === 'json') {\n return JSON.stringify(entity, null, 2);\n }\n return yaml.dump(entity);\n};\n"],"names":["formatEntities","entities","options","format","fields","noHeaders","JSON","stringify","yaml","dump","length","displayFields","rows","push","map","f","toUpperCase","entity","row","value","undefined","String","widths","_","i","Math","max","cell","padEnd","join","formatEntity"],"mappings":";;AAWA;;AAEC,IACM,MAAMA,cAAAA,GAAiB,CAC1BC,QAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,MAAM,EAAEC,MAAM,EAAEC,MAAM,EAAEC,SAAS,EAAE,GAAGH,OAAAA;AAEtC,IAAA,IAAIC,WAAW,MAAA,EAAQ;AACnB,QAAA,OAAOG,IAAAA,CAAKC,SAAS,CAACN,QAAAA,EAAU,IAAA,EAAM,CAAA,CAAA;AAC1C,IAAA;AAEA,IAAA,IAAIE,WAAW,MAAA,EAAQ;QACnB,OAAOK,IAAAA,CAAKC,IAAI,CAACR,QAAAA,CAAAA;AACrB,IAAA;;IAGA,IAAIA,QAAAA,CAASS,MAAM,KAAK,CAAA,EAAG;QACvB,OAAO,oBAAA;AACX,IAAA;AAEA,IAAA,MAAMC,gBAAgBP,MAAAA,IAAU;AAAC,QAAA,IAAA;AAAM,QAAA,MAAA;AAAQ,QAAA;AAAO,KAAA;AACtD,IAAA,MAAMQ,OAAmB,EAAE;AAE3B,IAAA,IAAI,CAACP,SAAAA,EAAW;QACZO,IAAAA,CAAKC,IAAI,CAACF,aAAAA,CAAcG,GAAG,CAACC,CAAAA,CAAAA,GAAKA,EAAEC,WAAW,EAAA,CAAA,CAAA;AAClD,IAAA;IAEA,KAAK,MAAMC,UAAUhB,QAAAA,CAAU;AAC3B,QAAA,MAAMiB,GAAAA,GAAMP,aAAAA,CAAcG,GAAG,CAACC,CAAAA,CAAAA,GAAAA;AAC1B,YAAA,MAAMI,KAAAA,GAASF,MAAkC,CAACF,CAAAA,CAAE;AACpD,YAAA,IAAII,KAAAA,KAAUC,SAAAA,IAAaD,KAAAA,KAAU,IAAA,EAAM,OAAO,GAAA;AAClD,YAAA,IAAI,OAAOA,KAAAA,KAAU,QAAA,EAAU,OAAOb,IAAAA,CAAKC,SAAS,CAACY,KAAAA,CAAAA;AACrD,YAAA,OAAOE,MAAAA,CAAOF,KAAAA,CAAAA;AAClB,QAAA,CAAA,CAAA;AACAP,QAAAA,IAAAA,CAAKC,IAAI,CAACK,GAAAA,CAAAA;AACd,IAAA;;AAGA,IAAA,MAAMI,SAASX,aAAAA,CAAcG,GAAG,CAAC,CAACS,CAAAA,EAAGC,IACjCC,IAAAA,CAAKC,GAAG,IAAId,IAAAA,CAAKE,GAAG,CAACI,CAAAA,GAAAA,GAAOA,GAAG,CAACM,CAAAA,CAAE,CAACd,MAAM,CAAA,CAAA,CAAA;;IAI7C,OAAOE,IAAAA,CACFE,GAAG,CAACI,CAAAA,MACDA,GAAAA,CAAIJ,GAAG,CAAC,CAACa,IAAAA,EAAMH,CAAAA,GAAMG,KAAKC,MAAM,CAACN,MAAM,CAACE,CAAAA,CAAE,GAAGK,IAAI,CAAC,IAAA,CAAA,CAAA,CAErDA,IAAI,CAAC,IAAA,CAAA;AACd;AAEA;;AAEC,IACM,MAAMC,YAAAA,GAAe,CACxBb,MAAAA,EACAf,OAAAA,GAAAA;IAEA,IAAIA,OAAAA,CAAQC,MAAM,KAAK,MAAA,EAAQ;AAC3B,QAAA,OAAOG,IAAAA,CAAKC,SAAS,CAACU,MAAAA,EAAQ,IAAA,EAAM,CAAA,CAAA;AACxC,IAAA;IACA,OAAOT,IAAAA,CAAKC,IAAI,CAACQ,MAAAA,CAAAA;AACrB;;;;"}
@@ -0,0 +1,3 @@
1
+ export * from './formatters';
2
+ export * from './commands';
3
+ export * from './builder';
@@ -0,0 +1,31 @@
1
+ import { SchemaRegistry } from '../schema/registry';
2
+ import { DiscoveredContextDir } from './walker';
3
+ export interface ContextRootOptions {
4
+ /** Starting directory for discovery */
5
+ startDir?: string;
6
+ /** Name of context directory (default: 'context') */
7
+ contextDirName?: string;
8
+ /** Max levels to search (default: 10) */
9
+ maxLevels?: number;
10
+ /** Markers that indicate project root */
11
+ projectMarkers?: string[];
12
+ /** Schema registry for type detection */
13
+ registry?: SchemaRegistry;
14
+ }
15
+ export interface ContextRoot {
16
+ /** All discovered context directories (closest first) */
17
+ directories: DiscoveredContextDir[];
18
+ /** Primary context directory for writes */
19
+ primary: string | undefined;
20
+ /** All paths to load context from (in merge order) */
21
+ contextPaths: string[];
22
+ /** All namespaces found across all directories */
23
+ allNamespaces: string[];
24
+ /** All types found across all directories */
25
+ allTypes: string[];
26
+ }
27
+ export declare const discoverContextRoot: (options?: ContextRootOptions) => Promise<ContextRoot>;
28
+ /**
29
+ * Create or ensure a context directory exists.
30
+ */
31
+ export declare const ensureContextRoot: (projectDir: string, contextDirName?: string) => Promise<string>;
@@ -0,0 +1,48 @@
1
+ import * as path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { createDirectoryWalker } from './walker.js';
4
+
5
+ const discoverContextRoot = async (options = {})=>{
6
+ var _directories_;
7
+ const { startDir = process.cwd(), contextDirName = 'context', maxLevels = 10, projectMarkers = [
8
+ '.git',
9
+ 'package.json'
10
+ ], registry } = options;
11
+ const walker = createDirectoryWalker({
12
+ startDir,
13
+ contextDirName,
14
+ maxLevels,
15
+ stopMarkers: projectMarkers,
16
+ registry
17
+ });
18
+ const directories = await walker.discover();
19
+ // Collect all namespaces and types (deduped)
20
+ const namespaceSet = new Set();
21
+ const typeSet = new Set();
22
+ for (const dir of directories){
23
+ for (const ns of dir.namespaces)namespaceSet.add(ns);
24
+ for (const type of dir.types)typeSet.add(type);
25
+ }
26
+ return {
27
+ directories,
28
+ primary: (_directories_ = directories[0]) === null || _directories_ === void 0 ? void 0 : _directories_.path,
29
+ contextPaths: directories.map((d)=>d.path),
30
+ allNamespaces: Array.from(namespaceSet),
31
+ allTypes: Array.from(typeSet)
32
+ };
33
+ };
34
+ /**
35
+ * Create or ensure a context directory exists.
36
+ */ const ensureContextRoot = async (projectDir, contextDirName = 'context')=>{
37
+ const contextPath = path.join(projectDir, contextDirName);
38
+ if (!existsSync(contextPath)) {
39
+ const fs = await import('node:fs/promises');
40
+ await fs.mkdir(contextPath, {
41
+ recursive: true
42
+ });
43
+ }
44
+ return contextPath;
45
+ };
46
+
47
+ export { discoverContextRoot, ensureContextRoot };
48
+ //# sourceMappingURL=context-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-root.js","sources":["../../src/discovery/context-root.ts"],"sourcesContent":["import * as path from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { SchemaRegistry } from '../schema/registry';\nimport { DiscoveredContextDir, createDirectoryWalker } from './walker';\n\nexport interface ContextRootOptions {\n /** Starting directory for discovery */\n startDir?: string;\n\n /** Name of context directory (default: 'context') */\n contextDirName?: string;\n\n /** Max levels to search (default: 10) */\n maxLevels?: number;\n\n /** Markers that indicate project root */\n projectMarkers?: string[];\n\n /** Schema registry for type detection */\n registry?: SchemaRegistry;\n}\n\nexport interface ContextRoot {\n /** All discovered context directories (closest first) */\n directories: DiscoveredContextDir[];\n\n /** Primary context directory for writes */\n primary: string | undefined;\n\n /** All paths to load context from (in merge order) */\n contextPaths: string[];\n\n /** All namespaces found across all directories */\n allNamespaces: string[];\n\n /** All types found across all directories */\n allTypes: string[];\n}\n\nexport const discoverContextRoot = async (\n options: ContextRootOptions = {}\n): Promise<ContextRoot> => {\n const {\n startDir = process.cwd(),\n contextDirName = 'context',\n maxLevels = 10,\n projectMarkers = ['.git', 'package.json'],\n registry,\n } = options;\n\n const walker = createDirectoryWalker({\n startDir,\n contextDirName,\n maxLevels,\n stopMarkers: projectMarkers,\n registry,\n });\n\n const directories = await walker.discover();\n\n // Collect all namespaces and types (deduped)\n const namespaceSet = new Set<string>();\n const typeSet = new Set<string>();\n\n for (const dir of directories) {\n for (const ns of dir.namespaces) namespaceSet.add(ns);\n for (const type of dir.types) typeSet.add(type);\n }\n\n return {\n directories,\n primary: directories[0]?.path,\n contextPaths: directories.map(d => d.path),\n allNamespaces: Array.from(namespaceSet),\n allTypes: Array.from(typeSet),\n };\n};\n\n/**\n * Create or ensure a context directory exists.\n */\nexport const ensureContextRoot = async (\n projectDir: string,\n contextDirName: string = 'context'\n): Promise<string> => {\n const contextPath = path.join(projectDir, contextDirName);\n\n if (!existsSync(contextPath)) {\n const fs = await import('node:fs/promises');\n await fs.mkdir(contextPath, { recursive: true });\n }\n\n return contextPath;\n};\n"],"names":["discoverContextRoot","options","directories","startDir","process","cwd","contextDirName","maxLevels","projectMarkers","registry","walker","createDirectoryWalker","stopMarkers","discover","namespaceSet","Set","typeSet","dir","ns","namespaces","add","type","types","primary","path","contextPaths","map","d","allNamespaces","Array","from","allTypes","ensureContextRoot","projectDir","contextPath","join","existsSync","fs","mkdir","recursive"],"mappings":";;;;AAuCO,MAAMA,mBAAAA,GAAsB,OAC/BC,OAAAA,GAA8B,EAAE,GAAA;AA+BnBC,IAAAA,IAAAA,aAAAA;AA7Bb,IAAA,MAAM,EACFC,QAAAA,GAAWC,OAAAA,CAAQC,GAAG,EAAE,EACxBC,cAAAA,GAAiB,SAAS,EAC1BC,SAAAA,GAAY,EAAE,EACdC,cAAAA,GAAiB;AAAC,QAAA,MAAA;AAAQ,QAAA;KAAe,EACzCC,QAAQ,EACX,GAAGR,OAAAA;AAEJ,IAAA,MAAMS,SAASC,qBAAAA,CAAsB;AACjCR,QAAAA,QAAAA;AACAG,QAAAA,cAAAA;AACAC,QAAAA,SAAAA;QACAK,WAAAA,EAAaJ,cAAAA;AACbC,QAAAA;AACJ,KAAA,CAAA;IAEA,MAAMP,WAAAA,GAAc,MAAMQ,MAAAA,CAAOG,QAAQ,EAAA;;AAGzC,IAAA,MAAMC,eAAe,IAAIC,GAAAA,EAAAA;AACzB,IAAA,MAAMC,UAAU,IAAID,GAAAA,EAAAA;IAEpB,KAAK,MAAME,OAAOf,WAAAA,CAAa;AAC3B,QAAA,KAAK,MAAMgB,EAAAA,IAAMD,GAAAA,CAAIE,UAAU,CAAEL,YAAAA,CAAaM,GAAG,CAACF,EAAAA,CAAAA;AAClD,QAAA,KAAK,MAAMG,IAAAA,IAAQJ,GAAAA,CAAIK,KAAK,CAAEN,OAAAA,CAAQI,GAAG,CAACC,IAAAA,CAAAA;AAC9C,IAAA;IAEA,OAAO;AACHnB,QAAAA,WAAAA;QACAqB,OAAO,EAAA,CAAErB,gBAAAA,WAAW,CAAC,EAAE,MAAA,IAAA,IAAdA,aAAAA,KAAAA,MAAAA,GAAAA,MAAAA,GAAAA,cAAgBsB,IAAI;AAC7BC,QAAAA,YAAAA,EAAcvB,YAAYwB,GAAG,CAACC,CAAAA,CAAAA,GAAKA,EAAEH,IAAI,CAAA;QACzCI,aAAAA,EAAeC,KAAAA,CAAMC,IAAI,CAAChB,YAAAA,CAAAA;QAC1BiB,QAAAA,EAAUF,KAAAA,CAAMC,IAAI,CAACd,OAAAA;AACzB,KAAA;AACJ;AAEA;;AAEC,IACM,MAAMgB,iBAAAA,GAAoB,OAC7BC,UAAAA,EACA3B,iBAAyB,SAAS,GAAA;AAElC,IAAA,MAAM4B,WAAAA,GAAcV,IAAAA,CAAKW,IAAI,CAACF,UAAAA,EAAY3B,cAAAA,CAAAA;IAE1C,IAAI,CAAC8B,WAAWF,WAAAA,CAAAA,EAAc;QAC1B,MAAMG,EAAAA,GAAK,MAAM,OAAO,kBAAA,CAAA;QACxB,MAAMA,EAAAA,CAAGC,KAAK,CAACJ,WAAAA,EAAa;YAAEK,SAAAA,EAAW;AAAK,SAAA,CAAA;AAClD,IAAA;IAEA,OAAOL,WAAAA;AACX;;;;"}
@@ -0,0 +1,13 @@
1
+ import { StorageProvider } from '../storage/interface';
2
+ import { SchemaRegistry } from '../schema/registry';
3
+ import { ContextRoot } from './context-root';
4
+ export interface HierarchicalProviderOptions {
5
+ contextRoot: ContextRoot;
6
+ registry: SchemaRegistry;
7
+ readonly?: boolean;
8
+ }
9
+ /**
10
+ * Storage provider that reads from multiple context directories
11
+ * and writes to the primary (closest) directory.
12
+ */
13
+ export declare const createHierarchicalProvider: (options: HierarchicalProviderOptions) => Promise<StorageProvider>;
@@ -0,0 +1,102 @@
1
+ import { createFileSystemProvider } from '../storage/filesystem.js';
2
+
3
+ /**
4
+ * Storage provider that reads from multiple context directories
5
+ * and writes to the primary (closest) directory.
6
+ */ const createHierarchicalProvider = async (options)=>{
7
+ const { contextRoot, registry, readonly = false } = options;
8
+ if (contextRoot.contextPaths.length === 0) {
9
+ throw new Error('No context directories found');
10
+ }
11
+ // Create read-only providers for each context directory
12
+ const readProviders = [];
13
+ for (const contextPath of contextRoot.contextPaths){
14
+ const fsProvider = await createFileSystemProvider({
15
+ basePath: contextPath,
16
+ registry,
17
+ createIfMissing: false,
18
+ readonly: true
19
+ });
20
+ await fsProvider.initialize();
21
+ readProviders.push(fsProvider);
22
+ }
23
+ // Primary provider for writes
24
+ const primaryProvider = await createFileSystemProvider({
25
+ basePath: contextRoot.primary,
26
+ registry,
27
+ createIfMissing: true,
28
+ readonly
29
+ });
30
+ await primaryProvider.initialize();
31
+ const findEntities = async (filter)=>{
32
+ const byId = new Map();
33
+ for (const p of [
34
+ ...readProviders
35
+ ].reverse()){
36
+ const results = await p.find(filter);
37
+ for (const entity of results){
38
+ byId.set(entity.id, entity);
39
+ }
40
+ }
41
+ let results = Array.from(byId.values());
42
+ if (filter.offset) results = results.slice(filter.offset);
43
+ if (filter.limit) results = results.slice(0, filter.limit);
44
+ return results;
45
+ };
46
+ return {
47
+ name: 'hierarchical',
48
+ location: contextRoot.primary,
49
+ registry,
50
+ async initialize () {},
51
+ async dispose () {
52
+ for (const p of readProviders)await p.dispose();
53
+ await primaryProvider.dispose();
54
+ },
55
+ async isAvailable () {
56
+ return primaryProvider.isAvailable();
57
+ },
58
+ // Read operations search all providers (closest first)
59
+ async get (type, id, namespace) {
60
+ for (const p of readProviders){
61
+ const entity = await p.get(type, id, namespace);
62
+ if (entity) return entity;
63
+ }
64
+ return undefined;
65
+ },
66
+ async getAll (type, namespace) {
67
+ const byId = new Map();
68
+ // Process in reverse order so closest overwrites
69
+ for (const p of [
70
+ ...readProviders
71
+ ].reverse()){
72
+ const entities = await p.getAll(type, namespace);
73
+ for (const entity of entities){
74
+ byId.set(entity.id, entity);
75
+ }
76
+ }
77
+ return Array.from(byId.values());
78
+ },
79
+ find: findEntities,
80
+ async exists (type, id, namespace) {
81
+ for (const p of readProviders){
82
+ if (await p.exists(type, id, namespace)) return true;
83
+ }
84
+ return false;
85
+ },
86
+ async count (filter) {
87
+ const results = await findEntities(filter);
88
+ return results.length;
89
+ },
90
+ // Write operations go to primary
91
+ save: (entity, namespace)=>primaryProvider.save(entity, namespace),
92
+ delete: (type, id, namespace)=>primaryProvider.delete(type, id, namespace),
93
+ saveBatch: (entities, namespace)=>primaryProvider.saveBatch(entities, namespace),
94
+ deleteBatch: (refs, namespace)=>primaryProvider.deleteBatch(refs, namespace),
95
+ listNamespaces: ()=>Promise.resolve(contextRoot.allNamespaces),
96
+ namespaceExists: (ns)=>Promise.resolve(contextRoot.allNamespaces.includes(ns)),
97
+ listTypes: ()=>Promise.resolve(contextRoot.allTypes)
98
+ };
99
+ };
100
+
101
+ export { createHierarchicalProvider };
102
+ //# sourceMappingURL=hierarchical-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchical-provider.js","sources":["../../src/discovery/hierarchical-provider.ts"],"sourcesContent":["import { StorageProvider, EntityFilter } from '../storage/interface';\nimport { BaseEntity } from '../schema/base';\nimport { SchemaRegistry } from '../schema/registry';\nimport { createFileSystemProvider } from '../storage/filesystem';\nimport { ContextRoot } from './context-root';\n\nexport interface HierarchicalProviderOptions {\n contextRoot: ContextRoot;\n registry: SchemaRegistry;\n readonly?: boolean;\n}\n\n/**\n * Storage provider that reads from multiple context directories\n * and writes to the primary (closest) directory.\n */\nexport const createHierarchicalProvider = async (\n options: HierarchicalProviderOptions\n): Promise<StorageProvider> => {\n const { contextRoot, registry, readonly = false } = options;\n\n if (contextRoot.contextPaths.length === 0) {\n throw new Error('No context directories found');\n }\n\n // Create read-only providers for each context directory\n const readProviders: StorageProvider[] = [];\n for (const contextPath of contextRoot.contextPaths) {\n const fsProvider = await createFileSystemProvider({\n basePath: contextPath,\n registry,\n createIfMissing: false,\n readonly: true,\n });\n await fsProvider.initialize();\n readProviders.push(fsProvider);\n }\n\n // Primary provider for writes\n const primaryProvider = await createFileSystemProvider({\n basePath: contextRoot.primary!,\n registry,\n createIfMissing: true,\n readonly,\n });\n await primaryProvider.initialize();\n\n const findEntities = async <T extends BaseEntity>(filter: EntityFilter): Promise<T[]> => {\n const byId = new Map<string, T>();\n\n for (const p of [...readProviders].reverse()) {\n const results = await p.find<T>(filter);\n for (const entity of results) {\n byId.set(entity.id, entity);\n }\n }\n\n let results = Array.from(byId.values());\n\n if (filter.offset) results = results.slice(filter.offset);\n if (filter.limit) results = results.slice(0, filter.limit);\n\n return results;\n };\n\n return {\n name: 'hierarchical',\n location: contextRoot.primary!,\n registry,\n\n async initialize() { },\n\n async dispose() {\n for (const p of readProviders) await p.dispose();\n await primaryProvider.dispose();\n },\n\n async isAvailable() {\n return primaryProvider.isAvailable();\n },\n\n // Read operations search all providers (closest first)\n async get<T extends BaseEntity>(type: string, id: string, namespace?: string) {\n for (const p of readProviders) {\n const entity = await p.get<T>(type, id, namespace);\n if (entity) return entity;\n }\n return undefined;\n },\n\n async getAll<T extends BaseEntity>(type: string, namespace?: string) {\n const byId = new Map<string, T>();\n\n // Process in reverse order so closest overwrites\n for (const p of [...readProviders].reverse()) {\n const entities = await p.getAll<T>(type, namespace);\n for (const entity of entities) {\n byId.set(entity.id, entity);\n }\n }\n\n return Array.from(byId.values());\n },\n\n find: findEntities,\n\n async exists(type: string, id: string, namespace?: string) {\n for (const p of readProviders) {\n if (await p.exists(type, id, namespace)) return true;\n }\n return false;\n },\n\n async count(filter: EntityFilter) {\n const results = await findEntities(filter);\n return results.length;\n },\n\n // Write operations go to primary\n save: (entity, namespace) => primaryProvider.save(entity, namespace),\n delete: (type, id, namespace) => primaryProvider.delete(type, id, namespace),\n saveBatch: (entities, namespace) => primaryProvider.saveBatch(entities, namespace),\n deleteBatch: (refs, namespace) => primaryProvider.deleteBatch(refs, namespace),\n\n listNamespaces: () => Promise.resolve(contextRoot.allNamespaces),\n namespaceExists: (ns) => Promise.resolve(contextRoot.allNamespaces.includes(ns)),\n listTypes: () => Promise.resolve(contextRoot.allTypes),\n };\n};\n"],"names":["createHierarchicalProvider","options","contextRoot","registry","readonly","contextPaths","length","Error","readProviders","contextPath","fsProvider","createFileSystemProvider","basePath","createIfMissing","initialize","push","primaryProvider","primary","findEntities","filter","byId","Map","p","reverse","results","find","entity","set","id","Array","from","values","offset","slice","limit","name","location","dispose","isAvailable","get","type","namespace","undefined","getAll","entities","exists","count","save","delete","saveBatch","deleteBatch","refs","listNamespaces","Promise","resolve","allNamespaces","namespaceExists","ns","includes","listTypes","allTypes"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { SchemaMap } from '../schema/registry';
2
+ import { ContextRootOptions } from './context-root';
3
+ import { OvercontextAPI } from '../api/context';
4
+ export * from './walker';
5
+ export * from './context-root';
6
+ export * from './hierarchical-provider';
7
+ export interface DiscoverOptions<TSchemas extends SchemaMap> extends ContextRootOptions {
8
+ /** Schemas to register */
9
+ schemas: TSchemas;
10
+ /** Custom plural names for types */
11
+ pluralNames?: Partial<Record<keyof TSchemas, string>>;
12
+ /** Whether context is readonly */
13
+ readonly?: boolean;
14
+ }
15
+ /**
16
+ * Discover context directories and create an API.
17
+ */
18
+ export declare const discoverOvercontext: <TSchemas extends SchemaMap>(options: DiscoverOptions<TSchemas>) => Promise<OvercontextAPI<TSchemas>>;
@@ -0,0 +1,47 @@
1
+ import 'node:fs/promises';
2
+ import 'node:fs';
3
+ import 'node:path';
4
+ import { discoverContextRoot } from './context-root.js';
5
+ export { ensureContextRoot } from './context-root.js';
6
+ import { createHierarchicalProvider } from './hierarchical-provider.js';
7
+ import { createSchemaRegistry } from '../schema/registry.js';
8
+ import { createContext } from '../api/context.js';
9
+
10
+ /**
11
+ * Discover context directories and create an API.
12
+ */ const discoverOvercontext = async (options)=>{
13
+ const { schemas, pluralNames = {}, readonly, ...rootOptions } = options;
14
+ // Create registry
15
+ const registry = createSchemaRegistry();
16
+ for (const [type, schema] of Object.entries(schemas)){
17
+ registry.register({
18
+ type,
19
+ schema: schema,
20
+ pluralName: pluralNames[type]
21
+ });
22
+ }
23
+ // Discover context directories
24
+ const contextRoot = await discoverContextRoot({
25
+ ...rootOptions,
26
+ registry
27
+ });
28
+ if (!contextRoot.primary) {
29
+ throw new Error('No context directory found');
30
+ }
31
+ // Create hierarchical provider
32
+ const provider = await createHierarchicalProvider({
33
+ contextRoot,
34
+ registry,
35
+ readonly
36
+ });
37
+ // Create context API
38
+ return createContext({
39
+ provider,
40
+ registry,
41
+ schemas,
42
+ defaultNamespace: undefined
43
+ });
44
+ };
45
+
46
+ export { createHierarchicalProvider, discoverContextRoot, discoverOvercontext };
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/discovery/index.ts"],"sourcesContent":["export * from './walker';\nexport * from './context-root';\nexport * from './hierarchical-provider';\n\nimport { z } from 'zod';\nimport { SchemaMap, createSchemaRegistry } from '../schema/registry';\nimport { BaseEntity } from '../schema/base';\nimport { discoverContextRoot, ContextRootOptions } from './context-root';\nimport { createHierarchicalProvider } from './hierarchical-provider';\nimport { createContext, OvercontextAPI } from '../api/context';\n\nexport interface DiscoverOptions<TSchemas extends SchemaMap> extends ContextRootOptions {\n /** Schemas to register */\n schemas: TSchemas;\n\n /** Custom plural names for types */\n pluralNames?: Partial<Record<keyof TSchemas, string>>;\n\n /** Whether context is readonly */\n readonly?: boolean;\n}\n\n/**\n * Discover context directories and create an API.\n */\nexport const discoverOvercontext = async <TSchemas extends SchemaMap>(\n options: DiscoverOptions<TSchemas>\n): Promise<OvercontextAPI<TSchemas>> => {\n const { schemas, pluralNames = {}, readonly, ...rootOptions } = options;\n\n // Create registry\n const registry = createSchemaRegistry();\n for (const [type, schema] of Object.entries(schemas)) {\n registry.register({\n type,\n schema: schema as z.ZodType<BaseEntity>,\n pluralName: (pluralNames as Record<string, string>)[type],\n });\n }\n\n // Discover context directories\n const contextRoot = await discoverContextRoot({\n ...rootOptions,\n registry,\n });\n\n if (!contextRoot.primary) {\n throw new Error('No context directory found');\n }\n\n // Create hierarchical provider\n const provider = await createHierarchicalProvider({\n contextRoot,\n registry,\n readonly,\n });\n\n // Create context API\n return createContext({\n provider,\n registry,\n schemas,\n defaultNamespace: undefined,\n });\n};\n"],"names":["discoverOvercontext","options","schemas","pluralNames","readonly","rootOptions","registry","createSchemaRegistry","type","schema","Object","entries","register","pluralName","contextRoot","discoverContextRoot","primary","Error","provider","createHierarchicalProvider","createContext","defaultNamespace","undefined"],"mappings":";;;;;;;;;AAsBA;;IAGO,MAAMA,mBAAAA,GAAsB,OAC/BC,OAAAA,GAAAA;IAEA,MAAM,EAAEC,OAAO,EAAEC,WAAAA,GAAc,EAAE,EAAEC,QAAQ,EAAE,GAAGC,WAAAA,EAAa,GAAGJ,OAAAA;;AAGhE,IAAA,MAAMK,QAAAA,GAAWC,oBAAAA,EAAAA;IACjB,KAAK,MAAM,CAACC,IAAAA,EAAMC,MAAAA,CAAO,IAAIC,MAAAA,CAAOC,OAAO,CAACT,OAAAA,CAAAA,CAAU;AAClDI,QAAAA,QAAAA,CAASM,QAAQ,CAAC;AACdJ,YAAAA,IAAAA;YACAC,MAAAA,EAAQA,MAAAA;YACRI,UAAAA,EAAaV,WAAsC,CAACK,IAAAA;AACxD,SAAA,CAAA;AACJ,IAAA;;IAGA,MAAMM,WAAAA,GAAc,MAAMC,mBAAAA,CAAoB;AAC1C,QAAA,GAAGV,WAAW;AACdC,QAAAA;AACJ,KAAA,CAAA;IAEA,IAAI,CAACQ,WAAAA,CAAYE,OAAO,EAAE;AACtB,QAAA,MAAM,IAAIC,KAAAA,CAAM,4BAAA,CAAA;AACpB,IAAA;;IAGA,MAAMC,QAAAA,GAAW,MAAMC,0BAAAA,CAA2B;AAC9CL,QAAAA,WAAAA;AACAR,QAAAA,QAAAA;AACAF,QAAAA;AACJ,KAAA,CAAA;;AAGA,IAAA,OAAOgB,aAAAA,CAAc;AACjBF,QAAAA,QAAAA;AACAZ,QAAAA,QAAAA;AACAJ,QAAAA,OAAAA;QACAmB,gBAAAA,EAAkBC;AACtB,KAAA,CAAA;AACJ;;;;"}
@@ -0,0 +1,36 @@
1
+ import { SchemaRegistry } from '../schema/registry';
2
+ export interface DiscoveredContextDir {
3
+ /** Absolute path to the context directory */
4
+ path: string;
5
+ /** Distance from starting point (0 = closest) */
6
+ level: number;
7
+ /** Namespaces found in this context dir */
8
+ namespaces: string[];
9
+ /** Entity types found (based on directory names) */
10
+ types: string[];
11
+ }
12
+ export interface WalkerOptions {
13
+ /** Directory to start searching from */
14
+ startDir: string;
15
+ /** Name of context directory to look for */
16
+ contextDirName: string;
17
+ /** Maximum levels to walk up */
18
+ maxLevels: number;
19
+ /** Stop walking when this directory is reached */
20
+ stopAt?: string;
21
+ /** Stop when finding a marker file (e.g., '.git', 'package.json') */
22
+ stopMarkers?: string[];
23
+ /** Schema registry to identify entity type directories */
24
+ registry?: SchemaRegistry;
25
+ }
26
+ export interface DirectoryWalker {
27
+ /**
28
+ * Find all context directories walking up from startDir.
29
+ */
30
+ discover(): Promise<DiscoveredContextDir[]>;
31
+ /**
32
+ * Check if a specific directory contains context.
33
+ */
34
+ hasContext(dir: string): Promise<boolean>;
35
+ }
36
+ export declare const createDirectoryWalker: (options: WalkerOptions) => DirectoryWalker;
@@ -0,0 +1,87 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import * as path from 'node:path';
4
+
5
+ const createDirectoryWalker = (options)=>{
6
+ const { startDir, contextDirName, maxLevels, stopAt, stopMarkers = [], registry } = options;
7
+ const shouldStop = async (dir)=>{
8
+ if (stopAt && dir === stopAt) return true;
9
+ for (const marker of stopMarkers){
10
+ if (existsSync(path.join(dir, marker))) {
11
+ return true;
12
+ }
13
+ }
14
+ const parent = path.dirname(dir);
15
+ return parent === dir; // Root
16
+ };
17
+ const getNamespacesAndTypes = async (contextDir)=>{
18
+ const namespaces = [];
19
+ const types = [];
20
+ try {
21
+ const entries = await fs.readdir(contextDir, {
22
+ withFileTypes: true
23
+ });
24
+ for (const entry of entries){
25
+ if (!entry.isDirectory()) continue;
26
+ const subPath = path.join(contextDir, entry.name);
27
+ const subEntries = await fs.readdir(subPath, {
28
+ withFileTypes: true
29
+ });
30
+ // Check if this is a type directory (has .yaml files)
31
+ const hasYamlFiles = subEntries.some((sub)=>sub.isFile() && (sub.name.endsWith('.yaml') || sub.name.endsWith('.yml')));
32
+ // Check if this looks like a type directory (known to registry)
33
+ const isTypeDir = registry ? !!registry.getTypeFromDirectory(entry.name) : hasYamlFiles;
34
+ if (isTypeDir) {
35
+ const typeName = (registry === null || registry === void 0 ? void 0 : registry.getTypeFromDirectory(entry.name)) || entry.name;
36
+ if (!types.includes(typeName)) {
37
+ types.push(typeName);
38
+ }
39
+ } else {
40
+ // Check if it's a namespace (contains type directories)
41
+ const hasTypeDirs = subEntries.some((sub)=>{
42
+ if (!sub.isDirectory()) return false;
43
+ return registry ? !!registry.getTypeFromDirectory(sub.name) : true; // Assume any subdir could be a type
44
+ });
45
+ if (hasTypeDirs) {
46
+ namespaces.push(entry.name);
47
+ }
48
+ }
49
+ }
50
+ } catch {
51
+ // Directory doesn't exist or can't be read
52
+ }
53
+ return {
54
+ namespaces,
55
+ types
56
+ };
57
+ };
58
+ return {
59
+ async discover () {
60
+ const discovered = [];
61
+ let currentDir = path.resolve(startDir);
62
+ let level = 0;
63
+ while(level < maxLevels){
64
+ const contextDir = path.join(currentDir, contextDirName);
65
+ if (existsSync(contextDir)) {
66
+ const { namespaces, types } = await getNamespacesAndTypes(contextDir);
67
+ discovered.push({
68
+ path: contextDir,
69
+ level,
70
+ namespaces,
71
+ types
72
+ });
73
+ }
74
+ if (await shouldStop(currentDir)) break;
75
+ currentDir = path.dirname(currentDir);
76
+ level++;
77
+ }
78
+ return discovered;
79
+ },
80
+ async hasContext (dir) {
81
+ return existsSync(path.join(dir, contextDirName));
82
+ }
83
+ };
84
+ };
85
+
86
+ export { createDirectoryWalker };
87
+ //# sourceMappingURL=walker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walker.js","sources":["../../src/discovery/walker.ts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { SchemaRegistry } from '../schema/registry';\n\nexport interface DiscoveredContextDir {\n /** Absolute path to the context directory */\n path: string;\n\n /** Distance from starting point (0 = closest) */\n level: number;\n\n /** Namespaces found in this context dir */\n namespaces: string[];\n\n /** Entity types found (based on directory names) */\n types: string[];\n}\n\nexport interface WalkerOptions {\n /** Directory to start searching from */\n startDir: string;\n\n /** Name of context directory to look for */\n contextDirName: string;\n\n /** Maximum levels to walk up */\n maxLevels: number;\n\n /** Stop walking when this directory is reached */\n stopAt?: string;\n\n /** Stop when finding a marker file (e.g., '.git', 'package.json') */\n stopMarkers?: string[];\n\n /** Schema registry to identify entity type directories */\n registry?: SchemaRegistry;\n}\n\nexport interface DirectoryWalker {\n /**\n * Find all context directories walking up from startDir.\n */\n discover(): Promise<DiscoveredContextDir[]>;\n\n /**\n * Check if a specific directory contains context.\n */\n hasContext(dir: string): Promise<boolean>;\n}\n\nexport const createDirectoryWalker = (options: WalkerOptions): DirectoryWalker => {\n const {\n startDir,\n contextDirName,\n maxLevels,\n stopAt,\n stopMarkers = [],\n registry,\n } = options;\n\n const shouldStop = async (dir: string): Promise<boolean> => {\n if (stopAt && dir === stopAt) return true;\n\n for (const marker of stopMarkers) {\n if (existsSync(path.join(dir, marker))) {\n return true;\n }\n }\n\n const parent = path.dirname(dir);\n return parent === dir; // Root\n };\n\n const getNamespacesAndTypes = async (\n contextDir: string\n ): Promise<{ namespaces: string[]; types: string[] }> => {\n const namespaces: string[] = [];\n const types: string[] = [];\n\n try {\n const entries = await fs.readdir(contextDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const subPath = path.join(contextDir, entry.name);\n const subEntries = await fs.readdir(subPath, { withFileTypes: true });\n\n // Check if this is a type directory (has .yaml files)\n const hasYamlFiles = subEntries.some(\n sub => sub.isFile() && (sub.name.endsWith('.yaml') || sub.name.endsWith('.yml'))\n );\n\n // Check if this looks like a type directory (known to registry)\n const isTypeDir = registry\n ? !!registry.getTypeFromDirectory(entry.name)\n : hasYamlFiles;\n\n if (isTypeDir) {\n const typeName = registry?.getTypeFromDirectory(entry.name) || entry.name;\n if (!types.includes(typeName)) {\n types.push(typeName);\n }\n } else {\n // Check if it's a namespace (contains type directories)\n const hasTypeDirs = subEntries.some(sub => {\n if (!sub.isDirectory()) return false;\n return registry\n ? !!registry.getTypeFromDirectory(sub.name)\n : true; // Assume any subdir could be a type\n });\n\n if (hasTypeDirs) {\n namespaces.push(entry.name);\n }\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n\n return { namespaces, types };\n };\n\n return {\n async discover(): Promise<DiscoveredContextDir[]> {\n const discovered: DiscoveredContextDir[] = [];\n let currentDir = path.resolve(startDir);\n let level = 0;\n\n while (level < maxLevels) {\n const contextDir = path.join(currentDir, contextDirName);\n\n if (existsSync(contextDir)) {\n const { namespaces, types } = await getNamespacesAndTypes(contextDir);\n discovered.push({ path: contextDir, level, namespaces, types });\n }\n\n if (await shouldStop(currentDir)) break;\n\n currentDir = path.dirname(currentDir);\n level++;\n }\n\n return discovered;\n },\n\n async hasContext(dir: string): Promise<boolean> {\n return existsSync(path.join(dir, contextDirName));\n },\n };\n};\n"],"names":["createDirectoryWalker","options","startDir","contextDirName","maxLevels","stopAt","stopMarkers","registry","shouldStop","dir","marker","existsSync","path","join","parent","dirname","getNamespacesAndTypes","contextDir","namespaces","types","entries","fs","readdir","withFileTypes","entry","isDirectory","subPath","name","subEntries","hasYamlFiles","some","sub","isFile","endsWith","isTypeDir","getTypeFromDirectory","typeName","includes","push","hasTypeDirs","discover","discovered","currentDir","resolve","level","hasContext"],"mappings":"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"}