delimit-cli 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -37
- package/bin/delimit-cli.js +185 -25
- package/lib/api-engine.js +39 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# delimit-cli
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Prevent breaking API changes before they reach production.**
|
|
4
|
+
|
|
5
|
+
Deterministic diff engine + policy enforcement + semver classification for OpenAPI specs. The independent successor to Optic.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/delimit-cli)
|
|
6
8
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -11,46 +13,76 @@
|
|
|
11
13
|
npm install -g delimit-cli
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## Quick Start
|
|
16
|
+
## Quick Start (Under 5 Minutes)
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
# Initialize a policy
|
|
20
|
-
delimit init
|
|
19
|
+
# 1. Initialize with a policy preset
|
|
20
|
+
delimit init --preset default
|
|
21
21
|
|
|
22
|
-
# Detect breaking changes
|
|
22
|
+
# 2. Detect breaking changes
|
|
23
23
|
delimit lint api/openapi-old.yaml api/openapi-new.yaml
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Generate a human-readable explanation
|
|
29
|
-
delimit explain api/openapi-old.yaml api/openapi-new.yaml
|
|
25
|
+
# 3. Add the GitHub Action for automated PR checks
|
|
26
|
+
# Copy .github/workflows/api-governance.yml (see CI section below)
|
|
30
27
|
```
|
|
31
28
|
|
|
29
|
+
## What It Catches
|
|
30
|
+
|
|
31
|
+
Delimit deterministically detects 23 types of API changes, including 10 breaking patterns:
|
|
32
|
+
|
|
33
|
+
- Endpoint or method removal
|
|
34
|
+
- Required parameter addition
|
|
35
|
+
- Response field removal
|
|
36
|
+
- Type changes
|
|
37
|
+
- Enum value removal
|
|
38
|
+
- And more
|
|
39
|
+
|
|
40
|
+
Every change is classified as `MAJOR`, `MINOR`, `PATCH`, or `NONE` per semver.
|
|
41
|
+
|
|
32
42
|
## Commands
|
|
33
43
|
|
|
34
44
|
| Command | Description |
|
|
35
45
|
|---------|-------------|
|
|
36
|
-
| `delimit init` | Create `.delimit/policies.yml` with
|
|
37
|
-
| `delimit lint <old> <new>` | Diff + policy check
|
|
46
|
+
| `delimit init` | Create `.delimit/policies.yml` with a policy preset |
|
|
47
|
+
| `delimit lint <old> <new>` | Diff + policy check — returns exit code 1 on violations |
|
|
38
48
|
| `delimit diff <old> <new>` | Raw diff with `[BREAKING]`/`[safe]` tags |
|
|
39
49
|
| `delimit explain <old> <new>` | Human-readable change explanation |
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
## Policy Presets
|
|
52
|
+
|
|
53
|
+
Choose a preset that fits your team:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
delimit init --preset strict # Public APIs, payments — zero tolerance
|
|
57
|
+
delimit init --preset default # Most teams — balanced rules
|
|
58
|
+
delimit init --preset relaxed # Internal APIs, startups — warnings only
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Preset | Breaking changes | Type changes | Field removal |
|
|
62
|
+
|--------|-----------------|--------------|---------------|
|
|
63
|
+
| `strict` | Error (blocks) | Error (blocks) | Error (blocks) |
|
|
64
|
+
| `default` | Error (blocks) | Warning | Error (blocks) |
|
|
65
|
+
| `relaxed` | Warning | Warning | Info |
|
|
66
|
+
|
|
67
|
+
Pass a preset directly to lint:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
delimit lint --policy strict old.yaml new.yaml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Options
|
|
42
74
|
|
|
43
75
|
```bash
|
|
44
|
-
#
|
|
76
|
+
# Semver classification with version bump
|
|
77
|
+
delimit lint old.yaml new.yaml --current-version 1.0.0
|
|
78
|
+
|
|
79
|
+
# Explainer templates
|
|
45
80
|
delimit explain old.yaml new.yaml -t migration
|
|
46
81
|
delimit explain old.yaml new.yaml -t pr_comment
|
|
47
82
|
delimit explain old.yaml new.yaml -t changelog
|
|
48
83
|
|
|
49
|
-
#
|
|
50
|
-
delimit lint old.yaml new.yaml --
|
|
51
|
-
|
|
52
|
-
# Use custom policy file
|
|
53
|
-
delimit lint old.yaml new.yaml -p .delimit/policies.yml
|
|
84
|
+
# JSON output for scripting
|
|
85
|
+
delimit lint old.yaml new.yaml --json
|
|
54
86
|
```
|
|
55
87
|
|
|
56
88
|
### Explainer Templates
|
|
@@ -67,34 +99,56 @@ delimit lint old.yaml new.yaml -p .delimit/policies.yml
|
|
|
67
99
|
|
|
68
100
|
## CI/CD Integration
|
|
69
101
|
|
|
70
|
-
|
|
102
|
+
Add this workflow to `.github/workflows/api-governance.yml`:
|
|
71
103
|
|
|
72
104
|
```yaml
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
name: API Governance
|
|
106
|
+
on:
|
|
107
|
+
pull_request:
|
|
108
|
+
paths:
|
|
109
|
+
- 'path/to/openapi.yaml' # adjust to your spec path
|
|
110
|
+
permissions:
|
|
111
|
+
contents: read
|
|
112
|
+
pull-requests: write
|
|
113
|
+
jobs:
|
|
114
|
+
api-governance:
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
steps:
|
|
117
|
+
- uses: actions/checkout@v4
|
|
118
|
+
- uses: actions/checkout@v4
|
|
119
|
+
with:
|
|
120
|
+
ref: ${{ github.event.pull_request.base.sha }}
|
|
121
|
+
path: _base
|
|
122
|
+
- uses: delimit-ai/delimit@v1
|
|
123
|
+
with:
|
|
124
|
+
old_spec: _base/path/to/openapi.yaml
|
|
125
|
+
new_spec: path/to/openapi.yaml
|
|
126
|
+
mode: advisory # or 'enforce' to block PRs
|
|
77
127
|
```
|
|
78
128
|
|
|
129
|
+
The action posts a PR comment with:
|
|
130
|
+
- Semver badge (`MAJOR` / `MINOR` / `PATCH`)
|
|
131
|
+
- Violation table with severity
|
|
132
|
+
- Expandable migration guide for breaking changes
|
|
133
|
+
|
|
79
134
|
See [Delimit API Governance](https://github.com/marketplace/actions/delimit-api-governance) on the GitHub Marketplace.
|
|
80
135
|
|
|
81
136
|
## Custom Policies
|
|
82
137
|
|
|
83
|
-
Create `.delimit/policies.yml
|
|
138
|
+
Create `.delimit/policies.yml` or start from a preset:
|
|
84
139
|
|
|
85
140
|
```yaml
|
|
141
|
+
override_defaults: false
|
|
142
|
+
|
|
86
143
|
rules:
|
|
87
|
-
- id:
|
|
88
|
-
|
|
144
|
+
- id: protect_v1
|
|
145
|
+
name: Protect V1 API
|
|
146
|
+
change_types: [endpoint_removed, method_removed, field_removed]
|
|
89
147
|
severity: error
|
|
90
148
|
action: forbid
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
change_types: [type_changed]
|
|
95
|
-
severity: warning
|
|
96
|
-
action: warn
|
|
97
|
-
message: "Type change may break clients"
|
|
149
|
+
conditions:
|
|
150
|
+
path_pattern: "^/v1/.*"
|
|
151
|
+
message: "V1 API is frozen. Make changes in V2."
|
|
98
152
|
```
|
|
99
153
|
|
|
100
154
|
## Supported Specs
|
|
@@ -105,7 +159,7 @@ rules:
|
|
|
105
159
|
|
|
106
160
|
## Links
|
|
107
161
|
|
|
108
|
-
- [GitHub Action](https://github.com/marketplace/actions/delimit-api-governance) —
|
|
162
|
+
- [GitHub Action](https://github.com/marketplace/actions/delimit-api-governance) — Automated PR checks
|
|
109
163
|
- [GitHub](https://github.com/delimit-ai/delimit) — Source code
|
|
110
164
|
- [Issues](https://github.com/delimit-ai/delimit/issues) — Bug reports and feature requests
|
|
111
165
|
|
package/bin/delimit-cli.js
CHANGED
|
@@ -741,29 +741,72 @@ program
|
|
|
741
741
|
|
|
742
742
|
const apiEngine = require('../lib/api-engine');
|
|
743
743
|
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const configDir = path.join(process.cwd(), '.delimit');
|
|
750
|
-
const policyFile = path.join(configDir, 'policies.yml');
|
|
744
|
+
// Policy preset templates
|
|
745
|
+
const POLICY_PRESETS = {
|
|
746
|
+
strict: `# Delimit Policy Preset: strict
|
|
747
|
+
# For public APIs, payment systems, and regulated environments.
|
|
748
|
+
# Zero tolerance for breaking changes.
|
|
751
749
|
|
|
752
|
-
|
|
753
|
-
console.log(chalk.yellow('Already initialized — .delimit/policies.yml exists'));
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
750
|
+
override_defaults: true
|
|
756
751
|
|
|
757
|
-
|
|
752
|
+
rules:
|
|
753
|
+
- id: no_endpoint_removal
|
|
754
|
+
name: Forbid Endpoint Removal
|
|
755
|
+
change_types: [endpoint_removed]
|
|
756
|
+
severity: error
|
|
757
|
+
action: forbid
|
|
758
|
+
message: "Endpoint {path} cannot be removed. Deprecate with Sunset header first."
|
|
759
|
+
|
|
760
|
+
- id: no_method_removal
|
|
761
|
+
name: Forbid Method Removal
|
|
762
|
+
change_types: [method_removed]
|
|
763
|
+
severity: error
|
|
764
|
+
action: forbid
|
|
765
|
+
message: "HTTP method removed from {path}. This breaks all clients."
|
|
766
|
+
|
|
767
|
+
- id: no_required_param_addition
|
|
768
|
+
name: Forbid Required Parameter Addition
|
|
769
|
+
change_types: [required_param_added]
|
|
770
|
+
severity: error
|
|
771
|
+
action: forbid
|
|
772
|
+
message: "Cannot add required parameter to {path}. Make it optional."
|
|
773
|
+
|
|
774
|
+
- id: no_field_removal
|
|
775
|
+
name: Forbid Response Field Removal
|
|
776
|
+
change_types: [field_removed]
|
|
777
|
+
severity: error
|
|
778
|
+
action: forbid
|
|
779
|
+
message: "Cannot remove field from {path}. Deprecate it first."
|
|
758
780
|
|
|
759
|
-
|
|
760
|
-
|
|
781
|
+
- id: no_type_change
|
|
782
|
+
name: Forbid Type Changes
|
|
783
|
+
change_types: [type_changed]
|
|
784
|
+
severity: error
|
|
785
|
+
action: forbid
|
|
786
|
+
message: "Type change at {path} breaks client deserialization."
|
|
787
|
+
|
|
788
|
+
- id: no_enum_removal
|
|
789
|
+
name: Forbid Enum Value Removal
|
|
790
|
+
change_types: [enum_value_removed]
|
|
791
|
+
severity: error
|
|
792
|
+
action: forbid
|
|
793
|
+
message: "Enum value removed at {path}."
|
|
794
|
+
|
|
795
|
+
- id: no_param_removal
|
|
796
|
+
name: Forbid Parameter Removal
|
|
797
|
+
change_types: [param_removed]
|
|
798
|
+
severity: error
|
|
799
|
+
action: forbid
|
|
800
|
+
message: "Parameter removed from {path}."
|
|
801
|
+
`,
|
|
802
|
+
default: `# Delimit Policy Preset: default
|
|
803
|
+
# Balanced rules for most teams. Blocks destructive changes, warns on risky ones.
|
|
804
|
+
# Uses built-in defaults — customize by adding rules below.
|
|
761
805
|
|
|
762
|
-
# Override built-in rules (default: false)
|
|
763
806
|
override_defaults: false
|
|
764
807
|
|
|
765
808
|
rules: []
|
|
766
|
-
# Example:
|
|
809
|
+
# Add custom rules here. Example:
|
|
767
810
|
# - id: protect_v1
|
|
768
811
|
# name: Protect V1 API
|
|
769
812
|
# change_types: [endpoint_removed, method_removed, field_removed]
|
|
@@ -772,9 +815,80 @@ rules: []
|
|
|
772
815
|
# conditions:
|
|
773
816
|
# path_pattern: "^/v1/.*"
|
|
774
817
|
# message: "V1 API is frozen. Make changes in V2."
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
818
|
+
`,
|
|
819
|
+
relaxed: `# Delimit Policy Preset: relaxed
|
|
820
|
+
# For internal APIs, early-stage startups, and rapid iteration.
|
|
821
|
+
# Only warns — never blocks CI.
|
|
822
|
+
|
|
823
|
+
override_defaults: true
|
|
824
|
+
|
|
825
|
+
rules:
|
|
826
|
+
- id: warn_endpoint_removal
|
|
827
|
+
name: Warn on Endpoint Removal
|
|
828
|
+
change_types: [endpoint_removed]
|
|
829
|
+
severity: warning
|
|
830
|
+
action: warn
|
|
831
|
+
message: "Endpoint {path} was removed. Check downstream consumers."
|
|
832
|
+
|
|
833
|
+
- id: warn_method_removal
|
|
834
|
+
name: Warn on Method Removal
|
|
835
|
+
change_types: [method_removed]
|
|
836
|
+
severity: warning
|
|
837
|
+
action: warn
|
|
838
|
+
message: "HTTP method removed from {path}."
|
|
839
|
+
|
|
840
|
+
- id: warn_required_param
|
|
841
|
+
name: Warn on Required Parameter Addition
|
|
842
|
+
change_types: [required_param_added]
|
|
843
|
+
severity: warning
|
|
844
|
+
action: warn
|
|
845
|
+
message: "New required parameter at {path}."
|
|
846
|
+
|
|
847
|
+
- id: warn_type_change
|
|
848
|
+
name: Warn on Type Changes
|
|
849
|
+
change_types: [type_changed]
|
|
850
|
+
severity: warning
|
|
851
|
+
action: warn
|
|
852
|
+
message: "Type changed at {path}."
|
|
853
|
+
|
|
854
|
+
- id: allow_field_removal
|
|
855
|
+
name: Allow Field Removal
|
|
856
|
+
change_types: [field_removed]
|
|
857
|
+
severity: info
|
|
858
|
+
action: allow
|
|
859
|
+
message: "Field removed from {path}."
|
|
860
|
+
`,
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
// Init command — scaffold .delimit/ config
|
|
864
|
+
program
|
|
865
|
+
.command('init')
|
|
866
|
+
.description('Initialize Delimit API governance in this project')
|
|
867
|
+
.option('--preset <name>', 'Policy preset: strict, default, or relaxed', 'default')
|
|
868
|
+
.action(async (options) => {
|
|
869
|
+
const configDir = path.join(process.cwd(), '.delimit');
|
|
870
|
+
const policyFile = path.join(configDir, 'policies.yml');
|
|
871
|
+
|
|
872
|
+
if (fs.existsSync(policyFile)) {
|
|
873
|
+
console.log(chalk.yellow('Already initialized — .delimit/policies.yml exists'));
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const preset = options.preset.toLowerCase();
|
|
878
|
+
if (!POLICY_PRESETS[preset]) {
|
|
879
|
+
console.log(chalk.red(`Unknown preset "${preset}". Choose: strict, default, or relaxed`));
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
884
|
+
fs.writeFileSync(policyFile, POLICY_PRESETS[preset]);
|
|
885
|
+
console.log(chalk.green(`Created .delimit/policies.yml (preset: ${preset})`));
|
|
886
|
+
console.log('');
|
|
887
|
+
console.log(` ${chalk.bold('strict')} — zero tolerance, all breaking changes are errors`);
|
|
888
|
+
console.log(` ${chalk.bold('default')} — balanced, blocks destructive changes, warns on risky`);
|
|
889
|
+
console.log(` ${chalk.bold('relaxed')} — warnings only, never blocks CI`);
|
|
890
|
+
console.log('');
|
|
891
|
+
console.log(`Switch preset: ${chalk.bold('delimit init --preset strict')}`);
|
|
778
892
|
console.log('');
|
|
779
893
|
console.log('Next steps:');
|
|
780
894
|
console.log(` ${chalk.bold('delimit lint')} old.yaml new.yaml — check for breaking changes`);
|
|
@@ -783,20 +897,66 @@ rules: []
|
|
|
783
897
|
});
|
|
784
898
|
|
|
785
899
|
// Lint command — diff + policy (primary command)
|
|
900
|
+
// Supports zero-spec mode: `delimit lint` (no args) auto-extracts from FastAPI
|
|
786
901
|
program
|
|
787
|
-
.command('lint
|
|
902
|
+
.command('lint [old_spec] [new_spec]')
|
|
788
903
|
.description('Lint API specs for breaking changes and policy violations')
|
|
789
904
|
.option('-p, --policy <file>', 'Custom policy file')
|
|
790
905
|
.option('--current-version <ver>', 'Current API version for semver bump')
|
|
791
906
|
.option('-n, --name <name>', 'API name for context')
|
|
792
907
|
.option('--json', 'Output raw JSON')
|
|
908
|
+
.option('-d, --dir <path>', 'Project directory for zero-spec mode', '.')
|
|
793
909
|
.action(async (oldSpec, newSpec, options) => {
|
|
794
910
|
try {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
911
|
+
let result;
|
|
912
|
+
|
|
913
|
+
if (!oldSpec || !newSpec) {
|
|
914
|
+
// Zero-spec mode: extract from framework source
|
|
915
|
+
console.log(chalk.gray('No spec files provided — detecting framework...'));
|
|
916
|
+
const zeroResult = apiEngine.zeroSpec(path.resolve(options.dir));
|
|
917
|
+
|
|
918
|
+
if (!zeroResult.success) {
|
|
919
|
+
console.error(chalk.red(`\n ${zeroResult.error}\n`));
|
|
920
|
+
if (zeroResult.error_type === 'no_framework') {
|
|
921
|
+
console.log(' Usage: delimit lint <old_spec> <new_spec>');
|
|
922
|
+
console.log(' Or run from a FastAPI project directory.\n');
|
|
923
|
+
}
|
|
924
|
+
process.exit(1);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
console.log(chalk.green(` ${zeroResult.message}`));
|
|
929
|
+
console.log(` Extracted: ${zeroResult.paths_count} paths, ${zeroResult.schemas_count} schemas`);
|
|
930
|
+
console.log(` Spec: ${zeroResult.spec_path}\n`);
|
|
931
|
+
|
|
932
|
+
// Check for baseline
|
|
933
|
+
const baselineDir = path.join(path.resolve(options.dir), '.delimit');
|
|
934
|
+
const baselinePath = path.join(baselineDir, 'baseline.yaml');
|
|
935
|
+
|
|
936
|
+
if (!fs.existsSync(baselinePath)) {
|
|
937
|
+
// First run: save baseline
|
|
938
|
+
fs.mkdirSync(baselineDir, { recursive: true });
|
|
939
|
+
const yaml = require('js-yaml');
|
|
940
|
+
fs.writeFileSync(baselinePath, yaml.dump(zeroResult.spec));
|
|
941
|
+
console.log(chalk.green(' Saved baseline to .delimit/baseline.yaml'));
|
|
942
|
+
console.log(' Run again after making changes to see the diff.\n');
|
|
943
|
+
process.exit(0);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Compare against baseline
|
|
948
|
+
result = apiEngine.lint(
|
|
949
|
+
baselinePath,
|
|
950
|
+
zeroResult.spec_path,
|
|
951
|
+
{ policy: options.policy, version: options.currentVersion, name: options.name }
|
|
952
|
+
);
|
|
953
|
+
} else {
|
|
954
|
+
result = apiEngine.lint(
|
|
955
|
+
path.resolve(oldSpec),
|
|
956
|
+
path.resolve(newSpec),
|
|
957
|
+
{ policy: options.policy, version: options.currentVersion, name: options.name }
|
|
958
|
+
);
|
|
959
|
+
}
|
|
800
960
|
|
|
801
961
|
if (options.json) {
|
|
802
962
|
console.log(JSON.stringify(result, null, 2));
|
package/lib/api-engine.js
CHANGED
|
@@ -153,4 +153,42 @@ function semver(oldSpec, newSpec, currentVersion) {
|
|
|
153
153
|
].join('\n'));
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* delimit zero-spec — extract OpenAPI from framework source code
|
|
158
|
+
*/
|
|
159
|
+
function zeroSpec(projectDir, opts = {}) {
|
|
160
|
+
const args = [];
|
|
161
|
+
if (opts.pythonBin) args.push(`python_bin=${pyStr(opts.pythonBin)}`);
|
|
162
|
+
|
|
163
|
+
return runGateway([
|
|
164
|
+
'import json, sys',
|
|
165
|
+
'sys.path.insert(0, ".")',
|
|
166
|
+
'from core.zero_spec.detector import detect_framework, Framework',
|
|
167
|
+
'from core.zero_spec.fastapi_extractor import extract_fastapi_spec',
|
|
168
|
+
'from core.zero_spec.nestjs_extractor import extract_nestjs_spec',
|
|
169
|
+
'from core.zero_spec.express_extractor import extract_express_spec',
|
|
170
|
+
`info = detect_framework(${pyStr(projectDir)})`,
|
|
171
|
+
'r = {"framework": info.framework.value, "confidence": info.confidence, "message": info.message}',
|
|
172
|
+
'if info.framework == Framework.FASTAPI:',
|
|
173
|
+
` ext = extract_fastapi_spec(info, ${pyStr(projectDir)}${opts.pythonBin ? `, python_bin=${pyStr(opts.pythonBin)}` : ''})`,
|
|
174
|
+
' r.update(ext)',
|
|
175
|
+
' if ext.get("success") and info.app_locations:',
|
|
176
|
+
' r["app_file"] = info.app_locations[0].file',
|
|
177
|
+
'elif info.framework == Framework.NESTJS:',
|
|
178
|
+
` ext = extract_nestjs_spec(info, ${pyStr(projectDir)})`,
|
|
179
|
+
' r.update(ext)',
|
|
180
|
+
' if ext.get("success") and info.app_locations:',
|
|
181
|
+
' r["app_file"] = info.app_locations[0].file',
|
|
182
|
+
'elif info.framework == Framework.EXPRESS:',
|
|
183
|
+
` ext = extract_express_spec(info, ${pyStr(projectDir)})`,
|
|
184
|
+
' r.update(ext)',
|
|
185
|
+
' if ext.get("success") and info.app_locations:',
|
|
186
|
+
' r["app_file"] = info.app_locations[0].file',
|
|
187
|
+
'else:',
|
|
188
|
+
' r["success"] = False',
|
|
189
|
+
' r["error"] = "No supported API framework detected"',
|
|
190
|
+
'print(json.dumps(r, default=str))',
|
|
191
|
+
].join('\n'));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = { lint, diff, explain, semver, zeroSpec, GATEWAY_ROOT };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Prevent breaking API changes before they reach production. Deterministic diff engine + policy enforcement + semver classification.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"delimit": "./bin/delimit-cli.js"
|