@cogitato/kit-cli 0.0.0-stage → 0.2.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/LICENSE +202 -0
- package/README.md +34 -2
- package/bin/kit.js +4 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +23 -0
- package/dist/doctor.js +86 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/export-flows.d.ts +8 -0
- package/dist/export-flows.js +15 -0
- package/dist/export-flows.js.map +1 -0
- package/dist/flows.d.ts +38 -0
- package/dist/flows.js +141 -0
- package/dist/flows.js.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +27 -0
- package/dist/format.js.map +1 -0
- package/dist/gaps.d.ts +21 -0
- package/dist/gaps.js +52 -0
- package/dist/gaps.js.map +1 -0
- package/dist/hook.d.ts +33 -0
- package/dist/hook.js +114 -0
- package/dist/hook.js.map +1 -0
- package/dist/hosting.d.ts +18 -0
- package/dist/hosting.js +51 -0
- package/dist/hosting.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/issue.d.ts +17 -0
- package/dist/issue.js +65 -0
- package/dist/issue.js.map +1 -0
- package/dist/links.d.ts +15 -0
- package/dist/links.js +60 -0
- package/dist/links.js.map +1 -0
- package/dist/preview.d.ts +16 -0
- package/dist/preview.js +38 -0
- package/dist/preview.js.map +1 -0
- package/dist/project.d.ts +55 -0
- package/dist/project.js +145 -0
- package/dist/project.js.map +1 -0
- package/dist/registry.d.ts +67 -0
- package/dist/registry.js +227 -0
- package/dist/registry.js.map +1 -0
- package/dist/rules/css-values.d.ts +31 -0
- package/dist/rules/css-values.js +185 -0
- package/dist/rules/css-values.js.map +1 -0
- package/dist/rules/css.d.ts +4 -0
- package/dist/rules/css.js +39 -0
- package/dist/rules/css.js.map +1 -0
- package/dist/rules/tsx.d.ts +26 -0
- package/dist/rules/tsx.js +191 -0
- package/dist/rules/tsx.js.map +1 -0
- package/dist/save.d.ts +30 -0
- package/dist/save.js +164 -0
- package/dist/save.js.map +1 -0
- package/dist/scaffold.d.ts +42 -0
- package/dist/scaffold.js +232 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/snapshots.d.ts +43 -0
- package/dist/snapshots.js +140 -0
- package/dist/snapshots.js.map +1 -0
- package/dist/status.d.ts +14 -0
- package/dist/status.js +38 -0
- package/dist/status.js.map +1 -0
- package/dist/validate.d.ts +18 -0
- package/dist/validate.js +72 -0
- package/dist/validate.js.map +1 -0
- package/dist/workspace.d.ts +18 -0
- package/dist/workspace.js +119 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +52 -3
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request kit can't carry out, with a message written for a designer ("There's already a
|
|
3
|
+
* screen called checkout.review."). The CLI prints it and exits 1; the MCP server returns it
|
|
4
|
+
* as a tool error.
|
|
5
|
+
*/
|
|
6
|
+
export declare class KitError extends Error {
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A request kit can't carry out, with a message written for a designer ("There's already a
|
|
3
|
+
* screen called checkout.review."). The CLI prints it and exits 1; the MCP server returns it
|
|
4
|
+
* as a tool error.
|
|
5
|
+
*/
|
|
6
|
+
export class KitError extends Error {
|
|
7
|
+
name = "KitError";
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,GAAG,UAAU,CAAC;CAC5B"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ExportResult } from "./flows.js";
|
|
2
|
+
import { type Project } from "./project.js";
|
|
3
|
+
export interface ExportOptions {
|
|
4
|
+
/** Don't write; report whether design/flows.json is up to date. */
|
|
5
|
+
check?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Reads every screen and src/flows.ts, and writes design/flows.json for the canvas. */
|
|
8
|
+
export declare function exportFlows(project: Project, opts?: ExportOptions): ExportResult;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { buildManifest, currentCommit, keptStatus, writeManifest } from "./flows.js";
|
|
2
|
+
import { sourceFiles } from "./project.js";
|
|
3
|
+
import { readRegistry } from "./registry.js";
|
|
4
|
+
/** Reads every screen and src/flows.ts, and writes design/flows.json for the canvas. */
|
|
5
|
+
export function exportFlows(project, opts = {}) {
|
|
6
|
+
const registry = readRegistry(project, sourceFiles(project));
|
|
7
|
+
const built = buildManifest(registry, currentCommit(project));
|
|
8
|
+
// With an unreadable screen, flow-step problems would only repeat it (see Registry.complete).
|
|
9
|
+
const flowIssues = registry.complete ? built.issues : [];
|
|
10
|
+
// Review statuses are set on the canvas, not in code, so they carry over from the current file.
|
|
11
|
+
const status = keptStatus(project, built.manifest);
|
|
12
|
+
const manifest = status ? { ...built.manifest, status } : built.manifest;
|
|
13
|
+
return writeManifest(project, { manifest, issues: [...registry.issues, ...flowIssues] }, opts.check ?? false);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=export-flows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-flows.js","sourceRoot":"","sources":["../src/export-flows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAqB,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAO7C,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,OAAsB,EAAE;IACpE,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,8FAA8F;IAC9F,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,gGAAgG;IAChG,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IACzE,OAAO,aAAa,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC;AAChH,CAAC"}
|
package/dist/flows.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type FlowManifest } from "@cogitato/contract";
|
|
2
|
+
import type { Issue } from "./issue.js";
|
|
3
|
+
import { type Project } from "./project.js";
|
|
4
|
+
import type { Registry } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Builds flows.json content from the registry. Screens are ordered by their first appearance
|
|
7
|
+
* in a flow (so the canvas reads left to right), then by id.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildManifest(reg: Registry, commit?: string): {
|
|
10
|
+
manifest: FlowManifest;
|
|
11
|
+
issues: Issue[];
|
|
12
|
+
};
|
|
13
|
+
export declare function currentCommit(project: Project): string | undefined;
|
|
14
|
+
export declare const serializeManifest: (m: FlowManifest) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Compares manifests ignoring `commit`, which changes on every commit, and review `status`, which
|
|
17
|
+
* is set on the canvas rather than derived from code. Pass `status: true` to compare it too.
|
|
18
|
+
*/
|
|
19
|
+
export declare function sameManifest(a: FlowManifest, b: FlowManifest, opts?: {
|
|
20
|
+
status?: boolean;
|
|
21
|
+
}): boolean;
|
|
22
|
+
/** Review statuses from the current flows.json, for the states that still exist. */
|
|
23
|
+
export declare function keptStatus(project: Project, manifest: FlowManifest): FlowManifest["status"] | undefined;
|
|
24
|
+
export declare function readFlowsJson(project: Project): FlowManifest | undefined;
|
|
25
|
+
/** Checks that design/flows.json matches the code. A warning: the next export fixes it. */
|
|
26
|
+
export declare function staleIssue(project: Project, manifest: FlowManifest): Issue | undefined;
|
|
27
|
+
export interface ExportResult {
|
|
28
|
+
manifest: FlowManifest;
|
|
29
|
+
issues: Issue[];
|
|
30
|
+
/** True when the file on disk differs from the export (ignoring commit). */
|
|
31
|
+
changed: boolean;
|
|
32
|
+
written: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Writes design/flows.json, unless there are problems or `check` is set. */
|
|
35
|
+
export declare function writeManifest(project: Project, built: {
|
|
36
|
+
manifest: FlowManifest;
|
|
37
|
+
issues: Issue[];
|
|
38
|
+
}, check: boolean): ExportResult;
|
package/dist/flows.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { SCHEMA_VERSIONS, validateFlows } from "@cogitato/contract";
|
|
4
|
+
import { LAYOUT } from "./project.js";
|
|
5
|
+
const FLOWS_SCHEMA = "../node_modules/@cogitato/contract/schema/flows.schema.json";
|
|
6
|
+
function unknownStep(reg, flow, ref, at) {
|
|
7
|
+
const dot = ref.lastIndexOf(".");
|
|
8
|
+
const screenId = ref.slice(0, dot);
|
|
9
|
+
const state = ref.slice(dot + 1);
|
|
10
|
+
const screen = reg.screens.find((s) => s.id === screenId);
|
|
11
|
+
const reason = dot < 0
|
|
12
|
+
? `steps are written as "{screen id}.{state}", like "checkout.payment.default"`
|
|
13
|
+
: screen
|
|
14
|
+
? `the ${screen.title} screen has no state called "${state}" (it has: ${screen.fixtures.map((f) => `"${f.key}"`).join(", ")})`
|
|
15
|
+
: `there's no screen with the id "${screenId}"`;
|
|
16
|
+
return {
|
|
17
|
+
code: "code-unknown-flow-step",
|
|
18
|
+
severity: "error",
|
|
19
|
+
file: at.file,
|
|
20
|
+
path: "",
|
|
21
|
+
line: at.line,
|
|
22
|
+
column: at.column,
|
|
23
|
+
message: `The "${flow.title}" flow mentions "${ref}", but ${reason}.`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Builds flows.json content from the registry. Screens are ordered by their first appearance
|
|
28
|
+
* in a flow (so the canvas reads left to right), then by id.
|
|
29
|
+
*/
|
|
30
|
+
export function buildManifest(reg, commit) {
|
|
31
|
+
const issues = [];
|
|
32
|
+
const known = new Set(reg.screens.flatMap((s) => s.fixtures.map((f) => `${s.id}.${f.key}`)));
|
|
33
|
+
const flows = reg.flows.map((flow) => {
|
|
34
|
+
for (const e of flow.edges) {
|
|
35
|
+
if (!known.has(e.from))
|
|
36
|
+
issues.push(unknownStep(reg, flow, e.from, e));
|
|
37
|
+
if (!known.has(e.to))
|
|
38
|
+
issues.push(unknownStep(reg, flow, e.to, e));
|
|
39
|
+
}
|
|
40
|
+
for (const n of flow.nodes ?? [])
|
|
41
|
+
if (!known.has(n.ref))
|
|
42
|
+
issues.push(unknownStep(reg, flow, n.ref, n.at));
|
|
43
|
+
const nodes = flow.nodes ? flow.nodes.map((n) => n.ref) : [...new Set(flow.edges.flatMap((e) => [e.from, e.to]))];
|
|
44
|
+
return {
|
|
45
|
+
id: flow.id,
|
|
46
|
+
title: flow.title,
|
|
47
|
+
nodes,
|
|
48
|
+
edges: flow.edges.map(({ from, to, label }) => (label !== undefined ? { from, to, label } : { from, to })),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const order = new Map();
|
|
52
|
+
for (const f of flows)
|
|
53
|
+
for (const node of f.nodes) {
|
|
54
|
+
const screenId = node.slice(0, node.lastIndexOf("."));
|
|
55
|
+
if (!order.has(screenId))
|
|
56
|
+
order.set(screenId, order.size);
|
|
57
|
+
}
|
|
58
|
+
const rank = (id) => order.get(id) ?? Number.MAX_SAFE_INTEGER;
|
|
59
|
+
const screens = [...reg.screens]
|
|
60
|
+
.sort((a, b) => rank(a.id) - rank(b.id) || a.id.localeCompare(b.id))
|
|
61
|
+
.map((s) => ({
|
|
62
|
+
id: s.id,
|
|
63
|
+
route: s.route,
|
|
64
|
+
title: s.title,
|
|
65
|
+
states: s.fixtures.map((f) => ({ id: f.key, fixture: f.key, title: f.title })),
|
|
66
|
+
}));
|
|
67
|
+
const manifest = {
|
|
68
|
+
$schema: FLOWS_SCHEMA,
|
|
69
|
+
schemaVersion: SCHEMA_VERSIONS.flows,
|
|
70
|
+
...(commit ? { commit } : {}),
|
|
71
|
+
screens,
|
|
72
|
+
flows,
|
|
73
|
+
};
|
|
74
|
+
return { manifest, issues };
|
|
75
|
+
}
|
|
76
|
+
export function currentCommit(project) {
|
|
77
|
+
const r = spawnSync("git", ["rev-parse", "--short", "HEAD"], { cwd: project.root, encoding: "utf8" });
|
|
78
|
+
const sha = r.status === 0 ? r.stdout.trim() : "";
|
|
79
|
+
return /^[0-9a-f]{7,40}$/.test(sha) ? sha : undefined;
|
|
80
|
+
}
|
|
81
|
+
export const serializeManifest = (m) => JSON.stringify(m, null, 2) + "\n";
|
|
82
|
+
/**
|
|
83
|
+
* Compares manifests ignoring `commit`, which changes on every commit, and review `status`, which
|
|
84
|
+
* is set on the canvas rather than derived from code. Pass `status: true` to compare it too.
|
|
85
|
+
*/
|
|
86
|
+
export function sameManifest(a, b, opts = {}) {
|
|
87
|
+
const strip = ({ commit: _c, status, ...rest }) => JSON.stringify(opts.status ? { ...rest, status: status ?? {} } : rest);
|
|
88
|
+
return strip(a) === strip(b);
|
|
89
|
+
}
|
|
90
|
+
/** Review statuses from the current flows.json, for the states that still exist. */
|
|
91
|
+
export function keptStatus(project, manifest) {
|
|
92
|
+
const existing = readFlowsJson(project)?.status;
|
|
93
|
+
if (!existing)
|
|
94
|
+
return undefined;
|
|
95
|
+
const nodes = new Set(manifest.screens.flatMap((s) => s.states.map((st) => `${s.id}.${st.id}`)));
|
|
96
|
+
const kept = Object.fromEntries(Object.entries(existing).filter(([node]) => nodes.has(node)));
|
|
97
|
+
return Object.keys(kept).length ? kept : undefined;
|
|
98
|
+
}
|
|
99
|
+
export function readFlowsJson(project) {
|
|
100
|
+
const path = project.abs(LAYOUT.flowsJson);
|
|
101
|
+
if (!existsSync(path))
|
|
102
|
+
return undefined;
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Checks that design/flows.json matches the code. A warning: the next export fixes it. */
|
|
111
|
+
export function staleIssue(project, manifest) {
|
|
112
|
+
const existing = readFlowsJson(project);
|
|
113
|
+
if (existing && sameManifest(existing, manifest))
|
|
114
|
+
return undefined;
|
|
115
|
+
return {
|
|
116
|
+
code: "code-flows-stale",
|
|
117
|
+
severity: "warning",
|
|
118
|
+
file: LAYOUT.flowsJson,
|
|
119
|
+
path: "",
|
|
120
|
+
message: existing
|
|
121
|
+
? "flows.json is out of date with the screens and flows in the code. Run the flow export to update it."
|
|
122
|
+
: "flows.json hasn't been created yet. Run the flow export to create it.",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Writes design/flows.json, unless there are problems or `check` is set. */
|
|
126
|
+
export function writeManifest(project, built, check) {
|
|
127
|
+
const issues = [...built.issues];
|
|
128
|
+
if (!issues.some((i) => i.severity === "error")) {
|
|
129
|
+
// Safety net: the shared validator must accept what we're about to write.
|
|
130
|
+
const r = validateFlows(built.manifest, LAYOUT.flowsJson);
|
|
131
|
+
issues.push(...r.issues);
|
|
132
|
+
}
|
|
133
|
+
const existing = readFlowsJson(project);
|
|
134
|
+
const changed = !existing || !sameManifest(existing, built.manifest, { status: true });
|
|
135
|
+
const ok = !issues.some((i) => i.severity === "error");
|
|
136
|
+
const written = ok && !check && (changed || existing?.commit !== built.manifest.commit);
|
|
137
|
+
if (written)
|
|
138
|
+
writeFileSync(project.abs(LAYOUT.flowsJson), serializeManifest(built.manifest));
|
|
139
|
+
return { manifest: built.manifest, issues, changed, written };
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=flows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.js","sourceRoot":"","sources":["../src/flows.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
ADDED
package/dist/format.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
2
|
+
/** Plain-language report for people. `--json` prints the ValidationReport instead. */
|
|
3
|
+
export function formatResult(r, success = "All good: the prototype follows the design contract.") {
|
|
4
|
+
const errors = r.issues.filter((i) => i.severity === "error").length;
|
|
5
|
+
const warnings = r.issues.length - errors;
|
|
6
|
+
if (r.issues.length === 0)
|
|
7
|
+
return success;
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push(errors
|
|
10
|
+
? `Found ${plural(errors, "problem")}${warnings ? ` and ${plural(warnings, "warning")}` : ""} to fix.`
|
|
11
|
+
: `No problems, but ${plural(warnings, "warning")}:`);
|
|
12
|
+
const byFile = new Map();
|
|
13
|
+
for (const i of r.issues)
|
|
14
|
+
byFile.set(i.file, [...(byFile.get(i.file) ?? []), i]);
|
|
15
|
+
for (const [file, list] of byFile) {
|
|
16
|
+
lines.push("", file);
|
|
17
|
+
for (const i of list) {
|
|
18
|
+
const where = i.line ? `line ${i.line}: ` : "";
|
|
19
|
+
const tag = i.severity === "warning" ? "(warning) " : "";
|
|
20
|
+
lines.push(` ${tag}${where}${i.message}`);
|
|
21
|
+
if (i.suggestion)
|
|
22
|
+
lines.push(` suggested: ${i.suggestion}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,IAAY,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAEhF,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,CAAmB,EAAE,OAAO,GAAG,sDAAsD;IAChH,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACrE,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IAC1C,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAE1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,MAAM;QACJ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU;QACtG,CAAC,CAAC,oBAAoB,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,CACvD,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjF,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,CAAC,UAAU;gBAAE,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
package/dist/gaps.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Gap } from "@cogitato/contract";
|
|
2
|
+
import { type Project } from "./project.js";
|
|
3
|
+
export interface AddGapOptions {
|
|
4
|
+
kind: Gap["kind"];
|
|
5
|
+
/** What's missing, e.g. "Stepper" or "color.feedback.warning-subtle". */
|
|
6
|
+
name: string;
|
|
7
|
+
/** What the designer was trying to do. */
|
|
8
|
+
context: string;
|
|
9
|
+
screen?: string;
|
|
10
|
+
workaround?: string;
|
|
11
|
+
requestedBy?: string;
|
|
12
|
+
/** For tests. */
|
|
13
|
+
now?: Date;
|
|
14
|
+
}
|
|
15
|
+
export interface AddGapResult {
|
|
16
|
+
gap: Gap;
|
|
17
|
+
/** False when the same open gap was already logged; it's returned instead of a duplicate. */
|
|
18
|
+
created: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Records something the design needed that the contract doesn't have, in design/contract/gaps.json. */
|
|
21
|
+
export declare function addGap(project: Project, opts: AddGapOptions): AddGapResult;
|
package/dist/gaps.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { kebabCase, validateFormat } from "@cogitato/contract";
|
|
4
|
+
import { KitError } from "./errors.js";
|
|
5
|
+
import { WORKSPACE_LAYOUT } from "./project.js";
|
|
6
|
+
const FILE = `${WORKSPACE_LAYOUT.contract}/gaps.json`;
|
|
7
|
+
const SCHEMA = "../../node_modules/@cogitato/contract/schema/gaps.schema.json";
|
|
8
|
+
function gitUser(project) {
|
|
9
|
+
const r = spawnSync("git", ["config", "user.name"], { cwd: project.workspace.root, encoding: "utf8" });
|
|
10
|
+
return r.status === 0 && r.stdout.trim() ? r.stdout.trim() : undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Records something the design needed that the contract doesn't have, in design/contract/gaps.json. */
|
|
13
|
+
export function addGap(project, opts) {
|
|
14
|
+
const path = project.workspace.abs(FILE);
|
|
15
|
+
let log = { $schema: SCHEMA, gaps: [] };
|
|
16
|
+
if (existsSync(path)) {
|
|
17
|
+
try {
|
|
18
|
+
log = JSON.parse(readFileSync(path, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new KitError(`${FILE} has a typo in it, so the gap couldn't be added. Ask a developer to fix the file.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const name = opts.name.trim();
|
|
25
|
+
const same = log.gaps.find((g) => g.status === "open" && g.kind === opts.kind && g.name.toLowerCase() === name.toLowerCase());
|
|
26
|
+
if (same)
|
|
27
|
+
return { gap: same, created: false };
|
|
28
|
+
const slug = kebabCase(name).replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "item";
|
|
29
|
+
const taken = new Set(log.gaps.map((g) => g.id));
|
|
30
|
+
let id = `gap_${slug}`;
|
|
31
|
+
for (let n = 2; taken.has(id); n++)
|
|
32
|
+
id = `gap_${slug}-${n}`;
|
|
33
|
+
const requestedBy = opts.requestedBy ?? gitUser(project);
|
|
34
|
+
const gap = {
|
|
35
|
+
id,
|
|
36
|
+
kind: opts.kind,
|
|
37
|
+
name,
|
|
38
|
+
context: opts.context.trim(),
|
|
39
|
+
...(opts.screen ? { screen: opts.screen } : {}),
|
|
40
|
+
...(opts.workaround?.trim() ? { workaround: opts.workaround.trim() } : {}),
|
|
41
|
+
...(requestedBy ? { requestedBy } : {}),
|
|
42
|
+
status: "open",
|
|
43
|
+
createdAt: (opts.now ?? new Date()).toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
44
|
+
};
|
|
45
|
+
const next = { ...log, gaps: [...log.gaps, gap] };
|
|
46
|
+
const check = validateFormat("gaps", next, FILE);
|
|
47
|
+
if (!check.ok)
|
|
48
|
+
throw new KitError(`The gap couldn't be recorded: ${check.issues.map((i) => i.message).join(" ")}`);
|
|
49
|
+
writeFileSync(path, JSON.stringify(next, null, 2) + "\n");
|
|
50
|
+
return { gap, created: true };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=gaps.js.map
|
package/dist/gaps.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gaps.js","sourceRoot":"","sources":["../src/gaps.ts"],"names":[],"mappings":"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"}
|
package/dist/hook.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type DoctorOptions } from "./doctor.js";
|
|
2
|
+
import { type Project, type Workspace } from "./project.js";
|
|
3
|
+
export type HookEvent = "post-edit" | "session-start";
|
|
4
|
+
export interface HookInput {
|
|
5
|
+
hook_event_name?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
tool_name?: string;
|
|
8
|
+
tool_input?: {
|
|
9
|
+
file_path?: string;
|
|
10
|
+
};
|
|
11
|
+
source?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface HookOutput {
|
|
14
|
+
decision?: "block";
|
|
15
|
+
reason?: string;
|
|
16
|
+
systemMessage?: string;
|
|
17
|
+
hookSpecificOutput?: {
|
|
18
|
+
hookEventName: "PostToolUse" | "SessionStart";
|
|
19
|
+
additionalContext?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare const MCP_TOOLS = "list_components, get_component, get_tokens, list_prototypes, create_screen, add_state, log_gap, validate, canvas_link, set_status";
|
|
23
|
+
/** Which files the edit hook checks: prototype source and the design/ folder. */
|
|
24
|
+
export declare function checkedByHook(rel: string): boolean;
|
|
25
|
+
/** After Claude writes or edits a file: keep flows.json fresh and send any problems back to Claude. */
|
|
26
|
+
export declare function postEditHook(project: Project, input: HookInput): HookOutput | undefined;
|
|
27
|
+
/** At session start: tell the designer (one line) and Claude (details) if the machine isn't ready. */
|
|
28
|
+
export declare function sessionStartHook(ws: Workspace, opts?: DoctorOptions): Promise<HookOutput>;
|
|
29
|
+
/**
|
|
30
|
+
* Runs a hook from its JSON input. Returns undefined when there's nothing to say (including
|
|
31
|
+
* when the session isn't in a Cogitato workspace at all).
|
|
32
|
+
*/
|
|
33
|
+
export declare function runHook(event: HookEvent, input: HookInput, start?: string): Promise<HookOutput | undefined>;
|
package/dist/hook.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { dirname, isAbsolute } from "node:path";
|
|
2
|
+
import { machineChecks, prototypeChecks } from "./doctor.js";
|
|
3
|
+
import { exportFlows } from "./export-flows.js";
|
|
4
|
+
import { formatResult } from "./format.js";
|
|
5
|
+
import { LAYOUT, WORKSPACE_LAYOUT, findProject, findWorkspace, isScreenFile, listPrototypes, projectIn, sourceFiles, } from "./project.js";
|
|
6
|
+
import { readRegistry } from "./registry.js";
|
|
7
|
+
import { validate } from "./validate.js";
|
|
8
|
+
import { validateWorkspace } from "./workspace.js";
|
|
9
|
+
export const MCP_TOOLS = "list_components, get_component, get_tokens, list_prototypes, create_screen, add_state, log_gap, validate, canvas_link, set_status";
|
|
10
|
+
const FIX_ADVICE = "Fix these before finishing. If the design needs something the kit doesn't have, use the nearest token or kit component " +
|
|
11
|
+
"and say so, or log a gap with the log_gap tool; don't hardcode values or build your own components.";
|
|
12
|
+
/** Which files the edit hook checks: prototype source and the design/ folder. */
|
|
13
|
+
export function checkedByHook(rel) {
|
|
14
|
+
if (rel.startsWith("..") || isAbsolute(rel))
|
|
15
|
+
return false;
|
|
16
|
+
if (rel.startsWith(`${LAYOUT.design}/`))
|
|
17
|
+
return rel.endsWith(".json") || rel.endsWith(".md");
|
|
18
|
+
return rel.startsWith(`${LAYOUT.src}/`) && /\.(tsx?|css)$/.test(rel) && !rel.endsWith(".d.ts");
|
|
19
|
+
}
|
|
20
|
+
/** After Claude writes or edits a file: keep flows.json fresh and send any problems back to Claude. */
|
|
21
|
+
export function postEditHook(project, input) {
|
|
22
|
+
const file = input.tool_input?.file_path;
|
|
23
|
+
if (!file)
|
|
24
|
+
return undefined;
|
|
25
|
+
const rel = project.rel(file);
|
|
26
|
+
if (!checkedByHook(rel))
|
|
27
|
+
return undefined;
|
|
28
|
+
const shown = `${WORKSPACE_LAYOUT.prototypes}/${project.name}/${rel}`;
|
|
29
|
+
if (rel === LAYOUT.flowsJson) {
|
|
30
|
+
return {
|
|
31
|
+
decision: "block",
|
|
32
|
+
reason: "design/flows.json is generated from the screens and src/flows.ts, so edits to it get overwritten. " +
|
|
33
|
+
"Change src/flows.ts (or the screen's states) instead; the file updates itself after each edit.",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (isScreenFile(rel) || rel === LAYOUT.flowsTs)
|
|
37
|
+
exportFlows(project);
|
|
38
|
+
const r = validate(project, { files: [rel] });
|
|
39
|
+
if (r.ok && r.issues.length === 0)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (r.ok) {
|
|
42
|
+
return { hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: `Cogitato check of ${shown}:\n${formatResult(r)}` } };
|
|
43
|
+
}
|
|
44
|
+
return { decision: "block", reason: `Cogitato check of ${shown}: ${formatResult(r)}\n\n${FIX_ADVICE}` };
|
|
45
|
+
}
|
|
46
|
+
/** After an edit to the shared design contract: check the contract, and report problems in it. */
|
|
47
|
+
function contractEditHook(ws, rel) {
|
|
48
|
+
const own = validateWorkspace(ws, { contractOnly: true }).issues;
|
|
49
|
+
if (!own.length)
|
|
50
|
+
return undefined;
|
|
51
|
+
const result = { ok: !own.some((i) => i.severity === "error"), issues: own };
|
|
52
|
+
return result.ok
|
|
53
|
+
? { hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: `Cogitato check of ${rel}:\n${formatResult(result)}` } }
|
|
54
|
+
: { decision: "block", reason: `Cogitato check of ${rel}: ${formatResult(result)}\n\n${FIX_ADVICE}` };
|
|
55
|
+
}
|
|
56
|
+
/** At session start: tell the designer (one line) and Claude (details) if the machine isn't ready. */
|
|
57
|
+
export async function sessionStartHook(ws, opts = {}) {
|
|
58
|
+
const names = listPrototypes(ws);
|
|
59
|
+
// Machine-wide checks run once; the rest once per prototype.
|
|
60
|
+
const checks = [...(await machineChecks(opts)), ...names.flatMap((n) => prototypeChecks(projectIn(ws, n)))];
|
|
61
|
+
const unique = (status) => {
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
return checks.filter((c) => c.status === status && !seen.has(c.message) && seen.add(c.message));
|
|
64
|
+
};
|
|
65
|
+
const problems = unique("problem");
|
|
66
|
+
const warnings = unique("warning");
|
|
67
|
+
const parts = names.map((name) => {
|
|
68
|
+
const p = projectIn(ws, name);
|
|
69
|
+
const reg = readRegistry(p, sourceFiles(p));
|
|
70
|
+
const states = reg.screens.reduce((n, s) => n + s.fixtures.length, 0);
|
|
71
|
+
return `${name} (${reg.screens.length} screen${reg.screens.length === 1 ? "" : "s"}, ${states} state${states === 1 ? "" : "s"})`;
|
|
72
|
+
});
|
|
73
|
+
const first = (parts.length
|
|
74
|
+
? `This is a Cogitato design workspace with ${parts.length} prototype${parts.length === 1 ? "" : "s"}: ${parts.join(", ")}. `
|
|
75
|
+
: "This is a Cogitato design workspace with no prototypes yet. ") +
|
|
76
|
+
"The design kit (design/contract and kit/) is shared by all of them; each prototype is a folder in prototypes/. " +
|
|
77
|
+
"When the person doesn't say which prototype they mean and there's more than one, ask.";
|
|
78
|
+
const context = [
|
|
79
|
+
first,
|
|
80
|
+
"The person here is usually a designer: plain language, never ask them to run commands.",
|
|
81
|
+
`Use the Cogitato MCP tools (${MCP_TOOLS}) instead of reading design/contract files, and the Cogitato skills for building screens and states.`,
|
|
82
|
+
"Every edit is checked automatically and design/flows.json is regenerated after each one, so there's no need to run kit commands yourself.",
|
|
83
|
+
...(problems.length
|
|
84
|
+
? ["", "Setup problems found:", ...problems.map((c) => `- ${c.message}`), "Offer to fix them with the Cogitato setup-machine skill, in plain words."]
|
|
85
|
+
: []),
|
|
86
|
+
...(warnings.length ? ["", "Notes:", ...warnings.map((c) => `- ${c.message}`)] : []),
|
|
87
|
+
].join("\n");
|
|
88
|
+
return {
|
|
89
|
+
...(problems.length
|
|
90
|
+
? { systemMessage: `Cogitato: this design workspace isn't ready on this computer yet. ${problems[0].message} Ask Claude to set it up.` }
|
|
91
|
+
: {}),
|
|
92
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: context },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Runs a hook from its JSON input. Returns undefined when there's nothing to say (including
|
|
97
|
+
* when the session isn't in a Cogitato workspace at all).
|
|
98
|
+
*/
|
|
99
|
+
export async function runHook(event, input, start = input.cwd ?? process.cwd()) {
|
|
100
|
+
const ws = findWorkspace(start);
|
|
101
|
+
if (!ws)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (event === "session-start")
|
|
104
|
+
return sessionStartHook(ws);
|
|
105
|
+
const file = input.tool_input?.file_path;
|
|
106
|
+
if (!file)
|
|
107
|
+
return undefined;
|
|
108
|
+
const rel = ws.rel(file);
|
|
109
|
+
if (rel.startsWith(`${WORKSPACE_LAYOUT.contract}/`) && /\.(json|md)$/.test(rel))
|
|
110
|
+
return contractEditHook(ws, rel);
|
|
111
|
+
const project = findProject(dirname(ws.abs(rel)));
|
|
112
|
+
return project && project.workspace.root === ws.root ? postEditHook(project, input) : undefined;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=hook.js.map
|
package/dist/hook.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,MAAM,EACN,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,SAAS,EACT,WAAW,GAGZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAuBnD,MAAM,CAAC,MAAM,SAAS,GACpB,mIAAmI,CAAC;AAEtI,MAAM,UAAU,GACd,yHAAyH;IACzH,qGAAqG,CAAC;AAExG,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7F,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjG,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,KAAgB;IAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,KAAK,GAAG,GAAG,gBAAgB,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;IAEtE,IAAI,GAAG,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,MAAM,EACJ,oGAAoG;gBACpG,gGAAgG;SACnG,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,MAAM,CAAC,OAAO;QAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAEtE,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACpD,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;QACT,OAAO,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,qBAAqB,KAAK,MAAM,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC;IACxI,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,UAAU,EAAE,EAAE,CAAC;AAC1G,CAAC;AAED,kGAAkG;AAClG,SAAS,gBAAgB,CAAC,EAAa,EAAE,GAAW;IAClD,MAAM,GAAG,GAAG,iBAAiB,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACjE,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,MAAM,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC7E,OAAO,MAAM,CAAC,EAAE;QACd,CAAC,CAAC,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,qBAAqB,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE;QACnI,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,GAAG,KAAK,YAAY,CAAC,MAAM,CAAC,OAAO,UAAU,EAAE,EAAE,CAAC;AAC1G,CAAC;AAED,sGAAsG;AACtG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAa,EAAE,OAAsB,EAAE;IAC5E,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IACjC,6DAA6D;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAClG,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAEnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACtE,OAAO,GAAG,IAAI,KAAK,GAAG,CAAC,OAAO,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IACnI,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GACT,CAAC,KAAK,CAAC,MAAM;QACX,CAAC,CAAC,4CAA4C,KAAK,CAAC,MAAM,aAAa,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QAC7H,CAAC,CAAC,8DAA8D,CAAC;QACnE,iHAAiH;QACjH,uFAAuF,CAAC;IAE1F,MAAM,OAAO,GAAG;QACd,KAAK;QACL,wFAAwF;QACxF,+BAA+B,SAAS,sGAAsG;QAC9I,2IAA2I;QAC3I,GAAG,CAAC,QAAQ,CAAC,MAAM;YACjB,CAAC,CAAC,CAAC,EAAE,EAAE,uBAAuB,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,0EAA0E,CAAC;YACrJ,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACrF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO;QACL,GAAG,CAAC,QAAQ,CAAC,MAAM;YACjB,CAAC,CAAC,EAAE,aAAa,EAAE,qEAAqE,QAAQ,CAAC,CAAC,CAAE,CAAC,OAAO,2BAA2B,EAAE;YACzI,CAAC,CAAC,EAAE,CAAC;QACP,kBAAkB,EAAE,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE;KAClF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAgB,EAAE,KAAgB,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;IAClG,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAC1B,IAAI,KAAK,KAAK,eAAe;QAAE,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,gBAAgB,CAAC,QAAQ,GAAG,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAClH,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClG,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Paths that every prototype in a workspace depends on: a change here rebuilds them all. */
|
|
2
|
+
export declare const SHARED_INPUTS: readonly ["design/", "kit/", "package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"];
|
|
3
|
+
/** One part of a host name: lowercase letters, digits and single dashes. */
|
|
4
|
+
export declare function slugPart(s: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* The DNS label of a branch preview: "<branch>--<prototype>--<org>". A label longer than 63
|
|
7
|
+
* characters keeps the prototype and org, and shortens the branch with a hash of the full label.
|
|
8
|
+
* If even that doesn't fit, the whole label is shortened. An empty branch or prototype part
|
|
9
|
+
* becomes "branch" or "prototype"; an empty org is an error. A label never starts with "xn--"
|
|
10
|
+
* (DNS keeps that for internationalized names): the branch part gets a "b-" prefix instead.
|
|
11
|
+
*/
|
|
12
|
+
export declare function previewHost(parts: {
|
|
13
|
+
branch: string;
|
|
14
|
+
prototype: string;
|
|
15
|
+
org: string;
|
|
16
|
+
}): string;
|
|
17
|
+
/** Which prototypes a set of changed (workspace-relative) paths affects, sorted. */
|
|
18
|
+
export declare function affectedPrototypes(changedPaths: string[], prototypes: string[]): string[];
|
package/dist/hosting.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
// Shared with the hosted platform (@cogitato/kit-cli/hosting): the plugin prints preview links
|
|
3
|
+
// and the platform serves previews, so both must name hosts and pick prototypes the same way.
|
|
4
|
+
/** Paths that every prototype in a workspace depends on: a change here rebuilds them all. */
|
|
5
|
+
export const SHARED_INPUTS = ["design/", "kit/", "package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"];
|
|
6
|
+
const MAX_LABEL = 63;
|
|
7
|
+
const HASH = 6;
|
|
8
|
+
/** One part of a host name: lowercase letters, digits and single dashes. */
|
|
9
|
+
export function slugPart(s) {
|
|
10
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11
|
+
}
|
|
12
|
+
const hash = (s) => createHash("sha256").update(s).digest("hex").slice(0, HASH);
|
|
13
|
+
const cut = (s, n) => s.slice(0, Math.max(0, n)).replace(/-+$/, "");
|
|
14
|
+
/**
|
|
15
|
+
* The DNS label of a branch preview: "<branch>--<prototype>--<org>". A label longer than 63
|
|
16
|
+
* characters keeps the prototype and org, and shortens the branch with a hash of the full label.
|
|
17
|
+
* If even that doesn't fit, the whole label is shortened. An empty branch or prototype part
|
|
18
|
+
* becomes "branch" or "prototype"; an empty org is an error. A label never starts with "xn--"
|
|
19
|
+
* (DNS keeps that for internationalized names): the branch part gets a "b-" prefix instead.
|
|
20
|
+
*/
|
|
21
|
+
export function previewHost(parts) {
|
|
22
|
+
const org = slugPart(parts.org);
|
|
23
|
+
if (!org)
|
|
24
|
+
throw new Error("previewHost needs an org name");
|
|
25
|
+
const tail = `--${slugPart(parts.prototype) || "prototype"}--${org}`;
|
|
26
|
+
let branch = slugPart(parts.branch) || "branch";
|
|
27
|
+
if (`${branch}${tail}`.startsWith("xn--"))
|
|
28
|
+
branch = `b-${branch}`;
|
|
29
|
+
const full = `${branch}${tail}`;
|
|
30
|
+
if (full.length <= MAX_LABEL)
|
|
31
|
+
return full;
|
|
32
|
+
const h = hash(full);
|
|
33
|
+
const room = MAX_LABEL - tail.length - HASH - 1;
|
|
34
|
+
if (room >= 1 && cut(branch, room))
|
|
35
|
+
return `${cut(branch, room)}-${h}${tail}`;
|
|
36
|
+
return `${cut(full, MAX_LABEL - HASH - 1)}-${h}`;
|
|
37
|
+
}
|
|
38
|
+
/** Which prototypes a set of changed (workspace-relative) paths affects, sorted. */
|
|
39
|
+
export function affectedPrototypes(changedPaths, prototypes) {
|
|
40
|
+
const known = new Set(prototypes);
|
|
41
|
+
const out = new Set();
|
|
42
|
+
for (const path of changedPaths) {
|
|
43
|
+
if (SHARED_INPUTS.some((s) => (s.endsWith("/") ? path.startsWith(s) : path === s)))
|
|
44
|
+
return [...known].sort();
|
|
45
|
+
const name = /^prototypes\/([^/]+)\//.exec(path)?.[1];
|
|
46
|
+
if (name && known.has(name))
|
|
47
|
+
out.add(name);
|
|
48
|
+
}
|
|
49
|
+
return [...out].sort();
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=hosting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosting.js","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"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"}
|