@ankhorage/doctor 0.10.12 → 0.10.14
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/authReadinessEnvironment.d.ts +3 -2
- package/dist/authReadinessEnvironment.d.ts.map +1 -1
- package/dist/authReadinessEnvironment.js.map +1 -1
- package/dist/authReadinessProvider.js +2 -2
- package/dist/authReadinessProvider.js.map +1 -1
- package/dist/authReadinessProviderEnvironment.d.ts +3 -2
- package/dist/authReadinessProviderEnvironment.d.ts.map +1 -1
- package/dist/authReadinessProviderEnvironment.js.map +1 -1
- package/dist/authReadinessUnsupported.d.ts +3 -2
- package/dist/authReadinessUnsupported.d.ts.map +1 -1
- package/dist/authReadinessUnsupported.js.map +1 -1
- package/dist/authReadinessUnsupportedBackend.js +2 -2
- package/dist/authReadinessUnsupportedBackend.js.map +1 -1
- package/dist/dependencyPolicyAnalysis.d.ts +6 -0
- package/dist/dependencyPolicyAnalysis.d.ts.map +1 -1
- package/dist/dependencyPolicyAnalysis.js +10 -68
- package/dist/dependencyPolicyAnalysis.js.map +1 -1
- package/dist/manifestAnalysis.d.ts.map +1 -1
- package/dist/manifestAnalysis.js +1 -9
- package/dist/manifestAnalysis.js.map +1 -1
- package/dist/readiness.d.ts +3 -2
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js.map +1 -1
- package/package.json +2 -2
- package/src/authReadinessEnvironment.ts +3 -6
- package/src/authReadinessProvider.ts +2 -2
- package/src/authReadinessProviderEnvironment.ts +3 -6
- package/src/authReadinessUnsupported.ts +3 -2
- package/src/authReadinessUnsupportedBackend.ts +2 -2
- package/src/dependencyPolicyAnalysis.ts +10 -95
- package/src/manifestAnalysis.ts +1 -9
- package/src/readiness.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e6c0f2d: Remove the obsolete Ankh-specific Devtools runtime dependency placement rule now that Ankh dynamically discovers providers instead of bundling core providers.
|
|
8
|
+
|
|
9
|
+
## 0.10.13
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 204c66d: Consume the current shared application-environment contracts for Auth readiness diagnostics.
|
|
14
|
+
|
|
3
15
|
## 0.10.12
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { AuthOAuthSetupPlan } from '@ankhorage/contracts/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';
|
|
3
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
3
4
|
import type { DoctorReadiness } from './readiness.js';
|
|
4
5
|
export declare function analyzeAuthReadinessEnvironment(input: {
|
|
5
6
|
readonly callbackRoute: unknown;
|
|
6
7
|
readonly credentialsRef: unknown;
|
|
7
8
|
readonly enabledTargets: readonly AppDeployTargetId[];
|
|
8
|
-
readonly environment:
|
|
9
|
+
readonly environment: AppEnvironmentId;
|
|
9
10
|
readonly plan: AuthOAuthSetupPlan;
|
|
10
11
|
readonly provider: string;
|
|
11
12
|
readonly targets: AppDeployTargets;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessEnvironment.d.ts","sourceRoot":"","sources":["../src/authReadinessEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"authReadinessEnvironment.d.ts","sourceRoot":"","sources":["../src/authReadinessEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAO1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,wBAAgB,+BAA+B,CAAC,KAAK,EAAE;IACrD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;CACpC,GAAG;IACF,QAAQ,CAAC,oBAAoB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5D,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;CAChD,CAgCA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessEnvironment.js","sourceRoot":"","sources":["../src/authReadinessEnvironment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authReadinessEnvironment.js","sourceRoot":"","sources":["../src/authReadinessEnvironment.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,0BAA0B,EAC1B,iCAAiC,GAClC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,uCAAuC,EAAE,MAAM,gCAAgC,CAAC;AAGzF,MAAM,UAAU,+BAA+B,CAAC,KAQ/C;IAIC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC1D,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAmB,EAAE;QACrE,MAAM,OAAO,GAAG,uCAAuC,CAAC;YACtD,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM;YACN,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QACH,IAAI,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,mBAAmB,CAAC,EAAE,CAAC;YACvE,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,aAAa,GAAG,iCAAiC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,OAAO;YACL,GAAG,aAAa;YAChB,QAAQ,EAAE,YAAY;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,OAAO,EAAE,0BAA0B,CAAC;gBAClC,GAAG,aAAa;gBAChB,OAAO;gBACP,MAAM;aACP,CAAC;YACF,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;YAClD,MAAM;YACN,SAAS,EAAE,kBAAkB;SAC9B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,oBAAoB,EAAE,CAAC,GAAG,oBAAoB,CAAC,EAAE,SAAS,EAAE,CAAC;AACxE,CAAC","sourcesContent":["import type { AuthOAuthSetupPlan } from '@ankhorage/contracts/auth';\nimport type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';\nimport type { AppEnvironmentId } from '@ankhorage/contracts/environments';\n\nimport {\n createAuthReadinessMessage,\n resolveNativeAuthReadinessContext,\n} from './authReadinessNativeContext.js';\nimport { collectMissingAuthReadinessRequirements } from './authReadinessRequirements.js';\nimport type { DoctorReadiness } from './readiness.js';\n\nexport function analyzeAuthReadinessEnvironment(input: {\n readonly callbackRoute: unknown;\n readonly credentialsRef: unknown;\n readonly enabledTargets: readonly AppDeployTargetId[];\n readonly environment: AppEnvironmentId;\n readonly plan: AuthOAuthSetupPlan;\n readonly provider: string;\n readonly targets: AppDeployTargets;\n}): {\n readonly missingNativeTargets: readonly AppDeployTargetId[];\n readonly readiness: readonly DoctorReadiness[];\n} {\n const missingNativeTargets = new Set<AppDeployTargetId>();\n const readiness = input.enabledTargets.map((target): DoctorReadiness => {\n const missing = collectMissingAuthReadinessRequirements({\n callbackRoute: input.callbackRoute,\n credentialsRef: input.credentialsRef,\n plan: input.plan,\n target,\n targets: input.targets,\n });\n if (target !== 'web' && missing.includes(`${target} deep-link scheme`)) {\n missingNativeTargets.add(target);\n }\n\n const nativeContext = resolveNativeAuthReadinessContext({ target, targets: input.targets });\n return {\n ...nativeContext,\n category: 'auth-oauth',\n environment: input.environment,\n message: createAuthReadinessMessage({\n ...nativeContext,\n missing,\n target,\n }),\n provider: input.provider,\n status: missing.length === 0 ? 'ready' : 'missing',\n target,\n transport: 'brokeredRedirect',\n };\n });\n\n return { missingNativeTargets: [...missingNativeTargets], readiness };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';
|
|
2
2
|
import { createNativeAuthCallbackDiagnostic } from './authReadinessNativeDiagnostic.js';
|
|
3
3
|
import { analyzeAuthProviderEnvironment } from './authReadinessProviderEnvironment.js';
|
|
4
4
|
import { createUnsupportedAuthDiagnostic } from './authReadinessUnsupported.js';
|
|
@@ -19,7 +19,7 @@ export function analyzeAuthProviderReadiness(input) {
|
|
|
19
19
|
const readiness = [];
|
|
20
20
|
const missingNativeTargets = new Set();
|
|
21
21
|
let unsupported = false;
|
|
22
|
-
for (const environment of
|
|
22
|
+
for (const environment of APP_ENVIRONMENT_IDS) {
|
|
23
23
|
const result = analyzeAuthProviderEnvironment({
|
|
24
24
|
callbackRoute: input.callbackRoute,
|
|
25
25
|
credentialsRef: input.provider.credentialsRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessProvider.js","sourceRoot":"","sources":["../src/authReadinessProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"authReadinessProvider.js","sourceRoot":"","sources":["../src/authReadinessProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,EAAE,kCAAkC,EAAE,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,8BAA8B,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAIrF,MAAM,UAAU,4BAA4B,CAAC,KAO5C;IACC,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,IAAI,UAAU,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACjE,IAAI,KAAK,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,6BAA6B,CAAC;YAC3C,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,QAAQ,EAAE,UAAU;SACrB,CAAC,CAAC;QACH,OAAO,EAAE,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;IACpF,CAAC;IAED,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,SAAS,GAAsB,EAAE,CAAC;IACxC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC1D,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,KAAK,MAAM,WAAW,IAAI,mBAAmB,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,8BAA8B,CAAC;YAC5C,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc;YAC7C,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW;YACX,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QACpC,WAAW,KAAK,MAAM,CAAC,WAAW,CAAC;QACnC,MAAM,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,WAAW,CAAC,IAAI,CACd,+BAA+B,CAAC;YAC9B,OAAO,EAAE,mBAAmB,UAAU,qEAAqE;YAC3G,IAAI,EAAE,KAAK,CAAC,YAAY;SACzB,CAAC,CACH,CAAC;IACJ,CAAC;IACD,oBAAoB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CACtC,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CACjF,CAAC;IACF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC","sourcesContent":["import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';\nimport { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';\n\nimport { createNativeAuthCallbackDiagnostic } from './authReadinessNativeDiagnostic.js';\nimport { analyzeAuthProviderEnvironment } from './authReadinessProviderEnvironment.js';\nimport { createUnsupportedAuthDiagnostic } from './authReadinessUnsupported.js';\nimport { analyzeUnsupportedAuthBackend } from './authReadinessUnsupportedBackend.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\nimport type { DoctorReadiness } from './readiness.js';\n\nexport function analyzeAuthProviderReadiness(input: {\n readonly authProvider: unknown;\n readonly callbackRoute: unknown;\n readonly enabledTargets: readonly AppDeployTargetId[];\n readonly manifestPath: string;\n readonly provider: Record<string, unknown>;\n readonly targets: AppDeployTargets;\n}): { readonly diagnostics: DoctorDiagnostic[]; readonly readiness: DoctorReadiness[] } {\n const providerId = typeof input.provider.id === 'string' ? input.provider.id : '';\n if (providerId === '') return { diagnostics: [], readiness: [] };\n if (input.authProvider !== 'supabase') {\n const result = analyzeUnsupportedAuthBackend({\n enabledTargets: input.enabledTargets,\n manifestPath: input.manifestPath,\n provider: providerId,\n });\n return { diagnostics: [...result.diagnostics], readiness: [...result.readiness] };\n }\n\n const diagnostics: DoctorDiagnostic[] = [];\n const readiness: DoctorReadiness[] = [];\n const missingNativeTargets = new Set<AppDeployTargetId>();\n let unsupported = false;\n\n for (const environment of APP_ENVIRONMENT_IDS) {\n const result = analyzeAuthProviderEnvironment({\n callbackRoute: input.callbackRoute,\n credentialsRef: input.provider.credentialsRef,\n enabledTargets: input.enabledTargets,\n environment,\n provider: providerId,\n targets: input.targets,\n });\n readiness.push(...result.readiness);\n unsupported ||= result.unsupported;\n result.missingNativeTargets.forEach((target) => missingNativeTargets.add(target));\n }\n\n if (unsupported) {\n diagnostics.push(\n createUnsupportedAuthDiagnostic({\n message: `OAuth provider \"${providerId}\" is not supported by the Supabase brokeredRedirect setup resolver.`,\n path: input.manifestPath,\n }),\n );\n }\n missingNativeTargets.forEach((target) =>\n diagnostics.push(createNativeAuthCallbackDiagnostic(target, input.manifestPath)),\n );\n return { diagnostics, readiness };\n}\n"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';
|
|
2
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
2
3
|
import type { DoctorReadiness } from './readiness.js';
|
|
3
4
|
export declare function analyzeAuthProviderEnvironment(input: {
|
|
4
5
|
readonly callbackRoute: unknown;
|
|
5
6
|
readonly credentialsRef: unknown;
|
|
6
7
|
readonly enabledTargets: readonly AppDeployTargetId[];
|
|
7
|
-
readonly environment:
|
|
8
|
+
readonly environment: AppEnvironmentId;
|
|
8
9
|
readonly provider: string;
|
|
9
10
|
readonly targets: AppDeployTargets;
|
|
10
11
|
}): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessProviderEnvironment.d.ts","sourceRoot":"","sources":["../src/authReadinessProviderEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"authReadinessProviderEnvironment.d.ts","sourceRoot":"","sources":["../src/authReadinessProviderEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAK1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;CACpC,GAAG;IACF,QAAQ,CAAC,oBAAoB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5D,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B,CA+BA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessProviderEnvironment.js","sourceRoot":"","sources":["../src/authReadinessProviderEnvironment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authReadinessProviderEnvironment.js","sourceRoot":"","sources":["../src/authReadinessProviderEnvironment.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAG/E,MAAM,UAAU,8BAA8B,CAAC,KAO9C;IAKC,MAAM,IAAI,GAAG,6BAA6B,CAAC;QACzC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,kBAAkB;QAC7B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,OAAO,EAAE,KAAK,CAAC,cAAc;KAC9B,CAAC,CAAC;IACH,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO;YACL,oBAAoB,EAAE,EAAE;YACxB,SAAS,EAAE,8BAA8B,CAAC;gBACxC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,OAAO,EAAE,KAAK,CAAC,cAAc;aAC9B,CAAC;YACF,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,+BAA+B,CAAC;YACjC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,IAAI;YACJ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;QACF,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC","sourcesContent":["import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';\nimport type { AppEnvironmentId } from '@ankhorage/contracts/environments';\nimport { resolveSupabaseOAuthSetupPlan } from '@ankhorage/supabase-auth';\n\nimport { analyzeAuthReadinessEnvironment } from './authReadinessEnvironment.js';\nimport { createUnsupportedAuthReadiness } from './authReadinessUnsupported.js';\nimport type { DoctorReadiness } from './readiness.js';\n\nexport function analyzeAuthProviderEnvironment(input: {\n readonly callbackRoute: unknown;\n readonly credentialsRef: unknown;\n readonly enabledTargets: readonly AppDeployTargetId[];\n readonly environment: AppEnvironmentId;\n readonly provider: string;\n readonly targets: AppDeployTargets;\n}): {\n readonly missingNativeTargets: readonly AppDeployTargetId[];\n readonly readiness: readonly DoctorReadiness[];\n readonly unsupported: boolean;\n} {\n const plan = resolveSupabaseOAuthSetupPlan({\n provider: input.provider,\n transport: 'brokeredRedirect',\n environment: input.environment,\n targets: input.enabledTargets,\n });\n if (plan === null) {\n return {\n missingNativeTargets: [],\n readiness: createUnsupportedAuthReadiness({\n environment: input.environment,\n provider: input.provider,\n targets: input.enabledTargets,\n }),\n unsupported: true,\n };\n }\n\n return {\n ...analyzeAuthReadinessEnvironment({\n callbackRoute: input.callbackRoute,\n credentialsRef: input.credentialsRef,\n enabledTargets: input.enabledTargets,\n environment: input.environment,\n plan,\n provider: input.provider,\n targets: input.targets,\n }),\n unsupported: false,\n };\n}\n"]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
2
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
2
3
|
import type { DoctorDiagnostic } from './diagnostics.js';
|
|
3
4
|
import type { DoctorReadiness } from './readiness.js';
|
|
4
5
|
export declare function createUnsupportedAuthReadiness(input: {
|
|
5
|
-
readonly environment:
|
|
6
|
+
readonly environment: AppEnvironmentId;
|
|
6
7
|
readonly provider: string;
|
|
7
8
|
readonly targets: readonly AppDeployTargetId[];
|
|
8
9
|
}): DoctorReadiness[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessUnsupported.d.ts","sourceRoot":"","sources":["../src/authReadinessUnsupported.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"authReadinessUnsupported.d.ts","sourceRoot":"","sources":["../src/authReadinessUnsupported.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD,GAAG,eAAe,EAAE,CAUpB;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GAAG,gBAAgB,CAQnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessUnsupported.js","sourceRoot":"","sources":["../src/authReadinessUnsupported.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authReadinessUnsupported.js","sourceRoot":"","sources":["../src/authReadinessUnsupported.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAI7E,MAAM,UAAU,8BAA8B,CAAC,KAI9C;IACC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACpC,QAAQ,EAAE,YAAY;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,OAAO,EAAE,8EAA8E;QACvF,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,aAAa;QACrB,MAAM;QACN,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,KAG/C;IACC,OAAO,6BAA6B,CAAC;QACnC,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,qCAAqC;QAC7C,QAAQ,EAAE,OAAO;KAClB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';\nimport type { AppEnvironmentId } from '@ankhorage/contracts/environments';\n\nimport { createAuthReadinessDiagnostic } from './authReadinessDiagnostic.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\nimport type { DoctorReadiness } from './readiness.js';\n\nexport function createUnsupportedAuthReadiness(input: {\n readonly environment: AppEnvironmentId;\n readonly provider: string;\n readonly targets: readonly AppDeployTargetId[];\n}): DoctorReadiness[] {\n return input.targets.map((target) => ({\n category: 'auth-oauth',\n environment: input.environment,\n message: 'The selected backend/provider/transport combination has no setup capability.',\n provider: input.provider,\n status: 'unsupported',\n target,\n transport: 'brokeredRedirect',\n }));\n}\n\nexport function createUnsupportedAuthDiagnostic(input: {\n readonly message: string;\n readonly path: string;\n}): DoctorDiagnostic {\n return createAuthReadinessDiagnostic({\n code: 'field-invalid',\n message: input.message,\n path: input.path,\n ruleId: 'manifest.auth.oauth.setup.supported',\n severity: 'error',\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';
|
|
2
2
|
import { createUnsupportedAuthDiagnostic, createUnsupportedAuthReadiness, } from './authReadinessUnsupported.js';
|
|
3
3
|
export function analyzeUnsupportedAuthBackend(input) {
|
|
4
4
|
return {
|
|
@@ -8,7 +8,7 @@ export function analyzeUnsupportedAuthBackend(input) {
|
|
|
8
8
|
path: input.manifestPath,
|
|
9
9
|
}),
|
|
10
10
|
],
|
|
11
|
-
readiness:
|
|
11
|
+
readiness: APP_ENVIRONMENT_IDS.flatMap((environment) => createUnsupportedAuthReadiness({
|
|
12
12
|
environment,
|
|
13
13
|
provider: input.provider,
|
|
14
14
|
targets: input.enabledTargets,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authReadinessUnsupportedBackend.js","sourceRoot":"","sources":["../src/authReadinessUnsupportedBackend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"authReadinessUnsupportedBackend.js","sourceRoot":"","sources":["../src/authReadinessUnsupportedBackend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,+BAA+B,CAAC;AAIvC,MAAM,UAAU,6BAA6B,CAAC,KAI7C;IAIC,OAAO;QACL,WAAW,EAAE;YACX,+BAA+B,CAAC;gBAC9B,OAAO,EAAE,wDAAwD;gBACjE,IAAI,EAAE,KAAK,CAAC,YAAY;aACzB,CAAC;SACH;QACD,SAAS,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CACrD,8BAA8B,CAAC;YAC7B,WAAW;YACX,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,OAAO,EAAE,KAAK,CAAC,cAAc;SAC9B,CAAC,CACH;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';\nimport { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';\n\nimport {\n createUnsupportedAuthDiagnostic,\n createUnsupportedAuthReadiness,\n} from './authReadinessUnsupported.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\nimport type { DoctorReadiness } from './readiness.js';\n\nexport function analyzeUnsupportedAuthBackend(input: {\n readonly enabledTargets: readonly AppDeployTargetId[];\n readonly manifestPath: string;\n readonly provider: string;\n}): {\n readonly diagnostics: readonly DoctorDiagnostic[];\n readonly readiness: readonly DoctorReadiness[];\n} {\n return {\n diagnostics: [\n createUnsupportedAuthDiagnostic({\n message: 'Configured OAuth backend has no Doctor setup resolver.',\n path: input.manifestPath,\n }),\n ],\n readiness: APP_ENVIRONMENT_IDS.flatMap((environment) =>\n createUnsupportedAuthReadiness({\n environment,\n provider: input.provider,\n targets: input.enabledTargets,\n }),\n ),\n };\n}\n"]}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type DoctorAnalysisRequest, type DoctorAnalysisResult } from './analysis.js';
|
|
2
|
+
/***
|
|
3
|
+
* Analyze a Doctor target and apply repository-level dependency and runtime policies.
|
|
4
|
+
*/
|
|
2
5
|
export declare function analyzeDoctorTarget(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
|
|
6
|
+
/***
|
|
7
|
+
* Analyze a Doctor target with CLI layout validation and repository-level policies.
|
|
8
|
+
*/
|
|
3
9
|
export declare function analyzeDoctorTargetWithCliLayout(request: DoctorAnalysisRequest): Promise<DoctorAnalysisResult>;
|
|
4
10
|
//# sourceMappingURL=dependencyPolicyAnalysis.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencyPolicyAnalysis.d.ts","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dependencyPolicyAnalysis.d.ts","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED;;GAEG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
|
|
@@ -1,80 +1,22 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
1
|
import { analyzeDoctorTarget as analyzeBaseDoctorTarget, } from './analysis.js';
|
|
4
2
|
import { applyBunRuntimePolicy } from './bunPolicyAnalysis.js';
|
|
5
3
|
import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
/***
|
|
5
|
+
* Analyze a Doctor target and apply repository-level dependency and runtime policies.
|
|
6
|
+
*/
|
|
9
7
|
export async function analyzeDoctorTarget(request) {
|
|
10
8
|
return applyRepositoryPolicies(await analyzeBaseDoctorTarget(request));
|
|
11
9
|
}
|
|
10
|
+
/***
|
|
11
|
+
* Analyze a Doctor target with CLI layout validation and repository-level policies.
|
|
12
|
+
*/
|
|
12
13
|
export async function analyzeDoctorTargetWithCliLayout(request) {
|
|
13
14
|
return applyRepositoryPolicies(await analyzeBaseDoctorTargetWithCliLayout(request));
|
|
14
15
|
}
|
|
16
|
+
/***
|
|
17
|
+
* Apply repository policies that remain independent from concrete package identities.
|
|
18
|
+
*/
|
|
15
19
|
async function applyRepositoryPolicies(result) {
|
|
16
|
-
return applyBunRuntimePolicy(
|
|
17
|
-
}
|
|
18
|
-
async function applyDevtoolsDependencyPlacementPolicy(result) {
|
|
19
|
-
if (!result.hasPackageJson) {
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
const packageJsonPath = path.join(result.targetPath, 'package.json');
|
|
23
|
-
const packageJson = await readPackageJson(packageJsonPath);
|
|
24
|
-
if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
|
|
25
|
-
return result;
|
|
26
|
-
}
|
|
27
|
-
const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
|
|
28
|
-
const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
|
|
29
|
-
const diagnosticsWithoutPlacementRule = result.diagnostics.filter((diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID);
|
|
30
|
-
const diagnostics = hasRuntimeDevtools
|
|
31
|
-
? diagnosticsWithoutPlacementRule
|
|
32
|
-
: [
|
|
33
|
-
...diagnosticsWithoutPlacementRule,
|
|
34
|
-
createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
|
|
35
|
-
];
|
|
36
|
-
return {
|
|
37
|
-
...result,
|
|
38
|
-
diagnostics,
|
|
39
|
-
fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
async function readPackageJson(packageJsonPath) {
|
|
43
|
-
try {
|
|
44
|
-
const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
45
|
-
return isRecord(parsed) ? parsed : null;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result) {
|
|
52
|
-
return {
|
|
53
|
-
code: 'missing-dependency',
|
|
54
|
-
message: '@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
|
|
55
|
-
path: packageJsonPath,
|
|
56
|
-
profile: result.profile,
|
|
57
|
-
ruleId: DEVTOOLS_REQUIRED_RULE_ID,
|
|
58
|
-
severity: 'error',
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function expectsDevtools(packageJson) {
|
|
62
|
-
const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
|
|
63
|
-
if (scripts === null) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
return Object.values(scripts).some((scriptValue) => isNonEmptyString(scriptValue) &&
|
|
67
|
-
(scriptValue.includes('ankhorage-eslint') ||
|
|
68
|
-
scriptValue.includes('ankhorage-prettier') ||
|
|
69
|
-
scriptValue.includes('ankhorage-knip')));
|
|
70
|
-
}
|
|
71
|
-
function hasDependency(dependencyMap, dependencyName) {
|
|
72
|
-
return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
|
|
73
|
-
}
|
|
74
|
-
function isNonEmptyString(value) {
|
|
75
|
-
return typeof value === 'string' && value.trim() !== '';
|
|
76
|
-
}
|
|
77
|
-
function isRecord(value) {
|
|
78
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
20
|
+
return applyBunRuntimePolicy(result);
|
|
79
21
|
}
|
|
80
22
|
//# sourceMappingURL=dependencyPolicyAnalysis.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencyPolicyAnalysis.js","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"dependencyPolicyAnalysis.js","sourceRoot":"","sources":["../src/dependencyPolicyAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,IAAI,uBAAuB,GAG/C,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gCAAgC,IAAI,oCAAoC,EAAE,MAAM,wBAAwB,CAAC;AAElH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAA8B;IAE9B,OAAO,uBAAuB,CAAC,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,OAA8B;IAE9B,OAAO,uBAAuB,CAAC,MAAM,oCAAoC,CAAC,OAAO,CAAC,CAAC,CAAC;AACtF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CACpC,MAA4B;IAE5B,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC","sourcesContent":["import {\n analyzeDoctorTarget as analyzeBaseDoctorTarget,\n type DoctorAnalysisRequest,\n type DoctorAnalysisResult,\n} from './analysis.js';\nimport { applyBunRuntimePolicy } from './bunPolicyAnalysis.js';\nimport { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';\n\n/***\n * Analyze a Doctor target and apply repository-level dependency and runtime policies.\n */\nexport async function analyzeDoctorTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyRepositoryPolicies(await analyzeBaseDoctorTarget(request));\n}\n\n/***\n * Analyze a Doctor target with CLI layout validation and repository-level policies.\n */\nexport async function analyzeDoctorTargetWithCliLayout(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n return applyRepositoryPolicies(await analyzeBaseDoctorTargetWithCliLayout(request));\n}\n\n/***\n * Apply repository policies that remain independent from concrete package identities.\n */\nasync function applyRepositoryPolicies(\n result: DoctorAnalysisResult,\n): Promise<DoctorAnalysisResult> {\n return applyBunRuntimePolicy(result);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifestAnalysis.d.ts","sourceRoot":"","sources":["../src/manifestAnalysis.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifestAnalysis.d.ts","sourceRoot":"","sources":["../src/manifestAnalysis.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EAGrB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,gBAAgB,EAIjB,MAAM,kBAAkB,CAAC;AAuB1B,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAqCtC;AAED,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAgC1F;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,OAAO,EACjB,YAAY,SAAkB,GAC7B,gBAAgB,EAAE,CAuEpB"}
|
package/dist/manifestAnalysis.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { AUTHZ_ENGINES, AUTHZ_KINDS } from '@ankhorage/contracts';
|
|
4
3
|
import { resolveAuthFlow } from '@ankhorage/contracts/auth';
|
|
5
4
|
import { analyzeAuthReadinessFile } from './authReadinessFileAnalysis.js';
|
|
6
5
|
import { analyzeSecretStoreManifest } from './secretManifestAnalysis.js';
|
|
6
|
+
const AUTHZ_KINDS = ['RBAC', 'ABAC'];
|
|
7
7
|
const MANIFEST_PROFILE = 'app-manifest';
|
|
8
8
|
const AUTH_ACTION_ROUTE_KEYS = [
|
|
9
9
|
'signInRoute',
|
|
@@ -276,14 +276,6 @@ function validateAuthorization(value, manifestPath, diagnostics) {
|
|
|
276
276
|
value: value.kind,
|
|
277
277
|
ruleId: 'manifest.auth.authorization.kind.valid',
|
|
278
278
|
});
|
|
279
|
-
validateAuthorizationField({
|
|
280
|
-
allowed: AUTHZ_ENGINES,
|
|
281
|
-
diagnostics,
|
|
282
|
-
fieldName: 'engine',
|
|
283
|
-
manifestPath,
|
|
284
|
-
value: value.engine,
|
|
285
|
-
ruleId: 'manifest.auth.authorization.engine.valid',
|
|
286
|
-
});
|
|
287
279
|
}
|
|
288
280
|
function validateAuthorizationField(request) {
|
|
289
281
|
if (typeof request.value === 'string' && request.allowed.includes(request.value)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifestAnalysis.js","sourceRoot":"","sources":["../src/manifestAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { AUTHZ_ENGINES, AUTHZ_KINDS } from '@ankhorage/contracts';\nimport { type AuthFlowConfig, resolveAuthFlow } from '@ankhorage/contracts/auth';\n\nimport type {\n DoctorAnalysisRequest,\n DoctorAnalysisResult,\n DoctorFixPlan,\n DoctorPlannedChange,\n} from './analysis.js';\nimport { analyzeAuthReadinessFile } from './authReadinessFileAnalysis.js';\nimport type {\n DoctorDiagnostic,\n DoctorDiagnosticCode,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\nimport { analyzeSecretStoreManifest } from './secretManifestAnalysis.js';\n\nconst MANIFEST_PROFILE = 'app-manifest' satisfies DoctorPolicyProfile;\nconst AUTH_ACTION_ROUTE_KEYS = [\n 'signInRoute',\n 'signUpRoute',\n 'signOutRoute',\n 'forgotPasswordRoute',\n 'otpRoute',\n] as const satisfies readonly (keyof AuthFlowConfig)[];\nconst OPTIONAL_ROUTE_KEYS = [\n 'signUpRoute',\n 'signOutRoute',\n 'forgotPasswordRoute',\n 'otpRoute',\n 'unauthorizedRoute',\n] as const satisfies readonly (keyof AuthFlowConfig)[];\n\ntype AuthActionRouteKey = (typeof AUTH_ACTION_ROUTE_KEYS)[number];\ntype OptionalRouteKey = (typeof OPTIONAL_ROUTE_KEYS)[number];\n\nexport async function analyzeAppManifestTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult | null> {\n const targetPath = path.resolve(request.cwd, request.inputPath ?? '.');\n const stats = await statOrNull(targetPath);\n\n if (\n stats === null ||\n !stats.isFile() ||\n path.extname(targetPath).toLowerCase() !== '.json' ||\n (request.mode !== 'validate' && request.mode !== 'fix')\n ) {\n return null;\n }\n\n const authReadiness = await analyzeAuthReadinessFile(targetPath);\n const diagnostics = [...(await analyzeAppManifestFile(targetPath)), ...authReadiness.diagnostics];\n const plannedChanges: DoctorPlannedChange[] = [];\n const fixPlan: DoctorFixPlan | null =\n request.mode === 'fix'\n ? {\n changes: plannedChanges,\n diagnostics,\n profile: MANIFEST_PROFILE,\n targetPath,\n }\n : null;\n\n return {\n appliedChecks: ['manifest'],\n diagnostics,\n fixPlan,\n hasPackageJson: false,\n plannedChanges,\n profile: MANIFEST_PROFILE,\n readiness: authReadiness.readiness,\n repoMarkers: [],\n targetPath,\n };\n}\n\nexport async function analyzeAppManifestFile(filePath: string): Promise<DoctorDiagnostic[]> {\n let source: string;\n\n try {\n source = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return [\n createManifestDiagnostic({\n code: 'invalid-app-manifest-json',\n message: `Could not read app manifest JSON: ${toErrorMessage(error)}`,\n path: filePath,\n ruleId: 'manifest.json.readable',\n }),\n ];\n }\n\n let manifest: unknown;\n\n try {\n manifest = JSON.parse(source) as unknown;\n } catch (error) {\n return [\n createManifestDiagnostic({\n code: 'invalid-app-manifest-json',\n message: `App manifest must contain valid JSON: ${toErrorMessage(error)}`,\n path: filePath,\n ruleId: 'manifest.json.valid',\n }),\n ];\n }\n\n return analyzeAppManifest(manifest, filePath);\n}\n\nexport function analyzeAppManifest(\n manifest: unknown,\n manifestPath = 'manifest.json',\n): DoctorDiagnostic[] {\n if (!isRecord(manifest)) {\n return [\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'App manifest root must be a JSON object.',\n path: manifestPath,\n ruleId: 'manifest.root.valid-shape',\n }),\n ];\n }\n\n const diagnostics: DoctorDiagnostic[] = [];\n const { settings } = manifest;\n\n if (isRecord(settings) && hasOwn(settings, 'authFlow')) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message:\n 'settings.authFlow was removed. Move this configuration manually to infra.auth.flow; Doctor will not migrate it automatically.',\n path: manifestPath,\n ruleId: 'manifest.settings.auth-flow.removed',\n }),\n );\n }\n\n const { infra } = manifest;\n if (infra === undefined) {\n return diagnostics;\n }\n\n if (!isRecord(infra)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.infra.valid-shape',\n }),\n );\n return diagnostics;\n }\n\n diagnostics.push(...analyzeSecretStoreManifest(infra, manifestPath));\n\n const { auth } = infra;\n if (auth === undefined) {\n return diagnostics;\n }\n\n if (!isRecord(auth)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra.auth must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.auth.valid-shape',\n }),\n );\n return diagnostics;\n }\n\n const flow = parseAuthFlow(auth.flow, manifestPath, diagnostics);\n if (flow !== null) {\n validateAuthFlowRelationships(flow, manifestPath, diagnostics);\n }\n\n validateAuthorization(auth.authorization, manifestPath, diagnostics);\n\n return diagnostics;\n}\n\nfunction parseAuthFlow(\n value: unknown,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): AuthFlowConfig | null {\n if (value === undefined) {\n return resolveAuthFlow();\n }\n\n if (!isRecord(value)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra.auth.flow must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.auth.flow.valid-shape',\n }),\n );\n return null;\n }\n\n const signInRoute = readRequiredRoute(value, 'signInRoute', manifestPath, diagnostics);\n const postSignInRoute = readRequiredRoute(value, 'postSignInRoute', manifestPath, diagnostics);\n const optionalRoutes = readOptionalRoutes(value, manifestPath, diagnostics);\n\n if (signInRoute === null || postSignInRoute === null || optionalRoutes === null) {\n return null;\n }\n\n return resolveAuthFlow({\n signInRoute,\n postSignInRoute,\n ...optionalRoutes,\n });\n}\n\nfunction readRequiredRoute(\n flow: Record<string, unknown>,\n key: 'postSignInRoute' | 'signInRoute',\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): string | null {\n const value = flow[key];\n\n if (typeof value !== 'string' || value.trim() === '') {\n diagnostics.push(\n createManifestDiagnostic({\n code: value === undefined ? 'field-missing' : 'field-invalid',\n message: `manifest.infra.auth.flow.${key} must be a non-empty string.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.required',\n }),\n );\n return null;\n }\n\n return value;\n}\n\nfunction readOptionalRoutes(\n flow: Record<string, unknown>,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): Pick<AuthFlowConfig, OptionalRouteKey> | null {\n const routes: Partial<Pick<AuthFlowConfig, OptionalRouteKey>> = {};\n let valid = true;\n\n for (const key of OPTIONAL_ROUTE_KEYS) {\n const value = flow[key];\n if (value === undefined) {\n continue;\n }\n\n if (typeof value !== 'string' || value.trim() === '') {\n valid = false;\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `manifest.infra.auth.flow.${key} must be a non-empty string when present.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.valid',\n }),\n );\n continue;\n }\n\n routes[key] = value;\n }\n\n return valid ? routes : null;\n}\n\nfunction validateAuthFlowRelationships(\n flow: AuthFlowConfig,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): void {\n const actionRoutes = AUTH_ACTION_ROUTE_KEYS.flatMap((key) => {\n const value = flow[key];\n return typeof value === 'string' ? [{ key, route: normalizeRoute(value) }] : [];\n });\n const routesByName = new Map<string, AuthActionRouteKey[]>();\n\n for (const entry of actionRoutes) {\n const keys = routesByName.get(entry.route) ?? [];\n keys.push(entry.key);\n routesByName.set(entry.route, keys);\n }\n\n for (const [route, keys] of routesByName) {\n if (keys.length < 2) {\n continue;\n }\n\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `Auth action routes must be unique; ${keys.join(', ')} all resolve to \"${route}\".`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.unique',\n }),\n );\n }\n\n const postSignInRoute = normalizeRoute(flow.postSignInRoute);\n const collidingAction = actionRoutes.find((entry) => entry.route === postSignInRoute);\n if (collidingAction !== undefined) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `postSignInRoute must resolve outside the auth action routes; it collides with ${collidingAction.key}.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.post-sign-in.distinct',\n }),\n );\n }\n\n if (flow.unauthorizedRoute === undefined) {\n return;\n }\n\n const unauthorizedRoute = normalizeRoute(flow.unauthorizedRoute);\n const signInRoute = normalizeRoute(flow.signInRoute);\n\n if (unauthorizedRoute === postSignInRoute) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'unauthorizedRoute must not resolve to postSignInRoute.',\n path: manifestPath,\n ruleId: 'manifest.auth.flow.unauthorized-route.valid',\n }),\n );\n }\n\n if (unauthorizedRoute === signInRoute) {\n return;\n }\n\n const collidingActionRoute = actionRoutes.find(\n (entry) => entry.key !== 'signInRoute' && entry.route === unauthorizedRoute,\n );\n if (collidingActionRoute !== undefined) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `unauthorizedRoute may alias signInRoute, but it must not collide with ${collidingActionRoute.key}.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.unauthorized-route.valid',\n }),\n );\n }\n}\n\nfunction validateAuthorization(\n value: unknown,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): void {\n if (value === undefined) {\n return;\n }\n\n if (!isRecord(value)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message:\n 'manifest.infra.auth.authorization must be a JSON object when explicitly configured.',\n path: manifestPath,\n ruleId: 'manifest.auth.authorization.valid-shape',\n }),\n );\n return;\n }\n\n validateAuthorizationField({\n allowed: AUTHZ_KINDS,\n diagnostics,\n fieldName: 'kind',\n manifestPath,\n value: value.kind,\n ruleId: 'manifest.auth.authorization.kind.valid',\n });\n validateAuthorizationField({\n allowed: AUTHZ_ENGINES,\n diagnostics,\n fieldName: 'engine',\n manifestPath,\n value: value.engine,\n ruleId: 'manifest.auth.authorization.engine.valid',\n });\n}\n\nfunction validateAuthorizationField(request: {\n readonly allowed: readonly string[];\n readonly diagnostics: DoctorDiagnostic[];\n readonly fieldName: 'engine' | 'kind';\n readonly manifestPath: string;\n readonly ruleId: DoctorRuleId;\n readonly value: unknown;\n}): void {\n if (typeof request.value === 'string' && request.allowed.includes(request.value)) {\n return;\n }\n\n request.diagnostics.push(\n createManifestDiagnostic({\n code: request.value === undefined ? 'field-missing' : 'field-invalid',\n message: `Explicit authorization.${request.fieldName} must be one of: ${request.allowed.join(', ')}.`,\n path: request.manifestPath,\n ruleId: request.ruleId,\n }),\n );\n}\n\nfunction createManifestDiagnostic(request: {\n readonly code: DoctorDiagnosticCode;\n readonly message: string;\n readonly path: string;\n readonly ruleId: DoctorRuleId;\n}): DoctorDiagnostic {\n return {\n ...request,\n profile: MANIFEST_PROFILE,\n severity: 'error',\n };\n}\n\nfunction normalizeRoute(route: string): string {\n const normalized = route.trim().replace(/^\\/+/, '').replace(/\\/+$/, '');\n return normalized === '' ? 'index' : normalized;\n}\n\nfunction hasOwn(record: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nasync function statOrNull(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch {\n return null;\n }\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manifestAnalysis.js","sourceRoot":"","sources":["../src/manifestAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { type AuthFlowConfig, resolveAuthFlow } from '@ankhorage/contracts/auth';\n\nimport type {\n DoctorAnalysisRequest,\n DoctorAnalysisResult,\n DoctorFixPlan,\n DoctorPlannedChange,\n} from './analysis.js';\nimport { analyzeAuthReadinessFile } from './authReadinessFileAnalysis.js';\nimport type {\n DoctorDiagnostic,\n DoctorDiagnosticCode,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\nimport { analyzeSecretStoreManifest } from './secretManifestAnalysis.js';\n\nconst AUTHZ_KINDS = ['RBAC', 'ABAC'] as const;\nconst MANIFEST_PROFILE = 'app-manifest' satisfies DoctorPolicyProfile;\nconst AUTH_ACTION_ROUTE_KEYS = [\n 'signInRoute',\n 'signUpRoute',\n 'signOutRoute',\n 'forgotPasswordRoute',\n 'otpRoute',\n] as const satisfies readonly (keyof AuthFlowConfig)[];\nconst OPTIONAL_ROUTE_KEYS = [\n 'signUpRoute',\n 'signOutRoute',\n 'forgotPasswordRoute',\n 'otpRoute',\n 'unauthorizedRoute',\n] as const satisfies readonly (keyof AuthFlowConfig)[];\n\ntype AuthActionRouteKey = (typeof AUTH_ACTION_ROUTE_KEYS)[number];\ntype OptionalRouteKey = (typeof OPTIONAL_ROUTE_KEYS)[number];\n\nexport async function analyzeAppManifestTarget(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult | null> {\n const targetPath = path.resolve(request.cwd, request.inputPath ?? '.');\n const stats = await statOrNull(targetPath);\n\n if (\n stats === null ||\n !stats.isFile() ||\n path.extname(targetPath).toLowerCase() !== '.json' ||\n (request.mode !== 'validate' && request.mode !== 'fix')\n ) {\n return null;\n }\n\n const authReadiness = await analyzeAuthReadinessFile(targetPath);\n const diagnostics = [...(await analyzeAppManifestFile(targetPath)), ...authReadiness.diagnostics];\n const plannedChanges: DoctorPlannedChange[] = [];\n const fixPlan: DoctorFixPlan | null =\n request.mode === 'fix'\n ? {\n changes: plannedChanges,\n diagnostics,\n profile: MANIFEST_PROFILE,\n targetPath,\n }\n : null;\n\n return {\n appliedChecks: ['manifest'],\n diagnostics,\n fixPlan,\n hasPackageJson: false,\n plannedChanges,\n profile: MANIFEST_PROFILE,\n readiness: authReadiness.readiness,\n repoMarkers: [],\n targetPath,\n };\n}\n\nexport async function analyzeAppManifestFile(filePath: string): Promise<DoctorDiagnostic[]> {\n let source: string;\n\n try {\n source = await fs.readFile(filePath, 'utf8');\n } catch (error) {\n return [\n createManifestDiagnostic({\n code: 'invalid-app-manifest-json',\n message: `Could not read app manifest JSON: ${toErrorMessage(error)}`,\n path: filePath,\n ruleId: 'manifest.json.readable',\n }),\n ];\n }\n\n let manifest: unknown;\n\n try {\n manifest = JSON.parse(source) as unknown;\n } catch (error) {\n return [\n createManifestDiagnostic({\n code: 'invalid-app-manifest-json',\n message: `App manifest must contain valid JSON: ${toErrorMessage(error)}`,\n path: filePath,\n ruleId: 'manifest.json.valid',\n }),\n ];\n }\n\n return analyzeAppManifest(manifest, filePath);\n}\n\nexport function analyzeAppManifest(\n manifest: unknown,\n manifestPath = 'manifest.json',\n): DoctorDiagnostic[] {\n if (!isRecord(manifest)) {\n return [\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'App manifest root must be a JSON object.',\n path: manifestPath,\n ruleId: 'manifest.root.valid-shape',\n }),\n ];\n }\n\n const diagnostics: DoctorDiagnostic[] = [];\n const { settings } = manifest;\n\n if (isRecord(settings) && hasOwn(settings, 'authFlow')) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message:\n 'settings.authFlow was removed. Move this configuration manually to infra.auth.flow; Doctor will not migrate it automatically.',\n path: manifestPath,\n ruleId: 'manifest.settings.auth-flow.removed',\n }),\n );\n }\n\n const { infra } = manifest;\n if (infra === undefined) {\n return diagnostics;\n }\n\n if (!isRecord(infra)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.infra.valid-shape',\n }),\n );\n return diagnostics;\n }\n\n diagnostics.push(...analyzeSecretStoreManifest(infra, manifestPath));\n\n const { auth } = infra;\n if (auth === undefined) {\n return diagnostics;\n }\n\n if (!isRecord(auth)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra.auth must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.auth.valid-shape',\n }),\n );\n return diagnostics;\n }\n\n const flow = parseAuthFlow(auth.flow, manifestPath, diagnostics);\n if (flow !== null) {\n validateAuthFlowRelationships(flow, manifestPath, diagnostics);\n }\n\n validateAuthorization(auth.authorization, manifestPath, diagnostics);\n\n return diagnostics;\n}\n\nfunction parseAuthFlow(\n value: unknown,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): AuthFlowConfig | null {\n if (value === undefined) {\n return resolveAuthFlow();\n }\n\n if (!isRecord(value)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'manifest.infra.auth.flow must be a JSON object when present.',\n path: manifestPath,\n ruleId: 'manifest.auth.flow.valid-shape',\n }),\n );\n return null;\n }\n\n const signInRoute = readRequiredRoute(value, 'signInRoute', manifestPath, diagnostics);\n const postSignInRoute = readRequiredRoute(value, 'postSignInRoute', manifestPath, diagnostics);\n const optionalRoutes = readOptionalRoutes(value, manifestPath, diagnostics);\n\n if (signInRoute === null || postSignInRoute === null || optionalRoutes === null) {\n return null;\n }\n\n return resolveAuthFlow({\n signInRoute,\n postSignInRoute,\n ...optionalRoutes,\n });\n}\n\nfunction readRequiredRoute(\n flow: Record<string, unknown>,\n key: 'postSignInRoute' | 'signInRoute',\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): string | null {\n const value = flow[key];\n\n if (typeof value !== 'string' || value.trim() === '') {\n diagnostics.push(\n createManifestDiagnostic({\n code: value === undefined ? 'field-missing' : 'field-invalid',\n message: `manifest.infra.auth.flow.${key} must be a non-empty string.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.required',\n }),\n );\n return null;\n }\n\n return value;\n}\n\nfunction readOptionalRoutes(\n flow: Record<string, unknown>,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): Pick<AuthFlowConfig, OptionalRouteKey> | null {\n const routes: Partial<Pick<AuthFlowConfig, OptionalRouteKey>> = {};\n let valid = true;\n\n for (const key of OPTIONAL_ROUTE_KEYS) {\n const value = flow[key];\n if (value === undefined) {\n continue;\n }\n\n if (typeof value !== 'string' || value.trim() === '') {\n valid = false;\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `manifest.infra.auth.flow.${key} must be a non-empty string when present.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.valid',\n }),\n );\n continue;\n }\n\n routes[key] = value;\n }\n\n return valid ? routes : null;\n}\n\nfunction validateAuthFlowRelationships(\n flow: AuthFlowConfig,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): void {\n const actionRoutes = AUTH_ACTION_ROUTE_KEYS.flatMap((key) => {\n const value = flow[key];\n return typeof value === 'string' ? [{ key, route: normalizeRoute(value) }] : [];\n });\n const routesByName = new Map<string, AuthActionRouteKey[]>();\n\n for (const entry of actionRoutes) {\n const keys = routesByName.get(entry.route) ?? [];\n keys.push(entry.key);\n routesByName.set(entry.route, keys);\n }\n\n for (const [route, keys] of routesByName) {\n if (keys.length < 2) {\n continue;\n }\n\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `Auth action routes must be unique; ${keys.join(', ')} all resolve to \"${route}\".`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.route.unique',\n }),\n );\n }\n\n const postSignInRoute = normalizeRoute(flow.postSignInRoute);\n const collidingAction = actionRoutes.find((entry) => entry.route === postSignInRoute);\n if (collidingAction !== undefined) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `postSignInRoute must resolve outside the auth action routes; it collides with ${collidingAction.key}.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.post-sign-in.distinct',\n }),\n );\n }\n\n if (flow.unauthorizedRoute === undefined) {\n return;\n }\n\n const unauthorizedRoute = normalizeRoute(flow.unauthorizedRoute);\n const signInRoute = normalizeRoute(flow.signInRoute);\n\n if (unauthorizedRoute === postSignInRoute) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: 'unauthorizedRoute must not resolve to postSignInRoute.',\n path: manifestPath,\n ruleId: 'manifest.auth.flow.unauthorized-route.valid',\n }),\n );\n }\n\n if (unauthorizedRoute === signInRoute) {\n return;\n }\n\n const collidingActionRoute = actionRoutes.find(\n (entry) => entry.key !== 'signInRoute' && entry.route === unauthorizedRoute,\n );\n if (collidingActionRoute !== undefined) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message: `unauthorizedRoute may alias signInRoute, but it must not collide with ${collidingActionRoute.key}.`,\n path: manifestPath,\n ruleId: 'manifest.auth.flow.unauthorized-route.valid',\n }),\n );\n }\n}\n\nfunction validateAuthorization(\n value: unknown,\n manifestPath: string,\n diagnostics: DoctorDiagnostic[],\n): void {\n if (value === undefined) {\n return;\n }\n\n if (!isRecord(value)) {\n diagnostics.push(\n createManifestDiagnostic({\n code: 'field-invalid',\n message:\n 'manifest.infra.auth.authorization must be a JSON object when explicitly configured.',\n path: manifestPath,\n ruleId: 'manifest.auth.authorization.valid-shape',\n }),\n );\n return;\n }\n\n validateAuthorizationField({\n allowed: AUTHZ_KINDS,\n diagnostics,\n fieldName: 'kind',\n manifestPath,\n value: value.kind,\n ruleId: 'manifest.auth.authorization.kind.valid',\n });\n}\n\nfunction validateAuthorizationField(request: {\n readonly allowed: readonly string[];\n readonly diagnostics: DoctorDiagnostic[];\n readonly fieldName: 'engine' | 'kind';\n readonly manifestPath: string;\n readonly ruleId: DoctorRuleId;\n readonly value: unknown;\n}): void {\n if (typeof request.value === 'string' && request.allowed.includes(request.value)) {\n return;\n }\n\n request.diagnostics.push(\n createManifestDiagnostic({\n code: request.value === undefined ? 'field-missing' : 'field-invalid',\n message: `Explicit authorization.${request.fieldName} must be one of: ${request.allowed.join(', ')}.`,\n path: request.manifestPath,\n ruleId: request.ruleId,\n }),\n );\n}\n\nfunction createManifestDiagnostic(request: {\n readonly code: DoctorDiagnosticCode;\n readonly message: string;\n readonly path: string;\n readonly ruleId: DoctorRuleId;\n}): DoctorDiagnostic {\n return {\n ...request,\n profile: MANIFEST_PROFILE,\n severity: 'error',\n };\n}\n\nfunction normalizeRoute(route: string): string {\n const normalized = route.trim().replace(/^\\/+/, '').replace(/\\/+$/, '');\n return normalized === '' ? 'index' : normalized;\n}\n\nfunction hasOwn(record: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nasync function statOrNull(filePath: string) {\n try {\n return await fs.stat(filePath);\n } catch {\n return null;\n }\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
package/dist/readiness.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { AuthOAuthProviderId, AuthOAuthTransportId } from '@ankhorage/contracts/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
3
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
3
4
|
export type DoctorReadinessStatus = 'missing' | 'ready' | 'unsupported';
|
|
4
5
|
export interface DoctorReadiness {
|
|
5
6
|
readonly callbackScheme?: string;
|
|
6
7
|
readonly category: 'auth-oauth';
|
|
7
|
-
readonly environment:
|
|
8
|
+
readonly environment: AppEnvironmentId;
|
|
8
9
|
readonly hostRequirement?: 'development-or-standalone-build';
|
|
9
10
|
readonly message: string;
|
|
10
11
|
readonly provider: AuthOAuthProviderId;
|
package/dist/readiness.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,iCAAiC,CAAC;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;CAC1C"}
|
package/dist/readiness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"","sourcesContent":["import type { AuthOAuthProviderId, AuthOAuthTransportId } from '@ankhorage/contracts/auth';\nimport type {
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"","sourcesContent":["import type { AuthOAuthProviderId, AuthOAuthTransportId } from '@ankhorage/contracts/auth';\nimport type { AppDeployTargetId } from '@ankhorage/contracts/deploy';\nimport type { AppEnvironmentId } from '@ankhorage/contracts/environments';\n\nexport type DoctorReadinessStatus = 'missing' | 'ready' | 'unsupported';\n\nexport interface DoctorReadiness {\n readonly callbackScheme?: string;\n readonly category: 'auth-oauth';\n readonly environment: AppEnvironmentId;\n readonly hostRequirement?: 'development-or-standalone-build';\n readonly message: string;\n readonly provider: AuthOAuthProviderId;\n readonly status: DoctorReadinessStatus;\n readonly target: AppDeployTargetId;\n readonly transport: AuthOAuthTransportId;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/doctor",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.14",
|
|
4
4
|
"description": "Executable Ankh provider and standalone CLI for lightweight repo and package compliance diagnostics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"knip:check": "ankhorage-knip"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@ankhorage/contracts": "^
|
|
80
|
+
"@ankhorage/contracts": "^19.1.0",
|
|
81
81
|
"@ankhorage/supabase-auth": "^1.2.11"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { AuthOAuthSetupPlan } from '@ankhorage/contracts/auth';
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
AppDeployTargetId,
|
|
5
|
-
AppDeployTargets,
|
|
6
|
-
} from '@ankhorage/contracts/deploy';
|
|
2
|
+
import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';
|
|
3
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
7
4
|
|
|
8
5
|
import {
|
|
9
6
|
createAuthReadinessMessage,
|
|
@@ -16,7 +13,7 @@ export function analyzeAuthReadinessEnvironment(input: {
|
|
|
16
13
|
readonly callbackRoute: unknown;
|
|
17
14
|
readonly credentialsRef: unknown;
|
|
18
15
|
readonly enabledTargets: readonly AppDeployTargetId[];
|
|
19
|
-
readonly environment:
|
|
16
|
+
readonly environment: AppEnvironmentId;
|
|
20
17
|
readonly plan: AuthOAuthSetupPlan;
|
|
21
18
|
readonly provider: string;
|
|
22
19
|
readonly targets: AppDeployTargets;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';
|
|
2
|
-
import {
|
|
2
|
+
import { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';
|
|
3
3
|
|
|
4
4
|
import { createNativeAuthCallbackDiagnostic } from './authReadinessNativeDiagnostic.js';
|
|
5
5
|
import { analyzeAuthProviderEnvironment } from './authReadinessProviderEnvironment.js';
|
|
@@ -32,7 +32,7 @@ export function analyzeAuthProviderReadiness(input: {
|
|
|
32
32
|
const missingNativeTargets = new Set<AppDeployTargetId>();
|
|
33
33
|
let unsupported = false;
|
|
34
34
|
|
|
35
|
-
for (const environment of
|
|
35
|
+
for (const environment of APP_ENVIRONMENT_IDS) {
|
|
36
36
|
const result = analyzeAuthProviderEnvironment({
|
|
37
37
|
callbackRoute: input.callbackRoute,
|
|
38
38
|
credentialsRef: input.provider.credentialsRef,
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
AppDeployTargetId,
|
|
4
|
-
AppDeployTargets,
|
|
5
|
-
} from '@ankhorage/contracts/deploy';
|
|
1
|
+
import type { AppDeployTargetId, AppDeployTargets } from '@ankhorage/contracts/deploy';
|
|
2
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
6
3
|
import { resolveSupabaseOAuthSetupPlan } from '@ankhorage/supabase-auth';
|
|
7
4
|
|
|
8
5
|
import { analyzeAuthReadinessEnvironment } from './authReadinessEnvironment.js';
|
|
@@ -13,7 +10,7 @@ export function analyzeAuthProviderEnvironment(input: {
|
|
|
13
10
|
readonly callbackRoute: unknown;
|
|
14
11
|
readonly credentialsRef: unknown;
|
|
15
12
|
readonly enabledTargets: readonly AppDeployTargetId[];
|
|
16
|
-
readonly environment:
|
|
13
|
+
readonly environment: AppEnvironmentId;
|
|
17
14
|
readonly provider: string;
|
|
18
15
|
readonly targets: AppDeployTargets;
|
|
19
16
|
}): {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
2
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
2
3
|
|
|
3
4
|
import { createAuthReadinessDiagnostic } from './authReadinessDiagnostic.js';
|
|
4
5
|
import type { DoctorDiagnostic } from './diagnostics.js';
|
|
5
6
|
import type { DoctorReadiness } from './readiness.js';
|
|
6
7
|
|
|
7
8
|
export function createUnsupportedAuthReadiness(input: {
|
|
8
|
-
readonly environment:
|
|
9
|
+
readonly environment: AppEnvironmentId;
|
|
9
10
|
readonly provider: string;
|
|
10
11
|
readonly targets: readonly AppDeployTargetId[];
|
|
11
12
|
}): DoctorReadiness[] {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
2
|
-
import {
|
|
2
|
+
import { APP_ENVIRONMENT_IDS } from '@ankhorage/contracts/environments';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
createUnsupportedAuthDiagnostic,
|
|
@@ -23,7 +23,7 @@ export function analyzeUnsupportedAuthBackend(input: {
|
|
|
23
23
|
path: input.manifestPath,
|
|
24
24
|
}),
|
|
25
25
|
],
|
|
26
|
-
readiness:
|
|
26
|
+
readiness: APP_ENVIRONMENT_IDS.flatMap((environment) =>
|
|
27
27
|
createUnsupportedAuthReadiness({
|
|
28
28
|
environment,
|
|
29
29
|
provider: input.provider,
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
1
|
import {
|
|
5
2
|
analyzeDoctorTarget as analyzeBaseDoctorTarget,
|
|
6
3
|
type DoctorAnalysisRequest,
|
|
@@ -8,112 +5,30 @@ import {
|
|
|
8
5
|
} from './analysis.js';
|
|
9
6
|
import { applyBunRuntimePolicy } from './bunPolicyAnalysis.js';
|
|
10
7
|
import { analyzeDoctorTargetWithCliLayout as analyzeBaseDoctorTargetWithCliLayout } from './cliLayoutAnalysis.js';
|
|
11
|
-
import type { DoctorDiagnostic } from './diagnostics.js';
|
|
12
|
-
|
|
13
|
-
const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
|
|
14
|
-
const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
|
|
15
|
-
const DEVTOOLS_REQUIRED_RULE_ID = 'package.dependencies.devtools.required';
|
|
16
8
|
|
|
9
|
+
/***
|
|
10
|
+
* Analyze a Doctor target and apply repository-level dependency and runtime policies.
|
|
11
|
+
*/
|
|
17
12
|
export async function analyzeDoctorTarget(
|
|
18
13
|
request: DoctorAnalysisRequest,
|
|
19
14
|
): Promise<DoctorAnalysisResult> {
|
|
20
15
|
return applyRepositoryPolicies(await analyzeBaseDoctorTarget(request));
|
|
21
16
|
}
|
|
22
17
|
|
|
18
|
+
/***
|
|
19
|
+
* Analyze a Doctor target with CLI layout validation and repository-level policies.
|
|
20
|
+
*/
|
|
23
21
|
export async function analyzeDoctorTargetWithCliLayout(
|
|
24
22
|
request: DoctorAnalysisRequest,
|
|
25
23
|
): Promise<DoctorAnalysisResult> {
|
|
26
24
|
return applyRepositoryPolicies(await analyzeBaseDoctorTargetWithCliLayout(request));
|
|
27
25
|
}
|
|
28
26
|
|
|
27
|
+
/***
|
|
28
|
+
* Apply repository policies that remain independent from concrete package identities.
|
|
29
|
+
*/
|
|
29
30
|
async function applyRepositoryPolicies(
|
|
30
31
|
result: DoctorAnalysisResult,
|
|
31
32
|
): Promise<DoctorAnalysisResult> {
|
|
32
|
-
return applyBunRuntimePolicy(
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function applyDevtoolsDependencyPlacementPolicy(
|
|
36
|
-
result: DoctorAnalysisResult,
|
|
37
|
-
): Promise<DoctorAnalysisResult> {
|
|
38
|
-
if (!result.hasPackageJson) {
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const packageJsonPath = path.join(result.targetPath, 'package.json');
|
|
43
|
-
const packageJson = await readPackageJson(packageJsonPath);
|
|
44
|
-
if (packageJson?.name !== ANKH_PACKAGE_NAME || !expectsDevtools(packageJson)) {
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : null;
|
|
49
|
-
const hasRuntimeDevtools = hasDependency(dependencies, DEVTOOLS_PACKAGE_NAME);
|
|
50
|
-
const diagnosticsWithoutPlacementRule = result.diagnostics.filter(
|
|
51
|
-
(diagnostic) => diagnostic.ruleId !== DEVTOOLS_REQUIRED_RULE_ID,
|
|
52
|
-
);
|
|
53
|
-
const diagnostics = hasRuntimeDevtools
|
|
54
|
-
? diagnosticsWithoutPlacementRule
|
|
55
|
-
: [
|
|
56
|
-
...diagnosticsWithoutPlacementRule,
|
|
57
|
-
createMissingRuntimeDevtoolsDiagnostic(packageJsonPath, result),
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
...result,
|
|
62
|
-
diagnostics,
|
|
63
|
-
fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {
|
|
68
|
-
try {
|
|
69
|
-
const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;
|
|
70
|
-
return isRecord(parsed) ? parsed : null;
|
|
71
|
-
} catch {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createMissingRuntimeDevtoolsDiagnostic(
|
|
77
|
-
packageJsonPath: string,
|
|
78
|
-
result: DoctorAnalysisResult,
|
|
79
|
-
): DoctorDiagnostic {
|
|
80
|
-
return {
|
|
81
|
-
code: 'missing-dependency',
|
|
82
|
-
message:
|
|
83
|
-
'@ankhorage/ankh bundles @ankhorage/devtools as a runtime core provider and must declare it in dependencies.',
|
|
84
|
-
path: packageJsonPath,
|
|
85
|
-
profile: result.profile,
|
|
86
|
-
ruleId: DEVTOOLS_REQUIRED_RULE_ID,
|
|
87
|
-
severity: 'error',
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function expectsDevtools(packageJson: Record<string, unknown>): boolean {
|
|
92
|
-
const scripts = isRecord(packageJson.scripts) ? packageJson.scripts : null;
|
|
93
|
-
if (scripts === null) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return Object.values(scripts).some(
|
|
98
|
-
(scriptValue) =>
|
|
99
|
-
isNonEmptyString(scriptValue) &&
|
|
100
|
-
(scriptValue.includes('ankhorage-eslint') ||
|
|
101
|
-
scriptValue.includes('ankhorage-prettier') ||
|
|
102
|
-
scriptValue.includes('ankhorage-knip')),
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function hasDependency(
|
|
107
|
-
dependencyMap: Record<string, unknown> | null,
|
|
108
|
-
dependencyName: string,
|
|
109
|
-
): boolean {
|
|
110
|
-
return dependencyMap !== null && isNonEmptyString(dependencyMap[dependencyName]);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function isNonEmptyString(value: unknown): value is string {
|
|
114
|
-
return typeof value === 'string' && value.trim() !== '';
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
118
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
33
|
+
return applyBunRuntimePolicy(result);
|
|
119
34
|
}
|
package/src/manifestAnalysis.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import { AUTHZ_ENGINES, AUTHZ_KINDS } from '@ankhorage/contracts';
|
|
5
4
|
import { type AuthFlowConfig, resolveAuthFlow } from '@ankhorage/contracts/auth';
|
|
6
5
|
|
|
7
6
|
import type {
|
|
@@ -19,6 +18,7 @@ import type {
|
|
|
19
18
|
} from './diagnostics.js';
|
|
20
19
|
import { analyzeSecretStoreManifest } from './secretManifestAnalysis.js';
|
|
21
20
|
|
|
21
|
+
const AUTHZ_KINDS = ['RBAC', 'ABAC'] as const;
|
|
22
22
|
const MANIFEST_PROFILE = 'app-manifest' satisfies DoctorPolicyProfile;
|
|
23
23
|
const AUTH_ACTION_ROUTE_KEYS = [
|
|
24
24
|
'signInRoute',
|
|
@@ -393,14 +393,6 @@ function validateAuthorization(
|
|
|
393
393
|
value: value.kind,
|
|
394
394
|
ruleId: 'manifest.auth.authorization.kind.valid',
|
|
395
395
|
});
|
|
396
|
-
validateAuthorizationField({
|
|
397
|
-
allowed: AUTHZ_ENGINES,
|
|
398
|
-
diagnostics,
|
|
399
|
-
fieldName: 'engine',
|
|
400
|
-
manifestPath,
|
|
401
|
-
value: value.engine,
|
|
402
|
-
ruleId: 'manifest.auth.authorization.engine.valid',
|
|
403
|
-
});
|
|
404
396
|
}
|
|
405
397
|
|
|
406
398
|
function validateAuthorizationField(request: {
|
package/src/readiness.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { AuthOAuthProviderId, AuthOAuthTransportId } from '@ankhorage/contracts/auth';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AppDeployTargetId } from '@ankhorage/contracts/deploy';
|
|
3
|
+
import type { AppEnvironmentId } from '@ankhorage/contracts/environments';
|
|
3
4
|
|
|
4
5
|
export type DoctorReadinessStatus = 'missing' | 'ready' | 'unsupported';
|
|
5
6
|
|
|
6
7
|
export interface DoctorReadiness {
|
|
7
8
|
readonly callbackScheme?: string;
|
|
8
9
|
readonly category: 'auth-oauth';
|
|
9
|
-
readonly environment:
|
|
10
|
+
readonly environment: AppEnvironmentId;
|
|
10
11
|
readonly hostRequirement?: 'development-or-standalone-build';
|
|
11
12
|
readonly message: string;
|
|
12
13
|
readonly provider: AuthOAuthProviderId;
|