@okfit/engine 0.7.5 → 0.8.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/index.d.ts +15 -1
- package/index.js +2 -1
- package/package.json +3 -3
- package/session/concept.js +34 -0
- package/session/range.js +3 -5
- package/validate/resources.js +25 -9
- package/version.js +1 -1
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Actor, BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, GraphLink, GraphNode, LinkGraph, LoadedBundle, OkfitConfig, OkfitConfigFile, StaleConcept, ValidationReport } from "@okfit/core";
|
|
1
|
+
import { Actor, BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, GraphLink, GraphNode, LinkGraph, LoadedBundle, LoadedConcept, OkfitConfig, OkfitConfigFile, StaleConcept, ValidationReport } from "@okfit/core";
|
|
2
2
|
import { Context, Crypto, DateTime, Effect, FileSystem, Layer, Option, Path, PlatformError, Result, Runtime, Schema } from "effect";
|
|
3
3
|
import { AppDirs, Xdg } from "@effected/xdg";
|
|
4
4
|
import { ConfigReadError } from "@effected/config-file";
|
|
@@ -1428,6 +1428,20 @@ export declare class BundleSession extends BundleSession_base {
|
|
|
1428
1428
|
static readonly make: (options: BundleSessionOptions) => Effect.Effect<BundleSessionShape, never, BundleSessionServices>;
|
|
1429
1429
|
}
|
|
1430
1430
|
//#endregion
|
|
1431
|
+
//#region src/session/concept.d.ts
|
|
1432
|
+
/**
|
|
1433
|
+
* The loaded concept at `absolutePath`, if any. `absolutePath` must already sit under
|
|
1434
|
+
* `bundle.root` (a real resolved path, never re-resolved here: `@okfit/core`'s own
|
|
1435
|
+
* `internal/posixPath.ts` avoids `path.resolve`/`path.relative` on purpose, because both read
|
|
1436
|
+
* `process.cwd()`, and this package holds the same line). Strips `bundle.root` as a plain
|
|
1437
|
+
* string prefix, then applies the same `ConceptId.fromPath` normalisation `withFallbackRange`
|
|
1438
|
+
* uses: `None` for a path outside the bundle, a reserved file (`index.md`, `log.md`), a
|
|
1439
|
+
* non-markdown file, or a markdown file that never decoded into a concept.
|
|
1440
|
+
*
|
|
1441
|
+
* @public
|
|
1442
|
+
*/
|
|
1443
|
+
export declare const conceptFor: (bundle: LoadedBundle, absolutePath: string) => Option.Option<LoadedConcept>;
|
|
1444
|
+
//#endregion
|
|
1431
1445
|
//#region src/session/range.d.ts
|
|
1432
1446
|
/**
|
|
1433
1447
|
* Give a range-less diagnostic somewhere to point (spec 4.3): the concept's
|
package/index.js
CHANGED
|
@@ -21,9 +21,10 @@ import { SkipReason, runSync } from "./sync/run.js";
|
|
|
21
21
|
import { SyncEnvelope, SyncModeEnvelope, syncEnvelope } from "./render/sync.js";
|
|
22
22
|
import { VerifyBatchEnvelope, VerifyEnvelope, verifyBatchEnvelope, verifyEnvelope } from "./render/verify.js";
|
|
23
23
|
import { Now, run } from "./validate/run.js";
|
|
24
|
+
import { conceptFor } from "./session/concept.js";
|
|
24
25
|
import { withFallbackRange } from "./session/range.js";
|
|
25
26
|
import { BundleSession } from "./session/BundleSession.js";
|
|
26
27
|
import { runStale } from "./stale/run.js";
|
|
27
28
|
import { runVerify, runVerifyBatch } from "./verify/run.js";
|
|
28
29
|
|
|
29
|
-
export { BundleSession, CONFIG_RELATIVE_PATH, ConfigMalformedError, ConfigPathNotFoundError, ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, DEFAULT_PROFILE_NAME, DistributionField, DocumentPathError, ENGINE_VERSION, ExternalReferences, GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, InitOverwriteError, JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, Now, OkfitPlatform, OverlayDocuments, ReferenceCheck, ReferenceState, SkipReason, StaleEnvelope, StaleItem, StaleSummary, SyncEnvelope, SyncModeEnvelope, SyncStagedLogError, VerifyBatchEnvelope, VerifyConceptNotFoundError, VerifyEnvelope, VerifySelectionError, VerifyUnsupportedFrontmatterError, buildConfigLayer, collect, configValue, contextEnvelope, files, forDiagnostics, graphEnvelope, json, jsonError, layerOverlayFileSystem, makeOverlayFileSystem, provideConfig, provideDocuments, resolveBundleRoot, resolveDocumentPath, resolveProjectConfig, resolveProjectRoot, run, runContext, runGraph, runStale, runSync, runVerify, runVerifyBatch, sort, staleEnvelope, syncEnvelope, tally, targetPaths, verifyBatchEnvelope, verifyEnvelope, withFallbackRange };
|
|
30
|
+
export { BundleSession, CONFIG_RELATIVE_PATH, ConfigMalformedError, ConfigPathNotFoundError, ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, DEFAULT_PROFILE_NAME, DistributionField, DocumentPathError, ENGINE_VERSION, ExternalReferences, GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, InitOverwriteError, JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, Now, OkfitPlatform, OverlayDocuments, ReferenceCheck, ReferenceState, SkipReason, StaleEnvelope, StaleItem, StaleSummary, SyncEnvelope, SyncModeEnvelope, SyncStagedLogError, VerifyBatchEnvelope, VerifyConceptNotFoundError, VerifyEnvelope, VerifySelectionError, VerifyUnsupportedFrontmatterError, buildConfigLayer, collect, conceptFor, configValue, contextEnvelope, files, forDiagnostics, graphEnvelope, json, jsonError, layerOverlayFileSystem, makeOverlayFileSystem, provideConfig, provideDocuments, resolveBundleRoot, resolveDocumentPath, resolveProjectConfig, resolveProjectRoot, run, runContext, runGraph, runStale, runSync, runVerify, runVerifyBatch, sort, staleEnvelope, syncEnvelope, tally, targetPaths, verifyBatchEnvelope, verifyEnvelope, withFallbackRange };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okfit/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The okfit engine: platform layer, config discovery, and the validate, verify, sync, init and context programs shared by the okfit CLI and MCP server.",
|
|
6
6
|
"keywords": [
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"@effected/walker": "^0.12.0",
|
|
47
47
|
"@effected/xdg": "^0.7.0",
|
|
48
48
|
"@effected/yaml": "^0.17.0",
|
|
49
|
-
"@okfit/core": "0.
|
|
50
|
-
"@okfit/profiles": "0.
|
|
49
|
+
"@okfit/core": "0.8.0",
|
|
50
|
+
"@okfit/profiles": "0.8.0",
|
|
51
51
|
"effect": "4.0.0-rc.117"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ConceptId } from "@okfit/core";
|
|
2
|
+
import { Option } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/session/concept.ts
|
|
5
|
+
/** Backslashes to forward slashes; the rest of this module treats every path as already resolved. */
|
|
6
|
+
const posixOf = (value) => value.split("\\").join("/");
|
|
7
|
+
/** Drops every trailing `/` without a regex (a `/\/+$/` pattern is quadratic on a long run of slashes). */
|
|
8
|
+
const stripTrailingSlashes = (path) => {
|
|
9
|
+
let end = path.length;
|
|
10
|
+
while (end > 0 && path.charCodeAt(end - 1) === 47) end -= 1;
|
|
11
|
+
return path.slice(0, end);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The loaded concept at `absolutePath`, if any. `absolutePath` must already sit under
|
|
15
|
+
* `bundle.root` (a real resolved path, never re-resolved here: `@okfit/core`'s own
|
|
16
|
+
* `internal/posixPath.ts` avoids `path.resolve`/`path.relative` on purpose, because both read
|
|
17
|
+
* `process.cwd()`, and this package holds the same line). Strips `bundle.root` as a plain
|
|
18
|
+
* string prefix, then applies the same `ConceptId.fromPath` normalisation `withFallbackRange`
|
|
19
|
+
* uses: `None` for a path outside the bundle, a reserved file (`index.md`, `log.md`), a
|
|
20
|
+
* non-markdown file, or a markdown file that never decoded into a concept.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
const conceptFor = (bundle, absolutePath) => {
|
|
25
|
+
const root = stripTrailingSlashes(posixOf(bundle.root));
|
|
26
|
+
const target = posixOf(absolutePath);
|
|
27
|
+
const prefix = `${root}/`;
|
|
28
|
+
if (!target.startsWith(prefix)) return Option.none();
|
|
29
|
+
const relative = target.slice(prefix.length);
|
|
30
|
+
return Option.flatMap(ConceptId.fromPath(relative), (id) => Option.fromUndefinedOr(bundle.concepts.get(id)));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { conceptFor };
|
package/session/range.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { conceptFor } from "./concept.js";
|
|
2
|
+
import { DiagnosticRange } from "@okfit/core";
|
|
2
3
|
import { Option } from "effect";
|
|
3
4
|
|
|
4
5
|
//#region src/session/range.ts
|
|
@@ -20,10 +21,7 @@ const FILE_START = DiagnosticRange.make({
|
|
|
20
21
|
*/
|
|
21
22
|
const withFallbackRange = (bundle, diagnostic) => {
|
|
22
23
|
if (diagnostic.range !== void 0 || diagnostic.file === "") return diagnostic;
|
|
23
|
-
const concept = Option.
|
|
24
|
-
onNone: () => void 0,
|
|
25
|
-
onSome: (id) => bundle.concepts.get(id)
|
|
26
|
-
});
|
|
24
|
+
const concept = Option.getOrUndefined(conceptFor(bundle, `${bundle.root}/${diagnostic.file}`));
|
|
27
25
|
const node = concept?.document.frontmatter;
|
|
28
26
|
if (concept === void 0 || node === void 0) return {
|
|
29
27
|
...diagnostic,
|
package/validate/resources.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Diagnostic, OkfitConfig } from "@okfit/core";
|
|
1
|
+
import { Diagnostic, DiagnosticRange, OkfitConfig } from "@okfit/core";
|
|
2
2
|
import { Effect, FileSystem, Path } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/validate/resources.ts
|
|
@@ -22,11 +22,23 @@ const stripSuffix = (value) => {
|
|
|
22
22
|
const at = value.search(/[?#]/);
|
|
23
23
|
return at === -1 ? value : value.slice(0, at);
|
|
24
24
|
};
|
|
25
|
-
/** Every `resource` value on a concept: the top-level field, then each source's. */
|
|
25
|
+
/** Every `resource` value on a concept, each with its own frontmatter path: the top-level field, then each source's. */
|
|
26
26
|
const resourceValuesOf = (concept) => {
|
|
27
27
|
const values = [];
|
|
28
|
-
if (concept.frontmatter.resource !== void 0) values.push(
|
|
29
|
-
|
|
28
|
+
if (concept.frontmatter.resource !== void 0) values.push({
|
|
29
|
+
value: concept.frontmatter.resource,
|
|
30
|
+
path: ["resource"]
|
|
31
|
+
});
|
|
32
|
+
concept.frontmatter.sources?.forEach((source, index) => {
|
|
33
|
+
values.push({
|
|
34
|
+
value: source.resource,
|
|
35
|
+
path: [
|
|
36
|
+
"sources",
|
|
37
|
+
index,
|
|
38
|
+
"resource"
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
});
|
|
30
42
|
return values;
|
|
31
43
|
};
|
|
32
44
|
/**
|
|
@@ -36,9 +48,11 @@ const resourceValuesOf = (concept) => {
|
|
|
36
48
|
* pattern) or a literal path resolved in D-23 order: relative to the
|
|
37
49
|
* concept's own directory, then (for a bare path) relative to the bundle
|
|
38
50
|
* root, or with a leading `/` relative to the bundle root only; only the
|
|
39
|
-
* latter is checked against the filesystem.
|
|
40
|
-
*
|
|
41
|
-
* `
|
|
51
|
+
* latter is checked against the filesystem. Ranges at the offending value
|
|
52
|
+
* (phase 4 decision 2, via `DiagnosticRange.forFrontmatterPath`) using the
|
|
53
|
+
* `["resource"]` or `["sources", index, "resource"]` path the value came
|
|
54
|
+
* from. Total over severity (mirrors `Provenance.lint`'s S-28 posture):
|
|
55
|
+
* returns `[]`, touching neither `FileSystem` nor `Path`, the moment
|
|
42
56
|
* `OkfitConfig.severityFor(config, "source-resource-missing")` is `"off"`.
|
|
43
57
|
*
|
|
44
58
|
* @public
|
|
@@ -49,7 +63,7 @@ const lintResources = (bundle, config) => Effect.gen(function* () {
|
|
|
49
63
|
const fs = yield* FileSystem.FileSystem;
|
|
50
64
|
const path = yield* Path.Path;
|
|
51
65
|
const diagnostics = [];
|
|
52
|
-
for (const [, concept] of bundle.concepts) for (const value of resourceValuesOf(concept)) {
|
|
66
|
+
for (const [, concept] of bundle.concepts) for (const { value, path: fieldPath } of resourceValuesOf(concept)) {
|
|
53
67
|
if (isDescriptor(value)) continue;
|
|
54
68
|
const stripped = stripSuffix(value);
|
|
55
69
|
const candidates = stripped.startsWith("/") ? [path.join(bundle.root, stripped.slice(1))] : [path.resolve(bundle.root, path.dirname(concept.path), stripped), ...stripped.startsWith("./") || stripped.startsWith("../") ? [] : [path.resolve(bundle.root, stripped)]];
|
|
@@ -59,11 +73,13 @@ const lintResources = (bundle, config) => Effect.gen(function* () {
|
|
|
59
73
|
break;
|
|
60
74
|
}
|
|
61
75
|
if (exists) continue;
|
|
76
|
+
const range = DiagnosticRange.forFrontmatterPath(concept.document, fieldPath);
|
|
62
77
|
diagnostics.push(Diagnostic.make({
|
|
63
78
|
file: concept.path,
|
|
64
79
|
code: "source-resource-missing",
|
|
65
80
|
severity,
|
|
66
|
-
message: `Resource "${value}" does not exist relative to ${concept.path}
|
|
81
|
+
message: `Resource "${value}" does not exist relative to ${concept.path}`,
|
|
82
|
+
...range === void 0 ? {} : { range }
|
|
67
83
|
}));
|
|
68
84
|
}
|
|
69
85
|
return diagnostics;
|