@ankhorage/doctor 0.7.3 → 0.9.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/CHANGELOG.md +12 -0
- package/dist/analysis.d.ts +2 -0
- package/dist/analysis.d.ts.map +1 -1
- package/dist/analysis.js.map +1 -1
- package/dist/authReadinessAnalysis.d.ts +8 -0
- package/dist/authReadinessAnalysis.d.ts.map +1 -0
- package/dist/authReadinessAnalysis.js +38 -0
- package/dist/authReadinessAnalysis.js.map +1 -0
- package/dist/authReadinessDiagnostic.d.ts +9 -0
- package/dist/authReadinessDiagnostic.d.ts.map +1 -0
- package/dist/authReadinessDiagnostic.js +11 -0
- package/dist/authReadinessDiagnostic.js.map +1 -0
- package/dist/authReadinessEnvironment.d.ts +16 -0
- package/dist/authReadinessEnvironment.d.ts.map +1 -0
- package/dist/authReadinessEnvironment.js +29 -0
- package/dist/authReadinessEnvironment.js.map +1 -0
- package/dist/authReadinessFileAnalysis.d.ts +3 -0
- package/dist/authReadinessFileAnalysis.d.ts.map +1 -0
- package/dist/authReadinessFileAnalysis.js +12 -0
- package/dist/authReadinessFileAnalysis.js.map +1 -0
- package/dist/authReadinessNativeDiagnostic.d.ts +4 -0
- package/dist/authReadinessNativeDiagnostic.d.ts.map +1 -0
- package/dist/authReadinessNativeDiagnostic.js +11 -0
- package/dist/authReadinessNativeDiagnostic.js.map +1 -0
- package/dist/authReadinessProvider.d.ts +15 -0
- package/dist/authReadinessProvider.d.ts.map +1 -0
- package/dist/authReadinessProvider.js +44 -0
- package/dist/authReadinessProvider.js.map +1 -0
- package/dist/authReadinessProviderEnvironment.d.ts +15 -0
- package/dist/authReadinessProviderEnvironment.d.ts.map +1 -0
- package/dist/authReadinessProviderEnvironment.js +35 -0
- package/dist/authReadinessProviderEnvironment.js.map +1 -0
- package/dist/authReadinessRequirements.d.ts +10 -0
- package/dist/authReadinessRequirements.d.ts.map +1 -0
- package/dist/authReadinessRequirements.js +34 -0
- package/dist/authReadinessRequirements.js.map +1 -0
- package/dist/authReadinessTargetFallback.d.ts +3 -0
- package/dist/authReadinessTargetFallback.d.ts.map +1 -0
- package/dist/authReadinessTargetFallback.js +32 -0
- package/dist/authReadinessTargetFallback.js.map +1 -0
- package/dist/authReadinessTargetResolution.d.ts +8 -0
- package/dist/authReadinessTargetResolution.d.ts.map +1 -0
- package/dist/authReadinessTargetResolution.js +2 -0
- package/dist/authReadinessTargetResolution.js.map +1 -0
- package/dist/authReadinessTargets.d.ts +3 -0
- package/dist/authReadinessTargets.d.ts.map +1 -0
- package/dist/authReadinessTargets.js +26 -0
- package/dist/authReadinessTargets.js.map +1 -0
- package/dist/authReadinessUnsupported.d.ts +13 -0
- package/dist/authReadinessUnsupported.d.ts.map +1 -0
- package/dist/authReadinessUnsupported.js +22 -0
- package/dist/authReadinessUnsupported.js.map +1 -0
- package/dist/authReadinessUnsupportedBackend.d.ts +12 -0
- package/dist/authReadinessUnsupportedBackend.d.ts.map +1 -0
- package/dist/authReadinessUnsupportedBackend.js +18 -0
- package/dist/authReadinessUnsupportedBackend.js.map +1 -0
- package/dist/bunPolicyAnalysis.d.ts +3 -0
- package/dist/bunPolicyAnalysis.d.ts.map +1 -0
- package/dist/bunPolicyAnalysis.js +190 -0
- package/dist/bunPolicyAnalysis.js.map +1 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +14 -0
- package/dist/commands.js.map +1 -1
- package/dist/dependencyPolicyAnalysis.d.ts.map +1 -1
- package/dist/dependencyPolicyAnalysis.js +6 -2
- package/dist/dependencyPolicyAnalysis.js.map +1 -1
- package/dist/diagnostics.d.ts +1 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/manifestAnalysis.d.ts.map +1 -1
- package/dist/manifestAnalysis.js +4 -1
- package/dist/manifestAnalysis.js.map +1 -1
- package/dist/readiness.d.ts +13 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +2 -0
- package/dist/readiness.js.map +1 -0
- package/package.json +3 -2
- package/src/analysis.ts +2 -0
- package/src/authReadinessAnalysis.ts +50 -0
- package/src/authReadinessDiagnostic.ts +18 -0
- package/src/authReadinessEnvironment.ts +51 -0
- package/src/authReadinessFileAnalysis.ts +14 -0
- package/src/authReadinessNativeDiagnostic.ts +17 -0
- package/src/authReadinessProvider.ts +61 -0
- package/src/authReadinessProviderEnvironment.ts +54 -0
- package/src/authReadinessRequirements.ts +50 -0
- package/src/authReadinessTargetFallback.ts +38 -0
- package/src/authReadinessTargetResolution.ts +9 -0
- package/src/authReadinessTargets.ts +35 -0
- package/src/authReadinessUnsupported.ts +34 -0
- package/src/authReadinessUnsupportedBackend.ts +34 -0
- package/src/bunPolicyAnalysis.ts +261 -0
- package/src/commands.ts +16 -0
- package/src/dependencyPolicyAnalysis.ts +9 -4
- package/src/diagnostics.ts +9 -0
- package/src/index.ts +3 -0
- package/src/manifestAnalysis.ts +4 -1
- package/src/readiness.ts +14 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
import type { DoctorAnalysisResult } from './analysis.js';
|
|
7
|
+
import type { DoctorDiagnostic, DoctorRuleId } from './diagnostics.js';
|
|
8
|
+
|
|
9
|
+
interface BunRuntimePolicySnapshot {
|
|
10
|
+
readonly packageManager: string;
|
|
11
|
+
readonly typesRange: string;
|
|
12
|
+
readonly version: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEVTOOLS_POLICY_SPECIFIER = '@ankhorage/devtools/policy';
|
|
16
|
+
const REPAIR_HINT = 'Run "ankh devtools sync" to repair the managed Bun state.';
|
|
17
|
+
const BUN_VERSION_PATTERN = /^\s*bun-version:\s*['"]?([^'"\s#]+)['"]?\s*(?:#.*)?$/mu;
|
|
18
|
+
const WORKFLOWS = [
|
|
19
|
+
{
|
|
20
|
+
relativePath: path.join('.github', 'workflows', 'ci.yml'),
|
|
21
|
+
ruleId: 'repo.workflows.ci.bun-policy',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
relativePath: path.join('.github', 'workflows', 'release.yml'),
|
|
25
|
+
ruleId: 'repo.workflows.release.bun-policy',
|
|
26
|
+
},
|
|
27
|
+
] as const satisfies readonly { readonly relativePath: string; readonly ruleId: DoctorRuleId }[];
|
|
28
|
+
|
|
29
|
+
export async function applyBunRuntimePolicy(
|
|
30
|
+
result: DoctorAnalysisResult,
|
|
31
|
+
): Promise<DoctorAnalysisResult> {
|
|
32
|
+
if (result.profile !== 'public-package' || !result.hasPackageJson) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const policy = await loadTargetBunRuntimePolicy(result.targetPath);
|
|
37
|
+
const packageJsonPath = path.join(result.targetPath, 'package.json');
|
|
38
|
+
const packageJson = await readPackageJson(packageJsonPath);
|
|
39
|
+
if (policy === null || packageJson === null) {
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const diagnostics = [
|
|
44
|
+
...result.diagnostics,
|
|
45
|
+
...analyzePackageBunPolicy(packageJson, packageJsonPath, result, policy),
|
|
46
|
+
...(await analyzeWorkflowBunPolicy(result, policy)),
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
return withDiagnostics(result, diagnostics);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function loadTargetBunRuntimePolicy(
|
|
53
|
+
targetPath: string,
|
|
54
|
+
): Promise<BunRuntimePolicySnapshot | null> {
|
|
55
|
+
const requireFromTarget = createRequire(path.join(targetPath, 'package.json'));
|
|
56
|
+
try {
|
|
57
|
+
const modulePath = requireFromTarget.resolve(DEVTOOLS_POLICY_SPECIFIER);
|
|
58
|
+
const importedModule = (await import(pathToFileURL(modulePath).href)) as unknown;
|
|
59
|
+
return parseBunRuntimePolicy(importedModule);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (isUnavailablePolicyError(error)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function analyzePackageBunPolicy(
|
|
69
|
+
packageJson: Record<string, unknown>,
|
|
70
|
+
packageJsonPath: string,
|
|
71
|
+
result: DoctorAnalysisResult,
|
|
72
|
+
policy: BunRuntimePolicySnapshot,
|
|
73
|
+
): DoctorDiagnostic[] {
|
|
74
|
+
const diagnostics: DoctorDiagnostic[] = [];
|
|
75
|
+
const { packageManager } = packageJson;
|
|
76
|
+
if (isNonEmptyString(packageManager) && packageManager.startsWith('bun@')) {
|
|
77
|
+
pushMismatch(diagnostics, {
|
|
78
|
+
actual: packageManager,
|
|
79
|
+
expected: policy.packageManager,
|
|
80
|
+
location: 'package.json#packageManager',
|
|
81
|
+
path: packageJsonPath,
|
|
82
|
+
profile: result.profile,
|
|
83
|
+
ruleId: 'package.json.package-manager.policy',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const devDependencies = isRecord(packageJson.devDependencies)
|
|
88
|
+
? packageJson.devDependencies
|
|
89
|
+
: null;
|
|
90
|
+
const bunTypes = devDependencies?.['@types/bun'];
|
|
91
|
+
if (isNonEmptyString(bunTypes)) {
|
|
92
|
+
pushMismatch(diagnostics, {
|
|
93
|
+
actual: bunTypes,
|
|
94
|
+
expected: policy.typesRange,
|
|
95
|
+
location: 'package.json#devDependencies.@types/bun',
|
|
96
|
+
path: packageJsonPath,
|
|
97
|
+
profile: result.profile,
|
|
98
|
+
ruleId: 'package.dependencies.types-bun.policy',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return diagnostics;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function analyzeWorkflowBunPolicy(
|
|
106
|
+
result: DoctorAnalysisResult,
|
|
107
|
+
policy: BunRuntimePolicySnapshot,
|
|
108
|
+
): Promise<DoctorDiagnostic[]> {
|
|
109
|
+
const diagnostics = await Promise.all(
|
|
110
|
+
WORKFLOWS.map(async (workflow) => await analyzeWorkflow(result, workflow, policy)),
|
|
111
|
+
);
|
|
112
|
+
return diagnostics.flat();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function analyzeWorkflow(
|
|
116
|
+
result: DoctorAnalysisResult,
|
|
117
|
+
workflow: (typeof WORKFLOWS)[number],
|
|
118
|
+
policy: BunRuntimePolicySnapshot,
|
|
119
|
+
): Promise<DoctorDiagnostic[]> {
|
|
120
|
+
const workflowPath = path.join(result.targetPath, workflow.relativePath);
|
|
121
|
+
const contents = await readTextOrNull(workflowPath);
|
|
122
|
+
if (contents === null) {
|
|
123
|
+
return [createWorkflowDiagnostic(result, workflow, policy, workflowPath, 'missing-path')];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const match = BUN_VERSION_PATTERN.exec(contents);
|
|
127
|
+
const actual = match?.[1];
|
|
128
|
+
if (!isNonEmptyString(actual)) {
|
|
129
|
+
return [createWorkflowDiagnostic(result, workflow, policy, workflowPath, 'field-missing')];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const diagnostics: DoctorDiagnostic[] = [];
|
|
133
|
+
pushMismatch(diagnostics, {
|
|
134
|
+
actual,
|
|
135
|
+
expected: policy.version,
|
|
136
|
+
location: `${workflow.relativePath}#bun-version`,
|
|
137
|
+
path: workflowPath,
|
|
138
|
+
profile: result.profile,
|
|
139
|
+
ruleId: workflow.ruleId,
|
|
140
|
+
});
|
|
141
|
+
return diagnostics;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function createWorkflowDiagnostic(
|
|
145
|
+
result: DoctorAnalysisResult,
|
|
146
|
+
workflow: (typeof WORKFLOWS)[number],
|
|
147
|
+
policy: BunRuntimePolicySnapshot,
|
|
148
|
+
workflowPath: string,
|
|
149
|
+
code: DoctorDiagnostic['code'],
|
|
150
|
+
): DoctorDiagnostic {
|
|
151
|
+
const detail =
|
|
152
|
+
code === 'missing-path'
|
|
153
|
+
? `Managed workflow is missing: ${workflow.relativePath}.`
|
|
154
|
+
: `${workflow.relativePath} does not define bun-version.`;
|
|
155
|
+
return {
|
|
156
|
+
code,
|
|
157
|
+
message: `${detail} Expected Bun ${policy.version}. ${REPAIR_HINT}`,
|
|
158
|
+
path: workflowPath,
|
|
159
|
+
profile: result.profile,
|
|
160
|
+
ruleId: workflow.ruleId,
|
|
161
|
+
severity: 'error',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function pushMismatch(
|
|
166
|
+
diagnostics: DoctorDiagnostic[],
|
|
167
|
+
input: {
|
|
168
|
+
readonly actual: string;
|
|
169
|
+
readonly expected: string;
|
|
170
|
+
readonly location: string;
|
|
171
|
+
readonly path: string;
|
|
172
|
+
readonly profile: DoctorAnalysisResult['profile'];
|
|
173
|
+
readonly ruleId: DoctorRuleId;
|
|
174
|
+
},
|
|
175
|
+
): void {
|
|
176
|
+
if (input.actual === input.expected) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
diagnostics.push({
|
|
180
|
+
code: 'field-invalid',
|
|
181
|
+
message: `Bun policy drift at ${input.location}: expected "${input.expected}", found "${input.actual}". ${REPAIR_HINT}`,
|
|
182
|
+
path: input.path,
|
|
183
|
+
profile: input.profile,
|
|
184
|
+
ruleId: input.ruleId,
|
|
185
|
+
severity: 'error',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function parseBunRuntimePolicy(importedModule: unknown): BunRuntimePolicySnapshot | null {
|
|
190
|
+
if (!isRecord(importedModule) || !isRecord(importedModule.bunRuntimePolicy)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const { packageManager, typesRange, version } = importedModule.bunRuntimePolicy;
|
|
194
|
+
return isNonEmptyString(packageManager) &&
|
|
195
|
+
isNonEmptyString(typesRange) &&
|
|
196
|
+
isNonEmptyString(version)
|
|
197
|
+
? { packageManager, typesRange, version }
|
|
198
|
+
: null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function withDiagnostics(
|
|
202
|
+
result: DoctorAnalysisResult,
|
|
203
|
+
diagnostics: readonly DoctorDiagnostic[],
|
|
204
|
+
): DoctorAnalysisResult {
|
|
205
|
+
return {
|
|
206
|
+
...result,
|
|
207
|
+
diagnostics,
|
|
208
|
+
fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {
|
|
213
|
+
try {
|
|
214
|
+
const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;
|
|
215
|
+
return isRecord(parsed) ? parsed : null;
|
|
216
|
+
} catch {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function readTextOrNull(filePath: string): Promise<string | null> {
|
|
222
|
+
try {
|
|
223
|
+
return await fs.readFile(filePath, 'utf8');
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isUnavailablePolicyError(error: unknown): boolean {
|
|
233
|
+
if (
|
|
234
|
+
isNodeError(error) &&
|
|
235
|
+
(error.code === 'MODULE_NOT_FOUND' ||
|
|
236
|
+
error.code === 'ERR_MODULE_NOT_FOUND' ||
|
|
237
|
+
error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')
|
|
238
|
+
) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
243
|
+
return (
|
|
244
|
+
message.includes(DEVTOOLS_POLICY_SPECIFIER) &&
|
|
245
|
+
(message.includes('Cannot find module') ||
|
|
246
|
+
message.includes('not exported') ||
|
|
247
|
+
message.includes('Package subpath'))
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
252
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
256
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
|
260
|
+
return error instanceof Error && 'code' in error;
|
|
261
|
+
}
|
package/src/commands.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type DoctorDiagnostic,
|
|
10
10
|
} from './diagnostics.js';
|
|
11
11
|
import { DOCTOR_CAPABILITIES, DOCTOR_COMMAND_CATEGORY } from './packageMetadata.js';
|
|
12
|
+
import type { DoctorReadiness } from './readiness.js';
|
|
12
13
|
|
|
13
14
|
type DoctorCommandName = 'fix' | 'package' | 'repo' | 'validate';
|
|
14
15
|
|
|
@@ -289,6 +290,17 @@ function renderAnalysisReport(
|
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
292
|
|
|
293
|
+
const readiness = result.readiness ?? [];
|
|
294
|
+
lines.push('');
|
|
295
|
+
lines.push('Readiness:');
|
|
296
|
+
if (readiness.length === 0) {
|
|
297
|
+
lines.push(' none');
|
|
298
|
+
} else {
|
|
299
|
+
for (const item of readiness) {
|
|
300
|
+
lines.push(renderReadinessLine(item));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
292
304
|
if (commandName === 'fix') {
|
|
293
305
|
lines.push('');
|
|
294
306
|
lines.push('Planned changes:');
|
|
@@ -316,6 +328,10 @@ function renderDiagnosticLine(diagnostic: DoctorDiagnostic): string {
|
|
|
316
328
|
return ` - ${diagnostic.severity.toUpperCase()} ${diagnostic.ruleId} (${diagnostic.code}): ${diagnostic.message}`;
|
|
317
329
|
}
|
|
318
330
|
|
|
331
|
+
function renderReadinessLine(item: DoctorReadiness): string {
|
|
332
|
+
return ` - ${item.target} ${item.environment} ${item.provider} ${item.transport}: ${item.status} — ${item.message}`;
|
|
333
|
+
}
|
|
334
|
+
|
|
319
335
|
function renderPlannedChangeLine(change: DoctorPlannedChange): string {
|
|
320
336
|
return ` - ${change.ruleId} [${change.kind}]: ${change.description}`;
|
|
321
337
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type DoctorAnalysisRequest,
|
|
7
7
|
type DoctorAnalysisResult,
|
|
8
8
|
} from './analysis.js';
|
|
9
|
+
import { applyBunRuntimePolicy } from './bunPolicyAnalysis.js';
|
|
9
10
|
import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
10
11
|
import type { DoctorDiagnostic } from './diagnostics.js';
|
|
11
12
|
|
|
@@ -16,15 +17,19 @@ const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
|
|
|
16
17
|
export async function analyzeDoctorTarget(
|
|
17
18
|
request: DoctorAnalysisRequest,
|
|
18
19
|
): Promise<DoctorAnalysisResult> {
|
|
19
|
-
return
|
|
20
|
+
return applyRepositoryPolicies(await analyzeBaseDoctorTarget(request));
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export async function analyzeDoctorTargetWithCliLayout(
|
|
23
24
|
request: DoctorAnalysisRequest,
|
|
24
25
|
): Promise<DoctorAnalysisResult> {
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
return applyRepositoryPolicies(await analyzeBaseDoctorTargetWithCliLayout(request));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function applyRepositoryPolicies(
|
|
30
|
+
result: DoctorAnalysisResult,
|
|
31
|
+
): Promise<DoctorAnalysisResult> {
|
|
32
|
+
return applyBunRuntimePolicy(await applyDevtoolsDependencyPlacementPolicy(result));
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
async function applyDevtoolsDependencyPlacementPolicy(
|
package/src/diagnostics.ts
CHANGED
|
@@ -30,13 +30,18 @@ export type DoctorRuleId =
|
|
|
30
30
|
| 'manifest.auth.flow.unauthorized-route.valid'
|
|
31
31
|
| 'manifest.auth.flow.valid-shape'
|
|
32
32
|
| 'manifest.auth.oauth.callback-route.valid'
|
|
33
|
+
| 'manifest.auth.oauth.callback-target.configured'
|
|
33
34
|
| 'manifest.auth.oauth.provider.credentials-ref.required'
|
|
34
35
|
| 'manifest.auth.oauth.provider.credentials-ref.valid'
|
|
35
36
|
| 'manifest.auth.oauth.provider.duplicate'
|
|
36
37
|
| 'manifest.auth.oauth.provider.inline-secret.disallowed'
|
|
37
38
|
| 'manifest.auth.oauth.providers.valid'
|
|
39
|
+
| 'manifest.auth.oauth.setup.supported'
|
|
38
40
|
| 'manifest.auth.oauth.valid-shape'
|
|
39
41
|
| 'manifest.auth.valid-shape'
|
|
42
|
+
| 'manifest.deploy.targets.enabled'
|
|
43
|
+
| 'manifest.deploy.targets.legacy-web'
|
|
44
|
+
| 'manifest.deploy.valid-shape'
|
|
40
45
|
| 'manifest.infra.valid-shape'
|
|
41
46
|
| 'manifest.json.readable'
|
|
42
47
|
| 'manifest.json.valid'
|
|
@@ -57,6 +62,7 @@ export type DoctorRuleId =
|
|
|
57
62
|
| 'package.dependencies.changesets.required'
|
|
58
63
|
| 'package.dependencies.devtools.required'
|
|
59
64
|
| 'package.dependencies.paradox.required'
|
|
65
|
+
| 'package.dependencies.types-bun.policy'
|
|
60
66
|
| 'package.dependencies.types-bun.required'
|
|
61
67
|
| 'package.dependencies.types-node.required'
|
|
62
68
|
| 'package.dependencies.typescript.required'
|
|
@@ -71,6 +77,7 @@ export type DoctorRuleId =
|
|
|
71
77
|
| 'package.json.license.required'
|
|
72
78
|
| 'package.json.name.required'
|
|
73
79
|
| 'package.json.package-manager.bun'
|
|
80
|
+
| 'package.json.package-manager.policy'
|
|
74
81
|
| 'package.json.package-manager.required'
|
|
75
82
|
| 'package.json.private.public-package-disallowed'
|
|
76
83
|
| 'package.json.publish-config.public'
|
|
@@ -99,6 +106,8 @@ export type DoctorRuleId =
|
|
|
99
106
|
| 'repo.changeset.required'
|
|
100
107
|
| 'repo.license.required'
|
|
101
108
|
| 'repo.readme.required'
|
|
109
|
+
| 'repo.workflows.ci.bun-policy'
|
|
110
|
+
| 'repo.workflows.release.bun-policy'
|
|
102
111
|
| 'repo.workflows.required'
|
|
103
112
|
| 'studio.dependencies.dnd.required'
|
|
104
113
|
| 'studio.dependencies.runtime.required'
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type {
|
|
|
7
7
|
DoctorTargetCheck,
|
|
8
8
|
DoctorTargetMode,
|
|
9
9
|
} from './analysis.js';
|
|
10
|
+
export { analyzeAuthReadiness, type DoctorAuthReadinessAnalysis } from './authReadinessAnalysis.js';
|
|
11
|
+
export { analyzeAuthReadinessFile } from './authReadinessFileAnalysis.js';
|
|
10
12
|
export {
|
|
11
13
|
createDoctorRuntimeProvider,
|
|
12
14
|
type CreateDoctorRuntimeProviderOptions,
|
|
@@ -37,3 +39,4 @@ export {
|
|
|
37
39
|
DOCTOR_PACKAGE_NAME,
|
|
38
40
|
DOCTOR_PACKAGE_VERSION,
|
|
39
41
|
} from './packageMetadata.js';
|
|
42
|
+
export type { DoctorReadiness, DoctorReadinessStatus } from './readiness.js';
|
package/src/manifestAnalysis.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
DoctorFixPlan,
|
|
11
11
|
DoctorPlannedChange,
|
|
12
12
|
} from './analysis.js';
|
|
13
|
+
import { analyzeAuthReadinessFile } from './authReadinessFileAnalysis.js';
|
|
13
14
|
import type {
|
|
14
15
|
DoctorDiagnostic,
|
|
15
16
|
DoctorDiagnosticCode,
|
|
@@ -52,7 +53,8 @@ export async function analyzeAppManifestTarget(
|
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
const
|
|
56
|
+
const authReadiness = await analyzeAuthReadinessFile(targetPath);
|
|
57
|
+
const diagnostics = [...(await analyzeAppManifestFile(targetPath)), ...authReadiness.diagnostics];
|
|
56
58
|
const plannedChanges: DoctorPlannedChange[] = [];
|
|
57
59
|
const fixPlan: DoctorFixPlan | null =
|
|
58
60
|
request.mode === 'fix'
|
|
@@ -71,6 +73,7 @@ export async function analyzeAppManifestTarget(
|
|
|
71
73
|
hasPackageJson: false,
|
|
72
74
|
plannedChanges,
|
|
73
75
|
profile: MANIFEST_PROFILE,
|
|
76
|
+
readiness: authReadiness.readiness,
|
|
74
77
|
repoMarkers: [],
|
|
75
78
|
targetPath,
|
|
76
79
|
};
|
package/src/readiness.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuthOAuthProviderId, AuthOAuthTransportId } from '@ankhorage/contracts/auth';
|
|
2
|
+
import type { AppDeployEnvironmentId, AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
3
|
+
|
|
4
|
+
export type DoctorReadinessStatus = 'missing' | 'ready' | 'unsupported';
|
|
5
|
+
|
|
6
|
+
export interface DoctorReadiness {
|
|
7
|
+
readonly category: 'auth-oauth';
|
|
8
|
+
readonly environment: AppDeployEnvironmentId;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
readonly provider: AuthOAuthProviderId;
|
|
11
|
+
readonly status: DoctorReadinessStatus;
|
|
12
|
+
readonly target: AppDeployTargetId;
|
|
13
|
+
readonly transport: AuthOAuthTransportId;
|
|
14
|
+
}
|