@skill-map/cli 0.18.0 → 0.19.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 +6 -7
- package/dist/cli.js +7011 -4047
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +277 -42
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +566 -68
- package/dist/kernel/index.js +277 -42
- package/dist/kernel/index.js.map +1 -1
- package/dist/migrations/001_initial.sql +122 -12
- package/dist/ui/chunk-7CAK6MVK.js +2638 -0
- package/dist/ui/chunk-BORRASJB.js +247 -0
- package/dist/ui/chunk-CZSS4D6J.js +454 -0
- package/dist/ui/chunk-EQD7AYYJ.js +227 -0
- package/dist/ui/chunk-ETTRVTFV.js +1 -0
- package/dist/ui/chunk-LFIE4SCX.js +965 -0
- package/dist/ui/chunk-OKO3QOH6.js +1 -0
- package/dist/ui/chunk-PMIMYHBM.js +61 -0
- package/dist/ui/chunk-UHFGCO24.js +1 -0
- package/dist/ui/chunk-VHIPW3TH.js +1 -0
- package/dist/ui/chunk-VWAUXWQX.js +237 -0
- package/dist/ui/index.html +10 -2
- package/dist/ui/main-BSYMJKTL.js +1 -0
- package/dist/ui/{styles-CBPFNGXA.css → styles-UAABA7VK.css} +1 -1
- package/migrations/001_initial.sql +122 -12
- 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/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Confidence, DuplicateExtensionError, EXTENSION_KINDS, ExecutionFailureReason, ExecutionKind, ExecutionRecord, ExecutionRunner, ExecutionStatus, ExportQueryError, Extension, ExtensionKind, FilesystemPort, HOOK_TRIGGERS, HistoryStats, HistoryStatsErrorRates, HistoryStatsExecutionsPerPeriod, HistoryStatsPerActionRate, HistoryStatsTokensPerAction, HistoryStatsTopNode, HistoryStatsTotals, IAction, IActionContext, IActionPrecondition, IActionResult, IAnnotationContribution, ICreateFsWatcherOptions, IDedicatedStorePersist, IDedicatedStoreWrapper, IDiscoveredPlugin, IEnrichmentRecord, IExportQuery, IExportSubset, IExtensionBase, IExtractor, IExtractorCallbacks, IExtractorContext, IExtractorRunRecord, IFormatter, IFormatterContext, IFsWatcher, IHook, IHookContext, IIssueRow, IKvStorePersist, IKvStoreWrapper, ILoadedExtension, INodeBundle, INodeChange, INodeCounts, INodeFilter, IPersistOptions, IPersistedEnrichment, IPluginManifest, IPluginStorageSchema, IPluginStore, IProvider, IRawNode, IRegisteredAnnotationKey, IRule, IRuleContext, IRunOptions, IRunResult, IScanDelta, ITransactionalStorage, IWalkOptions, IWatchBatch, IWatchEvent, InMemoryProgressEmitter, Issue, IssueFix, KV_SCHEMA_KEY, Kernel, LOG_LEVELS, Link, LinkKind, LinkLocation, LinkTrigger, LogRecord, LoggerPort, Node, NodeKind, NodeStat, PluginLoaderPort, ProgressEmitterPort, ProgressEvent, Registry, RenameOp, RunScanOptions, RunnerPort, ScanResult, ScanScannedBy, ScanStats, Severity, SilentLogger, Stability, StoragePort, TActionWrite, TExecutionMode, TGranularity, THookFilter, THookTrigger, TLogLevel, TLogMethodLevel, TNodeChangeReason, TPluginLoadStatus, TPluginStorage, TProgressListener, TWatchEventKind, TripleSplit, applyExportQuery, computeScanDelta, configureLogger, createChokidarWatcher, createKernel, detectRenamesAndOrphans, getActiveLogger, isEmptyDelta, isLogLevel, log, logLevelRank, makeDedicatedStoreWrapper, makeKvStoreWrapper, makePluginStore, mergeNodeWithEnrichments, parseExportQuery, parseLogLevel, qualifiedExtensionId, resetLogger, runExtractorsForNode, runScan, runScanWithRenames } from './kernel/index.js';
|
|
1
|
+
export { Confidence, DuplicateExtensionError, EXTENSION_KINDS, ExecutionFailureReason, ExecutionKind, ExecutionRecord, ExecutionRunner, ExecutionStatus, ExportQueryError, Extension, ExtensionKind, FilesystemPort, HOOK_TRIGGERS, HistoryStats, HistoryStatsErrorRates, HistoryStatsExecutionsPerPeriod, HistoryStatsPerActionRate, HistoryStatsTokensPerAction, HistoryStatsTopNode, HistoryStatsTotals, IAction, IActionContext, IActionPrecondition, IActionResult, IAnnotationContribution, ICreateFsWatcherOptions, IDedicatedStorePersist, IDedicatedStoreWrapper, IDiscoveredPlugin, IEnrichmentRecord, IExportQuery, IExportSubset, IExtensionBase, IExtractor, IExtractorCallbacks, IExtractorContext, IExtractorRunRecord, IFormatter, IFormatterContext, IFsWatcher, IHook, IHookContext, IIssueRow, IKvStorePersist, IKvStoreWrapper, ILoadedExtension, INodeBundle, INodeChange, INodeCounts, INodeFilter, IPersistOptions, IPersistedEnrichment, IPluginManifest, IPluginStorageSchema, IPluginStore, IProvider, IRawNode, IRegisteredAnnotationKey, IRegisteredViewContribution, IRule, IRuleContext, IRunOptions, IRunResult, IScanDelta, ISettingDeclaration, ITransactionalStorage, IViewContribution, IWalkOptions, IWatchBatch, IWatchEvent, InMemoryProgressEmitter, Issue, IssueFix, KV_SCHEMA_KEY, Kernel, LOG_LEVELS, Link, LinkKind, LinkLocation, LinkTrigger, LogRecord, LoggerPort, Node, NodeKind, NodeStat, PluginLoaderPort, ProgressEmitterPort, ProgressEvent, Registry, RenameOp, RunScanOptions, RunnerPort, ScanResult, ScanScannedBy, ScanStats, Severity, SilentLogger, Stability, StoragePort, TActionWrite, TContractName, TExecutionMode, TGranularity, THookFilter, THookTrigger, TInputTypeName, TLogLevel, TLogMethodLevel, TNodeChangeReason, TPluginLoadStatus, TPluginStorage, TProgressListener, TSettingValue, TSeverity, TWatchEventKind, TripleSplit, applyExportQuery, computeScanDelta, configureLogger, createChokidarWatcher, createKernel, detectRenamesAndOrphans, getActiveLogger, isEmptyDelta, isLogLevel, log, logLevelRank, makeDedicatedStoreWrapper, makeKvStoreWrapper, makePluginStore, mergeNodeWithEnrichments, parseExportQuery, parseLogLevel, qualifiedExtensionId, resetLogger, runExtractorsForNode, runScan, runScanWithRenames } from './kernel/index.js';
|
package/dist/index.js
CHANGED
|
@@ -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.19.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.19.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
|
},
|
|
@@ -535,6 +535,118 @@ import { readFileSync as readFileSync4 } from "fs";
|
|
|
535
535
|
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
536
536
|
import { createRequire as createRequire4 } from "module";
|
|
537
537
|
import { Ajv2020 as Ajv20204 } from "ajv/dist/2020.js";
|
|
538
|
+
var SCHEMA_FILES = {
|
|
539
|
+
node: "schemas/node.schema.json",
|
|
540
|
+
link: "schemas/link.schema.json",
|
|
541
|
+
issue: "schemas/issue.schema.json",
|
|
542
|
+
"scan-result": "schemas/scan-result.schema.json",
|
|
543
|
+
"execution-record": "schemas/execution-record.schema.json",
|
|
544
|
+
"project-config": "schemas/project-config.schema.json",
|
|
545
|
+
"plugins-registry": "schemas/plugins-registry.schema.json",
|
|
546
|
+
job: "schemas/job.schema.json",
|
|
547
|
+
"report-base": "schemas/report-base.schema.json",
|
|
548
|
+
"conformance-case": "schemas/conformance-case.schema.json",
|
|
549
|
+
"history-stats": "schemas/history-stats.schema.json",
|
|
550
|
+
"extension-provider": "schemas/extensions/provider.schema.json",
|
|
551
|
+
"extension-extractor": "schemas/extensions/extractor.schema.json",
|
|
552
|
+
"extension-rule": "schemas/extensions/rule.schema.json",
|
|
553
|
+
"extension-action": "schemas/extensions/action.schema.json",
|
|
554
|
+
"extension-formatter": "schemas/extensions/formatter.schema.json",
|
|
555
|
+
"extension-hook": "schemas/extensions/hook.schema.json",
|
|
556
|
+
"frontmatter-base": "schemas/frontmatter/base.schema.json"
|
|
557
|
+
};
|
|
558
|
+
var SUPPORTING_SCHEMAS = [
|
|
559
|
+
"schemas/extensions/base.schema.json",
|
|
560
|
+
"schemas/frontmatter/base.schema.json",
|
|
561
|
+
"schemas/summaries/security-scanner.schema.json",
|
|
562
|
+
"schemas/view-contracts.schema.json",
|
|
563
|
+
"schemas/input-types.schema.json"
|
|
564
|
+
];
|
|
565
|
+
var cachedValidators = null;
|
|
566
|
+
function loadSchemaValidators() {
|
|
567
|
+
if (cachedValidators !== null) return cachedValidators;
|
|
568
|
+
cachedValidators = buildSchemaValidators();
|
|
569
|
+
return cachedValidators;
|
|
570
|
+
}
|
|
571
|
+
function buildSchemaValidators() {
|
|
572
|
+
const specRoot = resolveSpecRoot2();
|
|
573
|
+
const ajv = new Ajv20204({
|
|
574
|
+
strict: false,
|
|
575
|
+
allErrors: true,
|
|
576
|
+
allowUnionTypes: true
|
|
577
|
+
});
|
|
578
|
+
applyAjvFormats(ajv);
|
|
579
|
+
for (const rel of SUPPORTING_SCHEMAS) {
|
|
580
|
+
const file = resolve4(specRoot, rel);
|
|
581
|
+
if (!existsSyncSafe(file)) continue;
|
|
582
|
+
const schema = JSON.parse(readFileSync4(file, "utf8"));
|
|
583
|
+
ajv.addSchema(schema);
|
|
584
|
+
}
|
|
585
|
+
const validators = /* @__PURE__ */ new Map();
|
|
586
|
+
for (const [name, rel] of Object.entries(SCHEMA_FILES)) {
|
|
587
|
+
const file = resolve4(specRoot, rel);
|
|
588
|
+
const schema = JSON.parse(readFileSync4(file, "utf8"));
|
|
589
|
+
const byId = typeof schema.$id === "string" ? ajv.getSchema(schema.$id) : void 0;
|
|
590
|
+
validators.set(name, byId ?? ajv.compile(schema));
|
|
591
|
+
}
|
|
592
|
+
const extensionByKind = {
|
|
593
|
+
provider: "extension-provider",
|
|
594
|
+
extractor: "extension-extractor",
|
|
595
|
+
rule: "extension-rule",
|
|
596
|
+
action: "extension-action",
|
|
597
|
+
formatter: "extension-formatter",
|
|
598
|
+
hook: "extension-hook"
|
|
599
|
+
};
|
|
600
|
+
const pluginManifestValidator = ajv.compile({
|
|
601
|
+
$ref: "https://skill-map.dev/spec/v0/plugins-registry.schema.json#/$defs/PluginManifest"
|
|
602
|
+
});
|
|
603
|
+
const contributionValidators = /* @__PURE__ */ new Map();
|
|
604
|
+
const VIEW_CONTRACTS_ID = "https://skill-map.dev/spec/v0/view-contracts.schema.json";
|
|
605
|
+
function getContributionValidator(contract) {
|
|
606
|
+
const existing = contributionValidators.get(contract);
|
|
607
|
+
if (existing) return existing;
|
|
608
|
+
const ref = `${VIEW_CONTRACTS_ID}#/$defs/payloads/${contract}`;
|
|
609
|
+
let compiled;
|
|
610
|
+
try {
|
|
611
|
+
compiled = ajv.compile({ $ref: ref });
|
|
612
|
+
} catch {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
contributionValidators.set(contract, compiled);
|
|
616
|
+
return compiled;
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
getValidator(name) {
|
|
620
|
+
const v = validators.get(name);
|
|
621
|
+
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
622
|
+
return v;
|
|
623
|
+
},
|
|
624
|
+
validatorForExtension(kind) {
|
|
625
|
+
return validators.get(extensionByKind[kind]);
|
|
626
|
+
},
|
|
627
|
+
validate(name, data) {
|
|
628
|
+
const v = validators.get(name);
|
|
629
|
+
if (!v) throw new Error(`Unknown schema: ${name}`);
|
|
630
|
+
if (v(data)) return { ok: true, data };
|
|
631
|
+
const errors = (v.errors ?? []).map(formatError).join("; ");
|
|
632
|
+
return { ok: false, errors };
|
|
633
|
+
},
|
|
634
|
+
validatePluginManifest(data) {
|
|
635
|
+
if (pluginManifestValidator(data)) return { ok: true, data };
|
|
636
|
+
const errors = (pluginManifestValidator.errors ?? []).map(formatError).join("; ");
|
|
637
|
+
return { ok: false, errors };
|
|
638
|
+
},
|
|
639
|
+
validateContributionPayload(contract, payload) {
|
|
640
|
+
const validator = getContributionValidator(contract);
|
|
641
|
+
if (!validator) {
|
|
642
|
+
return { ok: false, errors: "unknown-contract" };
|
|
643
|
+
}
|
|
644
|
+
if (validator(payload)) return { ok: true };
|
|
645
|
+
const errors = (validator.errors ?? []).map(formatError).join("; ");
|
|
646
|
+
return { ok: false, errors };
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
538
650
|
function buildProviderFrontmatterValidator(providers) {
|
|
539
651
|
const specRoot = resolveSpecRoot2();
|
|
540
652
|
const ajv = new Ajv20204({
|
|
@@ -592,6 +704,14 @@ function resolveSpecRoot2() {
|
|
|
592
704
|
);
|
|
593
705
|
}
|
|
594
706
|
}
|
|
707
|
+
function existsSyncSafe(path) {
|
|
708
|
+
try {
|
|
709
|
+
readFileSync4(path, "utf8");
|
|
710
|
+
return true;
|
|
711
|
+
} catch {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
595
715
|
|
|
596
716
|
// kernel/i18n/orchestrator.texts.ts
|
|
597
717
|
var ORCHESTRATOR_TEXTS = {
|
|
@@ -601,6 +721,8 @@ var ORCHESTRATOR_TEXTS = {
|
|
|
601
721
|
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
722
|
extensionErrorLinkKindNotDeclared: 'Extractor "{{extractorId}}" emitted a link of kind "{{linkKind}}" outside its declared `emitsLinkKinds` set [{{declaredKinds}}]. Link dropped.',
|
|
603
723
|
extensionErrorIssueInvalidSeverity: `Rule "{{ruleId}}" emitted an issue with invalid severity {{severity}} (allowed: 'error' | 'warn' | 'info'). Issue dropped.`,
|
|
724
|
+
extensionErrorContributionUnknownId: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}} but did not declare it in its `viewContributions` map. Contribution dropped.',
|
|
725
|
+
extensionErrorContributionPayloadInvalid: 'Extractor "{{extractorId}}" emitted contribution "{{contributionId}}" on {{nodePath}}; payload failed the "{{contract}}" schema: {{errors}}. Contribution dropped.',
|
|
604
726
|
runScanRootEmptyArray: "runScan: roots must contain at least one path (spec requires minItems: 1)",
|
|
605
727
|
runScanRootMissing: "runScan: root path '{{root}}' does not exist or is not a directory"
|
|
606
728
|
};
|
|
@@ -856,16 +978,19 @@ async function runScanInternal(_kernel, options) {
|
|
|
856
978
|
emitter.emit(evt);
|
|
857
979
|
await hookDispatcher.dispatch("extractor.completed", evt);
|
|
858
980
|
}
|
|
859
|
-
const
|
|
981
|
+
const ruleResult = await runRules(
|
|
860
982
|
exts.rules,
|
|
861
983
|
walked.nodes,
|
|
862
984
|
walked.internalLinks,
|
|
863
985
|
walked.orphanSidecars,
|
|
864
986
|
walked.sidecarRoots,
|
|
865
987
|
options.annotationContributions ?? [],
|
|
988
|
+
options.viewContributions ?? [],
|
|
866
989
|
emitter,
|
|
867
990
|
hookDispatcher
|
|
868
991
|
);
|
|
992
|
+
const issues = ruleResult.issues;
|
|
993
|
+
for (const c of ruleResult.contributions) walked.contributions.push(c);
|
|
869
994
|
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
870
995
|
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues) : [];
|
|
871
996
|
const stats = {
|
|
@@ -899,7 +1024,8 @@ async function runScanInternal(_kernel, options) {
|
|
|
899
1024
|
},
|
|
900
1025
|
renameOps,
|
|
901
1026
|
extractorRuns: walked.extractorRuns,
|
|
902
|
-
enrichments: walked.enrichments
|
|
1027
|
+
enrichments: walked.enrichments,
|
|
1028
|
+
contributions: walked.contributions
|
|
903
1029
|
};
|
|
904
1030
|
}
|
|
905
1031
|
function validateRoots(roots) {
|
|
@@ -944,9 +1070,10 @@ async function runExtractorsForNode(opts) {
|
|
|
944
1070
|
const internalLinks = [];
|
|
945
1071
|
const externalLinks = [];
|
|
946
1072
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
1073
|
+
const contributions = [];
|
|
1074
|
+
const validators = loadSchemaValidators();
|
|
947
1075
|
for (const extractor of opts.extractors) {
|
|
948
1076
|
const qualifiedId = qualifiedExtensionId(extractor.pluginId, extractor.id);
|
|
949
|
-
const isProb = extractor.mode === "probabilistic";
|
|
950
1077
|
const emitLink = (link) => {
|
|
951
1078
|
const validated = validateLink(extractor, link, opts.emitter);
|
|
952
1079
|
if (!validated) return;
|
|
@@ -966,10 +1093,55 @@ async function runExtractorsForNode(opts) {
|
|
|
966
1093
|
bodyHashAtEnrichment: opts.bodyHash,
|
|
967
1094
|
value: { ...partial },
|
|
968
1095
|
enrichedAt: Date.now(),
|
|
969
|
-
|
|
1096
|
+
// Extractors are deterministic-only; `is_probabilistic` is
|
|
1097
|
+
// reserved on the row for future Action-issued enrichments.
|
|
1098
|
+
isProbabilistic: false
|
|
970
1099
|
});
|
|
971
1100
|
}
|
|
972
1101
|
};
|
|
1102
|
+
const declaredContributions = readDeclaredContributions(extractor);
|
|
1103
|
+
const emitContribution = (contributionId, payload) => {
|
|
1104
|
+
const declared = declaredContributions.get(contributionId);
|
|
1105
|
+
if (!declared) {
|
|
1106
|
+
emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
|
|
1107
|
+
phase: "emitContribution",
|
|
1108
|
+
contributionId,
|
|
1109
|
+
reason: "unknown-contribution-id",
|
|
1110
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
|
|
1111
|
+
extractorId: qualifiedId,
|
|
1112
|
+
contributionId,
|
|
1113
|
+
nodePath: opts.node.path
|
|
1114
|
+
})
|
|
1115
|
+
});
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
const result = validators.validateContributionPayload(declared.contract, payload);
|
|
1119
|
+
if (!result.ok) {
|
|
1120
|
+
emitExtensionError(opts.emitter, qualifiedId, opts.node.path, {
|
|
1121
|
+
phase: "emitContribution",
|
|
1122
|
+
contributionId,
|
|
1123
|
+
contract: declared.contract,
|
|
1124
|
+
reason: result.errors,
|
|
1125
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
1126
|
+
extractorId: qualifiedId,
|
|
1127
|
+
contributionId,
|
|
1128
|
+
nodePath: opts.node.path,
|
|
1129
|
+
contract: declared.contract,
|
|
1130
|
+
errors: result.errors
|
|
1131
|
+
})
|
|
1132
|
+
});
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
contributions.push({
|
|
1136
|
+
pluginId: extractor.pluginId,
|
|
1137
|
+
extensionId: extractor.id,
|
|
1138
|
+
nodePath: opts.node.path,
|
|
1139
|
+
contributionId,
|
|
1140
|
+
contract: declared.contract,
|
|
1141
|
+
payload,
|
|
1142
|
+
emittedAt: Date.now()
|
|
1143
|
+
});
|
|
1144
|
+
};
|
|
973
1145
|
const store = opts.pluginStores?.get(extractor.pluginId);
|
|
974
1146
|
const ctx = buildExtractorContext(
|
|
975
1147
|
extractor,
|
|
@@ -978,6 +1150,7 @@ async function runExtractorsForNode(opts) {
|
|
|
978
1150
|
opts.frontmatter,
|
|
979
1151
|
emitLink,
|
|
980
1152
|
enrichNode,
|
|
1153
|
+
emitContribution,
|
|
981
1154
|
store
|
|
982
1155
|
);
|
|
983
1156
|
await extractor.extract(ctx);
|
|
@@ -985,9 +1158,32 @@ async function runExtractorsForNode(opts) {
|
|
|
985
1158
|
return {
|
|
986
1159
|
internalLinks,
|
|
987
1160
|
externalLinks,
|
|
988
|
-
enrichments: Array.from(enrichmentBuffer.values())
|
|
1161
|
+
enrichments: Array.from(enrichmentBuffer.values()),
|
|
1162
|
+
contributions
|
|
989
1163
|
};
|
|
990
1164
|
}
|
|
1165
|
+
function readDeclaredContributions(extension) {
|
|
1166
|
+
const out = /* @__PURE__ */ new Map();
|
|
1167
|
+
const raw = extension.viewContributions;
|
|
1168
|
+
if (typeof raw !== "object" || raw === null) return out;
|
|
1169
|
+
for (const [id, value] of Object.entries(raw)) {
|
|
1170
|
+
if (typeof value !== "object" || value === null) continue;
|
|
1171
|
+
const contract = value.contract;
|
|
1172
|
+
if (typeof contract !== "string") continue;
|
|
1173
|
+
out.set(id, { contract });
|
|
1174
|
+
}
|
|
1175
|
+
return out;
|
|
1176
|
+
}
|
|
1177
|
+
function emitExtensionError(emitter, qualifiedId, nodePath, data) {
|
|
1178
|
+
emitter.emit(
|
|
1179
|
+
makeEvent("extension.error", {
|
|
1180
|
+
kind: "contribution-rejected",
|
|
1181
|
+
extensionId: qualifiedId,
|
|
1182
|
+
nodePath,
|
|
1183
|
+
...data
|
|
1184
|
+
})
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
991
1187
|
function computeCacheDecision(opts) {
|
|
992
1188
|
const applicableExtractors = opts.extractors.filter(
|
|
993
1189
|
(ex) => ex.applicableKinds === void 0 || ex.applicableKinds.includes(opts.kind)
|
|
@@ -1110,6 +1306,7 @@ async function walkAndExtract(opts) {
|
|
|
1110
1306
|
const cachedPaths = /* @__PURE__ */ new Set();
|
|
1111
1307
|
const frontmatterIssues = [];
|
|
1112
1308
|
const enrichmentBuffer = /* @__PURE__ */ new Map();
|
|
1309
|
+
const contributionsBuffer = [];
|
|
1113
1310
|
const extractorRuns = [];
|
|
1114
1311
|
const sidecarRoots = /* @__PURE__ */ new Map();
|
|
1115
1312
|
let filesWalked = 0;
|
|
@@ -1122,9 +1319,11 @@ async function walkAndExtract(opts) {
|
|
|
1122
1319
|
if (list) list.push(qualified);
|
|
1123
1320
|
else shortIdToQualified.set(ex.id, [qualified]);
|
|
1124
1321
|
}
|
|
1322
|
+
const claimedPaths = /* @__PURE__ */ new Set();
|
|
1125
1323
|
for (const provider of providers) {
|
|
1126
1324
|
for await (const raw of resolveProviderWalk(provider)(roots, walkOptions)) {
|
|
1127
1325
|
filesWalked += 1;
|
|
1326
|
+
if (claimedPaths.has(raw.path)) continue;
|
|
1128
1327
|
const bodyHash = sha256(raw.body);
|
|
1129
1328
|
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
1130
1329
|
const priorNode = priorNodesByPath.get(raw.path);
|
|
@@ -1133,6 +1332,7 @@ async function walkAndExtract(opts) {
|
|
|
1133
1332
|
if (kind === null) {
|
|
1134
1333
|
continue;
|
|
1135
1334
|
}
|
|
1335
|
+
claimedPaths.add(raw.path);
|
|
1136
1336
|
index += 1;
|
|
1137
1337
|
const cacheDecision = computeCacheDecision({
|
|
1138
1338
|
extractors,
|
|
@@ -1160,8 +1360,6 @@ async function walkAndExtract(opts) {
|
|
|
1160
1360
|
priorLinksByOriginating,
|
|
1161
1361
|
priorFrontmatterIssuesByNode
|
|
1162
1362
|
});
|
|
1163
|
-
reused.node.stability = null;
|
|
1164
|
-
reused.node.version = null;
|
|
1165
1363
|
const reusedSidecarIssues = resolveAndApplySidecar(
|
|
1166
1364
|
reused.node,
|
|
1167
1365
|
raw.path,
|
|
@@ -1241,6 +1439,7 @@ async function walkAndExtract(opts) {
|
|
|
1241
1439
|
for (const enr of extractResult.enrichments) {
|
|
1242
1440
|
enrichmentBuffer.set(`${enr.nodePath}\0${enr.extractorId}`, enr);
|
|
1243
1441
|
}
|
|
1442
|
+
for (const c of extractResult.contributions) contributionsBuffer.push(c);
|
|
1244
1443
|
const ranAt = Date.now();
|
|
1245
1444
|
for (const ex of applicableExtractors) {
|
|
1246
1445
|
const qualified = qualifiedExtensionId(ex.pluginId, ex.id);
|
|
@@ -1263,6 +1462,7 @@ async function walkAndExtract(opts) {
|
|
|
1263
1462
|
filesWalked,
|
|
1264
1463
|
enrichments: [...enrichmentBuffer.values()],
|
|
1265
1464
|
extractorRuns,
|
|
1465
|
+
contributions: contributionsBuffer,
|
|
1266
1466
|
orphanSidecars,
|
|
1267
1467
|
sidecarRoots
|
|
1268
1468
|
};
|
|
@@ -1293,30 +1493,77 @@ function reuseCachedLink(link, shortIdToQualified, cachedQualifiedIds, applicabl
|
|
|
1293
1493
|
if (obsoleteSources.length === 0) return link;
|
|
1294
1494
|
return { ...link, sources: cachedSources };
|
|
1295
1495
|
}
|
|
1296
|
-
async function runRules(rules, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, emitter, hookDispatcher) {
|
|
1496
|
+
async function runRules(rules, nodes, internalLinks, orphanSidecars, sidecarRoots, annotationContributions, viewContributions, emitter, hookDispatcher) {
|
|
1297
1497
|
const issues = [];
|
|
1498
|
+
const contributions = [];
|
|
1499
|
+
const validators = loadSchemaValidators();
|
|
1298
1500
|
const ruleOrphans = orphanSidecars.map((o) => ({
|
|
1299
1501
|
relativePath: o.relativePath,
|
|
1300
1502
|
expectedMdPath: o.expectedMdPath
|
|
1301
1503
|
}));
|
|
1302
1504
|
for (const rule of rules) {
|
|
1505
|
+
const qualifiedId = qualifiedExtensionId(rule.pluginId, rule.id);
|
|
1506
|
+
const declaredContributions = readDeclaredContributions(rule);
|
|
1507
|
+
const emitContribution = (nodePath, contributionId, payload) => {
|
|
1508
|
+
const declared = declaredContributions.get(contributionId);
|
|
1509
|
+
if (!declared) {
|
|
1510
|
+
emitExtensionError(emitter, qualifiedId, nodePath, {
|
|
1511
|
+
phase: "emitContribution",
|
|
1512
|
+
contributionId,
|
|
1513
|
+
reason: "unknown-contribution-id",
|
|
1514
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionUnknownId, {
|
|
1515
|
+
extractorId: qualifiedId,
|
|
1516
|
+
contributionId,
|
|
1517
|
+
nodePath
|
|
1518
|
+
})
|
|
1519
|
+
});
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const result = validators.validateContributionPayload(declared.contract, payload);
|
|
1523
|
+
if (!result.ok) {
|
|
1524
|
+
emitExtensionError(emitter, qualifiedId, nodePath, {
|
|
1525
|
+
phase: "emitContribution",
|
|
1526
|
+
contributionId,
|
|
1527
|
+
contract: declared.contract,
|
|
1528
|
+
reason: result.errors,
|
|
1529
|
+
message: tx(ORCHESTRATOR_TEXTS.extensionErrorContributionPayloadInvalid, {
|
|
1530
|
+
extractorId: qualifiedId,
|
|
1531
|
+
contributionId,
|
|
1532
|
+
nodePath,
|
|
1533
|
+
contract: declared.contract,
|
|
1534
|
+
errors: result.errors
|
|
1535
|
+
})
|
|
1536
|
+
});
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
contributions.push({
|
|
1540
|
+
pluginId: rule.pluginId,
|
|
1541
|
+
extensionId: rule.id,
|
|
1542
|
+
nodePath,
|
|
1543
|
+
contributionId,
|
|
1544
|
+
contract: declared.contract,
|
|
1545
|
+
payload,
|
|
1546
|
+
emittedAt: Date.now()
|
|
1547
|
+
});
|
|
1548
|
+
};
|
|
1303
1549
|
const emitted = await rule.evaluate({
|
|
1304
1550
|
nodes,
|
|
1305
1551
|
links: internalLinks,
|
|
1306
1552
|
orphanSidecars: ruleOrphans,
|
|
1307
1553
|
sidecarRoots,
|
|
1308
|
-
annotationContributions
|
|
1554
|
+
annotationContributions,
|
|
1555
|
+
viewContributions,
|
|
1556
|
+
emitContribution
|
|
1309
1557
|
});
|
|
1310
1558
|
for (const issue of emitted) {
|
|
1311
1559
|
const validated = validateIssue(rule, issue, emitter);
|
|
1312
1560
|
if (validated) issues.push(validated);
|
|
1313
1561
|
}
|
|
1314
|
-
const
|
|
1315
|
-
const evt = makeEvent("rule.completed", { ruleId });
|
|
1562
|
+
const evt = makeEvent("rule.completed", { ruleId: qualifiedId });
|
|
1316
1563
|
emitter.emit(evt);
|
|
1317
1564
|
await hookDispatcher.dispatch("rule.completed", evt);
|
|
1318
1565
|
}
|
|
1319
|
-
return issues;
|
|
1566
|
+
return { issues, contributions };
|
|
1320
1567
|
}
|
|
1321
1568
|
function originatingNodeOf(link, priorNodePaths) {
|
|
1322
1569
|
if (link.kind === "supersedes" && !priorNodePaths.has(link.source)) {
|
|
@@ -1546,11 +1793,7 @@ function buildNode(args) {
|
|
|
1546
1793
|
linksOutCount: 0,
|
|
1547
1794
|
linksInCount: 0,
|
|
1548
1795
|
externalRefsCount: 0,
|
|
1549
|
-
frontmatter: args.frontmatter
|
|
1550
|
-
title: pickString(args.frontmatter["name"]),
|
|
1551
|
-
description: pickString(args.frontmatter["description"]),
|
|
1552
|
-
stability: null,
|
|
1553
|
-
version: null
|
|
1796
|
+
frontmatter: args.frontmatter
|
|
1554
1797
|
};
|
|
1555
1798
|
if (args.encoder) {
|
|
1556
1799
|
node.tokens = countTokens(args.encoder, args.frontmatterRaw, args.body);
|
|
@@ -1604,12 +1847,11 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
|
|
|
1604
1847
|
return issues;
|
|
1605
1848
|
}
|
|
1606
1849
|
const status = computeDriftStatus({
|
|
1607
|
-
storedBodyHash: result.parsed.
|
|
1608
|
-
storedFrontmatterHash: result.parsed.
|
|
1850
|
+
storedBodyHash: result.parsed.identityBodyHash,
|
|
1851
|
+
storedFrontmatterHash: result.parsed.identityFrontmatterHash,
|
|
1609
1852
|
liveBodyHash,
|
|
1610
1853
|
liveFrontmatterHash
|
|
1611
1854
|
});
|
|
1612
|
-
applyAnnotationsOverlay(node, result.parsed);
|
|
1613
1855
|
node.sidecar = {
|
|
1614
1856
|
present: true,
|
|
1615
1857
|
status,
|
|
@@ -1619,18 +1861,6 @@ function resolveAndApplySidecar(node, relativePath, roots, liveBodyHash, liveFro
|
|
|
1619
1861
|
sidecarRoots.set(node.path, result.parsed.raw);
|
|
1620
1862
|
return issues;
|
|
1621
1863
|
}
|
|
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
1864
|
function resolveAbsoluteMdPath(relativePath, roots) {
|
|
1635
1865
|
if (isAbsolute2(relativePath)) {
|
|
1636
1866
|
return existsSync6(relativePath) ? relativePath : null;
|
|
@@ -1650,10 +1880,7 @@ function relativePathFromRoots(absolutePath, roots) {
|
|
|
1650
1880
|
}
|
|
1651
1881
|
return absolutePath;
|
|
1652
1882
|
}
|
|
1653
|
-
function
|
|
1654
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1655
|
-
}
|
|
1656
|
-
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, store) {
|
|
1883
|
+
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, store) {
|
|
1657
1884
|
const scope = extractor.scope;
|
|
1658
1885
|
return {
|
|
1659
1886
|
node,
|
|
@@ -1661,6 +1888,7 @@ function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enr
|
|
|
1661
1888
|
frontmatter: scope === "body" ? {} : frontmatter,
|
|
1662
1889
|
emitLink,
|
|
1663
1890
|
enrichNode,
|
|
1891
|
+
emitContribution,
|
|
1664
1892
|
...store !== void 0 ? { store } : {}
|
|
1665
1893
|
};
|
|
1666
1894
|
}
|
|
@@ -2149,6 +2377,7 @@ function parseLogLevel(value) {
|
|
|
2149
2377
|
// kernel/index.ts
|
|
2150
2378
|
function createKernel() {
|
|
2151
2379
|
let annotationKeys = Object.freeze([]);
|
|
2380
|
+
let viewContributions = Object.freeze([]);
|
|
2152
2381
|
return {
|
|
2153
2382
|
registry: new Registry(),
|
|
2154
2383
|
getRegisteredAnnotationKeys() {
|
|
@@ -2156,6 +2385,12 @@ function createKernel() {
|
|
|
2156
2385
|
},
|
|
2157
2386
|
setRegisteredAnnotationKeys(entries) {
|
|
2158
2387
|
annotationKeys = Object.freeze([...entries]);
|
|
2388
|
+
},
|
|
2389
|
+
getRegisteredViewContributions() {
|
|
2390
|
+
return viewContributions;
|
|
2391
|
+
},
|
|
2392
|
+
setRegisteredViewContributions(entries) {
|
|
2393
|
+
viewContributions = Object.freeze([...entries]);
|
|
2159
2394
|
}
|
|
2160
2395
|
};
|
|
2161
2396
|
}
|