@coreframe/diagnostics 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/src/collectors/git.js +2 -0
- package/dist/src/collectors/index.d.ts +35 -0
- package/dist/src/collectors/index.js +1 -0
- package/dist/src/collectors/launch-readiness.js +1 -0
- package/dist/src/collectors/links.js +1 -0
- package/dist/src/collectors/package-json.js +1 -0
- package/dist/src/collectors.d.ts +21 -0
- package/dist/src/collectors.js +1 -0
- package/dist/src/evaluators/actions.js +1 -0
- package/dist/src/evaluators/index.d.ts +44 -0
- package/dist/src/evaluators/index.js +1 -0
- package/dist/src/evaluators/launch-scope.js +1 -0
- package/dist/src/evaluators/legal-review.js +1 -0
- package/dist/src/evaluators/product-identity.js +1 -0
- package/dist/src/evaluators.d.ts +12 -0
- package/dist/src/evaluators.js +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +1 -0
- package/dist/src/run.d.ts +12 -0
- package/dist/src/run.js +1 -0
- package/dist/src/types.d.ts +113 -0
- package/dist/src/view-model.d.ts +7 -0
- package/dist/src/view-model.js +1 -0
- package/package.json +29 -10
- package/readme.md +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @coreframe/diagnostics
|
|
2
|
+
|
|
3
|
+
Production-readiness diagnostics engine for Coreframe projects.
|
|
4
|
+
|
|
5
|
+
The package is intentionally UI-independent. It gathers local project facts,
|
|
6
|
+
normalizes them into a project snapshot, evaluates the snapshot into findings,
|
|
7
|
+
and builds action-oriented view models that can later be consumed by
|
|
8
|
+
`@coreframe/scripts` and `@coreframe/dashboard`.
|
|
9
|
+
|
|
10
|
+
This package is pre-alpha. Its public contracts are shaped for experimentation
|
|
11
|
+
before production command paths or dashboard UI depend on them.
|
|
12
|
+
|
|
13
|
+
Diagnostics focus on the work that is not completed by the generated Coreframe
|
|
14
|
+
template: human decisions, production-facing defaults, provider-console work,
|
|
15
|
+
and evidence needed before launch. They do not emit findings merely to confirm
|
|
16
|
+
that template-owned files, package scripts, or local defaults still exist.
|
|
17
|
+
|
|
18
|
+
A healthy check should answer at least one of these questions:
|
|
19
|
+
|
|
20
|
+
- What decision has not been made?
|
|
21
|
+
- What manual task still needs an owner?
|
|
22
|
+
- What production-facing default still needs personalization?
|
|
23
|
+
- What evidence would let a human call this ready?
|
|
24
|
+
|
|
25
|
+
Return no finding when there is no useful action. Local commands and passive
|
|
26
|
+
context can still be exposed as dashboard controls or facts without becoming
|
|
27
|
+
readiness findings.
|
|
28
|
+
|
|
29
|
+
## Pipeline
|
|
30
|
+
|
|
31
|
+
1. Create a `ProjectContext`.
|
|
32
|
+
2. Run collectors to gather deterministic project facts.
|
|
33
|
+
3. Build a normalized `ProjectSnapshot`.
|
|
34
|
+
4. Run evaluators to produce prioritized findings.
|
|
35
|
+
5. Attach safe commands, copyable agent prompts, and links.
|
|
36
|
+
6. Build a dashboard-oriented view model from the same diagnostic result.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { runDiagnostics } from "@coreframe/diagnostics"
|
|
40
|
+
|
|
41
|
+
const result = await runDiagnostics({ projectRoot: process.cwd() })
|
|
42
|
+
|
|
43
|
+
console.log(result.dashboard.headline.message)
|
|
44
|
+
console.log(result.dashboard.launchItems)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Collectors should prefer local, low-latency signals. A failed collector should
|
|
48
|
+
not fail the whole diagnostic run. Collectors must not inspect secret values.
|
|
49
|
+
|
|
50
|
+
## Adding Checks
|
|
51
|
+
|
|
52
|
+
Default collectors and evaluators live in one module per check.
|
|
53
|
+
|
|
54
|
+
- Add collectors under `src/collectors/` and register them in
|
|
55
|
+
`src/collectors/index.ts`.
|
|
56
|
+
- Add evaluators under `src/evaluators/` and register them in
|
|
57
|
+
`src/evaluators/index.ts`.
|
|
58
|
+
|
|
59
|
+
The registry files intentionally use explicit imports instead of glob imports so
|
|
60
|
+
the package stays easy to run through Node, Vitest, and the library build.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{execFile as e}from"node:child_process";import{promisify as t}from"node:util";const n=t(e),r={id:`project.git`,label:`Git state`,collect:async e=>({git:await i(e.projectRoot)})};async function i(e){let[t,n,r]=await Promise.all([a([`branch`,`--show-current`],e),a([`rev-parse`,`--short`,`HEAD`],e),a([`status`,`--short`],e)]),i=r.split(`
|
|
2
|
+
`).map(e=>e.trim()).filter(Boolean).length;return{available:!0,branch:t.trim()||void 0,shortCommit:n.trim()||void 0,dirty:i>0,changedFiles:i}}async function a(e,t){let{stdout:r}=await n(`git`,e,{cwd:t});return r}export{r as default};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { GitFact, LaunchReadinessFact, ProjectContext, ScriptsFact, ToolLink } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/collectors/index.d.ts
|
|
4
|
+
declare const defaultCollectors: ({
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
collect: (context: ProjectContext) => Promise<{
|
|
8
|
+
package: {
|
|
9
|
+
name: string | undefined;
|
|
10
|
+
version: string | undefined;
|
|
11
|
+
private: boolean | undefined;
|
|
12
|
+
};
|
|
13
|
+
scripts: ScriptsFact;
|
|
14
|
+
}>;
|
|
15
|
+
} | {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
collect: (context: ProjectContext) => Promise<{
|
|
19
|
+
git: GitFact;
|
|
20
|
+
}>;
|
|
21
|
+
} | {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
collect: (context: ProjectContext) => Promise<{
|
|
25
|
+
launchReadiness: LaunchReadinessFact;
|
|
26
|
+
}>;
|
|
27
|
+
} | {
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
collect: (context: ProjectContext) => Promise<{
|
|
31
|
+
links: ToolLink[];
|
|
32
|
+
}>;
|
|
33
|
+
})[];
|
|
34
|
+
//#endregion
|
|
35
|
+
export { defaultCollectors };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./git.js";import t from"./launch-readiness.js";import n from"./links.js";import r from"./package-json.js";const i=[r,e,t,n];export{i as defaultCollectors};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as e}from"node:fs/promises";import{join as t}from"node:path";const n={id:`launch.readiness`,label:`Launch readiness`,collect:async e=>({launchReadiness:await r(e.projectRoot)})};async function r(e){let[n,r,s,c,l,u,d]=await Promise.all([o(t(e,`docs/production-checklist.md`)),o(t(e,`src/web/root.tsx`)),o(t(e,`tauri/src-tauri/tauri.conf.json`)),o(t(e,`wrangler.jsonc`)),o(t(e,`content/legal/en/privacy.md`)),o(t(e,`content/legal/en/terms.md`)),o(t(e,`docs/privacy-security-notes.md`))]),f=a(s,`identifier`);return{productionChecklistExists:n!==void 0,defaultIdentity:[i(r?.includes(`<title>Coreframe</title>`),`Web title`,`Coreframe`),i(s?.includes(`"productName": "Coreframe"`),`App name`,`Coreframe`),i(f===`com.coreframe.app`||f===`com.example`||f?.startsWith(`com.example.`),`App identifier`,f),i(c?.includes(`"name": "api"`),`Worker name`,`api`)].filter(e=>e!==void 0),defaultLegalDocuments:[l?.includes(`Replace this template copy`)?`Privacy policy`:void 0,u?.includes(`Replace this template copy`)?`Terms of service`:void 0].filter(e=>e!==void 0),privacyReviewRecorded:d!==void 0&&!d.includes(`No final-pass review recorded yet`)&&/^- Date:\s*\S+/mu.test(d)}}function i(e,t,n){return e?{label:t,value:n}:void 0}function a(e,t){if(e)return RegExp(`"${t}"\\s*:\\s*"([^"]+)"`,`u`).exec(e)?.[1]}async function o(t){try{return await e(t,`utf8`)}catch(e){if(s(e)&&e.code===`ENOENT`)return;throw e}}function s(e){return e instanceof Error&&`code`in e}export{n as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={id:`project.links`,label:`Configured links`,collect:async e=>({links:t(e.env??process.env)})};function t(e){let t=[];return e.POSTHOG_PROJECT_URL&&t.push({id:`posthog`,label:`PostHog`,href:e.POSTHOG_PROJECT_URL,area:`integration`}),e.CLOUDFLARE_DASHBOARD_URL&&t.push({id:`cloudflare`,label:`Cloudflare`,href:e.CLOUDFLARE_DASHBOARD_URL,area:`deploy`}),e.DRIZZLE_STUDIO_URL&&t.push({id:`drizzle-studio`,label:`Drizzle Studio`,href:e.DRIZZLE_STUDIO_URL,area:`database`}),t}export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as e}from"node:fs/promises";import{join as t}from"node:path";const n={id:`project.package`,label:`Package identity`,collect:async e=>{let n=await i(t(e.projectRoot,`package.json`));return{package:{name:n.name,version:n.version,private:n.private},scripts:r(n.scripts??{})}}};function r(e){return{names:Object.keys(e).sort(),devCommand:e.dev,testCommand:e.test,typecheckCommand:e.typecheck,buildCommand:e.build,deployCommand:e[`deploy:production`]??e.deploy}}async function i(t){return JSON.parse(await e(t,`utf8`))}export{n as default};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CollectorResult, ProjectContext, ProjectFacts, ProjectSnapshot } from "./types.js";
|
|
2
|
+
import { defaultCollectors } from "./collectors/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/collectors.d.ts
|
|
5
|
+
type Collector = {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
collect: (context: ProjectContext) => Promise<Partial<ProjectFacts>>;
|
|
9
|
+
};
|
|
10
|
+
type RunCollectorsOptions = {
|
|
11
|
+
collectors?: Collector[];
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
};
|
|
14
|
+
declare const defaultCollectorTimeoutMs = 1500;
|
|
15
|
+
declare function collectSnapshot(context: ProjectContext, options?: RunCollectorsOptions): Promise<ProjectSnapshot>;
|
|
16
|
+
declare function runCollectors(context: ProjectContext, options?: RunCollectorsOptions): Promise<{
|
|
17
|
+
facts: ProjectFacts;
|
|
18
|
+
collectors: CollectorResult[];
|
|
19
|
+
}>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Collector, RunCollectorsOptions, collectSnapshot, defaultCollectorTimeoutMs, defaultCollectors, runCollectors };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defaultCollectors as e}from"./collectors/index.js";const t=1500;async function n(e,t={}){let n=await r(e,t);return{projectRoot:e.projectRoot,environment:e.environment??`development`,facts:n.facts,collectors:n.collectors,collectedAt:new Date().toISOString()}}async function r(t,n={}){let r=n.timeoutMs??t.collectorTimeoutMs??1500,a=n.collectors??e,o={},s=[];for(let e of a){let n=Date.now();try{let a=await i(e.collect(t),r,e.id);Object.assign(o,a),s.push({id:e.id,label:e.label,state:`available`,durationMs:Date.now()-n})}catch(t){s.push({id:e.id,label:e.label,state:`unavailable`,durationMs:Date.now()-n,reason:t instanceof Error?t.message:String(t)})}}return{facts:o,collectors:s}}async function i(e,t,n){let r;try{return await Promise.race([e,new Promise((e,i)=>{r=setTimeout(()=>{i(Error(`Collector ${n} timed out after ${t}ms`))},t)})])}finally{r&&clearTimeout(r)}}export{n as collectSnapshot,t as defaultCollectorTimeoutMs,e as defaultCollectors,r as runCollectors};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t){return{kind:`agent_prompt`,label:e,prompt:t}}export{e as agentPrompt};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AgentPromptAction, Evidence, ProjectSnapshot } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/evaluators/index.d.ts
|
|
4
|
+
declare const defaultEvaluators: ({
|
|
5
|
+
id: string;
|
|
6
|
+
evaluate: (snapshot: ProjectSnapshot) => {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
severity: "warning";
|
|
11
|
+
status: "needs_action";
|
|
12
|
+
area: "launch";
|
|
13
|
+
actions: AgentPromptAction[];
|
|
14
|
+
}[];
|
|
15
|
+
} | {
|
|
16
|
+
id: string;
|
|
17
|
+
evaluate: (snapshot: ProjectSnapshot) => {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
summary: string;
|
|
21
|
+
severity: "warning";
|
|
22
|
+
status: "needs_action";
|
|
23
|
+
area: "product";
|
|
24
|
+
evidence: Evidence[];
|
|
25
|
+
actions: AgentPromptAction[];
|
|
26
|
+
}[];
|
|
27
|
+
} | {
|
|
28
|
+
id: string;
|
|
29
|
+
evaluate: (snapshot: ProjectSnapshot) => {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
summary: string;
|
|
33
|
+
severity: "warning";
|
|
34
|
+
status: "needs_action";
|
|
35
|
+
area: "legal";
|
|
36
|
+
evidence: {
|
|
37
|
+
label: string;
|
|
38
|
+
value: string;
|
|
39
|
+
}[];
|
|
40
|
+
actions: AgentPromptAction[];
|
|
41
|
+
}[];
|
|
42
|
+
})[];
|
|
43
|
+
//#endregion
|
|
44
|
+
export { defaultEvaluators };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./launch-scope.js";import t from"./legal-review.js";import n from"./product-identity.js";const r=[e,n,t];export{r as defaultEvaluators};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{agentPrompt as e}from"./actions.js";const t={id:`launch.scope`,evaluate:t=>t.facts.launchReadiness?.productionChecklistExists?[]:[{id:`launch.scope.undecided`,title:`First-release scope has not been decided`,summary:`The template wires several optional production services, but the project does not record which ones are required, deferred, or removed for the first release.`,severity:`warning`,status:`needs_action`,area:`launch`,actions:[e(`Decide first-release services`,`Inspect docs/stack-setup.md and the integrations wired into this Coreframe project. Ask me which capabilities are required for the first production release, which are deferred, and whether any additional services are needed. Then create docs/production-checklist.md for the confirmed scope without creating accounts or production credentials.`)]}]};export{t as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{agentPrompt as e}from"./actions.js";const t={id:`legal.review`,evaluate:t=>{let n=t.facts.launchReadiness;if(!n)return[];let r=[...n.defaultLegalDocuments,...n.privacyReviewRecorded?[]:[`Privacy/security final-pass review`]];return r.length===0?[]:[{id:`legal.review.incomplete`,title:`Legal and privacy review needs human decisions`,summary:`Production-facing legal copy or the privacy/security evidence review is still in its template state.`,severity:`warning`,status:`needs_action`,area:`legal`,evidence:[{label:`Outstanding`,value:r.join(`, `)}],actions:[e(`Prepare legal and privacy review`,`Inspect the shipped product behavior, content/legal/en/privacy.md, content/legal/en/terms.md, docs/privacy-security-notes.md, and the privacy/security final-pass rule. Replace unsupported template claims with evidence-backed notes, identify the company, contact, jurisdiction, retention, deletion, and billing decisions that require human or legal review, and do not present generated copy as legal advice.`)]}]}};export{t as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{agentPrompt as e}from"./actions.js";const t={id:`product.identity`,evaluate:t=>{let n=[...t.facts.launchReadiness?.defaultIdentity??[]];return t.facts.package?.name===`coreframe`&&n.unshift({label:`Package name`,value:`coreframe`}),n.length===0?[]:[{id:`product.identity.defaults`,title:`Production identity still uses placeholders`,summary:`Names or identifiers visible to users and deployment platforms still use template or development defaults.`,severity:`warning`,status:`needs_action`,area:`product`,evidence:n,actions:[e(`Personalize product identity`,`Ask me for the approved product name, package slug, deployment name, and organization-owned reverse-domain native app identifier. Update only the identity values that still use template or development defaults, ensure com.example is replaced before native distribution, then summarize any store, DNS, or provider-console names that still require human action.`)]}]}};export{t as default};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Finding, FindingArea, ProjectSnapshot } from "./types.js";
|
|
2
|
+
import { defaultEvaluators } from "./evaluators/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/evaluators.d.ts
|
|
5
|
+
type Evaluator = {
|
|
6
|
+
id: string;
|
|
7
|
+
evaluate: (snapshot: ProjectSnapshot) => Finding[];
|
|
8
|
+
};
|
|
9
|
+
declare function evaluateSnapshot(snapshot: ProjectSnapshot, evaluators?: Evaluator[]): Finding[];
|
|
10
|
+
declare function findingsByArea(findings: Finding[], area: FindingArea): Finding[];
|
|
11
|
+
//#endregion
|
|
12
|
+
export { Evaluator, defaultEvaluators, evaluateSnapshot, findingsByArea };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defaultEvaluators as e}from"./evaluators/index.js";function t(t,r=e){return n(r.flatMap(e=>e.evaluate(t)))}function n(e){let t={error:0,warning:1,info:2},n={blocked:0,needs_action:1,unknown:2,ok:3};return[...e].sort((e,r)=>{let i=t[e.severity]-t[r.severity];return i===0?n[e.status]-n[r.status]:i})}function r(e,t){return e.filter(e=>e.area===t)}export{e as defaultEvaluators,t as evaluateSnapshot,r as findingsByArea};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AgentPromptAction, CollectorResult, CollectorState, CommandAction, DashboardModel, DiagnosticAction, DiagnosticResult, DiagnosticSeverity, DiagnosticStatus, EnvironmentMode, Evidence, Finding, FindingArea, GitFact, HeadlineStatus, LaunchReadinessFact, LinkAction, PackageFact, ProjectContext, ProjectFacts, ProjectSnapshot, ScriptsFact, ToolLink } from "./types.js";
|
|
2
|
+
import { defaultCollectors } from "./collectors/index.js";
|
|
3
|
+
import { Collector, RunCollectorsOptions, collectSnapshot, defaultCollectorTimeoutMs, runCollectors } from "./collectors.js";
|
|
4
|
+
import { defaultEvaluators } from "./evaluators/index.js";
|
|
5
|
+
import { Evaluator, evaluateSnapshot, findingsByArea } from "./evaluators.js";
|
|
6
|
+
import { RunDiagnosticsOptions, runDiagnostics } from "./run.js";
|
|
7
|
+
import { buildDashboardModel, visibleLinks } from "./view-model.js";
|
|
8
|
+
export { type AgentPromptAction, type Collector, type CollectorResult, type CollectorState, type CommandAction, type DashboardModel, type DiagnosticAction, type DiagnosticResult, type DiagnosticSeverity, type DiagnosticStatus, type EnvironmentMode, type Evaluator, type Evidence, type Finding, type FindingArea, type GitFact, type HeadlineStatus, type LaunchReadinessFact, type LinkAction, type PackageFact, type ProjectContext, type ProjectFacts, type ProjectSnapshot, type RunCollectorsOptions, type RunDiagnosticsOptions, type ScriptsFact, type ToolLink, buildDashboardModel, collectSnapshot, defaultCollectorTimeoutMs, defaultCollectors, defaultEvaluators, evaluateSnapshot, findingsByArea, runCollectors, runDiagnostics, visibleLinks };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{defaultCollectors as e}from"./collectors/index.js";import{collectSnapshot as t,defaultCollectorTimeoutMs as n,runCollectors as r}from"./collectors.js";import{defaultEvaluators as i}from"./evaluators/index.js";import{evaluateSnapshot as a,findingsByArea as o}from"./evaluators.js";import{buildDashboardModel as s,visibleLinks as c}from"./view-model.js";import{runDiagnostics as l}from"./run.js";export{s as buildDashboardModel,t as collectSnapshot,n as defaultCollectorTimeoutMs,e as defaultCollectors,i as defaultEvaluators,a as evaluateSnapshot,o as findingsByArea,r as runCollectors,l as runDiagnostics,c as visibleLinks};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DiagnosticResult, ProjectContext } from "./types.js";
|
|
2
|
+
import { Collector, RunCollectorsOptions } from "./collectors.js";
|
|
3
|
+
import { Evaluator } from "./evaluators.js";
|
|
4
|
+
|
|
5
|
+
//#region src/run.d.ts
|
|
6
|
+
type RunDiagnosticsOptions = RunCollectorsOptions & {
|
|
7
|
+
collectors?: Collector[];
|
|
8
|
+
evaluators?: Evaluator[];
|
|
9
|
+
};
|
|
10
|
+
declare function runDiagnostics(context: ProjectContext, options?: RunDiagnosticsOptions): Promise<DiagnosticResult>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { RunDiagnosticsOptions, runDiagnostics };
|
package/dist/src/run.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{collectSnapshot as e}from"./collectors.js";import{evaluateSnapshot as t}from"./evaluators.js";import{buildDashboardModel as n}from"./view-model.js";async function r(r,i={}){let a=await e(r,i),o=t(a,i.evaluators);return{snapshot:a,findings:o,dashboard:n(a,o)}}export{r as runDiagnostics};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type EnvironmentMode = "development" | "preview" | "production";
|
|
3
|
+
type DiagnosticSeverity = "info" | "warning" | "error";
|
|
4
|
+
type DiagnosticStatus = "ok" | "needs_action" | "blocked" | "unknown";
|
|
5
|
+
type FindingArea = "setup" | "local_dev" | "database" | "deploy" | "runtime" | "integration" | "launch" | "legal" | "product" | "project";
|
|
6
|
+
type CollectorState = "available" | "unavailable";
|
|
7
|
+
type HeadlineStatus = "ready" | "needs_setup" | "broken_locally" | "not_deploy_ready" | "unknown";
|
|
8
|
+
type ProjectContext = {
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
environment?: EnvironmentMode;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
collectorTimeoutMs?: number;
|
|
13
|
+
};
|
|
14
|
+
type ToolLink = {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
href: string;
|
|
18
|
+
area?: FindingArea;
|
|
19
|
+
};
|
|
20
|
+
type PackageFact = {
|
|
21
|
+
name?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
private?: boolean;
|
|
24
|
+
};
|
|
25
|
+
type ScriptsFact = {
|
|
26
|
+
names: string[];
|
|
27
|
+
devCommand?: string;
|
|
28
|
+
testCommand?: string;
|
|
29
|
+
typecheckCommand?: string;
|
|
30
|
+
buildCommand?: string;
|
|
31
|
+
deployCommand?: string;
|
|
32
|
+
};
|
|
33
|
+
type LaunchReadinessFact = {
|
|
34
|
+
productionChecklistExists: boolean;
|
|
35
|
+
defaultIdentity: Evidence[];
|
|
36
|
+
defaultLegalDocuments: string[];
|
|
37
|
+
privacyReviewRecorded: boolean;
|
|
38
|
+
};
|
|
39
|
+
type GitFact = {
|
|
40
|
+
available: boolean;
|
|
41
|
+
branch?: string;
|
|
42
|
+
shortCommit?: string;
|
|
43
|
+
dirty: boolean;
|
|
44
|
+
changedFiles: number;
|
|
45
|
+
};
|
|
46
|
+
type ProjectFacts = {
|
|
47
|
+
package?: PackageFact;
|
|
48
|
+
scripts?: ScriptsFact;
|
|
49
|
+
launchReadiness?: LaunchReadinessFact;
|
|
50
|
+
git?: GitFact;
|
|
51
|
+
links?: ToolLink[];
|
|
52
|
+
};
|
|
53
|
+
type CollectorResult = {
|
|
54
|
+
id: string;
|
|
55
|
+
label: string;
|
|
56
|
+
state: CollectorState;
|
|
57
|
+
durationMs: number;
|
|
58
|
+
reason?: string;
|
|
59
|
+
};
|
|
60
|
+
type ProjectSnapshot = {
|
|
61
|
+
projectRoot: string;
|
|
62
|
+
environment: EnvironmentMode;
|
|
63
|
+
facts: ProjectFacts;
|
|
64
|
+
collectors: CollectorResult[];
|
|
65
|
+
collectedAt: string;
|
|
66
|
+
};
|
|
67
|
+
type Evidence = {
|
|
68
|
+
label: string;
|
|
69
|
+
value?: string | number | boolean;
|
|
70
|
+
};
|
|
71
|
+
type AgentPromptAction = {
|
|
72
|
+
kind: "agent_prompt";
|
|
73
|
+
label: string;
|
|
74
|
+
prompt: string;
|
|
75
|
+
};
|
|
76
|
+
type CommandAction = {
|
|
77
|
+
kind: "command";
|
|
78
|
+
label: string;
|
|
79
|
+
command: string;
|
|
80
|
+
safety: "local_safe" | "requires_confirmation";
|
|
81
|
+
};
|
|
82
|
+
type LinkAction = {
|
|
83
|
+
kind: "link";
|
|
84
|
+
label: string;
|
|
85
|
+
href: string;
|
|
86
|
+
};
|
|
87
|
+
type DiagnosticAction = AgentPromptAction | CommandAction | LinkAction;
|
|
88
|
+
type Finding = {
|
|
89
|
+
id: string;
|
|
90
|
+
title: string;
|
|
91
|
+
summary: string;
|
|
92
|
+
severity: DiagnosticSeverity;
|
|
93
|
+
status: DiagnosticStatus;
|
|
94
|
+
area: FindingArea;
|
|
95
|
+
evidence?: Evidence[];
|
|
96
|
+
actions: DiagnosticAction[];
|
|
97
|
+
};
|
|
98
|
+
type DashboardModel = {
|
|
99
|
+
headline: {
|
|
100
|
+
status: HeadlineStatus;
|
|
101
|
+
message: string;
|
|
102
|
+
};
|
|
103
|
+
launchItems: Finding[];
|
|
104
|
+
problems: Finding[];
|
|
105
|
+
links: ToolLink[];
|
|
106
|
+
};
|
|
107
|
+
type DiagnosticResult = {
|
|
108
|
+
snapshot: ProjectSnapshot;
|
|
109
|
+
findings: Finding[];
|
|
110
|
+
dashboard: DashboardModel;
|
|
111
|
+
};
|
|
112
|
+
//#endregion
|
|
113
|
+
export { AgentPromptAction, CollectorResult, CollectorState, CommandAction, DashboardModel, DiagnosticAction, DiagnosticResult, DiagnosticSeverity, DiagnosticStatus, EnvironmentMode, Evidence, Finding, FindingArea, GitFact, HeadlineStatus, LaunchReadinessFact, LinkAction, PackageFact, ProjectContext, ProjectFacts, ProjectSnapshot, ScriptsFact, ToolLink };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DashboardModel, Finding, ProjectSnapshot, ToolLink } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/view-model.d.ts
|
|
4
|
+
declare function buildDashboardModel(snapshot: ProjectSnapshot, findings: Finding[]): DashboardModel;
|
|
5
|
+
declare function visibleLinks(snapshot: ProjectSnapshot): ToolLink[];
|
|
6
|
+
//#endregion
|
|
7
|
+
export { buildDashboardModel, visibleLinks };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,r){let a=r.filter(n);return{headline:t(e,r),launchItems:a,problems:r.filter(i),links:e.facts.links??[]}}function t(e,t){let r=t.find(e=>e.status===`blocked`),a=t.find(e=>e.severity===`error`),o=t.filter(n),s=t.filter(i);return r||a?{status:`broken_locally`,message:r?.title??a?.title??`Project is blocked`}:o.length>0?{status:o.some(e=>e.area===`launch`)?`needs_setup`:`not_deploy_ready`,message:`${o.length} launch item${o.length===1?``:`s`} need attention.`}:s.length>0?{status:`unknown`,message:`${s.length} project problem${s.length===1?``:`s`} need attention.`}:t.some(e=>e.status===`unknown`)?{status:`unknown`,message:`Some diagnostics are unavailable.`}:{status:`ready`,message:`${e.facts.package?.name??`Project`} has no outstanding production-readiness findings.`}}function n(e){return e.status!==`ok`&&r(e.area)}function r(e){return[`database`,`deploy`,`integration`,`launch`,`legal`,`product`].includes(e)}function i(e){return e.status!==`ok`&&!r(e.area)}function a(e){return e.facts.links??[]}export{e as buildDashboardModel,a as visibleLinks};
|
package/package.json
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreframe/diagnostics",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/airlock-labs/coreframe.git",
|
|
7
|
+
"directory": "diagnostics"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/src/index.d.ts",
|
|
16
|
+
"default": "./dist/src/index.js"
|
|
17
|
+
}
|
|
8
18
|
},
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^26.1.0",
|
|
21
|
+
"tsdown": "0.22.3",
|
|
22
|
+
"typescript": "6.0.3",
|
|
23
|
+
"vitest": "^4.1.9"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
29
|
+
"format": "oxfmt",
|
|
30
|
+
"lint": "oxlint"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/readme.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Coming soon...
|