functional-examples 0.0.0-alpha.1 → 0.0.0-alpha.2

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 (38) hide show
  1. package/README.md +168 -26
  2. package/README.md.template +106 -0
  3. package/dist/cli/commands/scan.d.ts +1 -1
  4. package/dist/cli/commands/validate.d.ts +2 -2
  5. package/dist/cli/commands/validate.d.ts.map +1 -1
  6. package/dist/cli/commands/validate.js +92 -25
  7. package/dist/cli/commands/validate.js.map +1 -1
  8. package/dist/cli/index.d.ts +3 -3
  9. package/dist/config/resolver.d.ts +10 -10
  10. package/dist/config/resolver.d.ts.map +1 -1
  11. package/dist/config/resolver.js +156 -57
  12. package/dist/config/resolver.js.map +1 -1
  13. package/dist/config/types.d.ts +1 -1
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/plugins/pipeline.d.ts +4 -0
  16. package/dist/plugins/pipeline.d.ts.map +1 -1
  17. package/dist/plugins/pipeline.js +10 -1
  18. package/dist/plugins/pipeline.js.map +1 -1
  19. package/dist/plugins/registry.d.ts +1 -0
  20. package/dist/plugins/registry.d.ts.map +1 -1
  21. package/dist/plugins/registry.js +2 -3
  22. package/dist/plugins/registry.js.map +1 -1
  23. package/dist/scanner/scan.d.ts +3 -3
  24. package/dist/scanner/scan.d.ts.map +1 -1
  25. package/dist/scanner/scan.js.map +1 -1
  26. package/dist/scanner/scanner.d.ts.map +1 -1
  27. package/dist/scanner/scanner.js +5 -5
  28. package/dist/scanner/scanner.js.map +1 -1
  29. package/dist/schema/merger.d.ts.map +1 -1
  30. package/dist/schema/merger.js +43 -5
  31. package/dist/schema/merger.js.map +1 -1
  32. package/dist/schema/validator.d.ts.map +1 -1
  33. package/dist/schema/validator.js +7 -1
  34. package/dist/schema/validator.js.map +1 -1
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/dist/types/index.js.map +1 -1
  38. package/package.json +4 -3
package/README.md CHANGED
@@ -24,27 +24,33 @@ yarn add functional-examples
24
24
  ### Scanning for Examples
25
25
 
26
26
  ```typescript
27
- import { scanExamples } from 'functional-examples';
27
+ /**
28
+ * Basic example: Scanning for examples in a directory
29
+ */
30
+ import { resolveConfig, scanExamples } from 'functional-examples';
28
31
 
29
- const { examples, errors } = await scanExamples('./examples');
32
+ async function main() {
33
+ // Resolve config (auto-detects installed plugins)
34
+ const config = await resolveConfig({ root: './examples' });
30
35
 
31
- for (const example of examples) {
32
- console.log(example.metadata.title);
33
- console.log(example.files.map(f => f.path));
34
- }
35
- ```
36
+ // Scan for examples
37
+ const result = await scanExamples(config);
36
38
 
37
- ### Extracting Code Regions
39
+ console.log(`Found ${result.examples.length} examples:`);
40
+ for (const example of result.examples) {
41
+ console.log(` - ${example.title} (${example.id})`);
42
+ }
38
43
 
39
- ```typescript
40
- import { extractRegion, parseRegions } from 'functional-examples';
44
+ if (result.errors.length > 0) {
45
+ console.log(`\n${result.errors.length} errors occurred:`);
46
+ for (const error of result.errors) {
47
+ console.log(` - ${error.path}: ${error.message}`);
48
+ }
49
+ }
50
+ }
41
51
 
42
- // Extract a specific region
43
- const setupCode = extractRegion(code, 'setup', { extension: 'ts' });
52
+ main().catch(console.error);
44
53
 
45
- // Get all regions
46
- const regions = parseRegions(code, { extension: 'py' });
47
- console.log(regions['main']?.content);
48
54
  ```
49
55
 
50
56
  ## Example Formats
