@skill-map/cli 0.18.0 → 0.20.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 +4 -0
- package/dist/cli/tutorial/sm-tutorial.md +9 -10
- package/dist/cli.js +11062 -7069
- package/dist/cli.js.map +1 -1
- package/dist/conformance/index.js +1 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +424 -148
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +776 -119
- package/dist/kernel/index.js +424 -148
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +125 -15
- package/dist/ui/chunk-2W62S3FU.js +2638 -0
- package/dist/ui/chunk-C7QWBAYP.js +247 -0
- package/dist/ui/chunk-DLT5AP43.js +237 -0
- package/dist/ui/chunk-HJSRMZTK.js +450 -0
- package/dist/ui/chunk-HOBQ4G4O.js +125 -0
- package/dist/ui/chunk-IBUV6OG2.js +1 -0
- package/dist/ui/chunk-LQTUSDHD.js +124 -0
- package/dist/ui/chunk-QICH7GU2.js +5 -0
- package/dist/ui/chunk-UJRROL5X.js +1 -0
- package/dist/ui/chunk-VLNLJAUB.js +61 -0
- package/dist/ui/chunk-W3JLG7BI.js +965 -0
- package/dist/ui/index.html +10 -2
- package/dist/ui/main-QHE47BCM.js +1 -0
- package/dist/ui/{styles-CBPFNGXA.css → styles-VJ5Q6D2X.css} +1 -1
- package/migrations/001_initial.sql +125 -15
- package/package.json +2 -2
- package/dist/migrations/002_sidecar_columns.sql +0 -53
- package/dist/migrations/003_drop_node_author.sql +0 -20
- package/dist/migrations/004_sidecar_root_json.sql +0 -23
- package/dist/migrations/005_node_favorites.sql +0 -20
- package/dist/ui/chunk-JKJGGXCS.js +0 -1025
- package/dist/ui/chunk-SX2A3WBX.js +0 -247
- package/dist/ui/chunk-TWZHUCAT.js +0 -237
- package/dist/ui/chunk-WTAL2RK4.js +0 -1
- package/dist/ui/chunk-Z3UJHHTC.js +0 -3091
- package/dist/ui/main-AAYGMON4.js +0 -1
- package/migrations/002_sidecar_columns.sql +0 -53
- package/migrations/003_drop_node_author.sql +0 -20
- package/migrations/004_sidecar_root_json.sql +0 -23
- package/migrations/005_node_favorites.sql +0 -20
package/dist/kernel/index.js
CHANGED
|
@@ -28,7 +28,7 @@ function tx(template, vars = {}) {
|
|
|
28
28
|
var EXTENSION_KINDS = Object.freeze([
|
|
29
29
|
"provider",
|
|
30
30
|
"extractor",
|
|
31
|
-
"
|
|
31
|
+
"analyzer",
|
|
32
32
|
"action",
|
|
33
33
|
"formatter",
|
|
34
34
|
"hook"
|
|
@@ -103,7 +103,7 @@ import yaml4 from "js-yaml";
|
|
|
103
103
|
// package.json
|
|
104
104
|
var package_default = {
|
|
105
105
|
name: "@skill-map/cli",
|
|
106
|
-
version: "0.
|
|
106
|
+
version: "0.20.0",
|
|
107
107
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
108
108
|
license: "MIT",
|
|
109
109
|
type: "module",
|
|
@@ -169,7 +169,7 @@ var package_default = {
|
|
|
169
169
|
},
|
|
170
170
|
dependencies: {
|
|
171
171
|
"@hono/node-server": "2.0.1",
|
|
172
|
-
"@skill-map/spec": "0.
|
|
172
|
+
"@skill-map/spec": "0.20.0",
|
|
173
173
|
ajv: "8.18.0",
|
|
174
174
|
"ajv-formats": "3.0.1",
|
|
175
175
|
chokidar: "5.0.0",
|
|
@@ -263,15 +263,15 @@ function readSidecarFor(mdAbsolutePath) {
|
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
265
|
const root = parsedYaml;
|
|
266
|
-
const
|
|
266
|
+
const identityBlock = root["identity"];
|
|
267
267
|
const annotationsRaw = root["annotations"];
|
|
268
268
|
const annotations = isPlainObject(annotationsRaw) ? Object.keys(annotationsRaw).length === 0 ? null : annotationsRaw : null;
|
|
269
269
|
return {
|
|
270
270
|
parsed: {
|
|
271
271
|
filePath: sidecarPath,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
identityBodyHash: String(identityBlock["bodyHash"]),
|
|
273
|
+
identityFrontmatterHash: String(identityBlock["frontmatterHash"]),
|
|
274
|
+
identityPath: String(identityBlock["path"]),
|
|
275
275
|
annotations,
|
|
276
276
|
raw: root
|
|
277
277
|
},
|
|
@@ -508,18 +508,20 @@ function formatAjvErrors(errors) {
|
|
|
508
508
|
|
|
509
509
|
// kernel/extensions/hook.ts
|
|
510
510
|
var HOOK_TRIGGERS = Object.freeze([
|
|
511
|
+
"boot",
|
|
511
512
|
"scan.started",
|
|
512
513
|
"scan.completed",
|
|
513
514
|
"extractor.completed",
|
|
514
|
-
"
|
|
515
|
+
"analyzer.completed",
|
|
515
516
|
"action.completed",
|
|
516
517
|
"job.spawning",
|
|
517
518
|
"job.completed",
|
|
518
|
-
"job.failed"
|
|
519
|
+
"job.failed",
|
|
520
|
+
"shutdown"
|
|
519
521
|
]);
|
|
520
522
|
|
|
521
523
|
// kernel/adapters/plugin-loader.ts
|
|
522
|
-
var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "
|
|
524
|
+
var KNOWN_KINDS = /* @__PURE__ */ new Set(["provider", "extractor", "analyzer", "action", "formatter", "hook"]);
|
|
523
525
|
var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
|
|
524
526
|
var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
|
|
525
527
|
function installedSpecVersion() {
|
|
@@ -535,6 +537,135 @@ import { readFileSync as readFileSync4 } from "fs";
|
|
|
535
537
|
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
536
538
|
import { createRequire as createRequire4 } from "module";
|
|
537
539
|
import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
|
|
540
|
+
var SCHEMA_FILES = {
|
|
541
|
+
node: "schemas/node.schema.json",
|
|
542
|
+
link: "schemas/link.schema.json",
|
|
543
|
+
issue: "schemas/issue.schema.json",
|
|
544
|
+
"scan-result": "schemas/scan-result.schema.json",
|
|
545
|
+
"execution-record": "schemas/execution-record.schema.json",
|
|
546
|
+
"project-config": "schemas/project-config.schema.json",
|
|
547
|
+
"plugins-registry": "schemas/plugins-registry.schema.json",
|
|
548
|
+
job: "schemas/job.schema.json",
|
|
549
|
+
"report-base": "schemas/report-base.schema.json",
|
|
550
|
+
"conformance-case": "schemas/conformance-case.schema.json",
|
|
551
|
+
"history-stats": "schemas/history-stats.schema.json",
|
|
552
|
+
"extension-provider": "schemas/extensions/provider.schema.json",
|
|
553
|
+
"extension-extractor": "schemas/extensions/extractor.schema.json",
|
|
554
|
+
"extension-analyzer": "schemas/extensions/analyzer.schema.json",
|
|
555
|
+
"extension-action": "schemas/extensions/action.schema.json",
|
|
556
|
+
"extension-formatter": "schemas/extensions/formatter.schema.json",
|
|
557
|
+
"extension-hook": "schemas/extensions/hook.schema.json",
|
|
558
|
+
"frontmatter-base": "schemas/frontmatter/base.schema.json"
|
|
559
|
+
};
|
|
560
|
+
var SUPPORTING_SCHEMAS = [
|
|
561
|
+
"schemas/extensions/base.schema.json",
|
|
562
|
+
"schemas/frontmatter/base.schema.json",
|
|
563
|
+
"schemas/summaries/security-scanner.schema.json",
|
|
564
|
+
"schemas/view-slots.schema.json",
|
|
565
|
+
"schemas/input-types.schema.json"
|
|
566
|
+
];
|
|
567
|
+
var cachedValidators = null;
|
|
568
|
+
function loadSchemaValidators() {
|
|
569
|
+
if (cachedValidators !== null) return cachedValidators;
|
|
570
|
+
cachedValidators = buildSchemaValidators();
|
|
571
|
+
return cachedValidators;
|
|
572
|
+
}
|
|
573
|
+
function buildSchemaValidators() {
|
|
574
|
+
const specRoot = resolveSpecRoot2();
|
|
575
|
+
const ajv = new Ajv20204({
|
|
576
|
+
strict: false,
|
|
577
|
+
allErrors: true,
|
|
578
|
+
allowUnionTypes: true
|
|
579
|
+
});
|
|
580
|
+
applyAjvFormats(ajv);
|
|
581
|
+
for (const rel of SUPPORTING_SCHEMAS) {
|
|
582
|
+
const file = resolve4(specRoot, rel);
|
|
583
|
+
if (!existsSyncSafe(file)) continue;
|
|
584
|
+
const schema = JSON.parse(readFileSync4(file, "utf8"));
|
|
585
|
+
ajv.addSchema(schema);
|
|
586
|
+
}
|
|
587
|
+
const validators = /* @__PURE__ */ new Map();
|
|
588
|
+
for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
|
|
589
|
+
const file = resolve4(specRoot, rel);
|
|
590
|
+
const schema = JSON.parse(readFileSync4(file, "utf8"));
|
|
591
|
+
const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
|
|
592
|
+
validators.set(name, byId ?? ajv.compile(schema));
|
|
593
|
+
}
|
|
594
|
+
const extensionByKind = {
|
|
595
|
+
provider: "extension-provider",
|
|
596
|
+
extractor: "extension-extractor",
|
|
597
|
+
analyzer: "extension-analyzer",
|
|
598
|
+
action: "extension-action",
|
|
599
|
+
formatter: "extension-formatter",
|
|
600
|
+
hook: "extension-hook"
|
|
601
|
+
};
|
|
602
|
+
const pluginManifestValidator = ajv.compile({
|
|
603
|
+
$ref: "https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
|
|
604
|
+
});
|
|
605
|
+
const contributionValidators = /* @__PURE__ */ new Map();
|
|
606
|
+
const VIEW_SLOTS_ID = "https://skill-map.dev/spec/v0/view-slots.schema.json";
|
|
607
|
+
const KNOWN_SLOTS = /* @__PURE__ */ new Set([
|
|
608
|
+
"card.title.right",
|
|
609
|
+
"card.subtitle.left",
|
|
610
|
+
"card.footer.left.counter",
|
|
611
|
+
"card.footer.right",
|
|
612
|
+
"graph.node.alert",
|
|
613
|
+
"inspector.header.badge.counter",
|
|
614
|
+
"inspector.header.badge.tag",
|
|
615
|
+
"inspector.body.panel.breakdown",
|
|
616
|
+
"inspector.body.panel.records",
|
|
617
|
+
"inspector.body.panel.tree",
|
|
618
|
+
"inspector.body.panel.key-values",
|
|
619
|
+
"inspector.body.panel.link-list",
|
|
620
|
+
"inspector.body.panel.markdown",
|
|
621
|
+
"topbar.actions.indicator"
|
|
622
|
+
]);
|
|
623
|
+
function getContributionValidator(slot) {
|
|
624
|
+
if (!KNOWN_SLOTS.has(slot)) return null;
|
|
625
|
+
const existing = contributionValidators.get(slot);
|
|
626
|
+
if (existing) return existing;
|
|
627
|
+
const ref = `${VIEW_SLOTS_ID}#/$defs/payloads/${slot}`;
|
|
628
|
+
let compiled;
|
|
629
|
+
try {
|
|
630
|
+
compiled = ajv.compile({ $ref: ref });
|
|
631
|
+
} catch {
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
contributionValidators.set(slot, compiled);
|
|
635
|
+
return compiled;
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
getValidator(name) {
|
|
639
|
+
const v = validators.get(name);
|
|
640
|
+
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
641
|
+
return v;
|
|
642
|
+
},
|
|
643
|
+
validatorForExtension(kind) {
|
|
644
|
+
return validators.get(extensionByKind[kind]);
|
|
645
|
+
},
|
|
646
|
+
validate(name, data) {
|
|
647
|
+
const v = validators.get(name);
|
|
648
|
+
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
649
|
+
if (v(data)) return { ok: true, data };
|
|
650
|
+
const errors = (v.errors ?? []).map(formatError).join("; ");
|
|
651
|
+
return { ok: false, errors };
|
|
652
|
+
},
|
|
653
|
+
validatePluginManifest(data) {
|
|
654
|
+
if (pluginManifestValidator(data)) return { ok: true, data };
|
|
655
|
+
const errors = (pluginManifestValidator.errors ?? []).map(formatError).join("; ");
|
|
656
|
+
return { ok: false, errors };
|
|
657
|
+
},
|
|
658
|
+
validateContributionPayload(slot, payload) {
|
|
659
|
+
const validator = getContributionValidator(slot);
|
|
660
|
+
if (!validator) {
|
|
661
|
+
return { ok: false, errors: "unknown-slot" };
|
|
662
|
+
}
|
|
663
|
+
if (validator(payload)) return { ok: true };
|
|
664
|
+
const errors = (validator.errors ?? []).map(formatError).join("; ");
|
|
665
|
+
return { ok: false, errors };
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
538
669
|
function buildProviderFrontmatterValidator(providers) {
|
|
539
670
|
const specRoot = resolveSpecRoot2();
|
|
540
671
|
const ajv = new Ajv20204({
|
|
@@ -592,6 +723,14 @@ function resolveSpecRoot2() {
|
|
|
592
723
|
);
|
|
593
724
|
}
|
|
594
725
|
}
|
|
726
|
+
function existsSyncSafe(path) {
|
|
727
|
+
try {
|
|
728
|
+
readFileSync4(path, "utf8");
|
|
729
|
+
return true;
|
|
730
|
+
} catch {
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
595
734
|
|
|
596
735
|
// kernel/i18n/orchestrator.texts.ts
|
|
597
736
|
var ORCHESTRATOR_TEXTS = {
|
|
@@ -600,7 +739,9 @@ var ORCHESTRATOR_TEXTS = {
|
|
|
600
739
|
frontmatterMalformedByteOrderMark: "Frontmatter fence in {{path}} is preceded by a UTF-8 byte-order mark (BOM); the file was scanned as body-only. Re-save the file as UTF-8 without BOM. The metadata block was silently lost.",
|
|
601
740
|
frontmatterMalformedMissingClose: "Frontmatter in {{path}} opens with `---` but never closes \u2014 no matching `---` line at column 0 was found. The file was scanned as body-only and every metadata field was silently lost. Add a closing `---` line below the metadata block.",
|
|
602
741
|
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
603
|
-
extensionErrorIssueInvalidSeverity: `Rule "{{
|
|
742
|
+
extensionErrorIssueInvalidSeverity: `Rule "{{analyzerId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
743
|
+
extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
|
|
744
|
+
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{slot}}" schema: {{errors}}. Contribution dropped.',
|
|
604
745
|
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
605
746
|
runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
|
|
606
747
|
};
|
|
@@ -667,7 +808,7 @@ function readDefaultsFromDisk() {
|
|
|
667
808
|
return "";
|
|
668
809
|
}
|
|
669
810
|
|
|
670
|
-
//
|
|
811
|
+
// built-in-plugins/parsers/frontmatter-yaml/index.ts
|
|
671
812
|
import yaml3 from "js-yaml";
|
|
672
813
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
673
814
|
var FORBIDDEN_FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
@@ -693,7 +834,7 @@ var frontmatterYamlParser = {
|
|
|
693
834
|
}
|
|
694
835
|
};
|
|
695
836
|
|
|
696
|
-
//
|
|
837
|
+
// built-in-plugins/parsers/plain/index.ts
|
|
697
838
|
var plainParser = {
|
|
698
839
|
id: "plain",
|
|
699
840
|
parse(raw, _path) {
|
|
@@ -794,6 +935,85 @@ function resolveProviderWalk(provider) {
|
|
|
794
935
|
};
|
|
795
936
|
}
|
|
796
937
|
|
|
938
|
+
// kernel/extensions/hook-dispatcher.ts
|
|
939
|
+
function makeHookDispatcher(hooks, emitter) {
|
|
940
|
+
if (hooks.length === 0) {
|
|
941
|
+
return { dispatch: async () => {
|
|
942
|
+
} };
|
|
943
|
+
}
|
|
944
|
+
const byTrigger = /* @__PURE__ */ new Map();
|
|
945
|
+
for (const hook of hooks) {
|
|
946
|
+
if (hook.mode === "probabilistic") {
|
|
947
|
+
const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
|
|
948
|
+
log.warn(
|
|
949
|
+
`Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
|
|
950
|
+
{ hookId: qualifiedId, mode: "probabilistic" }
|
|
951
|
+
);
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
for (const trig of hook.triggers) {
|
|
955
|
+
const bucket = byTrigger.get(trig);
|
|
956
|
+
if (bucket) bucket.push(hook);
|
|
957
|
+
else byTrigger.set(trig, [hook]);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return {
|
|
961
|
+
async dispatch(trigger, event) {
|
|
962
|
+
const subs = byTrigger.get(trigger);
|
|
963
|
+
if (!subs || subs.length === 0) return;
|
|
964
|
+
for (const hook of subs) {
|
|
965
|
+
if (!matchesFilter(hook, event)) continue;
|
|
966
|
+
const ctx = buildHookContext(hook, trigger, event);
|
|
967
|
+
try {
|
|
968
|
+
await hook.on(ctx);
|
|
969
|
+
} catch (err) {
|
|
970
|
+
const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
|
|
971
|
+
const message = formatErrorMessage(err);
|
|
972
|
+
emitter.emit(
|
|
973
|
+
makeEvent("extension.error", {
|
|
974
|
+
kind: "hook-error",
|
|
975
|
+
extensionId: qualifiedId,
|
|
976
|
+
trigger,
|
|
977
|
+
message
|
|
978
|
+
})
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function makeEvent(type, data) {
|
|
986
|
+
return { type, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
987
|
+
}
|
|
988
|
+
function matchesFilter(hook, event) {
|
|
989
|
+
if (!hook.filter) return true;
|
|
990
|
+
const data = event.data ?? {};
|
|
991
|
+
for (const [key, expected] of Object.entries(hook.filter)) {
|
|
992
|
+
if (data[key] !== expected) return false;
|
|
993
|
+
}
|
|
994
|
+
return true;
|
|
995
|
+
}
|
|
996
|
+
function buildHookContext(_hook, trigger, event) {
|
|
997
|
+
const data = event.data ?? {};
|
|
998
|
+
const ctx = {
|
|
999
|
+
event: {
|
|
1000
|
+
type: trigger,
|
|
1001
|
+
timestamp: event.timestamp,
|
|
1002
|
+
...event.runId !== void 0 ? { runId: event.runId } : {},
|
|
1003
|
+
...event.jobId !== void 0 ? { jobId: event.jobId } : {},
|
|
1004
|
+
data: event.data
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
if (typeof data["extractorId"] === "string") ctx.extractorId = data["extractorId"];
|
|
1008
|
+
if (typeof data["analyzerId"] === "string") ctx.analyzerId = data["analyzerId"];
|
|
1009
|
+
if (typeof data["actionId"] === "string") ctx.actionId = data["actionId"];
|
|
1010
|
+
if (data["node"] && typeof data["node"] === "object") {
|
|
1011
|
+
ctx.node = data["node"];
|
|
1012
|
+
}
|
|
1013
|
+
if (data["jobResult"] !== void 0) ctx.jobResult = data["jobResult"];
|
|
1014
|
+
return ctx;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
797
1017
|
// kernel/orchestrator.ts
|
|
798
1018
|
var SCANNED_BY = {
|
|
799
1019
|
name: "skill-map",
|
|
@@ -819,7 +1039,7 @@ async function runScanInternal(_kernel, options) {
|
|
|
819
1039
|
const start = Date.now();
|
|
820
1040
|
const scannedAt = start;
|
|
821
1041
|
const emitter = options.emitter ?? new InMemoryProgressEmitter();
|
|
822
|
-
const exts = options.extensions ?? { providers: [], extractors: [],
|
|
1042
|
+
const exts = options.extensions ?? { providers: [], extractors: [], analyzers: [] };
|
|
823
1043
|
const hookDispatcher = makeHookDispatcher(exts.hooks ?? [], emitter);
|
|
824
1044
|
const tokenize = options.tokenize !== false;
|
|
825
1045
|
const scope = options.scope ?? "project";
|
|
@@ -856,16 +1076,28 @@ async function runScanInternal(_kernel, options) {
|
|
|
856
1076
|
emitter.emit(evt);
|
|
857
1077
|
await hookDispatcher.dispatch("extractor.completed", evt);
|
|
858
1078
|
}
|
|
859
|
-
const
|
|
860
|
-
exts.
|
|
1079
|
+
const analyzerResult = await runAnalyzers(
|
|
1080
|
+
exts.analyzers,
|
|
861
1081
|
walked.nodes,
|
|
862
1082
|
walked.internalLinks,
|
|
863
1083
|
walked.orphanSidecars,
|
|
864
1084
|
walked.sidecarRoots,
|
|
865
1085
|
options.annotationContributions ?? [],
|
|
1086
|
+
options.viewContributions ?? [],
|
|
1087
|
+
options.orphanJobFiles ?? [],
|
|
1088
|
+
options.referenceablePaths,
|
|
1089
|
+
options.cwd,
|
|
866
1090
|
emitter,
|
|
867
1091
|
hookDispatcher
|
|
868
1092
|
);
|
|
1093
|
+
const issues = analyzerResult.issues;
|
|
1094
|
+
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
1095
|
+
for (const analyzer of exts.analyzers ?? []) {
|
|
1096
|
+
if (analyzer.viewContributions === void 0) continue;
|
|
1097
|
+
for (const node of walked.nodes) {
|
|
1098
|
+
walked.freshlyRunTuples.add(`${analyzer.pluginId}/${analyzer.id}/${node.path}`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
869
1101
|
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
870
1102
|
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
|
|
871
1103
|
const stats = {
|
|
@@ -899,7 +1131,9 @@ async function runScanInternal(_kernel, options) {
|
|
|
899
1131
|
},
|
|
900
1132
|
renameOps,
|
|
901
1133
|
extractorRuns: walked.extractorRuns,
|
|
902
|
-
enrichments: walked.enrichments
|
|
1134
|
+
enrichments: walked.enrichments,
|
|
1135
|
+
contributions: walked.contributions,
|
|
1136
|
+
freshlyRunTuples: walked.freshlyRunTuples
|
|
903
1137
|
};
|
|
904
1138
|
}
|
|
905
1139
|
function validateRoots(roots) {
|
|
@@ -931,7 +1165,7 @@ function indexPriorSnapshot(prior) {
|
|
|
931
1165
|
else priorLinksByOriginating.set(key, [link]);
|
|
932
1166
|
}
|
|
933
1167
|
for (const issue of prior.issues) {
|
|
934
|
-
if (issue.
|
|
1168
|
+
if (issue.analyzerId !== "frontmatter-invalid" && issue.analyzerId !== "frontmatter-malformed") continue;
|
|
935
1169
|
if (issue.nodeIds.length !== 1) continue;
|
|
936
1170
|
const path = issue.nodeIds[0];
|
|
937
1171
|
const list = priorFrontmatterIssuesByNode.get(path);
|
|
@@ -944,9 +1178,10 @@ async function runExtractorsForNode(opts) {
|
|
|
944
1178
|
const internalLinks = [];
|
|
945
1179
|
const externalLinks = [];
|
|
946
1180
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
1181
|
+
const contributions = [];
|
|
1182
|
+
const validators = loadSchemaValidators();
|
|
947
1183
|
for (const extractor of opts.extractors) {
|
|
948
1184
|
const qualifiedId = qualifiedExtensionId(extractor.pluginId, extractor.id);
|
|
949
|
-
const isProb = extractor.mode === "probabilistic";
|
|
950
1185
|
const emitLink = (link) => {
|
|
951
1186
|
const validated = validateLink(extractor, link, opts.emitter);
|
|
952
1187
|
if (!validated) return;
|
|
@@ -966,9 +1201,54 @@ async function runExtractorsForNode(opts) {
|
|
|
966
1201
|
bodyHashAtEnrichment: opts.bodyHash,
|
|
967
1202
|
value: { ...partial },
|
|
968
1203
|
enrichedAt: Date.now(),
|
|
969
|
-
|
|
1204
|
+
// Extractors are deterministic-only; `is_probabilistic` is
|
|
1205
|
+
// reserved on the row for future Action-issued enrichments.
|
|
1206
|
+
isProbabilistic: false
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
const declaredContributions = readDeclaredContributions(extractor);
|
|
1211
|
+
const emitContribution = (contributionId, payload) => {
|
|
1212
|
+
const declared = declaredContributions.get(contributionId);
|
|
1213
|
+
if (!declared) {
|
|
1214
|
+
emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
|
|
1215
|
+
phase: "emitContribution",
|
|
1216
|
+
contributionId,
|
|
1217
|
+
reason: "unknown-contribution-id",
|
|
1218
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
|
|
1219
|
+
extractorId: qualifiedId,
|
|
1220
|
+
contributionId,
|
|
1221
|
+
nodePath: opts.node.path
|
|
1222
|
+
})
|
|
1223
|
+
});
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
1227
|
+
if (!result.ok) {
|
|
1228
|
+
emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
|
|
1229
|
+
phase: "emitContribution",
|
|
1230
|
+
contributionId,
|
|
1231
|
+
slot: declared.slot,
|
|
1232
|
+
reason: result.errors,
|
|
1233
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
1234
|
+
extractorId: qualifiedId,
|
|
1235
|
+
contributionId,
|
|
1236
|
+
nodePath: opts.node.path,
|
|
1237
|
+
slot: declared.slot,
|
|
1238
|
+
errors: result.errors
|
|
1239
|
+
})
|
|
970
1240
|
});
|
|
1241
|
+
return;
|
|
971
1242
|
}
|
|
1243
|
+
contributions.push({
|
|
1244
|
+
pluginId: extractor.pluginId,
|
|
1245
|
+
extensionId: extractor.id,
|
|
1246
|
+
nodePath: opts.node.path,
|
|
1247
|
+
contributionId,
|
|
1248
|
+
slot: declared.slot,
|
|
1249
|
+
payload,
|
|
1250
|
+
emittedAt: Date.now()
|
|
1251
|
+
});
|
|
972
1252
|
};
|
|
973
1253
|
const store = opts.pluginStores?.get(extractor.pluginId);
|
|
974
1254
|
const ctx = buildExtractorContext(
|
|
@@ -978,6 +1258,7 @@ async function runExtractorsForNode(opts) {
|
|
|
978
1258
|
opts.frontmatter,
|
|
979
1259
|
emitLink,
|
|
980
1260
|
enrichNode,
|
|
1261
|
+
emitContribution,
|
|
981
1262
|
store
|
|
982
1263
|
);
|
|
983
1264
|
await extractor.extract(ctx);
|
|
@@ -985,9 +1266,32 @@ async function runExtractorsForNode(opts) {
|
|
|
985
1266
|
return {
|
|
986
1267
|
internalLinks,
|
|
987
1268
|
externalLinks,
|
|
988
|
-
enrichments: Array.from(enrichmentBuffer.values())
|
|
1269
|
+
enrichments: Array.from(enrichmentBuffer.values()),
|
|
1270
|
+
contributions
|
|
989
1271
|
};
|
|
990
1272
|
}
|
|
1273
|
+
function readDeclaredContributions(extension) {
|
|
1274
|
+
const out = /* @__PURE__ */ new Map();
|
|
1275
|
+
const raw = extension.viewContributions;
|
|
1276
|
+
if (typeof raw !== "object" || raw === null) return out;
|
|
1277
|
+
for (const [id, value] of Object.entries(raw)) {
|
|
1278
|
+
if (typeof value !== "object" || value === null) continue;
|
|
1279
|
+
const slot = value.slot;
|
|
1280
|
+
if (typeof slot !== "string") continue;
|
|
1281
|
+
out.set(id, { slot });
|
|
1282
|
+
}
|
|
1283
|
+
return out;
|
|
1284
|
+
}
|
|
1285
|
+
function emitExtensionError(emitter, qualifiedId, nodePath, data) {
|
|
1286
|
+
emitter.emit(
|
|
1287
|
+
makeEvent("extension.error", {
|
|
1288
|
+
kind: "contribution-rejected",
|
|
1289
|
+
extensionId: qualifiedId,
|
|
1290
|
+
nodePath,
|
|
1291
|
+
...data
|
|
1292
|
+
})
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
991
1295
|
function computeCacheDecision(opts) {
|
|
992
1296
|
const applicableExtractors = opts.extractors.filter(
|
|
993
1297
|
(ex) => ex.applicableKinds === void 0 || ex.applicableKinds.includes(opts.kind)
|
|
@@ -1110,6 +1414,8 @@ async function walkAndExtract(opts) {
|
|
|
1110
1414
|
const cachedPaths = /* @__PURE__ */ new Set();
|
|
1111
1415
|
const frontmatterIssues = [];
|
|
1112
1416
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
1417
|
+
const contributionsBuffer = [];
|
|
1418
|
+
const freshlyRunTuples = /* @__PURE__ */ new Set();
|
|
1113
1419
|
const extractorRuns = [];
|
|
1114
1420
|
const sidecarRoots = /* @__PURE__ */ new Map();
|
|
1115
1421
|
let filesWalked = 0;
|
|
@@ -1122,9 +1428,11 @@ async function walkAndExtract(opts) {
|
|
|
1122
1428
|
if (list) list.push(qualified);
|
|
1123
1429
|
else shortIdToQualified.set(ex.id, [qualified]);
|
|
1124
1430
|
}
|
|
1431
|
+
const claimedPaths = /* @__PURE__ */ new Set();
|
|
1125
1432
|
for (const provider of providers) {
|
|
1126
1433
|
for await (const raw of resolveProviderWalk(provider)(roots, walkOptions)) {
|
|
1127
1434
|
filesWalked += 1;
|
|
1435
|
+
if (claimedPaths.has(raw.path)) continue;
|
|
1128
1436
|
const bodyHash = sha256(raw.body);
|
|
1129
1437
|
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
1130
1438
|
const priorNode = priorNodesByPath.get(raw.path);
|
|
@@ -1133,6 +1441,7 @@ async function walkAndExtract(opts) {
|
|
|
1133
1441
|
if (kind === null) {
|
|
1134
1442
|
continue;
|
|
1135
1443
|
}
|
|
1444
|
+
claimedPaths.add(raw.path);
|
|
1136
1445
|
index += 1;
|
|
1137
1446
|
const cacheDecision = computeCacheDecision({
|
|
1138
1447
|
extractors,
|
|
@@ -1160,8 +1469,6 @@ async function walkAndExtract(opts) {
|
|
|
1160
1469
|
priorLinksByOriginating,
|
|
1161
1470
|
priorFrontmatterIssuesByNode
|
|
1162
1471
|
});
|
|
1163
|
-
reused.node.stability = null;
|
|
1164
|
-
reused.node.version = null;
|
|
1165
1472
|
const reusedSidecarIssues = resolveAndApplySidecar(
|
|
1166
1473
|
reused.node,
|
|
1167
1474
|
raw.path,
|
|
@@ -1227,6 +1534,9 @@ async function walkAndExtract(opts) {
|
|
|
1227
1534
|
...partialCacheHit ? { partialCache: true } : {}
|
|
1228
1535
|
}));
|
|
1229
1536
|
const extractorsToRun = partialCacheHit ? missingExtractors : applicableExtractors;
|
|
1537
|
+
for (const ex of extractorsToRun) {
|
|
1538
|
+
freshlyRunTuples.add(`${ex.pluginId}/${ex.id}/${node.path}`);
|
|
1539
|
+
}
|
|
1230
1540
|
const extractResult = await runExtractorsForNode({
|
|
1231
1541
|
extractors: extractorsToRun,
|
|
1232
1542
|
node,
|
|
@@ -1241,6 +1551,7 @@ async function walkAndExtract(opts) {
|
|
|
1241
1551
|
for (const enr of extractResult.enrichments) {
|
|
1242
1552
|
enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
1243
1553
|
}
|
|
1554
|
+
for (const c of extractResult.contributions) contributionsBuffer.push(c);
|
|
1244
1555
|
const ranAt = Date.now();
|
|
1245
1556
|
for (const ex of applicableExtractors) {
|
|
1246
1557
|
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
@@ -1263,6 +1574,8 @@ async function walkAndExtract(opts) {
|
|
|
1263
1574
|
filesWalked,
|
|
1264
1575
|
enrichments: [...enrichmentBuffer.values()],
|
|
1265
1576
|
extractorRuns,
|
|
1577
|
+
contributions: contributionsBuffer,
|
|
1578
|
+
freshlyRunTuples,
|
|
1266
1579
|
orphanSidecars,
|
|
1267
1580
|
sidecarRoots
|
|
1268
1581
|
};
|
|
@@ -1293,30 +1606,80 @@ function reuseCachedLink(link, shortIdToQualified, cachedQualifiedIds, applicabl
|
|
|
1293
1606
|
if (obsoleteSources.length === 0) return link;
|
|
1294
1607
|
return { ...link, sources: cachedSources };
|
|
1295
1608
|
}
|
|
1296
|
-
async function
|
|
1609
|
+
async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, orphanJobFiles, referenceablePaths, cwd, emitter, hookDispatcher) {
|
|
1297
1610
|
const issues = [];
|
|
1298
|
-
const
|
|
1611
|
+
const contributions = [];
|
|
1612
|
+
const validators = loadSchemaValidators();
|
|
1613
|
+
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
1299
1614
|
relativePath: o.relativePath,
|
|
1300
1615
|
expectedMdPath: o.expectedMdPath
|
|
1301
1616
|
}));
|
|
1302
|
-
for (const
|
|
1303
|
-
const
|
|
1617
|
+
for (const analyzer of analyzers) {
|
|
1618
|
+
const qualifiedId = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
1619
|
+
const declaredContributions = readDeclaredContributions(analyzer);
|
|
1620
|
+
const emitContribution = (nodePath, contributionId, payload) => {
|
|
1621
|
+
const declared = declaredContributions.get(contributionId);
|
|
1622
|
+
if (!declared) {
|
|
1623
|
+
emitExtensionError(emitter, qualifiedId, nodePath, {
|
|
1624
|
+
phase: "emitContribution",
|
|
1625
|
+
contributionId,
|
|
1626
|
+
reason: "unknown-contribution-id",
|
|
1627
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
|
|
1628
|
+
extractorId: qualifiedId,
|
|
1629
|
+
contributionId,
|
|
1630
|
+
nodePath
|
|
1631
|
+
})
|
|
1632
|
+
});
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const result = validators.validateContributionPayload(declared.slot, payload);
|
|
1636
|
+
if (!result.ok) {
|
|
1637
|
+
emitExtensionError(emitter, qualifiedId, nodePath, {
|
|
1638
|
+
phase: "emitContribution",
|
|
1639
|
+
contributionId,
|
|
1640
|
+
slot: declared.slot,
|
|
1641
|
+
reason: result.errors,
|
|
1642
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
1643
|
+
extractorId: qualifiedId,
|
|
1644
|
+
contributionId,
|
|
1645
|
+
nodePath,
|
|
1646
|
+
slot: declared.slot,
|
|
1647
|
+
errors: result.errors
|
|
1648
|
+
})
|
|
1649
|
+
});
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
contributions.push({
|
|
1653
|
+
pluginId: analyzer.pluginId,
|
|
1654
|
+
extensionId: analyzer.id,
|
|
1655
|
+
nodePath,
|
|
1656
|
+
contributionId,
|
|
1657
|
+
slot: declared.slot,
|
|
1658
|
+
payload,
|
|
1659
|
+
emittedAt: Date.now()
|
|
1660
|
+
});
|
|
1661
|
+
};
|
|
1662
|
+
const emitted = await analyzer.evaluate({
|
|
1304
1663
|
nodes,
|
|
1305
1664
|
links: internalLinks,
|
|
1306
|
-
orphanSidecars:
|
|
1665
|
+
orphanSidecars: analyzerOrphans,
|
|
1307
1666
|
sidecarRoots,
|
|
1308
|
-
annotationContributions
|
|
1667
|
+
annotationContributions,
|
|
1668
|
+
viewContributions,
|
|
1669
|
+
orphanJobFiles,
|
|
1670
|
+
...referenceablePaths ? { referenceablePaths } : {},
|
|
1671
|
+
...cwd ? { cwd } : {},
|
|
1672
|
+
emitContribution
|
|
1309
1673
|
});
|
|
1310
1674
|
for (const issue of emitted) {
|
|
1311
|
-
const validated = validateIssue(
|
|
1675
|
+
const validated = validateIssue(analyzer, issue, emitter);
|
|
1312
1676
|
if (validated) issues.push(validated);
|
|
1313
1677
|
}
|
|
1314
|
-
const
|
|
1315
|
-
const evt = makeEvent("rule.completed", { ruleId });
|
|
1678
|
+
const evt = makeEvent("analyzer.completed", { analyzerId: qualifiedId });
|
|
1316
1679
|
emitter.emit(evt);
|
|
1317
|
-
await hookDispatcher.dispatch("
|
|
1680
|
+
await hookDispatcher.dispatch("analyzer.completed", evt);
|
|
1318
1681
|
}
|
|
1319
|
-
return issues;
|
|
1682
|
+
return { issues, contributions };
|
|
1320
1683
|
}
|
|
1321
1684
|
function originatingNodeOf(link, priorNodePaths) {
|
|
1322
1685
|
if (link.kind === "supersedes" && !priorNodePaths.has(link.source)) {
|
|
@@ -1370,7 +1733,7 @@ function claimSingletonRenames(opts) {
|
|
|
1370
1733
|
const fromPath = remaining[0];
|
|
1371
1734
|
ops.push({ from: fromPath, to: toPath, confidence: "medium" });
|
|
1372
1735
|
opts.issues.push({
|
|
1373
|
-
|
|
1736
|
+
analyzerId: "auto-rename-medium",
|
|
1374
1737
|
severity: "warn",
|
|
1375
1738
|
nodeIds: [toPath],
|
|
1376
1739
|
message: `Auto-rename (medium confidence): ${fromPath} \u2192 ${toPath}`,
|
|
@@ -1390,7 +1753,7 @@ function flagAmbiguousRenames(opts) {
|
|
|
1390
1753
|
const remaining = candidates.filter((p) => !opts.claimedDeleted.has(p));
|
|
1391
1754
|
if (remaining.length > 1) {
|
|
1392
1755
|
opts.issues.push({
|
|
1393
|
-
|
|
1756
|
+
analyzerId: "auto-rename-ambiguous",
|
|
1394
1757
|
severity: "warn",
|
|
1395
1758
|
nodeIds: [toPath],
|
|
1396
1759
|
message: `Auto-rename ambiguous: ${toPath} matches ${remaining.length} prior frontmatters \u2014 pick one with \`sm orphans undo-rename ${toPath} --from <old.path>\`.`,
|
|
@@ -1403,7 +1766,7 @@ function flagOrphans(opts) {
|
|
|
1403
1766
|
for (const fromPath of opts.deletedPaths) {
|
|
1404
1767
|
if (opts.claimedDeleted.has(fromPath)) continue;
|
|
1405
1768
|
opts.issues.push({
|
|
1406
|
-
|
|
1769
|
+
analyzerId: "orphan",
|
|
1407
1770
|
severity: "info",
|
|
1408
1771
|
nodeIds: [fromPath],
|
|
1409
1772
|
message: `Orphan history: ${fromPath} was deleted; no rename match found.`,
|
|
@@ -1452,83 +1815,6 @@ var EXTERNAL_URL_SCHEME_RE = /^[a-z][a-z0-9+\-.]+:/i;
|
|
|
1452
1815
|
function isExternalUrlLink(link) {
|
|
1453
1816
|
return EXTERNAL_URL_SCHEME_RE.test(link.target);
|
|
1454
1817
|
}
|
|
1455
|
-
function makeEvent(type, data) {
|
|
1456
|
-
return { type, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data };
|
|
1457
|
-
}
|
|
1458
|
-
function makeHookDispatcher(hooks, emitter) {
|
|
1459
|
-
if (hooks.length === 0) {
|
|
1460
|
-
return { dispatch: async () => {
|
|
1461
|
-
} };
|
|
1462
|
-
}
|
|
1463
|
-
const byTrigger = /* @__PURE__ */ new Map();
|
|
1464
|
-
for (const hook of hooks) {
|
|
1465
|
-
if (hook.mode === "probabilistic") {
|
|
1466
|
-
const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
|
|
1467
|
-
log.warn(
|
|
1468
|
-
`Probabilistic hook ${qualifiedId} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
|
|
1469
|
-
{ hookId: qualifiedId, mode: "probabilistic" }
|
|
1470
|
-
);
|
|
1471
|
-
continue;
|
|
1472
|
-
}
|
|
1473
|
-
for (const trig of hook.triggers) {
|
|
1474
|
-
const bucket = byTrigger.get(trig);
|
|
1475
|
-
if (bucket) bucket.push(hook);
|
|
1476
|
-
else byTrigger.set(trig, [hook]);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
return {
|
|
1480
|
-
async dispatch(trigger, event) {
|
|
1481
|
-
const subs = byTrigger.get(trigger);
|
|
1482
|
-
if (!subs || subs.length === 0) return;
|
|
1483
|
-
for (const hook of subs) {
|
|
1484
|
-
if (!matchesFilter(hook, event)) continue;
|
|
1485
|
-
const ctx = buildHookContext(hook, trigger, event);
|
|
1486
|
-
try {
|
|
1487
|
-
await hook.on(ctx);
|
|
1488
|
-
} catch (err) {
|
|
1489
|
-
const qualifiedId = qualifiedExtensionId(hook.pluginId, hook.id);
|
|
1490
|
-
const message = formatErrorMessage(err);
|
|
1491
|
-
emitter.emit(
|
|
1492
|
-
makeEvent("extension.error", {
|
|
1493
|
-
kind: "hook-error",
|
|
1494
|
-
extensionId: qualifiedId,
|
|
1495
|
-
trigger,
|
|
1496
|
-
message
|
|
1497
|
-
})
|
|
1498
|
-
);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
};
|
|
1503
|
-
}
|
|
1504
|
-
function matchesFilter(hook, event) {
|
|
1505
|
-
if (!hook.filter) return true;
|
|
1506
|
-
const data = event.data ?? {};
|
|
1507
|
-
for (const [key, expected] of Object.entries(hook.filter)) {
|
|
1508
|
-
if (data[key] !== expected) return false;
|
|
1509
|
-
}
|
|
1510
|
-
return true;
|
|
1511
|
-
}
|
|
1512
|
-
function buildHookContext(_hook, trigger, event) {
|
|
1513
|
-
const data = event.data ?? {};
|
|
1514
|
-
const ctx = {
|
|
1515
|
-
event: {
|
|
1516
|
-
type: trigger,
|
|
1517
|
-
timestamp: event.timestamp,
|
|
1518
|
-
...event.runId !== void 0 ? { runId: event.runId } : {},
|
|
1519
|
-
...event.jobId !== void 0 ? { jobId: event.jobId } : {},
|
|
1520
|
-
data: event.data
|
|
1521
|
-
}
|
|
1522
|
-
};
|
|
1523
|
-
if (typeof data["extractorId"] === "string") ctx.extractorId = data["extractorId"];
|
|
1524
|
-
if (typeof data["ruleId"] === "string") ctx.ruleId = data["ruleId"];
|
|
1525
|
-
if (typeof data["actionId"] === "string") ctx.actionId = data["actionId"];
|
|
1526
|
-
if (data["node"] && typeof data["node"] === "object") {
|
|
1527
|
-
ctx.node = data["node"];
|
|
1528
|
-
}
|
|
1529
|
-
if (data["jobResult"] !== void 0) ctx.jobResult = data["jobResult"];
|
|
1530
|
-
return ctx;
|
|
1531
|
-
}
|
|
1532
1818
|
function buildNode(args) {
|
|
1533
1819
|
const bytesFrontmatter = Buffer.byteLength(args.frontmatterRaw, "utf8");
|
|
1534
1820
|
const bytesBody = Buffer.byteLength(args.body, "utf8");
|
|
@@ -1546,11 +1832,7 @@ function buildNode(args) {
|
|
|
1546
1832
|
linksOutCount: 0,
|
|
1547
1833
|
linksInCount: 0,
|
|
1548
1834
|
externalRefsCount: 0,
|
|
1549
|
-
frontmatter: args.frontmatter
|
|
1550
|
-
title: pickString(args.frontmatter["name"]),
|
|
1551
|
-
description: pickString(args.frontmatter["description"]),
|
|
1552
|
-
stability: null,
|
|
1553
|
-
version: null
|
|
1835
|
+
frontmatter: args.frontmatter
|
|
1554
1836
|
};
|
|
1555
1837
|
if (args.encoder) {
|
|
1556
1838
|
node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
|
|
@@ -1594,7 +1876,7 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
|
|
|
1594
1876
|
node.sidecar = { present: true, status: null, annotations: null, root: null };
|
|
1595
1877
|
for (const parseIssue of result.issues) {
|
|
1596
1878
|
issues.push({
|
|
1597
|
-
|
|
1879
|
+
analyzerId: "invalid-sidecar",
|
|
1598
1880
|
severity: "warn",
|
|
1599
1881
|
nodeIds: [node.path],
|
|
1600
1882
|
message: parseIssue.message,
|
|
@@ -1604,12 +1886,11 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
|
|
|
1604
1886
|
return issues;
|
|
1605
1887
|
}
|
|
1606
1888
|
const status = computeDriftStatus({
|
|
1607
|
-
storedBodyHash: result.parsed.
|
|
1608
|
-
storedFrontmatterHash: result.parsed.
|
|
1889
|
+
storedBodyHash: result.parsed.identityBodyHash,
|
|
1890
|
+
storedFrontmatterHash: result.parsed.identityFrontmatterHash,
|
|
1609
1891
|
liveBodyHash,
|
|
1610
1892
|
liveFrontmatterHash
|
|
1611
1893
|
});
|
|
1612
|
-
applyAnnotationsOverlay(node, result.parsed);
|
|
1613
1894
|
node.sidecar = {
|
|
1614
1895
|
present: true,
|
|
1615
1896
|
status,
|
|
@@ -1619,18 +1900,6 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
|
|
|
1619
1900
|
sidecarRoots.set(node.path, result.parsed.raw);
|
|
1620
1901
|
return issues;
|
|
1621
1902
|
}
|
|
1622
|
-
function applyAnnotationsOverlay(node, parsed) {
|
|
1623
|
-
const annotations = parsed.annotations;
|
|
1624
|
-
if (annotations === null) return;
|
|
1625
|
-
const stability = annotations["stability"];
|
|
1626
|
-
if (stability === "experimental" || stability === "stable" || stability === "deprecated") {
|
|
1627
|
-
node.stability = stability;
|
|
1628
|
-
}
|
|
1629
|
-
const version = annotations["version"];
|
|
1630
|
-
if (typeof version === "number" && Number.isInteger(version) && version >= 1) {
|
|
1631
|
-
node.version = version;
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
1903
|
function resolveAbsoluteMdPath(relativePath, roots) {
|
|
1635
1904
|
if (isAbsolute2(relativePath)) {
|
|
1636
1905
|
return existsSync6(relativePath) ? relativePath : null;
|
|
@@ -1650,10 +1919,7 @@ function relativePathFromRoots(absolutePath, roots) {
|
|
|
1650
1919
|
}
|
|
1651
1920
|
return absolutePath;
|
|
1652
1921
|
}
|
|
1653
|
-
function
|
|
1654
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1655
|
-
}
|
|
1656
|
-
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, store) {
|
|
1922
|
+
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, store) {
|
|
1657
1923
|
const scope = extractor.scope;
|
|
1658
1924
|
return {
|
|
1659
1925
|
node,
|
|
@@ -1661,6 +1927,7 @@ function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enr
|
|
|
1661
1927
|
frontmatter: scope === "body" ? {} : frontmatter,
|
|
1662
1928
|
emitLink,
|
|
1663
1929
|
enrichNode,
|
|
1930
|
+
emitContribution,
|
|
1664
1931
|
...store !== void 0 ? { store } : {}
|
|
1665
1932
|
};
|
|
1666
1933
|
}
|
|
@@ -1690,7 +1957,7 @@ function validateFrontmatter(providerFrontmatter, provider, kind, frontmatter, p
|
|
|
1690
1957
|
const result = providerFrontmatter.validate(provider, kind, frontmatter);
|
|
1691
1958
|
if (result.ok) return null;
|
|
1692
1959
|
return {
|
|
1693
|
-
|
|
1960
|
+
analyzerId: "frontmatter-invalid",
|
|
1694
1961
|
severity: strict ? "error" : "warn",
|
|
1695
1962
|
nodeIds: [path],
|
|
1696
1963
|
message: tx(ORCHESTRATOR_TEXTS.frontmatterInvalid, { path, kind, errors: result.errors }),
|
|
@@ -1701,7 +1968,7 @@ function detectMalformedFrontmatter(body, path, strict) {
|
|
|
1701
1968
|
const hint = classifyMalformedFrontmatter(body);
|
|
1702
1969
|
if (!hint) return null;
|
|
1703
1970
|
return {
|
|
1704
|
-
|
|
1971
|
+
analyzerId: "frontmatter-malformed",
|
|
1705
1972
|
severity: strict ? "error" : "warn",
|
|
1706
1973
|
nodeIds: [path],
|
|
1707
1974
|
message: malformedMessage(hint, path),
|
|
@@ -1735,25 +2002,25 @@ function malformedMessage(hint, path) {
|
|
|
1735
2002
|
return tx(ORCHESTRATOR_TEXTS.frontmatterMalformedMissingClose, { path });
|
|
1736
2003
|
}
|
|
1737
2004
|
}
|
|
1738
|
-
function validateIssue(
|
|
2005
|
+
function validateIssue(analyzer, issue, emitter) {
|
|
1739
2006
|
const severity = issue.severity;
|
|
1740
2007
|
if (severity !== "error" && severity !== "warn" && severity !== "info") {
|
|
1741
|
-
const qualifiedId = `${
|
|
2008
|
+
const qualifiedId = `${analyzer.pluginId}/${analyzer.id}`;
|
|
1742
2009
|
emitter.emit(
|
|
1743
2010
|
makeEvent("extension.error", {
|
|
1744
2011
|
kind: "issue-invalid-severity",
|
|
1745
2012
|
extensionId: qualifiedId,
|
|
1746
2013
|
severity,
|
|
1747
|
-
issue: {
|
|
2014
|
+
issue: { analyzerId: issue.analyzerId || analyzer.id, message: issue.message, nodeIds: issue.nodeIds },
|
|
1748
2015
|
message: tx(ORCHESTRATOR_TEXTS.extensionErrorIssueInvalidSeverity, {
|
|
1749
|
-
|
|
2016
|
+
analyzerId: qualifiedId,
|
|
1750
2017
|
severity: JSON.stringify(severity)
|
|
1751
2018
|
})
|
|
1752
2019
|
})
|
|
1753
2020
|
);
|
|
1754
2021
|
return null;
|
|
1755
2022
|
}
|
|
1756
|
-
return { ...issue,
|
|
2023
|
+
return { ...issue, analyzerId: issue.analyzerId || analyzer.id };
|
|
1757
2024
|
}
|
|
1758
2025
|
function recomputeLinkCounts(nodes, links) {
|
|
1759
2026
|
const byPath2 = /* @__PURE__ */ new Map();
|
|
@@ -1990,10 +2257,10 @@ function diffIssues(priorIssues, currentIssues) {
|
|
|
1990
2257
|
}
|
|
1991
2258
|
function issueIdentity(issue) {
|
|
1992
2259
|
const ids = [...issue.nodeIds].sort().join(",");
|
|
1993
|
-
return `${issue.
|
|
2260
|
+
return `${issue.analyzerId}\0${ids}\0${issue.message}`;
|
|
1994
2261
|
}
|
|
1995
2262
|
function byIssueSort(a, b) {
|
|
1996
|
-
if (a.
|
|
2263
|
+
if (a.analyzerId !== b.analyzerId) return a.analyzerId.localeCompare(b.analyzerId);
|
|
1997
2264
|
return a.message.localeCompare(b.message);
|
|
1998
2265
|
}
|
|
1999
2266
|
|
|
@@ -2149,6 +2416,7 @@ function parseLogLevel(value) {
|
|
|
2149
2416
|
// kernel/index.ts
|
|
2150
2417
|
function createKernel() {
|
|
2151
2418
|
let annotationKeys = Object.freeze([]);
|
|
2419
|
+
let viewContributions = Object.freeze([]);
|
|
2152
2420
|
return {
|
|
2153
2421
|
registry: new Registry(),
|
|
2154
2422
|
getRegisteredAnnotationKeys() {
|
|
@@ -2156,6 +2424,12 @@ function createKernel() {
|
|
|
2156
2424
|
},
|
|
2157
2425
|
setRegisteredAnnotationKeys(entries) {
|
|
2158
2426
|
annotationKeys = Object.freeze([...entries]);
|
|
2427
|
+
},
|
|
2428
|
+
getRegisteredViewContributions() {
|
|
2429
|
+
return viewContributions;
|
|
2430
|
+
},
|
|
2431
|
+
setRegisteredViewContributions(entries) {
|
|
2432
|
+
viewContributions = Object.freeze([...entries]);
|
|
2159
2433
|
}
|
|
2160
2434
|
};
|
|
2161
2435
|
}
|
|
@@ -2181,6 +2455,8 @@ export {
|
|
|
2181
2455
|
log,
|
|
2182
2456
|
logLevelRank,
|
|
2183
2457
|
makeDedicatedStoreWrapper,
|
|
2458
|
+
makeEvent,
|
|
2459
|
+
makeHookDispatcher,
|
|
2184
2460
|
makeKvStoreWrapper,
|
|
2185
2461
|
makePluginStore,
|
|
2186
2462
|
mergeNodeWithEnrichments,
|