@okfit/engine 0.8.0 → 0.9.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 CHANGED
@@ -4,6 +4,8 @@ The shared engine behind [okfit](https://github.com/spencerbeggs/okfit): the pla
4
4
 
5
5
  You probably want `@okfit/plugin` (both bins), `@okfit/cli` (the `okfit` bin) or `@okfit/mcp` (the `okfit-mcp` bin) instead. This package is what they are built from.
6
6
 
7
+ `FrontmatterEdits` is a public facade over the `verify` splice machinery: it produces `MarkdownEdit`s for a concept's top-level `status` scalar and its `verified` list, with whole-file offsets into the source as passed (BOM included), so a consumer such as `@okfit/lsp` can apply them directly or map them to editor ranges without adjustment.
8
+
7
9
  ## License
8
10
 
9
11
  [MIT](LICENSE)
@@ -0,0 +1,76 @@
1
+ import { documentNewline, locate, locateTopLevelScalar, stripBom } from "../verify/locate.js";
2
+ import { splice, spliceTopLevelScalar } from "../verify/splice.js";
3
+ import { Effect, Runtime, Schema } from "effect";
4
+ import { MarkdownEdit } from "@effected/markdown";
5
+
6
+ //#region src/edits/FrontmatterEdits.ts
7
+ /**
8
+ * A top-level frontmatter key `FrontmatterEdits` was asked to edit turned
9
+ * out to be a shape it cannot splice: `shape` is whichever locator string
10
+ * `verify/locate.ts` produced for that key (e.g. `"flow-mapping"`,
11
+ * `"alias"`, `"status-block-scalar"`). The file is never touched when this
12
+ * is raised -- callers should surface it and let a human edit by hand.
13
+ *
14
+ * @public
15
+ */
16
+ var UnsupportedFrontmatterError = class extends Schema.TaggedError()("UnsupportedFrontmatterError", {
17
+ key: Schema.String,
18
+ shape: Schema.String
19
+ }) {
20
+ [Runtime.errorExitCode] = 3;
21
+ get message() {
22
+ return `"${this.key}"'s frontmatter value is a shape FrontmatterEdits cannot edit (${this.shape}); edit it by hand`;
23
+ }
24
+ };
25
+ /** `edit` shifted forward by `by` bytes, or `edit` itself when `by` is zero. */
26
+ const shift = (edit, by) => by === 0 ? edit : MarkdownEdit.make({
27
+ offset: edit.offset + by,
28
+ length: edit.length,
29
+ content: edit.content
30
+ });
31
+ /**
32
+ * A public facade over the `verify/locate.ts` and `verify/splice.ts`
33
+ * machinery: byte-range `MarkdownEdit`s for a concept's top-level `status`
34
+ * scalar and its `verified` list. `verified` builds the exact same edit
35
+ * `okfit verify`'s `prepareVerify` builds -- same `locate`, same `splice`,
36
+ * same `documentNewline` newline choice -- so its output matches
37
+ * `okfit verify`'s splice byte for byte; `status` reuses the identical
38
+ * newline rule for consistency, over `locateTopLevelScalar` and
39
+ * `spliceTopLevelScalar` instead.
40
+ *
41
+ * Every offset returned is a WHOLE-FILE offset into `source` AS PASSED --
42
+ * BOM included when present -- so a caller can apply the edits with
43
+ * `MarkdownEdit.applyAll(source, edits)` directly, or map them to editor
44
+ * ranges without adjustment. Internally the BOM is stripped for parsing and
45
+ * its length added back to every offset before returning.
46
+ *
47
+ * @public
48
+ */
49
+ var FrontmatterEdits = class {
50
+ constructor() {}
51
+ /** Edits that set top-level `status` to `status`, replacing an existing scalar or inserting after `title:` (else `type:`). */
52
+ static status = (source, status) => Effect.gen(function* () {
53
+ const { text, bom } = stripBom(source);
54
+ const target = yield* locateTopLevelScalar(text, "status");
55
+ if (target._tag === "unsupported") return yield* new UnsupportedFrontmatterError({
56
+ key: "status",
57
+ shape: target.shape
58
+ });
59
+ const edit = spliceTopLevelScalar(target, "status", status, documentNewline(text));
60
+ return [shift(edit, bom.length)];
61
+ });
62
+ /** Edits that append one verified entry, the same splice `okfit verify` performs. */
63
+ static verified = (source, entry) => Effect.gen(function* () {
64
+ const { text, bom } = stripBom(source);
65
+ const target = yield* locate(text);
66
+ if (target._tag === "unsupported") return yield* new UnsupportedFrontmatterError({
67
+ key: "verified",
68
+ shape: target.shape
69
+ });
70
+ const edit = splice(target, entry, documentNewline(text));
71
+ return [shift(edit, bom.length)];
72
+ });
73
+ };
74
+
75
+ //#endregion
76
+ export { FrontmatterEdits, UnsupportedFrontmatterError };
package/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { Actor, BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, GraphLink, GraphNode, LinkGraph, LoadedBundle, LoadedConcept, OkfitConfig, OkfitConfigFile, StaleConcept, ValidationReport } from "@okfit/core";
1
+ import { Actor, BundleLoadError, ConceptId, Diagnostic, DiagnosticRange, DiagnosticSeverity, GraphLink, GraphNode, LinkGraph, LoadedBundle, LoadedConcept, OkfitConfig, OkfitConfigFile, StaleConcept, Status, 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";
5
5
  import { GeneratedAtError, GitHistory, GitHistoryError, Layout, Profile, ProfileDiagnostic } from "@okfit/profiles";
6
- import { FrontmatterWriteError, MarkdownParseError } from "@effected/markdown";
6
+ import { FrontmatterWriteError, MarkdownEdit, MarkdownParseError } from "@effected/markdown";
7
+ import { YamlParseError } from "@effected/yaml";
7
8
  import * as NodeServices from "@effect/platform-node/NodeServices";
9
+ import { Distribution as Distribution$1 } from "@effected/engine";
8
10
  import { Git, GitCommandError, UnknownRefError } from "@effected/git";
9
11
  //#region src/config/anchor.d.ts
10
12
  /**
@@ -375,6 +377,53 @@ interface ContextResult {
375
377
  */
376
378
  export declare const runContext: (options: ContextRunOptions) => Effect.Effect<ContextResult, never, FileSystem.FileSystem | Path.Path>;
377
379
  //#endregion
380
+ //#region src/edits/FrontmatterEdits.d.ts
381
+ declare const UnsupportedFrontmatterError_base: Schema.Class<UnsupportedFrontmatterError, Schema.TaggedStruct<"UnsupportedFrontmatterError", {
382
+ readonly key: Schema.String;
383
+ readonly shape: Schema.String;
384
+ }>, import("effect/Cause").YieldableError>;
385
+ /**
386
+ * A top-level frontmatter key `FrontmatterEdits` was asked to edit turned
387
+ * out to be a shape it cannot splice: `shape` is whichever locator string
388
+ * `verify/locate.ts` produced for that key (e.g. `"flow-mapping"`,
389
+ * `"alias"`, `"status-block-scalar"`). The file is never touched when this
390
+ * is raised -- callers should surface it and let a human edit by hand.
391
+ *
392
+ * @public
393
+ */
394
+ export declare class UnsupportedFrontmatterError extends UnsupportedFrontmatterError_base {
395
+ readonly [Runtime.errorExitCode] = 3;
396
+ get message(): string;
397
+ }
398
+ /**
399
+ * A public facade over the `verify/locate.ts` and `verify/splice.ts`
400
+ * machinery: byte-range `MarkdownEdit`s for a concept's top-level `status`
401
+ * scalar and its `verified` list. `verified` builds the exact same edit
402
+ * `okfit verify`'s `prepareVerify` builds -- same `locate`, same `splice`,
403
+ * same `documentNewline` newline choice -- so its output matches
404
+ * `okfit verify`'s splice byte for byte; `status` reuses the identical
405
+ * newline rule for consistency, over `locateTopLevelScalar` and
406
+ * `spliceTopLevelScalar` instead.
407
+ *
408
+ * Every offset returned is a WHOLE-FILE offset into `source` AS PASSED --
409
+ * BOM included when present -- so a caller can apply the edits with
410
+ * `MarkdownEdit.applyAll(source, edits)` directly, or map them to editor
411
+ * ranges without adjustment. Internally the BOM is stripped for parsing and
412
+ * its length added back to every offset before returning.
413
+ *
414
+ * @public
415
+ */
416
+ export declare class FrontmatterEdits {
417
+ private constructor();
418
+ /** Edits that set top-level `status` to `status`, replacing an existing scalar or inserting after `title:` (else `type:`). */
419
+ static readonly status: (source: string, status: Status) => Effect.Effect<ReadonlyArray<MarkdownEdit>, YamlParseError | UnsupportedFrontmatterError>;
420
+ /** Edits that append one verified entry, the same splice `okfit verify` performs. */
421
+ static readonly verified: (source: string, entry: {
422
+ readonly by: string;
423
+ readonly at: string;
424
+ }) => Effect.Effect<ReadonlyArray<MarkdownEdit>, YamlParseError | UnsupportedFrontmatterError>;
425
+ }
426
+ //#endregion
378
427
  //#region src/external/ExternalReferences.d.ts
379
428
  /**
380
429
  * Reachability of an external reference: `unknown` means never checked.
@@ -756,16 +805,18 @@ export declare const contextEnvelope: (input: {
756
805
  * reports; `engine_version` and `okf_version` are the pair a reader
757
806
  * compares.
758
807
  *
808
+ * Re-exported from `@effected/engine`'s own `Distribution` -- the shape
809
+ * both `@okfit/cli`'s `CurrentDistribution` (K-137/carrier-version-threading)
810
+ * and every JSON envelope in this package agree on, instead of two
811
+ * independently hand-rolled `{ name, version }` structs drifting apart.
812
+ *
759
813
  * @public
760
814
  */
761
- interface Distribution {
762
- readonly name: string;
763
- readonly version: string;
764
- }
815
+ type Distribution = Distribution$1;
765
816
  /** Every envelope's `distribution` field: a `Distribution` or `null`. @public */
766
- export declare const DistributionField: Schema.NullOr<Schema.Struct<{
767
- readonly name: Schema.String;
768
- readonly version: Schema.String;
817
+ export declare const DistributionField: import("effect/Schema").NullOr<import("effect/Schema").Struct<{
818
+ readonly name: import("effect/Schema").String;
819
+ readonly version: import("effect/Schema").String;
769
820
  }>>;
770
821
  /** @public */
771
822
  export type DistributionField = typeof DistributionField.Type;
package/index.js CHANGED
@@ -3,6 +3,7 @@ import { ConfigMalformedError, ConfigPathNotFoundError, DocumentPathError, InitO
3
3
  import { buildConfigLayer, provideConfig } from "./config/layer.js";
4
4
  import { DEFAULT_PROFILE_NAME, resolveProjectConfig } from "./config/resolve.js";
5
5
  import { runContext } from "./context/run.js";
6
+ import { FrontmatterEdits, UnsupportedFrontmatterError } from "./edits/FrontmatterEdits.js";
6
7
  import { ExternalReferences, ReferenceCheck, ReferenceState } from "./external/ExternalReferences.js";
7
8
  import { runGraph } from "./graph/run.js";
8
9
  import { CONFIG_RELATIVE_PATH, configValue, files, targetPaths } from "./init/scaffold.js";
@@ -27,4 +28,4 @@ import { BundleSession } from "./session/BundleSession.js";
27
28
  import { runStale } from "./stale/run.js";
28
29
  import { runVerify, runVerifyBatch } from "./verify/run.js";
29
30
 
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 };
31
+ export { BundleSession, CONFIG_RELATIVE_PATH, ConfigMalformedError, ConfigPathNotFoundError, ContextEnvelope, ContextField, ContextFieldValue, ContextTag, ContextType, DEFAULT_PROFILE_NAME, DistributionField, DocumentPathError, ENGINE_VERSION, ExternalReferences, FrontmatterEdits, GraphEdgeEnvelope, GraphEnvelope, GraphNodeEnvelope, GraphSummary, InitOverwriteError, JsonDiagnostic, JsonEnvelope, JsonErrorEnvelope, JsonSummary, Now, OkfitPlatform, OverlayDocuments, ReferenceCheck, ReferenceState, SkipReason, StaleEnvelope, StaleItem, StaleSummary, SyncEnvelope, SyncModeEnvelope, SyncStagedLogError, UnsupportedFrontmatterError, 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.8.0",
3
+ "version": "0.9.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": [
@@ -37,10 +37,11 @@
37
37
  "@effect/platform-node": "4.0.0-rc.117",
38
38
  "@effected/app": "^0.18.0",
39
39
  "@effected/config-file": "^0.12.0",
40
+ "@effected/engine": "^0.1.0",
40
41
  "@effected/git": "^0.17.0",
41
42
  "@effected/glob": "^0.8.0",
42
43
  "@effected/jsonc": "^0.13.0",
43
- "@effected/markdown": "^0.12.0",
44
+ "@effected/markdown": "^0.12.1",
44
45
  "@effected/store": "^0.10.0",
45
46
  "@effected/toml": "^0.9.0",
46
47
  "@effected/walker": "^0.12.0",
@@ -1,11 +1,8 @@
1
- import { Schema } from "effect";
1
+ import { DistributionField } from "@effected/engine";
2
2
 
3
3
  //#region src/render/distribution.ts
4
4
  /** Every envelope's `distribution` field: a `Distribution` or `null`. @public */
5
- const DistributionField = Schema.NullOr(Schema.Struct({
6
- name: Schema.String,
7
- version: Schema.String
8
- }));
5
+ const DistributionField$1 = DistributionField;
9
6
 
10
7
  //#endregion
11
- export { DistributionField };
8
+ export { DistributionField$1 as DistributionField };
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.59.1"
8
+ "packageVersion": "7.59.2"
9
9
  }
10
10
  ]
11
11
  }
package/verify/locate.js CHANGED
@@ -55,19 +55,16 @@ const hasMergeKey = (node) => {
55
55
  return false;
56
56
  };
57
57
  /**
58
- * Split `source`'s frontmatter, parse its value, find the top-level
59
- * `verified` pair, and classify it (contract §3.1). Fails only when the
60
- * frontmatter YAML is fatally unparseable — impossible for a concept that
61
- * reached `bundle.concepts`, but typed rather than assumed.
62
- *
63
- * Offset translation is `valueStart = 3 + newline.length` — `"---".length`
64
- * plus the opening fence's own terminator, taken from `split`'s fidelity
65
- * field rather than re-derived with `indexOf` (contract §12 note 9). Every
66
- * whole-file offset below is `valueStart + <yaml offset>`.
58
+ * The shared prologue every locator in this file starts with: split
59
+ * `source`'s frontmatter, parse its value, and confirm the parsed document
60
+ * is a (non-flow) top-level mapping. Returns the same `"no-frontmatter"` /
61
+ * `"not-a-mapping"` `unsupported` shapes each locator returned inline
62
+ * before this helper existed -- callers still name those shapes themselves,
63
+ * this only removes the duplicated parsing steps that produced them.
67
64
  *
68
65
  * @internal
69
66
  */
70
- const locate = Effect.fn("okfit/verify/locate")(function* (source) {
67
+ const parseTopLevelMapping = Effect.fn("okfit/verify/parseTopLevelMapping")(function* (source) {
71
68
  const block = FrontmatterSource.split(source).frontmatter;
72
69
  if (block === void 0) return {
73
70
  _tag: "unsupported",
@@ -80,6 +77,29 @@ const locate = Effect.fn("okfit/verify/locate")(function* (source) {
80
77
  _tag: "unsupported",
81
78
  shape: "not-a-mapping"
82
79
  };
80
+ return {
81
+ value,
82
+ valueStart,
83
+ contents
84
+ };
85
+ });
86
+ /**
87
+ * Split `source`'s frontmatter, parse its value, find the top-level
88
+ * `verified` pair, and classify it (contract §3.1). Fails only when the
89
+ * frontmatter YAML is fatally unparseable — impossible for a concept that
90
+ * reached `bundle.concepts`, but typed rather than assumed.
91
+ *
92
+ * Offset translation is `valueStart = 3 + newline.length` — `"---".length`
93
+ * plus the opening fence's own terminator, taken from `split`'s fidelity
94
+ * field rather than re-derived with `indexOf` (contract §12 note 9). Every
95
+ * whole-file offset below is `valueStart + <yaml offset>`.
96
+ *
97
+ * @internal
98
+ */
99
+ const locate = Effect.fn("okfit/verify/locate")(function* (source) {
100
+ const parsed = yield* parseTopLevelMapping(source);
101
+ if ("_tag" in parsed) return parsed;
102
+ const { value, valueStart, contents } = parsed;
83
103
  const pair = contents.items.find((item) => item.key instanceof YamlScalar && item.key.value === "verified");
84
104
  if (pair === void 0) return {
85
105
  _tag: "absent",
@@ -158,18 +178,9 @@ const locate = Effect.fn("okfit/verify/locate")(function* (source) {
158
178
  * @internal
159
179
  */
160
180
  const locateGeneratedField = Effect.fn("okfit/verify/locateGeneratedField")(function* (source, field) {
161
- const block = FrontmatterSource.split(source).frontmatter;
162
- if (block === void 0) return {
163
- _tag: "unsupported",
164
- shape: "no-frontmatter"
165
- };
166
- const value = block.value;
167
- const valueStart = 3 + (block.newline ?? "\n").length;
168
- const contents = (yield* YamlDocument.parse(value)).contents;
169
- if (!(contents instanceof YamlMap)) return {
170
- _tag: "unsupported",
171
- shape: "not-a-mapping"
172
- };
181
+ const parsed = yield* parseTopLevelMapping(source);
182
+ if ("_tag" in parsed) return parsed;
183
+ const { value, valueStart, contents } = parsed;
173
184
  const pair = contents.items.find((item) => item.key instanceof YamlScalar && item.key.value === "generated");
174
185
  if (pair === void 0) return {
175
186
  _tag: "unsupported",
@@ -254,20 +265,77 @@ const locateGenerated = (source) => locateGeneratedField(source, "at");
254
265
  * @internal
255
266
  */
256
267
  const locateGeneratedBodySha256 = (source) => locateGeneratedField(source, "body_sha256");
257
- /** @internal */
258
- const locateGeneratedBlock = Effect.fn("okfit/verify/locateGeneratedBlock")(function* (source) {
259
- const block = FrontmatterSource.split(source).frontmatter;
260
- if (block === void 0) return {
268
+ /**
269
+ * Locate a top-level scalar key (`key`) inside a concept's frontmatter,
270
+ * mirroring `locateGeneratedField`'s parsing prologue. Unlike `generated`'s
271
+ * fields, this key is a direct top-level sibling, not nested inside a block
272
+ * mapping, so there is no intermediate `generated:` lookup and no indent to
273
+ * track for the insert case -- `title:`/`type:` anchor the insert instead.
274
+ *
275
+ * @internal
276
+ */
277
+ const locateTopLevelScalar = Effect.fn("okfit/verify/locateTopLevelScalar")(function* (source, key) {
278
+ const parsed = yield* parseTopLevelMapping(source);
279
+ if ("_tag" in parsed) return parsed;
280
+ const { value, valueStart, contents } = parsed;
281
+ if (contents.style === "flow") return {
261
282
  _tag: "unsupported",
262
- shape: "no-frontmatter"
283
+ shape: "flow-mapping"
263
284
  };
264
- const value = block.value;
265
- const valueStart = 3 + (block.newline ?? "\n").length;
266
- const contents = (yield* YamlDocument.parse(value)).contents;
267
- if (!(contents instanceof YamlMap)) return {
285
+ const pair = contents.items.find((item) => item.key instanceof YamlScalar && item.key.value === key);
286
+ if (pair !== void 0) {
287
+ const node = pair.value;
288
+ if (node === null) return {
289
+ _tag: "unsupported",
290
+ shape: `${key}-empty`
291
+ };
292
+ if (node instanceof YamlAlias) return {
293
+ _tag: "unsupported",
294
+ shape: "alias"
295
+ };
296
+ if (!(node instanceof YamlScalar)) return {
297
+ _tag: "unsupported",
298
+ shape: `${key}-not-scalar`
299
+ };
300
+ if (node.style === "block-literal" || node.style === "block-folded") return {
301
+ _tag: "unsupported",
302
+ shape: `${key}-block-scalar`
303
+ };
304
+ return {
305
+ _tag: "replaceScalar",
306
+ start: valueStart + node.offset,
307
+ end: valueStart + node.offset + node.length,
308
+ quote: node.style
309
+ };
310
+ }
311
+ const findKey = (name) => contents.items.find((item) => item.key instanceof YamlScalar && item.key.value === name);
312
+ const titlePair = findKey("title");
313
+ const anchor = titlePair !== void 0 ? {
314
+ pair: titlePair,
315
+ afterKey: "title"
316
+ } : void 0;
317
+ const typePair = anchor === void 0 ? findKey("type") : void 0;
318
+ const resolved = anchor ?? (typePair !== void 0 ? {
319
+ pair: typePair,
320
+ afterKey: "type"
321
+ } : void 0);
322
+ if (resolved === void 0) return {
268
323
  _tag: "unsupported",
269
- shape: "not-a-mapping"
324
+ shape: "no-anchor-key"
325
+ };
326
+ const end = resolved.pair.value !== null ? resolved.pair.value.offset + resolved.pair.value.length : resolved.pair.key.offset + resolved.pair.key.length;
327
+ const nl = value.indexOf("\n", end);
328
+ return {
329
+ _tag: "insertAfterKey",
330
+ insertAt: valueStart + (value[end - 1] === "\n" ? end : nl === -1 ? value.length : nl + 1),
331
+ afterKey: resolved.afterKey
270
332
  };
333
+ });
334
+ /** @internal */
335
+ const locateGeneratedBlock = Effect.fn("okfit/verify/locateGeneratedBlock")(function* (source) {
336
+ const parsed = yield* parseTopLevelMapping(source);
337
+ if ("_tag" in parsed) return parsed;
338
+ const { value, valueStart, contents } = parsed;
271
339
  if (contents.items.find((item) => item.key instanceof YamlScalar && item.key.value === "generated") !== void 0) return {
272
340
  _tag: "unsupported",
273
341
  shape: "generated-present"
@@ -279,4 +347,4 @@ const locateGeneratedBlock = Effect.fn("okfit/verify/locateGeneratedBlock")(func
279
347
  });
280
348
 
281
349
  //#endregion
282
- export { detectNewline, documentNewline, indentAt, locate, locateGenerated, locateGeneratedBlock, locateGeneratedBodySha256, stripBom };
350
+ export { detectNewline, documentNewline, indentAt, locate, locateGenerated, locateGeneratedBlock, locateGeneratedBodySha256, locateTopLevelScalar, stripBom };
package/verify/splice.js CHANGED
@@ -93,6 +93,30 @@ const spliceGeneratedFields = (atTarget, bodySha256Target, encodedAt, bodySha256
93
93
  return [spliceGenerated(atTarget, encodedAt, newline, "at"), spliceGenerated(bodySha256Target, bodySha256, newline, "body_sha256")];
94
94
  };
95
95
  /**
96
+ * Build the one edit for `target` (a top-level scalar key such as
97
+ * `status`). Never calls a YAML stringifier: the only syntax emitted is
98
+ * `<key>: `, the preserved quote character (if any), and `newline`.
99
+ *
100
+ * @internal
101
+ */
102
+ const spliceTopLevelScalar = (target, key, value, newline) => {
103
+ switch (target._tag) {
104
+ case "insertAfterKey": return MarkdownEdit.make({
105
+ offset: target.insertAt,
106
+ length: 0,
107
+ content: `${key}: ${value}${newline}`
108
+ });
109
+ case "replaceScalar": {
110
+ const quoted = target.quote === "single-quoted" ? `'${value}'` : target.quote === "double-quoted" ? `"${value}"` : value;
111
+ return MarkdownEdit.make({
112
+ offset: target.start,
113
+ length: target.end - target.start,
114
+ content: quoted
115
+ });
116
+ }
117
+ }
118
+ };
119
+ /**
96
120
  * A whole `generated:` block mapping, appended as the LAST top-level key
97
121
  * (issue #73) — block style, matching how this repository writes every
98
122
  * `generated:` and how `splice`'s own `absent` case writes `verified:`.
@@ -106,4 +130,4 @@ const spliceGeneratedBlock = (target, fields, newline) => MarkdownEdit.make({
106
130
  });
107
131
 
108
132
  //#endregion
109
- export { splice, spliceGenerated, spliceGeneratedBlock, spliceGeneratedFields };
133
+ export { splice, spliceGenerated, spliceGeneratedBlock, spliceGeneratedFields, spliceTopLevelScalar };
package/version.js CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @public
13
13
  */
14
- const ENGINE_VERSION = "0.8.0";
14
+ const ENGINE_VERSION = "0.9.0";
15
15
 
16
16
  //#endregion
17
17
  export { ENGINE_VERSION };