@@ -64,7 +70,6 @@ examples/
64
70
  id: my-example
65
71
  title: My Example
66
72
  description: Demonstrates something useful
67
- entryPoint: main.ts
68
73
  ```
69
74
 
70
75
  ### File-based (YAML frontmatter)
@@ -97,24 +102,161 @@ Supports 30+ languages with automatic comment syntax detection.
97
102
  ## Custom Extractors
98
103
 
99
104
  ```typescript
100
- import { ExtractorRegistry, MetadataExtractor } from 'functional-examples';
105
+ /**
106
+ * Custom TOML-based extractor example.
107
+ *
108
+ * This demonstrates how to create your own extractor that:
109
+ * 1. Scans for a specific file pattern (meta.toml)
110
+ * 2. Parses metadata from that file format
111
+ * 3. Claims files and returns Example objects
112
+ */
113
+ import {
114
+ type Example,
115
+ type Extractor,
116
+ type ExtractorResult,
117
+ } from 'functional-examples';
118
+ import { readdirSync, type Dirent } from 'node:fs';
119
+ import { readFile } from 'node:fs/promises';
120
+ import path, { join } from 'node:path';
121
+
122
+ /**
123
+ * Metadata structure for TOML examples.
124
+ * You can define any shape that fits your use case.
125
+ */
126
+ export interface TomlMetadata {
127
+ id: string;
128
+ title: string;
129
+ description?: string;
130
+ author?: string;
131
+ }
101
132
 
