@north-light/crouter-api 0.3.181 → 0.3.183
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
|
@@ -41,18 +41,11 @@ autostart hook.
|
|
|
41
41
|
canvas, worktree, and human-in-the-loop.
|
|
42
42
|
- **`routes` / `API_VERSION`** — pure path builders for the `/v1` surface.
|
|
43
43
|
- **`ApiError` / `ErrorBody` / `isErrorBody`** — the error contract.
|
|
44
|
-
- **`@north-light/crouter-api/doc-link-grammar`** — the `[[canonical/name]]`
|
|
45
|
-
memory-document link grammar (`findDocLinks`, `docLinkNames`,
|
|
46
|
-
`isDocLinkName`). Zero imports and browser-safe, so a UI can scan transcript
|
|
47
|
-
markdown for doc links in the renderer and hand each name to
|
|
48
|
-
`client.resolveMemoryDoc(name, nodeId)`. Import the subpath, not the root, in
|
|
49
|
-
a browser bundle — the root pulls `CrtrClient` and its Node built-ins.
|
|
50
44
|
|
|
51
45
|
## Source of truth & releases
|
|
52
46
|
|
|
53
47
|
The source lives in the [`crouter`](https://github.com/vallum-security/crouter)
|
|
54
|
-
repo at `src/api
|
|
55
|
-
|
|
56
|
-
source of its own). It is published by the `publish-api.yml`
|
|
48
|
+
repo at `src/api/`; this package is a build-and-publish mirror of that subtree
|
|
49
|
+
(it carries no source of its own). It is published by the `publish-api.yml`
|
|
57
50
|
GitHub Action when a tag matching `api-vX.Y.Z` is pushed — the tag version must
|
|
58
51
|
match this package's `version`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter-api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.183",
|
|
4
4
|
"description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, and the CrtrClient. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api/index.js",
|
|
@@ -11,12 +11,6 @@
|
|
|
11
11
|
"import": "./dist/api/index.js",
|
|
12
12
|
"require": "./dist/api/index.js",
|
|
13
13
|
"default": "./dist/api/index.js"
|
|
14
|
-
},
|
|
15
|
-
"./doc-link-grammar": {
|
|
16
|
-
"types": "./dist/core/memory/doc-link-grammar.d.ts",
|
|
17
|
-
"import": "./dist/core/memory/doc-link-grammar.js",
|
|
18
|
-
"require": "./dist/core/memory/doc-link-grammar.js",
|
|
19
|
-
"default": "./dist/core/memory/doc-link-grammar.js"
|
|
20
14
|
}
|
|
21
15
|
},
|
|
22
16
|
"files": [
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** One `[[name]]` link occurrence found in a document body. */
|
|
2
|
-
export interface DocLink {
|
|
3
|
-
/** Character index of the first `[` of `[[`. */
|
|
4
|
-
start: number;
|
|
5
|
-
/** Character index just past the closing `]]`. */
|
|
6
|
-
end: number;
|
|
7
|
-
/** The canonical document name between the brackets. */
|
|
8
|
-
name: string;
|
|
9
|
-
}
|
|
10
|
-
/** Is `name` a well-formed canonical doc-link name? */
|
|
11
|
-
export declare function isDocLinkName(name: string): boolean;
|
|
12
|
-
/**
|
|
13
|
-
* Find every `[[name]]` doc link in a markdown body, in order of appearance,
|
|
14
|
-
* skipping fenced code blocks and inline code spans (a link mentioned inside
|
|
15
|
-
* code is an example, not a reference). Occurrences are NOT deduplicated —
|
|
16
|
-
* callers collapse as needed.
|
|
17
|
-
*/
|
|
18
|
-
export declare function findDocLinks(body: string): DocLink[];
|
|
19
|
-
/** The distinct link names in a body, first-appearance order. */
|
|
20
|
-
export declare function docLinkNames(body: string): string[];
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// doc-link-grammar.ts — the single source of truth for the `[[canonical/name]]`
|
|
2
|
-
// memory-document link grammar. Zero imports, pure string ops, browser-safe —
|
|
3
|
-
// any surface that highlights, resolves, or lints doc links imports THIS module
|
|
4
|
-
// rather than re-implementing the bracket scan or the name shape. It ships to
|
|
5
|
-
// external consumers as `@north-light/crouter-api/doc-link-grammar`, so a
|
|
6
|
-
// browser UI rendering a node's transcript scans with the same grammar crtrd
|
|
7
|
-
// validates `/v1/memory/resolve` names with.
|
|
8
|
-
//
|
|
9
|
-
// A doc link is a durable cross-reference written INSIDE a memory document's
|
|
10
|
-
// body, pointing at another memory document by its exact canonical name (the
|
|
11
|
-
// same identifier `crtr memory read <name>` takes; a directory INDEX is linked
|
|
12
|
-
// by its folded bare-directory name). It is distinct from the interactive
|
|
13
|
-
// `/name` inline-reference grammar (`inline-ref-grammar.ts`), which scans a
|
|
14
|
-
// human's live composer text — links live in stored markdown and survive there.
|
|
15
|
-
//
|
|
16
|
-
// Links are POINTERS, never transclusion: nothing in the runtime auto-loads a
|
|
17
|
-
// linked body into context. `crtr memory lint` fails a link that resolves to no
|
|
18
|
-
// document; `crtr memory read` surfaces the resolved links of the doc it loads.
|
|
19
|
-
//
|
|
20
|
-
// This module never resolves anything: `findDocLinks` returns candidate links;
|
|
21
|
-
// a caller decides a link is live by testing `name` against the corpus of
|
|
22
|
-
// resolvable canonical names.
|
|
23
|
-
// A canonical doc name: one or more `[A-Za-z0-9_-]+` segments joined by `/`.
|
|
24
|
-
// Anything else between `[[` `]]` (spaces, `|` aliases, empty segments) is NOT
|
|
25
|
-
// a doc link — the grammar has no alias/label form, by design.
|
|
26
|
-
const NAME_RE = /^[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*$/;
|
|
27
|
-
const LINK_RE = /\[\[([^\[\]\n]+)\]\]/g;
|
|
28
|
-
/** Is `name` a well-formed canonical doc-link name? */
|
|
29
|
-
export function isDocLinkName(name) {
|
|
30
|
-
return NAME_RE.test(name);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Find every `[[name]]` doc link in a markdown body, in order of appearance,
|
|
34
|
-
* skipping fenced code blocks and inline code spans (a link mentioned inside
|
|
35
|
-
* code is an example, not a reference). Occurrences are NOT deduplicated —
|
|
36
|
-
* callers collapse as needed.
|
|
37
|
-
*/
|
|
38
|
-
export function findDocLinks(body) {
|
|
39
|
-
const links = [];
|
|
40
|
-
for (const segment of proseSegments(body)) {
|
|
41
|
-
LINK_RE.lastIndex = 0;
|
|
42
|
-
let m;
|
|
43
|
-
while ((m = LINK_RE.exec(segment.text)) !== null) {
|
|
44
|
-
const name = m[1];
|
|
45
|
-
if (!isDocLinkName(name))
|
|
46
|
-
continue;
|
|
47
|
-
links.push({ start: segment.offset + m.index, end: segment.offset + m.index + m[0].length, name });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return links;
|
|
51
|
-
}
|
|
52
|
-
/** The distinct link names in a body, first-appearance order. */
|
|
53
|
-
export function docLinkNames(body) {
|
|
54
|
-
const seen = new Set();
|
|
55
|
-
const out = [];
|
|
56
|
-
for (const link of findDocLinks(body)) {
|
|
57
|
-
if (seen.has(link.name))
|
|
58
|
-
continue;
|
|
59
|
-
seen.add(link.name);
|
|
60
|
-
out.push(link.name);
|
|
61
|
-
}
|
|
62
|
-
return out;
|
|
63
|
-
}
|
|
64
|
-
const FENCE_RE = /^ {0,3}(`{3,}|~{3,})/;
|
|
65
|
-
function* proseSegments(body) {
|
|
66
|
-
let offset = 0;
|
|
67
|
-
let fence = null; // the opening fence marker while inside a block
|
|
68
|
-
for (const line of body.split('\n')) {
|
|
69
|
-
const lineLen = line.length + 1; // + '\n' (harmless overcount on last line)
|
|
70
|
-
const fenceMatch = line.match(FENCE_RE);
|
|
71
|
-
if (fence !== null) {
|
|
72
|
-
// Inside a fenced block: only a closing fence of the same char and at
|
|
73
|
-
// least the same length ends it. Nothing in here is prose.
|
|
74
|
-
if (fenceMatch && fenceMatch[1][0] === fence[0] && fenceMatch[1].length >= fence.length) {
|
|
75
|
-
fence = null;
|
|
76
|
-
}
|
|
77
|
-
offset += lineLen;
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (fenceMatch) {
|
|
81
|
-
fence = fenceMatch[1];
|
|
82
|
-
offset += lineLen;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
// Prose line: split around inline code spans (single-backtick pairs).
|
|
86
|
-
yield* proseParts(line, offset);
|
|
87
|
-
offset += lineLen;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
function* proseParts(line, offset) {
|
|
91
|
-
let i = 0;
|
|
92
|
-
while (i < line.length) {
|
|
93
|
-
const open = line.indexOf('`', i);
|
|
94
|
-
if (open === -1) {
|
|
95
|
-
yield { offset: offset + i, text: line.slice(i) };
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
// Backtick run: an inline span is delimited by runs of equal length.
|
|
99
|
-
let runEnd = open;
|
|
100
|
-
while (runEnd < line.length && line[runEnd] === '`')
|
|
101
|
-
runEnd++;
|
|
102
|
-
const run = line.slice(open, runEnd);
|
|
103
|
-
const close = line.indexOf(run, runEnd);
|
|
104
|
-
if (close === -1) {
|
|
105
|
-
// Unbalanced backticks: treat the rest as prose past the run.
|
|
106
|
-
yield { offset: offset + i, text: line.slice(i, open) };
|
|
107
|
-
i = runEnd;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
yield { offset: offset + i, text: line.slice(i, open) };
|
|
111
|
-
i = close + run.length;
|
|
112
|
-
}
|
|
113
|
-
}
|