flowfig 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -23
- package/dist/atlas.js +6 -2
- package/dist/auto.js +104 -9
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +9 -4
- package/dist/check.d.ts +4 -3
- package/dist/check.js +143 -8
- package/dist/cli.js +180 -70
- package/dist/diff.d.ts +2 -0
- package/dist/diff.js +79 -24
- package/dist/element-looks.js +4 -0
- package/dist/element-world.js +6 -0
- package/dist/element.d.ts +27 -0
- package/dist/element.js +67 -0
- package/dist/explain.d.ts +45 -0
- package/dist/explain.js +161 -0
- package/dist/frames.d.ts +34 -0
- package/dist/frames.js +117 -0
- package/dist/index.js +328 -152
- package/dist/load.js +9 -4
- package/dist/looks.d.ts +15 -0
- package/dist/looks.js +32 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.js +14 -6
- package/dist/model.d.ts +105 -3
- package/dist/model.js +221 -21
- package/dist/scene.d.ts +2 -0
- package/dist/source.d.ts +4 -2
- package/dist/source.js +15 -9
- package/dist/svg.d.ts +12 -0
- package/dist/svg.js +245 -81
- package/dist/verify.d.ts +6 -0
- package/dist/verify.js +104 -56
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world-art.d.ts +59 -0
- package/dist/world-art.js +854 -0
- package/dist/world.d.ts +233 -0
- package/dist/world.js +1577 -0
- package/package.json +9 -3
package/dist/verify.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import { edgeId, nodes, toBeat } from './model.js';
|
|
2
|
+
import { edgeId, nodes, scenes, toBeat } from './model.js';
|
|
3
3
|
import { headingSlug, links } from './source.js';
|
|
4
4
|
import { codeFile, isDefined, outside, readFile } from './code.js';
|
|
5
5
|
import { edgeResult } from './edges.js';
|
|
@@ -31,6 +31,7 @@ export const coverageLine = (figure, c) => `${figure}: ${c.boxesDefined} of ${c.
|
|
|
31
31
|
(c.found + c.notFound + c.unsure + c.notChecked
|
|
32
32
|
? `; edges: ${c.found} found, ${c.notFound} not found, ${c.unsure} unsure, ${c.notChecked} not checked`
|
|
33
33
|
: '');
|
|
34
|
+
export const levelLines = (c) => (c.levels ?? []).map(({ path, coverage: l }) => `[${path}] ${l.boxesDefined} of ${l.boxes} parts defined; edges: ${l.found} found, ${l.notFound} not found, ${l.unsure} unsure, ${l.notChecked} not checked`);
|
|
34
35
|
export const unsureLines = (c) => c.unsureEdges.map((e) => `unsure edge "${e.id}": ${e.reason}`);
|
|
35
36
|
export const SPEC_MARK = '<metadata id="figure-spec">';
|
|
36
37
|
export function detailFindings(fig, root, read = readFile) {
|
|
@@ -49,69 +50,116 @@ export function verifyReport(fig, { root = process.cwd(), read = readFile, cache
|
|
|
49
50
|
const findings = [];
|
|
50
51
|
const coverage = emptyCoverage();
|
|
51
52
|
findings.push(...detailFindings(fig, root, read));
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
53
|
+
const linked = links(fig);
|
|
54
|
+
if (!linked.length)
|
|
54
55
|
return {
|
|
55
56
|
findings: [...findings, { rule: 'no-source', severity: 'warning', ids: [], message: 'no box, edge or hop has a source' }],
|
|
56
57
|
coverage,
|
|
57
58
|
};
|
|
58
59
|
const files = new Map();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
const levels = [];
|
|
61
|
+
const all = scenes(fig);
|
|
62
|
+
const byKey = new Map(all.map((s) => [s.path.join('>'), s]));
|
|
63
|
+
const sourceOf = new Map(all.flatMap((s) => nodes(s.props.layout).map((n) => [n.id, n.source])));
|
|
64
|
+
for (const scene of all) {
|
|
65
|
+
const level = emptyCoverage();
|
|
66
|
+
for (const l of linked.filter((x) => (x.level ?? '') === scene.path.join('>'))) {
|
|
67
|
+
const full = resolve(root, l.path);
|
|
68
|
+
if (!files.has(full))
|
|
69
|
+
files.set(full, outside(root, l.path) ? null : read(full));
|
|
70
|
+
const text = files.get(full);
|
|
71
|
+
const before = findings.length;
|
|
72
|
+
let doubt = false;
|
|
73
|
+
const fail = (rule, what) => findings.push({ rule, severity: 'error', ids: [], message: `${l.owner} -> ${l.source}: ${what}` });
|
|
74
|
+
if (text == null)
|
|
75
|
+
fail('missing-file', 'file not found');
|
|
76
|
+
else if (l.symbol) {
|
|
77
|
+
const code = codeFile(root, l.path, read, cache);
|
|
78
|
+
if (code) {
|
|
79
|
+
const r = defines(root, code, l.symbol, read, cache);
|
|
80
|
+
if (r === false)
|
|
81
|
+
fail('missing-symbol', 'symbol not defined');
|
|
82
|
+
doubt = r == null;
|
|
83
|
+
}
|
|
84
|
+
else if (!new RegExp(`(^|[^\\w$])${escape(l.symbol)}(?![\\w$])`).test(text) &&
|
|
85
|
+
!(/\.(md|markdown)$/i.test(l.path) && hasHeading(text, l.symbol)))
|
|
86
|
+
fail('missing-symbol', 'symbol not found');
|
|
87
|
+
}
|
|
88
|
+
if (l.owner.startsWith('box "') || l.owner.startsWith('part "')) {
|
|
89
|
+
level.boxes++;
|
|
90
|
+
if (findings.length === before && !doubt)
|
|
91
|
+
level.boxesDefined++;
|
|
76
92
|
}
|
|
77
|
-
else if (!new RegExp(`(^|[^\\w$])${escape(l.symbol)}(?![\\w$])`).test(text) &&
|
|
78
|
-
!(/\.(md|markdown)$/i.test(l.path) && hasHeading(text, l.symbol)))
|
|
79
|
-
fail('missing-symbol', 'symbol not found');
|
|
80
|
-
}
|
|
81
|
-
if (l.owner.startsWith('box "')) {
|
|
82
|
-
coverage.boxes++;
|
|
83
|
-
if (findings.length === before && !doubt)
|
|
84
|
-
coverage.boxesDefined++;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const source = new Map(nodes(fig.layout).map((n) => [n.id, n.source]));
|
|
88
|
-
const check = (who, id, caller, callee, via) => {
|
|
89
|
-
const { result, reason } = caller ? edgeResult(root, caller, callee, via, read, cache) : { result: 'not-checked', reason: '' };
|
|
90
|
-
if (result === 'found')
|
|
91
|
-
coverage.found++;
|
|
92
|
-
else if (result === 'not-found') {
|
|
93
|
-
coverage.notFound++;
|
|
94
|
-
findings.push({ rule: 'edge-not-found', severity: 'warning', ids: [id], message: `${who}: ${reason}` });
|
|
95
|
-
}
|
|
96
|
-
else if (result === 'unsure') {
|
|
97
|
-
coverage.unsure++;
|
|
98
|
-
coverage.unsureEdges.push({ id, reason });
|
|
99
93
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
94
|
+
const props = scene.props;
|
|
95
|
+
const source = new Map(nodes(props.layout).map((n) => [n.id, n.source]));
|
|
96
|
+
const call = (caller, callee, via) => caller ? edgeResult(root, caller, callee, via, read, cache) : { result: 'not-checked', reason: '' };
|
|
97
|
+
const ports = new Set(scene.ports.map((p) => p.id));
|
|
98
|
+
const edgeCall = (e) => {
|
|
99
|
+
const direct = call(e.source ?? source.get(e.from), source.get(e.to), e.via);
|
|
100
|
+
const into = ports.has(e.from);
|
|
101
|
+
if (direct.result === 'found' || e.source != null || e.via != null || into === ports.has(e.to))
|
|
102
|
+
return direct;
|
|
103
|
+
const n = into ? e.from : e.to;
|
|
104
|
+
const chains = [];
|
|
105
|
+
const inner = [];
|
|
106
|
+
let callee = source.get(into ? e.to : e.from);
|
|
107
|
+
for (let k = scene.path.length; k > 0 && byKey.get(scene.path.slice(0, k).join('>')).ports.some((p) => p.id === n); k--) {
|
|
108
|
+
const owner = sourceOf.get(scene.path[k - 1]);
|
|
109
|
+
inner.push([owner, callee]);
|
|
110
|
+
callee = owner;
|
|
111
|
+
chains.push([...inner, into ? [source.get(n), owner] : [owner, source.get(n)]].map(([a, b]) => call(a, b)));
|
|
114
112
|
}
|
|
113
|
+
if (chains.some((c) => c.every((r) => r.result === 'found')))
|
|
114
|
+
return { result: 'found', reason: '' };
|
|
115
|
+
for (const r of ['not-found', 'not-checked'])
|
|
116
|
+
if (direct.result === r && chains.every((c) => c.some((x) => x.result === r)))
|
|
117
|
+
return direct;
|
|
118
|
+
const doubt = [direct, ...chains.flat()].find((r) => r.result === 'unsure');
|
|
119
|
+
return {
|
|
120
|
+
result: 'unsure',
|
|
121
|
+
reason: doubt?.reason ?? `${direct.reason}; the call through box "${scene.path.at(-1)}" is not checked`,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const check = (who, id, { result, reason }) => {
|
|
125
|
+
if (result === 'found')
|
|
126
|
+
level.found++;
|
|
127
|
+
else if (result === 'not-found') {
|
|
128
|
+
level.notFound++;
|
|
129
|
+
findings.push({
|
|
130
|
+
rule: 'edge-not-found',
|
|
131
|
+
severity: 'warning',
|
|
132
|
+
ids: [id],
|
|
133
|
+
message: `${who}${scene.line ? ` [${scene.line}]` : ''}: ${reason}`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
else if (result === 'unsure') {
|
|
137
|
+
level.unsure++;
|
|
138
|
+
level.unsureEdges.push({ id, reason });
|
|
139
|
+
}
|
|
140
|
+
else
|
|
141
|
+
level.notChecked++;
|
|
142
|
+
};
|
|
143
|
+
for (const e of props.edges)
|
|
144
|
+
check(`edge "${edgeId(e)}"`, edgeId(e), edgeCall(e));
|
|
145
|
+
const byId = new Map(props.edges.map((e) => [edgeId(e), e]));
|
|
146
|
+
for (const step of props.steps ?? [])
|
|
147
|
+
for (const beat of step.flow)
|
|
148
|
+
for (const h of toBeat(beat).hops) {
|
|
149
|
+
const e = byId.get(h.edge);
|
|
150
|
+
if (!e || (h.source == null && h.via == null))
|
|
151
|
+
continue;
|
|
152
|
+
const [a, b] = h.back ? [e.to, e.from] : [e.from, e.to];
|
|
153
|
+
check(`hop on "${h.edge}"`, h.edge, call(h.source ?? (h.back ? undefined : e.source) ?? source.get(a), source.get(b), h.via));
|
|
154
|
+
}
|
|
155
|
+
for (const k of ['boxes', 'boxesDefined', 'found', 'notFound', 'unsure', 'notChecked'])
|
|
156
|
+
coverage[k] += level[k];
|
|
157
|
+
coverage.unsureEdges.push(...level.unsureEdges);
|
|
158
|
+
if (scene.line)
|
|
159
|
+
levels.push({ path: scene.line, coverage: level });
|
|
160
|
+
}
|
|
161
|
+
if (levels.length)
|
|
162
|
+
coverage.levels = levels;
|
|
115
163
|
return { findings, coverage };
|
|
116
164
|
}
|
|
117
165
|
export const verify = (fig, opts = {}) => verifyReport(fig, opts).findings;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.12.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// The release list bumps this with package.json.
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.12.0';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { FigLook } from './model.ts';
|
|
2
|
+
export type Pt = [number, number];
|
|
3
|
+
export type P3 = [number, number, number];
|
|
4
|
+
export declare const U = 40, C: number, S: number;
|
|
5
|
+
export declare const P: (x: number, y: number, z?: number) => Pt;
|
|
6
|
+
export declare const BY = 1.6;
|
|
7
|
+
export declare const antenna: ([x, y, z]: P3) => string;
|
|
8
|
+
export declare function hull(points: Pt[]): Pt[];
|
|
9
|
+
export type Lot = {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
hull: Pt[];
|
|
13
|
+
tag: Pt;
|
|
14
|
+
door: number;
|
|
15
|
+
depot: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
} | null;
|
|
19
|
+
roof: Pt | null;
|
|
20
|
+
};
|
|
21
|
+
export declare function street(looks: (FigLook | undefined)[], withAntenna: boolean[], pitch: number, step?: number): {
|
|
22
|
+
svg: string;
|
|
23
|
+
lots: Lot[];
|
|
24
|
+
bends: Pt[];
|
|
25
|
+
};
|
|
26
|
+
export declare const LANE = 6.6, TD = 0.78, BAR_Y = 5.7, GZ = 0.8, GL = 1.2;
|
|
27
|
+
export declare const BED_ROWS: number[];
|
|
28
|
+
export declare const INK: {
|
|
29
|
+
ink: string;
|
|
30
|
+
data: string;
|
|
31
|
+
fail: string;
|
|
32
|
+
muted: string;
|
|
33
|
+
};
|
|
34
|
+
export declare const UNIT: Record<'box' | 'barrel' | 'notes', string>;
|
|
35
|
+
export declare function truckArt(depot: {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
}): {
|
|
39
|
+
door: string;
|
|
40
|
+
yard: string;
|
|
41
|
+
road: string;
|
|
42
|
+
slots: {
|
|
43
|
+
L: number[];
|
|
44
|
+
R: number[];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export declare const PHONE: {
|
|
48
|
+
svg: string;
|
|
49
|
+
w: number;
|
|
50
|
+
h: number;
|
|
51
|
+
};
|
|
52
|
+
export type Gate = {
|
|
53
|
+
px: number;
|
|
54
|
+
ex: number;
|
|
55
|
+
y: number;
|
|
56
|
+
z: number;
|
|
57
|
+
L: number;
|
|
58
|
+
};
|
|
59
|
+
export declare function gateArt(g: Gate): string;
|