@jamesaphoenix/tx-core 0.4.4 → 0.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/dist/db.d.ts +4 -9
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +6 -80
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +67 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +44 -10
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -28
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +22 -10
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +97 -39
- package/dist/layer.js.map +1 -1
- package/dist/mappers/anchor.d.ts +28 -0
- package/dist/mappers/anchor.d.ts.map +1 -0
- package/dist/mappers/anchor.js +105 -0
- package/dist/mappers/anchor.js.map +1 -0
- package/dist/mappers/candidate.d.ts +25 -0
- package/dist/mappers/candidate.d.ts.map +1 -0
- package/dist/mappers/candidate.js +83 -0
- package/dist/mappers/candidate.js.map +1 -0
- package/dist/mappers/doc.d.ts +2 -4
- package/dist/mappers/doc.d.ts.map +1 -1
- package/dist/mappers/doc.js +7 -4
- package/dist/mappers/doc.js.map +1 -1
- package/dist/mappers/edge.d.ts +19 -0
- package/dist/mappers/edge.d.ts.map +1 -0
- package/dist/mappers/edge.js +81 -0
- package/dist/mappers/edge.js.map +1 -0
- package/dist/mappers/index.d.ts +7 -3
- package/dist/mappers/index.d.ts.map +1 -1
- package/dist/mappers/index.js +14 -6
- package/dist/mappers/index.js.map +1 -1
- package/dist/mappers/message.d.ts +15 -0
- package/dist/mappers/message.d.ts.map +1 -0
- package/dist/mappers/message.js +58 -0
- package/dist/mappers/message.js.map +1 -0
- package/dist/repo/anchor-repo.d.ts +52 -0
- package/dist/repo/anchor-repo.d.ts.map +1 -0
- package/dist/repo/anchor-repo.js +245 -0
- package/dist/repo/anchor-repo.js.map +1 -0
- package/dist/repo/candidate-repo.d.ts +16 -0
- package/dist/repo/candidate-repo.d.ts.map +1 -0
- package/dist/repo/candidate-repo.js +164 -0
- package/dist/repo/candidate-repo.js.map +1 -0
- package/dist/repo/compaction-repo.d.ts +41 -0
- package/dist/repo/compaction-repo.d.ts.map +1 -0
- package/dist/repo/compaction-repo.js +84 -0
- package/dist/repo/compaction-repo.js.map +1 -0
- package/dist/repo/doc-repo.d.ts +68 -51
- package/dist/repo/doc-repo.d.ts.map +1 -1
- package/dist/repo/doc-repo.js +120 -54
- package/dist/repo/doc-repo.js.map +1 -1
- package/dist/repo/edge-repo.d.ts +26 -0
- package/dist/repo/edge-repo.d.ts.map +1 -0
- package/dist/repo/edge-repo.js +258 -0
- package/dist/repo/edge-repo.js.map +1 -0
- package/dist/repo/index.d.ts +8 -3
- package/dist/repo/index.d.ts.map +1 -1
- package/dist/repo/index.js +7 -2
- package/dist/repo/index.js.map +1 -1
- package/dist/repo/message-repo.d.ts +55 -0
- package/dist/repo/message-repo.d.ts.map +1 -0
- package/dist/repo/message-repo.js +132 -0
- package/dist/repo/message-repo.js.map +1 -0
- package/dist/services/agent-service.d.ts +18 -23
- package/dist/services/agent-service.d.ts.map +1 -1
- package/dist/services/agent-service.js +9 -0
- package/dist/services/agent-service.js.map +1 -1
- package/dist/services/anchor-service.d.ts +147 -0
- package/dist/services/anchor-service.d.ts.map +1 -0
- package/dist/services/anchor-service.js +540 -0
- package/dist/services/anchor-service.js.map +1 -0
- package/dist/services/anchor-verification.d.ts +102 -0
- package/dist/services/anchor-verification.d.ts.map +1 -0
- package/dist/services/anchor-verification.js +817 -0
- package/dist/services/anchor-verification.js.map +1 -0
- package/dist/services/ast-grep-service.d.ts +58 -0
- package/dist/services/ast-grep-service.d.ts.map +1 -0
- package/dist/services/ast-grep-service.js +427 -0
- package/dist/services/ast-grep-service.js.map +1 -0
- package/dist/services/attempt-service.d.ts.map +1 -1
- package/dist/services/attempt-service.js +4 -1
- package/dist/services/attempt-service.js.map +1 -1
- package/dist/services/auto-sync-service.d.ts.map +1 -1
- package/dist/services/auto-sync-service.js +7 -7
- package/dist/services/auto-sync-service.js.map +1 -1
- package/dist/services/candidate-extractor-service.d.ts +44 -0
- package/dist/services/candidate-extractor-service.d.ts.map +1 -0
- package/dist/services/candidate-extractor-service.js +175 -0
- package/dist/services/candidate-extractor-service.js.map +1 -0
- package/dist/services/claim-service.d.ts.map +1 -1
- package/dist/services/claim-service.js +0 -8
- package/dist/services/claim-service.js.map +1 -1
- package/dist/services/compaction-service.d.ts +105 -0
- package/dist/services/compaction-service.d.ts.map +1 -0
- package/dist/services/compaction-service.js +281 -0
- package/dist/services/compaction-service.js.map +1 -0
- package/dist/services/cycle-scan-service.d.ts +1 -5
- package/dist/services/cycle-scan-service.d.ts.map +1 -1
- package/dist/services/cycle-scan-service.js +49 -19
- package/dist/services/cycle-scan-service.js.map +1 -1
- package/dist/services/daemon-service.d.ts +2 -8
- package/dist/services/daemon-service.d.ts.map +1 -1
- package/dist/services/daemon-service.js +21 -35
- package/dist/services/daemon-service.js.map +1 -1
- package/dist/services/doc-service.d.ts +25 -32
- package/dist/services/doc-service.d.ts.map +1 -1
- package/dist/services/doc-service.js +206 -190
- package/dist/services/doc-service.js.map +1 -1
- package/dist/services/edge-service.d.ts +78 -0
- package/dist/services/edge-service.d.ts.map +1 -0
- package/dist/services/edge-service.js +158 -0
- package/dist/services/edge-service.js.map +1 -0
- package/dist/services/embedding-service.d.ts +2 -2
- package/dist/services/embedding-service.d.ts.map +1 -1
- package/dist/services/embedding-service.js +7 -13
- package/dist/services/embedding-service.js.map +1 -1
- package/dist/services/feedback-tracker.d.ts +64 -0
- package/dist/services/feedback-tracker.d.ts.map +1 -0
- package/dist/services/feedback-tracker.js +110 -0
- package/dist/services/feedback-tracker.js.map +1 -0
- package/dist/services/file-watcher-service.d.ts.map +1 -1
- package/dist/services/file-watcher-service.js +1 -3
- package/dist/services/file-watcher-service.js.map +1 -1
- package/dist/services/graph-expansion.d.ts +158 -0
- package/dist/services/graph-expansion.d.ts.map +1 -0
- package/dist/services/graph-expansion.js +487 -0
- package/dist/services/graph-expansion.js.map +1 -0
- package/dist/services/index.d.ts +19 -8
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +18 -7
- package/dist/services/index.js.map +1 -1
- package/dist/services/learning-service.d.ts.map +1 -1
- package/dist/services/learning-service.js +22 -14
- package/dist/services/learning-service.js.map +1 -1
- package/dist/services/llm-service.d.ts +61 -39
- package/dist/services/llm-service.d.ts.map +1 -1
- package/dist/services/llm-service.js +199 -113
- package/dist/services/llm-service.js.map +1 -1
- package/dist/services/message-service.d.ts +57 -0
- package/dist/services/message-service.d.ts.map +1 -0
- package/dist/services/message-service.js +78 -0
- package/dist/services/message-service.js.map +1 -0
- package/dist/services/orchestrator-service.d.ts.map +1 -1
- package/dist/services/orchestrator-service.js +19 -20
- package/dist/services/orchestrator-service.js.map +1 -1
- package/dist/services/promotion-service.d.ts +67 -0
- package/dist/services/promotion-service.d.ts.map +1 -0
- package/dist/services/promotion-service.js +151 -0
- package/dist/services/promotion-service.js.map +1 -0
- package/dist/services/query-expansion-service.d.ts +7 -22
- package/dist/services/query-expansion-service.d.ts.map +1 -1
- package/dist/services/query-expansion-service.js +41 -75
- package/dist/services/query-expansion-service.js.map +1 -1
- package/dist/services/retriever-service.d.ts +8 -5
- package/dist/services/retriever-service.d.ts.map +1 -1
- package/dist/services/retriever-service.js +150 -15
- package/dist/services/retriever-service.js.map +1 -1
- package/dist/services/swarm-verification.d.ts +104 -0
- package/dist/services/swarm-verification.d.ts.map +1 -0
- package/dist/services/swarm-verification.js +406 -0
- package/dist/services/swarm-verification.js.map +1 -0
- package/dist/services/sync-service.d.ts.map +1 -1
- package/dist/services/sync-service.js +8 -9
- package/dist/services/sync-service.js.map +1 -1
- package/dist/services/task-service.d.ts.map +1 -1
- package/dist/services/task-service.js +3 -8
- package/dist/services/task-service.js.map +1 -1
- package/dist/services/tracing-service.js +6 -6
- package/dist/services/tracing-service.js.map +1 -1
- package/dist/services/transcript-adapter.d.ts.map +1 -1
- package/dist/services/transcript-adapter.js +1 -1
- package/dist/services/transcript-adapter.js.map +1 -1
- package/dist/services/worker-process.d.ts.map +1 -1
- package/dist/services/worker-process.js +8 -30
- package/dist/services/worker-process.js.map +1 -1
- package/dist/utils/doc-hash.d.ts +0 -4
- package/dist/utils/doc-hash.d.ts.map +1 -1
- package/dist/utils/doc-hash.js.map +1 -1
- package/dist/utils/doc-renderer.d.ts +31 -26
- package/dist/utils/doc-renderer.d.ts.map +1 -1
- package/dist/utils/doc-renderer.js +0 -7
- package/dist/utils/doc-renderer.js.map +1 -1
- package/dist/utils/llm-json.d.ts +17 -0
- package/dist/utils/llm-json.d.ts.map +1 -0
- package/dist/utils/llm-json.js +51 -0
- package/dist/utils/llm-json.js.map +1 -0
- package/dist/utils/toml-config.d.ts +10 -16
- package/dist/utils/toml-config.d.ts.map +1 -1
- package/dist/utils/toml-config.js +3 -1
- package/dist/utils/toml-config.js.map +1 -1
- package/dist/worker/run-worker.d.ts.map +1 -1
- package/dist/worker/run-worker.js +2 -7
- package/dist/worker/run-worker.js.map +1 -1
- package/package.json +9 -8
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
*
|
|
7
7
|
* YAML content lives on disk (.tx/docs/); DB stores metadata + links only.
|
|
8
8
|
*/
|
|
9
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "node:fs";
|
|
9
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync, } from "node:fs";
|
|
10
10
|
import { resolve, dirname, join } from "node:path";
|
|
11
11
|
import { Context, Effect, Layer } from "effect";
|
|
12
12
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
13
13
|
import { DocRepository } from "../repo/doc-repo.js";
|
|
14
14
|
import { ValidationError, DocNotFoundError, DocLockedError, InvalidDocYamlError, InvariantNotFoundError, } from "../errors.js";
|
|
15
15
|
import { computeDocHash } from "../utils/doc-hash.js";
|
|
16
|
-
import { renderDocToMarkdown, renderIndexToMarkdown } from "../utils/doc-renderer.js";
|
|
16
|
+
import { renderDocToMarkdown, renderIndexToMarkdown, } from "../utils/doc-renderer.js";
|
|
17
17
|
import { readTxConfig } from "../utils/toml-config.js";
|
|
18
18
|
import { DOC_KINDS, INVARIANT_ENFORCEMENT_TYPES, } from "@jamesaphoenix/tx-types";
|
|
19
19
|
// Local string arrays for .includes() (avoids readonly cast)
|
|
@@ -38,12 +38,16 @@ const kindSubdir = (kind) => {
|
|
|
38
38
|
/** Resolve the YAML file path for a doc. */
|
|
39
39
|
const resolveYamlPath = (docsPath, kind, name) => {
|
|
40
40
|
const sub = kindSubdir(kind);
|
|
41
|
-
return sub
|
|
41
|
+
return sub
|
|
42
|
+
? resolve(docsPath, sub, `${name}.yml`)
|
|
43
|
+
: resolve(docsPath, `${name}.yml`);
|
|
42
44
|
};
|
|
43
45
|
/** Resolve the MD file path for a doc. */
|
|
44
46
|
const resolveMdPath = (docsPath, kind, name) => {
|
|
45
47
|
const sub = kindSubdir(kind);
|
|
46
|
-
return sub
|
|
48
|
+
return sub
|
|
49
|
+
? resolve(docsPath, sub, `${name}.md`)
|
|
50
|
+
: resolve(docsPath, `${name}.md`);
|
|
47
51
|
};
|
|
48
52
|
/** Validate YAML content and return parsed object. */
|
|
49
53
|
const validateYaml = (name, content) => {
|
|
@@ -52,10 +56,16 @@ const validateYaml = (name, content) => {
|
|
|
52
56
|
parsed = parseYaml(content);
|
|
53
57
|
}
|
|
54
58
|
catch (e) {
|
|
55
|
-
throw new InvalidDocYamlError({
|
|
59
|
+
throw new InvalidDocYamlError({
|
|
60
|
+
name,
|
|
61
|
+
reason: `YAML parse error: ${String(e)}`,
|
|
62
|
+
});
|
|
56
63
|
}
|
|
57
64
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
58
|
-
throw new InvalidDocYamlError({
|
|
65
|
+
throw new InvalidDocYamlError({
|
|
66
|
+
name,
|
|
67
|
+
reason: "YAML must be an object (not array or scalar)",
|
|
68
|
+
});
|
|
59
69
|
}
|
|
60
70
|
return parsed;
|
|
61
71
|
};
|
|
@@ -63,7 +73,10 @@ const validateYaml = (name, content) => {
|
|
|
63
73
|
const validateKind = (name, parsed, expectedKind) => {
|
|
64
74
|
const yamlKind = parsed.kind;
|
|
65
75
|
if (yamlKind && typeof yamlKind === "string" && yamlKind !== expectedKind) {
|
|
66
|
-
throw new InvalidDocYamlError({
|
|
76
|
+
throw new InvalidDocYamlError({
|
|
77
|
+
name,
|
|
78
|
+
reason: `YAML kind '${yamlKind}' does not match expected kind '${expectedKind}'`,
|
|
79
|
+
});
|
|
67
80
|
}
|
|
68
81
|
};
|
|
69
82
|
export class DocService extends Context.Tag("DocService")() {
|
|
@@ -94,36 +107,174 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
94
107
|
writeFileSync(mdPath, md, "utf8");
|
|
95
108
|
return mdPath;
|
|
96
109
|
};
|
|
110
|
+
/** Generate index.yml and index.md from all docs in DB. */
|
|
111
|
+
function generateIndexEffect(docsPath) {
|
|
112
|
+
return Effect.gen(function* () {
|
|
113
|
+
const allDocs = yield* docRepo.findAll();
|
|
114
|
+
const allLinks = yield* docRepo.getAllLinks();
|
|
115
|
+
const overviewDoc = allDocs.find((d) => d.kind === "overview");
|
|
116
|
+
const prds = allDocs
|
|
117
|
+
.filter((d) => d.kind === "prd")
|
|
118
|
+
.map((d) => ({ name: d.name, title: d.title, status: d.status }));
|
|
119
|
+
const designDocs = allDocs
|
|
120
|
+
.filter((d) => d.kind === "design")
|
|
121
|
+
.map((d) => {
|
|
122
|
+
const implLink = allLinks.find((l) => l.toDocId === d.id && l.linkType === "prd_to_design");
|
|
123
|
+
const implDoc = implLink
|
|
124
|
+
? allDocs.find((dd) => dd.id === implLink.fromDocId)
|
|
125
|
+
: undefined;
|
|
126
|
+
return {
|
|
127
|
+
name: d.name,
|
|
128
|
+
title: d.title,
|
|
129
|
+
status: d.status,
|
|
130
|
+
implements: implDoc?.name,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
const links = allLinks.map((l) => {
|
|
134
|
+
const from = allDocs.find((d) => d.id === l.fromDocId);
|
|
135
|
+
const to = allDocs.find((d) => d.id === l.toDocId);
|
|
136
|
+
return {
|
|
137
|
+
from: from?.name ?? String(l.fromDocId),
|
|
138
|
+
to: to?.name ?? String(l.toDocId),
|
|
139
|
+
type: l.linkType,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
// Invariant summary
|
|
143
|
+
const allInvariants = yield* docRepo.findInvariants();
|
|
144
|
+
const activeInvariants = allInvariants.filter((i) => i.status === "active");
|
|
145
|
+
const byEnforcement = {};
|
|
146
|
+
const bySubsystem = {};
|
|
147
|
+
for (const inv of activeInvariants) {
|
|
148
|
+
byEnforcement[inv.enforcement] =
|
|
149
|
+
(byEnforcement[inv.enforcement] ?? 0) + 1;
|
|
150
|
+
const sub = inv.subsystem ?? "system";
|
|
151
|
+
bySubsystem[sub] = (bySubsystem[sub] ?? 0) + 1;
|
|
152
|
+
}
|
|
153
|
+
const indexData = {
|
|
154
|
+
overview: overviewDoc?.name,
|
|
155
|
+
prds,
|
|
156
|
+
design_docs: designDocs,
|
|
157
|
+
links,
|
|
158
|
+
invariant_summary: activeInvariants.length > 0
|
|
159
|
+
? {
|
|
160
|
+
total: activeInvariants.length,
|
|
161
|
+
by_enforcement: byEnforcement,
|
|
162
|
+
by_subsystem: bySubsystem,
|
|
163
|
+
}
|
|
164
|
+
: undefined,
|
|
165
|
+
};
|
|
166
|
+
// Write index.yml
|
|
167
|
+
const indexYamlObj = {
|
|
168
|
+
generated: true,
|
|
169
|
+
generated_at: new Date().toISOString(),
|
|
170
|
+
};
|
|
171
|
+
if (indexData.overview) {
|
|
172
|
+
indexYamlObj.overview = indexData.overview;
|
|
173
|
+
}
|
|
174
|
+
if (prds.length > 0) {
|
|
175
|
+
indexYamlObj.prds = prds.map((p) => ({
|
|
176
|
+
name: p.name,
|
|
177
|
+
title: p.title,
|
|
178
|
+
status: p.status,
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
if (designDocs.length > 0) {
|
|
182
|
+
indexYamlObj.design_docs = designDocs.map((dd) => {
|
|
183
|
+
const entry = {
|
|
184
|
+
name: dd.name,
|
|
185
|
+
title: dd.title,
|
|
186
|
+
status: dd.status,
|
|
187
|
+
};
|
|
188
|
+
if (dd.implements)
|
|
189
|
+
entry.implements = dd.implements;
|
|
190
|
+
return entry;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
const indexYamlPath = resolve(docsPath, "index.yml");
|
|
194
|
+
ensureDir(indexYamlPath);
|
|
195
|
+
writeFileSync(indexYamlPath, stringifyYaml(indexYamlObj), "utf8");
|
|
196
|
+
// Write index.md
|
|
197
|
+
const indexMd = renderIndexToMarkdown(indexData);
|
|
198
|
+
const indexMdPath = resolve(docsPath, "index.md");
|
|
199
|
+
writeFileSync(indexMdPath, indexMd, "utf8");
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/** Sync invariants from a single doc's YAML into DB. */
|
|
203
|
+
function syncInvariantsForDoc(doc) {
|
|
204
|
+
return Effect.gen(function* () {
|
|
205
|
+
const docsPath = getDocsPath();
|
|
206
|
+
const yamlPath = resolveYamlPath(docsPath, doc.kind, doc.name);
|
|
207
|
+
if (!existsSync(yamlPath)) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
const yamlContent = readFileSync(yamlPath, "utf8");
|
|
211
|
+
const parsed = validateYaml(doc.name, yamlContent);
|
|
212
|
+
const invariantsRaw = parsed.invariants;
|
|
213
|
+
if (!Array.isArray(invariantsRaw) || invariantsRaw.length === 0) {
|
|
214
|
+
yield* docRepo.deprecateInvariantsNotIn(doc.id, []);
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
const synced = [];
|
|
218
|
+
const activeIds = [];
|
|
219
|
+
for (const raw of invariantsRaw) {
|
|
220
|
+
if (typeof raw !== "object" || raw === null)
|
|
221
|
+
continue;
|
|
222
|
+
const inv = raw;
|
|
223
|
+
const id = typeof inv.id === "string" ? inv.id : null;
|
|
224
|
+
const rule = typeof inv.rule === "string" ? inv.rule : null;
|
|
225
|
+
const enforcement = typeof inv.enforcement === "string" ? inv.enforcement : null;
|
|
226
|
+
if (!id || !rule || !enforcement)
|
|
227
|
+
continue;
|
|
228
|
+
if (!enforcementStrings.includes(enforcement))
|
|
229
|
+
continue;
|
|
230
|
+
const input = {
|
|
231
|
+
id,
|
|
232
|
+
rule,
|
|
233
|
+
enforcement,
|
|
234
|
+
docId: doc.id,
|
|
235
|
+
subsystem: typeof inv.subsystem === "string"
|
|
236
|
+
? inv.subsystem
|
|
237
|
+
: inv.subsystem === null
|
|
238
|
+
? null
|
|
239
|
+
: undefined,
|
|
240
|
+
testRef: typeof inv.test_ref === "string" ? inv.test_ref : undefined,
|
|
241
|
+
lintRule: typeof inv.lint_rule === "string" ? inv.lint_rule : undefined,
|
|
242
|
+
promptRef: typeof inv.prompt_ref === "string" ? inv.prompt_ref : undefined,
|
|
243
|
+
};
|
|
244
|
+
const result = yield* docRepo.upsertInvariant(input);
|
|
245
|
+
synced.push(result);
|
|
246
|
+
activeIds.push(id);
|
|
247
|
+
}
|
|
248
|
+
yield* docRepo.deprecateInvariantsNotIn(doc.id, activeIds);
|
|
249
|
+
return synced;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
97
252
|
return {
|
|
98
253
|
create: (input) => Effect.gen(function* () {
|
|
99
254
|
const { kind, name, title, yamlContent, metadata } = input;
|
|
100
|
-
// Validate kind
|
|
101
255
|
if (!docKindStrings.includes(kind)) {
|
|
102
256
|
return yield* Effect.fail(new ValidationError({ reason: `Invalid doc kind: ${kind}` }));
|
|
103
257
|
}
|
|
104
|
-
// Validate name (no spaces, lowercase with dashes)
|
|
105
258
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
|
|
106
|
-
return yield* Effect.fail(new ValidationError({
|
|
259
|
+
return yield* Effect.fail(new ValidationError({
|
|
260
|
+
reason: `Invalid doc name: ${name}. Use alphanumeric with dashes/dots.`,
|
|
261
|
+
}));
|
|
107
262
|
}
|
|
108
|
-
// Validate YAML
|
|
109
263
|
const parsed = validateYaml(name, yamlContent);
|
|
110
264
|
validateKind(name, parsed, kind);
|
|
111
|
-
// Check uniqueness
|
|
112
265
|
const existing = yield* docRepo.findByName(name);
|
|
113
266
|
if (existing) {
|
|
114
|
-
return yield* Effect.fail(new ValidationError({
|
|
267
|
+
return yield* Effect.fail(new ValidationError({
|
|
268
|
+
reason: `Doc '${name}' already exists (v${existing.version})`,
|
|
269
|
+
}));
|
|
115
270
|
}
|
|
116
|
-
// Compute hash and write file
|
|
117
271
|
const hash = computeDocHash(yamlContent);
|
|
118
272
|
const docsPath = getDocsPath();
|
|
119
273
|
const filePath = resolveYamlPath(docsPath, kind, name);
|
|
120
274
|
ensureDir(filePath);
|
|
121
275
|
writeFileSync(filePath, yamlContent, "utf8");
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
? `${name}.yml`
|
|
125
|
-
: join(kind, `${name}.yml`);
|
|
126
|
-
const insertInput = {
|
|
276
|
+
const relPath = kind === "overview" ? `${name}.yml` : join(kind, `${name}.yml`);
|
|
277
|
+
const doc = yield* docRepo.insert({
|
|
127
278
|
hash,
|
|
128
279
|
kind,
|
|
129
280
|
name,
|
|
@@ -132,13 +283,13 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
132
283
|
filePath: relPath,
|
|
133
284
|
parentDocId: null,
|
|
134
285
|
metadata: metadata ? JSON.stringify(metadata) : undefined,
|
|
135
|
-
};
|
|
136
|
-
const doc = yield* docRepo.insert(insertInput);
|
|
137
|
-
// Auto-render on create
|
|
286
|
+
});
|
|
138
287
|
try {
|
|
139
288
|
renderSingleDoc(doc, docsPath);
|
|
140
289
|
}
|
|
141
|
-
catch {
|
|
290
|
+
catch {
|
|
291
|
+
/* non-fatal */
|
|
292
|
+
}
|
|
142
293
|
yield* generateIndexEffect(docsPath);
|
|
143
294
|
return doc;
|
|
144
295
|
}),
|
|
@@ -157,29 +308,25 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
157
308
|
if (doc.status === "locked") {
|
|
158
309
|
return yield* Effect.fail(new DocLockedError({ name, version: doc.version }));
|
|
159
310
|
}
|
|
160
|
-
// Validate YAML
|
|
161
311
|
const parsed = validateYaml(name, yamlContent);
|
|
162
312
|
validateKind(name, parsed, doc.kind);
|
|
163
|
-
// Compute new hash
|
|
164
313
|
const hash = computeDocHash(yamlContent);
|
|
165
|
-
// Write file
|
|
166
314
|
const docsPath = getDocsPath();
|
|
167
315
|
const filePath = resolveYamlPath(docsPath, doc.kind, name);
|
|
168
316
|
ensureDir(filePath);
|
|
169
317
|
writeFileSync(filePath, yamlContent, "utf8");
|
|
170
|
-
// Update title if changed in YAML
|
|
171
318
|
const title = typeof parsed.title === "string" ? parsed.title : doc.title;
|
|
172
319
|
yield* docRepo.update(doc.id, { hash, title });
|
|
173
|
-
// Re-fetch to return updated doc
|
|
174
320
|
const updated = yield* docRepo.findById(doc.id);
|
|
175
321
|
if (!updated) {
|
|
176
322
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
177
323
|
}
|
|
178
|
-
// Auto-render on update
|
|
179
324
|
try {
|
|
180
325
|
renderSingleDoc(updated, docsPath);
|
|
181
326
|
}
|
|
182
|
-
catch {
|
|
327
|
+
catch {
|
|
328
|
+
/* non-fatal */
|
|
329
|
+
}
|
|
183
330
|
yield* generateIndexEffect(docsPath);
|
|
184
331
|
return updated;
|
|
185
332
|
}),
|
|
@@ -189,7 +336,6 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
189
336
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
190
337
|
}
|
|
191
338
|
if (doc.status === "locked") {
|
|
192
|
-
// Already locked — return as-is
|
|
193
339
|
return doc;
|
|
194
340
|
}
|
|
195
341
|
const lockedAt = new Date().toISOString();
|
|
@@ -198,12 +344,13 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
198
344
|
if (!locked) {
|
|
199
345
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
200
346
|
}
|
|
201
|
-
// Auto-render on lock (final version)
|
|
202
347
|
const docsPath = getDocsPath();
|
|
203
348
|
try {
|
|
204
349
|
renderSingleDoc(locked, docsPath);
|
|
205
350
|
}
|
|
206
|
-
catch {
|
|
351
|
+
catch {
|
|
352
|
+
/* non-fatal */
|
|
353
|
+
}
|
|
207
354
|
yield* generateIndexEffect(docsPath);
|
|
208
355
|
return locked;
|
|
209
356
|
}),
|
|
@@ -216,9 +363,7 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
216
363
|
if (doc.status === "locked") {
|
|
217
364
|
return yield* Effect.fail(new DocLockedError({ name, version: doc.version }));
|
|
218
365
|
}
|
|
219
|
-
// Delete from DB (CASCADE handles links + invariants)
|
|
220
366
|
yield* docRepo.remove(doc.id);
|
|
221
|
-
// Remove YAML + MD files from disk
|
|
222
367
|
const docsPath = getDocsPath();
|
|
223
368
|
const yamlPath = resolveYamlPath(docsPath, doc.kind, name);
|
|
224
369
|
const mdPath = resolveMdPath(docsPath, doc.kind, name);
|
|
@@ -226,20 +371,22 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
226
371
|
if (existsSync(yamlPath))
|
|
227
372
|
unlinkSync(yamlPath);
|
|
228
373
|
}
|
|
229
|
-
catch {
|
|
374
|
+
catch {
|
|
375
|
+
/* non-fatal */
|
|
376
|
+
}
|
|
230
377
|
try {
|
|
231
378
|
if (existsSync(mdPath))
|
|
232
379
|
unlinkSync(mdPath);
|
|
233
380
|
}
|
|
234
|
-
catch {
|
|
235
|
-
|
|
381
|
+
catch {
|
|
382
|
+
/* non-fatal */
|
|
383
|
+
}
|
|
236
384
|
yield* generateIndexEffect(docsPath);
|
|
237
385
|
}),
|
|
238
386
|
render: (name) => Effect.gen(function* () {
|
|
239
387
|
const docsPath = getDocsPath();
|
|
240
388
|
const rendered = [];
|
|
241
389
|
if (name) {
|
|
242
|
-
// Render single doc
|
|
243
390
|
const doc = yield* docRepo.findByName(name);
|
|
244
391
|
if (!doc) {
|
|
245
392
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
@@ -247,18 +394,16 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
247
394
|
rendered.push(renderSingleDoc(doc, docsPath));
|
|
248
395
|
}
|
|
249
396
|
else {
|
|
250
|
-
// Render all docs
|
|
251
397
|
const allDocs = yield* docRepo.findAll();
|
|
252
398
|
for (const doc of allDocs) {
|
|
253
399
|
try {
|
|
254
400
|
rendered.push(renderSingleDoc(doc, docsPath));
|
|
255
401
|
}
|
|
256
402
|
catch {
|
|
257
|
-
|
|
403
|
+
/* skip docs with missing YAML */
|
|
258
404
|
}
|
|
259
405
|
}
|
|
260
406
|
}
|
|
261
|
-
// Always regenerate index
|
|
262
407
|
yield* generateIndexEffect(docsPath);
|
|
263
408
|
return rendered;
|
|
264
409
|
}),
|
|
@@ -268,13 +413,16 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
268
413
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
269
414
|
}
|
|
270
415
|
if (doc.status !== "locked") {
|
|
271
|
-
return yield* Effect.fail(new ValidationError({
|
|
416
|
+
return yield* Effect.fail(new ValidationError({
|
|
417
|
+
reason: `Doc '${name}' must be locked before creating a new version`,
|
|
418
|
+
}));
|
|
272
419
|
}
|
|
273
|
-
// Read existing YAML content
|
|
274
420
|
const docsPath = getDocsPath();
|
|
275
421
|
const yamlPath = resolveYamlPath(docsPath, doc.kind, name);
|
|
276
422
|
if (!existsSync(yamlPath)) {
|
|
277
|
-
return yield* Effect.fail(new ValidationError({
|
|
423
|
+
return yield* Effect.fail(new ValidationError({
|
|
424
|
+
reason: `YAML file not found for '${name}'`,
|
|
425
|
+
}));
|
|
278
426
|
}
|
|
279
427
|
const yamlContent = readFileSync(yamlPath, "utf8");
|
|
280
428
|
const hash = computeDocHash(yamlContent);
|
|
@@ -282,7 +430,7 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
282
430
|
const relPath = doc.kind === "overview"
|
|
283
431
|
? `${name}.yml`
|
|
284
432
|
: join(doc.kind, `${name}.yml`);
|
|
285
|
-
const
|
|
433
|
+
const newDoc = yield* docRepo.insert({
|
|
286
434
|
hash,
|
|
287
435
|
kind: doc.kind,
|
|
288
436
|
name,
|
|
@@ -290,13 +438,13 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
290
438
|
version: newVersion,
|
|
291
439
|
filePath: relPath,
|
|
292
440
|
parentDocId: doc.id,
|
|
293
|
-
};
|
|
294
|
-
const newDoc = yield* docRepo.insert(insertInput);
|
|
295
|
-
// Auto-render on version create
|
|
441
|
+
});
|
|
296
442
|
try {
|
|
297
443
|
renderSingleDoc(newDoc, docsPath);
|
|
298
444
|
}
|
|
299
|
-
catch {
|
|
445
|
+
catch {
|
|
446
|
+
/* non-fatal */
|
|
447
|
+
}
|
|
300
448
|
yield* generateIndexEffect(docsPath);
|
|
301
449
|
return newDoc;
|
|
302
450
|
}),
|
|
@@ -309,11 +457,10 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
309
457
|
if (!toDoc) {
|
|
310
458
|
return yield* Effect.fail(new DocNotFoundError({ name: toName }));
|
|
311
459
|
}
|
|
312
|
-
// Determine link type
|
|
313
460
|
const resolvedType = linkType ?? inferLinkType(fromDoc.kind, toDoc.kind);
|
|
314
461
|
if (!resolvedType) {
|
|
315
462
|
return yield* Effect.fail(new ValidationError({
|
|
316
|
-
reason: `Cannot infer link type from ${fromDoc.kind} → ${toDoc.kind}. Provide explicit linkType
|
|
463
|
+
reason: `Cannot infer link type from ${fromDoc.kind} → ${toDoc.kind}. Provide explicit linkType.`,
|
|
317
464
|
}));
|
|
318
465
|
}
|
|
319
466
|
return yield* docRepo.createLink(fromDoc.id, toDoc.id, resolvedType);
|
|
@@ -331,9 +478,10 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
331
478
|
return yield* Effect.fail(new DocNotFoundError({ name: designName }));
|
|
332
479
|
}
|
|
333
480
|
if (parentDoc.kind !== "design") {
|
|
334
|
-
return yield* Effect.fail(new ValidationError({
|
|
481
|
+
return yield* Effect.fail(new ValidationError({
|
|
482
|
+
reason: `Patches can only be created on design docs, got '${parentDoc.kind}'`,
|
|
483
|
+
}));
|
|
335
484
|
}
|
|
336
|
-
// Create minimal patch YAML using a proper serializer to avoid injection
|
|
337
485
|
const patchYaml = stringifyYaml({
|
|
338
486
|
kind: "design",
|
|
339
487
|
name: patchName,
|
|
@@ -358,13 +506,13 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
358
506
|
filePath: relPath,
|
|
359
507
|
parentDocId: null,
|
|
360
508
|
});
|
|
361
|
-
// Create design_patch link
|
|
362
509
|
yield* docRepo.createLink(patchDoc.id, parentDoc.id, "design_patch");
|
|
363
|
-
// Auto-render on patch create
|
|
364
510
|
try {
|
|
365
511
|
renderSingleDoc(patchDoc, docsPath);
|
|
366
512
|
}
|
|
367
|
-
catch {
|
|
513
|
+
catch {
|
|
514
|
+
/* non-fatal */
|
|
515
|
+
}
|
|
368
516
|
yield* generateIndexEffect(docsPath);
|
|
369
517
|
return patchDoc;
|
|
370
518
|
}),
|
|
@@ -382,7 +530,6 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
382
530
|
return yield* Effect.fail(new DocNotFoundError({ name }));
|
|
383
531
|
}
|
|
384
532
|
const warnings = [];
|
|
385
|
-
// Check if YAML file hash matches DB hash
|
|
386
533
|
const docsPath = getDocsPath();
|
|
387
534
|
const yamlPath = resolveYamlPath(docsPath, doc.kind, name);
|
|
388
535
|
if (existsSync(yamlPath)) {
|
|
@@ -395,7 +542,6 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
395
542
|
else {
|
|
396
543
|
warnings.push(`YAML file missing: ${yamlPath}`);
|
|
397
544
|
}
|
|
398
|
-
// Check tasks linked to this doc
|
|
399
545
|
const taskLinks = yield* docRepo.getTaskLinksForDoc(doc.id);
|
|
400
546
|
if (taskLinks.length === 0 && doc.kind === "design") {
|
|
401
547
|
warnings.push(`Design doc '${name}' has no linked tasks`);
|
|
@@ -435,7 +581,6 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
435
581
|
const allLinks = yield* docRepo.getAllLinks();
|
|
436
582
|
const nodes = [];
|
|
437
583
|
const edges = [];
|
|
438
|
-
// Doc nodes
|
|
439
584
|
for (const doc of allDocs) {
|
|
440
585
|
nodes.push({
|
|
441
586
|
id: `doc:${doc.id}`,
|
|
@@ -444,7 +589,6 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
444
589
|
status: doc.status,
|
|
445
590
|
});
|
|
446
591
|
}
|
|
447
|
-
// Doc-doc edges
|
|
448
592
|
for (const link of allLinks) {
|
|
449
593
|
edges.push({
|
|
450
594
|
source: `doc:${link.fromDocId}`,
|
|
@@ -452,13 +596,11 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
452
596
|
type: link.linkType,
|
|
453
597
|
});
|
|
454
598
|
}
|
|
455
|
-
// Task-doc edges (collect task nodes from task_doc_links for each doc)
|
|
456
599
|
for (const doc of allDocs) {
|
|
457
600
|
const taskLinks = yield* docRepo.getTaskLinksForDoc(doc.id);
|
|
458
601
|
for (const tl of taskLinks) {
|
|
459
|
-
// Add task node if not already added
|
|
460
602
|
const taskNodeId = `task:${tl.taskId}`;
|
|
461
|
-
if (!nodes.some(n => n.id === taskNodeId)) {
|
|
603
|
+
if (!nodes.some((n) => n.id === taskNodeId)) {
|
|
462
604
|
nodes.push({
|
|
463
605
|
id: taskNodeId,
|
|
464
606
|
label: tl.taskId,
|
|
@@ -475,131 +617,5 @@ export const DocServiceLive = Layer.effect(DocService, Effect.gen(function* () {
|
|
|
475
617
|
return { nodes, edges };
|
|
476
618
|
}),
|
|
477
619
|
};
|
|
478
|
-
/** Generate index.yml and index.md from all docs in DB. */
|
|
479
|
-
function generateIndexEffect(docsPath) {
|
|
480
|
-
return Effect.gen(function* () {
|
|
481
|
-
const allDocs = yield* docRepo.findAll();
|
|
482
|
-
const allLinks = yield* docRepo.getAllLinks();
|
|
483
|
-
const overviewDoc = allDocs.find(d => d.kind === "overview");
|
|
484
|
-
const prds = allDocs
|
|
485
|
-
.filter(d => d.kind === "prd")
|
|
486
|
-
.map(d => ({ name: d.name, title: d.title, status: d.status }));
|
|
487
|
-
const designDocs = allDocs
|
|
488
|
-
.filter(d => d.kind === "design")
|
|
489
|
-
.map(d => {
|
|
490
|
-
// Find the prd this design implements (via prd_to_design link)
|
|
491
|
-
const implLink = allLinks.find(l => l.toDocId === d.id && l.linkType === "prd_to_design");
|
|
492
|
-
const implDoc = implLink ? allDocs.find(dd => dd.id === implLink.fromDocId) : undefined;
|
|
493
|
-
return {
|
|
494
|
-
name: d.name,
|
|
495
|
-
title: d.title,
|
|
496
|
-
status: d.status,
|
|
497
|
-
implements: implDoc?.name,
|
|
498
|
-
};
|
|
499
|
-
});
|
|
500
|
-
const links = allLinks.map(l => {
|
|
501
|
-
const from = allDocs.find(d => d.id === l.fromDocId);
|
|
502
|
-
const to = allDocs.find(d => d.id === l.toDocId);
|
|
503
|
-
return {
|
|
504
|
-
from: from?.name ?? String(l.fromDocId),
|
|
505
|
-
to: to?.name ?? String(l.toDocId),
|
|
506
|
-
type: l.linkType,
|
|
507
|
-
};
|
|
508
|
-
});
|
|
509
|
-
// Invariant summary
|
|
510
|
-
const allInvariants = yield* docRepo.findInvariants();
|
|
511
|
-
const activeInvariants = allInvariants.filter(i => i.status === "active");
|
|
512
|
-
const byEnforcement = {};
|
|
513
|
-
const bySubsystem = {};
|
|
514
|
-
for (const inv of activeInvariants) {
|
|
515
|
-
byEnforcement[inv.enforcement] = (byEnforcement[inv.enforcement] ?? 0) + 1;
|
|
516
|
-
const sub = inv.subsystem ?? "system";
|
|
517
|
-
bySubsystem[sub] = (bySubsystem[sub] ?? 0) + 1;
|
|
518
|
-
}
|
|
519
|
-
const indexData = {
|
|
520
|
-
overview: overviewDoc?.name,
|
|
521
|
-
prds,
|
|
522
|
-
design_docs: designDocs,
|
|
523
|
-
links,
|
|
524
|
-
invariant_summary: activeInvariants.length > 0
|
|
525
|
-
? { total: activeInvariants.length, by_enforcement: byEnforcement, by_subsystem: bySubsystem }
|
|
526
|
-
: undefined,
|
|
527
|
-
};
|
|
528
|
-
// Write index.yml using a proper YAML serializer to avoid injection
|
|
529
|
-
const indexYamlObj = {
|
|
530
|
-
generated: true,
|
|
531
|
-
generated_at: new Date().toISOString(),
|
|
532
|
-
};
|
|
533
|
-
if (indexData.overview) {
|
|
534
|
-
indexYamlObj.overview = indexData.overview;
|
|
535
|
-
}
|
|
536
|
-
if (prds.length > 0) {
|
|
537
|
-
indexYamlObj.prds = prds.map(p => ({ name: p.name, title: p.title, status: p.status }));
|
|
538
|
-
}
|
|
539
|
-
if (designDocs.length > 0) {
|
|
540
|
-
indexYamlObj.design_docs = designDocs.map(dd => {
|
|
541
|
-
const entry = { name: dd.name, title: dd.title, status: dd.status };
|
|
542
|
-
if (dd.implements)
|
|
543
|
-
entry.implements = dd.implements;
|
|
544
|
-
return entry;
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
const indexYamlPath = resolve(docsPath, "index.yml");
|
|
548
|
-
ensureDir(indexYamlPath);
|
|
549
|
-
writeFileSync(indexYamlPath, stringifyYaml(indexYamlObj), "utf8");
|
|
550
|
-
// Write index.md
|
|
551
|
-
const indexMd = renderIndexToMarkdown(indexData);
|
|
552
|
-
const indexMdPath = resolve(docsPath, "index.md");
|
|
553
|
-
writeFileSync(indexMdPath, indexMd, "utf8");
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
/** Sync invariants from a single doc's YAML into DB. */
|
|
557
|
-
function syncInvariantsForDoc(doc) {
|
|
558
|
-
return Effect.gen(function* () {
|
|
559
|
-
const docsPath = getDocsPath();
|
|
560
|
-
const yamlPath = resolveYamlPath(docsPath, doc.kind, doc.name);
|
|
561
|
-
if (!existsSync(yamlPath)) {
|
|
562
|
-
return [];
|
|
563
|
-
}
|
|
564
|
-
const yamlContent = readFileSync(yamlPath, "utf8");
|
|
565
|
-
const parsed = validateYaml(doc.name, yamlContent);
|
|
566
|
-
const invariantsRaw = parsed.invariants;
|
|
567
|
-
if (!Array.isArray(invariantsRaw) || invariantsRaw.length === 0) {
|
|
568
|
-
// Deprecate any existing invariants for this doc
|
|
569
|
-
yield* docRepo.deprecateInvariantsNotIn(doc.id, []);
|
|
570
|
-
return [];
|
|
571
|
-
}
|
|
572
|
-
const synced = [];
|
|
573
|
-
const activeIds = [];
|
|
574
|
-
for (const raw of invariantsRaw) {
|
|
575
|
-
if (typeof raw !== "object" || raw === null)
|
|
576
|
-
continue;
|
|
577
|
-
const inv = raw;
|
|
578
|
-
const id = typeof inv.id === "string" ? inv.id : null;
|
|
579
|
-
const rule = typeof inv.rule === "string" ? inv.rule : null;
|
|
580
|
-
const enforcement = typeof inv.enforcement === "string" ? inv.enforcement : null;
|
|
581
|
-
if (!id || !rule || !enforcement)
|
|
582
|
-
continue;
|
|
583
|
-
if (!enforcementStrings.includes(enforcement))
|
|
584
|
-
continue;
|
|
585
|
-
const input = {
|
|
586
|
-
id,
|
|
587
|
-
rule,
|
|
588
|
-
enforcement: enforcement,
|
|
589
|
-
docId: doc.id,
|
|
590
|
-
subsystem: typeof inv.subsystem === "string" ? inv.subsystem : (inv.subsystem === null ? null : undefined),
|
|
591
|
-
testRef: typeof inv.test_ref === "string" ? inv.test_ref : undefined,
|
|
592
|
-
lintRule: typeof inv.lint_rule === "string" ? inv.lint_rule : undefined,
|
|
593
|
-
promptRef: typeof inv.prompt_ref === "string" ? inv.prompt_ref : undefined,
|
|
594
|
-
};
|
|
595
|
-
const result = yield* docRepo.upsertInvariant(input);
|
|
596
|
-
synced.push(result);
|
|
597
|
-
activeIds.push(id);
|
|
598
|
-
}
|
|
599
|
-
// Deprecate invariants no longer in YAML
|
|
600
|
-
yield* docRepo.deprecateInvariantsNotIn(doc.id, activeIds);
|
|
601
|
-
return synced;
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
620
|
}));
|
|
605
621
|
//# sourceMappingURL=doc-service.js.map
|