102
- class TomlExtractor implements MetadataExtractor {
103
- readonly name = 'toml';
133
+ /**
134
+ * Create a custom extractor that reads TOML metadata files.
135
+ *
136
+ * Extractors implement a candidate-based pattern: they're called with
137
+ * pre-filtered candidates (files and directories) and decide which to handle.
138
+ */
139
+ export function createTomlExtractor(): Extractor<TomlMetadata> {
140
+ return {
141
+ name: 'toml-extractor',
142
+
143
+ async extract(
144
+ candidates: Dirent[]
145
+ ): Promise<ExtractorResult<TomlMetadata>> {
146
+ const examples: Example<TomlMetadata>[] = [];
147
+ const claimedFiles = new Set<string>();
148
+ const errors: { path: string; message: string }[] = [];
149
+
150
+ // Find meta.toml files from candidates
151
+ const tomlFiles: string[] = [];
152
+
153
+ for (const candidate of candidates) {
154
+ const fullPath = path.join(candidate.parentPath, candidate.name);
155
+
156
+ if (candidate.isFile()) {
157
+ // Direct file candidate: check if it's a meta.toml
158
+ if (candidate.name === 'meta.toml') {
159
+ tomlFiles.push(fullPath);
160
+ }
161
+ } else if (candidate.isDirectory()) {
162
+ // Directory candidate: look for meta.toml inside
163
+ const metaPath = path.join(fullPath, 'meta.toml');
164
+ try {
165
+ await readFile(metaPath, 'utf-8');
166
+ tomlFiles.push(metaPath);
167
+ } catch {
168
+ // No meta.toml in this directory, skip
169
+ }
170
+ }
171
+ }
172
+
173
+ for (const tomlFile of tomlFiles) {
174
+ try {
175
+ const content = await readFile(tomlFile, 'utf-8');
176
+ const metadata = parseSimpleToml(content);
177
+
178
+ const exampleDir = path.dirname(tomlFile);
179
+
180
+ // Collect all files in the example directory
181
+ const files = collectExampleFiles(exampleDir);
182
+
183
+ // Claim all files
184
+ for (const file of files) {
185
+ claimedFiles.add(file);
186
+ }
187
+
188
+ examples.push({
189
+ id: metadata.id,
190
+ title: metadata.title,
191
+ description: metadata.description,
192
+ rootPath: exampleDir,
193
+ files: files.map((f) => ({
194
+ absolutePath: f,
195
+ relativePath: path.relative(exampleDir, f),
196
+ })),
197
+ metadata,
198
+ extractorName: 'toml-extractor',
199
+ });
200
+ } catch (err) {
201
+ errors.push({
202
+ path: tomlFile,
203
+ message: `Failed to parse: ${(err as Error).message}`,
204
+ });
205
+ }
206
+ }
207
+
208
+ return { examples, errors, claimedFiles };
209
+ },
210
+ };
211
+ }
104
212
 
105
- canExtract(context) {
106
- return context.entries?.includes('meta.toml') ?? false;
213
+ /**
214
+ * Simplified TOML parser for demonstration.
215
+ * In production, use a proper TOML library like @iarna/toml.
216
+ */
217
+ function parseSimpleToml(content: string): TomlMetadata {
218
+ const lines = content.split('\n');
219
+ const result: Record<string, string> = {};
220
+
221
+ for (const line of lines) {
222
+ // Match: key = "value"
223
+ const match = line.match(/^(\w+)\s*=\s*"(.*)"/);
224
+ if (match) {
225
+ result[match[1]] = match[2];
226
+ }
107
227
  }
108
228
 
109
- async extract(context) {
110
- // Custom extraction logic
229
+ if (!result['id'] || !result['title']) {
230
+ throw new Error('TOML must have id and title fields');
111
231
  }
232
+
233
+ return {
234
+ id: result['id'],
235
+ title: result['title'],
236
+ description: result['description'],
237
+ author: result['author'],
238
+ };
112
239
  }
113
240
 
114
- const registry = new ExtractorRegistry()
115
- .register(new TomlExtractor());
241
+ function collectExampleFiles(root: string) {
242
+ if (root.endsWith('node_modules')) {
243
+ return [];
244
+ }
245
+
246
+ let files: string[] = [];
247
+ const entries = readdirSync(root, { withFileTypes: true });
248
+ for (const entry of entries) {
249
+ if (entry.isDirectory()) {
250
+ files = files.concat(
251
+ collectExampleFiles(join(entry.parentPath, entry.name))
252
+ );
253
+ } else {
254
+ files.push(join(entry.parentPath, entry.name));
255
+ }
256
+ }
257
+ return files;
258
+ }
116
259
 
117
- const scanner = new ExampleScanner({ extractors: registry });
118
260
  ```
119
261
 
120
262
  ## API Reference
@@ -0,0 +1,106 @@
1
+ # functional-examples
2
+
3
+ A language-agnostic library for treating code examples as first-class citizens.
4
+
5
+ ## Features
6
+
7
+ - **Pluggable metadata extraction**: YAML frontmatter and meta.yml built-in
8
+ - **Language-aware region parsing**: Extracts code regions with comment-style detection
9
+ - **Flexible scanning**: Directory-based and file-based example discovery
10
+ - **Type-safe API**: Full TypeScript support with generic metadata types
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install functional-examples
16
+ # or
17
+ pnpm add functional-examples
18
+ # or
19
+ yarn add functional-examples
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Scanning for Examples
25
+
26
+ <%= example('basic-usage').file('scan.ts') %>
27
+
28
+ ## Example Formats
29
+
30
+ ### Directory-based (meta.yml)
31
+
32
+ ```
33
+ examples/
34
+ my-example/
35
+ meta.yml
36
+ main.ts
37
+ helper.ts
38
+ ```
39
+
40
+ **meta.yml:**
41
+ ```yaml
42
+ id: my-example
43
+ title: My Example
44
+ description: Demonstrates something useful
45
+ ```
46
+
47
+ ### File-based (YAML frontmatter)
48
+
49
+ ```typescript
50
+ // ---
51
+ // title: My Example
52
+ // description: A single-file example
53
+ // ---
54
+
55
+ console.log('Hello, world!');
56
+ ```
57
+
58
+ ## Region Markers
59
+
60
+ Mark regions in your code for extraction:
61
+
62
+ ```typescript
63
+ // #region setup
64
+ const db = createDatabase();
65
+ // #endregion setup
66
+
67
+ // #region main
68
+ await db.query('SELECT * FROM users');
69
+ // #endregion main
70
+ ```
71
+
72
+ Supports 30+ languages with automatic comment syntax detection.
73
+
74
+ ## Custom Extractors
75
+
76
+ <%= example('custom-extractor').file('toml-extractor.ts') %>
77
+
78
+ ## API Reference
79
+
80
+ ### Scanner
81
+
82
+ - `scanExamples(directory, options?)` - Scan a directory for examples
83
+ - `ExampleScanner` - Class for customized scanning
84
+
85
+ ### Extractors
86
+
87
+ - `createDefaultRegistry()` - Create registry with built-in extractors
88
+ - `YamlFrontmatterExtractor` - Single-file YAML frontmatter
89
+ - `MetaYmlExtractor` - Directory-based meta.yml
90
+
91
+ ### Regions
92
+
93
+ - `parseRegions(code, options?)` - Parse all regions
94
+ - `extractRegion(code, regionId, options?)` - Extract single region
95
+ - `stripRegionMarkers(code, options?)` - Remove all markers
96
+ - `listRegions(code, options?)` - List region IDs
97
+ - `LANGUAGE_CONFIGS` - Language comment syntax mappings
98
+
99
+ ### File Helpers
100
+
101
+ - `readExampleFile(path, options?)` - Read file with optional region
102
+ - `readExampleFiles(directory, files)` - Read multiple files
103
+
104
+ ## License
105
+
106
+ MIT
@@ -9,7 +9,7 @@ export declare const scanCommand: import("cli-forge").CLI<{
9
9
  } & {} & {
10
10
  config?: string;
11
11
  } & {} & {
12
- format?: "yaml" | "json" | "table";
12
+ format?: "json" | "table" | "yaml";
13
13
  } & {} & {
14
14
  output?: string;
15
15
  } & {} & {
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Validate command - validate configuration file
2
+ * Validate command - validate configuration and example metadata
3
3
  */
4
4
  export declare const validateCommand: import("cli-forge").CLI<{
5
5
  unmatched: string[];
6
6
  '--'?: string[];
7
7
  } & {
8
8
  strict?: boolean;
9
- } & {}, Promise<void>, {}, undefined>;
9
+ } & {}, Promise<any>, {}, undefined>;
10
10
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,eAAe;;;;;qCAkE1B,CAAC"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,eAAO,MAAM,eAAe;;;;;oCAsJ1B,CAAC"}
@@ -1,11 +1,14 @@
1
1
  /**
2
- * Validate command - validate configuration file
2
+ * Validate command - validate configuration and example metadata
3
3
  */
4
4
  import { cli } from 'cli-forge';
5
5
  import { resolveConfig } from '../../config/resolver.js';
6
6
  import { validateConfig } from '../../config/validator.js';
7
+ import { scanExamples } from '../../scanner/index.js';
8
+ import { mergeMetadataSchemas } from '../../schema/merger.js';
9
+ import { createSchemaValidator } from '../../schema/validator.js';
7
10
  export const validateCommand = cli('validate', {
8
- description: 'Validate the configuration file',
11
+ description: 'Validate configuration and example metadata',
9
12
  builder: (cmd) => cmd.option('strict', {
10
13
  type: 'boolean',
11
14
  description: 'Treat warnings as errors',
@@ -22,44 +25,108 @@ export const validateCommand = cli('validate', {
22
25
  }
23
26
  console.log(`Validating: ${configPath}`);
24
27
  console.log('');
25
- const errors = validateConfig(config);
26
- if (errors.length > 0) {
28
+ // Phase 1: Validate config structure
29
+ const configErrors = validateConfig(config);
30
+ if (configErrors.length > 0) {
27
31
  console.error('Configuration errors:');
28
- for (const error of errors) {
32
+ for (const error of configErrors) {
29
33
  console.error(` - ${error.path}: ${error.message}`);
30
34
  }
31
35
  process.exit(1);
32
36
  }
37
+ let resolved;
33
38
  try {
34
- const resolved = await resolveConfig(config);
35
- console.log('Configuration is valid!');
39
+ resolved = await resolveConfig(config);
40
+ }
41
+ catch (error) {
42
+ console.error('Failed to resolve configuration:');
43
+ console.error(` ${error.message}`);
44
+ return void process.exit(1);
45
+ }
46
+ console.log('Configuration is valid!');
47
+ console.log('');
48
+ console.log(`Extractors: ${resolved.extractors.length}`);
49
+ for (const extractor of resolved.extractors) {
50
+ console.log(` - ${extractor.name}`);
51
+ }
52
+ if (resolved.pathMappings.length > 0) {
36
53
  console.log('');
37
- console.log(`Extractors: ${resolved.extractors.length}`);
38
- for (const extractor of resolved.extractors) {
39
- console.log(` - ${extractor.name}`);
54
+ console.log(`Path mappings: ${resolved.pathMappings.length}`);
55
+ for (const mapping of resolved.pathMappings) {
56
+ console.log(` - ${mapping.pattern} -> ${mapping.extractor}`);
40
57
  }
41
- if (resolved.pathMappings.length > 0) {
42
- console.log('');
43
- console.log(`Path mappings: ${resolved.pathMappings.length}`);
44
- for (const mapping of resolved.pathMappings) {
45
- console.log(` - ${mapping.pattern} -> ${mapping.extractor}`);
46
- }
47
- }
48
- console.log('');
49
- console.log('Scan config:');
50
- console.log(` include: ${resolved.scan.include.join(', ') || '(all)'}`);
51
- console.log(` exclude: ${resolved.scan.exclude.join(', ') || '(none)'}`);
52
- if (resolved.extractors.length === 0 && options.strict) {
58
+ }
59
+ console.log('');
60
+ console.log('Scan config:');
61
+ console.log(` include: ${resolved.scan.include.join(', ') || '(all)'}`);
62
+ console.log(` exclude: ${resolved.scan.exclude.join(', ') || '(none)'}`);
63
+ if (resolved.extractors.length === 0) {
64
+ if (options.strict) {
53
65
  console.error('');
54
66
  console.error('Warning: No extractors could be loaded.');
55
67
  process.exit(1);
56
68
  }
69
+ return;
57
70
  }
58
- catch (error) {
59
- console.error('Failed to resolve configuration:');
60
- console.error(` ${error.message}`);
71
+ // Phase 2: Scan examples and validate metadata
72
+ console.log('');
73
+ console.log('Scanning examples...');
74
+ const scanResult = await scanExamples(resolved);
75
+ console.log(`Found ${scanResult.examples.length} example(s) in ${scanResult.stats.durationMs}ms`);
76
+ if (scanResult.examples.length === 0) {
77
+ if (options.strict) {
78
+ console.error('');
79
+ console.error('Warning: No examples found.');
80
+ process.exit(1);
81
+ }
82
+ return;
83
+ }
84
+ // Phase 3: Validate metadata against the combined schema
85
+ const pluginSchemas = resolved.registry.getSchemas();
86
+ const mergedSchema = mergeMetadataSchemas({
87
+ configSchema: resolved.metadata,
88
+ pluginSchemas,
89
+ });
90
+ delete mergedSchema['$schema'];
91
+ const validateSchema = createSchemaValidator(mergedSchema);
92
+ let metadataErrorCount = 0;
93
+ const exampleErrors = [];
94
+ for (const example of scanResult.examples) {
95
+ const result = validateSchema(example.metadata);
96
+ if (!result.success) {
97
+ const messages = result.errors.map((e) => `${e.path || '(root)'}: ${e.message}`);
98
+ exampleErrors.push({ displayPath: example.displayPath, messages });
99
+ metadataErrorCount += messages.length;
100
+ }
101
+ }
102
+ // Report scan-level errors (extractor failures, conflicts, etc.)
103
+ if (scanResult.errors.length > 0) {
104
+ console.error('');
105
+ console.error(`Scan errors (${scanResult.errors.length}):`);
106
+ for (const error of scanResult.errors) {
107
+ console.error(` ${error.path}:`);
108
+ console.error(` ${error.message}`);
109
+ }
110
+ }
111
+ // Report metadata validation errors
112
+ if (exampleErrors.length > 0) {
113
+ console.error('');
114
+ console.error(`Metadata validation errors (${metadataErrorCount} across ${exampleErrors.length} example(s)):`);
115
+ for (const { displayPath, messages } of exampleErrors) {
116
+ console.error(` ${displayPath}:`);
117
+ for (const msg of messages) {
118
+ console.error(` - ${msg}`);
119
+ }
120
+ }
121
+ }
122
+ const totalErrors = scanResult.errors.length + metadataErrorCount;
123
+ if (totalErrors > 0) {
124
+ console.error('');
125
+ console.error(`Validation failed with ${totalErrors} error(s).`);
61
126
  process.exit(1);
62
127
  }
128
+ console.log('');
129
+ console.log('All examples pass metadata validation!');
63
130
  },
64
131
  });
65
132
  //# sourceMappingURL=validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/cli/commands/validate.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@ declare const app: import("cli-forge").CLI<{
17
17
  } & {} & {
18
18
  config?: string;
19
19
  } & {} & {
20
- format?: "yaml" | "json" | "table";
20
+ format?: "table" | "json" | "yaml";
21
21
  } & {} & {
22
22
  output?: string;
23
23
  } & {} & {
@@ -39,7 +39,7 @@ declare const app: import("cli-forge").CLI<{
39
39
  '--'?: string[];
40
40
  } & {
41
41
  strict?: boolean;
42
- } & {}, Promise<void>, {}, undefined>, import("cli-forge").CLI<{
42
+ } & {}, Promise<any>, {}, undefined>, import("cli-forge").CLI<{
43
43
  unmatched: string[];
44
44
  '--'?: string[];
45
45
  } & {
@@ -53,7 +53,7 @@ declare const app: import("cli-forge").CLI<{
53
53
  unmatched: string[];
54
54
  '--'?: string[];
55
55
  } & {
56
- format?: "ts" | "json";
56
+ format?: "json" | "ts";
57
57
  } & {} & {
58
58
  output?: string;
59
59
  } & {} & {
@@ -1,27 +1,27 @@
1
1
  /**
2
- * Configuration resolver - converts config references to actual extractors
2
+ * Configuration resolver - converts config references to actual plugins/extractors
3
3
  */
4
4
  import type { ResolvedConfig } from '../types/index.js';
5
5
  import type { ConfigWithRoot } from './types.js';
6
6
  export type { ConfigValidationError, ResolvedConfig } from '../types/index.js';
7
7
  /**
8
- * Resolve a configuration to actual extractor instances.
8
+ * Resolve a configuration to actual plugin and extractor instances.
9
9
  *
10
- * If no extractors are specified, attempts to auto-detect installed extractors.
10
+ * Plugin resolution follows this priority:
11
+ * 1. If `config.plugins` contains Plugin instances (from TS configs), use directly
12
+ * 2. If `config.plugins` contains string/tuple references (from JSON configs),
13
+ * resolve them by dynamically importing the package and calling its factory
14
+ * 3. If no plugins specified at all, auto-detect installed plugin packages
11
15
  *
12
16
  * @example
13
17
  * ```typescript
14
18
  * import { resolveConfig } from 'functional-examples';
15
19
  *
16
- * // Auto-detect extractors
17
- * const config = await resolveConfig({});
20
+ * // Auto-detect plugins from installed packages
21
+ * const config = await resolveConfig({ root: './examples' });
18
22
  *
19
23
  * // Use with scanExamples
20
- * const result = await scanExamples({
21
- * root: './examples',
22
- * extractors: config.extractors,
23
- * pathMappings: config.pathMappings,
24
- * });
24
+ * const result = await scanExamples(config);
25
25
  * ```
26
26
  */
27
27
  export declare function resolveConfig<TMetadata = Record<string, unknown>>(config: ConfigWithRoot<TMetadata>): Promise<ResolvedConfig<TMetadata>>;
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/config/resolver.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAIV,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAkB/E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,GAChC,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CA0EpC"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/config/resolver.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAKV,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAmB/E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,GAChC,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CA0EpC"}