@geml/geml 1.7.1 → 1.7.2

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
@@ -96,8 +96,11 @@ Every command reads a file path, or `-` for stdin. Exit codes: `0` ok ·
96
96
  ```sh
97
97
  geml doc.geml # document-model JSON (default --to json)
98
98
  geml doc.geml --to md|html|geml # convert; geml notes.md -> GEML
99
+ geml list doc.geml # CALL FIRST: every block, its address, kind, line range
100
+ geml find "text" doc.geml|dir # search block CONTENT -> file<TAB>address; exit 1 = no hit
99
101
  geml get doc.geml ['<selector>'] # list addressable blocks, or print what the selector matches
100
- geml set doc.geml '<selector>' [--head|--body] [--in F[#src]] # replace ONE block's content
102
+ geml get doc.geml '#sec' --intro # a section cuts three ways: --head | --intro | --body
103
+ geml set doc.geml '<selector>' [--head|--intro|--body] [--in F[#src]] # replace ONE block's content
101
104
  geml add doc.geml (--append|--before #id|--after #id) [--in F[#src]] # insert a fragment
102
105
  geml delete doc.geml '#id' ['#id2' …] # remove one or more blocks
103
106
  geml rename doc.geml '#old' '#new' # rename an id + every reference to it
@@ -126,9 +129,22 @@ The agent loop: `geml get` a block → `set`/`add`/`delete`/`rename` it →
126
129
 
127
130
  `get` answers with N contents when N match (document order, count on stderr);
128
131
  `set` writes ONE block, so a selector matching several is refused (exit 2) with
129
- the unique address of each candidate. `--head` is the head line, `--body` the
130
- body; both round-trip — `geml get f X --body | geml set f X --body` leaves the
131
- file byte-identical.
132
+ the unique address of each candidate. A section cuts three ways: `--head` is the
133
+ heading line, `--intro` its opening region everything under it up to its first
134
+ subheading — and `--body` everything under it, so `--body` always contains
135
+ `--intro`, and equals it when the section has no subheading. All three
136
+ round-trip — `geml get f X --body | geml set f X --body` leaves the file
137
+ byte-identical — and `--intro` is how a section's opening is edited without
138
+ pulling its subsections into context. A block has no intro; asking for one is a
139
+ usage error rather than a quiet fall back to the body.
140
+
141
+ A write is refused when it would break the document, never merely because it
142
+ removes something. A replacement that drops blocks is carried out and the
143
+ dropped blocks are named on stderr — unnamed ones counted, references left
144
+ dangling reported — with `geml revert` as the way back. That is the same stance
145
+ `delete` takes, so removing content has one rule rather than two, and no region
146
+ becomes uneditable because something inside it happens to carry an id. The
147
+ round trip above drops nothing: the blocks came back in the text you sent.
132
148
 
133
149
  A `@<hex>` **content address** is the first 8 hex of the SHA-256 of the block's
134
150
  own text (line endings normalized to LF, no trailing newline), with `~1`, `~2`…
@@ -25,6 +25,20 @@ function splitLines(source) {
25
25
  function stripEnding(line) {
26
26
  return line.replace(/(\r\n|\r|\n)$/, "");
27
27
  }
28
+ // A local mirror of geml.ts's trimSpaceTabEnd — geml.ts already imports THIS
29
+ // module (normalizeBlockId), so importing back would close a cycle. Linear on
30
+ // purpose: `/[ \t]+$/` restarts the run at every index inside a run that never
31
+ // reaches the end of the line, which is quadratic in the line's length.
32
+ function trimSpaceTabEnd(s) {
33
+ let i = s.length;
34
+ while (i > 0) {
35
+ const c = s.charCodeAt(i - 1);
36
+ if (c !== 0x20 && c !== 0x09)
37
+ break;
38
+ i--;
39
+ }
40
+ return i === s.length ? s : s.slice(0, i);
41
+ }
28
42
  // Rewrite the id inside a `{…}` attribute block to `#newId`, keeping the braces
29
43
  // and every other class/attr byte. If no id is present, insert `#newId` as the
30
44
  // first token. The id token sits at a token boundary (`{` or whitespace) and
@@ -98,7 +112,7 @@ export function normalizeBlockId(blockSrc, newId) {
98
112
  const openLen = /^=+/.exec(f[1])[0].length;
99
113
  for (let j = hi + 1; j < lines.length; j++) {
100
114
  const ct = stripEnding(lines[j]);
101
- const trimmed = ct.replace(/[ \t]+$/, "");
115
+ const trimmed = trimSpaceTabEnd(ct);
102
116
  if (/^=+$/.test(trimmed) && trimmed.length === openLen)
103
117
  break; // plain close: done
104
118
  const cm = /^(={3,}[ \t]+#)([^\s}]+)([ \t]*)$/.exec(ct);
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};