youiyoux 0.1.2 → 0.1.4
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 +10 -0
- package/dist/chunks/{chunk-S7UV34H5.js → chunk-42YJX3W5.js} +1 -3
- package/dist/chunks/{chunk-FUB7B3VK.js → chunk-4BBYLTZX.js} +10 -10
- package/dist/chunks/chunk-52KOKYY6.js +8710 -0
- package/dist/chunks/chunk-FJZFCAL5.js +129 -0
- package/dist/chunks/chunk-FVVL5JCY.js +24 -0
- package/dist/chunks/{chunk-UHPK62Q5.js → chunk-M5XZWCPS.js} +240 -60
- package/dist/chunks/{chunk-ZLRFD5JI.js → chunk-NYDJYMID.js} +263 -16
- package/dist/chunks/{src-7ENJHYG4.js → chunk-RDTHHRZN.js} +319 -165
- package/dist/chunks/{chunk-E4HNK7VF.js → chunk-WNTS7ZO4.js} +1 -1
- package/dist/chunks/chunk-XAEMVEYX.js +188 -0
- package/dist/chunks/{chunk-CLNMVQZJ.js → chunk-YFCMTTXS.js} +708 -494
- package/dist/chunks/chunk-YJ7WMBTT.js +13761 -0
- package/dist/chunks/src-5IP4NGKT.js +115 -0
- package/dist/chunks/src-6CJR547Y.js +124 -0
- package/dist/chunks/{src-WQWYJ6IQ.js → src-BPETAFLW.js} +175 -76
- package/dist/chunks/{src-B742AYFC.js → src-LLV7IY6W.js} +55 -16
- package/dist/chunks/src-RHNZELGE.js +306 -0
- package/dist/chunks/{src-4KFNL3MY.js → src-SFKFERZ2.js} +18 -8
- package/dist/chunks/{src-VEHGMKYV.js → src-VL2ZYO6X.js} +4 -4
- package/dist/hub/assets/Badge-CUJUit4d.js +1 -0
- package/dist/hub/assets/CandidatePanel-CnbK4S0j.js +1 -0
- package/dist/hub/assets/CatalogScreen-B5OVL-AR.js +1 -0
- package/dist/hub/assets/ChangesScreen-GwGqJwxN.js +1 -0
- package/dist/hub/assets/CheatSheet-BLSOq-xa.js +1 -0
- package/dist/hub/assets/CodeBlock-CsiGFgvI.js +1 -0
- package/dist/hub/assets/CommandPalette-CAWQfdXJ.js +1 -0
- package/dist/hub/assets/CopyButton-DE8drl4P.js +1 -0
- package/dist/hub/assets/Disclosure-B1kTgPgk.js +1 -0
- package/dist/hub/assets/DiscoverScreen-Tl_Z6mgY.js +2 -0
- package/dist/hub/assets/DraftEditor-yIDapXTs.js +5 -0
- package/dist/hub/assets/Field-LwrJXoOs.js +1 -0
- package/dist/hub/assets/GapsScreen-DXZAlm6v.js +1 -0
- package/dist/hub/assets/HomeScreen-CEpiVhOa.js +1 -0
- package/dist/hub/assets/InventoryScreen-CEG3Pmw_.js +1 -0
- package/dist/hub/assets/KindIcon-BJ6bz-ny.js +1 -0
- package/dist/hub/assets/LibraryScreen-IaVnxrX9.js +1 -0
- package/dist/hub/assets/LicenceBadge-CLCUMMHE.js +1 -0
- package/dist/hub/assets/Opened-DJyf4Hnj.js +1 -0
- package/dist/hub/assets/Preview-C069zVMP.js +1 -0
- package/dist/hub/assets/RuleDetail-B4g2-TRe.js +3 -0
- package/dist/hub/assets/RulesScreen-CAaXIWjO.js +3 -0
- package/dist/hub/assets/ScreenImage-DcI7_G3O.js +1 -0
- package/dist/hub/assets/ScreensScreen-DwczCjZD.js +1 -0
- package/dist/hub/assets/SearchField-DaCpNAj6.js +1 -0
- package/dist/hub/assets/Section-BTnvUq_W.js +1 -0
- package/dist/hub/assets/Segmented-Dzi1x49S.js +1 -0
- package/dist/hub/assets/ServiceDown-Bv-BPXro.js +1 -0
- package/dist/hub/assets/SpecimenCapture-DlsjshVE.js +1 -0
- package/dist/hub/assets/Specimens-C-lHp7sr.js +2 -0
- package/dist/hub/assets/StackScreen-DkmELsqR.js +1 -0
- package/dist/hub/assets/StubScreens-Bt7lWLVK.js +1 -0
- package/dist/hub/assets/StyleScreen-tq1tbRBo.js +1 -0
- package/dist/hub/assets/Swatch-KDz4qxdM.js +1 -0
- package/dist/hub/assets/Tabs-1QQDS0I6.js +1 -0
- package/dist/hub/assets/TokenDetail-DvCw6GqC.js +1 -0
- package/dist/hub/assets/TokenValue-CsPRc2ai.js +1 -0
- package/dist/hub/assets/TokensScreen-DoENV8Xy.js +1 -0
- package/dist/hub/assets/arrow-right-DcbNDzqs.js +1 -0
- package/dist/hub/assets/bot-B3gC9oUR.js +1 -0
- package/dist/hub/assets/button-6mvMVtpb.js +1 -0
- package/dist/hub/assets/button.demo-DuU0DQbv.js +1 -0
- package/dist/hub/assets/card.demo-CufAxMAA.js +1 -0
- package/dist/hub/assets/chevron-down-CzN07vMi.js +1 -0
- package/dist/hub/assets/circle-alert-DT2LC4yf.js +1 -0
- package/dist/hub/assets/circle-check-DkwBMT62.js +1 -0
- package/dist/hub/assets/client-D7whEyep.js +1 -0
- package/dist/hub/assets/clsx-DB0hHKMi.js +1 -0
- package/dist/hub/assets/compass-BkKOZPYe.js +1 -0
- package/dist/hub/assets/contrast-BkNHUAPa.js +1 -0
- package/dist/hub/assets/createLucideIcon-CGDJWtKO.js +1 -0
- package/dist/hub/assets/discover-XjJEViwk.js +1 -0
- package/dist/hub/assets/dist-BYQYaPBV.js +1 -0
- package/dist/hub/assets/dist-Bk4srtDn.js +41 -0
- package/dist/hub/assets/dist-BogNzeK9.js +1 -0
- package/dist/hub/assets/dist-D0z7Bvsy.js +1 -0
- package/dist/hub/assets/download-BQI-Si9R.js +1 -0
- package/dist/hub/assets/engine-CMmt9yEz.js +1 -0
- package/dist/hub/assets/esm-NPpg23M8.js +1 -0
- package/dist/hub/assets/family-tiles-CyTdy_cP.js +1 -0
- package/dist/hub/assets/format-DxAYNTN_.js +1 -0
- package/dist/hub/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/hub/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/hub/assets/geist-latin-core-vkBVTr4s.woff2 +0 -0
- package/dist/hub/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/hub/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/hub/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/hub/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-core-DccGGzmg.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/hub/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/hub/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/hub/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/hub/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/hub/assets/git-pull-request-arrow-RqUUIRCU.js +1 -0
- package/dist/hub/assets/input.demo-238gqWpz.js +1 -0
- package/dist/hub/assets/inventory-BI8QBdar.js +1 -0
- package/dist/hub/assets/inventory-source-lZYzcA8O.js +1 -0
- package/dist/hub/assets/jsx-runtime-D6zNwnaE.js +9 -0
- package/dist/hub/assets/layers-DfzdK_wG.js +1 -0
- package/dist/hub/assets/layout-grid-xFY9L9Oc.js +1 -0
- package/dist/hub/assets/library-W5R8GwvG.js +1 -0
- package/dist/hub/assets/main-7snyIdoi.css +2 -0
- package/dist/hub/assets/main-B9CIkk5m.js +2 -0
- package/dist/hub/assets/minus-BDifFhOo.js +1 -0
- package/dist/hub/assets/monitor-X_lELkcU.js +1 -0
- package/dist/hub/assets/mouse-pointer-click-CVJ6hWg8.js +1 -0
- package/dist/hub/assets/paintbrush-DOkEgSTO.js +1 -0
- package/dist/hub/assets/plural-BbGQPQKA.js +1 -0
- package/dist/hub/assets/preview-BA8CPsKb.css +2 -0
- package/dist/hub/assets/preview-DUm13vr1.js +3 -0
- package/dist/hub/assets/protocol-caP6umNL.js +1 -0
- package/dist/hub/assets/react-DB-4Zxce.js +1 -0
- package/dist/hub/assets/shield-check-0ka0kQct.js +1 -0
- package/dist/hub/assets/trash-B7aEfJps.js +1 -0
- package/dist/hub/assets/triangle-alert-Dkdi9eDR.js +1 -0
- package/dist/hub/assets/try-live-zX3CSd3P.js +1 -0
- package/dist/hub/assets/utils-BoaXfs8y.js +1 -0
- package/dist/hub/assets/values-DzxR_TlI.js +1 -0
- package/dist/hub/assets/worker-Bh-wSp42.js +1 -0
- package/dist/hub/assets/workflow-jpV8wh-U.js +1 -0
- package/dist/hub/assets/x-MLbFVa4v.js +1 -0
- package/dist/hub/favicon.svg +1 -0
- package/dist/hub/index.html +35 -0
- package/dist/hub/kit/candidates.json +1 -0
- package/dist/hub/kit/chunks/chunk-2NYFUOIV.js +14 -0
- package/dist/hub/kit/chunks/chunk-DCANE7XH.js +1 -0
- package/dist/hub/kit/chunks/chunk-FTGHCY4I.js +14 -0
- package/dist/hub/kit/chunks/chunk-IBF3UTJF.js +33 -0
- package/dist/hub/kit/chunks/chunk-LLXY4VSH.js +1 -0
- package/dist/hub/kit/chunks/chunk-PUPZMW4P.js +14 -0
- package/dist/hub/kit/chunks/chunk-PWLNZQET.js +1 -0
- package/dist/hub/kit/chunks/chunk-ULXWVOKJ.js +1 -0
- package/dist/hub/kit/chunks/chunk-WM4BGGMK.js +1 -0
- package/dist/hub/kit/chunks/chunk-ZQUNRK72.js +1 -0
- package/dist/hub/kit/class-variance-authority.js +1 -0
- package/dist/hub/kit/clsx.js +1 -0
- package/dist/hub/kit/fonts/geist-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-cyrillic-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-latin-ext-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-latin-wght-normal.woff2 +0 -0
- package/dist/hub/kit/fonts/geist-vietnamese-wght-normal.woff2 +0 -0
- package/dist/hub/kit/frame.css.src +73 -0
- package/dist/hub/kit/frame.js +2 -0
- package/dist/hub/kit/lucide-react.js +1878 -0
- package/dist/hub/kit/preview.html +13 -0
- package/dist/hub/kit/radix-ui-react-dialog.js +41 -0
- package/dist/hub/kit/radix-ui-react-label.js +1 -0
- package/dist/hub/kit/radix-ui-react-separator.js +1 -0
- package/dist/hub/kit/radix-ui-react-slot.js +1 -0
- package/dist/hub/kit/radix-ui-react-switch.js +1 -0
- package/dist/hub/kit/radix-ui-react-tabs.js +1 -0
- package/dist/hub/kit/react-dom-client.js +1 -0
- package/dist/hub/kit/react-dom.js +1 -0
- package/dist/hub/kit/react-jsx-runtime.js +1 -0
- package/dist/hub/kit/react.js +1 -0
- package/dist/hub/kit/tailwind-merge.js +1 -0
- package/dist/hub/kit/tailwindcss/index.css +950 -0
- package/dist/hub/kit/tailwindcss/preflight.css +398 -0
- package/dist/hub/kit/tailwindcss/theme.css +510 -0
- package/dist/hub/kit/tailwindcss/utilities.css +1 -0
- package/dist/hub-demo/frame/button.demo.js +1 -0
- package/dist/hub-demo/frame/card.demo.js +1 -0
- package/dist/hub-demo/frame/demos.js +6 -0
- package/dist/hub-demo/frame/frame.css +770 -0
- package/dist/hub-demo/frame/input.demo.js +1 -0
- package/dist/hub-demo/preview/components/button.demo.tsx +47 -0
- package/dist/hub-demo/preview/components/button.tsx +31 -0
- package/dist/hub-demo/preview/components/card.demo.tsx +26 -0
- package/dist/hub-demo/preview/components/card.tsx +20 -0
- package/dist/hub-demo/preview/components/input.demo.tsx +39 -0
- package/dist/hub-demo/preview/components/input.tsx +19 -0
- package/dist/hub-demo/preview/components/utils.ts +5 -0
- package/dist/hub-demo/preview/system.css +161 -0
- package/dist/hub-demo/public/data/demo-app.usage.bedd9c82fe.json +1 -0
- package/dist/hub-demo/public/data/demo.catalog.a605de2281.json +1 -0
- package/dist/hub-demo/public/data/gaps.129a93538d.json +1 -0
- package/dist/hub-demo/public/data/inventory.165a4f6408.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.badge.ad0238784e.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.button.3249daff35.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.card.9ea88708ac.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.dialog.8b6574d93a.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.input.bdffb756a7.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.link-as-button.1ce7faa1f3.json +1 -0
- package/dist/hub-demo/public/data/inventory.console.tabs.813e694c44.json +1 -0
- package/dist/hub-demo/public/data/inventory.extras.e5d06c6ff4.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.badge.047a68f644.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.button.720938384e.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.card.d8df5f6873.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.link-as-button.9c1f8a55bf.json +1 -0
- package/dist/hub-demo/public/data/inventory.site.tabs.59bd0200b9.json +1 -0
- package/dist/hub-demo/public/data/manifest.json +39 -0
- package/dist/hub-demo/public/data/specimens.console.dark.046471907d.css +786 -0
- package/dist/hub-demo/public/data/specimens.console.light.aaffa2d213.css +786 -0
- package/dist/hub-demo/public/data/specimens.site.dark.94a6706908.css +92 -0
- package/dist/hub-demo/public/data/specimens.site.light.3ddbc8d28d.css +92 -0
- package/dist/hub-demo/public/previews/button.dark.webp +0 -0
- package/dist/hub-demo/public/previews/button.light.webp +0 -0
- package/dist/hub-demo/public/previews/card.dark.webp +0 -0
- package/dist/hub-demo/public/previews/card.light.webp +0 -0
- package/dist/hub-demo/public/previews/input.dark.webp +0 -0
- package/dist/hub-demo/public/previews/input.light.webp +0 -0
- package/dist/hub-previews.js +173 -0
- package/dist/hub-server.js +6329 -0
- package/dist/hub-snapshot.js +573 -0
- package/dist/schema/youi.gaps.schema.json +235 -0
- package/dist/schema/youi.request.schema.json +229 -0
- package/dist/tailwind-worker.js +3 -3
- package/dist/youi.js +10 -8
- package/dist/youiyoux.js +10 -8
- package/package.json +4 -4
- package/dist/chunks/chunk-3ZHGONNA.js +0 -21797
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { createRequire as __youiRequire } from "node:module"; const require = __youiRequire(import.meta.url);
|
|
1
|
+
import { createRequire as __youiRequire } from "node:module"; import { dirname as __youiDir } from "node:path"; import { fileURLToPath as __youiPath } from "node:url"; const require = __youiRequire(import.meta.url); const __youiDirname = __youiDir(__youiPath(import.meta.url));
|
|
2
2
|
import {
|
|
3
|
+
PICTURE_SCALE,
|
|
3
4
|
SCANNER_VERSION,
|
|
4
|
-
buildTokens,
|
|
5
5
|
checkInputFor,
|
|
6
|
-
contrastText,
|
|
7
|
-
cssValue,
|
|
8
|
-
gradePairs,
|
|
9
6
|
loadProject,
|
|
10
|
-
resolverLayout,
|
|
11
|
-
ringOffset,
|
|
12
7
|
ruleCapability,
|
|
13
8
|
runChecks,
|
|
14
9
|
scanProject,
|
|
15
|
-
|
|
10
|
+
screenSlug,
|
|
16
11
|
tokenImpact
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import
|
|
12
|
+
} from "./chunk-52KOKYY6.js";
|
|
13
|
+
import {
|
|
14
|
+
buildTokens,
|
|
15
|
+
contrastText,
|
|
16
|
+
cssValue,
|
|
17
|
+
gradePairs,
|
|
18
|
+
resolverLayout,
|
|
19
|
+
ringOffset,
|
|
20
|
+
systemPairs
|
|
21
|
+
} from "./chunk-YJ7WMBTT.js";
|
|
19
22
|
import {
|
|
20
23
|
BUILT_IN_CHECKS,
|
|
21
24
|
COMPONENT_STATES,
|
|
@@ -23,6 +26,10 @@ import {
|
|
|
23
26
|
ElementId,
|
|
24
27
|
LIBRARY_DIR,
|
|
25
28
|
LibraryEntry,
|
|
29
|
+
REQUEST_ID,
|
|
30
|
+
REQUEST_STATUSES,
|
|
31
|
+
RequestDoc,
|
|
32
|
+
RequestTarget,
|
|
26
33
|
RuleCheck,
|
|
27
34
|
RuleRollout,
|
|
28
35
|
RuleSpec,
|
|
@@ -38,10 +45,10 @@ import {
|
|
|
38
45
|
toPosix,
|
|
39
46
|
tokenValueProblems,
|
|
40
47
|
writeFileAtomic
|
|
41
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-NYDJYMID.js";
|
|
42
49
|
import {
|
|
43
50
|
__toESM
|
|
44
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-WNTS7ZO4.js";
|
|
45
52
|
|
|
46
53
|
// ../workflow/src/design-files.ts
|
|
47
54
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
@@ -356,10 +363,10 @@ async function git(cwd, args, options = {}) {
|
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
function gitWithInput(cwd, args, options) {
|
|
359
|
-
return new Promise((
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
360
367
|
const child = execFile("git", args, { cwd, maxBuffer: MAX_BUFFER, env: { ...process.env, ...options.env } }, (error, stdout, stderr) => {
|
|
361
368
|
if (error) reject(Object.assign(error, { stderr }));
|
|
362
|
-
else
|
|
369
|
+
else resolve(stdout);
|
|
363
370
|
});
|
|
364
371
|
child.stdin?.end(options.input);
|
|
365
372
|
});
|
|
@@ -407,12 +414,8 @@ async function isAncestor(top, commit, tip) {
|
|
|
407
414
|
return false;
|
|
408
415
|
}
|
|
409
416
|
}
|
|
410
|
-
async function listAt(top, commit, dir) {
|
|
411
|
-
const out = await gitOrNull(top, ["ls-tree", "--name-only", `${commit}:${dir}`]);
|
|
412
|
-
return out ? out.split("\n").filter(Boolean) : [];
|
|
413
|
-
}
|
|
414
|
-
async function listTreeAt(top, commit, dir) {
|
|
415
|
-
const out = await gitOrNull(top, ["ls-tree", "-r", "--name-only", `${commit}:${dir}`]);
|
|
417
|
+
async function listAt(top, commit, dir, options = {}) {
|
|
418
|
+
const out = await gitOrNull(top, ["ls-tree", ...options.recursive ? ["-r"] : [], "--name-only", `${commit}:${dir}`]);
|
|
416
419
|
return out ? out.split("\n").filter(Boolean) : [];
|
|
417
420
|
}
|
|
418
421
|
async function hasChanges(top, paths) {
|
|
@@ -436,14 +439,14 @@ async function fileDigest(file) {
|
|
|
436
439
|
return sha256(await readFile(file));
|
|
437
440
|
}
|
|
438
441
|
function streamDigest(cwd, args) {
|
|
439
|
-
return new Promise((
|
|
442
|
+
return new Promise((resolve, reject) => {
|
|
440
443
|
const hash = createHash("sha256");
|
|
441
444
|
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
442
445
|
let stderr = "";
|
|
443
446
|
child.stdout.on("data", (chunk) => hash.update(chunk));
|
|
444
447
|
child.stderr.on("data", (chunk) => stderr += chunk.toString());
|
|
445
448
|
child.on("error", reject);
|
|
446
|
-
child.on("close", (code) => code === 0 ?
|
|
449
|
+
child.on("close", (code) => code === 0 ? resolve(hash.digest("hex")) : reject(new GitError(`git ${args.join(" ")} failed in ${cwd}: ${stderr.trim()}`)));
|
|
447
450
|
});
|
|
448
451
|
}
|
|
449
452
|
var FALLBACK_IDENTITY = { name: "YouIyouX rule editor", email: "rule-editor@youiyoux.local" };
|
|
@@ -685,8 +688,10 @@ var DesignFiles = class {
|
|
|
685
688
|
}
|
|
686
689
|
/** The path a rule lives at, or would be created at. */
|
|
687
690
|
async rulePath(commit, ruleId) {
|
|
688
|
-
|
|
689
|
-
|
|
691
|
+
return (await this.ruleFiles(commit)).get(ruleId)?.path ?? this.newRulePath(ruleId);
|
|
692
|
+
}
|
|
693
|
+
/** Where a rule not in the design repository yet is created. */
|
|
694
|
+
newRulePath(ruleId) {
|
|
690
695
|
return `${this.rulesDir}/${parseId(ruleId)?.name ?? "new-rule"}.rule.yaml`;
|
|
691
696
|
}
|
|
692
697
|
async contractPath(commit, componentId) {
|
|
@@ -712,7 +717,7 @@ var DesignFiles = class {
|
|
|
712
717
|
const existing = rules.get(rule.id);
|
|
713
718
|
const sibling = [...rules.values()][0]?.text;
|
|
714
719
|
const schemaRef = (sibling ? (0, import_yaml2.parse)(sibling)?.$schema : void 0) ?? DEFAULT_SCHEMA_REF;
|
|
715
|
-
const path = existing?.path ??
|
|
720
|
+
const path = existing?.path ?? this.newRulePath(rule.id);
|
|
716
721
|
const files = {};
|
|
717
722
|
const ruleText = renderRuleYaml(rule, existing?.text ?? null, schemaRef);
|
|
718
723
|
if (ruleText !== existing?.text) files[path] = ruleText;
|
|
@@ -735,7 +740,7 @@ var DesignFiles = class {
|
|
|
735
740
|
/** Every file in the tokens directory at `commit` (design-root-relative path → text): what a token build reads. */
|
|
736
741
|
async tokenTree(commit) {
|
|
737
742
|
const dir = this.tokensDir;
|
|
738
|
-
const names = await
|
|
743
|
+
const names = await listAt(this.repo.top, commit, this.top(dir), { recursive: true });
|
|
739
744
|
const entries = await Promise.all(names.map(async (name) => [`${dir}/${name}`, await this.read(commit, `${dir}/${name}`) ?? ""]));
|
|
740
745
|
return new Map(entries);
|
|
741
746
|
}
|
|
@@ -801,11 +806,10 @@ var scannerExecution = {
|
|
|
801
806
|
// ../workflow/src/http.ts
|
|
802
807
|
import { createServer } from "node:http";
|
|
803
808
|
import { timingSafeEqual } from "node:crypto";
|
|
804
|
-
import { readFile as readFile2
|
|
805
|
-
import { extname, join as join3, normalize, resolve, sep } from "node:path";
|
|
809
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
806
810
|
|
|
807
811
|
// ../workflow/src/policy.ts
|
|
808
|
-
var PERSON_ONLY = /* @__PURE__ */ new Set(["approve", "handoff"]);
|
|
812
|
+
var PERSON_ONLY = /* @__PURE__ */ new Set(["approve", "handoff", "close"]);
|
|
809
813
|
var WorkflowError = class extends Error {
|
|
810
814
|
name = "WorkflowError";
|
|
811
815
|
code;
|
|
@@ -817,7 +821,7 @@ var WorkflowError = class extends Error {
|
|
|
817
821
|
}
|
|
818
822
|
};
|
|
819
823
|
function authorize(actor, action) {
|
|
820
|
-
if (PERSON_ONLY.has(action) && actor.kind !== "person") throw new WorkflowError("forbidden", `an ${actor.kind} cannot ${action}: approving a design change is a person's decision`);
|
|
824
|
+
if (PERSON_ONLY.has(action) && actor.kind !== "person") throw new WorkflowError("forbidden", action === "close" ? `an ${actor.kind} cannot close or reopen a request: mark it addressed, and a person closes it` : `an ${actor.kind} cannot ${action}: approving a design change is a person's decision`);
|
|
821
825
|
}
|
|
822
826
|
|
|
823
827
|
// ../workflow/src/http.ts
|
|
@@ -846,22 +850,14 @@ var CreateInput = external_exports.union([
|
|
|
846
850
|
external_exports.discriminatedUnion("mode", [external_exports.object({ kind: external_exports.literal("rule").optional(), mode: external_exports.literal("edit"), ruleId: ElementId }), external_exports.object({ kind: external_exports.literal("rule").optional(), mode: external_exports.literal("create") })])
|
|
847
851
|
]);
|
|
848
852
|
var SaveInput = external_exports.object({ revision: external_exports.number().int().positive(), content: external_exports.union([DraftContentInput, StyleContentInput, LibraryContentInput]) });
|
|
853
|
+
var RequestInput = external_exports.object({ kind: external_exports.enum(["comment", "change"]), body: external_exports.string().min(1).max(4e3), target: RequestTarget });
|
|
854
|
+
var ReplyInput = external_exports.object({ body: external_exports.string().min(1).max(4e3) });
|
|
855
|
+
var StatusInput = external_exports.object({ status: external_exports.enum(REQUEST_STATUSES), body: external_exports.string().max(4e3).optional() });
|
|
849
856
|
function styleContent(content) {
|
|
850
857
|
return { ...content, tokens: content.tokens.map((t) => Object.fromEntries(Object.entries(t).filter(([, v]) => v !== void 0))) };
|
|
851
858
|
}
|
|
852
859
|
var ApproveInput = external_exports.object({ revision: external_exports.number().int().positive(), previewId: external_exports.string().min(1).max(40), acknowledgeContrast: external_exports.array(external_exports.string().min(1).max(600)).max(1e3).optional() });
|
|
853
860
|
var STATUS = { "bad-request": 400, forbidden: 403, "not-found": 404, conflict: 409, stale: 409, invalid: 422, internal: 500 };
|
|
854
|
-
var TYPES = {
|
|
855
|
-
".html": "text/html; charset=utf-8",
|
|
856
|
-
".js": "text/javascript; charset=utf-8",
|
|
857
|
-
".css": "text/css; charset=utf-8",
|
|
858
|
-
".json": "application/json; charset=utf-8",
|
|
859
|
-
".svg": "image/svg+xml",
|
|
860
|
-
".webp": "image/webp",
|
|
861
|
-
".png": "image/png",
|
|
862
|
-
".woff2": "font/woff2",
|
|
863
|
-
".ico": "image/x-icon"
|
|
864
|
-
};
|
|
865
861
|
var LOOPBACK_HOST = /^(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?$/;
|
|
866
862
|
var LOOPBACK_ORIGIN = /^http:\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?$/;
|
|
867
863
|
var BASE_HEADERS = { "x-content-type-options": "nosniff", "x-frame-options": "DENY", "content-security-policy": "frame-ancestors 'none'" };
|
|
@@ -927,6 +923,31 @@ async function api(req, res, path, options) {
|
|
|
927
923
|
if ("kind" in input && input.kind === "library") return send(res, 201, await workflow.create(actor, { kind: "library", content: input.content }));
|
|
928
924
|
return send(res, 201, await workflow.create(actor, "kind" in input && input.kind === "style" ? { kind: "style", ...input.content ? { content: styleContent(input.content) } : {} } : input));
|
|
929
925
|
}
|
|
926
|
+
if (path === "/api/requests" && method === "GET") return send(res, 200, await (await workflow.requests()).list());
|
|
927
|
+
if (path === "/api/requests" && method === "POST") return send(res, 201, await (await workflow.requests()).create(actor, parse2(RequestInput, await readJson(req))));
|
|
928
|
+
const request = /^\/api\/requests\/(r-[0-9a-f]{10})(?:\/(replies|status|compare|before\.webp|after\.webp))?$/.exec(path);
|
|
929
|
+
if (request) {
|
|
930
|
+
const [, id, action] = request;
|
|
931
|
+
const store = await workflow.requests();
|
|
932
|
+
if (!action && method === "GET") return send(res, 200, await store.get(id));
|
|
933
|
+
if (action === "compare" && method === "GET") {
|
|
934
|
+
const { before, after } = await store.compare(id);
|
|
935
|
+
const view = (picture, which) => picture ? { ...picture, file: void 0, url: `/api/requests/${id}/${which}.webp` } : null;
|
|
936
|
+
return send(res, 200, { before: view(before, "before"), after: view(after, "after") });
|
|
937
|
+
}
|
|
938
|
+
if ((action === "before.webp" || action === "after.webp") && method === "GET") {
|
|
939
|
+
const picture = (await store.compare(id))[action === "before.webp" ? "before" : "after"];
|
|
940
|
+
if (!picture) throw new WorkflowError("not-found", `${id} has no ${action.replace(".webp", "")} picture`);
|
|
941
|
+
res.writeHead(200, { ...BASE_HEADERS, "content-type": "image/webp", "cache-control": "no-store" });
|
|
942
|
+
res.end(await readFile2(picture.file));
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (action === "replies" && method === "POST") return send(res, 200, await store.reply(actor, id, parse2(ReplyInput, await readJson(req)).body));
|
|
946
|
+
if (action === "status" && method === "POST") {
|
|
947
|
+
const input = parse2(StatusInput, await readJson(req));
|
|
948
|
+
return send(res, 200, await store.setStatus(actor, id, input.status, input.body ?? ""));
|
|
949
|
+
}
|
|
950
|
+
}
|
|
930
951
|
const match = /^\/api\/drafts\/(d-[0-9a-f]{10})(?:\/(preview|approve|rebase|handoff))?$/.exec(path);
|
|
931
952
|
if (match) {
|
|
932
953
|
const id = match[1];
|
|
@@ -944,27 +965,6 @@ async function api(req, res, path, options) {
|
|
|
944
965
|
}
|
|
945
966
|
throw new WorkflowError("not-found", `no endpoint ${method} ${path}`);
|
|
946
967
|
}
|
|
947
|
-
async function staticFile(res, root, path) {
|
|
948
|
-
const base = resolve(root);
|
|
949
|
-
let decoded;
|
|
950
|
-
try {
|
|
951
|
-
decoded = decodeURIComponent(path);
|
|
952
|
-
} catch {
|
|
953
|
-
throw new HttpError(400, "malformed path");
|
|
954
|
-
}
|
|
955
|
-
const wanted = resolve(base, `.${normalize(decoded)}`);
|
|
956
|
-
if (wanted !== base && !wanted.startsWith(`${base}${sep}`)) throw new HttpError(404, "not found");
|
|
957
|
-
let file = wanted;
|
|
958
|
-
const info = await stat(file).catch(() => null);
|
|
959
|
-
if (!info?.isFile()) {
|
|
960
|
-
if (extname(path) in TYPES || /\.(?:map|txt)$/.test(path)) throw new HttpError(404, "not found");
|
|
961
|
-
file = join3(base, "index.html");
|
|
962
|
-
}
|
|
963
|
-
const body = await readFile2(file);
|
|
964
|
-
const immutable = file.includes(`${sep}assets${sep}`);
|
|
965
|
-
res.writeHead(200, { ...BASE_HEADERS, "content-type": TYPES[extname(file)] ?? "application/octet-stream", "cache-control": immutable ? "public, max-age=31536000, immutable" : "no-cache" });
|
|
966
|
-
res.end(body);
|
|
967
|
-
}
|
|
968
968
|
function createHandler(options) {
|
|
969
969
|
return (req, res) => {
|
|
970
970
|
void (async () => {
|
|
@@ -972,8 +972,7 @@ function createHandler(options) {
|
|
|
972
972
|
const path = new URL(req.url ?? "/", "http://loopback").pathname;
|
|
973
973
|
screen(req, options.sessionKey);
|
|
974
974
|
if (path === "/api" || path.startsWith("/api/")) return await api(req, res, path, options);
|
|
975
|
-
|
|
976
|
-
await staticFile(res, options.webRoot, path);
|
|
975
|
+
throw new HttpError(404, "not found");
|
|
977
976
|
} catch (error) {
|
|
978
977
|
fail(res, error);
|
|
979
978
|
}
|
|
@@ -1059,7 +1058,7 @@ var ORDER = { new: 0, kept: 1, resolved: 2 };
|
|
|
1059
1058
|
function finding(v, change) {
|
|
1060
1059
|
return { file: v.file, line: v.line, column: v.column, text: v.text, message: v.message, ...v.suggestion ? { suggestion: v.suggestion } : {}, severity: v.severity, change };
|
|
1061
1060
|
}
|
|
1062
|
-
var
|
|
1061
|
+
var notRunReason = (run2) => run2 && (run2.state === "unsupported" || run2.state === "error") ? run2.reason ?? run2.state : null;
|
|
1063
1062
|
function limitsOf(rule, baseline, scan, flagged, kept, baselineNotRun) {
|
|
1064
1063
|
const limits = [];
|
|
1065
1064
|
if (baselineNotRun) limits.push(`The published rule could not run on this snapshot (${baselineNotRun}), so nothing counts as already flagged or no longer flagged.`);
|
|
@@ -1077,10 +1076,10 @@ function limitsOf(rule, baseline, scan, flagged, kept, baselineNotRun) {
|
|
|
1077
1076
|
function comparePreview(input) {
|
|
1078
1077
|
const { project, scan, execution, draft, baseline } = input;
|
|
1079
1078
|
const next = execution.evaluate(project, scan, [draft]);
|
|
1080
|
-
const draftNotRun =
|
|
1079
|
+
const draftNotRun = notRunReason(next.checks[0]);
|
|
1081
1080
|
if (draftNotRun) throw new WorkflowError("invalid", `the draft's check did not run: ${draftNotRun}`, [`check: ${draftNotRun}`]);
|
|
1082
1081
|
const published = baseline ? execution.evaluate(project, scan, [baseline]) : null;
|
|
1083
|
-
const baselineNotRun =
|
|
1082
|
+
const baselineNotRun = notRunReason(published?.checks[0]);
|
|
1084
1083
|
const before = published && !baselineNotRun ? published.violations : [];
|
|
1085
1084
|
const beforeKeys = new Set(before.map(keyOf));
|
|
1086
1085
|
const nextKeys = new Set(next.violations.map(keyOf));
|
|
@@ -1101,11 +1100,173 @@ function comparePreview(input) {
|
|
|
1101
1100
|
};
|
|
1102
1101
|
}
|
|
1103
1102
|
|
|
1104
|
-
// ../workflow/src/
|
|
1103
|
+
// ../workflow/src/requests.ts
|
|
1105
1104
|
import { randomBytes } from "node:crypto";
|
|
1105
|
+
import { existsSync, statSync } from "node:fs";
|
|
1106
|
+
import { copyFile, mkdir, readdir, readFile as readFile3, stat } from "node:fs/promises";
|
|
1107
|
+
import { join as join3 } from "node:path";
|
|
1108
|
+
var REQUESTS_DIR = ".youi/requests";
|
|
1109
|
+
var FILE = /^(r-[0-9a-f]{10})\.json$/;
|
|
1110
|
+
var authorOf = (actor) => ({ kind: actor.kind, name: actor.name });
|
|
1111
|
+
var screensDir = (root) => join3(root, ".youi/screens");
|
|
1112
|
+
var filedAt = (request) => request.thread.filter((e) => e.status === "open").at(-1)?.at ?? request.createdAt;
|
|
1113
|
+
var RequestStore = class {
|
|
1114
|
+
dir;
|
|
1115
|
+
root;
|
|
1116
|
+
project;
|
|
1117
|
+
options;
|
|
1118
|
+
/** `root`: the product's root, where `.youi/` lives; `project`: its id. */
|
|
1119
|
+
constructor(root, project, options = {}) {
|
|
1120
|
+
this.dir = join3(root, REQUESTS_DIR);
|
|
1121
|
+
this.root = root;
|
|
1122
|
+
this.project = project;
|
|
1123
|
+
this.options = options;
|
|
1124
|
+
}
|
|
1125
|
+
now() {
|
|
1126
|
+
return (this.options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
1127
|
+
}
|
|
1128
|
+
file(id) {
|
|
1129
|
+
if (!REQUEST_ID.test(id)) throw new WorkflowError("bad-request", `"${id.slice(0, 40)}" is not a request id (r-<10 hex>)`);
|
|
1130
|
+
return join3(this.dir, `${id}.json`);
|
|
1131
|
+
}
|
|
1132
|
+
async write(request) {
|
|
1133
|
+
const checked = RequestDoc.parse(request);
|
|
1134
|
+
await writeFileAtomic(this.file(checked.id), `${JSON.stringify(checked, null, 2)}
|
|
1135
|
+
`);
|
|
1136
|
+
return checked;
|
|
1137
|
+
}
|
|
1138
|
+
/** Every request, newest first. */
|
|
1139
|
+
async list() {
|
|
1140
|
+
const names = await readdir(this.dir).catch(() => []);
|
|
1141
|
+
const requests = [];
|
|
1142
|
+
const unreadable = [];
|
|
1143
|
+
for (const name of names.sort()) {
|
|
1144
|
+
const match = FILE.exec(name);
|
|
1145
|
+
if (!match) continue;
|
|
1146
|
+
const file = join3(REQUESTS_DIR, name);
|
|
1147
|
+
let value;
|
|
1148
|
+
try {
|
|
1149
|
+
value = JSON.parse(await readFile3(join3(this.dir, name), "utf8"));
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
unreadable.push({ file, reason: error instanceof Error ? error.message.split("\n")[0] : String(error) });
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const result = RequestDoc.safeParse(value);
|
|
1155
|
+
if (!result.success) unreadable.push({ file, reason: result.error.issues.map((i) => `${i.path.join(".") || "file"}: ${i.message}`).join("; ") });
|
|
1156
|
+
else if (result.data.id !== match[1]) unreadable.push({ file, reason: "its id is not its file name" });
|
|
1157
|
+
else requests.push(result.data);
|
|
1158
|
+
}
|
|
1159
|
+
requests.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b.id));
|
|
1160
|
+
return { requests, unreadable };
|
|
1161
|
+
}
|
|
1162
|
+
async get(id) {
|
|
1163
|
+
const text = await readFile3(this.file(id), "utf8").catch(() => null);
|
|
1164
|
+
if (text === null) throw new WorkflowError("not-found", `no request ${id}`);
|
|
1165
|
+
const parsed = RequestDoc.safeParse(JSON.parse(text));
|
|
1166
|
+
if (!parsed.success) throw new WorkflowError("invalid", `${REQUESTS_DIR}/${id}.json is not a valid youi.request/v1`, parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`));
|
|
1167
|
+
return parsed.data;
|
|
1168
|
+
}
|
|
1169
|
+
async create(actor, input) {
|
|
1170
|
+
authorize(actor, "draft");
|
|
1171
|
+
const body = input.body.trim();
|
|
1172
|
+
if (!body) throw new WorkflowError("invalid", "a request needs words: what to look at, or what to change");
|
|
1173
|
+
const at = this.now();
|
|
1174
|
+
const id = (this.options.newId ?? (() => `r-${randomBytes(5).toString("hex")}`))();
|
|
1175
|
+
const shot = join3(screensDir(this.root), `${screenSlug(input.target.route)}.light.webp`);
|
|
1176
|
+
let before;
|
|
1177
|
+
if (existsSync(shot)) {
|
|
1178
|
+
before = `${id}.before.webp`;
|
|
1179
|
+
await mkdir(this.dir, { recursive: true });
|
|
1180
|
+
await copyFile(shot, join3(this.dir, before));
|
|
1181
|
+
}
|
|
1182
|
+
return this.write({ schema: "youi.request/v1", id, project: this.project, kind: input.kind, status: "open", body, author: authorOf(actor), createdAt: at, updatedAt: at, target: input.target, ...before ? { before } : {}, thread: [] });
|
|
1183
|
+
}
|
|
1184
|
+
async reply(actor, id, body) {
|
|
1185
|
+
authorize(actor, "draft");
|
|
1186
|
+
const text = body.trim();
|
|
1187
|
+
if (!text) throw new WorkflowError("invalid", "a reply needs words");
|
|
1188
|
+
const request = await this.get(id);
|
|
1189
|
+
const at = this.now();
|
|
1190
|
+
return this.write({ ...request, updatedAt: at, thread: [...request.thread, { author: authorOf(actor), at, body: text }] });
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Before and after for a request: the picture kept when it was filed, and the screen's picture
|
|
1194
|
+
* now, with the component found again on it by its file and line (from the new capture's places).
|
|
1195
|
+
*/
|
|
1196
|
+
async compare(id) {
|
|
1197
|
+
const request = await this.get(id);
|
|
1198
|
+
const t = request.target;
|
|
1199
|
+
const before = request.before === `${request.id}.before.webp` && existsSync(join3(this.dir, request.before)) ? { file: join3(this.dir, request.before), box: t.box ?? null, page: t.page ?? null, at: request.createdAt } : null;
|
|
1200
|
+
const slug = screenSlug(t.route);
|
|
1201
|
+
const shot = join3(screensDir(this.root), `${slug}.light.webp`);
|
|
1202
|
+
const info = await stat(shot).catch(() => null);
|
|
1203
|
+
if (!info) return { before, after: null };
|
|
1204
|
+
let box = t.box ?? null;
|
|
1205
|
+
let page = t.page ?? null;
|
|
1206
|
+
let found = false;
|
|
1207
|
+
try {
|
|
1208
|
+
const places = JSON.parse(await readFile3(join3(screensDir(this.root), `${slug}.places.json`), "utf8"));
|
|
1209
|
+
const again = t.file && t.line ? places.places?.find((p) => p.file === t.file && p.line === t.line && p.name === t.name) : void 0;
|
|
1210
|
+
if (places.size) page = places.size;
|
|
1211
|
+
if (again) {
|
|
1212
|
+
box = again.box;
|
|
1213
|
+
found = true;
|
|
1214
|
+
}
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
const at = info.mtime.toISOString();
|
|
1218
|
+
return { before, after: { file: shot, box, page, at, sinceFiled: at > filedAt(request), found } };
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Move a request on: `addressed` (anyone; an agent says what it did), or `closed` and `open`
|
|
1222
|
+
* (a person's: closing accepts the work, reopening says it is not done).
|
|
1223
|
+
*/
|
|
1224
|
+
async setStatus(actor, id, status, body = "") {
|
|
1225
|
+
if (status !== "addressed") authorize(actor, "close");
|
|
1226
|
+
else authorize(actor, "draft");
|
|
1227
|
+
const request = await this.get(id);
|
|
1228
|
+
if (request.status === status) throw new WorkflowError("conflict", `${id} is already ${status}`);
|
|
1229
|
+
if (status === "addressed" && request.status === "closed") throw new WorkflowError("conflict", `${id} is closed: a person reopens it first`);
|
|
1230
|
+
const at = this.now();
|
|
1231
|
+
return this.write({ ...request, status, updatedAt: at, thread: [...request.thread, { author: authorOf(actor), at, body: body.trim(), status }] });
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
function requestBrief(request, root) {
|
|
1235
|
+
const t = request.target;
|
|
1236
|
+
const picture = join3(root, ".youi/screens", `${screenSlug(t.route)}.light.webp`);
|
|
1237
|
+
const at = t.file && t.line ? `${t.file}:${t.line}` : null;
|
|
1238
|
+
const every = t.scope === "element" && t.element;
|
|
1239
|
+
const where = every ? `every use of ${t.element} (system_where_used lists them)` : at ? `${at}` : `the ${t.name} on ${t.route} (its line was not captured: find it from the picture and the route)`;
|
|
1240
|
+
const shot = existsSync(picture) ? statSync(picture).mtime.toISOString() : null;
|
|
1241
|
+
const recaptured = shot === null ? null : shot > filedAt(request);
|
|
1242
|
+
const capture = `screens_capture with route ${t.route} (or youiyoux screens capture --base <the app in development> --route ${t.route})`;
|
|
1243
|
+
const next = request.status === "closed" ? "Closed by a person: nothing to do." : request.status === "addressed" && recaptured === false ? `Marked addressed, but ${t.route} has not been captured since this was filed, so the person compares against the old picture: run ${capture}. A person then checks it and closes or reopens it.` : request.status === "addressed" ? "Marked addressed: a person checks it and closes or reopens it. Reply only if asked." : request.kind === "comment" ? `A comment on ${where}: reply with request_reply if it asks something; change code only if it asks for a change.` : `Make the change at ${where}${t.lookalike && t.element ? `, using the System's ${t.element} rather than the hand-built lookalike where that is the fix` : ""}; capture the screen again (${capture}) so the person sees it after; then call request_addressed with what you changed (file:line). Ask with request_reply first if it is unclear. A person closes it.`;
|
|
1244
|
+
return {
|
|
1245
|
+
id: request.id,
|
|
1246
|
+
kind: request.kind,
|
|
1247
|
+
status: request.status,
|
|
1248
|
+
ask: request.body,
|
|
1249
|
+
from: `${request.author.kind} ${request.author.name}`,
|
|
1250
|
+
createdAt: request.createdAt,
|
|
1251
|
+
route: t.route,
|
|
1252
|
+
component: t.name,
|
|
1253
|
+
element: t.element ?? null,
|
|
1254
|
+
lookalike: t.lookalike === true,
|
|
1255
|
+
at,
|
|
1256
|
+
scope: every ? `everywhere ${t.element} is used` : "this one, on this screen",
|
|
1257
|
+
picture: existsSync(picture) ? picture : null,
|
|
1258
|
+
recaptured,
|
|
1259
|
+
box: t.box ? { x: Math.round(t.box.x * PICTURE_SCALE), y: Math.round(t.box.y * PICTURE_SCALE), width: Math.round(t.box.width * PICTURE_SCALE), height: Math.round(t.box.height * PICTURE_SCALE) } : null,
|
|
1260
|
+
thread: request.thread.map((e) => `${e.author.kind} ${e.author.name}${e.status ? ` (marked ${e.status})` : ""}: ${e.body || "(no words)"}`),
|
|
1261
|
+
next
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// ../workflow/src/service.ts
|
|
1266
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1106
1267
|
|
|
1107
1268
|
// ../workflow/src/style-preview.ts
|
|
1108
|
-
import { mkdir, mkdtemp as mkdtemp2, rm as rm2, writeFile } from "node:fs/promises";
|
|
1269
|
+
import { mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm2, writeFile } from "node:fs/promises";
|
|
1109
1270
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
1110
1271
|
import { dirname, join as join4 } from "node:path";
|
|
1111
1272
|
var MAX_LISTED = 5e3;
|
|
@@ -1113,7 +1274,7 @@ async function buildFrom(project, tree) {
|
|
|
1113
1274
|
const root = await mkdtemp2(join4(tmpdir2(), "youi-style-"));
|
|
1114
1275
|
try {
|
|
1115
1276
|
for (const [path, text] of tree) {
|
|
1116
|
-
await
|
|
1277
|
+
await mkdir2(dirname(join4(root, path)), { recursive: true });
|
|
1117
1278
|
await writeFile(join4(root, path), text);
|
|
1118
1279
|
}
|
|
1119
1280
|
return (await buildTokens(root, project.system.config, project.config.outputs)).tokens;
|
|
@@ -1271,7 +1432,8 @@ function previewSummary(draft) {
|
|
|
1271
1432
|
const p = draft.preview;
|
|
1272
1433
|
return p ? `changes ${p.totals.tokens} token(s) and ${p.totals.contracts} component(s): ${p.totals.sites} site(s) and ${p.totals.direct} direct use(s) in ${p.totals.files} file(s) on ${p.totals.routes} screen(s)` : "no preview";
|
|
1273
1434
|
}
|
|
1274
|
-
var
|
|
1435
|
+
var capitalized = (text) => `${text[0].toUpperCase()}${text.slice(1)}`;
|
|
1436
|
+
var randomId = (prefix) => `${prefix}-${randomBytes2(5).toString("hex")}`;
|
|
1275
1437
|
var RuleWorkflow = class {
|
|
1276
1438
|
options;
|
|
1277
1439
|
execution;
|
|
@@ -1282,6 +1444,26 @@ var RuleWorkflow = class {
|
|
|
1282
1444
|
this.options = options;
|
|
1283
1445
|
this.execution = options.execution ?? scannerExecution;
|
|
1284
1446
|
}
|
|
1447
|
+
/** The product's comments and change requests on its captured screens (requests.ts), in its `.youi/requests`. */
|
|
1448
|
+
async requests() {
|
|
1449
|
+
const project = await this.project();
|
|
1450
|
+
return new RequestStore(project.root, project.config.id, this.options.now ? { now: this.options.now } : {});
|
|
1451
|
+
}
|
|
1452
|
+
/** The project and its design definitions, as they are on disk now. */
|
|
1453
|
+
project() {
|
|
1454
|
+
const { projectRoot, configPath, designRoot, productRoot } = this.options;
|
|
1455
|
+
return loadProject(projectRoot, { ...configPath ? { configPath } : {}, ...designRoot ? { designRoot } : {}, ...productRoot ? { productRoot } : {} });
|
|
1456
|
+
}
|
|
1457
|
+
/** What every kind of preview starts with: the draft revision and snapshot it read, and how long it took. */
|
|
1458
|
+
previewHead(draft, before, started) {
|
|
1459
|
+
return { id: this.id("p"), at: this.now(), draftRevision: draft.revision, draftDigest: draft.digest, snapshot: before.snapshot, durationMs: Math.round(performance.now() - started) };
|
|
1460
|
+
}
|
|
1461
|
+
/** The design repository and project now, refused when they moved since `before` (while a preview ran). */
|
|
1462
|
+
async unchangedSince(before) {
|
|
1463
|
+
const after = await this.current();
|
|
1464
|
+
if (after.snapshot.digest !== before.snapshot.digest) throw new WorkflowError("stale", "the project or design repository changed while the preview ran; run it again");
|
|
1465
|
+
return after;
|
|
1466
|
+
}
|
|
1285
1467
|
now() {
|
|
1286
1468
|
return (this.options.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1287
1469
|
}
|
|
@@ -1300,8 +1482,7 @@ var RuleWorkflow = class {
|
|
|
1300
1482
|
}
|
|
1301
1483
|
}
|
|
1302
1484
|
async current() {
|
|
1303
|
-
const
|
|
1304
|
-
const project = await loadProject(projectRoot, { ...configPath ? { configPath } : {}, ...designRoot ? { designRoot } : {}, ...productRoot ? { productRoot } : {} });
|
|
1485
|
+
const project = await this.project();
|
|
1305
1486
|
const repo = await locate(project.repo.root);
|
|
1306
1487
|
if (!repo) throw new WorkflowError("invalid", `the design definitions at ${project.repo.root} are not in a git repository; drafts need a commit to start from`);
|
|
1307
1488
|
const branch = await defaultBranch(repo.top);
|
|
@@ -1446,13 +1627,13 @@ var RuleWorkflow = class {
|
|
|
1446
1627
|
current.project.system.rules.map(async (rule) => {
|
|
1447
1628
|
const places = scan.report.violations.filter((v) => v.rule === rule.id);
|
|
1448
1629
|
const run2 = scan.report.checks.find((c) => c.rule === rule.id);
|
|
1449
|
-
const
|
|
1630
|
+
const notRun = notRunReason(run2);
|
|
1450
1631
|
return {
|
|
1451
1632
|
rule: { ...rule, execution: ruleCapability(rule) },
|
|
1452
1633
|
file: files.get(rule.id)?.path ?? await current.files.rulePath(current.tip, rule.id),
|
|
1453
1634
|
components: current.project.system.components.filter((c) => c.contract.rules.includes(rule.id)).map((c) => c.contract.id),
|
|
1454
|
-
flagged:
|
|
1455
|
-
...
|
|
1635
|
+
flagged: notRun ? null : places.length,
|
|
1636
|
+
...notRun ? { notRun } : {},
|
|
1456
1637
|
places: places.slice(0, MAX_PLACES).map(({ file, line, column, text, message }) => ({ file, line, column, text, message }))
|
|
1457
1638
|
};
|
|
1458
1639
|
})
|
|
@@ -1511,7 +1692,7 @@ var RuleWorkflow = class {
|
|
|
1511
1692
|
}
|
|
1512
1693
|
/** A style draft starts from the default branch tip, bound to every file it could touch. */
|
|
1513
1694
|
async createStyle(actor, current, initial) {
|
|
1514
|
-
const content = normalizeStyleContent(initial ??
|
|
1695
|
+
const content = normalizeStyleContent(initial ?? emptyStyleContent());
|
|
1515
1696
|
const files = {};
|
|
1516
1697
|
for (const path of await current.files.stylePaths(current.tip)) files[path] = await current.files.digest(current.tip, path);
|
|
1517
1698
|
const at = this.now();
|
|
@@ -1563,7 +1744,7 @@ var RuleWorkflow = class {
|
|
|
1563
1744
|
approval: null,
|
|
1564
1745
|
handoff: null,
|
|
1565
1746
|
discarded: false,
|
|
1566
|
-
events: [this.event(actor, "created", `${
|
|
1747
|
+
events: [this.event(actor, "created", `${capitalized(describeLibrary(content))}, from ${current.branch}@${current.tip.slice(0, 7)}.`)]
|
|
1567
1748
|
};
|
|
1568
1749
|
await this.options.store.put(draft);
|
|
1569
1750
|
return await this.view(draft, current);
|
|
@@ -1632,20 +1813,14 @@ var RuleWorkflow = class {
|
|
|
1632
1813
|
const scan = await scanProject(before.project);
|
|
1633
1814
|
const baseline = before.project.system.rules.find((r) => r.id === draft.ruleId) ?? null;
|
|
1634
1815
|
const comparison = comparePreview({ project: before.project, scan, execution: this.execution, draft: rule, baseline: draft.mode === "edit" ? baseline : null });
|
|
1635
|
-
const after = await this.
|
|
1636
|
-
if (after.snapshot.digest !== before.snapshot.digest) throw new WorkflowError("stale", "the project or design repository changed while the preview ran; run it again");
|
|
1816
|
+
const after = await this.unchangedSince(before);
|
|
1637
1817
|
const { report } = scan;
|
|
1638
1818
|
const designLimits = [
|
|
1639
1819
|
...before.designDirty ? ["The design repository has uncommitted changes or another branch checked out; this preview reads the working tree."] : [],
|
|
1640
1820
|
...before.snapshot.projectCommit === null ? [`${report.project} is not a git repository: later edits to it cannot be detected, so this preview cannot go stale.`] : []
|
|
1641
1821
|
];
|
|
1642
1822
|
const preview = {
|
|
1643
|
-
|
|
1644
|
-
at: this.now(),
|
|
1645
|
-
draftRevision: draft.revision,
|
|
1646
|
-
draftDigest: draft.digest,
|
|
1647
|
-
snapshot: before.snapshot,
|
|
1648
|
-
durationMs: Math.round(performance.now() - started),
|
|
1823
|
+
...this.previewHead(draft, before, started),
|
|
1649
1824
|
scan: { status: report.status, files: report.coverage.filesScanned, failed: report.coverage.failed },
|
|
1650
1825
|
...comparison,
|
|
1651
1826
|
limits: [...comparison.limits, ...designLimits]
|
|
@@ -1676,20 +1851,14 @@ var RuleWorkflow = class {
|
|
|
1676
1851
|
if (!impact) indexed = false;
|
|
1677
1852
|
swaps.push({ target, kind: "family", name: null, looks: impact?.looks ?? null, files: impact?.files ?? null, routes: impact?.routes ?? null, listed: impact?.listed ?? null });
|
|
1678
1853
|
}
|
|
1679
|
-
const after = await this.
|
|
1680
|
-
if (after.snapshot.digest !== before.snapshot.digest) throw new WorkflowError("stale", "the project or design repository changed while the preview ran; run it again");
|
|
1854
|
+
const after = await this.unchangedSince(before);
|
|
1681
1855
|
const entry = draft.content.entry;
|
|
1682
1856
|
const preview = {
|
|
1683
|
-
|
|
1684
|
-
at: this.now(),
|
|
1685
|
-
draftRevision: draft.revision,
|
|
1686
|
-
draftDigest: draft.digest,
|
|
1687
|
-
snapshot: before.snapshot,
|
|
1688
|
-
durationMs: Math.round(performance.now() - started),
|
|
1857
|
+
...this.previewHead(draft, before, started),
|
|
1689
1858
|
file,
|
|
1690
1859
|
exists,
|
|
1691
1860
|
stage: draft.content.request === "adopt" ? "candidate" : "library",
|
|
1692
|
-
license:
|
|
1861
|
+
license: licenceOf(draft.content),
|
|
1693
1862
|
install: entry.install?.command ?? null,
|
|
1694
1863
|
swaps,
|
|
1695
1864
|
limits: [
|
|
@@ -1699,7 +1868,7 @@ var RuleWorkflow = class {
|
|
|
1699
1868
|
...swaps.some((s) => s.kind === "element") ? ["System elements are named, not counted: youiyoux where-used counts their sites."] : []
|
|
1700
1869
|
]
|
|
1701
1870
|
};
|
|
1702
|
-
const next = { ...draft, preview, updatedAt: this.now(), events: [...draft.events, this.event(actor, "previewed", `${
|
|
1871
|
+
const next = { ...draft, preview, updatedAt: this.now(), events: [...draft.events, this.event(actor, "previewed", `${capitalized(previewSummary({ ...draft, preview }))}.`)] };
|
|
1703
1872
|
await this.options.store.put(next);
|
|
1704
1873
|
return this.refreshed(next, after);
|
|
1705
1874
|
}
|
|
@@ -1711,16 +1880,10 @@ var RuleWorkflow = class {
|
|
|
1711
1880
|
const started = performance.now();
|
|
1712
1881
|
const scan = await scanProject(before.project);
|
|
1713
1882
|
const impact = await previewStyle({ project: before.project, scan, files: before.files, commit: draft.base.commit, content: draft.content, published: await before.tokens() });
|
|
1714
|
-
const after = await this.
|
|
1715
|
-
if (after.snapshot.digest !== before.snapshot.digest) throw new WorkflowError("stale", "the project or design repository changed while the preview ran; run it again");
|
|
1883
|
+
const after = await this.unchangedSince(before);
|
|
1716
1884
|
const { report } = scan;
|
|
1717
1885
|
const preview = {
|
|
1718
|
-
|
|
1719
|
-
at: this.now(),
|
|
1720
|
-
draftRevision: draft.revision,
|
|
1721
|
-
draftDigest: draft.digest,
|
|
1722
|
-
snapshot: before.snapshot,
|
|
1723
|
-
durationMs: Math.round(performance.now() - started),
|
|
1886
|
+
...this.previewHead(draft, before, started),
|
|
1724
1887
|
scan: { status: report.status, files: report.coverage.filesScanned, failed: report.coverage.failed },
|
|
1725
1888
|
...impact,
|
|
1726
1889
|
limits: [
|
|
@@ -1925,7 +2088,7 @@ function shellQuote(path) {
|
|
|
1925
2088
|
}
|
|
1926
2089
|
|
|
1927
2090
|
// ../workflow/src/store.ts
|
|
1928
|
-
import { mkdir as
|
|
2091
|
+
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile4 } from "node:fs/promises";
|
|
1929
2092
|
import { join as join5 } from "node:path";
|
|
1930
2093
|
var DRAFT_ID = /^d-[0-9a-f]{10}$/;
|
|
1931
2094
|
var FORMAT = "youiyoux.draft/v1";
|
|
@@ -1955,14 +2118,14 @@ var FileStore = class {
|
|
|
1955
2118
|
this.artifacts = join5(dir, "handoffs");
|
|
1956
2119
|
}
|
|
1957
2120
|
async list() {
|
|
1958
|
-
await
|
|
1959
|
-
const names = (await
|
|
2121
|
+
await mkdir3(this.drafts, { recursive: true });
|
|
2122
|
+
const names = (await readdir2(this.drafts)).filter((name) => /^d-[0-9a-f]{10}\.json$/.test(name)).sort();
|
|
1960
2123
|
const drafts = [];
|
|
1961
2124
|
const unreadable = [];
|
|
1962
2125
|
for (const name of names) {
|
|
1963
2126
|
const file = join5(this.drafts, name);
|
|
1964
2127
|
try {
|
|
1965
|
-
drafts.push(decode(await
|
|
2128
|
+
drafts.push(decode(await readFile4(file, "utf8"), file));
|
|
1966
2129
|
} catch (error) {
|
|
1967
2130
|
unreadable.push({ file, reason: error instanceof Error ? error.message : String(error) });
|
|
1968
2131
|
}
|
|
@@ -1974,7 +2137,7 @@ var FileStore = class {
|
|
|
1974
2137
|
const file = join5(this.drafts, `${id}.json`);
|
|
1975
2138
|
let text;
|
|
1976
2139
|
try {
|
|
1977
|
-
text = await
|
|
2140
|
+
text = await readFile4(file, "utf8");
|
|
1978
2141
|
} catch (error) {
|
|
1979
2142
|
if (error.code === "ENOENT") return null;
|
|
1980
2143
|
throw error;
|
|
@@ -2012,72 +2175,63 @@ var MemoryStore = class {
|
|
|
2012
2175
|
return `memory:${name}`;
|
|
2013
2176
|
}
|
|
2014
2177
|
};
|
|
2178
|
+
|
|
2015
2179
|
export {
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
DesignFiles,
|
|
2019
|
-
RuleWorkflow as DesignWorkflow,
|
|
2020
|
-
FileStore,
|
|
2021
|
-
GitError,
|
|
2022
|
-
JsonEditError,
|
|
2023
|
-
MAX_FINDINGS,
|
|
2024
|
-
MAX_LISTED,
|
|
2025
|
-
MAX_TITLE,
|
|
2026
|
-
MemoryStore,
|
|
2027
|
-
RuleWorkflow,
|
|
2028
|
-
SESSION_HEADER,
|
|
2029
|
-
WORKFLOW_VERSION,
|
|
2030
|
-
WorkflowError,
|
|
2031
|
-
adoptionRefusal,
|
|
2032
|
-
authorize,
|
|
2033
|
-
branchTip,
|
|
2034
|
-
changesDigest,
|
|
2035
|
-
comparePreview,
|
|
2036
|
-
componentChanges,
|
|
2037
|
-
contentDigest,
|
|
2038
|
-
createHandler,
|
|
2039
|
-
createReviewBranch,
|
|
2040
|
-
defaultBranch,
|
|
2180
|
+
readJsonPath,
|
|
2181
|
+
setJsonPath,
|
|
2041
2182
|
deleteJsonPath,
|
|
2042
|
-
describeContrast,
|
|
2043
|
-
describeLibrary,
|
|
2044
|
-
describeStyle,
|
|
2045
|
-
draftStatus,
|
|
2046
|
-
effectiveStateEdits,
|
|
2047
|
-
effectiveTokenEdits,
|
|
2048
|
-
emptyStyleContent,
|
|
2049
2183
|
fieldsFromRule,
|
|
2184
|
+
newRuleFields,
|
|
2185
|
+
toRuleSpec,
|
|
2186
|
+
validateContent,
|
|
2187
|
+
contentDigest,
|
|
2188
|
+
renderRuleYaml,
|
|
2189
|
+
renderContract,
|
|
2190
|
+
draftStatus,
|
|
2191
|
+
componentChanges,
|
|
2050
2192
|
git,
|
|
2051
|
-
|
|
2193
|
+
locate,
|
|
2194
|
+
defaultBranch,
|
|
2195
|
+
branchTip,
|
|
2052
2196
|
headCommit,
|
|
2197
|
+
readAt,
|
|
2053
2198
|
isAncestor,
|
|
2054
|
-
libraryDigest,
|
|
2055
|
-
libraryPath,
|
|
2056
|
-
librarySlug,
|
|
2057
2199
|
listAt,
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
normalizeLibraryContent,
|
|
2200
|
+
hasChanges,
|
|
2201
|
+
changesDigest,
|
|
2202
|
+
createReviewBranch,
|
|
2203
|
+
emptyStyleContent,
|
|
2063
2204
|
normalizeStyleContent,
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
renderContract,
|
|
2069
|
-
renderContractStates,
|
|
2070
|
-
renderLibraryEntry,
|
|
2071
|
-
renderRuleYaml,
|
|
2205
|
+
styleDigest,
|
|
2206
|
+
tokenPathOf,
|
|
2207
|
+
validateStyleContent,
|
|
2208
|
+
effectiveStateEdits,
|
|
2072
2209
|
renderTokenEdits,
|
|
2210
|
+
renderContractStates,
|
|
2211
|
+
styleSlug,
|
|
2212
|
+
describeStyle,
|
|
2213
|
+
DesignFiles,
|
|
2073
2214
|
scannerExecution,
|
|
2215
|
+
WorkflowError,
|
|
2216
|
+
authorize,
|
|
2074
2217
|
serveWorkflow,
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2218
|
+
normalizeLibraryContent,
|
|
2219
|
+
libraryDigest,
|
|
2220
|
+
libraryPath,
|
|
2221
|
+
librarySlug,
|
|
2222
|
+
licenceOf,
|
|
2223
|
+
adoptionRefusal,
|
|
2081
2224
|
validateLibraryContent,
|
|
2082
|
-
|
|
2225
|
+
renderLibraryEntry,
|
|
2226
|
+
describeLibrary,
|
|
2227
|
+
notRunReason,
|
|
2228
|
+
comparePreview,
|
|
2229
|
+
REQUESTS_DIR,
|
|
2230
|
+
RequestStore,
|
|
2231
|
+
requestBrief,
|
|
2232
|
+
MAX_LISTED,
|
|
2233
|
+
previewStyle,
|
|
2234
|
+
RuleWorkflow,
|
|
2235
|
+
FileStore,
|
|
2236
|
+
MemoryStore
|
|
2083
2237
|
};
|