@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 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.7.5",
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.7.4",
50
- "@okfit/profiles": "0.7.5",
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 { ConceptId, DiagnosticRange } from "@okfit/core";
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.match(ConceptId.fromPath(diagnostic.file), {
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,
@@ -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(concept.frontmatter.resource);
29
- for (const source of concept.frontmatter.sources ?? []) values.push(source.resource);
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. Total over severity (mirrors
40
- * `Provenance.lint`'s S-28 posture): returns `[]`, touching neither
41
- * `FileSystem` nor `Path`, the moment
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;
package/version.js CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @public
13
13
  */
14
- const ENGINE_VERSION = "0.7.5";
14
+ const ENGINE_VERSION = "0.8.0";
15
15
 
16
16
  //#endregion
17
17
  export { ENGINE_VERSION };