stratifyjs 1.0.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +354 -0
  3. package/lib/adapters/config-file-loader.d.ts +8 -0
  4. package/lib/adapters/config-file-loader.js +55 -0
  5. package/lib/adapters/file-system-discovery.d.ts +22 -0
  6. package/lib/adapters/file-system-discovery.js +61 -0
  7. package/lib/api/api.d.ts +42 -0
  8. package/lib/api/api.js +75 -0
  9. package/lib/api/index.d.ts +15 -0
  10. package/lib/api/index.js +12 -0
  11. package/lib/cli/command-handler.d.ts +6 -0
  12. package/lib/cli/command-handler.js +50 -0
  13. package/lib/cli/index.d.ts +2 -0
  14. package/lib/cli/index.js +23 -0
  15. package/lib/cli/options.d.ts +18 -0
  16. package/lib/cli/options.js +21 -0
  17. package/lib/cli/output-helpers.d.ts +9 -0
  18. package/lib/cli/output-helpers.js +22 -0
  19. package/lib/core/config-defaults.d.ts +7 -0
  20. package/lib/core/config-defaults.js +22 -0
  21. package/lib/core/config-schema.d.ts +11 -0
  22. package/lib/core/config-schema.js +102 -0
  23. package/lib/core/errors.d.ts +44 -0
  24. package/lib/core/errors.js +25 -0
  25. package/lib/core/formatters/console-formatter.d.ts +3 -0
  26. package/lib/core/formatters/console-formatter.js +34 -0
  27. package/lib/core/formatters/json-formatter.d.ts +5 -0
  28. package/lib/core/formatters/json-formatter.js +11 -0
  29. package/lib/core/package-parser.d.ts +11 -0
  30. package/lib/core/package-parser.js +37 -0
  31. package/lib/core/report-builder.d.ts +18 -0
  32. package/lib/core/report-builder.js +17 -0
  33. package/lib/core/result.d.ts +33 -0
  34. package/lib/core/result.js +24 -0
  35. package/lib/core/rules.d.ts +14 -0
  36. package/lib/core/rules.js +19 -0
  37. package/lib/core/validation.d.ts +5 -0
  38. package/lib/core/validation.js +51 -0
  39. package/lib/types/types.d.ts +66 -0
  40. package/lib/types/types.js +1 -0
  41. package/package.json +67 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Scott Mikula
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # Stratify
2
+
3
+ Enforce architectural layer boundaries in monorepos. Catches invalid cross-layer dependencies at build time by analyzing `workspace:` protocol imports in `package.json` files.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install stratifyjs
9
+ # or
10
+ yarn add stratifyjs
11
+ ```
12
+
13
+ For CLI-only usage you can install globally:
14
+
15
+ ```bash
16
+ npm install -g stratifyjs
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ 1. Add a `"layer"` field to each workspace `package.json`:
22
+
23
+ ```json
24
+ {
25
+ "name": "my-feature",
26
+ "layer": "features",
27
+ "dependencies": {
28
+ "my-core-lib": "workspace:*"
29
+ }
30
+ }
31
+ ```
32
+
33
+ 2. Create a `stratify.config.json` at your workspace root:
34
+
35
+ ```json
36
+ {
37
+ "layers": {
38
+ "features": {
39
+ "description": "Feature packages",
40
+ "allowedDependencies": ["core", "shared"]
41
+ },
42
+ "core": {
43
+ "description": "Core business logic",
44
+ "allowedDependencies": ["shared"]
45
+ },
46
+ "shared": {
47
+ "description": "Shared utilities",
48
+ "allowedDependencies": []
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ 3. Run:
55
+
56
+ ```bash
57
+ stratify --config stratify.config.json
58
+ ```
59
+
60
+ ## CLI Usage
61
+
62
+ ```
63
+ stratify [options]
64
+ ```
65
+
66
+ | Option | Default | Description |
67
+ | --------------------- | ---------------------- | ---------------------------------------------------- |
68
+ | `-c, --config <path>` | `stratify.config.json` | Path to the layer config file (relative to root) |
69
+ | `-r, --root <path>` | `process.cwd()` | Workspace root directory |
70
+ | `-m, --mode <mode>` | Config value or `warn` | Override enforcement mode: `error`, `warn`, or `off` |
71
+ | `--format <type>` | `console` | Output format: `console` or `json` |
72
+ | `-V, --version` | | Print version |
73
+ | `-h, --help` | | Print help |
74
+
75
+ ### Examples
76
+
77
+ ```bash
78
+ # Basic check with defaults
79
+ stratify
80
+
81
+ # Explicit config and root
82
+ stratify --config stratify.config.json --root /path/to/monorepo
83
+
84
+ # Fail CI on violations
85
+ stratify --mode error
86
+
87
+ # Machine-readable output
88
+ stratify --format json
89
+
90
+ # Combine options
91
+ stratify -c stratify.config.json -r ../.. -m error --format console
92
+ ```
93
+
94
+ ### Exit Codes
95
+
96
+ | Code | Meaning |
97
+ | ---- | --------------------------------------------------------------- |
98
+ | `0` | No violations, or mode is `warn`/`off` |
99
+ | `1` | Violations found and mode is `error`, or a fatal error occurred |
100
+
101
+ ## Programmatic API
102
+
103
+ The library API is available for custom tooling, editor integrations, or CI pipelines.
104
+
105
+ ### `enforceLayersAsync(options?)`
106
+
107
+ Run the full enforcement pipeline: load config → discover packages → validate → report.
108
+
109
+ ```typescript
110
+ import { enforceLayersAsync, formatResults } from 'stratifyjs';
111
+
112
+ const result = await enforceLayersAsync({
113
+ workspaceRoot: '/path/to/monorepo',
114
+ configPath: 'stratify.config.json',
115
+ mode: 'error', // optional override
116
+ });
117
+
118
+ if (!result.success) {
119
+ console.error('Error:', result.error);
120
+ process.exit(1);
121
+ }
122
+
123
+ const { violations, packages, config, report, warnings } = result.value;
124
+ console.log(`Checked ${packages.length} packages, found ${violations.length} violations`);
125
+
126
+ // Format for display
127
+ const output = formatResults(result.value, 'console', 'warn');
128
+ console.log(output);
129
+ ```
130
+
131
+ ### `validateConfig(raw)`
132
+
133
+ Validate a raw config object without reading from disk. Useful for editor integrations.
134
+
135
+ ```typescript
136
+ import { validateConfig } from 'stratifyjs';
137
+
138
+ const result = validateConfig({
139
+ layers: {
140
+ core: { allowedDependencies: [] },
141
+ features: { allowedDependencies: ['core'] },
142
+ },
143
+ });
144
+
145
+ if (result.success) {
146
+ console.log('Valid config:', result.value);
147
+ } else {
148
+ console.error('Invalid:', result.error);
149
+ }
150
+ ```
151
+
152
+ ### `formatResults(result, format, mode)`
153
+
154
+ Format an `EnforceLayersResult` as a string for display.
155
+
156
+ ```typescript
157
+ import { formatResults } from 'stratifyjs';
158
+
159
+ const consoleOutput = formatResults(result.value, 'console', 'warn');
160
+ const jsonOutput = formatResults(result.value, 'json', 'error');
161
+ ```
162
+
163
+ ## Config File Format
164
+
165
+ The config file (default: `stratify.config.json`) is a JSON object with three sections:
166
+
167
+ ```json
168
+ {
169
+ "layers": { ... },
170
+ "enforcement": { ... },
171
+ "workspaces": { ... }
172
+ }
173
+ ```
174
+
175
+ ### `layers` (required)
176
+
177
+ A map of layer names to their definitions. Each layer must specify which other layers it is allowed to depend on.
178
+
179
+ ```json
180
+ {
181
+ "layers": {
182
+ "adapters": {
183
+ "description": "I/O and external integrations",
184
+ "allowedDependencies": ["core", "types"]
185
+ },
186
+ "core": {
187
+ "description": "Pure business logic",
188
+ "allowedDependencies": ["types"]
189
+ },
190
+ "types": {
191
+ "description": "Shared type definitions",
192
+ "allowedDependencies": []
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ | Field | Type | Required | Description |
199
+ | --------------------- | ---------- | -------- | -------------------------------------------------------------------- |
200
+ | `description` | `string` | No | Human-readable description of the layer's purpose |
201
+ | `allowedDependencies` | `string[]` | **Yes** | Layer names this layer may depend on. Use `"*"` to allow all layers. |
202
+
203
+ ### `enforcement` (optional)
204
+
205
+ Controls how violations are reported.
206
+
207
+ ```json
208
+ {
209
+ "enforcement": {
210
+ "mode": "error"
211
+ }
212
+ }
213
+ ```
214
+
215
+ | Field | Type | Default | Description |
216
+ | ------ | ---------------------------- | -------- | ------------------------------------------------------------------------- |
217
+ | `mode` | `"error" \| "warn" \| "off"` | `"warn"` | `error` = non-zero exit on violations; `warn` = report only; `off` = skip |
218
+
219
+ ### `workspaces` (optional)
220
+
221
+ Controls which packages are discovered for validation.
222
+
223
+ ```json
224
+ {
225
+ "workspaces": {
226
+ "patterns": ["packages/**/*", "shared/**/*"]
227
+ }
228
+ }
229
+ ```
230
+
231
+ | Field | Type | Default | Description |
232
+ | ---------- | ---------- | ------------------- | ------------------------------------------------------------------------------- |
233
+ | `patterns` | `string[]` | `["packages/**/*"]` | Glob patterns to locate workspace packages (each must contain a `package.json`) |
234
+
235
+ Ignored paths (hardcoded): `**/node_modules/**`, `**/lib/**`, `**/dist/**`.
236
+
237
+ ## Layer Definition Reference
238
+
239
+ ### Package Assignment
240
+
241
+ Each workspace package declares its layer via the `"layer"` field in `package.json`:
242
+
243
+ ```json
244
+ {
245
+ "name": "my-package",
246
+ "version": "1.0.0",
247
+ "layer": "core"
248
+ }
249
+ ```
250
+
251
+ Packages missing the `"layer"` field produce a `missing-layer` violation.
252
+
253
+ ### Dependency Detection
254
+
255
+ Only `workspace:` protocol dependencies are checked, across all three dependency fields:
256
+
257
+ - `dependencies`
258
+ - `devDependencies`
259
+ - `peerDependencies`
260
+
261
+ External (npm registry) dependencies are ignored — layers only govern internal monorepo boundaries.
262
+
263
+ ### Violation Types
264
+
265
+ | Type | Description |
266
+ | -------------------- | ------------------------------------------------------------------------------ |
267
+ | `missing-layer` | Package has no `"layer"` field in its `package.json` |
268
+ | `unknown-layer` | Package declares a layer not defined in the config |
269
+ | `invalid-dependency` | Package depends on another package whose layer is not in `allowedDependencies` |
270
+
271
+ ### Wildcard Dependencies
272
+
273
+ Use `"*"` to allow a layer to depend on any other layer:
274
+
275
+ ```json
276
+ {
277
+ "layers": {
278
+ "app": {
279
+ "description": "Application entry points — can use anything",
280
+ "allowedDependencies": ["*"]
281
+ }
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### Full Config Example
287
+
288
+ ```json
289
+ {
290
+ "layers": {
291
+ "app": {
292
+ "description": "Application entry points",
293
+ "allowedDependencies": ["features", "core", "shared"]
294
+ },
295
+ "features": {
296
+ "description": "Feature modules",
297
+ "allowedDependencies": ["core", "shared"]
298
+ },
299
+ "core": {
300
+ "description": "Core business logic",
301
+ "allowedDependencies": ["shared"]
302
+ },
303
+ "shared": {
304
+ "description": "Shared utilities and types",
305
+ "allowedDependencies": []
306
+ }
307
+ },
308
+ "enforcement": {
309
+ "mode": "error"
310
+ },
311
+ "workspaces": {
312
+ "patterns": ["packages/**/*", "libs/**/*"]
313
+ }
314
+ }
315
+ ```
316
+
317
+ ## CI Integration
318
+
319
+ Add to your CI pipeline to enforce boundaries on every PR:
320
+
321
+ ```yaml
322
+ # GitHub Actions
323
+ - name: Enforce layers
324
+ run: npx stratify --config stratify.config.json --mode error
325
+ ```
326
+
327
+ ```yaml
328
+ # Azure Pipelines
329
+ - script: npx stratify --config stratify.config.json --mode error
330
+ displayName: 'Enforce layer boundaries'
331
+ ```
332
+
333
+ ## Development
334
+
335
+ ```bash
336
+ # Install dependencies
337
+ yarn install
338
+
339
+ # Run tests
340
+ yarn test
341
+
342
+ # Run tests in watch mode
343
+ yarn test:watch
344
+
345
+ # Build (compile TypeScript → lib/)
346
+ yarn build
347
+
348
+ # Type-check without emitting
349
+ yarn typecheck
350
+ ```
351
+
352
+ ## License
353
+
354
+ MIT
@@ -0,0 +1,8 @@
1
+ import type { ResolvedConfig } from '../types/types.js';
2
+ import type { ConfigError } from '../core/errors.js';
3
+ import type { Result } from '../core/result.js';
4
+ /**
5
+ * Load a layer config file from disk, validate it, and apply defaults.
6
+ * I/O adapter — reads from the file system.
7
+ */
8
+ export declare function loadConfigFromFile(workspaceRoot: string, configPath: string): Promise<Result<ResolvedConfig, ConfigError>>;
@@ -0,0 +1,55 @@
1
+ import { readFile, access } from 'fs/promises';
2
+ import { resolve } from 'path';
3
+ import { ok, err } from '../core/result.js';
4
+ import { validateConfigSchema } from '../core/config-schema.js';
5
+ import { applyDefaults } from '../core/config-defaults.js';
6
+ /**
7
+ * Load a layer config file from disk, validate it, and apply defaults.
8
+ * I/O adapter — reads from the file system.
9
+ */
10
+ export async function loadConfigFromFile(workspaceRoot, configPath) {
11
+ const fullPath = resolve(workspaceRoot, configPath);
12
+ // Check file exists
13
+ try {
14
+ await access(fullPath);
15
+ }
16
+ catch {
17
+ return err({
18
+ type: 'config-not-found',
19
+ message: `Config file not found: ${fullPath}`,
20
+ path: fullPath,
21
+ });
22
+ }
23
+ // Read file
24
+ let content;
25
+ try {
26
+ content = await readFile(fullPath, 'utf-8');
27
+ }
28
+ catch (error) {
29
+ return err({
30
+ type: 'config-read-error',
31
+ message: error instanceof Error ? error.message : String(error),
32
+ path: fullPath,
33
+ cause: error,
34
+ });
35
+ }
36
+ // Parse JSON
37
+ let rawConfig;
38
+ try {
39
+ rawConfig = JSON.parse(content);
40
+ }
41
+ catch (error) {
42
+ return err({
43
+ type: 'config-parse-error',
44
+ message: error instanceof Error ? error.message : String(error),
45
+ path: fullPath,
46
+ cause: error,
47
+ });
48
+ }
49
+ // Validate schema
50
+ const validated = validateConfigSchema(rawConfig);
51
+ if (!validated.success) {
52
+ return validated;
53
+ }
54
+ return ok(applyDefaults(validated.value));
55
+ }
@@ -0,0 +1,22 @@
1
+ import type { Package, WorkspaceConfig } from '../types/types.js';
2
+ import type { DiscoveryError } from '../core/errors.js';
3
+ import type { Result } from '../core/result.js';
4
+ /**
5
+ * Non-fatal warning produced during discovery.
6
+ */
7
+ export interface DiscoveryWarning {
8
+ path: string;
9
+ message: string;
10
+ }
11
+ /**
12
+ * Successful discovery result containing packages and any non-fatal warnings.
13
+ */
14
+ export interface DiscoveryResult {
15
+ packages: Package[];
16
+ warnings: DiscoveryWarning[];
17
+ }
18
+ /**
19
+ * Discover all packages in the workspace by globbing for package.json files.
20
+ * I/O adapter — reads from the file system using glob + readFile.
21
+ */
22
+ export declare function discoverPackages(root: string, config: WorkspaceConfig, ignore?: string[]): Promise<Result<DiscoveryResult, DiscoveryError>>;
@@ -0,0 +1,61 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { resolve } from 'path';
3
+ import { glob } from 'glob';
4
+ import { ok, err } from '../core/result.js';
5
+ import { parsePackageJson } from '../core/package-parser.js';
6
+ const DEFAULT_IGNORE = ['**/node_modules/**', '**/lib/**', '**/dist/**'];
7
+ /**
8
+ * Discover all packages in the workspace by globbing for package.json files.
9
+ * I/O adapter — reads from the file system using glob + readFile.
10
+ */
11
+ export async function discoverPackages(root, config, ignore = DEFAULT_IGNORE) {
12
+ const allPaths = [];
13
+ for (const pattern of config.patterns) {
14
+ try {
15
+ const paths = await glob(`${pattern}/package.json`, {
16
+ cwd: root,
17
+ ignore,
18
+ });
19
+ allPaths.push(...paths);
20
+ }
21
+ catch (error) {
22
+ return err({
23
+ type: 'glob-failed',
24
+ message: error instanceof Error ? error.message : String(error),
25
+ pattern,
26
+ cause: error,
27
+ });
28
+ }
29
+ }
30
+ const uniquePaths = [...new Set(allPaths)];
31
+ const settledPackages = await Promise.allSettled(uniquePaths.map(async (relativePath) => {
32
+ const fullPath = resolve(root, relativePath);
33
+ const content = await readFile(fullPath, 'utf-8');
34
+ const parsed = JSON.parse(content);
35
+ return { relativePath, fullPath, parsed };
36
+ }));
37
+ const packages = [];
38
+ const warnings = [];
39
+ for (let i = 0; i < settledPackages.length; i++) {
40
+ const entry = settledPackages[i];
41
+ const relativePath = uniquePaths[i];
42
+ const fullPath = resolve(root, relativePath);
43
+ if (entry.status === 'rejected') {
44
+ const reason = entry.reason;
45
+ warnings.push({
46
+ path: fullPath,
47
+ message: reason instanceof Error ? reason.message : String(reason),
48
+ });
49
+ continue;
50
+ }
51
+ const { parsed } = entry.value;
52
+ const result = parsePackageJson(parsed, relativePath);
53
+ if (result.success) {
54
+ packages.push(result.value);
55
+ }
56
+ else {
57
+ warnings.push({ path: fullPath, message: result.error.message });
58
+ }
59
+ }
60
+ return ok({ packages, warnings });
61
+ }
@@ -0,0 +1,42 @@
1
+ import type { ResolvedConfig, Violation, Package, EnforcementConfig } from '../types/types.js';
2
+ import type { LayerError, ConfigError } from '../core/errors.js';
3
+ import type { Result } from '../core/result.js';
4
+ import { type DiscoveryWarning } from '../adapters/file-system-discovery.js';
5
+ import { type ValidationReport } from '../core/report-builder.js';
6
+ /**
7
+ * Options for the enforce-layers library API.
8
+ */
9
+ export interface EnforceLayersOptions {
10
+ /** Workspace root directory. Defaults to process.cwd(). */
11
+ workspaceRoot?: string;
12
+ /** Path to the config file, relative to workspaceRoot. */
13
+ configPath?: string;
14
+ /** Provide a pre-built config directly, skipping file loading. */
15
+ config?: ResolvedConfig;
16
+ /** Override the enforcement mode from config. */
17
+ mode?: EnforcementConfig['mode'];
18
+ }
19
+ /**
20
+ * Result of running layer enforcement.
21
+ */
22
+ export interface EnforceLayersResult {
23
+ violations: Violation[];
24
+ packages: Package[];
25
+ config: ResolvedConfig;
26
+ report: ValidationReport;
27
+ warnings: DiscoveryWarning[];
28
+ }
29
+ /**
30
+ * Run layer enforcement programmatically.
31
+ * Returns a Result
32
+ */
33
+ export declare function enforceLayersAsync(options?: EnforceLayersOptions): Promise<Result<EnforceLayersResult, LayerError>>;
34
+ /**
35
+ * Validate a raw config object without reading from a file.
36
+ * Useful for editor integrations or programmatic config construction.
37
+ */
38
+ export declare function validateConfig(raw: unknown): Result<ResolvedConfig, ConfigError>;
39
+ /**
40
+ * Format an enforcement result for display.
41
+ */
42
+ export declare function formatResults(result: EnforceLayersResult, format?: 'console' | 'json', mode?: EnforcementConfig['mode']): string;
package/lib/api/api.js ADDED
@@ -0,0 +1,75 @@
1
+ import { resolve } from 'path';
2
+ import { ok } from '../core/result.js';
3
+ import { validateConfigSchema } from '../core/config-schema.js';
4
+ import { applyDefaults } from '../core/config-defaults.js';
5
+ import { validatePackages } from '../core/validation.js';
6
+ import { loadConfigFromFile } from '../adapters/config-file-loader.js';
7
+ import { discoverPackages } from '../adapters/file-system-discovery.js';
8
+ import { buildReport } from '../core/report-builder.js';
9
+ import { formatConsole } from '../core/formatters/console-formatter.js';
10
+ import { formatJson } from '../core/formatters/json-formatter.js';
11
+ /**
12
+ * Run layer enforcement programmatically.
13
+ * Returns a Result
14
+ */
15
+ export async function enforceLayersAsync(options = {}) {
16
+ const startTime = performance.now();
17
+ const workspaceRoot = resolve(options.workspaceRoot ?? process.cwd());
18
+ // Resolve config
19
+ let config;
20
+ if (options.config) {
21
+ config = options.config;
22
+ }
23
+ else {
24
+ const configPath = options.configPath ?? 'stratify.config.json';
25
+ const configResult = await loadConfigFromFile(workspaceRoot, configPath);
26
+ if (!configResult.success) {
27
+ return configResult;
28
+ }
29
+ config = configResult.value;
30
+ }
31
+ // Apply mode override
32
+ if (options.mode) {
33
+ config = {
34
+ ...config,
35
+ enforcement: { ...config.enforcement, mode: options.mode },
36
+ };
37
+ }
38
+ // Discover packages
39
+ const discoveryResult = await discoverPackages(workspaceRoot, config.workspaces);
40
+ if (!discoveryResult.success) {
41
+ return discoveryResult;
42
+ }
43
+ const { packages, warnings } = discoveryResult.value;
44
+ // Validate packages against config
45
+ const violations = validatePackages(packages, config);
46
+ const duration = performance.now() - startTime;
47
+ const report = buildReport(violations, {
48
+ totalPackages: packages.length,
49
+ duration,
50
+ });
51
+ return ok({ violations, packages, config, report, warnings });
52
+ }
53
+ /**
54
+ * Validate a raw config object without reading from a file.
55
+ * Useful for editor integrations or programmatic config construction.
56
+ */
57
+ export function validateConfig(raw) {
58
+ const validated = validateConfigSchema(raw);
59
+ if (!validated.success) {
60
+ return validated;
61
+ }
62
+ return ok(applyDefaults(validated.value));
63
+ }
64
+ /**
65
+ * Format an enforcement result for display.
66
+ */
67
+ export function formatResults(result, format = 'console', mode = 'warn') {
68
+ switch (format) {
69
+ case 'json':
70
+ return formatJson(result.report);
71
+ case 'console':
72
+ default:
73
+ return formatConsole(result.report, mode);
74
+ }
75
+ }
@@ -0,0 +1,15 @@
1
+ export { enforceLayersAsync, validateConfig, formatResults } from './api.js';
2
+ export type { EnforceLayersOptions, EnforceLayersResult } from './api.js';
3
+ export type { Package, LayerConfig, LayerDefinition, LayerMap, ResolvedConfig, EnforcementConfig, WorkspaceConfig, Violation, ViolationType, } from '../types/types.js';
4
+ export type { LayerError, ConfigError, DiscoveryError } from '../core/errors.js';
5
+ export { formatLayerError } from '../core/errors.js';
6
+ export type { Result } from '../core/result.js';
7
+ export { ok, err, isOk, isErr } from '../core/result.js';
8
+ export type { ValidationReport } from '../core/report-builder.js';
9
+ export { buildReport } from '../core/report-builder.js';
10
+ export { validatePackages } from '../core/validation.js';
11
+ export { validateConfigSchema } from '../core/config-schema.js';
12
+ export { applyDefaults } from '../core/config-defaults.js';
13
+ export { parsePackageJson, extractWorkspaceDependencies } from '../core/package-parser.js';
14
+ export { formatConsole } from '../core/formatters/console-formatter.js';
15
+ export { formatJson } from '../core/formatters/json-formatter.js';
@@ -0,0 +1,12 @@
1
+ // Main API
2
+ export { enforceLayersAsync, validateConfig, formatResults } from './api.js';
3
+ export { formatLayerError } from '../core/errors.js';
4
+ export { ok, err, isOk, isErr } from '../core/result.js';
5
+ export { buildReport } from '../core/report-builder.js';
6
+ // Advanced: individual components for custom pipelines
7
+ export { validatePackages } from '../core/validation.js';
8
+ export { validateConfigSchema } from '../core/config-schema.js';
9
+ export { applyDefaults } from '../core/config-defaults.js';
10
+ export { parsePackageJson, extractWorkspaceDependencies } from '../core/package-parser.js';
11
+ export { formatConsole } from '../core/formatters/console-formatter.js';
12
+ export { formatJson } from '../core/formatters/json-formatter.js';
@@ -0,0 +1,6 @@
1
+ import type { CliOptions } from './options.js';
2
+ /**
3
+ * Handle the main enforce command.
4
+ * Returns an exit code: 0 = success, 1 = failure.
5
+ */
6
+ export declare function handleEnforceCommand(options: CliOptions): Promise<number>;