explainer-video 0.0.0-stage → 0.4.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/.claude-plugin/plugin.json +12 -0
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/cordis.patch.yml +7 -0
- package/lib/index.js +35 -0
- package/package.json +56 -4
- package/plugin.json +13 -0
- package/skills/coursework-author/.synced-from +5 -0
- package/skills/coursework-author/SKILL.md +209 -0
- package/skills/craft-codeflow-flows/SKILL.md +116 -0
- package/skills/craft-codeflow-mine/SKILL.md +207 -0
- package/skills/craft-codeflow-plan/SKILL.md +65 -0
- package/skills/craft-codeflow-scene/SKILL.md +168 -0
- package/skills/craft-video-align/SKILL.md +109 -0
- package/skills/craft-video-assemble/SKILL.md +69 -0
- package/skills/craft-video-audio/SKILL.md +59 -0
- package/skills/craft-video-enrich/SKILL.md +46 -0
- package/skills/craft-video-plan/SKILL.md +57 -0
- package/skills/craft-video-publish/SKILL.md +79 -0
- package/skills/craft-video-render/SKILL.md +68 -0
- package/skills/craft-video-scene/SKILL.md +112 -0
- package/skills/craft-video-script/SKILL.md +80 -0
- package/skills/explainer-video-from-codebase/FLOWS.md +577 -0
- package/skills/explainer-video-from-codebase/SKILL.md +659 -0
- package/skills/explainer-video-from-codebase/tools/apply-flows.mjs +531 -0
- package/skills/explainer-video-from-codebase/tools/approve-flows.mjs +56 -0
- package/skills/explainer-video-from-codebase/tools/codemap-md.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/config/dotenv.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/json.mjs +14 -0
- package/skills/explainer-video-from-codebase/tools/config/redact.mjs +16 -0
- package/skills/explainer-video-from-codebase/tools/config/toml-lite.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/yaml-lite.mjs +18 -0
- package/skills/explainer-video-from-codebase/tools/excerpt-check.mjs +186 -0
- package/skills/explainer-video-from-codebase/tools/excerpt.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/flow-diagram.mjs +168 -0
- package/skills/explainer-video-from-codebase/tools/flow-plan.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/flows-md.mjs +87 -0
- package/skills/explainer-video-from-codebase/tools/lang/c.mjs +684 -0
- package/skills/explainer-video-from-codebase/tools/lang/go.mjs +673 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-config.mjs +143 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-deps.mjs +62 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-stdlib.mjs +24 -0
- package/skills/explainer-video-from-codebase/tools/lang/python.mjs +609 -0
- package/skills/explainer-video-from-codebase/tools/lang/typescript.mjs +963 -0
- package/skills/explainer-video-from-codebase/tools/lib/decompose.mjs +102 -0
- package/skills/explainer-video-from-codebase/tools/lib/groups.mjs +77 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-coverage.mjs +68 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-v8.mjs +232 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe.mjs +162 -0
- package/skills/explainer-video-from-codebase/tools/lib/redact.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/sandbox.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/lib/test-links.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/toml.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-c.mjs +95 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-go.mjs +101 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-js.mjs +136 -0
- package/skills/explainer-video-from-codebase/tools/lib/tree-sitter.mjs +19 -0
- package/skills/explainer-video-from-codebase/tools/lib/walk.mjs +138 -0
- package/skills/explainer-video-from-codebase/tools/mine.mjs +409 -0
- package/skills/explainer-video-from-codebase/tools/propose.mjs +127 -0
- package/skills/explainer-video-from-codebase/tools/rank.mjs +119 -0
- package/skills/explainer-video-from-codebase/tools/ranking-md.mjs +76 -0
- package/skills/explainer-video-from-codebase/tools/rules/c-handlers.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/celery-taskiq.mjs +48 -0
- package/skills/explainer-video-from-codebase/tools/rules/click-typer.mjs +6 -0
- package/skills/explainer-video-from-codebase/tools/rules/express.mjs +72 -0
- package/skills/explainer-video-from-codebase/tools/rules/fastapi.mjs +99 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-http.mjs +173 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-test.mjs +108 -0
- package/skills/explainer-video-from-codebase/tools/rules/grpc.mjs +57 -0
- package/skills/explainer-video-from-codebase/tools/rules/gtest.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/rules/haystack.mjs +55 -0
- package/skills/explainer-video-from-codebase/tools/rules/jest-vitest.mjs +100 -0
- package/skills/explainer-video-from-codebase/tools/rules/js-http-client.mjs +111 -0
- package/skills/explainer-video-from-codebase/tools/rules/nextjs.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/process-spawn.mjs +49 -0
- package/skills/explainer-video-from-codebase/tools/rules/py-http-client.mjs +26 -0
- package/skills/explainer-video-from-codebase/tools/rules/pytest.mjs +107 -0
- package/skills/explainer-video-from-codebase/tools/rules/react-router.mjs +17 -0
- package/skills/explainer-video-from-codebase/tools/rules/registry.mjs +31 -0
- package/skills/explainer-video-from-codebase/tools/trace/c/fmtrace-sh +14 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-net.cjs +33 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-srcmap.cjs +41 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/fmtrace_plugin.py +87 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/sitecustomize.py +330 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/linux-bwrap.sh +73 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/macos-nonet.sb +41 -0
- package/skills/explainer-video-from-codebase/tools/trace.mjs +577 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-c.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-cpp.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-go.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-javascript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-python.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-typescript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/web-tree-sitter.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/MANIFEST.json +172 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-c.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-cpp.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-go.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.js +4081 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.wasm +0 -0
- package/skills/explainer-video-from-coursework/ARCHITECTURE.md +59 -0
- package/skills/explainer-video-from-coursework/PIPELINE.md +343 -0
- package/skills/explainer-video-from-coursework/ROADMAP.md +725 -0
- package/skills/explainer-video-from-coursework/SERIES.md +232 -0
- package/skills/explainer-video-from-coursework/SETUP.md +105 -0
- package/skills/explainer-video-from-coursework/SKILL.md +858 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-chart.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-equation.js +10 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-hubspoke.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-loopflow.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-sequence.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-timeline.js +11 -0
- package/skills/explainer-video-from-coursework/src/scene-code.html +127 -0
- package/skills/explainer-video-from-coursework/src/scene-code.js +379 -0
- package/skills/explainer-video-from-coursework/src/scene-core.js +1250 -0
- package/skills/explainer-video-from-coursework/src/scene-diagram.html +50 -0
- package/skills/explainer-video-from-coursework/src/scene-svg.html +50 -0
- package/skills/explainer-video-from-coursework/src/timeline.js +297 -0
- package/skills/explainer-video-from-coursework/src/timing-chart.js +160 -0
- package/skills/explainer-video-from-coursework/tools/align.mjs +419 -0
- package/skills/explainer-video-from-coursework/tools/apply-enrichment.mjs +206 -0
- package/skills/explainer-video-from-coursework/tools/assemble.mjs +223 -0
- package/skills/explainer-video-from-coursework/tools/build-audio.mjs +234 -0
- package/skills/explainer-video-from-coursework/tools/chromium.mjs +73 -0
- package/skills/explainer-video-from-coursework/tools/debug-page.mjs +75 -0
- package/skills/explainer-video-from-coursework/tools/env.mjs +30 -0
- package/skills/explainer-video-from-coursework/tools/frame-dedup.mjs +131 -0
- package/skills/explainer-video-from-coursework/tools/key-source.mjs +151 -0
- package/skills/explainer-video-from-coursework/tools/model.mjs +21 -0
- package/skills/explainer-video-from-coursework/tools/naming.mjs +38 -0
- package/skills/explainer-video-from-coursework/tools/onset-audit.mjs +51 -0
- package/skills/explainer-video-from-coursework/tools/pacing-check.mjs +350 -0
- package/skills/explainer-video-from-coursework/tools/planner.mjs +616 -0
- package/skills/explainer-video-from-coursework/tools/publish.mjs +230 -0
- package/skills/explainer-video-from-coursework/tools/render.mjs +205 -0
- package/skills/explainer-video-from-coursework/tools/resolve-beats.mjs +239 -0
- package/skills/explainer-video-from-coursework/tools/script-check.mjs +174 -0
- package/skills/explainer-video-from-coursework/tools/stamp.mjs +71 -0
- package/skills/explainer-video-from-coursework/tools/timing-report.mjs +299 -0
- package/skills/explainer-video-from-coursework/tools/transcribe-gemini.mjs +96 -0
- package/skills/explainer-video-from-coursework/tools/tts-gemini.mjs +394 -0
- package/skills/explainer-video-from-coursework/tools/verify-audio.mjs +88 -0
- package/skills/explainer-video-from-coursework/tools/workdir.mjs +124 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js +8 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js.LICENSE.txt +16 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* Config-aware dispatch for Python (design rev3 §3.6). For every factory that can return two or more classes,
|
|
2
|
+
evaluate each alternative's branch guards with three-valued logic under the settings defaults in code and under
|
|
3
|
+
each in-repo config file that sets a key the guards read (a "profile"). Every alternative is tagged with what
|
|
4
|
+
selects it. A site is
|
|
5
|
+
config-narrowed when every profile decides exactly one alternative,
|
|
6
|
+
config-dependent when a guard reads a function parameter (runtime input) or a value no profile settles.
|
|
7
|
+
Profiles carry their file's group: production and dev sites are decided under code defaults and prod/dev files
|
|
8
|
+
only; an eval or test data file decides only eval or test sites. B14: `getattr(settings, "x", default)` reads a
|
|
9
|
+
declared field x exactly as `settings.x` does (profile value, else the field default); when no settings field
|
|
10
|
+
declares x the default decides, since a settings object ignores undeclared env vars.
|
|
11
|
+
Values are evaluated as read; what the report shows of them goes through shownValue (a secret is shown only as set). */
|
|
12
|
+
import { shownValue } from '../config/redact.mjs';
|
|
13
|
+
|
|
14
|
+
const UNK = { unk: true };
|
|
15
|
+
const isU = v => v && v.unk;
|
|
16
|
+
const truthy = v => isU(v) ? UNK : !(v === null || v === false || v === '' || v === 0 || v === undefined);
|
|
17
|
+
const not3 = v => isU(v) ? UNK : !v;
|
|
18
|
+
const and3 = (a, b) => (a === false || b === false) ? false : (isU(a) || isU(b)) ? UNK : true;
|
|
19
|
+
const or3 = (a, b) => (a === true || b === true) ? true : (isU(a) || isU(b)) ? UNK : false;
|
|
20
|
+
const DATA = /(Response|Result|Payload|Model|Schema|Error|Exception)$/;
|
|
21
|
+
// a guard that only tests a private module global (`_instance is None`, `key in _cache`) is a cache check, not a selector
|
|
22
|
+
const isCacheGuard = text => { const ids = text.match(/[A-Za-z_]\w*/g) || [];
|
|
23
|
+
const free = ids.filter(i => !/^(is|not|None|in|and|or|self|cache_key|key)$/.test(i));
|
|
24
|
+
return free.length > 0 && free.every(i => i.startsWith('_')); };
|
|
25
|
+
const PROFILE_GROUPS = { prod: ['prod', 'dev'], dev: ['prod', 'dev'], eval: ['prod', 'dev', 'eval'], test: ['prod', 'dev', 'test'] };
|
|
26
|
+
|
|
27
|
+
export function classifyDispatch({ py, factories, entries, configKV, parser }) {
|
|
28
|
+
const fields = new Map(py.settingsFields.map(f => [f.name, f]));
|
|
29
|
+
const props = new Map(py.settingsProps.map(p => [p.name, p]));
|
|
30
|
+
const envOf = f => [f.name.toUpperCase(), ...(f.alias ? [f.alias.toUpperCase()] : [])];
|
|
31
|
+
const parseDefault = f => {
|
|
32
|
+
if (f.default === undefined) return UNK;
|
|
33
|
+
if (f.defaultIsString) return f.default;
|
|
34
|
+
if (f.default === 'None') return null; if (f.default === 'True') return true; if (f.default === 'False') return false;
|
|
35
|
+
if (/^-?\d+(\.\d+)?$/.test(f.default)) return Number(f.default);
|
|
36
|
+
return UNK;
|
|
37
|
+
};
|
|
38
|
+
const coerce = (f, v) => { const d = parseDefault(f);
|
|
39
|
+
if (typeof d === 'boolean') return /^(1|true|yes|on)$/i.test(v);
|
|
40
|
+
if (typeof d === 'number') return Number(v);
|
|
41
|
+
return v; };
|
|
42
|
+
const exprOf = text => { const t = parser.parse(`(${text})\n`); const n = t.rootNode.namedChildren[0]?.namedChildren[0]?.namedChildren[0]; return n; };
|
|
43
|
+
|
|
44
|
+
function evaluate(text, fn, profile, refs, depth = 0) {
|
|
45
|
+
const alias = py.fnAlias.get(fn) || {}, params = new Set(py.symbols.get(fn)?.params || []);
|
|
46
|
+
const settingsVal = (name, fallback) => {
|
|
47
|
+
refs.settings.add(name);
|
|
48
|
+
if (fields.has(name)) { const f = fields.get(name);
|
|
49
|
+
for (const e of envOf(f)) if (profile.values.has(e)) return coerce(f, profile.values.get(e).value);
|
|
50
|
+
return parseDefault(f); }
|
|
51
|
+
if (props.has(name)) return ev(exprOf(props.get(name).expr), true);
|
|
52
|
+
return fallback === undefined ? UNK : fallback;
|
|
53
|
+
};
|
|
54
|
+
const isSettingsObj = (n, inProp) => n && ((n.type === 'identifier' && (n.text === 'settings' || (inProp && n.text === 'self'))) ||
|
|
55
|
+
(n.type === 'call' && /get_settings$/.test(n.childForFieldName('function').text)) || (n.type === 'attribute' && /^self\._?settings$/.test(n.text)));
|
|
56
|
+
function ev(n, inProp = false) {
|
|
57
|
+
if (!n) return UNK;
|
|
58
|
+
switch (n.type) {
|
|
59
|
+
case 'parenthesized_expression': return ev(n.namedChildren[0], inProp);
|
|
60
|
+
case 'string': return n.namedChildren.filter(c => c.type === 'string_content').map(c => c.text).join('');
|
|
61
|
+
case 'integer': case 'float': return Number(n.text);
|
|
62
|
+
case 'true': return true; case 'false': return false; case 'none': return null;
|
|
63
|
+
case 'not_operator': return not3(truthy(ev(n.childForFieldName('argument'), inProp)));
|
|
64
|
+
case 'boolean_operator': { const op = n.children.find(c => !c.isNamed && /and|or/.test(c.type))?.type;
|
|
65
|
+
const a = truthy(ev(n.childForFieldName('left'), inProp)), b = truthy(ev(n.childForFieldName('right'), inProp));
|
|
66
|
+
return op === 'and' ? and3(a, b) : or3(a, b); }
|
|
67
|
+
case 'comparison_operator': {
|
|
68
|
+
const ops = n.children.filter(c => !c.isNamed).map(c => c.type).join(' ');
|
|
69
|
+
const [l, r] = n.namedChildren.map(x => ev(x, inProp));
|
|
70
|
+
if (/^is not$/.test(ops)) return isU(l) ? UNK : l !== null && l !== undefined;
|
|
71
|
+
if (/^is$/.test(ops)) return isU(l) ? UNK : l === null || l === undefined;
|
|
72
|
+
if (isU(l) || isU(r)) return UNK;
|
|
73
|
+
if (ops === '==') return l === r; if (ops === '!=') return l !== r;
|
|
74
|
+
if (ops === 'in') return typeof r === 'string' && typeof l === 'string' ? r.includes(l) : UNK;
|
|
75
|
+
if (ops === 'not in') return typeof r === 'string' && typeof l === 'string' ? !r.includes(l) : UNK;
|
|
76
|
+
return UNK; }
|
|
77
|
+
case 'call': { const f = n.childForFieldName('function'); const args = n.childForFieldName('arguments').namedChildren;
|
|
78
|
+
if (f.type === 'attribute' && /^(lower|upper|strip)$/.test(f.childForFieldName('attribute').text)) {
|
|
79
|
+
const v = ev(f.childForFieldName('object'), inProp); if (isU(v) || typeof v !== 'string') return v === null ? UNK : (isU(v) ? UNK : v);
|
|
80
|
+
const m = f.childForFieldName('attribute').text; return m === 'lower' ? v.toLowerCase() : m === 'upper' ? v.toUpperCase() : v.trim(); }
|
|
81
|
+
if (f.text === 'getattr' && isSettingsObj(args[0], inProp) && args[1]?.type === 'string')
|
|
82
|
+
return settingsVal(ev(args[1], inProp), args[2] ? ev(args[2], inProp) : undefined);
|
|
83
|
+
if (f.text === 'bool') return truthy(ev(args[0], inProp));
|
|
84
|
+
if (f.text === 'str') { const v = ev(args[0], inProp); return isU(v) ? UNK : String(v); }
|
|
85
|
+
return UNK; }
|
|
86
|
+
case 'attribute': {
|
|
87
|
+
const obj = n.childForFieldName('object');
|
|
88
|
+
if (isSettingsObj(obj, inProp)) return settingsVal(n.childForFieldName('attribute').text);
|
|
89
|
+
return UNK; }
|
|
90
|
+
case 'identifier': {
|
|
91
|
+
if (alias[n.text] && depth < 4) return evaluate(alias[n.text], fn, profile, refs, depth + 1);
|
|
92
|
+
if (params.has(n.text)) { refs.params.add(n.text); return UNK; }
|
|
93
|
+
if (n.text.startsWith('_')) { refs.cache.add(n.text); return UNK; }
|
|
94
|
+
return UNK; }
|
|
95
|
+
}
|
|
96
|
+
return UNK;
|
|
97
|
+
}
|
|
98
|
+
return ev(exprOf(text));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const isEntry = new Set(entries.map(e => e.id));
|
|
102
|
+
const groupOf = id => py.symbols.get(id)?.group || null;
|
|
103
|
+
const sites = Object.entries(factories).map(([fn, rets]) => ({ fn, rets, classes: [...new Set(rets.map(r => r.cls || 'ext:' + r.ext))] }))
|
|
104
|
+
.filter(s => s.classes.length > 1 && !isEntry.has(s.fn) && !s.classes.every(c => DATA.test(c)));
|
|
105
|
+
|
|
106
|
+
// profiles: code defaults, then one per in-repo config file that sets any key a guard reads
|
|
107
|
+
const probe = new Set();
|
|
108
|
+
for (const s of sites) for (const r of s.rets) for (const gd of r.guards) { const refs = { settings: new Set(), params: new Set(), cache: new Set() };
|
|
109
|
+
evaluate(gd.text, r.fn || s.fn, { values: new Map() }, refs); for (const x of refs.settings) probe.add(x); }
|
|
110
|
+
for (const p of props.values()) if (probe.has(p.name)) for (const m of (p.expr || '').matchAll(/self\.(\w+)/g)) probe.add(m[1]);
|
|
111
|
+
const relevantEnv = new Set([...probe].filter(x => fields.has(x)).flatMap(x => envOf(fields.get(x))));
|
|
112
|
+
const byFile = new Map();
|
|
113
|
+
for (const kv of configKV) if (relevantEnv.has(kv.key.toUpperCase()))
|
|
114
|
+
(byFile.get(kv.file) || byFile.set(kv.file, { group: kv.group, values: new Map() }).get(kv.file)).values.set(kv.key.toUpperCase(), kv);
|
|
115
|
+
const profiles = [{ name: 'code defaults', group: 'prod', values: new Map() }, ...[...byFile].map(([file, p]) => ({ name: file, group: p.group, values: p.values }))];
|
|
116
|
+
|
|
117
|
+
const report = [];
|
|
118
|
+
for (const s of sites) {
|
|
119
|
+
const group = groupOf(s.fn) || 'prod';
|
|
120
|
+
const ps = profiles.filter((p, i) => i === 0 || (PROFILE_GROUPS[group] || []).includes(p.group));
|
|
121
|
+
const alts = s.rets.map(r => {
|
|
122
|
+
const guards = r.guards.filter(gd => !isCacheGuard(gd.text));
|
|
123
|
+
const refs = { settings: new Set(), params: new Set(), cache: new Set() };
|
|
124
|
+
const sel = ps.map(p => { let v = true; for (const gd of guards) { const x = truthy(evaluate(gd.text, s.fn, p, refs)); v = and3(v, gd.neg ? not3(x) : x); } return isU(v) ? '?' : v ? 'yes' : 'no'; });
|
|
125
|
+
const cls = r.cls || 'ext:' + r.ext;
|
|
126
|
+
return { cls, group: r.cls ? groupOf(r.cls) : 'ext', line: r.line, guards: guards.map(gd => (gd.neg ? 'not ' : '') + gd.text.replace(/\s+/g, ' ')),
|
|
127
|
+
settings: [...refs.settings], params: [...refs.params], sel };
|
|
128
|
+
});
|
|
129
|
+
// merge identical class rows (singleton paths produce duplicates)
|
|
130
|
+
const merged = []; for (const a of alts) if (!merged.some(x => x.cls === a.cls && x.guards.join('&') === a.guards.join('&'))) merged.push(a);
|
|
131
|
+
const settingsRead = [...new Set(merged.flatMap(a => a.settings))];
|
|
132
|
+
const params = [...new Set(merged.flatMap(a => a.params))];
|
|
133
|
+
const inRepoValues = settingsRead.filter(x => fields.has(x)).map(x => ({ setting: x, env: envOf(fields.get(x))[0], default: shownValue(envOf(fields.get(x)).join(' '), fields.get(x).default),
|
|
134
|
+
files: ps.slice(1).filter(p => envOf(fields.get(x)).some(e => p.values.has(e))).map(p => { const kv = envOf(fields.get(x)).map(e => p.values.get(e)).find(Boolean); return `${p.name}:${kv.line}=${shownValue(kv.key, kv.value)}`; }) }));
|
|
135
|
+
const decided = ps.map((p, i) => { const yes = merged.filter(a => a.sel[i] === 'yes'); const maybe = merged.filter(a => a.sel[i] === '?');
|
|
136
|
+
return yes.length === 1 && !maybe.length ? yes[0].cls : maybe.length ? `? (${[...yes, ...maybe].map(a => a.cls.split(/[:.]/).pop()).join('|')})` : 'none'; });
|
|
137
|
+
const classification = params.length ? 'config-dependent (runtime input)' : decided.every(d => !d.startsWith('?')) ? 'config-narrowed' : 'config-dependent (value not in repo)';
|
|
138
|
+
report.push({ factory: s.fn, group, classification,
|
|
139
|
+
alternatives: merged.map(a => ({ cls: a.cls, group: a.group, line: a.line, guards: a.guards, sel: Object.fromEntries(ps.map((p, i) => [p.name, a.sel[i]])) })),
|
|
140
|
+
settingsRead, params, inRepoValues, decidedByProfile: Object.fromEntries(ps.map((p, i) => [p.name, decided[i]])) });
|
|
141
|
+
}
|
|
142
|
+
return { profiles: profiles.map(p => ({ name: p.name, group: p.group, keys: [...p.values.keys()] })), sites: report, guardKeys: relevantEnv };
|
|
143
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* The third-party names a repo declares as dependencies: requirements*.txt, pyproject.toml ([project] dependencies and
|
|
2
|
+
optional-dependencies, Poetry dependency tables), setup.cfg install_requires / extras_require, and Pipfile. Names are
|
|
3
|
+
normalised (case, `-` / `_` / `.`) so `Stripe` declares `import stripe`. Read, never executed. Returns the names and
|
|
4
|
+
the TOML files the reader rejected, whose names come from a line scan instead. */
|
|
5
|
+
import { parseToml } from '../lib/toml.mjs';
|
|
6
|
+
export const DEPENDENCY_FILES = /(^|\/)(requirements[\w.-]*\.txt|pyproject\.toml|setup\.cfg|Pipfile)$/;
|
|
7
|
+
export const normaliseDep = name => name.toLowerCase().replace(/[-_.]+/g, '_');
|
|
8
|
+
|
|
9
|
+
const reqName = spec => /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(spec)?.[1] ?? null;
|
|
10
|
+
const isTable = v => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
11
|
+
const STRING = /"((?:[^"\\]|\\.)*)"|'([^']*)'/g;
|
|
12
|
+
const quoted = line => [...line.matchAll(STRING)].map(m => m[1] ?? m[2]);
|
|
13
|
+
const closesArray = line => /\]/.test(line.replace(STRING, '').replace(/#.*/, ''));
|
|
14
|
+
|
|
15
|
+
// the names a TOML file the reader rejected still shows, line by line: a `]` ends an array only outside a string
|
|
16
|
+
function scanTomlLines(lines, add) {
|
|
17
|
+
let table = '', inArray = false;
|
|
18
|
+
for (const ln of lines) {
|
|
19
|
+
const head = /^\s*\[([^\]]+)\]\s*(#.*)?$/.exec(ln);
|
|
20
|
+
if (head) { table = head[1].trim(); inArray = false; continue; }
|
|
21
|
+
const keyTable = /^(tool\.poetry(\.group\.[\w-]+)?\.(dev-)?dependencies|packages|dev-packages)$/.test(table);
|
|
22
|
+
if (keyTable) { const k = /^\s*["']?([A-Za-z0-9][\w.-]*)["']?\s*=/.exec(ln); if (k) add(k[1]); continue; }
|
|
23
|
+
if (table !== 'project' && table !== 'project.optional-dependencies') continue;
|
|
24
|
+
const start = /^\s*([\w-]+)\s*=\s*\[(.*)$/.exec(ln);
|
|
25
|
+
if (start && (table === 'project.optional-dependencies' || start[1] === 'dependencies')) {
|
|
26
|
+
for (const s of quoted(start[2])) add(reqName(s));
|
|
27
|
+
inArray = !closesArray(start[2]);
|
|
28
|
+
} else if (inArray) { for (const s of quoted(ln)) add(reqName(s)); if (closesArray(ln)) inArray = false; }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function declaredDependencies(files) {
|
|
33
|
+
const out = new Set(), unparsed = [];
|
|
34
|
+
const add = n => { if (n && !/^python$/i.test(n)) out.add(normaliseDep(n)); };
|
|
35
|
+
for (const { rel, text } of files) {
|
|
36
|
+
const lines = text.split('\n');
|
|
37
|
+
if (/requirements[\w.-]*\.txt$/.test(rel)) { for (const ln of lines) if (!/^\s*(#|-)/.test(ln)) add(reqName(ln.replace(/#.*/, ''))); continue; }
|
|
38
|
+
if (/(^|\/)setup\.cfg$/.test(rel)) {
|
|
39
|
+
let table = '', inReq = false;
|
|
40
|
+
for (const ln of lines) {
|
|
41
|
+
const head = /^\s*\[([^\]]+)\]/.exec(ln);
|
|
42
|
+
if (head) { table = head[1].trim(); inReq = false; continue; }
|
|
43
|
+
const kv = /^([\w.-]+)\s*=\s*(.*)$/.exec(ln);
|
|
44
|
+
if (kv) { inReq = (table === 'options' && kv[1] === 'install_requires') || table === 'options.extras_require'; if (inReq) add(reqName(kv[2])); continue; }
|
|
45
|
+
if (inReq && /^\s+\S/.test(ln)) add(reqName(ln));
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// pyproject.toml and Pipfile are TOML: parsed, so a `]` inside a string (`"uvicorn[standard]"`) is no array end
|
|
50
|
+
let doc;
|
|
51
|
+
try { doc = parseToml(text); } catch { unparsed.push(rel); scanTomlLines(lines, add); continue; }
|
|
52
|
+
const keys = t => isTable(t) ? Object.keys(t) : [];
|
|
53
|
+
const specs = a => Array.isArray(a) ? a.filter(s => typeof s === 'string') : [];
|
|
54
|
+
if (/(^|\/)Pipfile$/.test(rel)) { for (const n of [...keys(doc.packages), ...keys(doc['dev-packages'])]) add(n); continue; }
|
|
55
|
+
const project = isTable(doc.project) ? doc.project : {}, poetry = isTable(doc.tool?.poetry) ? doc.tool.poetry : {};
|
|
56
|
+
for (const s of specs(project.dependencies)) add(reqName(s));
|
|
57
|
+
for (const a of Object.values(isTable(project['optional-dependencies']) ? project['optional-dependencies'] : {})) for (const s of specs(a)) add(reqName(s));
|
|
58
|
+
for (const n of [...keys(poetry.dependencies), ...keys(poetry['dev-dependencies']),
|
|
59
|
+
...Object.values(isTable(poetry.group) ? poetry.group : {}).flatMap(g => keys(g?.dependencies))]) add(n);
|
|
60
|
+
}
|
|
61
|
+
return { names: out, unparsed };
|
|
62
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* Top-level names of the Python standard library (CPython 3.13's sys.stdlib_module_names, plus modules removed in
|
|
2
|
+
3.12 that older code still imports), frozen here so mining is the same on every machine. An in-repo module or
|
|
3
|
+
package dir with one of these names never captures the standard-library import. */
|
|
4
|
+
export const STDLIB = new Set([
|
|
5
|
+
'abc', 'antigravity', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'base64', 'bdb',
|
|
6
|
+
'binascii', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cmath', 'cmd', 'code', 'codecs', 'codeop',
|
|
7
|
+
'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy',
|
|
8
|
+
'copyreg', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils',
|
|
9
|
+
'doctest', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput',
|
|
10
|
+
'fnmatch', 'fractions', 'ftplib', 'functools', 'gc', 'genericpath', 'getopt', 'getpass', 'gettext', 'glob',
|
|
11
|
+
'graphlib', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'idlelib', 'imaplib', 'imp', 'importlib',
|
|
12
|
+
'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'linecache', 'locale', 'logging', 'lzma', 'mailbox',
|
|
13
|
+
'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msvcrt', 'multiprocessing', 'netrc', 'nt', 'ntpath',
|
|
14
|
+
'nturl2path', 'numbers', 'opcode', 'operator', 'optparse', 'os', 'pathlib', 'pdb', 'pickle', 'pickletools',
|
|
15
|
+
'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd',
|
|
16
|
+
'py_compile', 'pyclbr', 'pydoc', 'pydoc_data', 'pyexpat', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib',
|
|
17
|
+
'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil',
|
|
18
|
+
'signal', 'site', 'smtpd', 'smtplib', 'socket', 'socketserver', 'sqlite3', 'sre_compile', 'sre_constants',
|
|
19
|
+
'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'symtable', 'sys',
|
|
20
|
+
'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'tempfile', 'termios', 'textwrap', 'this', 'threading', 'time',
|
|
21
|
+
'timeit', 'tkinter', 'token', 'tokenize', 'tomllib', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle',
|
|
22
|
+
'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uuid', 'venv', 'warnings', 'wave', 'weakref',
|
|
23
|
+
'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo',
|
|
24
|
+
]);
|