uidex 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli.cjs +1111 -87
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/cloud/index.cjs +375 -72
- package/dist/cloud/index.cjs.map +1 -1
- package/dist/cloud/index.d.cts +82 -0
- package/dist/cloud/index.d.ts +82 -0
- package/dist/cloud/index.js +376 -71
- package/dist/cloud/index.js.map +1 -1
- package/dist/headless/index.cjs +623 -469
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.d.cts +77 -75
- package/dist/headless/index.d.ts +77 -75
- package/dist/headless/index.js +627 -469
- package/dist/headless/index.js.map +1 -1
- package/dist/index.cjs +4258 -2884
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +275 -234
- package/dist/index.d.ts +275 -234
- package/dist/index.js +4280 -2890
- package/dist/index.js.map +1 -1
- package/dist/playwright/index.cjs +4 -4
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.js +3 -3
- package/dist/playwright/index.js.map +1 -1
- package/dist/playwright/reporter.cjs +3 -3
- package/dist/playwright/reporter.cjs.map +1 -1
- package/dist/playwright/reporter.js +3 -3
- package/dist/playwright/reporter.js.map +1 -1
- package/dist/react/index.cjs +4299 -2906
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +206 -200
- package/dist/react/index.d.ts +206 -200
- package/dist/react/index.js +4339 -2926
- package/dist/react/index.js.map +1 -1
- package/dist/scan/index.cjs +201 -49
- package/dist/scan/index.cjs.map +1 -1
- package/dist/scan/index.d.cts +27 -1
- package/dist/scan/index.d.ts +27 -1
- package/dist/scan/index.js +200 -48
- package/dist/scan/index.js.map +1 -1
- package/package.json +8 -14
- package/templates/claude/api.md +110 -0
package/dist/scan/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
// src/scan/index.ts
|
|
30
|
+
// src/scanner/scan/index.ts
|
|
31
31
|
var scan_exports = {};
|
|
32
32
|
__export(scan_exports, {
|
|
33
33
|
CONFIG_FILENAME: () => CONFIG_FILENAME,
|
|
@@ -54,11 +54,11 @@ __export(scan_exports, {
|
|
|
54
54
|
});
|
|
55
55
|
module.exports = __toCommonJS(scan_exports);
|
|
56
56
|
|
|
57
|
-
// src/scan/discover.ts
|
|
57
|
+
// src/scanner/scan/discover.ts
|
|
58
58
|
var fs = __toESM(require("fs"), 1);
|
|
59
59
|
var path = __toESM(require("path"), 1);
|
|
60
60
|
|
|
61
|
-
// src/scan/config.ts
|
|
61
|
+
// src/scanner/scan/config.ts
|
|
62
62
|
var DEFAULT_TYPE_MODE = "strict";
|
|
63
63
|
var WELL_KNOWN_FILES = {
|
|
64
64
|
page: "uidex.page.ts",
|
|
@@ -222,7 +222,7 @@ var DEFAULT_CONVENTIONS = {
|
|
|
222
222
|
regions: "landmarks"
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
-
// src/scan/discover.ts
|
|
225
|
+
// src/scanner/scan/discover.ts
|
|
226
226
|
var CONFIG_FILENAME = ".uidex.json";
|
|
227
227
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
228
228
|
"node_modules",
|
|
@@ -281,7 +281,7 @@ function discover(options = {}) {
|
|
|
281
281
|
return results.sort((a, b) => a.configPath.localeCompare(b.configPath));
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
// src/scan/walk.ts
|
|
284
|
+
// src/scanner/scan/walk.ts
|
|
285
285
|
var fs2 = __toESM(require("fs"), 1);
|
|
286
286
|
var path2 = __toESM(require("path"), 1);
|
|
287
287
|
var DEFAULT_INCLUDES = ["**/*.{ts,tsx,js,jsx,mjs,cjs}"];
|
|
@@ -406,12 +406,13 @@ function* walkDir(root, dir) {
|
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
// src/scan/extract-uidex-export.ts
|
|
409
|
+
// src/scanner/scan/extract-uidex-export.ts
|
|
410
410
|
var KIND_DISCRIMINATORS = [
|
|
411
411
|
"page",
|
|
412
412
|
"feature",
|
|
413
413
|
"primitive",
|
|
414
414
|
"widget",
|
|
415
|
+
"region",
|
|
415
416
|
"flow",
|
|
416
417
|
"notFlow"
|
|
417
418
|
];
|
|
@@ -424,15 +425,23 @@ var ALLOWED_FIELDS = {
|
|
|
424
425
|
"acceptance",
|
|
425
426
|
"description"
|
|
426
427
|
]),
|
|
427
|
-
feature: /* @__PURE__ */ new Set([
|
|
428
|
+
feature: /* @__PURE__ */ new Set([
|
|
429
|
+
"feature",
|
|
430
|
+
"name",
|
|
431
|
+
"features",
|
|
432
|
+
"acceptance",
|
|
433
|
+
"description"
|
|
434
|
+
]),
|
|
428
435
|
primitive: /* @__PURE__ */ new Set(["primitive", "name", "description"]),
|
|
429
436
|
widget: /* @__PURE__ */ new Set(["widget", "name", "acceptance", "description"]),
|
|
437
|
+
region: /* @__PURE__ */ new Set(["region", "name", "description"]),
|
|
430
438
|
flow: /* @__PURE__ */ new Set(["flow", "notFlow", "name", "description"])
|
|
431
439
|
};
|
|
432
440
|
var FALSEABLE = /* @__PURE__ */ new Set([
|
|
433
441
|
"page",
|
|
434
442
|
"feature",
|
|
435
|
-
"primitive"
|
|
443
|
+
"primitive",
|
|
444
|
+
"region"
|
|
436
445
|
]);
|
|
437
446
|
var ExtractError = class extends Error {
|
|
438
447
|
code;
|
|
@@ -1160,7 +1169,7 @@ function buildMetadata(value, file, headerPos, diagnostics) {
|
|
|
1160
1169
|
line: pos.line
|
|
1161
1170
|
});
|
|
1162
1171
|
}
|
|
1163
|
-
const features = kind === "page" ? readStringArrayField(byKey, "features") : void 0;
|
|
1172
|
+
const features = kind === "page" || kind === "feature" ? readStringArrayField(byKey, "features") : void 0;
|
|
1164
1173
|
const widgets = kind === "page" ? readStringArrayField(byKey, "widgets") : void 0;
|
|
1165
1174
|
const notFlow = kind === "flow" && discriminator === "notFlow" ? true : void 0;
|
|
1166
1175
|
const metadata = {
|
|
@@ -1255,7 +1264,7 @@ function posAt(content, offset) {
|
|
|
1255
1264
|
return { offset, line, column: offset - lineStart + 1 };
|
|
1256
1265
|
}
|
|
1257
1266
|
|
|
1258
|
-
// src/scan/jsx-ancestry.ts
|
|
1267
|
+
// src/scanner/scan/jsx-ancestry.ts
|
|
1259
1268
|
var DATA_ATTR_RE = /\bdata-uidex(?:-(region|widget|primitive))?\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
1260
1269
|
function parseDataAttrs(tagSource) {
|
|
1261
1270
|
if (!tagSource.includes("data-uidex")) return [];
|
|
@@ -1444,7 +1453,7 @@ function findTagEnd(content, start) {
|
|
|
1444
1453
|
return -1;
|
|
1445
1454
|
}
|
|
1446
1455
|
|
|
1447
|
-
// src/scan/extract.ts
|
|
1456
|
+
// src/scanner/scan/extract.ts
|
|
1448
1457
|
var JSDOC_BLOCK = /\/\*\*([\s\S]*?)\*\//g;
|
|
1449
1458
|
function lineAt(content, index) {
|
|
1450
1459
|
let line = 1;
|
|
@@ -1547,10 +1556,10 @@ function extractOne(file) {
|
|
|
1547
1556
|
return annotations;
|
|
1548
1557
|
}
|
|
1549
1558
|
|
|
1550
|
-
// src/scan/resolve.ts
|
|
1559
|
+
// src/scanner/scan/resolve.ts
|
|
1551
1560
|
var path3 = __toESM(require("path"), 1);
|
|
1552
1561
|
|
|
1553
|
-
// src/entities/types.ts
|
|
1562
|
+
// src/shared/entities/types.ts
|
|
1554
1563
|
var ENTITY_KINDS = [
|
|
1555
1564
|
"route",
|
|
1556
1565
|
"page",
|
|
@@ -1583,7 +1592,7 @@ function assertEntityKind(kind) {
|
|
|
1583
1592
|
if (!KIND_SET.has(kind)) throw new UnknownEntityKindError(kind);
|
|
1584
1593
|
}
|
|
1585
1594
|
|
|
1586
|
-
// src/entities/registry.ts
|
|
1595
|
+
// src/shared/entities/registry.ts
|
|
1587
1596
|
function emptyStore() {
|
|
1588
1597
|
return {
|
|
1589
1598
|
route: /* @__PURE__ */ new Map(),
|
|
@@ -1665,10 +1674,33 @@ function createRegistry() {
|
|
|
1665
1674
|
return ids.has(entity.id);
|
|
1666
1675
|
});
|
|
1667
1676
|
};
|
|
1668
|
-
|
|
1677
|
+
const reports = /* @__PURE__ */ new Map();
|
|
1678
|
+
const reportsCbs = /* @__PURE__ */ new Set();
|
|
1679
|
+
const setReports = (kind, id, records) => {
|
|
1680
|
+
reports.set(`${kind}:${id}`, records);
|
|
1681
|
+
for (const cb of reportsCbs) cb();
|
|
1682
|
+
};
|
|
1683
|
+
const getReports = (kind, id) => reports.get(`${kind}:${id}`) ?? [];
|
|
1684
|
+
const listReportKeys = () => Array.from(reports.keys());
|
|
1685
|
+
const onReportsChange = (cb) => {
|
|
1686
|
+
reportsCbs.add(cb);
|
|
1687
|
+
return () => reportsCbs.delete(cb);
|
|
1688
|
+
};
|
|
1689
|
+
return {
|
|
1690
|
+
add,
|
|
1691
|
+
get,
|
|
1692
|
+
list,
|
|
1693
|
+
query,
|
|
1694
|
+
byScope,
|
|
1695
|
+
touchedBy,
|
|
1696
|
+
setReports,
|
|
1697
|
+
getReports,
|
|
1698
|
+
listReportKeys,
|
|
1699
|
+
onReportsChange
|
|
1700
|
+
};
|
|
1669
1701
|
}
|
|
1670
1702
|
|
|
1671
|
-
// src/scan/routes.ts
|
|
1703
|
+
// src/scanner/scan/routes.ts
|
|
1672
1704
|
var PAGE_BASENAME = /^page\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
1673
1705
|
var PAGES_ROUTER_BASENAME = /\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
1674
1706
|
var ROUTE_BASENAME = /^route\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
@@ -1734,7 +1766,7 @@ function pathToId(routePath) {
|
|
|
1734
1766
|
return routePath.replace(/^\/+/, "").replace(/\[\.{3}([^\]]+)\]/g, "$1").replace(/\[([^\]]+)\]/g, "$1").replace(/\//g, "-").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1735
1767
|
}
|
|
1736
1768
|
|
|
1737
|
-
// src/scan/resolve.ts
|
|
1769
|
+
// src/scanner/scan/resolve.ts
|
|
1738
1770
|
var DOM_ATTR_KINDS = /* @__PURE__ */ new Set([
|
|
1739
1771
|
"element",
|
|
1740
1772
|
"region",
|
|
@@ -2041,6 +2073,21 @@ function resolve2(ctx) {
|
|
|
2041
2073
|
};
|
|
2042
2074
|
registry.add(region);
|
|
2043
2075
|
}
|
|
2076
|
+
for (const ef of ctx.extracted) {
|
|
2077
|
+
const exp = exportFor(ef.file.displayPath, "region");
|
|
2078
|
+
if (!exp) continue;
|
|
2079
|
+
if (exp.id === false) continue;
|
|
2080
|
+
if (typeof exp.id === "string" && !registry.get("region", exp.id)) {
|
|
2081
|
+
const meta = buildMetaFromExport(exp);
|
|
2082
|
+
const region = {
|
|
2083
|
+
kind: "region",
|
|
2084
|
+
id: exp.id,
|
|
2085
|
+
loc: { file: ef.file.displayPath, line: exp.loc.line },
|
|
2086
|
+
...meta ? { meta } : {}
|
|
2087
|
+
};
|
|
2088
|
+
registry.add(region);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2044
2091
|
const primitiveConventions = conventions.primitives;
|
|
2045
2092
|
for (const ef of ctx.extracted) {
|
|
2046
2093
|
const file = ef.file.displayPath;
|
|
@@ -2219,7 +2266,7 @@ function dedupe(arr) {
|
|
|
2219
2266
|
return Array.from(new Set(arr));
|
|
2220
2267
|
}
|
|
2221
2268
|
|
|
2222
|
-
// src/scan/audit.ts
|
|
2269
|
+
// src/scanner/scan/audit.ts
|
|
2223
2270
|
var path4 = __toESM(require("path"), 1);
|
|
2224
2271
|
var MARKER_FILENAMES = ["UIDEX_PAGE.md", "UIDEX_FEATURE.md"];
|
|
2225
2272
|
function audit(opts) {
|
|
@@ -2349,17 +2396,38 @@ function audit(opts) {
|
|
|
2349
2396
|
}
|
|
2350
2397
|
}
|
|
2351
2398
|
if (lint) {
|
|
2399
|
+
const dynamicAttrRe = /\bdata-uidex(?:-(region|widget|primitive))?\s*=\s*\{/g;
|
|
2352
2400
|
for (const f of files) {
|
|
2353
|
-
const lines = f.content.split("\n");
|
|
2354
|
-
const candidateRe = /<(button|a|input|select|textarea)(?=[\s/>])([^>]*)>/g;
|
|
2355
2401
|
let m;
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
const idx = m.index;
|
|
2402
|
+
dynamicAttrRe.lastIndex = 0;
|
|
2403
|
+
while ((m = dynamicAttrRe.exec(f.content)) !== null) {
|
|
2404
|
+
const kind = m[1] ?? "element";
|
|
2360
2405
|
let line = 1;
|
|
2361
|
-
for (let i = 0; i <
|
|
2362
|
-
|
|
2406
|
+
for (let i = 0; i < m.index; i++) if (f.content[i] === "\n") line++;
|
|
2407
|
+
const attrName = m[1] ? `data-uidex-${m[1]}` : "data-uidex";
|
|
2408
|
+
diagnostics.push({
|
|
2409
|
+
code: "dynamic-attr",
|
|
2410
|
+
severity: "warning",
|
|
2411
|
+
message: `\`${attrName}={\u2026}\` uses a dynamic expression; the scanner cannot resolve the ${kind} id statically`,
|
|
2412
|
+
file: f.displayPath,
|
|
2413
|
+
line,
|
|
2414
|
+
hint: dynamicAttrHint(kind)
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
if (lint) {
|
|
2420
|
+
for (const f of files) {
|
|
2421
|
+
const tagRe = /<(button|a|input|select|textarea)(?=[\s/>])/g;
|
|
2422
|
+
let m;
|
|
2423
|
+
while ((m = tagRe.exec(f.content)) !== null) {
|
|
2424
|
+
const afterTag = m.index + m[0].length;
|
|
2425
|
+
const closeIdx = findJsxOpeningEnd(f.content, afterTag);
|
|
2426
|
+
if (closeIdx === -1) continue;
|
|
2427
|
+
const attrs = f.content.slice(afterTag, closeIdx);
|
|
2428
|
+
if (attrs.includes("data-uidex")) continue;
|
|
2429
|
+
let line = 1;
|
|
2430
|
+
for (let i = 0; i < m.index; i++) if (f.content[i] === "\n") line++;
|
|
2363
2431
|
diagnostics.push({
|
|
2364
2432
|
code: "missing-element-annotation",
|
|
2365
2433
|
severity: "info",
|
|
@@ -2374,6 +2442,15 @@ function audit(opts) {
|
|
|
2374
2442
|
const primitives = registry.list("primitive");
|
|
2375
2443
|
const byName = /* @__PURE__ */ new Map();
|
|
2376
2444
|
for (const p2 of primitives) byName.set(p2.id, p2);
|
|
2445
|
+
const declaredFeatures = /* @__PURE__ */ new Map();
|
|
2446
|
+
for (const ef of extracted) {
|
|
2447
|
+
if (!ef.metadata) continue;
|
|
2448
|
+
for (const m of ef.metadata) {
|
|
2449
|
+
if (m.features && m.features.length > 0) {
|
|
2450
|
+
declaredFeatures.set(ef.file.displayPath, new Set(m.features));
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2377
2454
|
for (const f of files) {
|
|
2378
2455
|
const importRe = /import\s+(?:[^'"]+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
2379
2456
|
let m;
|
|
@@ -2388,14 +2465,19 @@ function audit(opts) {
|
|
|
2388
2465
|
if (!scope) continue;
|
|
2389
2466
|
const [kind, id] = scope.split(":");
|
|
2390
2467
|
const importerSegments = f.displayPath.split("/");
|
|
2391
|
-
if (
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
});
|
|
2468
|
+
if (importerSegments.includes(id) && importerSegments.includes(kind + "s")) {
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
if (kind === "feature" && importerSegments.includes(id)) continue;
|
|
2472
|
+
if (kind === "feature" && declaredFeatures.get(f.displayPath)?.has(id)) {
|
|
2473
|
+
continue;
|
|
2398
2474
|
}
|
|
2475
|
+
diagnostics.push({
|
|
2476
|
+
code: "scope-leak",
|
|
2477
|
+
severity: "warning",
|
|
2478
|
+
message: `Primitive "${primitive.id}" is scoped to ${scope} but is imported from ${f.displayPath}`,
|
|
2479
|
+
file: f.displayPath
|
|
2480
|
+
});
|
|
2399
2481
|
}
|
|
2400
2482
|
}
|
|
2401
2483
|
}
|
|
@@ -2555,6 +2637,63 @@ function extractEntitiesArray(source) {
|
|
|
2555
2637
|
}
|
|
2556
2638
|
return null;
|
|
2557
2639
|
}
|
|
2640
|
+
function findJsxOpeningEnd(src, start) {
|
|
2641
|
+
let i = start;
|
|
2642
|
+
while (i < src.length) {
|
|
2643
|
+
const ch = src[i];
|
|
2644
|
+
if (ch === ">" || ch === "/" && src[i + 1] === ">") return i;
|
|
2645
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
2646
|
+
i = skipString2(src, i);
|
|
2647
|
+
} else if (ch === "{") {
|
|
2648
|
+
i = skipBraces(src, i);
|
|
2649
|
+
} else {
|
|
2650
|
+
i++;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
return -1;
|
|
2654
|
+
}
|
|
2655
|
+
function skipString2(src, start) {
|
|
2656
|
+
const quote = src[start];
|
|
2657
|
+
let i = start + 1;
|
|
2658
|
+
while (i < src.length) {
|
|
2659
|
+
if (src[i] === "\\" && quote !== "`") {
|
|
2660
|
+
i += 2;
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (quote === "`" && src[i] === "$" && src[i + 1] === "{") {
|
|
2664
|
+
i = skipBraces(src, i + 1);
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (src[i] === quote) return i + 1;
|
|
2668
|
+
i++;
|
|
2669
|
+
}
|
|
2670
|
+
return i;
|
|
2671
|
+
}
|
|
2672
|
+
function skipBraces(src, start) {
|
|
2673
|
+
let depth = 1;
|
|
2674
|
+
let i = start + 1;
|
|
2675
|
+
while (i < src.length && depth > 0) {
|
|
2676
|
+
const ch = src[i];
|
|
2677
|
+
if (ch === "{") {
|
|
2678
|
+
depth++;
|
|
2679
|
+
i++;
|
|
2680
|
+
} else if (ch === "}") {
|
|
2681
|
+
depth--;
|
|
2682
|
+
i++;
|
|
2683
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
2684
|
+
i = skipString2(src, i);
|
|
2685
|
+
} else {
|
|
2686
|
+
i++;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
return i;
|
|
2690
|
+
}
|
|
2691
|
+
function dynamicAttrHint(kind) {
|
|
2692
|
+
if (kind === "region") {
|
|
2693
|
+
return `Use a string literal: \`data-uidex-region="id"\`, or declare the region via \`export const uidex = { region: "id" } as const satisfies Uidex.Region\` on the file that passes the region value`;
|
|
2694
|
+
}
|
|
2695
|
+
return `The scanner requires string-literal attribute values. If this component forwards the annotation via a prop, restructure so the caller provides the annotated element directly (e.g. via a slot or render prop) with a string-literal \`data-uidex\` attribute`;
|
|
2696
|
+
}
|
|
2558
2697
|
function stableStringify(value) {
|
|
2559
2698
|
return JSON.stringify(value, stableReplacer);
|
|
2560
2699
|
}
|
|
@@ -2569,7 +2708,7 @@ function stableReplacer(_key, value) {
|
|
|
2569
2708
|
return value;
|
|
2570
2709
|
}
|
|
2571
2710
|
|
|
2572
|
-
// src/scan/emit.ts
|
|
2711
|
+
// src/scanner/scan/emit.ts
|
|
2573
2712
|
function sortById(arr) {
|
|
2574
2713
|
return [...arr].sort((a, b) => a.id.localeCompare(b.id));
|
|
2575
2714
|
}
|
|
@@ -2693,6 +2832,7 @@ function emit(opts) {
|
|
|
2693
2832
|
lines.push(" export interface Feature {");
|
|
2694
2833
|
lines.push(" feature: FeatureId | false");
|
|
2695
2834
|
lines.push(" name?: string");
|
|
2835
|
+
lines.push(" features?: readonly FeatureId[]");
|
|
2696
2836
|
lines.push(" acceptance?: readonly string[]");
|
|
2697
2837
|
lines.push(" description?: string");
|
|
2698
2838
|
lines.push(" }");
|
|
@@ -2707,6 +2847,11 @@ function emit(opts) {
|
|
|
2707
2847
|
lines.push(" acceptance?: readonly string[]");
|
|
2708
2848
|
lines.push(" description?: string");
|
|
2709
2849
|
lines.push(" }");
|
|
2850
|
+
lines.push(" export interface Region {");
|
|
2851
|
+
lines.push(" region: RegionId | false");
|
|
2852
|
+
lines.push(" name?: string");
|
|
2853
|
+
lines.push(" description?: string");
|
|
2854
|
+
lines.push(" }");
|
|
2710
2855
|
lines.push(" export interface Flow {");
|
|
2711
2856
|
lines.push(" flow: FlowId");
|
|
2712
2857
|
lines.push(" name?: string");
|
|
@@ -2749,7 +2894,7 @@ function emit(opts) {
|
|
|
2749
2894
|
return lines.join("\n");
|
|
2750
2895
|
}
|
|
2751
2896
|
|
|
2752
|
-
// src/scan/git.ts
|
|
2897
|
+
// src/scanner/scan/git.ts
|
|
2753
2898
|
var import_node_child_process = require("child_process");
|
|
2754
2899
|
function runGit(args, cwd) {
|
|
2755
2900
|
try {
|
|
@@ -2781,7 +2926,7 @@ function parseGitHubRef(ref) {
|
|
|
2781
2926
|
return m ? m[1] : null;
|
|
2782
2927
|
}
|
|
2783
2928
|
|
|
2784
|
-
// src/scan/scaffold.ts
|
|
2929
|
+
// src/scanner/scan/scaffold.ts
|
|
2785
2930
|
var fs3 = __toESM(require("fs"), 1);
|
|
2786
2931
|
var path5 = __toESM(require("path"), 1);
|
|
2787
2932
|
function scaffoldWidgetSpec(opts) {
|
|
@@ -2844,7 +2989,7 @@ function renderSpec(args) {
|
|
|
2844
2989
|
return lines.join("\n");
|
|
2845
2990
|
}
|
|
2846
2991
|
|
|
2847
|
-
// src/scan/pipeline.ts
|
|
2992
|
+
// src/scanner/scan/pipeline.ts
|
|
2848
2993
|
var fs4 = __toESM(require("fs"), 1);
|
|
2849
2994
|
var path6 = __toESM(require("path"), 1);
|
|
2850
2995
|
function runScan(opts = {}) {
|
|
@@ -2918,18 +3063,18 @@ function writeScanResult(result) {
|
|
|
2918
3063
|
fs4.writeFileSync(result.outputPath, result.generated, "utf8");
|
|
2919
3064
|
}
|
|
2920
3065
|
|
|
2921
|
-
// src/scan/cli.ts
|
|
3066
|
+
// src/scanner/scan/cli.ts
|
|
2922
3067
|
var fs7 = __toESM(require("fs"), 1);
|
|
2923
3068
|
var path9 = __toESM(require("path"), 1);
|
|
2924
3069
|
|
|
2925
|
-
// src/scan/ai/index.ts
|
|
3070
|
+
// src/scanner/scan/ai/index.ts
|
|
2926
3071
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
2927
3072
|
|
|
2928
|
-
// src/scan/ai/providers/claude.ts
|
|
3073
|
+
// src/scanner/scan/ai/providers/claude.ts
|
|
2929
3074
|
var fs6 = __toESM(require("fs"), 1);
|
|
2930
3075
|
var path8 = __toESM(require("path"), 1);
|
|
2931
3076
|
|
|
2932
|
-
// src/scan/ai/templates.ts
|
|
3077
|
+
// src/scanner/scan/ai/templates.ts
|
|
2933
3078
|
var fs5 = __toESM(require("fs"), 1);
|
|
2934
3079
|
var path7 = __toESM(require("path"), 1);
|
|
2935
3080
|
function templatePath(rel) {
|
|
@@ -2956,15 +3101,16 @@ function readTemplate(rel) {
|
|
|
2956
3101
|
return fs5.readFileSync(templatePath(rel), "utf8");
|
|
2957
3102
|
}
|
|
2958
3103
|
|
|
2959
|
-
// src/scan/ai/providers/claude.ts
|
|
3104
|
+
// src/scanner/scan/ai/providers/claude.ts
|
|
2960
3105
|
var CLAUDE_FILES = [
|
|
2961
3106
|
{ dest: ".claude/rules/uidex.md", template: "claude/rules.md" },
|
|
2962
|
-
{ dest: ".claude/commands/uidex/audit.md", template: "claude/audit.md" }
|
|
3107
|
+
{ dest: ".claude/commands/uidex/audit.md", template: "claude/audit.md" },
|
|
3108
|
+
{ dest: ".claude/commands/uidex/api.md", template: "claude/api.md" }
|
|
2963
3109
|
];
|
|
2964
3110
|
var claudeProvider = {
|
|
2965
3111
|
id: "claude",
|
|
2966
3112
|
label: "Claude Code",
|
|
2967
|
-
description: "Adds .claude/rules/uidex.md and
|
|
3113
|
+
description: "Adds .claude/rules/uidex.md, /uidex:audit, and /uidex:api slash commands.",
|
|
2968
3114
|
async install({ cwd, force }) {
|
|
2969
3115
|
const changes = [];
|
|
2970
3116
|
for (const file of CLAUDE_FILES) {
|
|
@@ -3012,13 +3158,13 @@ function cleanupEmpty(dir) {
|
|
|
3012
3158
|
}
|
|
3013
3159
|
}
|
|
3014
3160
|
|
|
3015
|
-
// src/scan/ai/providers/index.ts
|
|
3161
|
+
// src/scanner/scan/ai/providers/index.ts
|
|
3016
3162
|
var PROVIDERS = [claudeProvider];
|
|
3017
3163
|
function getProvider(id) {
|
|
3018
3164
|
return PROVIDERS.find((p2) => p2.id === id);
|
|
3019
3165
|
}
|
|
3020
3166
|
|
|
3021
|
-
// src/scan/ai/index.ts
|
|
3167
|
+
// src/scanner/scan/ai/index.ts
|
|
3022
3168
|
async function runAiCommand(opts) {
|
|
3023
3169
|
const { cwd, argv } = opts;
|
|
3024
3170
|
const sub = argv[0];
|
|
@@ -3129,8 +3275,8 @@ function err(exitCode, stderr) {
|
|
|
3129
3275
|
return { exitCode, stdout: "", stderr };
|
|
3130
3276
|
}
|
|
3131
3277
|
|
|
3132
|
-
// src/
|
|
3133
|
-
function
|
|
3278
|
+
// src/scanner/cli/parse-args.ts
|
|
3279
|
+
function parseArgs(args) {
|
|
3134
3280
|
const positional = [];
|
|
3135
3281
|
const flags = {};
|
|
3136
3282
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -3154,9 +3300,11 @@ function parseFlags2(args) {
|
|
|
3154
3300
|
}
|
|
3155
3301
|
return { positional, flags };
|
|
3156
3302
|
}
|
|
3303
|
+
|
|
3304
|
+
// src/scanner/scan/cli.ts
|
|
3157
3305
|
async function run(opts) {
|
|
3158
3306
|
const cwd = opts.cwd ?? process.cwd();
|
|
3159
|
-
const { positional, flags } =
|
|
3307
|
+
const { positional, flags } = parseArgs(opts.argv);
|
|
3160
3308
|
const command = positional[0] ?? "help";
|
|
3161
3309
|
const writer = createWriter();
|
|
3162
3310
|
try {
|
|
@@ -3200,6 +3348,10 @@ function helpText2() {
|
|
|
3200
3348
|
" scan [flags] Run the scanner pipeline",
|
|
3201
3349
|
" scaffold widget <id> Emit a Playwright spec from a widget's acceptance",
|
|
3202
3350
|
" ai <install|uninstall|providers> Manage AI assistant integrations",
|
|
3351
|
+
" api <METHOD> <PATH> Call the uidex API",
|
|
3352
|
+
" api --list Show available API routes",
|
|
3353
|
+
" api login Authenticate via browser",
|
|
3354
|
+
" api login --token <tok> Store an auth token directly",
|
|
3203
3355
|
"",
|
|
3204
3356
|
"Flags:",
|
|
3205
3357
|
" --check Verify the on-disk gen file matches a fresh scan; exit non-zero on drift (read-only)",
|