sysprom 1.22.1 → 1.23.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.
@@ -1,10 +1,10 @@
1
1
  import * as z from "zod";
2
- import { readFileSync, writeFileSync } from "node:fs";
3
- import { resolve } from "node:path";
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, } from "node:fs";
3
+ import { resolve, extname } from "node:path";
4
4
  import { syncDocumentsOp, } from "../../operations/index.js";
5
5
  import { detectChanges } from "../../sync.js";
6
6
  import { markdownToJson } from "../../md-to-json.js";
7
- import { jsonToMarkdownSingle } from "../../json-to-md.js";
7
+ import { jsonToMarkdownSingle, jsonToMarkdownMultiDoc, } from "../../json-to-md.js";
8
8
  import { canonicalise } from "../../canonical-json.js";
9
9
  import { SysProMDocument } from "../../schema.js";
10
10
  /**
@@ -22,8 +22,10 @@ export function syncCommand(input) {
22
22
  if (!SysProMDocument.is(jsonDoc)) {
23
23
  throw new Error("JSON file is not a valid SysProM document");
24
24
  }
25
- // Parse Markdown to document
26
- const mdDoc = markdownToJson(mdPath);
25
+ // Parse Markdown to document, or create empty doc if it doesn't exist yet
26
+ const mdDoc = existsSync(mdPath)
27
+ ? markdownToJson(mdPath)
28
+ : { nodes: [], relationships: [] };
27
29
  // Detect which side changed
28
30
  const changes = detectChanges(jsonPath, mdPath);
29
31
  // Perform sync operation
@@ -34,6 +36,8 @@ export function syncCommand(input) {
34
36
  mdChanged: changes.mdChanged,
35
37
  strategy,
36
38
  });
39
+ // Track if markdown file existed before sync
40
+ const mdExistedBefore = existsSync(mdPath);
37
41
  // Write results if not dry-run
38
42
  if (!dryRun) {
39
43
  // Write synced document back to both formats
@@ -41,10 +45,26 @@ export function syncCommand(input) {
41
45
  // Update JSON
42
46
  writeFileSync(jsonPath, canonicalise(result.synced, { indent: "\t" }) + "\n");
43
47
  // Update Markdown
44
- const mdContent = jsonToMarkdownSingle(result.synced);
45
- writeFileSync(mdPath, mdContent);
48
+ // If output path is an existing directory or doesn't look like a .md file,
49
+ // write multi-doc output into the directory. Otherwise write single-file MD.
50
+ if (mdExistedBefore && statSync(mdPath).isDirectory()) {
51
+ jsonToMarkdownMultiDoc(result.synced, mdPath);
52
+ }
53
+ else if (extname(mdPath) === ".md") {
54
+ const mdContent = jsonToMarkdownSingle(result.synced);
55
+ writeFileSync(mdPath, mdContent);
56
+ }
57
+ else {
58
+ // Treat as directory: ensure it exists and write multi-doc
59
+ mkdirSync(mdPath, { recursive: true });
60
+ jsonToMarkdownMultiDoc(result.synced, mdPath);
61
+ }
46
62
  }
47
63
  }
64
+ // If markdown file didn't exist before but does now (we created it), mark mdChanged as true
65
+ if (!mdExistedBefore && existsSync(mdPath) && !dryRun) {
66
+ result.mdChanged = true;
67
+ }
48
68
  return result;
49
69
  }
50
70
  const syncOpts = z
@@ -97,5 +117,9 @@ export const syncCommandDef = {
97
117
  if (result.changedNodes.length > 0) {
98
118
  console.log(` Changed nodes: ${result.changedNodes.join(", ")}`);
99
119
  }
120
+ const hasDrift = result.jsonChanged || result.mdChanged || result.conflict;
121
+ if ((opts.dryRun || opts.report) && hasDrift) {
122
+ process.exitCode = 1;
123
+ }
100
124
  },
101
125
  };
package/dist/src/sync.js CHANGED
@@ -1,4 +1,4 @@
1
- import { readFileSync, statSync } from "node:fs";
1
+ import { readFileSync, statSync, existsSync } from "node:fs";
2
2
  import { createHash } from "node:crypto";
3
3
  import { markdownToJson } from "./md-to-json.js";
4
4
  import { SysProMDocument } from "./schema.js";
@@ -42,8 +42,10 @@ export function detectChanges(jsonPath, mdPath) {
42
42
  if (!SysProMDocument.is(jsonDoc)) {
43
43
  throw new Error("JSON file is not a valid SysProM document");
44
44
  }
45
- // Parse Markdown to document
46
- const mdDoc = markdownToJson(mdPath);
45
+ // Parse Markdown to document (or treat as empty if it doesn't exist)
46
+ const mdDoc = existsSync(mdPath)
47
+ ? markdownToJson(mdPath)
48
+ : { nodes: [], relationships: [] };
47
49
  // Compare parsed documents
48
50
  const jsonHash = normaliseHash(jsonDoc);
49
51
  const mdHash = normaliseHash(mdDoc);
@@ -55,6 +57,14 @@ export function detectChanges(jsonPath, mdPath) {
55
57
  conflict: false,
56
58
  };
57
59
  }
60
+ // If markdown file doesn't exist, treat as if JSON changed (JSON is the source)
61
+ if (!existsSync(mdPath)) {
62
+ return {
63
+ jsonChanged: true,
64
+ mdChanged: false,
65
+ conflict: false,
66
+ };
67
+ }
58
68
  // Parsed documents differ. Use file modification times to determine which changed.
59
69
  // The file that was modified more recently is the one that diverged.
60
70
  const jsonStats = statSync(jsonPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sysprom",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "SysProM — System Provenance Model CLI and library",
5
5
  "author": "ExaDev",
6
6
  "homepage": "https://exadev.github.io/SysProM",