halfcycle 0.4.5 → 0.4.6
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/.claude-plugin/plugin.json +1 -1
- package/dist/bin.d.ts +2 -0
- package/dist/bin.d.ts.map +1 -1
- package/dist/build-record/obtain.d.ts +3 -1
- package/dist/build-record/obtain.d.ts.map +1 -1
- package/dist/build-record/write.d.ts +5 -1
- package/dist/build-record/write.d.ts.map +1 -1
- package/dist/cli-contract.d.ts +1 -1
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli.js +1178 -85
- package/dist/cli.js.map +3 -3
- package/dist/draw/index.d.ts +91 -0
- package/dist/draw/index.d.ts.map +1 -0
- package/dist/draw/write.d.ts +232 -0
- package/dist/draw/write.d.ts.map +1 -0
- package/dist/index.js +27 -11
- package/dist/index.js.map +3 -3
- package/dist/install.d.ts.map +1 -1
- package/dist/missing-route.d.ts +36 -0
- package/dist/missing-route.d.ts.map +1 -0
- package/dist/system-report.d.ts +168 -0
- package/dist/system-report.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client half of `npx halfcycle@latest draw architecture` (System view · E1,
|
|
3
|
+
* feature `diagram-files-for-docs`, AC1, AC4, AC5, AC6 and AC8's missing route).
|
|
4
|
+
*
|
|
5
|
+
* THE ORDER IS THE SPEC'S. (1) read and check `docs/system.json`; (2) report it and
|
|
6
|
+
* get its version — both through `system`'s own first stage,
|
|
7
|
+
* `checkAndReportDescription` (`../system-report.ts`), never a copy of it; (3) ask the
|
|
8
|
+
* service to draw that version; (4) check all five files, every look's picture and the
|
|
9
|
+
* Mermaid, then write them; (5) print the Markdown, whose image line is the embedded
|
|
10
|
+
* look's PNG (`EMBED_IMAGE_REL`, `./write.ts`, composed from the same table as that
|
|
11
|
+
* file's path). The place and the files already there are checked on the machine
|
|
12
|
+
* before anything is sent, and again just before writing, so a refusal this command
|
|
13
|
+
* can make locally never costs a request, and a change made while it waited is still
|
|
14
|
+
* seen.
|
|
15
|
+
*
|
|
16
|
+
* ONE RESPONSE-HANDLING RULE, the Build Record path's. A body that parses under
|
|
17
|
+
* `systemDrawResultSchema` (`@halfcycle/events`, the one home of the shape) is
|
|
18
|
+
* authoritative, whatever its status. Otherwise the service's missing-route answer to
|
|
19
|
+
* this POST means it predates drawing — asked of `isMissingRoute`
|
|
20
|
+
* (`../missing-route.ts`), the one predicate, never a copy of its expression; a
|
|
21
|
+
* missing report route says the same, because either way this service cannot draw.
|
|
22
|
+
* Anything else is reported by its status and the service's own sentence.
|
|
23
|
+
*
|
|
24
|
+
* NO BYTE OF THE RESPONSE DECIDES A PATH (INV-023): the response gives contents only;
|
|
25
|
+
* where they go is `./write.ts`'s constants.
|
|
26
|
+
*
|
|
27
|
+
* WHAT LEAVES THE MACHINE. The checked description (through the shared stage), then
|
|
28
|
+
* only `{ version, picture: 'architecture' }`.
|
|
29
|
+
*
|
|
30
|
+
* INV-001: Node builtins, the global `fetch`, `@halfcycle/events` and `@halfcycle/core`
|
|
31
|
+
* only. SHIPS TO CLIENT DISK inside the CLI: every string here is a sentence a person
|
|
32
|
+
* reads, so none names an internal register, route or setting; no comment sits beside
|
|
33
|
+
* an object member.
|
|
34
|
+
*/
|
|
35
|
+
import { type SystemDrawDrawn } from '@halfcycle/events';
|
|
36
|
+
import type { RepoCredential } from '../open-phase.js';
|
|
37
|
+
import { type SystemDeps, type SystemRun } from '../system-report.js';
|
|
38
|
+
import { type DiagramSet, type DiagramWriteResult } from './write.js';
|
|
39
|
+
export { ARCHITECTURE_NAME, DIAGRAM_DIR_REL, DIAGRAM_FILES, DIAGRAM_MARKER_LINE, EMBED_IMAGE_REL, EMBEDDED_LOOK, PICTURE_NAMES, checkPictures, diagramRel, pngOf, svgOf, diagramPaths, writeDiagramSet, } from './write.js';
|
|
40
|
+
/** The usage line, printed for any argument but `architecture`. */
|
|
41
|
+
export declare const DRAW_USAGE = "npx halfcycle@latest draw architecture [--repo <root>]";
|
|
42
|
+
/**
|
|
43
|
+
* What the arguments after `draw` came to: the repository root to work in, or usage.
|
|
44
|
+
* Exactly one positional, `architecture`, and at most one `--repo <root>`; anything
|
|
45
|
+
* else, including no argument, is usage. A phase name never reaches here in E1.
|
|
46
|
+
*/
|
|
47
|
+
export type DrawArgs = {
|
|
48
|
+
ok: true;
|
|
49
|
+
repoRoot: string | undefined;
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
};
|
|
53
|
+
export declare function parseDrawArgs(argv: readonly string[]): DrawArgs;
|
|
54
|
+
/**
|
|
55
|
+
* What came of asking for the drawing. `drawn`: the Mermaid and every look's picture,
|
|
56
|
+
* parsed under the strict result schema (so no look is missing and none is extra) but
|
|
57
|
+
* not yet checked. `refused`: the service's reason. `no-route`: this service cannot draw yet.
|
|
58
|
+
* `unavailable`: it could not be drawn, and `why` says so.
|
|
59
|
+
*/
|
|
60
|
+
export type DrawOutcome = {
|
|
61
|
+
kind: 'drawn';
|
|
62
|
+
drawn: SystemDrawDrawn;
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'refused';
|
|
65
|
+
reason: string;
|
|
66
|
+
} | {
|
|
67
|
+
kind: 'no-route';
|
|
68
|
+
} | {
|
|
69
|
+
kind: 'unavailable';
|
|
70
|
+
why: string;
|
|
71
|
+
};
|
|
72
|
+
/** The address the drawing is asked of: the engagement is one path segment, encoded once. */
|
|
73
|
+
export declare function systemDrawUrl(credential: RepoCredential): string;
|
|
74
|
+
/**
|
|
75
|
+
* Ask the service to draw `version`. Never throws: the network, the service and an
|
|
76
|
+
* answer this CLI cannot read are each an outcome the caller prints.
|
|
77
|
+
*/
|
|
78
|
+
export declare function requestDrawing(credential: RepoCredential, version: number): Promise<DrawOutcome>;
|
|
79
|
+
/** The pieces `runDraw` reaches outside itself through; replaced in tests. */
|
|
80
|
+
export interface DrawDeps extends SystemDeps {
|
|
81
|
+
readonly draw: (credential: RepoCredential, version: number) => Promise<DrawOutcome>;
|
|
82
|
+
readonly write: (repoRoot: string, set: DiagramSet) => DiagramWriteResult;
|
|
83
|
+
}
|
|
84
|
+
/** The Markdown to paste into a document directly under `docs/`, and the line before it. */
|
|
85
|
+
export declare function pasteLines(mermaid: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* The whole verb, in the order the module header gives. Exit 0 only when the five
|
|
88
|
+
* files are written or already up to date and the Markdown is printed.
|
|
89
|
+
*/
|
|
90
|
+
export declare function runDraw(repoRoot: string, deps: DrawDeps): Promise<SystemRun>;
|
|
91
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/draw/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAkD,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAWL,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,KAAK,EACL,KAAK,EACL,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,mEAAmE;AACnE,eAAO,MAAM,UAAU,2DAA2D,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AAElF,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CAiB/D;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,MAAM,CAEhE;AAUD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAmDtG;AAED,8EAA8E;AAC9E,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,KAAK,kBAAkB,CAAC;CAC3E;AAMD,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASlD;AAYD;;;GAGG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAiElF"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The architecture pictures writer — what `draw architecture` writes into the person's
|
|
3
|
+
* repository (System view · E1, feature `diagram-files-for-docs`, AC4, AC5, AC6 and
|
|
4
|
+
* the "someone else's file in the way" behaviour).
|
|
5
|
+
*
|
|
6
|
+
* NO BYTE OF THE RESPONSE DECIDES A PATH (INV-023). Every path here is composed from
|
|
7
|
+
* `repoRoot` (the caller's `--repo` or the working directory), the constant directory
|
|
8
|
+
* `docs/diagrams` and the constant name `architecture` with a constant extension. The
|
|
9
|
+
* response contributes file CONTENTS only; nothing in it is ever joined, resolved or
|
|
10
|
+
* compared as a path. `DiagramPaths` is built by `diagramPaths(repoRoot)` alone.
|
|
11
|
+
*
|
|
12
|
+
* FIVE FILES, ONE TABLE. Each look in `SYSTEM_PICTURE_THEMES` (`@halfcycle/events`, the
|
|
13
|
+
* one list of looks) has a PNG and an SVG; the Mermaid is one file, because Mermaid has
|
|
14
|
+
* no look. A look selects its file name from `PICTURE_NAMES`, a constant typed against
|
|
15
|
+
* `SystemPictureTheme`, so a look the list does not hold has no name and the compiler
|
|
16
|
+
* refuses a look the table forgets. The looks are iterated from the list, never from
|
|
17
|
+
* the response's keys: the strict result schema already refused any other key, and
|
|
18
|
+
* this module would not read one if it came. Which look documents embed is
|
|
19
|
+
* `EMBEDDED_LOOK`, and only there: the printed image line (`EMBED_IMAGE_REL`) is
|
|
20
|
+
* composed from the same file-name table as that look's path, so the line and the file
|
|
21
|
+
* cannot name different pictures.
|
|
22
|
+
*
|
|
23
|
+
* THE REALPATH CHECK (AC5). Before anything is made or written, the realpath of the
|
|
24
|
+
* nearest existing ancestor of `docs/diagrams` must lie inside the realpath of the
|
|
25
|
+
* repository. "Exists" is asked with `lstat`, so a dangling link is the ancestor and
|
|
26
|
+
* its failed realpath refuses, rather than being stepped over as absent. A planted
|
|
27
|
+
* `docs` link pointing out of the repository therefore refuses before `mkdir`. The
|
|
28
|
+
* same check runs again on `docs/diagrams` itself after `mkdir`, before the first file
|
|
29
|
+
* is created, so a directory made through a link cannot be written into. RESIDUAL: a
|
|
30
|
+
* process racing this one, swapping a directory for a link between that second check
|
|
31
|
+
* and a rename, is not closed by a userspace check; the window is a few syscalls.
|
|
32
|
+
*
|
|
33
|
+
* IN THE WAY (the spec's ownership rule). The command writes only files it recognises
|
|
34
|
+
* as its own: an SVG whose first line is the marker. Any SVG without the marker is
|
|
35
|
+
* named and nothing is written. Each PNG is ours only beside ITS OWN marked SVG
|
|
36
|
+
* (`architecture.png` with `architecture.svg`, `architecture.flightdeck.png` with
|
|
37
|
+
* `architecture.flightdeck.svg`), never another look's. The Mermaid file is ours only
|
|
38
|
+
* beside the embedded look's marked SVG, `architecture.svg`, the one whose name it
|
|
39
|
+
* shares. Anything at one of the five paths that is not a regular file (a link, a
|
|
40
|
+
* folder) is in the way too: replacing a link would silently turn it into a file, and
|
|
41
|
+
* following one would write somewhere this check never looked.
|
|
42
|
+
*
|
|
43
|
+
* LEFTOVER TEMPORARIES, BY THE SAME RULE. A run that dies (killed, power cut) between
|
|
44
|
+
* staging and its last rename leaves `<name>.tmp` files with no chance to clear them.
|
|
45
|
+
* Each one is judged by the rule its final file is judged by, one rule for both: an
|
|
46
|
+
* SVG temporary is ours if its first line is the marker; a PNG temporary is ours only
|
|
47
|
+
* beside its own look's marked SVG, at the final path or as that SVG's own marked
|
|
48
|
+
* temporary; the Mermaid temporary likewise only beside the embedded look's marked SVG
|
|
49
|
+
* or its marked temporary. Anything else at a temporary path (a person's own file,
|
|
50
|
+
* unmarked or unpaired, or anything that is not a regular file) is in the way, named,
|
|
51
|
+
* and never deleted. Ours are removed just before staging, so the exclusive create
|
|
52
|
+
* below never meets one and never has to delete a file it did not create. They are
|
|
53
|
+
* not removed on an up-to-date run, which touches no file (AC6); a dead run's
|
|
54
|
+
* temporaries always sit beside a final file that differs from them, so the same
|
|
55
|
+
* description run again is never up to date while they remain. RESIDUAL: a death
|
|
56
|
+
* between the create and the first byte written leaves an empty SVG temporary, which
|
|
57
|
+
* carries no marker and so is named as in the way; the window is one syscall.
|
|
58
|
+
*
|
|
59
|
+
* ALL OR NOTHING (AC4). Every check on the five files runs before any file is touched.
|
|
60
|
+
* Then each changed file is written to `<name>.tmp` beside it, every temporary first,
|
|
61
|
+
* and renamed into place one by one. If a rename fails, every file already renamed
|
|
62
|
+
* gets its previous bytes back, or is removed if it did not exist before; a file that
|
|
63
|
+
* cannot be put back is removed and named. The SVGs are renamed FIRST on purpose, then
|
|
64
|
+
* the PNGs, then the Mermaid (`DIAGRAM_FILES`'s order), and every SVG temporary is
|
|
65
|
+
* staged before any PNG or Mermaid one: if the process dies at any point, every PNG
|
|
66
|
+
* and Mermaid file or temporary it left has its owning marked SVG (final or temporary)
|
|
67
|
+
* already there, so the next run recognises all of it as its own, clears the
|
|
68
|
+
* temporaries and writes.
|
|
69
|
+
*
|
|
70
|
+
* UP TO DATE (AC6). Each of the five files' bytes is compared with the new bytes first,
|
|
71
|
+
* and an equal file is not written at all, so an unchanged description leaves every
|
|
72
|
+
* modification time as it was.
|
|
73
|
+
*
|
|
74
|
+
* ONE SEAM. The file operations go through `RecordPairIo`, the Build Record writer's
|
|
75
|
+
* seam (`../build-record/write.ts`), widened to take bytes, so the exclusive create
|
|
76
|
+
* has one implementation and a test can fail any one rename.
|
|
77
|
+
*
|
|
78
|
+
* INV-001: Node builtins, `@halfcycle/events` and this package only. SHIPS TO CLIENT
|
|
79
|
+
* DISK inside the CLI: every string here is a sentence a person reads, and no comment
|
|
80
|
+
* sits beside an object member.
|
|
81
|
+
*/
|
|
82
|
+
import { type SystemDrawDrawn, type SystemPictureTheme } from '@halfcycle/events';
|
|
83
|
+
import { type RecordPairIo } from '../build-record/write.js';
|
|
84
|
+
/** The one directory the pictures go in, relative to the repository root, `/`-separated. */
|
|
85
|
+
export declare const DIAGRAM_DIR_REL = "docs/diagrams";
|
|
86
|
+
/** The one picture name E1 draws; also the Mermaid file's name. */
|
|
87
|
+
export declare const ARCHITECTURE_NAME = "architecture";
|
|
88
|
+
/**
|
|
89
|
+
* Each look's file name, before its extension: Broadsheet takes the bare name, Flight
|
|
90
|
+
* Deck the name with its look after it. Typed against the one list of looks, so the
|
|
91
|
+
* table cannot hold a look the list does not, or miss one it does.
|
|
92
|
+
*/
|
|
93
|
+
export declare const PICTURE_NAMES: Readonly<Record<SystemPictureTheme, string>>;
|
|
94
|
+
/**
|
|
95
|
+
* The look documents embed, and the only place that choice is made. Its PNG is the
|
|
96
|
+
* printed image line, and its SVG is the one the Mermaid file sits beside.
|
|
97
|
+
*/
|
|
98
|
+
export declare const EMBEDDED_LOOK: SystemPictureTheme;
|
|
99
|
+
/**
|
|
100
|
+
* The first line of every SVG this command writes, and the only thing that makes an
|
|
101
|
+
* existing SVG ours. The service prepends it; the CLI checks it before writing and
|
|
102
|
+
* reads it on the next run. Its one home is the description contract.
|
|
103
|
+
*/
|
|
104
|
+
export declare const DIAGRAM_MARKER_LINE = "<!-- drawn by Halfcycle from docs/system.json -->";
|
|
105
|
+
/** One look's SVG or PNG. */
|
|
106
|
+
export type PictureFile = `${SystemPictureTheme}.svg` | `${SystemPictureTheme}.png`;
|
|
107
|
+
/** Which of the five files: a look's SVG or PNG, or the one Mermaid file. */
|
|
108
|
+
export type DiagramFile = PictureFile | 'mmd';
|
|
109
|
+
/** A look's SVG. */
|
|
110
|
+
export declare function svgOf(look: SystemPictureTheme): PictureFile;
|
|
111
|
+
/** A look's PNG. */
|
|
112
|
+
export declare function pngOf(look: SystemPictureTheme): PictureFile;
|
|
113
|
+
/**
|
|
114
|
+
* The five files, in the order they are renamed: every SVG, then every PNG, then the
|
|
115
|
+
* Mermaid, as the module header says why.
|
|
116
|
+
*/
|
|
117
|
+
export declare const DIAGRAM_FILES: readonly DiagramFile[];
|
|
118
|
+
/**
|
|
119
|
+
* The embedded picture as a document directly under `docs/` names it:
|
|
120
|
+
* `diagrams/architecture.png`. Composed from the same table as that file's path.
|
|
121
|
+
*/
|
|
122
|
+
export declare const EMBED_IMAGE_REL: string;
|
|
123
|
+
/** The fixed paths, composed from `repoRoot` and constants only. */
|
|
124
|
+
export interface DiagramPaths {
|
|
125
|
+
readonly repoRoot: string;
|
|
126
|
+
readonly dir: string;
|
|
127
|
+
readonly file: Readonly<Record<DiagramFile, string>>;
|
|
128
|
+
}
|
|
129
|
+
export declare function diagramPaths(repoRoot: string): DiagramPaths;
|
|
130
|
+
/** A file's place as a person finds it: `docs/diagrams/architecture.svg`. */
|
|
131
|
+
export declare function diagramRel(which: DiagramFile): string;
|
|
132
|
+
/**
|
|
133
|
+
* The five files in the order a person reads them: each look's PNG then its SVG, then
|
|
134
|
+
* the Mermaid file.
|
|
135
|
+
*/
|
|
136
|
+
export declare const DIAGRAM_FILES_AS_LISTED: readonly DiagramFile[];
|
|
137
|
+
/** `a`, `a and b`, `a, b and c`: a list as a sentence says it. */
|
|
138
|
+
export declare function spokenList(items: readonly string[]): string;
|
|
139
|
+
/**
|
|
140
|
+
* What the place the pictures go came to. `ok`: safe to write there. Otherwise the
|
|
141
|
+
* sentence to print, already naming the remedy.
|
|
142
|
+
*/
|
|
143
|
+
export type PlaceCheck = {
|
|
144
|
+
ok: true;
|
|
145
|
+
} | {
|
|
146
|
+
ok: false;
|
|
147
|
+
why: string;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* THE REALPATH CHECK (AC5), as the module header states it: the nearest ancestor of
|
|
151
|
+
* `docs/diagrams` that `lstat` finds must resolve inside the repository and be a
|
|
152
|
+
* folder. Run before anything is sent and again before anything is made.
|
|
153
|
+
*/
|
|
154
|
+
export declare function checkDiagramPlace(paths: DiagramPaths): PlaceCheck;
|
|
155
|
+
/** What is at the five paths now. `null`: nothing. Bytes: a regular file's contents. */
|
|
156
|
+
export type ExistingSet = Readonly<Record<DiagramFile, Uint8Array | null>>;
|
|
157
|
+
/**
|
|
158
|
+
* What reading the existing set came to: the set and which leftover temporaries are
|
|
159
|
+
* ours to clear, or the first file in the way (`temp`: the temporary beside it).
|
|
160
|
+
*/
|
|
161
|
+
export type ExistingRead = {
|
|
162
|
+
ok: true;
|
|
163
|
+
existing: ExistingSet;
|
|
164
|
+
ourTemps: readonly DiagramFile[];
|
|
165
|
+
} | {
|
|
166
|
+
ok: false;
|
|
167
|
+
inTheWay: DiagramFile;
|
|
168
|
+
temp: boolean;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Read what is at the five paths and at their five temporaries, and apply the one
|
|
172
|
+
* ownership rule to both, as the module header states it. The final files are judged
|
|
173
|
+
* first, in the order that names the deciding file: every SVG, then each PNG against
|
|
174
|
+
* its own look's SVG, then the Mermaid file against the embedded look's SVG. Then the
|
|
175
|
+
* temporaries in the same order, where a PNG or Mermaid temporary's owner may be the
|
|
176
|
+
* marked SVG at its final path or that SVG's own marked temporary. Anything that is not
|
|
177
|
+
* a regular file, final or temporary, is in the way.
|
|
178
|
+
*/
|
|
179
|
+
export declare function readExistingSet(paths: DiagramPaths): ExistingRead;
|
|
180
|
+
/** The sentence for a file in the way, or for the temporary file beside it when `temp`. */
|
|
181
|
+
export declare function inTheWayLine(which: DiagramFile, temp?: boolean): string;
|
|
182
|
+
/** The five files as bytes, checked and ready to write. */
|
|
183
|
+
export type DiagramSet = Readonly<Record<DiagramFile, Uint8Array>>;
|
|
184
|
+
/** What checking the drawn pictures came to: the bytes, or what was wrong, in plain words. */
|
|
185
|
+
export type PictureCheck = {
|
|
186
|
+
ok: true;
|
|
187
|
+
set: DiagramSet;
|
|
188
|
+
} | {
|
|
189
|
+
ok: false;
|
|
190
|
+
why: string;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* AC4's checks on what the service drew, all before anything is written, for each look
|
|
194
|
+
* in the list: the PNG's signature and decoded size; the SVG's marker line, its `<svg`
|
|
195
|
+
* start and `</svg>` end (one newline allowed after), and its size. Then the Mermaid's
|
|
196
|
+
* `flowchart` start and its size, once. The sizes are the events package's bounds,
|
|
197
|
+
* checked again on the bytes about to be written rather than trusted from the parse.
|
|
198
|
+
* Each sentence names the file the bad bytes were for, so a person reading it knows
|
|
199
|
+
* which picture was wrong.
|
|
200
|
+
*/
|
|
201
|
+
export declare function checkPictures(drawn: SystemDrawDrawn): PictureCheck;
|
|
202
|
+
/**
|
|
203
|
+
* What writing came to. `up-to-date`: every file already held these bytes and none
|
|
204
|
+
* was touched. `written`: the files in `changed` were replaced or created.
|
|
205
|
+
* `in-the-way` / `unsafe`: refused before anything was made, with the sentence.
|
|
206
|
+
* `failed`: a write failed; every file is as it was, except those in `removed`, which
|
|
207
|
+
* could not be put back and were removed.
|
|
208
|
+
*/
|
|
209
|
+
export type DiagramWriteResult = {
|
|
210
|
+
kind: 'up-to-date';
|
|
211
|
+
} | {
|
|
212
|
+
kind: 'written';
|
|
213
|
+
changed: DiagramFile[];
|
|
214
|
+
} | {
|
|
215
|
+
kind: 'in-the-way';
|
|
216
|
+
file: DiagramFile;
|
|
217
|
+
temp: boolean;
|
|
218
|
+
} | {
|
|
219
|
+
kind: 'unsafe';
|
|
220
|
+
why: string;
|
|
221
|
+
} | {
|
|
222
|
+
kind: 'failed';
|
|
223
|
+
file: DiagramFile;
|
|
224
|
+
removed: DiagramFile[];
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Write the checked pictures into `docs/diagrams`, all or nothing, as the module
|
|
228
|
+
* header states. Never throws for a file-system failure: it is a `failed` result,
|
|
229
|
+
* and the set on disk is the old one.
|
|
230
|
+
*/
|
|
231
|
+
export declare function writeDiagramSet(repoRoot: string, set: DiagramSet, io?: RecordPairIo): DiagramWriteResult;
|
|
232
|
+
//# sourceMappingURL=write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/draw/write.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1356,7 +1356,7 @@ function createProjectIdentityFile(targetRepoRoot) {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
|
|
1358
1358
|
// dist/install-manifest.js
|
|
1359
|
-
import { Buffer } from "node:buffer";
|
|
1359
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
1360
1360
|
import { createHash } from "node:crypto";
|
|
1361
1361
|
var INSTALL_MANIFEST_REL = ".halfcycle/install-manifest.json";
|
|
1362
1362
|
var INSTALL_MANIFEST_FORMAT = "halfcycle-install-manifest/v1";
|
|
@@ -1367,7 +1367,7 @@ function lineKey(line) {
|
|
|
1367
1367
|
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
1368
1368
|
}
|
|
1369
1369
|
function fileSha256(bytes) {
|
|
1370
|
-
const text = typeof bytes === "string" ? bytes :
|
|
1370
|
+
const text = typeof bytes === "string" ? bytes : Buffer2.from(bytes).toString("utf-8");
|
|
1371
1371
|
return sha256Hex(text.split("\n").map(lineKey).join("\n"));
|
|
1372
1372
|
}
|
|
1373
1373
|
function canonicalJson(value) {
|
|
@@ -1813,6 +1813,8 @@ var WRITE_ALLOWLIST = [
|
|
|
1813
1813
|
BUNDLE_PIN_REL,
|
|
1814
1814
|
".halfcycle/bin",
|
|
1815
1815
|
"docs/build-records",
|
|
1816
|
+
// Architecture pictures the draw command writes from Halfcycle's answer. The installer never writes here; keep the entry so this path stays declared as one Halfcycle writes.
|
|
1817
|
+
"docs/diagrams",
|
|
1816
1818
|
"test/fixtures/captured",
|
|
1817
1819
|
MCP_REGISTRATION_REL,
|
|
1818
1820
|
".gitattributes",
|
|
@@ -3889,10 +3891,14 @@ function assertInsideOutDir(outDir, candidate, identity) {
|
|
|
3889
3891
|
throw new InvalidPhaseIdentityError(identity, BUILD_RECORD_DIR);
|
|
3890
3892
|
}
|
|
3891
3893
|
}
|
|
3892
|
-
function createExclusiveOnDisk(path,
|
|
3894
|
+
function createExclusiveOnDisk(path, data) {
|
|
3895
|
+
const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
3893
3896
|
const fd = openSync(path, "wx");
|
|
3894
3897
|
try {
|
|
3895
|
-
|
|
3898
|
+
let written = 0;
|
|
3899
|
+
while (written < bytes.length) {
|
|
3900
|
+
written += writeSync(fd, bytes, written, bytes.length - written);
|
|
3901
|
+
}
|
|
3896
3902
|
} catch (err) {
|
|
3897
3903
|
closeSync(fd);
|
|
3898
3904
|
rmSync3(path, { force: true });
|
|
@@ -4009,6 +4015,22 @@ function replaceOne(io, tmp, finalPath, text) {
|
|
|
4009
4015
|
|
|
4010
4016
|
// dist/build-record/obtain.js
|
|
4011
4017
|
import { relative as relative3 } from "node:path";
|
|
4018
|
+
|
|
4019
|
+
// dist/missing-route.js
|
|
4020
|
+
var MESSAGE = {
|
|
4021
|
+
GET: /^Route GET .* not found\.$/s,
|
|
4022
|
+
POST: /^Route POST .* not found\.$/s
|
|
4023
|
+
};
|
|
4024
|
+
function isMissingRoute(method, status, body) {
|
|
4025
|
+
if (status !== 404 || typeof body !== "object" || body === null)
|
|
4026
|
+
return false;
|
|
4027
|
+
const { error, message } = body;
|
|
4028
|
+
if (error !== "Not Found" || typeof message !== "string")
|
|
4029
|
+
return false;
|
|
4030
|
+
return MESSAGE[method].test(message);
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
// dist/build-record/obtain.js
|
|
4012
4034
|
var REFUSAL_FOLLOW_UP = {
|
|
4013
4035
|
"still-open": "Nothing was written. The record is produced when the phase closes.",
|
|
4014
4036
|
"reopened": "Nothing was written. A new record is produced when the phase closes again.",
|
|
@@ -4020,12 +4042,6 @@ var REFUSAL_REASONS = phaseRecordRefusalReasonSchema.options;
|
|
|
4020
4042
|
function buildRecordUrl(credential, phase) {
|
|
4021
4043
|
return `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/phases/${encodeURIComponent(phase)}/build-record`;
|
|
4022
4044
|
}
|
|
4023
|
-
function isMissingRoute(status, body) {
|
|
4024
|
-
if (status !== 404 || typeof body !== "object" || body === null)
|
|
4025
|
-
return false;
|
|
4026
|
-
const { error, message } = body;
|
|
4027
|
-
return error === "Not Found" && typeof message === "string" && /^Route GET .* not found\.$/s.test(message);
|
|
4028
|
-
}
|
|
4029
4045
|
async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_PAIR_IO) {
|
|
4030
4046
|
const url = buildRecordUrl(credential, phase);
|
|
4031
4047
|
let res;
|
|
@@ -4082,7 +4098,7 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
|
|
|
4082
4098
|
headline: latestCloseHeadline(answer.record)
|
|
4083
4099
|
};
|
|
4084
4100
|
}
|
|
4085
|
-
if (isMissingRoute(res.status, raw)) {
|
|
4101
|
+
if (isMissingRoute("GET", res.status, raw)) {
|
|
4086
4102
|
return { kind: "unavailable", why: "this Halfcycle service does not provide Build Records yet." };
|
|
4087
4103
|
}
|
|
4088
4104
|
const message = typeof raw === "object" && raw !== null && typeof raw.message === "string" ? raw.message : null;
|