@equationalapplications/core-okf 5.3.1 → 5.5.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 +14 -2
- package/dist/index.d.mts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js +383 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +372 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
A zero-dependency library for parsing and producing [Open Knowledge Format (OKF) v0.1](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) bundles. This package provides the raw primitives to work with OKF frontmatter, concept documents, and index/log files, completely decoupled from any specific database or data model.
|
|
7
|
+
A zero-dependency library for parsing and producing [Open Knowledge Format (OKF) v0.1 and v0.2](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) bundles. This package provides the raw primitives to work with OKF frontmatter, concept documents, and index/log files, completely decoupled from any specific database or data model.
|
|
8
8
|
|
|
9
9
|
For a ready-made `MemoryDump` ⇄ OKF bundle adapter, see the [OKF Import/Export section in `@equationalapplications/core-llm-wiki`](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#okf-importexport).
|
|
10
10
|
|
|
11
|
-
For the llm-wiki interoperability
|
|
11
|
+
For the llm-wiki interoperability profiles (`llm-wiki/1` and `llm-wiki/2`), see [`docs/okf-profile.md`](https://github.com/equationalapplications/expo-llm-wiki/blob/main/docs/okf-profile.md). Conformance fixtures live in [`fixtures/`](./fixtures/).
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -75,3 +75,15 @@ const links = extractMarkdownLinks('See [preferences](facts/fact_abc.md) for mor
|
|
|
75
75
|
| [@equationalapplications/core-llm-tools](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core-llm-tools/README.md) | Gemini tool schemas and capability injector |
|
|
76
76
|
| [**@equationalapplications/core-okf**](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/okf/README.md) | Zero-dependency Open Knowledge Format (OKF) v0.1 primitives — parse and produce interoperable knowledge bundles. |
|
|
77
77
|
| [@equationalapplications/schema-org-llm-wiki](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/schema-org/README.md) | Curated schema.org warm-agent ontology manifest |
|
|
78
|
+
|
|
79
|
+
## OKF v0.2 conformance
|
|
80
|
+
|
|
81
|
+
This package conforms to [OKF v0.2](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md). New helpers for v0.2:
|
|
82
|
+
|
|
83
|
+
- `deriveTrustTier(verified)` — derive `'unverified' | 'machine-confirmed' | 'human-reviewed'`.
|
|
84
|
+
- `isStaleAfter(staleAfter, now)` — staleness check against an absolute `YYYY-MM-DD` cutoff.
|
|
85
|
+
- `parseVerifiedFlexible(value)` — accept either an array or a bare `{ by, at }` mapping.
|
|
86
|
+
- `extractFootnotes(body)` / `serializeFootnotes(footnotes)` — preserve footnote attribution verbatim.
|
|
87
|
+
- `parseFlowMapping(text)` / `parseFlowSequence(text)` — flow parsing with at most one level of mapping-value nesting (no anchors/aliases outside quoted strings).
|
|
88
|
+
|
|
89
|
+
The `serializeFrontmatter` function emits plain objects as flow mappings and actor strings (containing `/` or `:`) via `serializeActorString`. Anchor/alias expansion is rejected; only **unquoted** `&` or `*` characters are treated as opaque (quoted spans are excluded from the scan, so URL query strings like `"https://x/a?p=1&q=2"` remain valid).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
type OkfFrontmatterScalar = string | number | boolean | null;
|
|
2
|
-
type OkfFrontmatterValue = OkfFrontmatterScalar | OkfFrontmatterScalar[]
|
|
2
|
+
type OkfFrontmatterValue = OkfFrontmatterScalar | OkfFrontmatterScalar[] | {
|
|
3
|
+
[key: string]: OkfFrontmatterValue | undefined;
|
|
4
|
+
} | OkfFrontmatterValue[];
|
|
3
5
|
interface OkfFrontmatter {
|
|
4
6
|
type: string;
|
|
5
7
|
title?: string;
|
|
@@ -30,8 +32,54 @@ interface OkfMarkdownLink {
|
|
|
30
32
|
text: string;
|
|
31
33
|
path: string;
|
|
32
34
|
}
|
|
35
|
+
type OkfStatus = 'draft' | 'stable' | 'deprecated';
|
|
36
|
+
type OkfActorKind = 'agent' | 'human' | 'process';
|
|
37
|
+
interface OkfGenerated {
|
|
38
|
+
by: string;
|
|
39
|
+
at: string;
|
|
40
|
+
}
|
|
41
|
+
interface OkfVerifiedEntry {
|
|
42
|
+
by: string;
|
|
43
|
+
at: string;
|
|
44
|
+
[key: string]: OkfFrontmatterScalar;
|
|
45
|
+
}
|
|
46
|
+
type OkfVerified = OkfVerifiedEntry[];
|
|
47
|
+
interface OkfSourceUsageWindow {
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
[key: string]: OkfFrontmatterScalar;
|
|
51
|
+
}
|
|
52
|
+
interface OkfSource {
|
|
53
|
+
id?: string;
|
|
54
|
+
resource: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
author?: string;
|
|
57
|
+
usage_count?: number;
|
|
58
|
+
last_modified?: string;
|
|
59
|
+
usage_window?: OkfSourceUsageWindow;
|
|
60
|
+
[key: string]: OkfFrontmatterValue | undefined;
|
|
61
|
+
}
|
|
62
|
+
interface OkfFootnote {
|
|
63
|
+
id: string;
|
|
64
|
+
body: string;
|
|
65
|
+
}
|
|
33
66
|
|
|
67
|
+
declare function serializeActorString(value: string): string;
|
|
34
68
|
declare function serializeFrontmatter(fm: OkfFrontmatter): string;
|
|
69
|
+
/**
|
|
70
|
+
* `depth` counts levels of **mapping-value nesting** only: it increments when a
|
|
71
|
+
* flow MAPPING's key has a value that is itself a flow collection, and is
|
|
72
|
+
* rejected once it would exceed 1 (spec §2.6's "at most one level of nesting").
|
|
73
|
+
* It does NOT increment when a flow SEQUENCE's item is itself a mapping or
|
|
74
|
+
* sequence — `sources: [ {...}, {...} ]` (a flat array of objects) is the base
|
|
75
|
+
* shape v0.2 uses everywhere and is not "nesting" in the spec's sense. This is
|
|
76
|
+
* what lets `sources: [ { usage_window: { from, to } } ]` parse (sequence item
|
|
77
|
+
* is a mapping at depth 0; that mapping's `usage_window` value is a mapping at
|
|
78
|
+
* depth 1 — one level, allowed) while `{ a: { b: { c: 1 } } }` is rejected
|
|
79
|
+
* (`a`'s value nests at depth 1; `b`'s value would need depth 2 — rejected).
|
|
80
|
+
*/
|
|
81
|
+
declare function parseFlowMapping(text: string, depth?: number): Record<string, OkfFrontmatterValue> | null;
|
|
82
|
+
declare function parseFlowSequence(text: string, depth?: number): OkfFrontmatterValue[] | null;
|
|
35
83
|
/**
|
|
36
84
|
* Parses the subset of YAML frontmatter that {@link serializeFrontmatter} produces:
|
|
37
85
|
* scalar string/number/boolean/null values, quoted strings, and block string-lists.
|
|
@@ -105,4 +153,31 @@ declare function isAllowedOkfPath(filePath: string): boolean;
|
|
|
105
153
|
*/
|
|
106
154
|
declare function extractMarkdownLinks(body: string): OkfMarkdownLink[];
|
|
107
155
|
|
|
108
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Extracts footnote definitions from a body. OKF v0.2 footnote attribution uses
|
|
158
|
+
* `[^id]` markers whose label joins to `sources[].id`. This helper parses the
|
|
159
|
+
* `[^id]: text` definition lines at the body tail and returns them as opaque
|
|
160
|
+
* text — we deliberately do NOT try to interpret which body span uses which
|
|
161
|
+
* footnote; the body is the source of truth and is preserved verbatim on
|
|
162
|
+
* round-trip.
|
|
163
|
+
*/
|
|
164
|
+
declare function extractFootnotes(body: string): OkfFootnote[];
|
|
165
|
+
/** Joins footnote definitions back into a body block. Continuation lines are
|
|
166
|
+
* indented (two spaces) so that {@link extractFootnotes} — which only
|
|
167
|
+
* recognizes continuation lines that are indented per CommonMark — round-trips
|
|
168
|
+
* multi-line bodies. */
|
|
169
|
+
declare function serializeFootnotes(footnotes: OkfFootnote[]): string;
|
|
170
|
+
|
|
171
|
+
type TrustTier = 'unverified' | 'machine-confirmed' | 'human-reviewed';
|
|
172
|
+
declare function deriveTrustTier(verified: OkfVerified | undefined): TrustTier;
|
|
173
|
+
declare function isStaleAfter(staleAfter: string | number | null, now: number): boolean;
|
|
174
|
+
declare function parseVerifiedFlexible(value: unknown): OkfVerified;
|
|
175
|
+
declare function formatVerifiedJson(entries: OkfVerified): string;
|
|
176
|
+
declare function formatSourcesJson(sources: OkfSource[], sharedWindow?: OkfSourceUsageWindow | null): string;
|
|
177
|
+
declare function latestVerified(entries: OkfVerified | undefined, _now: number): {
|
|
178
|
+
by: string;
|
|
179
|
+
at: number;
|
|
180
|
+
} | null;
|
|
181
|
+
declare function parseCitationsList(body: string): string[];
|
|
182
|
+
|
|
183
|
+
export { type OkfActorKind, type OkfFile, type OkfFootnote, type OkfFrontmatter, type OkfFrontmatterScalar, type OkfFrontmatterValue, type OkfGenerated, type OkfIndexEntry, type OkfIndexSection, type OkfLogEntry, type OkfMarkdownLink, type OkfSource, type OkfSourceUsageWindow, type OkfStatus, type OkfVerified, type OkfVerifiedEntry, type TrustTier, appendEventIdComment, appendRelatedSection, buildConceptDocument, buildEntityIndexMd, buildIndexMd, buildLogMd, buildRootIndexMd, deriveTrustTier, extractFootnotes, extractMarkdownLinks, formatSourcesJson, formatVerifiedJson, isAllowedOkfPath, isStaleAfter, latestVerified, parseCitationsList, parseConcept, parseEntityIndexMd, parseEventIdComment, parseFlowMapping, parseFlowSequence, parseFrontmatter, parseLogMd, parseRootIndexMd, parseVerifiedFlexible, serializeActorString, serializeFootnotes, serializeFrontmatter, splitRelatedSection };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
type OkfFrontmatterScalar = string | number | boolean | null;
|
|
2
|
-
type OkfFrontmatterValue = OkfFrontmatterScalar | OkfFrontmatterScalar[]
|
|
2
|
+
type OkfFrontmatterValue = OkfFrontmatterScalar | OkfFrontmatterScalar[] | {
|
|
3
|
+
[key: string]: OkfFrontmatterValue | undefined;
|
|
4
|
+
} | OkfFrontmatterValue[];
|
|
3
5
|
interface OkfFrontmatter {
|
|
4
6
|
type: string;
|
|
5
7
|
title?: string;
|
|
@@ -30,8 +32,54 @@ interface OkfMarkdownLink {
|
|
|
30
32
|
text: string;
|
|
31
33
|
path: string;
|
|
32
34
|
}
|
|
35
|
+
type OkfStatus = 'draft' | 'stable' | 'deprecated';
|
|
36
|
+
type OkfActorKind = 'agent' | 'human' | 'process';
|
|
37
|
+
interface OkfGenerated {
|
|
38
|
+
by: string;
|
|
39
|
+
at: string;
|
|
40
|
+
}
|
|
41
|
+
interface OkfVerifiedEntry {
|
|
42
|
+
by: string;
|
|
43
|
+
at: string;
|
|
44
|
+
[key: string]: OkfFrontmatterScalar;
|
|
45
|
+
}
|
|
46
|
+
type OkfVerified = OkfVerifiedEntry[];
|
|
47
|
+
interface OkfSourceUsageWindow {
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
[key: string]: OkfFrontmatterScalar;
|
|
51
|
+
}
|
|
52
|
+
interface OkfSource {
|
|
53
|
+
id?: string;
|
|
54
|
+
resource: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
author?: string;
|
|
57
|
+
usage_count?: number;
|
|
58
|
+
last_modified?: string;
|
|
59
|
+
usage_window?: OkfSourceUsageWindow;
|
|
60
|
+
[key: string]: OkfFrontmatterValue | undefined;
|
|
61
|
+
}
|
|
62
|
+
interface OkfFootnote {
|
|
63
|
+
id: string;
|
|
64
|
+
body: string;
|
|
65
|
+
}
|
|
33
66
|
|
|
67
|
+
declare function serializeActorString(value: string): string;
|
|
34
68
|
declare function serializeFrontmatter(fm: OkfFrontmatter): string;
|
|
69
|
+
/**
|
|
70
|
+
* `depth` counts levels of **mapping-value nesting** only: it increments when a
|
|
71
|
+
* flow MAPPING's key has a value that is itself a flow collection, and is
|
|
72
|
+
* rejected once it would exceed 1 (spec §2.6's "at most one level of nesting").
|
|
73
|
+
* It does NOT increment when a flow SEQUENCE's item is itself a mapping or
|
|
74
|
+
* sequence — `sources: [ {...}, {...} ]` (a flat array of objects) is the base
|
|
75
|
+
* shape v0.2 uses everywhere and is not "nesting" in the spec's sense. This is
|
|
76
|
+
* what lets `sources: [ { usage_window: { from, to } } ]` parse (sequence item
|
|
77
|
+
* is a mapping at depth 0; that mapping's `usage_window` value is a mapping at
|
|
78
|
+
* depth 1 — one level, allowed) while `{ a: { b: { c: 1 } } }` is rejected
|
|
79
|
+
* (`a`'s value nests at depth 1; `b`'s value would need depth 2 — rejected).
|
|
80
|
+
*/
|
|
81
|
+
declare function parseFlowMapping(text: string, depth?: number): Record<string, OkfFrontmatterValue> | null;
|
|
82
|
+
declare function parseFlowSequence(text: string, depth?: number): OkfFrontmatterValue[] | null;
|
|
35
83
|
/**
|
|
36
84
|
* Parses the subset of YAML frontmatter that {@link serializeFrontmatter} produces:
|
|
37
85
|
* scalar string/number/boolean/null values, quoted strings, and block string-lists.
|
|
@@ -105,4 +153,31 @@ declare function isAllowedOkfPath(filePath: string): boolean;
|
|
|
105
153
|
*/
|
|
106
154
|
declare function extractMarkdownLinks(body: string): OkfMarkdownLink[];
|
|
107
155
|
|
|
108
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Extracts footnote definitions from a body. OKF v0.2 footnote attribution uses
|
|
158
|
+
* `[^id]` markers whose label joins to `sources[].id`. This helper parses the
|
|
159
|
+
* `[^id]: text` definition lines at the body tail and returns them as opaque
|
|
160
|
+
* text — we deliberately do NOT try to interpret which body span uses which
|
|
161
|
+
* footnote; the body is the source of truth and is preserved verbatim on
|
|
162
|
+
* round-trip.
|
|
163
|
+
*/
|
|
164
|
+
declare function extractFootnotes(body: string): OkfFootnote[];
|
|
165
|
+
/** Joins footnote definitions back into a body block. Continuation lines are
|
|
166
|
+
* indented (two spaces) so that {@link extractFootnotes} — which only
|
|
167
|
+
* recognizes continuation lines that are indented per CommonMark — round-trips
|
|
168
|
+
* multi-line bodies. */
|
|
169
|
+
declare function serializeFootnotes(footnotes: OkfFootnote[]): string;
|
|
170
|
+
|
|
171
|
+
type TrustTier = 'unverified' | 'machine-confirmed' | 'human-reviewed';
|
|
172
|
+
declare function deriveTrustTier(verified: OkfVerified | undefined): TrustTier;
|
|
173
|
+
declare function isStaleAfter(staleAfter: string | number | null, now: number): boolean;
|
|
174
|
+
declare function parseVerifiedFlexible(value: unknown): OkfVerified;
|
|
175
|
+
declare function formatVerifiedJson(entries: OkfVerified): string;
|
|
176
|
+
declare function formatSourcesJson(sources: OkfSource[], sharedWindow?: OkfSourceUsageWindow | null): string;
|
|
177
|
+
declare function latestVerified(entries: OkfVerified | undefined, _now: number): {
|
|
178
|
+
by: string;
|
|
179
|
+
at: number;
|
|
180
|
+
} | null;
|
|
181
|
+
declare function parseCitationsList(body: string): string[];
|
|
182
|
+
|
|
183
|
+
export { type OkfActorKind, type OkfFile, type OkfFootnote, type OkfFrontmatter, type OkfFrontmatterScalar, type OkfFrontmatterValue, type OkfGenerated, type OkfIndexEntry, type OkfIndexSection, type OkfLogEntry, type OkfMarkdownLink, type OkfSource, type OkfSourceUsageWindow, type OkfStatus, type OkfVerified, type OkfVerifiedEntry, type TrustTier, appendEventIdComment, appendRelatedSection, buildConceptDocument, buildEntityIndexMd, buildIndexMd, buildLogMd, buildRootIndexMd, deriveTrustTier, extractFootnotes, extractMarkdownLinks, formatSourcesJson, formatVerifiedJson, isAllowedOkfPath, isStaleAfter, latestVerified, parseCitationsList, parseConcept, parseEntityIndexMd, parseEventIdComment, parseFlowMapping, parseFlowSequence, parseFrontmatter, parseLogMd, parseRootIndexMd, parseVerifiedFlexible, serializeActorString, serializeFootnotes, serializeFrontmatter, splitRelatedSection };
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,10 @@ function quoteString(value) {
|
|
|
26
26
|
function serializeScalarString(value) {
|
|
27
27
|
return needsQuoting(value) ? quoteString(value) : value;
|
|
28
28
|
}
|
|
29
|
+
function serializeActorString(value) {
|
|
30
|
+
if (/[/:]/.test(value)) return quoteString(value);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
29
33
|
function serializeKey(key) {
|
|
30
34
|
if (/\s/.test(key) || needsQuoting(key) || isIso8601Timestamp(key)) {
|
|
31
35
|
return quoteString(key);
|
|
@@ -37,8 +41,52 @@ function serializeValue(value) {
|
|
|
37
41
|
if (typeof value === "boolean") return String(value);
|
|
38
42
|
if (typeof value === "number") return String(value);
|
|
39
43
|
if (typeof value === "string") return serializeScalarString(value);
|
|
44
|
+
if (Array.isArray(value)) return serializeFlowSequenceValue(value);
|
|
45
|
+
if (typeof value === "object") return serializeFlowMappingValue(value);
|
|
40
46
|
throw new Error(`Unsupported frontmatter value type: ${typeof value}`);
|
|
41
47
|
}
|
|
48
|
+
function needsFlowQuoting(value) {
|
|
49
|
+
return needsQuoting(value) || /[,{}[\]/:]/.test(value);
|
|
50
|
+
}
|
|
51
|
+
function serializeFlowScalarString(value) {
|
|
52
|
+
return needsFlowQuoting(value) ? quoteString(value) : value;
|
|
53
|
+
}
|
|
54
|
+
function serializeFlowMappingValue(obj, depth = 0) {
|
|
55
|
+
const entries = Object.entries(obj).filter(([, v]) => v !== void 0);
|
|
56
|
+
if (entries.length === 0) return "{}";
|
|
57
|
+
const parts = entries.map(([k, v]) => {
|
|
58
|
+
let rendered;
|
|
59
|
+
if (typeof v === "string") rendered = serializeFlowScalarString(v);
|
|
60
|
+
else if (v === null || typeof v === "boolean" || typeof v === "number") rendered = serializeValue(v);
|
|
61
|
+
else if (Array.isArray(v)) {
|
|
62
|
+
if (depth >= 1) throw new Error(`serializeFlowMappingValue: nesting deeper than one level is not supported (key "${k}")`);
|
|
63
|
+
rendered = serializeFlowSequenceValue(v, depth + 1);
|
|
64
|
+
} else if (typeof v === "object") {
|
|
65
|
+
if (depth >= 1) throw new Error(`serializeFlowMappingValue: nesting deeper than one level is not supported (key "${k}")`);
|
|
66
|
+
rendered = serializeFlowMappingValue(v, depth + 1);
|
|
67
|
+
} else {
|
|
68
|
+
throw new Error(`serializeFlowMappingValue: unsupported value type for key "${k}"`);
|
|
69
|
+
}
|
|
70
|
+
return `${serializeKey(k)}: ${rendered}`;
|
|
71
|
+
});
|
|
72
|
+
return `{ ${parts.join(", ")} }`;
|
|
73
|
+
}
|
|
74
|
+
function serializeFlowSequenceValue(arr, depth = 0) {
|
|
75
|
+
if (arr.length === 0) return "[]";
|
|
76
|
+
const parts = arr.map((item) => {
|
|
77
|
+
if (typeof item === "string") return serializeFlowScalarString(item);
|
|
78
|
+
if (item === null || typeof item === "boolean" || typeof item === "number") return serializeValue(item);
|
|
79
|
+
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
80
|
+
return serializeFlowMappingValue(item, depth);
|
|
81
|
+
}
|
|
82
|
+
if (Array.isArray(item)) {
|
|
83
|
+
if (depth >= 1) throw new Error(`serializeFlowSequenceValue: nested array deeper than one level is not supported`);
|
|
84
|
+
return serializeFlowSequenceValue(item, depth + 1);
|
|
85
|
+
}
|
|
86
|
+
throw new Error("serializeFlowSequenceValue: unsupported item type");
|
|
87
|
+
});
|
|
88
|
+
return `[ ${parts.join(", ")} ]`;
|
|
89
|
+
}
|
|
42
90
|
function serializeFrontmatter(fm) {
|
|
43
91
|
const lines = ["---"];
|
|
44
92
|
for (const [key, value] of Object.entries(fm)) {
|
|
@@ -46,16 +94,16 @@ function serializeFrontmatter(fm) {
|
|
|
46
94
|
if (Array.isArray(value)) {
|
|
47
95
|
if (value.length === 0) {
|
|
48
96
|
lines.push(`${serializeKey(key)}: []`);
|
|
97
|
+
} else if (value.some((item) => item !== null && typeof item === "object")) {
|
|
98
|
+
lines.push(`${serializeKey(key)}: ${serializeFlowSequenceValue(value)}`);
|
|
49
99
|
} else {
|
|
50
100
|
lines.push(`${serializeKey(key)}:`);
|
|
51
101
|
for (const item of value) {
|
|
52
|
-
|
|
53
|
-
lines.push(` - ${serializeScalarString(item)}`);
|
|
54
|
-
} else {
|
|
55
|
-
lines.push(` - ${serializeValue(item)}`);
|
|
56
|
-
}
|
|
102
|
+
lines.push(` - ${typeof item === "string" ? serializeScalarString(item) : serializeValue(item)}`);
|
|
57
103
|
}
|
|
58
104
|
}
|
|
105
|
+
} else if (value !== null && typeof value === "object") {
|
|
106
|
+
lines.push(`${serializeKey(key)}: ${serializeFlowMappingValue(value)}`);
|
|
59
107
|
} else {
|
|
60
108
|
lines.push(`${serializeKey(key)}: ${serializeValue(value)}`);
|
|
61
109
|
}
|
|
@@ -134,6 +182,121 @@ function matchFrontmatterKeyValue(line) {
|
|
|
134
182
|
const tail = line.slice(key.length + 1);
|
|
135
183
|
return { key, value: tail.trimStart(), hasValue: tail.trim().length > 0 };
|
|
136
184
|
}
|
|
185
|
+
function hasUnquotedAnchorOrAlias(text) {
|
|
186
|
+
let quote = null;
|
|
187
|
+
for (let i = 0; i < text.length; i++) {
|
|
188
|
+
const ch = text[i];
|
|
189
|
+
if (quote) {
|
|
190
|
+
if (ch === "\\" && i + 1 < text.length) {
|
|
191
|
+
i++;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (ch === quote) quote = null;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (ch === '"' || ch === "'") {
|
|
198
|
+
quote = ch;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (ch === "&" || ch === "*") return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function splitFlowEntries(inner) {
|
|
206
|
+
const entries = [];
|
|
207
|
+
let buf = "";
|
|
208
|
+
let i = 0;
|
|
209
|
+
let quote = null;
|
|
210
|
+
let depthBrace = 0;
|
|
211
|
+
let depthBracket = 0;
|
|
212
|
+
while (i < inner.length) {
|
|
213
|
+
const ch = inner[i];
|
|
214
|
+
if (quote) {
|
|
215
|
+
buf += ch;
|
|
216
|
+
if (ch === "\\" && i + 1 < inner.length) {
|
|
217
|
+
buf += inner[i + 1];
|
|
218
|
+
i += 2;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (ch === quote) quote = null;
|
|
222
|
+
i++;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (ch === '"' || ch === "'") {
|
|
226
|
+
quote = ch;
|
|
227
|
+
buf += ch;
|
|
228
|
+
i++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (ch === "{") depthBrace++;
|
|
232
|
+
else if (ch === "}") depthBrace--;
|
|
233
|
+
else if (ch === "[") depthBracket++;
|
|
234
|
+
else if (ch === "]") depthBracket--;
|
|
235
|
+
if (ch === "," && depthBrace === 0 && depthBracket === 0) {
|
|
236
|
+
entries.push(buf.trim());
|
|
237
|
+
buf = "";
|
|
238
|
+
i++;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
buf += ch;
|
|
242
|
+
i++;
|
|
243
|
+
}
|
|
244
|
+
if (buf.trim().length > 0) entries.push(buf.trim());
|
|
245
|
+
if (depthBrace !== 0 || depthBracket !== 0 || quote !== null) return null;
|
|
246
|
+
return entries;
|
|
247
|
+
}
|
|
248
|
+
function parseFlowMapping(text, depth = 0) {
|
|
249
|
+
const trimmed = text.trim();
|
|
250
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
|
|
251
|
+
if (hasUnquotedAnchorOrAlias(trimmed)) return null;
|
|
252
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
253
|
+
if (inner.length === 0) return {};
|
|
254
|
+
const entries = splitFlowEntries(inner);
|
|
255
|
+
if (!entries) return null;
|
|
256
|
+
const out = {};
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
const kv = matchFrontmatterKeyValue(entry);
|
|
259
|
+
if (!kv || !kv.hasValue) return null;
|
|
260
|
+
const v = kv.value.trim();
|
|
261
|
+
if (v.startsWith("{") || v.startsWith("[")) {
|
|
262
|
+
if (depth >= 1) return null;
|
|
263
|
+
const nested = v.startsWith("{") ? parseFlowMapping(v, depth + 1) : parseFlowSequence(v, depth + 1);
|
|
264
|
+
if (nested === null) return null;
|
|
265
|
+
out[parseKey(kv.key)] = nested;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
out[parseKey(kv.key)] = parseScalarValue(v);
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
function parseFlowSequence(text, depth = 0) {
|
|
273
|
+
const trimmed = text.trim();
|
|
274
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
275
|
+
if (hasUnquotedAnchorOrAlias(trimmed)) return null;
|
|
276
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
277
|
+
if (inner.length === 0) return [];
|
|
278
|
+
const entries = splitFlowEntries(inner);
|
|
279
|
+
if (!entries) return null;
|
|
280
|
+
const out = [];
|
|
281
|
+
for (const entry of entries) {
|
|
282
|
+
const v = entry.trim();
|
|
283
|
+
if (v.startsWith("{")) {
|
|
284
|
+
const nested = parseFlowMapping(v, depth);
|
|
285
|
+
if (nested === null) return null;
|
|
286
|
+
out.push(nested);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (v.startsWith("[")) {
|
|
290
|
+
if (depth >= 1) return null;
|
|
291
|
+
const nested = parseFlowSequence(v, depth + 1);
|
|
292
|
+
if (nested === null) return null;
|
|
293
|
+
out.push(nested);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
out.push(parseScalarValue(v));
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
137
300
|
function parseFrontmatter(content) {
|
|
138
301
|
const lines = content.split(/\r?\n/);
|
|
139
302
|
const fallback = { frontmatter: { type: "" }, rest: content };
|
|
@@ -171,6 +334,34 @@ function parseFrontmatter(content) {
|
|
|
171
334
|
frontmatter[key] = items;
|
|
172
335
|
continue;
|
|
173
336
|
}
|
|
337
|
+
const valueRaw = kv.value.trim();
|
|
338
|
+
if (valueRaw.startsWith("{")) {
|
|
339
|
+
const flowObj = parseFlowMapping(valueRaw);
|
|
340
|
+
if (flowObj !== null) {
|
|
341
|
+
frontmatter[parseKey(kv.key)] = flowObj;
|
|
342
|
+
i++;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
frontmatter[parseKey(kv.key)] = null;
|
|
346
|
+
i++;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (valueRaw.startsWith("[")) {
|
|
350
|
+
if (valueRaw === "[]") {
|
|
351
|
+
frontmatter[parseKey(kv.key)] = [];
|
|
352
|
+
i++;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const flowSeq = parseFlowSequence(valueRaw);
|
|
356
|
+
if (flowSeq !== null) {
|
|
357
|
+
frontmatter[parseKey(kv.key)] = flowSeq;
|
|
358
|
+
i++;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
frontmatter[parseKey(kv.key)] = null;
|
|
362
|
+
i++;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
174
365
|
frontmatter[parseKey(kv.key)] = parseScalarValue(kv.value);
|
|
175
366
|
i++;
|
|
176
367
|
}
|
|
@@ -427,6 +618,181 @@ function extractMarkdownLinks(body) {
|
|
|
427
618
|
return links;
|
|
428
619
|
}
|
|
429
620
|
|
|
621
|
+
// src/footnotes.ts
|
|
622
|
+
var FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s?(.*)$/;
|
|
623
|
+
function extractFootnotes(body) {
|
|
624
|
+
const out = [];
|
|
625
|
+
const lines = body.split(/\r?\n/);
|
|
626
|
+
let i = 0;
|
|
627
|
+
while (i < lines.length) {
|
|
628
|
+
const m = FOOTNOTE_DEFINITION.exec(lines[i]);
|
|
629
|
+
if (!m) {
|
|
630
|
+
i++;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
const id = m[1];
|
|
634
|
+
const firstLine = m[2] ?? "";
|
|
635
|
+
const collected = [firstLine];
|
|
636
|
+
i++;
|
|
637
|
+
while (i < lines.length) {
|
|
638
|
+
const next = lines[i];
|
|
639
|
+
if (FOOTNOTE_DEFINITION.test(next)) break;
|
|
640
|
+
if (/^\s{2,}\S/.test(next) || /^\s{2,}/.test(next)) {
|
|
641
|
+
collected.push(next.replace(/^\s{2}/, ""));
|
|
642
|
+
i++;
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
out.push({ id, body: collected.join("\n").trimEnd() });
|
|
648
|
+
}
|
|
649
|
+
return out;
|
|
650
|
+
}
|
|
651
|
+
function serializeFootnotes(footnotes) {
|
|
652
|
+
return footnotes.map((f) => {
|
|
653
|
+
const lines = f.body.split("\n");
|
|
654
|
+
const [firstLine, ...continuation] = lines;
|
|
655
|
+
const tail = continuation.map((line) => `
|
|
656
|
+
${line}`).join("");
|
|
657
|
+
return `[^${f.id}]: ${firstLine ?? ""}${tail}`;
|
|
658
|
+
}).join("\n");
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/v02-helpers.ts
|
|
662
|
+
function deriveTrustTier(verified) {
|
|
663
|
+
if (!verified || verified.length === 0) return "unverified";
|
|
664
|
+
for (const v of verified) {
|
|
665
|
+
if (typeof v?.by === "string" && v.by.startsWith("human:")) return "human-reviewed";
|
|
666
|
+
}
|
|
667
|
+
return "machine-confirmed";
|
|
668
|
+
}
|
|
669
|
+
function parseStaleAfter(staleAfter) {
|
|
670
|
+
if (staleAfter == null) return null;
|
|
671
|
+
if (typeof staleAfter === "number") return Number.isFinite(staleAfter) ? staleAfter : null;
|
|
672
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(staleAfter);
|
|
673
|
+
if (!match) return null;
|
|
674
|
+
const year = Number(match[1]);
|
|
675
|
+
const month = Number(match[2]);
|
|
676
|
+
const day = Number(match[3]);
|
|
677
|
+
const ts = Date.UTC(year, month - 1, day);
|
|
678
|
+
if (!Number.isFinite(ts)) return null;
|
|
679
|
+
const d = new Date(ts);
|
|
680
|
+
if (d.getUTCFullYear() !== year || d.getUTCMonth() + 1 !== month || d.getUTCDate() !== day) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
return ts;
|
|
684
|
+
}
|
|
685
|
+
function isStaleAfter(staleAfter, now) {
|
|
686
|
+
const cutoff = parseStaleAfter(staleAfter);
|
|
687
|
+
if (cutoff == null) return false;
|
|
688
|
+
return now >= cutoff;
|
|
689
|
+
}
|
|
690
|
+
function parseVerifiedFlexible(value) {
|
|
691
|
+
if (value == null) return [];
|
|
692
|
+
if (Array.isArray(value)) {
|
|
693
|
+
return value.filter(
|
|
694
|
+
(v) => !!v && typeof v === "object" && typeof v.by === "string" && typeof v.at === "string"
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
if (typeof value === "object" && typeof value.by === "string" && typeof value.at === "string") {
|
|
698
|
+
return [value];
|
|
699
|
+
}
|
|
700
|
+
return [];
|
|
701
|
+
}
|
|
702
|
+
function formatVerifiedJson(entries) {
|
|
703
|
+
const sorted = [...entries].sort((a, b) => {
|
|
704
|
+
const ta = Date.parse(a.at);
|
|
705
|
+
const tb = Date.parse(b.at);
|
|
706
|
+
const fa = Number.isFinite(ta);
|
|
707
|
+
const fb = Number.isFinite(tb);
|
|
708
|
+
if (fa && fb) return ta - tb;
|
|
709
|
+
if (fa && !fb) return -1;
|
|
710
|
+
if (!fa && fb) return 1;
|
|
711
|
+
return a.at < b.at ? -1 : a.at > b.at ? 1 : 0;
|
|
712
|
+
});
|
|
713
|
+
return JSON.stringify(sorted);
|
|
714
|
+
}
|
|
715
|
+
function formatSourcesJson(sources, sharedWindow) {
|
|
716
|
+
const folded = sources.map(
|
|
717
|
+
(s) => s.usage_window ? s : sharedWindow ? { ...s, usage_window: sharedWindow } : s
|
|
718
|
+
);
|
|
719
|
+
return JSON.stringify(folded);
|
|
720
|
+
}
|
|
721
|
+
function latestVerified(entries, _now) {
|
|
722
|
+
if (!entries || entries.length === 0) return null;
|
|
723
|
+
let best = null;
|
|
724
|
+
let bestAt = -Infinity;
|
|
725
|
+
for (const e of entries) {
|
|
726
|
+
const ts = Date.parse(e.at);
|
|
727
|
+
if (Number.isFinite(ts) && ts > bestAt) {
|
|
728
|
+
best = e;
|
|
729
|
+
bestAt = ts;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (!best) return null;
|
|
733
|
+
return { by: best.by, at: bestAt };
|
|
734
|
+
}
|
|
735
|
+
var URL_LINE = /^\s*-\s+(https?:\/\/\S+)\s*$/;
|
|
736
|
+
function parseCitationsList(body) {
|
|
737
|
+
const lines = body.split(/\r?\n/);
|
|
738
|
+
let inFence = false;
|
|
739
|
+
let fenceMarker = "";
|
|
740
|
+
let fenceLength = 0;
|
|
741
|
+
let startIdx = -1;
|
|
742
|
+
const isFenceClose = (marker, fullMatch, line) => {
|
|
743
|
+
if (marker[0] !== fenceMarker) return false;
|
|
744
|
+
if (marker.length < fenceLength) return false;
|
|
745
|
+
return /^\s*$/.test(line.slice(fullMatch.length));
|
|
746
|
+
};
|
|
747
|
+
for (let i = 0; i < lines.length; i++) {
|
|
748
|
+
const line = lines[i];
|
|
749
|
+
const fence = /^\s*(```+|~~~+)/.exec(line);
|
|
750
|
+
if (fence) {
|
|
751
|
+
const marker = fence[1];
|
|
752
|
+
if (!inFence) {
|
|
753
|
+
inFence = true;
|
|
754
|
+
fenceMarker = marker[0];
|
|
755
|
+
fenceLength = marker.length;
|
|
756
|
+
} else if (isFenceClose(marker, fence[0], line)) {
|
|
757
|
+
inFence = false;
|
|
758
|
+
fenceMarker = "";
|
|
759
|
+
fenceLength = 0;
|
|
760
|
+
}
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
if (!inFence && /^\s*#{1,2}\s*citations\s*$/i.test(line)) {
|
|
764
|
+
startIdx = i;
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (startIdx === -1) return [];
|
|
769
|
+
const out = [];
|
|
770
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
771
|
+
const line = lines[i];
|
|
772
|
+
const fence = /^\s*(```+|~~~+)/.exec(line);
|
|
773
|
+
if (fence) {
|
|
774
|
+
const marker = fence[1];
|
|
775
|
+
if (!inFence) {
|
|
776
|
+
inFence = true;
|
|
777
|
+
fenceMarker = marker[0];
|
|
778
|
+
fenceLength = marker.length;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (isFenceClose(marker, fence[0], line)) {
|
|
782
|
+
inFence = false;
|
|
783
|
+
fenceMarker = "";
|
|
784
|
+
fenceLength = 0;
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (!inFence && /^\s*#{1,2}\s+/.test(line)) break;
|
|
789
|
+
if (inFence) continue;
|
|
790
|
+
const m = URL_LINE.exec(line);
|
|
791
|
+
if (m) out.push(m[1]);
|
|
792
|
+
}
|
|
793
|
+
return out;
|
|
794
|
+
}
|
|
795
|
+
|
|
430
796
|
exports.appendEventIdComment = appendEventIdComment;
|
|
431
797
|
exports.appendRelatedSection = appendRelatedSection;
|
|
432
798
|
exports.buildConceptDocument = buildConceptDocument;
|
|
@@ -434,14 +800,26 @@ exports.buildEntityIndexMd = buildEntityIndexMd;
|
|
|
434
800
|
exports.buildIndexMd = buildIndexMd;
|
|
435
801
|
exports.buildLogMd = buildLogMd;
|
|
436
802
|
exports.buildRootIndexMd = buildRootIndexMd;
|
|
803
|
+
exports.deriveTrustTier = deriveTrustTier;
|
|
804
|
+
exports.extractFootnotes = extractFootnotes;
|
|
437
805
|
exports.extractMarkdownLinks = extractMarkdownLinks;
|
|
806
|
+
exports.formatSourcesJson = formatSourcesJson;
|
|
807
|
+
exports.formatVerifiedJson = formatVerifiedJson;
|
|
438
808
|
exports.isAllowedOkfPath = isAllowedOkfPath;
|
|
809
|
+
exports.isStaleAfter = isStaleAfter;
|
|
810
|
+
exports.latestVerified = latestVerified;
|
|
811
|
+
exports.parseCitationsList = parseCitationsList;
|
|
439
812
|
exports.parseConcept = parseConcept;
|
|
440
813
|
exports.parseEntityIndexMd = parseEntityIndexMd;
|
|
441
814
|
exports.parseEventIdComment = parseEventIdComment;
|
|
815
|
+
exports.parseFlowMapping = parseFlowMapping;
|
|
816
|
+
exports.parseFlowSequence = parseFlowSequence;
|
|
442
817
|
exports.parseFrontmatter = parseFrontmatter;
|
|
443
818
|
exports.parseLogMd = parseLogMd;
|
|
444
819
|
exports.parseRootIndexMd = parseRootIndexMd;
|
|
820
|
+
exports.parseVerifiedFlexible = parseVerifiedFlexible;
|
|
821
|
+
exports.serializeActorString = serializeActorString;
|
|
822
|
+
exports.serializeFootnotes = serializeFootnotes;
|
|
445
823
|
exports.serializeFrontmatter = serializeFrontmatter;
|
|
446
824
|
exports.splitRelatedSection = splitRelatedSection;
|
|
447
825
|
//# sourceMappingURL=index.js.map
|