@wrenautomation/knowledge 0.0.0-stage → 0.58.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.
@@ -0,0 +1,247 @@
1
+ /**
2
+ * The index: what a repo holds at one commit, in Postgres (or PGlite). Never the truth: rebuilt from
3
+ * git at will. A sync applies the diff from the last indexed commit to HEAD and the new head in one
4
+ * transaction, so a replay is safe and a crash leaves the old state. Rows carry `repo`: one index can
5
+ * hold many repos (a client each) for an admin view.
6
+ */
7
+ import { and, desc, eq, ilike, inArray, or, sql } from "drizzle-orm";
8
+ import { integer, jsonb, pgTable, primaryKey, text } from "drizzle-orm/pg-core";
9
+ import { diff, head, isAncestor, tryGit } from "./git.js";
10
+ import { edgesOf } from "./records.js";
11
+ import { loadAt, RECORD_DIRS, readPathsAt } from "./repo.js";
12
+ // bump when a table changes: the index drops and rebuilds itself from git
13
+ const SCHEMA = "1";
14
+ export const metaT = pgTable("knowledge_meta", { repo: text().notNull(), key: text().notNull(), value: text().notNull() }, (t) => [primaryKey({ columns: [t.repo, t.key] })]);
15
+ export const recordsT = pgTable("knowledge_records", {
16
+ repo: text().notNull(),
17
+ id: text().notNull(),
18
+ type: text().notNull(),
19
+ title: text().notNull(),
20
+ status: text().notNull(),
21
+ version: integer().notNull(),
22
+ by: text().notNull(),
23
+ why: text().notNull(),
24
+ path: text().notNull(),
25
+ topic: text(),
26
+ source: text(),
27
+ body: text().notNull(),
28
+ data: jsonb().notNull(),
29
+ created: text().notNull(),
30
+ updated: text().notNull(),
31
+ }, (t) => [primaryKey({ columns: [t.repo, t.id] })]);
32
+ export const citesT = pgTable("knowledge_cites", { repo: text().notNull(), from: text().notNull(), to: text().notNull() }, (t) => [primaryKey({ columns: [t.repo, t.from, t.to] })]);
33
+ const DDL = [
34
+ `create table if not exists knowledge_meta (repo text not null, key text not null, value text not null, primary key (repo, key))`,
35
+ `create table if not exists knowledge_records (repo text not null, id text not null, type text not null, title text not null, status text not null, version integer not null, "by" text not null, why text not null, path text not null, topic text, source text, body text not null, data jsonb not null, created text not null, updated text not null, primary key (repo, id))`,
36
+ `create index if not exists knowledge_records_path on knowledge_records (repo, path)`,
37
+ `create table if not exists knowledge_cites (repo text not null, "from" text not null, "to" text not null, primary key (repo, "from", "to"))`,
38
+ `create index if not exists knowledge_cites_to on knowledge_cites (repo, "to")`,
39
+ ];
40
+ const DROP = [
41
+ "drop table if exists knowledge_cites",
42
+ "drop table if exists knowledge_records",
43
+ "drop table if exists knowledge_meta",
44
+ ];
45
+ export class RecordIndex {
46
+ db;
47
+ close_;
48
+ ready = false;
49
+ constructor(db, close_ = async () => { }) {
50
+ this.db = db;
51
+ this.close_ = close_;
52
+ }
53
+ close() {
54
+ return this.close_();
55
+ }
56
+ /** Create the tables; a schema change drops and rebuilds them. */
57
+ async init() {
58
+ if (this.ready)
59
+ return;
60
+ for (const q of DDL)
61
+ await this.db.execute(sql.raw(q));
62
+ const [v] = await this.db
63
+ .select()
64
+ .from(metaT)
65
+ .where(and(eq(metaT.repo, "*"), eq(metaT.key, "schema")));
66
+ if (v?.value !== SCHEMA) {
67
+ for (const q of DROP)
68
+ await this.db.execute(sql.raw(q));
69
+ for (const q of DDL)
70
+ await this.db.execute(sql.raw(q));
71
+ await this.db.insert(metaT).values({ repo: "*", key: "schema", value: SCHEMA });
72
+ }
73
+ this.ready = true;
74
+ }
75
+ async indexedHead(repo) {
76
+ await this.init();
77
+ const [r] = await this.db
78
+ .select()
79
+ .from(metaT)
80
+ .where(and(eq(metaT.repo, repo), eq(metaT.key, "head")));
81
+ return r?.value ?? null;
82
+ }
83
+ /** Bring `repo` up to the checkout's HEAD. `full` rebuilds it from the files. */
84
+ async sync(root, repo, opts = {}) {
85
+ await this.init();
86
+ const h = head(root);
87
+ const last = opts.full ? null : await this.indexedHead(repo);
88
+ if (!h)
89
+ return { repo, head: null, mode: "none", upserted: 0, deleted: 0, broken: [] };
90
+ if (last === h)
91
+ return { repo, head: h, mode: "none", upserted: 0, deleted: 0, broken: [] };
92
+ const known = last && tryGit(root, ["cat-file", "-e", `${last}^{commit}`]).ok;
93
+ const incremental = !!(last && known && isAncestor(root, last, h));
94
+ let gone = [];
95
+ let removed = 0;
96
+ let tree;
97
+ if (incremental) {
98
+ const d = diff(root, last, h, RECORD_DIRS);
99
+ tree = readPathsAt(root, h, d.changed);
100
+ gone = [...d.deleted, ...d.changed];
101
+ removed = d.deleted.length;
102
+ }
103
+ else
104
+ tree = loadAt(root, h);
105
+ await this.db.transaction(async (tx) => {
106
+ if (!incremental) {
107
+ await tx.delete(citesT).where(eq(citesT.repo, repo));
108
+ removed = (await tx.delete(recordsT).where(eq(recordsT.repo, repo)).returning({ id: recordsT.id })).length;
109
+ }
110
+ else if (gone.length) {
111
+ const old = await tx
112
+ .delete(recordsT)
113
+ .where(and(eq(recordsT.repo, repo), inArray(recordsT.path, gone)))
114
+ .returning({ id: recordsT.id });
115
+ const ids = old.map((o) => o.id);
116
+ if (ids.length)
117
+ await tx.delete(citesT).where(and(eq(citesT.repo, repo), inArray(citesT.from, ids)));
118
+ }
119
+ for (const { path, doc } of tree.docs) {
120
+ const row = toRow(repo, path, doc.fm, doc.body);
121
+ await tx
122
+ .insert(recordsT)
123
+ .values(row)
124
+ .onConflictDoUpdate({ target: [recordsT.repo, recordsT.id], set: row });
125
+ await tx.delete(citesT).where(and(eq(citesT.repo, repo), eq(citesT.from, doc.fm.id)));
126
+ const edges = edgesOf(doc.fm).map((to) => ({ repo, from: doc.fm.id, to }));
127
+ if (edges.length)
128
+ await tx.insert(citesT).values(edges).onConflictDoNothing();
129
+ }
130
+ await tx
131
+ .insert(metaT)
132
+ .values({ repo, key: "head", value: h })
133
+ .onConflictDoUpdate({ target: [metaT.repo, metaT.key], set: { value: h } });
134
+ });
135
+ return {
136
+ repo,
137
+ head: h,
138
+ mode: incremental ? "diff" : "full",
139
+ upserted: tree.docs.length,
140
+ deleted: removed,
141
+ broken: tree.broken,
142
+ };
143
+ }
144
+ async get(repo, id) {
145
+ await this.init();
146
+ const [r] = await this.db
147
+ .select()
148
+ .from(recordsT)
149
+ .where(and(eq(recordsT.repo, repo), eq(recordsT.id, id)));
150
+ if (!r)
151
+ return null;
152
+ const cites = await this.linked(repo, id, "to");
153
+ const citedBy = await this.linked(repo, id, "from");
154
+ return { record: r, cites, citedBy };
155
+ }
156
+ /** other = "to": what `id` cites; "from": what cites `id`. */
157
+ async linked(repo, id, other) {
158
+ const col = other === "to" ? citesT.from : citesT.to;
159
+ const rows = await this.db
160
+ .select({
161
+ id: citesT[other],
162
+ type: recordsT.type,
163
+ title: recordsT.title,
164
+ status: recordsT.status,
165
+ })
166
+ .from(citesT)
167
+ .leftJoin(recordsT, and(eq(recordsT.repo, citesT.repo), eq(recordsT.id, citesT[other])))
168
+ .where(and(eq(citesT.repo, repo), eq(col, id)));
169
+ return rows.map((r) => ({
170
+ id: r.id,
171
+ type: r.type,
172
+ title: r.title,
173
+ status: r.status ?? "missing",
174
+ }));
175
+ }
176
+ async find(repo, f = {}) {
177
+ await this.init();
178
+ const where = [];
179
+ if (repo)
180
+ where.push(eq(recordsT.repo, repo));
181
+ if (f.type)
182
+ where.push(eq(recordsT.type, f.type));
183
+ if (f.topic)
184
+ where.push(eq(recordsT.topic, f.topic));
185
+ if (f.status)
186
+ where.push(eq(recordsT.status, f.status));
187
+ for (const w of (f.q ?? "").split(/\s+/).filter(Boolean)) {
188
+ const like = `%${w.replace(/[%_\\]/g, (c) => `\\${c}`)}%`;
189
+ where.push(or(ilike(recordsT.title, like), ilike(recordsT.body, like), ilike(recordsT.id, like)));
190
+ }
191
+ return this.db
192
+ .select()
193
+ .from(recordsT)
194
+ .where(where.length ? and(...where) : undefined)
195
+ .orderBy(desc(recordsT.updated))
196
+ .limit(f.limit ?? 50);
197
+ }
198
+ /** Live records whose evidence is missing, retired, or changed after them. */
199
+ async stale(repo) {
200
+ await this.init();
201
+ const res = await this.db.execute(sql `
202
+ select r.id, r.type, r.title, c."to" as cites, t.status as cited_status,
203
+ case when t.id is null then 'missing' when t.status = 'retired' then 'retired' else 'changed' end as why
204
+ from knowledge_cites c
205
+ join knowledge_records r on r.repo = c.repo and r.id = c."from"
206
+ left join knowledge_records t on t.repo = c.repo and t.id = c."to"
207
+ where c.repo = ${repo} and r.status = 'live'
208
+ and (t.id is null or t.status = 'retired' or t.updated > r.updated)
209
+ order by r.id`);
210
+ return rowsOf(res);
211
+ }
212
+ }
213
+ function toRow(repo, path, fm, body) {
214
+ return {
215
+ repo,
216
+ id: fm.id,
217
+ type: fm.type,
218
+ title: fm.title,
219
+ status: fm.status,
220
+ version: fm.version,
221
+ by: fm.by,
222
+ why: fm.why,
223
+ path,
224
+ topic: fm.type === "rule" ? fm.topic : null,
225
+ source: fm.type === "finding" ? fm.source : null,
226
+ body,
227
+ data: fm,
228
+ created: fm.created,
229
+ updated: fm.updated,
230
+ };
231
+ }
232
+ // PGlite answers { rows }, postgres-js an array
233
+ const rowsOf = (r) => Array.isArray(r) ? r : (r.rows ?? []);
234
+ /** PGlite in a folder (a checkout's own), or Postgres when a URL is given. */
235
+ export async function openIndex(opts) {
236
+ if (opts.url) {
237
+ const { default: postgres } = await import("postgres");
238
+ const { drizzle } = await import("drizzle-orm/postgres-js");
239
+ const client = postgres(opts.url, { max: 2, onnotice: () => { } });
240
+ return new RecordIndex(drizzle(client), () => client.end());
241
+ }
242
+ const { PGlite } = await import("@electric-sql/pglite");
243
+ const { drizzle } = await import("drizzle-orm/pglite");
244
+ const client = opts.dir ? new PGlite(opts.dir) : new PGlite();
245
+ return new RecordIndex(drizzle(client), () => client.close());
246
+ }
247
+ //# sourceMappingURL=record-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-index.js","sourceRoot":"","sources":["../src/record-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,226 @@
1
+ import { z } from "zod";
2
+ export declare const TYPES: readonly ["source", "finding", "verdict", "rule"];
3
+ export type RecordType = (typeof TYPES)[number];
4
+ export declare const DIRS: Record<RecordType, string>;
5
+ export declare const Id: z.ZodString;
6
+ export declare const SOURCE_KINDS: readonly ["site", "page", "video", "reel", "post", "person", "tool", "doc", "paper", "feed", "repo", "list", "other"];
7
+ export declare const Source: z.ZodObject<{
8
+ type: z.ZodLiteral<"source">;
9
+ kind: z.ZodEnum<{
10
+ site: "site";
11
+ page: "page";
12
+ video: "video";
13
+ reel: "reel";
14
+ post: "post";
15
+ person: "person";
16
+ tool: "tool";
17
+ doc: "doc";
18
+ paper: "paper";
19
+ feed: "feed";
20
+ repo: "repo";
21
+ list: "list";
22
+ other: "other";
23
+ }>;
24
+ url: z.ZodOptional<z.ZodURL>;
25
+ handle: z.ZodOptional<z.ZodString>;
26
+ priority: z.ZodOptional<z.ZodNumber>;
27
+ media: z.ZodOptional<z.ZodObject<{
28
+ sha256: z.ZodString;
29
+ type: z.ZodString;
30
+ }, z.core.$strip>>;
31
+ id: z.ZodString;
32
+ title: z.ZodString;
33
+ status: z.ZodEnum<{
34
+ live: "live";
35
+ retired: "retired";
36
+ }>;
37
+ version: z.ZodNumber;
38
+ by: z.ZodString;
39
+ why: z.ZodString;
40
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
41
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
42
+ created: z.ZodPreprocess<z.ZodString, unknown>;
43
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
44
+ }, z.core.$loose>;
45
+ export declare const Finding: z.ZodObject<{
46
+ type: z.ZodLiteral<"finding">;
47
+ source: z.ZodString;
48
+ quote: z.ZodString;
49
+ page: z.ZodOptional<z.ZodURL>;
50
+ at: z.ZodOptional<z.ZodPreprocess<z.ZodString, unknown>>;
51
+ run: z.ZodOptional<z.ZodString>;
52
+ id: z.ZodString;
53
+ title: z.ZodString;
54
+ status: z.ZodEnum<{
55
+ live: "live";
56
+ retired: "retired";
57
+ }>;
58
+ version: z.ZodNumber;
59
+ by: z.ZodString;
60
+ why: z.ZodString;
61
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
62
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
63
+ created: z.ZodPreprocess<z.ZodString, unknown>;
64
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
65
+ }, z.core.$loose>;
66
+ export declare const Verdict: z.ZodObject<{
67
+ type: z.ZodLiteral<"verdict">;
68
+ from: z.ZodEnum<{
69
+ other: "other";
70
+ vireo: "vireo";
71
+ orrery: "orrery";
72
+ owner: "owner";
73
+ }>;
74
+ ref: z.ZodString;
75
+ score: z.ZodOptional<z.ZodNumber>;
76
+ id: z.ZodString;
77
+ title: z.ZodString;
78
+ status: z.ZodEnum<{
79
+ live: "live";
80
+ retired: "retired";
81
+ }>;
82
+ version: z.ZodNumber;
83
+ by: z.ZodString;
84
+ why: z.ZodString;
85
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
86
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
87
+ created: z.ZodPreprocess<z.ZodString, unknown>;
88
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
89
+ }, z.core.$loose>;
90
+ export declare const Rule: z.ZodObject<{
91
+ type: z.ZodLiteral<"rule">;
92
+ topic: z.ZodString;
93
+ order: z.ZodOptional<z.ZodNumber>;
94
+ id: z.ZodString;
95
+ title: z.ZodString;
96
+ status: z.ZodEnum<{
97
+ live: "live";
98
+ retired: "retired";
99
+ }>;
100
+ version: z.ZodNumber;
101
+ by: z.ZodString;
102
+ why: z.ZodString;
103
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
104
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
105
+ created: z.ZodPreprocess<z.ZodString, unknown>;
106
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
107
+ }, z.core.$loose>;
108
+ export declare const Frontmatter: z.ZodDiscriminatedUnion<[z.ZodObject<{
109
+ type: z.ZodLiteral<"source">;
110
+ kind: z.ZodEnum<{
111
+ site: "site";
112
+ page: "page";
113
+ video: "video";
114
+ reel: "reel";
115
+ post: "post";
116
+ person: "person";
117
+ tool: "tool";
118
+ doc: "doc";
119
+ paper: "paper";
120
+ feed: "feed";
121
+ repo: "repo";
122
+ list: "list";
123
+ other: "other";
124
+ }>;
125
+ url: z.ZodOptional<z.ZodURL>;
126
+ handle: z.ZodOptional<z.ZodString>;
127
+ priority: z.ZodOptional<z.ZodNumber>;
128
+ media: z.ZodOptional<z.ZodObject<{
129
+ sha256: z.ZodString;
130
+ type: z.ZodString;
131
+ }, z.core.$strip>>;
132
+ id: z.ZodString;
133
+ title: z.ZodString;
134
+ status: z.ZodEnum<{
135
+ live: "live";
136
+ retired: "retired";
137
+ }>;
138
+ version: z.ZodNumber;
139
+ by: z.ZodString;
140
+ why: z.ZodString;
141
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
142
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
143
+ created: z.ZodPreprocess<z.ZodString, unknown>;
144
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
145
+ }, z.core.$loose>, z.ZodObject<{
146
+ type: z.ZodLiteral<"finding">;
147
+ source: z.ZodString;
148
+ quote: z.ZodString;
149
+ page: z.ZodOptional<z.ZodURL>;
150
+ at: z.ZodOptional<z.ZodPreprocess<z.ZodString, unknown>>;
151
+ run: z.ZodOptional<z.ZodString>;
152
+ id: z.ZodString;
153
+ title: z.ZodString;
154
+ status: z.ZodEnum<{
155
+ live: "live";
156
+ retired: "retired";
157
+ }>;
158
+ version: z.ZodNumber;
159
+ by: z.ZodString;
160
+ why: z.ZodString;
161
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
162
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
163
+ created: z.ZodPreprocess<z.ZodString, unknown>;
164
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
165
+ }, z.core.$loose>, z.ZodObject<{
166
+ type: z.ZodLiteral<"verdict">;
167
+ from: z.ZodEnum<{
168
+ other: "other";
169
+ vireo: "vireo";
170
+ orrery: "orrery";
171
+ owner: "owner";
172
+ }>;
173
+ ref: z.ZodString;
174
+ score: z.ZodOptional<z.ZodNumber>;
175
+ id: z.ZodString;
176
+ title: z.ZodString;
177
+ status: z.ZodEnum<{
178
+ live: "live";
179
+ retired: "retired";
180
+ }>;
181
+ version: z.ZodNumber;
182
+ by: z.ZodString;
183
+ why: z.ZodString;
184
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
185
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
186
+ created: z.ZodPreprocess<z.ZodString, unknown>;
187
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
188
+ }, z.core.$loose>, z.ZodObject<{
189
+ type: z.ZodLiteral<"rule">;
190
+ topic: z.ZodString;
191
+ order: z.ZodOptional<z.ZodNumber>;
192
+ id: z.ZodString;
193
+ title: z.ZodString;
194
+ status: z.ZodEnum<{
195
+ live: "live";
196
+ retired: "retired";
197
+ }>;
198
+ version: z.ZodNumber;
199
+ by: z.ZodString;
200
+ why: z.ZodString;
201
+ cites: z.ZodDefault<z.ZodArray<z.ZodString>>;
202
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
203
+ created: z.ZodPreprocess<z.ZodString, unknown>;
204
+ updated: z.ZodPreprocess<z.ZodString, unknown>;
205
+ }, z.core.$loose>], "type">;
206
+ export type Frontmatter = z.infer<typeof Frontmatter>;
207
+ export interface Doc {
208
+ fm: Frontmatter;
209
+ body: string;
210
+ }
211
+ /** Where a record lives in the repo. A rule sits under its topic. */
212
+ export declare function pathOf(fm: Pick<Frontmatter, "type" | "id"> & {
213
+ topic?: unknown;
214
+ }): string;
215
+ /** The ids a record leans on: its cites, and a finding's source. */
216
+ export declare function edgesOf(fm: Frontmatter): string[];
217
+ export declare function newId(title: string): string;
218
+ export type Parsed = {
219
+ ok: true;
220
+ doc: Doc;
221
+ } | {
222
+ ok: false;
223
+ error: string;
224
+ };
225
+ export declare function parse(text: string): Parsed;
226
+ export declare function serialize(doc: Doc): string;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * A record: one markdown file, frontmatter + body. Design: wren/designs/2026-11-09-knowledge-repo.md.
3
+ * Unknown frontmatter keys are kept (never drop data); key order on write is fixed, so diffs stay small.
4
+ */
5
+ import { randomBytes } from "node:crypto";
6
+ import matter from "gray-matter";
7
+ import { z } from "zod";
8
+ export const TYPES = ["source", "finding", "verdict", "rule"];
9
+ export const DIRS = {
10
+ source: "sources",
11
+ finding: "findings",
12
+ verdict: "verdicts",
13
+ rule: "rules",
14
+ };
15
+ // readable ids: a source keeps its natural one (youtube-uSTGNHGFOAo); new ones are slug + random tail
16
+ export const Id = z
17
+ .string()
18
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/, "id: letters, digits, . _ -");
19
+ // YAML reads a bare timestamp as a Date; every stamp is stored as a full UTC ISO string
20
+ const Stamp = z.preprocess((v) => (v instanceof Date ? v.toISOString() : v), z.string().refine((s) => !Number.isNaN(Date.parse(s)), "not a date"));
21
+ const Text = z.preprocess((v) => (typeof v === "number" ? String(v) : v), z.string());
22
+ const base = {
23
+ id: Id,
24
+ title: z.string().min(1),
25
+ status: z.enum(["live", "retired"]),
26
+ version: z.number().int().positive(),
27
+ by: z.string().min(1),
28
+ why: z.string().min(1),
29
+ cites: z.array(Id).default([]),
30
+ tags: z.array(z.string()).default([]),
31
+ created: Stamp,
32
+ updated: Stamp,
33
+ };
34
+ export const SOURCE_KINDS = [
35
+ "site",
36
+ "page",
37
+ "video",
38
+ "reel",
39
+ "post",
40
+ "person",
41
+ "tool",
42
+ "doc",
43
+ "paper",
44
+ "feed",
45
+ "repo",
46
+ "list",
47
+ "other",
48
+ ];
49
+ export const Source = z.looseObject({
50
+ ...base,
51
+ type: z.literal("source"),
52
+ kind: z.enum(SOURCE_KINDS),
53
+ url: z.url().optional(),
54
+ handle: z.string().optional(),
55
+ // 1 (low) to 9: how much a build leans on it
56
+ priority: z.number().int().min(1).max(9).optional(),
57
+ // a file in the media bucket, by content hash
58
+ media: z.object({ sha256: z.string().regex(/^[0-9a-f]{64}$/), type: z.string() }).optional(),
59
+ });
60
+ export const Finding = z.looseObject({
61
+ ...base,
62
+ type: z.literal("finding"),
63
+ source: Id,
64
+ quote: z.string().min(1),
65
+ page: z.url().optional(),
66
+ // where in the source: a time (0:16), a page, a slide
67
+ at: Text.optional(),
68
+ run: z.string().optional(),
69
+ });
70
+ export const Verdict = z.looseObject({
71
+ ...base,
72
+ type: z.literal("verdict"),
73
+ from: z.enum(["vireo", "orrery", "owner", "other"]),
74
+ // the producer's own handle: a Vireo batch ref (hero:3), a lane id
75
+ ref: z.string().min(1),
76
+ score: z.number().optional(),
77
+ });
78
+ export const Rule = z.looseObject({
79
+ ...base,
80
+ type: z.literal("rule"),
81
+ topic: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, "topic: lowercase slug"),
82
+ order: z.number().optional(),
83
+ });
84
+ export const Frontmatter = z.discriminatedUnion("type", [Source, Finding, Verdict, Rule]);
85
+ /** Where a record lives in the repo. A rule sits under its topic. */
86
+ export function pathOf(fm) {
87
+ const dir = DIRS[fm.type];
88
+ return fm.type === "rule" ? `${dir}/${String(fm.topic)}/${fm.id}.md` : `${dir}/${fm.id}.md`;
89
+ }
90
+ /** The ids a record leans on: its cites, and a finding's source. */
91
+ export function edgesOf(fm) {
92
+ return [...new Set(fm.type === "finding" ? [...fm.cites, fm.source] : fm.cites)];
93
+ }
94
+ const B32 = "0123456789abcdefghjkmnpqrstvwxyz";
95
+ export function newId(title) {
96
+ const slug = title
97
+ .toLowerCase()
98
+ .normalize("NFKD")
99
+ .replace(/[^a-z0-9]+/g, "-")
100
+ .replace(/^-+|-+$/g, "")
101
+ .slice(0, 40)
102
+ .replace(/-+$/, "");
103
+ const tail = [...randomBytes(6)].map((b) => B32[b % 32]).join("");
104
+ return slug ? `${slug}-${tail}` : tail;
105
+ }
106
+ export function parse(text) {
107
+ let m;
108
+ try {
109
+ // options object: gray-matter caches option-less calls by text and hands back one shared object
110
+ m = matter(text, {});
111
+ }
112
+ catch (e) {
113
+ return { ok: false, error: `frontmatter: ${e.message.split("\n")[0]}` };
114
+ }
115
+ const r = Frontmatter.safeParse(m.data);
116
+ if (!r.success)
117
+ return {
118
+ ok: false,
119
+ error: r.error.issues
120
+ .map((i) => `${i.path.join(".") || "frontmatter"}: ${i.message}`)
121
+ .join("; "),
122
+ };
123
+ const fm = r.data;
124
+ fm.created = new Date(fm.created).toISOString();
125
+ fm.updated = new Date(fm.updated).toISOString();
126
+ return { ok: true, doc: { fm, body: m.content.replace(/^\n+/, "").replace(/\s+$/, "") } };
127
+ }
128
+ const HEAD = ["id", "type", "title", "status", "version", "by", "why", "cites", "tags"];
129
+ const TAIL = ["created", "updated"];
130
+ export function serialize(doc) {
131
+ const fm = doc.fm;
132
+ const out = {};
133
+ const put = (k) => {
134
+ const v = fm[k];
135
+ if (v === undefined || (Array.isArray(v) && v.length === 0))
136
+ return;
137
+ out[k] = v;
138
+ };
139
+ for (const k of HEAD)
140
+ put(k);
141
+ for (const k of Object.keys(fm)
142
+ .filter((k) => !HEAD.includes(k) && !TAIL.includes(k))
143
+ .sort())
144
+ put(k);
145
+ for (const k of TAIL)
146
+ put(k);
147
+ return matter.stringify(doc.body ? `\n${doc.body}\n` : "", out);
148
+ }
149
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../src/records.ts"],"names":[],"mappings":"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"}