@popoverai/dotrequirements 0.27.4 → 0.28.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 +24 -20
- package/dist/cli.js +34 -12
- package/dist/commands/aliases.d.ts +26 -0
- package/dist/commands/aliases.js +31 -0
- package/dist/commands/diff.d.ts +14 -0
- package/dist/commands/diff.js +62 -0
- package/dist/commands/init.js +5 -5
- package/dist/commands/link.d.ts +1 -1
- package/dist/commands/link.js +10 -7
- package/dist/commands/sync-common.d.ts +21 -0
- package/dist/commands/sync-common.js +24 -0
- package/dist/commands/sync.d.ts +26 -0
- package/dist/commands/sync.js +182 -0
- package/dist/convex.d.ts +1 -1
- package/dist/convex.js +2 -2
- package/dist/push/core.d.ts +18 -116
- package/dist/push/core.js +16 -267
- package/dist/push/index.d.ts +3 -3
- package/dist/push/index.js +4 -4
- package/dist/requirements/style-guide.js +3 -3
- package/dist/schema/schemas.js +1 -1
- package/dist/sync/compare.d.ts +21 -0
- package/dist/sync/compare.js +285 -0
- package/dist/sync/execute.d.ts +48 -0
- package/dist/sync/execute.js +186 -0
- package/dist/sync/index.d.ts +21 -0
- package/dist/sync/index.js +52 -0
- package/dist/sync/local-files.d.ts +26 -0
- package/dist/sync/local-files.js +91 -0
- package/dist/sync/plan.d.ts +39 -0
- package/dist/sync/plan.js +90 -0
- package/dist/sync/render.d.ts +17 -0
- package/dist/sync/render.js +46 -0
- package/dist/sync/segment.d.ts +40 -0
- package/dist/sync/segment.js +76 -0
- package/dist/sync/snapshot.d.ts +30 -0
- package/dist/sync/snapshot.js +118 -0
- package/dist/sync/types.d.ts +82 -0
- package/dist/sync/types.js +12 -0
- package/dist/templates/context-file-section.md +2 -1
- package/dist/templates/requirements-readme.js +3 -4
- package/dist/templates/requirements-readme.ts +3 -4
- package/dist/templates/skills/codebase-to-spec/SKILL.md +2 -2
- package/dist/utils/project-settings.d.ts +8 -2
- package/dist/utils/project-settings.js +47 -24
- package/package.json +1 -1
- package/dist/commands/pull.d.ts +0 -8
- package/dist/commands/pull.js +0 -230
- package/dist/commands/push.d.ts +0 -6
- package/dist/commands/push.js +0 -244
package/dist/push/core.js
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Parsing local files
|
|
7
|
-
* - Dry run validation with Convex
|
|
8
|
-
* - Conflict detection
|
|
9
|
-
* - Executing the push
|
|
10
|
-
* - Updating local files with document IDs and timestamps
|
|
2
|
+
* Shared parse-and-write-back primitives for syncing local requirements files
|
|
3
|
+
* to the cloud. The comparator-driven sync path (`packages/cli/src/sync/`)
|
|
4
|
+
* consumes these; the former dry-run/execute push pipeline was retired with the
|
|
5
|
+
* `push` command (its behavior now lives in the sync executor).
|
|
11
6
|
*/
|
|
12
7
|
import * as fs from "node:fs";
|
|
13
|
-
import * as path from "node:path";
|
|
14
|
-
import { ConvexHttpClient } from "convex/browser";
|
|
15
8
|
import YAML from "yaml";
|
|
16
|
-
import {
|
|
17
|
-
import { buildRequirementsFile, getAllRequirements, parseRequirementKey, parseRequirementsFromFile, } from "../schema/index.js";
|
|
9
|
+
import { getAllRequirements, parseRequirementKey, parseRequirementsFromFile, } from "../schema/index.js";
|
|
18
10
|
import { extractFrontmatterBlock, stripFrontmatterBlock, } from "../schema/parser-core.js";
|
|
19
11
|
/** Base URL of the web app, where synced documents are reviewed. */
|
|
20
12
|
export const WEB_APP_URL = "https://app.dotrequirements.io";
|
|
@@ -26,7 +18,7 @@ export const WEB_APP_URL = "https://app.dotrequirements.io";
|
|
|
26
18
|
*/
|
|
27
19
|
export function extractMarkdownContent(rawContent) {
|
|
28
20
|
// Shared CRLF-normalizing helper (SYNC-FORMAT-1): the stripped body is
|
|
29
|
-
//
|
|
21
|
+
// synced as the document's canonical markdownContent (SYNC-ARCH-1), so a
|
|
30
22
|
// CRLF-authored file must not leak its frontmatter fence into the cloud
|
|
31
23
|
// body.
|
|
32
24
|
return stripFrontmatterBlock(rawContent);
|
|
@@ -55,11 +47,11 @@ function extractRawFrontmatter(rawContent) {
|
|
|
55
47
|
return undefined;
|
|
56
48
|
}
|
|
57
49
|
/**
|
|
58
|
-
* DOC-HEADER-14: merge the validated (and
|
|
50
|
+
* DOC-HEADER-14: merge the validated (and sync-updated) metadata over the raw
|
|
59
51
|
* frontmatter so unrecognized keys survive the rewrite while the fields the
|
|
60
|
-
*
|
|
52
|
+
* sync owns (document ID, pulledAt, defaultPrefix, version) stay updated.
|
|
61
53
|
*/
|
|
62
|
-
function mergeMetadataWithRawFrontmatter(metadata, rawFrontmatter) {
|
|
54
|
+
export function mergeMetadataWithRawFrontmatter(metadata, rawFrontmatter) {
|
|
63
55
|
if (!rawFrontmatter) {
|
|
64
56
|
return metadata;
|
|
65
57
|
}
|
|
@@ -77,11 +69,11 @@ function mergeMetadataWithRawFrontmatter(metadata, rawFrontmatter) {
|
|
|
77
69
|
return merged;
|
|
78
70
|
}
|
|
79
71
|
/**
|
|
80
|
-
* Parse files for
|
|
72
|
+
* Parse files for sync. Returns parsed files with metadata and content.
|
|
81
73
|
*
|
|
82
74
|
* Throws (via `parseRequirementsFromFile`) if any file is syntactically
|
|
83
75
|
* invalid. Callers that need one bad file not to abort the batch should parse
|
|
84
|
-
* files individually and collect failures
|
|
76
|
+
* files individually and collect failures.
|
|
85
77
|
*/
|
|
86
78
|
export function parseFilesForPush(filePaths) {
|
|
87
79
|
const parsedFiles = [];
|
|
@@ -128,11 +120,11 @@ export function parseFilesForPush(filePaths) {
|
|
|
128
120
|
/**
|
|
129
121
|
* SYNC-FAIL-4.0: parse files one at a time so a single invalid file cannot
|
|
130
122
|
* abort the batch — its failure is collected per file while the valid files
|
|
131
|
-
* still parse.
|
|
132
|
-
* so their isolation semantics cannot drift.
|
|
123
|
+
* still parse.
|
|
133
124
|
*
|
|
134
125
|
* Note: cross-file checks that need the whole batch (e.g. SYNC-FAIL-3
|
|
135
|
-
* duplicate document IDs) are enforced
|
|
126
|
+
* duplicate document IDs) are enforced by the sync path against the local
|
|
127
|
+
* snapshot, not here.
|
|
136
128
|
*/
|
|
137
129
|
export function parseFilesForPushIndividually(filePaths) {
|
|
138
130
|
const parsedFiles = [];
|
|
@@ -154,7 +146,7 @@ export function parseFilesForPushIndividually(filePaths) {
|
|
|
154
146
|
return { parsedFiles, totalRequirements, parseFailures };
|
|
155
147
|
}
|
|
156
148
|
/**
|
|
157
|
-
* SYNC-FAIL-3: two ParsedFiles carrying the same document.id would both
|
|
149
|
+
* SYNC-FAIL-3: two ParsedFiles carrying the same document.id would both sync
|
|
158
150
|
* as updates to one cloud document — last writer wins and the first spec is
|
|
159
151
|
* silently destroyed. Abort instead, naming both files and the shared id.
|
|
160
152
|
*/
|
|
@@ -170,252 +162,9 @@ export function assertNoDuplicateDocumentIds(parsedFiles) {
|
|
|
170
162
|
` ${existingPath}\n` +
|
|
171
163
|
` ${file.filePath}\n` +
|
|
172
164
|
`Each file must map to its own cloud document. Remove the "id:" line ` +
|
|
173
|
-
`from the copy's frontmatter so it
|
|
165
|
+
`from the copy's frontmatter so it syncs as a new document, then sync again.`);
|
|
174
166
|
}
|
|
175
167
|
filesByDocId.set(docId, file.filePath);
|
|
176
168
|
}
|
|
177
169
|
}
|
|
178
|
-
// ============================================================================
|
|
179
|
-
// Dry Run
|
|
180
|
-
// ============================================================================
|
|
181
|
-
/**
|
|
182
|
-
* Execute dry run phase: validate all files against Convex and detect conflicts.
|
|
183
|
-
*/
|
|
184
|
-
export async function dryRunPush(parsedFiles, credentials) {
|
|
185
|
-
// SYNC-FAIL-3.0: callers that assemble ParsedFiles themselves (e.g. the
|
|
186
|
-
// MCP handler parses files one at a time) still abort before any cloud write
|
|
187
|
-
assertNoDuplicateDocumentIds(parsedFiles);
|
|
188
|
-
const client = new ConvexHttpClient(credentials.convexUrl);
|
|
189
|
-
const dryRunResults = [];
|
|
190
|
-
// Phase 1: Dry run to categorize all files
|
|
191
|
-
for (const file of parsedFiles) {
|
|
192
|
-
const doc = file.metadata.document;
|
|
193
|
-
const fileName = path.basename(file.filePath);
|
|
194
|
-
if (!doc) {
|
|
195
|
-
// No document section - mark as invalid locally
|
|
196
|
-
dryRunResults.push({
|
|
197
|
-
file,
|
|
198
|
-
result: {
|
|
199
|
-
dryRun: true,
|
|
200
|
-
action: "invalid",
|
|
201
|
-
title: fileName,
|
|
202
|
-
error: "Missing document section in frontmatter",
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
const result = (await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
|
|
209
|
-
projectAuth: {
|
|
210
|
-
projectSlug: credentials.projectId,
|
|
211
|
-
projectSecret: credentials.projectSecret,
|
|
212
|
-
},
|
|
213
|
-
target: { type: "project", slug: credentials.projectId },
|
|
214
|
-
documentId: doc.id,
|
|
215
|
-
title: doc.title,
|
|
216
|
-
markdownContent: file.markdownContent,
|
|
217
|
-
defaultPrefix: doc.defaultPrefix,
|
|
218
|
-
dryRun: true,
|
|
219
|
-
}));
|
|
220
|
-
dryRunResults.push({ file, result });
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
dryRunResults.push({
|
|
224
|
-
file,
|
|
225
|
-
result: {
|
|
226
|
-
dryRun: true,
|
|
227
|
-
action: "invalid",
|
|
228
|
-
title: doc.title,
|
|
229
|
-
error: err.message,
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// Categorize results
|
|
235
|
-
const updates = dryRunResults.filter((r) => r.result.action === "update");
|
|
236
|
-
const creates = dryRunResults.filter((r) => r.result.action === "create");
|
|
237
|
-
const notFound = dryRunResults.filter((r) => r.result.action === "not_found");
|
|
238
|
-
const invalid = dryRunResults.filter((r) => r.result.action === "invalid");
|
|
239
|
-
// Check for conflicts on updates (cloud changed since last pull)
|
|
240
|
-
const conflicts = [];
|
|
241
|
-
if (updates.length > 0) {
|
|
242
|
-
const docIds = updates.map((u) => u.result.documentId);
|
|
243
|
-
const cloudMetadata = await client.query(api.documents.queries.getDocumentsMetadata, {
|
|
244
|
-
projectAuth: {
|
|
245
|
-
projectSlug: credentials.projectId,
|
|
246
|
-
projectSecret: credentials.projectSecret,
|
|
247
|
-
},
|
|
248
|
-
target: { type: "project", slug: credentials.projectId },
|
|
249
|
-
documentIds: docIds,
|
|
250
|
-
});
|
|
251
|
-
const cloudMetaByDocId = new Map(cloudMetadata.map((m) => [m.documentId, m]));
|
|
252
|
-
for (const item of updates) {
|
|
253
|
-
const docId = item.result.documentId;
|
|
254
|
-
const cloudMeta = cloudMetaByDocId.get(docId);
|
|
255
|
-
if (cloudMeta && item.file.metadata.pulledAt) {
|
|
256
|
-
const pulledAtMs = new Date(item.file.metadata.pulledAt).getTime();
|
|
257
|
-
if (cloudMeta.updatedAt > pulledAtMs) {
|
|
258
|
-
conflicts.push({ item, cloudMeta });
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return {
|
|
264
|
-
updates,
|
|
265
|
-
creates,
|
|
266
|
-
notFound,
|
|
267
|
-
invalid,
|
|
268
|
-
conflicts,
|
|
269
|
-
totalRequirements: parsedFiles.reduce((sum, f) => sum + f.requirementCount, 0),
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
// ============================================================================
|
|
273
|
-
// Execute Push
|
|
274
|
-
// ============================================================================
|
|
275
|
-
/**
|
|
276
|
-
* Execute the push: save all pushable files to Convex and update local files.
|
|
277
|
-
*/
|
|
278
|
-
export async function executePush(dryRunResult, credentials) {
|
|
279
|
-
const client = new ConvexHttpClient(credentials.convexUrl);
|
|
280
|
-
const pushableFiles = [
|
|
281
|
-
...dryRunResult.updates,
|
|
282
|
-
...dryRunResult.creates,
|
|
283
|
-
...dryRunResult.notFound,
|
|
284
|
-
];
|
|
285
|
-
let created = 0;
|
|
286
|
-
let updated = 0;
|
|
287
|
-
const errors = [];
|
|
288
|
-
const importWarnings = [];
|
|
289
|
-
const synced = [];
|
|
290
|
-
const writeBackWarnings = [];
|
|
291
|
-
for (const { file, result } of pushableFiles) {
|
|
292
|
-
const doc = file.metadata.document;
|
|
293
|
-
const fileName = path.basename(file.filePath);
|
|
294
|
-
// For not_found, clear the ID so we create a new document
|
|
295
|
-
const effectiveDocId = result.action === "not_found" ? undefined : doc.id;
|
|
296
|
-
try {
|
|
297
|
-
const rawResult = (await client.mutation(api.documents.saveWithRequirements.saveWithRequirements, {
|
|
298
|
-
projectAuth: {
|
|
299
|
-
projectSlug: credentials.projectId,
|
|
300
|
-
projectSecret: credentials.projectSecret,
|
|
301
|
-
},
|
|
302
|
-
target: { type: "project", slug: credentials.projectId },
|
|
303
|
-
documentId: effectiveDocId,
|
|
304
|
-
title: doc.title,
|
|
305
|
-
markdownContent: file.markdownContent,
|
|
306
|
-
defaultPrefix: doc.defaultPrefix,
|
|
307
|
-
// IMPORT-2: forward the CTS run marker when the file is stamped
|
|
308
|
-
runMarker: file.metadata.ctsRun,
|
|
309
|
-
dryRun: false,
|
|
310
|
-
}));
|
|
311
|
-
// The server returns a structured result iff we sent a runMarker
|
|
312
|
-
const pushResult = typeof rawResult === "string" ? rawResult : rawResult.documentId;
|
|
313
|
-
if (typeof rawResult !== "string") {
|
|
314
|
-
const status = rawResult.importStatus;
|
|
315
|
-
if (status === "invalid_marker" ||
|
|
316
|
-
status === "unsupported_version" ||
|
|
317
|
-
status === "already_used") {
|
|
318
|
-
// IMPORT-3.2/3.3: loud, never fatal
|
|
319
|
-
importWarnings.push({ fileName, status });
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const isCreate = result.action === "create" || result.action === "not_found";
|
|
323
|
-
if (isCreate) {
|
|
324
|
-
// New document - write ID back to file
|
|
325
|
-
doc.id = pushResult;
|
|
326
|
-
file.metadata.pulledAt = new Date().toISOString();
|
|
327
|
-
file.metadata.version = 1;
|
|
328
|
-
created++;
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
// Update pulledAt to reflect this push
|
|
332
|
-
file.metadata.pulledAt = new Date().toISOString();
|
|
333
|
-
file.metadata.version = (file.metadata.version || 0) + 1;
|
|
334
|
-
updated++;
|
|
335
|
-
}
|
|
336
|
-
// SYNC-FAIL-2: the local write-back is separate from the cloud save —
|
|
337
|
-
// the cloud document already exists at this point, so a write-back
|
|
338
|
-
// failure is a warning on a synced document, never a push failure
|
|
339
|
-
// (which would invite a retry that mints a duplicate cloud document).
|
|
340
|
-
try {
|
|
341
|
-
// DOC-HEADER-14: unrecognized frontmatter keys survive the rewrite
|
|
342
|
-
const updatedContent = buildRequirementsFile(mergeMetadataWithRawFrontmatter(file.metadata, file.rawFrontmatter), file.markdownContent);
|
|
343
|
-
fs.writeFileSync(file.filePath, updatedContent, "utf-8");
|
|
344
|
-
}
|
|
345
|
-
catch (writeErr) {
|
|
346
|
-
writeBackWarnings.push({
|
|
347
|
-
fileName,
|
|
348
|
-
filePath: file.filePath,
|
|
349
|
-
documentId: pushResult,
|
|
350
|
-
error: writeErr instanceof Error ? writeErr.message : String(writeErr),
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
// SYNC-LAND-1: every synced document gets its web URL in the result
|
|
354
|
-
synced.push({
|
|
355
|
-
fileName,
|
|
356
|
-
documentId: pushResult,
|
|
357
|
-
url: `${WEB_APP_URL}/documents/${pushResult}`,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
catch (err) {
|
|
361
|
-
errors.push({
|
|
362
|
-
fileName,
|
|
363
|
-
filePath: file.filePath,
|
|
364
|
-
error: errorDisplayMessage(err),
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return {
|
|
369
|
-
created,
|
|
370
|
-
updated,
|
|
371
|
-
errors,
|
|
372
|
-
importWarnings,
|
|
373
|
-
synced,
|
|
374
|
-
writeBackWarnings,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Servers throw ConvexError({kind, message}) for expected failures (e.g.
|
|
379
|
-
* LIMITS-4.2's limit error); the client-side Error message embeds that data
|
|
380
|
-
* as JSON. Surface the human-readable message it carries instead of the blob.
|
|
381
|
-
*/
|
|
382
|
-
function errorDisplayMessage(err) {
|
|
383
|
-
const data = err.data;
|
|
384
|
-
const fromData = humanMessage(data);
|
|
385
|
-
if (fromData)
|
|
386
|
-
return fromData;
|
|
387
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
388
|
-
// ConvexError messages may embed the data JSON directly or after a
|
|
389
|
-
// "ConvexError:" prefix — try the trailing {...} chunk
|
|
390
|
-
const jsonStart = message.indexOf("{");
|
|
391
|
-
if (jsonStart !== -1) {
|
|
392
|
-
try {
|
|
393
|
-
const parsed = JSON.parse(message.slice(jsonStart));
|
|
394
|
-
const fromMessage = humanMessage(parsed);
|
|
395
|
-
if (fromMessage)
|
|
396
|
-
return fromMessage;
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
// fall through to the raw message
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return message;
|
|
403
|
-
}
|
|
404
|
-
function humanMessage(data) {
|
|
405
|
-
if (typeof data === "string") {
|
|
406
|
-
try {
|
|
407
|
-
return humanMessage(JSON.parse(data));
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
return undefined;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if (data &&
|
|
414
|
-
typeof data === "object" &&
|
|
415
|
-
"message" in data &&
|
|
416
|
-
typeof data.message === "string") {
|
|
417
|
-
return data.message;
|
|
418
|
-
}
|
|
419
|
-
return undefined;
|
|
420
|
-
}
|
|
421
170
|
//# sourceMappingURL=core.js.map
|
package/dist/push/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Push Module
|
|
3
3
|
*
|
|
4
|
-
* Shared
|
|
5
|
-
*
|
|
4
|
+
* Shared parse-and-write-back primitives for syncing local requirements files
|
|
5
|
+
* to the cloud, consumed by the comparator-driven sync path.
|
|
6
6
|
*/
|
|
7
|
-
export {
|
|
7
|
+
export { assertNoDuplicateDocumentIds, extractMarkdownContent, mergeMetadataWithRawFrontmatter, type ParsedFile, type ParseFailure, parseFilesForPush, parseFilesForPushIndividually, WEB_APP_URL, } from "./core.js";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/push/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Push Module
|
|
3
3
|
*
|
|
4
|
-
* Shared
|
|
5
|
-
*
|
|
4
|
+
* Shared parse-and-write-back primitives for syncing local requirements files
|
|
5
|
+
* to the cloud, consumed by the comparator-driven sync path.
|
|
6
6
|
*/
|
|
7
|
-
export {
|
|
7
|
+
export { assertNoDuplicateDocumentIds,
|
|
8
8
|
// Functions
|
|
9
|
-
extractMarkdownContent, parseFilesForPush, parseFilesForPushIndividually, WEB_APP_URL, } from "./core.js";
|
|
9
|
+
extractMarkdownContent, mergeMetadataWithRawFrontmatter, parseFilesForPush, parseFilesForPushIndividually, WEB_APP_URL, } from "./core.js";
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -312,9 +312,9 @@ ${body}
|
|
|
312
312
|
## Next Steps
|
|
313
313
|
|
|
314
314
|
1. **Create file**: Save this template as \`${filePath}\` and edit it for your feature
|
|
315
|
-
2. **Refine style** (optional): Run \`style-check\`
|
|
316
|
-
3. **Validate syntax**: Run \`validate\`
|
|
317
|
-
4. **
|
|
315
|
+
2. **Refine style** (optional): Run \`style-check\` for AI feedback
|
|
316
|
+
3. **Validate syntax**: Run \`validate\` to verify format
|
|
317
|
+
4. **Sync to cloud**: Run \`dotrequirements sync\` to reconcile the repo and the cloud
|
|
318
318
|
|
|
319
319
|
**Note**: Requirements files can be colocated with code (\`src/auth.requirements.md\`) or centralized in \`.requirements/\` directory.`;
|
|
320
320
|
}
|
package/dist/schema/schemas.js
CHANGED
|
@@ -140,7 +140,7 @@ export function validateForPush(metadata) {
|
|
|
140
140
|
if (!metadata.document) {
|
|
141
141
|
return {
|
|
142
142
|
valid: false,
|
|
143
|
-
reason: "Missing document section. Add a document section with a title to
|
|
143
|
+
reason: "Missing document section. Add a document section with a title to sync this file.",
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
// Must have title
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo/cloud comparator (DIFF-*). A pure function of two snapshots: it
|
|
3
|
+
* reads no disk and makes no network calls. `dotreq diff` renders its result,
|
|
4
|
+
* `dotreq sync` acts on it, CI asserts on it.
|
|
5
|
+
*/
|
|
6
|
+
import type { CloudSnapshot, ComparisonResult, LocalSnapshot } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Two local files carrying the same document id would both pair to one cloud
|
|
9
|
+
* document and both try to write it — last writer wins, silently destroying the
|
|
10
|
+
* first (SYNC-FAIL-3). Return the offending ids with their file paths so the
|
|
11
|
+
* caller can abort before any write.
|
|
12
|
+
*/
|
|
13
|
+
export declare function duplicateLocalDocumentIds(local: LocalSnapshot): Array<{
|
|
14
|
+
documentId: string;
|
|
15
|
+
filePaths: string[];
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Compare the whole repo against the whole cloud, pairing documents by id.
|
|
19
|
+
*/
|
|
20
|
+
export declare function compareSnapshots(local: LocalSnapshot, cloud: CloudSnapshot): ComparisonResult;
|
|
21
|
+
//# sourceMappingURL=compare.d.ts.map
|