@planu/cli 0.97.2 → 0.99.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/dist/config/license-plans.json +8 -3
- package/dist/config/mobile-ci-templates/android-github-actions.yml +149 -0
- package/dist/config/mobile-ci-templates/ios-github-actions.yml +120 -0
- package/dist/engine/api-validation/graphql-schema-validator.d.ts +3 -0
- package/dist/engine/api-validation/graphql-schema-validator.d.ts.map +1 -0
- package/dist/engine/api-validation/graphql-schema-validator.js +134 -0
- package/dist/engine/api-validation/graphql-schema-validator.js.map +1 -0
- package/dist/engine/api-validation/index.d.ts +3 -0
- package/dist/engine/api-validation/index.d.ts.map +1 -0
- package/dist/engine/api-validation/index.js +4 -0
- package/dist/engine/api-validation/index.js.map +1 -0
- package/dist/engine/api-validation/openapi-impl-validator.d.ts +3 -0
- package/dist/engine/api-validation/openapi-impl-validator.d.ts.map +1 -0
- package/dist/engine/api-validation/openapi-impl-validator.js +205 -0
- package/dist/engine/api-validation/openapi-impl-validator.js.map +1 -0
- package/dist/engine/ci-generator/android-jobs.d.ts +17 -0
- package/dist/engine/ci-generator/android-jobs.d.ts.map +1 -0
- package/dist/engine/ci-generator/android-jobs.js +168 -0
- package/dist/engine/ci-generator/android-jobs.js.map +1 -0
- package/dist/engine/ci-generator/ios-jobs.d.ts +17 -0
- package/dist/engine/ci-generator/ios-jobs.d.ts.map +1 -0
- package/dist/engine/ci-generator/ios-jobs.js +151 -0
- package/dist/engine/ci-generator/ios-jobs.js.map +1 -0
- package/dist/engine/ci-generator/yaml-builder.d.ts +10 -1
- package/dist/engine/ci-generator/yaml-builder.d.ts.map +1 -1
- package/dist/engine/ci-generator/yaml-builder.js +46 -1
- package/dist/engine/ci-generator/yaml-builder.js.map +1 -1
- package/dist/engine/coverage-gap-analyzer.d.ts +6 -0
- package/dist/engine/coverage-gap-analyzer.d.ts.map +1 -0
- package/dist/engine/coverage-gap-analyzer.js +177 -0
- package/dist/engine/coverage-gap-analyzer.js.map +1 -0
- package/dist/engine/dashboard/templates-kanban.d.ts.map +1 -1
- package/dist/engine/dashboard/templates-kanban.js +22 -0
- package/dist/engine/dashboard/templates-kanban.js.map +1 -1
- package/dist/engine/dashboard/templates-layout.d.ts.map +1 -1
- package/dist/engine/dashboard/templates-layout.js +59 -7
- package/dist/engine/dashboard/templates-layout.js.map +1 -1
- package/dist/engine/mutation-config-generator.d.ts +6 -0
- package/dist/engine/mutation-config-generator.d.ts.map +1 -0
- package/dist/engine/mutation-config-generator.js +111 -0
- package/dist/engine/mutation-config-generator.js.map +1 -0
- package/dist/engine/product-intelligence/index.d.ts +7 -0
- package/dist/engine/product-intelligence/index.d.ts.map +1 -0
- package/dist/engine/product-intelligence/index.js +211 -0
- package/dist/engine/product-intelligence/index.js.map +1 -0
- package/dist/engine/tdd-scaffold-generator.d.ts +7 -0
- package/dist/engine/tdd-scaffold-generator.d.ts.map +1 -0
- package/dist/engine/tdd-scaffold-generator.js +252 -0
- package/dist/engine/tdd-scaffold-generator.js.map +1 -0
- package/dist/engine/telemetry/error-reporter.d.ts +9 -2
- package/dist/engine/telemetry/error-reporter.d.ts.map +1 -1
- package/dist/engine/telemetry/error-reporter.js +31 -4
- package/dist/engine/telemetry/error-reporter.js.map +1 -1
- package/dist/engine/version-resolver.d.ts +10 -0
- package/dist/engine/version-resolver.d.ts.map +1 -0
- package/dist/engine/version-resolver.js +144 -0
- package/dist/engine/version-resolver.js.map +1 -0
- package/dist/engine/web-fetcher/stack-advisor.d.ts.map +1 -1
- package/dist/engine/web-fetcher/stack-advisor.js +32 -4
- package/dist/engine/web-fetcher/stack-advisor.js.map +1 -1
- package/dist/index.js +9 -11
- package/dist/index.js.map +1 -1
- package/dist/tools/checkpoint-handler.d.ts.map +1 -1
- package/dist/tools/checkpoint-handler.js +24 -1
- package/dist/tools/checkpoint-handler.js.map +1 -1
- package/dist/tools/dashboard.d.ts.map +1 -1
- package/dist/tools/dashboard.js +2 -0
- package/dist/tools/dashboard.js.map +1 -1
- package/dist/tools/product-insights-handler.d.ts +11 -0
- package/dist/tools/product-insights-handler.d.ts.map +1 -0
- package/dist/tools/product-insights-handler.js +102 -0
- package/dist/tools/product-insights-handler.js.map +1 -0
- package/dist/tools/register-product-insights-tools.d.ts +3 -0
- package/dist/tools/register-product-insights-tools.d.ts.map +1 -0
- package/dist/tools/register-product-insights-tools.js +22 -0
- package/dist/tools/register-product-insights-tools.js.map +1 -0
- package/dist/tools/register-spec-322-tools.d.ts +3 -0
- package/dist/tools/register-spec-322-tools.d.ts.map +1 -0
- package/dist/tools/register-spec-322-tools.js +41 -0
- package/dist/tools/register-spec-322-tools.js.map +1 -0
- package/dist/tools/register-spec-323-tools.d.ts +3 -0
- package/dist/tools/register-spec-323-tools.d.ts.map +1 -0
- package/dist/tools/register-spec-323-tools.js +57 -0
- package/dist/tools/register-spec-323-tools.js.map +1 -0
- package/dist/tools/safe-handler.d.ts.map +1 -1
- package/dist/tools/safe-handler.js +19 -1
- package/dist/tools/safe-handler.js.map +1 -1
- package/dist/tools/tdd-scaffold-handler.d.ts +5 -0
- package/dist/tools/tdd-scaffold-handler.d.ts.map +1 -0
- package/dist/tools/tdd-scaffold-handler.js +92 -0
- package/dist/tools/tdd-scaffold-handler.js.map +1 -0
- package/dist/tools/validate-api-contract-handler.d.ts +3 -0
- package/dist/tools/validate-api-contract-handler.d.ts.map +1 -0
- package/dist/tools/validate-api-contract-handler.js +74 -0
- package/dist/tools/validate-api-contract-handler.js.map +1 -0
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +24 -2
- package/dist/tools/validate.js.map +1 -1
- package/dist/types/api-contract.d.ts +58 -0
- package/dist/types/api-contract.d.ts.map +1 -1
- package/dist/types/ci.d.ts +8 -0
- package/dist/types/ci.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/product-intelligence.d.ts +60 -0
- package/dist/types/product-intelligence.d.ts.map +1 -0
- package/dist/types/product-intelligence.js +3 -0
- package/dist/types/product-intelligence.js.map +1 -0
- package/dist/types/telemetry.d.ts +1 -1
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/types/testing.d.ts +60 -0
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/version-resolution.d.ts +23 -0
- package/dist/types/version-resolution.d.ts.map +1 -0
- package/dist/types/version-resolution.js +3 -0
- package/dist/types/version-resolution.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +8 -3
- package/src/config/mobile-ci-templates/android-github-actions.yml +149 -0
- package/src/config/mobile-ci-templates/ios-github-actions.yml +120 -0
|
@@ -87,7 +87,8 @@
|
|
|
87
87
|
"check_spec_lock",
|
|
88
88
|
"configure_desktop_notifications",
|
|
89
89
|
"desktop_notification_status",
|
|
90
|
-
"test_notification"
|
|
90
|
+
"test_notification",
|
|
91
|
+
"tdd_scaffold"
|
|
91
92
|
],
|
|
92
93
|
"proTools": [
|
|
93
94
|
"audit",
|
|
@@ -273,7 +274,11 @@
|
|
|
273
274
|
"configure_compliance",
|
|
274
275
|
"apply_spec_template",
|
|
275
276
|
"publish_spec_template",
|
|
276
|
-
"search_spec_templates"
|
|
277
|
+
"search_spec_templates",
|
|
278
|
+
"validate_api_contract",
|
|
279
|
+
"coverage_gap_analyzer",
|
|
280
|
+
"run_mutation_hints",
|
|
281
|
+
"product_insights"
|
|
277
282
|
],
|
|
278
283
|
"alwaysAllowed": [
|
|
279
284
|
"activate_license",
|
|
@@ -292,4 +297,4 @@
|
|
|
292
297
|
"Planu Team": "team"
|
|
293
298
|
},
|
|
294
299
|
"upgradeUrl": "https://planu.dev/pricing"
|
|
295
|
-
}
|
|
300
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
name: Android CI/CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
|
|
12
|
+
android-test:
|
|
13
|
+
name: Android — Gradle Unit Tests
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup JDK
|
|
20
|
+
uses: actions/setup-java@v4
|
|
21
|
+
with:
|
|
22
|
+
java-version: '17'
|
|
23
|
+
distribution: temurin
|
|
24
|
+
|
|
25
|
+
- name: Cache Gradle packages
|
|
26
|
+
uses: actions/cache@v4
|
|
27
|
+
with:
|
|
28
|
+
path: |
|
|
29
|
+
~/.gradle/caches
|
|
30
|
+
~/.gradle/wrapper
|
|
31
|
+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
32
|
+
restore-keys: ${{ runner.os }}-gradle-
|
|
33
|
+
|
|
34
|
+
- name: Grant Gradle execute permission
|
|
35
|
+
run: chmod +x gradlew
|
|
36
|
+
|
|
37
|
+
- name: Run unit tests
|
|
38
|
+
run: ./gradlew :{{module}}:testDebugUnitTest
|
|
39
|
+
|
|
40
|
+
- name: Upload test reports
|
|
41
|
+
uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: test-results
|
|
44
|
+
path: '{{module}}/build/reports/tests/'
|
|
45
|
+
if-no-files-found: warn
|
|
46
|
+
|
|
47
|
+
android-sign:
|
|
48
|
+
name: Android — Sign Release AAB
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: android-test
|
|
51
|
+
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
|
|
52
|
+
env:
|
|
53
|
+
KEYSTORE_FILE: ${{ secrets.KEYSTORE_FILE }}
|
|
54
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
55
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
56
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
57
|
+
steps:
|
|
58
|
+
- name: Checkout code
|
|
59
|
+
uses: actions/checkout@v4
|
|
60
|
+
|
|
61
|
+
- name: Setup JDK
|
|
62
|
+
uses: actions/setup-java@v4
|
|
63
|
+
with:
|
|
64
|
+
java-version: '17'
|
|
65
|
+
distribution: temurin
|
|
66
|
+
|
|
67
|
+
- name: Cache Gradle packages
|
|
68
|
+
uses: actions/cache@v4
|
|
69
|
+
with:
|
|
70
|
+
path: |
|
|
71
|
+
~/.gradle/caches
|
|
72
|
+
~/.gradle/wrapper
|
|
73
|
+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
74
|
+
restore-keys: ${{ runner.os }}-gradle-
|
|
75
|
+
|
|
76
|
+
- name: Grant Gradle execute permission
|
|
77
|
+
run: chmod +x gradlew
|
|
78
|
+
|
|
79
|
+
- name: Decode keystore
|
|
80
|
+
run: echo "$KEYSTORE_FILE" | base64 --decode > keystore.jks
|
|
81
|
+
env:
|
|
82
|
+
KEYSTORE_FILE: ${{ secrets.KEYSTORE_FILE }}
|
|
83
|
+
|
|
84
|
+
- name: Build signed release AAB
|
|
85
|
+
run: |
|
|
86
|
+
./gradlew :{{module}}:bundleRelease \
|
|
87
|
+
-Pandroid.injected.signing.store.file=$(pwd)/keystore.jks \
|
|
88
|
+
-Pandroid.injected.signing.store.password="$KEYSTORE_PASSWORD" \
|
|
89
|
+
-Pandroid.injected.signing.key.alias="$KEY_ALIAS" \
|
|
90
|
+
-Pandroid.injected.signing.key.password="$KEY_PASSWORD"
|
|
91
|
+
env:
|
|
92
|
+
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
93
|
+
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
94
|
+
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
95
|
+
|
|
96
|
+
- name: Upload signed AAB
|
|
97
|
+
uses: actions/upload-artifact@v4
|
|
98
|
+
with:
|
|
99
|
+
name: release-aab
|
|
100
|
+
path: '{{module}}/build/outputs/bundle/release/*.aab'
|
|
101
|
+
|
|
102
|
+
android-deploy:
|
|
103
|
+
name: Android — Play Console Deploy
|
|
104
|
+
runs-on: ubuntu-latest
|
|
105
|
+
needs: android-sign
|
|
106
|
+
if: github.ref == 'refs/heads/main'
|
|
107
|
+
env:
|
|
108
|
+
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}
|
|
109
|
+
steps:
|
|
110
|
+
- name: Checkout code
|
|
111
|
+
uses: actions/checkout@v4
|
|
112
|
+
|
|
113
|
+
- name: Download signed AAB
|
|
114
|
+
uses: actions/download-artifact@v4
|
|
115
|
+
with:
|
|
116
|
+
name: release-aab
|
|
117
|
+
path: ./release
|
|
118
|
+
|
|
119
|
+
- name: Setup Ruby for Fastlane
|
|
120
|
+
uses: ruby/setup-ruby@v1
|
|
121
|
+
with:
|
|
122
|
+
ruby-version: 'bundler-cache: true'
|
|
123
|
+
|
|
124
|
+
- name: Install Fastlane
|
|
125
|
+
run: gem install fastlane --no-document
|
|
126
|
+
|
|
127
|
+
- name: Decode Play Store JSON key
|
|
128
|
+
run: echo "$PLAY_STORE_JSON_KEY" > play-store-key.json
|
|
129
|
+
env:
|
|
130
|
+
PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}
|
|
131
|
+
|
|
132
|
+
- name: Upload to Play Console (internal track)
|
|
133
|
+
run: |
|
|
134
|
+
fastlane supply \
|
|
135
|
+
--aab ./release/{{module}}/build/outputs/bundle/release/*.aab \
|
|
136
|
+
--package_name "{{bundleId}}" \
|
|
137
|
+
--track internal \
|
|
138
|
+
--json_key ./play-store-key.json
|
|
139
|
+
|
|
140
|
+
# Required GitHub Secrets:
|
|
141
|
+
# KEYSTORE_FILE — Base64-encoded .jks keystore file
|
|
142
|
+
# KEYSTORE_PASSWORD — Password for the keystore file
|
|
143
|
+
# KEY_ALIAS — Alias of the signing key in the keystore
|
|
144
|
+
# KEY_PASSWORD — Password for the signing key
|
|
145
|
+
# PLAY_STORE_JSON_KEY — JSON content of the Google Play service account key
|
|
146
|
+
#
|
|
147
|
+
# Placeholders:
|
|
148
|
+
# {{module}} — Gradle module name (e.g. app)
|
|
149
|
+
# {{bundleId}} — Android application ID (e.g. com.example.myapp)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
name: iOS CI/CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
|
|
12
|
+
ios-test:
|
|
13
|
+
name: iOS — Xcode Build & Test
|
|
14
|
+
runs-on: macos-latest
|
|
15
|
+
env:
|
|
16
|
+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
17
|
+
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
|
|
18
|
+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Select Xcode version
|
|
24
|
+
run: sudo xcode-select -s /Applications/Xcode.app
|
|
25
|
+
|
|
26
|
+
- name: Build and test (Xcode)
|
|
27
|
+
run: |
|
|
28
|
+
xcodebuild clean build test \
|
|
29
|
+
-scheme "{{scheme}}" \
|
|
30
|
+
-destination 'platform=iOS Simulator,name=iPhone 15,OS=latest' \
|
|
31
|
+
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO \
|
|
32
|
+
| xcpretty && exit "${PIPESTATUS[0]}"
|
|
33
|
+
|
|
34
|
+
ios-testflight:
|
|
35
|
+
name: iOS — TestFlight Upload
|
|
36
|
+
runs-on: macos-latest
|
|
37
|
+
needs: ios-test
|
|
38
|
+
if: github.ref == 'refs/heads/develop'
|
|
39
|
+
env:
|
|
40
|
+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
41
|
+
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
|
|
42
|
+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
|
43
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
44
|
+
steps:
|
|
45
|
+
- name: Checkout code
|
|
46
|
+
uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- name: Select Xcode version
|
|
49
|
+
run: sudo xcode-select -s /Applications/Xcode.app
|
|
50
|
+
|
|
51
|
+
- name: Setup Ruby for Fastlane
|
|
52
|
+
uses: ruby/setup-ruby@v1
|
|
53
|
+
with:
|
|
54
|
+
ruby-version: 'bundler-cache: true'
|
|
55
|
+
|
|
56
|
+
- name: Install Fastlane
|
|
57
|
+
run: gem install fastlane --no-document
|
|
58
|
+
|
|
59
|
+
- name: Build IPA
|
|
60
|
+
run: |
|
|
61
|
+
fastlane gym \
|
|
62
|
+
--scheme "{{scheme}}" \
|
|
63
|
+
--export_method app-store \
|
|
64
|
+
--output_directory ./build
|
|
65
|
+
env:
|
|
66
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
67
|
+
|
|
68
|
+
- name: Upload to TestFlight
|
|
69
|
+
run: fastlane pilot upload --ipa ./build/*.ipa --skip_waiting_for_build_processing true
|
|
70
|
+
|
|
71
|
+
ios-appstore:
|
|
72
|
+
name: iOS — App Store Submission
|
|
73
|
+
runs-on: macos-latest
|
|
74
|
+
needs: ios-test
|
|
75
|
+
if: github.ref == 'refs/heads/main'
|
|
76
|
+
env:
|
|
77
|
+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
78
|
+
APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
|
|
79
|
+
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
|
80
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
81
|
+
steps:
|
|
82
|
+
- name: Checkout code
|
|
83
|
+
uses: actions/checkout@v4
|
|
84
|
+
|
|
85
|
+
- name: Select Xcode version
|
|
86
|
+
run: sudo xcode-select -s /Applications/Xcode.app
|
|
87
|
+
|
|
88
|
+
- name: Setup Ruby for Fastlane
|
|
89
|
+
uses: ruby/setup-ruby@v1
|
|
90
|
+
with:
|
|
91
|
+
ruby-version: 'bundler-cache: true'
|
|
92
|
+
|
|
93
|
+
- name: Install Fastlane
|
|
94
|
+
run: gem install fastlane --no-document
|
|
95
|
+
|
|
96
|
+
- name: Build IPA (production)
|
|
97
|
+
run: |
|
|
98
|
+
fastlane gym \
|
|
99
|
+
--scheme "{{scheme}}" \
|
|
100
|
+
--configuration Release \
|
|
101
|
+
--export_method app-store \
|
|
102
|
+
--output_directory ./build
|
|
103
|
+
env:
|
|
104
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
105
|
+
|
|
106
|
+
- name: Submit to App Store
|
|
107
|
+
run: |
|
|
108
|
+
fastlane deliver \
|
|
109
|
+
--ipa ./build/*.ipa \
|
|
110
|
+
--submit_for_review false \
|
|
111
|
+
--automatic_release false
|
|
112
|
+
|
|
113
|
+
# Required GitHub Secrets:
|
|
114
|
+
# APPLE_API_KEY — Base64-encoded .p8 private key from App Store Connect
|
|
115
|
+
# APPLE_API_ISSUER_ID — Issuer ID from App Store Connect API keys page
|
|
116
|
+
# APPLE_API_KEY_ID — Key ID from App Store Connect API keys page
|
|
117
|
+
# MATCH_PASSWORD — Encryption passphrase for Fastlane Match certificates
|
|
118
|
+
#
|
|
119
|
+
# Placeholders:
|
|
120
|
+
# {{scheme}} — Replace with your Xcode scheme name (e.g. MyApp)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-schema-validator.d.ts","sourceRoot":"","sources":["../../../src/engine/api-validation/graphql-schema-validator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,sBAAsB,CAAC;AAuF9B,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAgD5B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// engine/api-validation/graphql-schema-validator.ts — Validate GraphQL schema vs resolvers (SPEC-322)
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// GraphQL SDL parser (minimal — extracts Query/Mutation/Subscription fields)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const OPERATION_TYPE_REGEX = /type\s+(Query|Mutation|Subscription)\s*\{([^}]+)\}/gs;
|
|
8
|
+
const FIELD_NAME_REGEX = /^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\([^)]*\))?\s*:/m;
|
|
9
|
+
function parseGraphQLOperations(sdl) {
|
|
10
|
+
const operations = [];
|
|
11
|
+
let typeMatch;
|
|
12
|
+
const typeRegex = new RegExp(OPERATION_TYPE_REGEX.source, OPERATION_TYPE_REGEX.flags);
|
|
13
|
+
while ((typeMatch = typeRegex.exec(sdl)) !== null) {
|
|
14
|
+
const kind = typeMatch[1];
|
|
15
|
+
const body = typeMatch[2] ?? '';
|
|
16
|
+
const lines = body.split('\n');
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
const fieldMatch = FIELD_NAME_REGEX.exec(line);
|
|
19
|
+
if (fieldMatch?.[1]) {
|
|
20
|
+
operations.push({ name: fieldMatch[1], kind });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return operations;
|
|
25
|
+
}
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Resolver scanner
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const RESOLVER_PATTERNS = [
|
|
30
|
+
// Apollo: { Query: { resolverName() } }
|
|
31
|
+
/['"`]([a-zA-Z_][a-zA-Z0-9_]*)['"`]\s*:\s*(?:async\s+)?(?:function|\(|[a-zA-Z_])/g,
|
|
32
|
+
// Direct assignment: const resolverNameResolver = async () =>
|
|
33
|
+
/(?:const|let)\s+([a-zA-Z_][a-zA-Z0-9_]*Resolver)\s*=/g,
|
|
34
|
+
// Object method shorthand: resolverName(parent, args, ctx)
|
|
35
|
+
/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*(?:parent|root|_),?\s*(?:args|_args)/g,
|
|
36
|
+
];
|
|
37
|
+
async function scanSourceFilesForResolvers(projectPath) {
|
|
38
|
+
const patterns = [
|
|
39
|
+
`${projectPath}/**/*.ts`,
|
|
40
|
+
`${projectPath}/**/*.tsx`,
|
|
41
|
+
`${projectPath}/**/*.js`,
|
|
42
|
+
`${projectPath}/**/*.jsx`,
|
|
43
|
+
];
|
|
44
|
+
const ignores = ['**/node_modules/**', '**/dist/**', '**/.next/**', '**/build/**'];
|
|
45
|
+
const resolvers = [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const files = await glob(patterns, { ignore: ignores });
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
const content = await readFile(file, 'utf-8').catch(() => '');
|
|
50
|
+
if (!content) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!content.includes('resolver') &&
|
|
54
|
+
!content.includes('Query') &&
|
|
55
|
+
!content.includes('Mutation')) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
for (const pattern of RESOLVER_PATTERNS) {
|
|
59
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
60
|
+
let match;
|
|
61
|
+
while ((match = re.exec(content)) !== null) {
|
|
62
|
+
const name = match[1];
|
|
63
|
+
if (name && !seen.has(name)) {
|
|
64
|
+
seen.add(name);
|
|
65
|
+
resolvers.push({ name, file });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return resolvers;
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Main validator
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
export async function validateGraphQLSchema(schemaPath, projectPath) {
|
|
76
|
+
const sdl = await readFile(schemaPath, 'utf-8');
|
|
77
|
+
const operations = parseGraphQLOperations(sdl);
|
|
78
|
+
const resolvers = await scanSourceFilesForResolvers(projectPath);
|
|
79
|
+
const resolverNames = new Set(resolvers.map((r) => r.name));
|
|
80
|
+
const missingEndpoints = [];
|
|
81
|
+
const undocumentedEndpoints = [];
|
|
82
|
+
for (const op of operations) {
|
|
83
|
+
if (!resolverNames.has(op.name)) {
|
|
84
|
+
missingEndpoints.push({
|
|
85
|
+
path: `${op.kind}.${op.name}`,
|
|
86
|
+
method: op.kind,
|
|
87
|
+
severity: 'critical',
|
|
88
|
+
message: `GraphQL ${op.kind} "${op.name}" is defined in the schema but no resolver implementation was detected in the project source files.`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const opNames = new Set(operations.map((o) => o.name));
|
|
93
|
+
for (const resolver of resolvers) {
|
|
94
|
+
if (!opNames.has(resolver.name) && isLikelyOperationResolver(resolver.name)) {
|
|
95
|
+
undocumentedEndpoints.push({
|
|
96
|
+
path: resolver.name,
|
|
97
|
+
method: 'resolver',
|
|
98
|
+
severity: 'info',
|
|
99
|
+
message: `Resolver "${resolver.name}" found in ${resolver.file} does not correspond to any operation in the GraphQL schema.`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const findings = [...missingEndpoints, ...undocumentedEndpoints];
|
|
104
|
+
return {
|
|
105
|
+
contractFormat: 'graphql',
|
|
106
|
+
contractVersion: 'SDL',
|
|
107
|
+
contractTitle: 'GraphQL Schema',
|
|
108
|
+
totalContractEndpoints: operations.length,
|
|
109
|
+
totalImplEndpoints: resolvers.length,
|
|
110
|
+
missingEndpoints,
|
|
111
|
+
undocumentedEndpoints,
|
|
112
|
+
schemaDrift: [],
|
|
113
|
+
breakingChanges: [],
|
|
114
|
+
findings,
|
|
115
|
+
summary: buildSummary(missingEndpoints.length, undocumentedEndpoints.length),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function isLikelyOperationResolver(name) {
|
|
119
|
+
return /^(?:get|create|update|delete|list|find|add|remove|fetch|load|save|set)[A-Z]/.test(name);
|
|
120
|
+
}
|
|
121
|
+
function buildSummary(missing, undocumented) {
|
|
122
|
+
if (missing === 0 && undocumented === 0) {
|
|
123
|
+
return 'GraphQL schema and resolver implementations are fully aligned.';
|
|
124
|
+
}
|
|
125
|
+
const parts = [];
|
|
126
|
+
if (missing > 0) {
|
|
127
|
+
parts.push(`${missing} operation(s) in schema have no resolver implementation`);
|
|
128
|
+
}
|
|
129
|
+
if (undocumented > 0) {
|
|
130
|
+
parts.push(`${undocumented} resolver(s) found that are not in the schema`);
|
|
131
|
+
}
|
|
132
|
+
return parts.join('; ') + '.';
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=graphql-schema-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-schema-validator.js","sourceRoot":"","sources":["../../../src/engine/api-validation/graphql-schema-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/api-validation/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/api-validation/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi-impl-validator.d.ts","sourceRoot":"","sources":["../../../src/engine/api-validation/openapi-impl-validator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,sBAAsB,CAAC;AAsJ9B,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CA6D5B"}
|