canopycms 0.0.39 → 0.0.40

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.
Files changed (43) hide show
  1. package/dist/cli/generate-ai-content.js +7 -2
  2. package/dist/cli/init.js +7 -2
  3. package/dist/cli/sync.d.ts.map +1 -1
  4. package/dist/cli/sync.js +13 -103
  5. package/dist/cli/sync.js.map +1 -1
  6. package/dist/cli/template-files/canopy.ts.template +30 -2
  7. package/dist/config/helpers.d.ts.map +1 -1
  8. package/dist/config/helpers.js +5 -0
  9. package/dist/config/helpers.js.map +1 -1
  10. package/dist/config/schemas/config.d.ts +21 -0
  11. package/dist/config/schemas/config.d.ts.map +1 -1
  12. package/dist/config/schemas/config.js +6 -0
  13. package/dist/config/schemas/config.js.map +1 -1
  14. package/dist/config/types.d.ts +19 -0
  15. package/dist/config/types.d.ts.map +1 -1
  16. package/dist/context.d.ts +19 -10
  17. package/dist/context.d.ts.map +1 -1
  18. package/dist/context.js +5 -0
  19. package/dist/context.js.map +1 -1
  20. package/dist/dev-content-watcher.d.ts +37 -0
  21. package/dist/dev-content-watcher.d.ts.map +1 -0
  22. package/dist/dev-content-watcher.js +122 -0
  23. package/dist/dev-content-watcher.js.map +1 -0
  24. package/dist/entry-schema.d.ts +38 -0
  25. package/dist/entry-schema.d.ts.map +1 -1
  26. package/dist/entry-schema.js +33 -0
  27. package/dist/entry-schema.js.map +1 -1
  28. package/dist/schema/meta-loader.d.ts.map +1 -1
  29. package/dist/schema/meta-loader.js +3 -0
  30. package/dist/schema/meta-loader.js.map +1 -1
  31. package/dist/server.d.ts +4 -0
  32. package/dist/server.d.ts.map +1 -1
  33. package/dist/server.js +2 -0
  34. package/dist/server.js.map +1 -1
  35. package/dist/static/index.d.ts +51 -0
  36. package/dist/static/index.d.ts.map +1 -0
  37. package/dist/static/index.js +24 -0
  38. package/dist/static/index.js.map +1 -0
  39. package/dist/sync-core.d.ts +57 -0
  40. package/dist/sync-core.d.ts.map +1 -0
  41. package/dist/sync-core.js +166 -0
  42. package/dist/sync-core.js.map +1 -0
  43. package/package.json +5 -1
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Prompt-free core of the content sync between the working tree and CMS branch workspaces in
3
+ * `.canopy-dev/content-branches/`. The interactive CLI (cli/sync.ts) wraps these with prompts; the
4
+ * dev content watcher (dev-content-watcher.ts) reuses the diff helpers for divergence detection.
5
+ */
6
+ import fs from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import { simpleGit } from 'simple-git';
9
+ import { filePathExists } from './utils/fs.js';
10
+ /** Git tag marking the last known sync point, used as the merge base for `sync both` 3-way merges. */
11
+ export const SYNC_BASE_TAG = 'canopycms-sync-base';
12
+ /** Validate that a resolved path stays within the expected parent directory. */
13
+ export function assertWithinDir(resolved, parent, label) {
14
+ const normalizedResolved = path.resolve(resolved);
15
+ const normalizedParent = path.resolve(parent);
16
+ if (!normalizedResolved.startsWith(normalizedParent + path.sep) &&
17
+ normalizedResolved !== normalizedParent) {
18
+ throw new Error(`${label} escapes the expected directory: ${resolved}`);
19
+ }
20
+ }
21
+ /**
22
+ * Safely replace a directory by renaming the old one to a backup, renaming
23
+ * the new one into place, then deleting the backup. If interrupted between
24
+ * steps, at least one copy always exists on disk.
25
+ */
26
+ export async function safeReplaceDir(oldDir, newDir) {
27
+ const backupDir = `${oldDir}.sync-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
28
+ const oldExists = await filePathExists(oldDir);
29
+ if (oldExists) {
30
+ await fs.rename(oldDir, backupDir);
31
+ }
32
+ try {
33
+ await fs.rename(newDir, oldDir);
34
+ }
35
+ catch (err) {
36
+ // Restore backup if the rename failed
37
+ if (oldExists) {
38
+ await fs.rename(backupDir, oldDir).catch(() => { });
39
+ }
40
+ throw err;
41
+ }
42
+ if (oldExists) {
43
+ await fs.rm(backupDir, { recursive: true, force: true }).catch(() => { });
44
+ }
45
+ }
46
+ /** Recursively list all file paths relative to `dir`. Skips .git and symlinks. */
47
+ export async function listFilesRecursive(dir, prefix = '') {
48
+ const results = [];
49
+ let entries;
50
+ try {
51
+ entries = await fs.readdir(dir, { withFileTypes: true });
52
+ }
53
+ catch {
54
+ return results;
55
+ }
56
+ for (const entry of entries) {
57
+ if (entry.name === '.git')
58
+ continue;
59
+ if (entry.isSymbolicLink())
60
+ continue;
61
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
62
+ if (entry.isDirectory()) {
63
+ results.push(...(await listFilesRecursive(path.join(dir, entry.name), rel)));
64
+ }
65
+ else {
66
+ results.push(rel);
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ /** Recursively copy a directory, creating the destination if needed. Skips .git directories and symlinks. */
72
+ export async function copyDir(src, dest) {
73
+ await fs.mkdir(dest, { recursive: true });
74
+ const entries = await fs.readdir(src, { withFileTypes: true });
75
+ for (const entry of entries) {
76
+ if (entry.name === '.git')
77
+ continue;
78
+ if (entry.isSymbolicLink())
79
+ continue;
80
+ const srcPath = path.join(src, entry.name);
81
+ const destPath = path.join(dest, entry.name);
82
+ if (entry.isDirectory()) {
83
+ await copyDir(srcPath, destPath);
84
+ }
85
+ else {
86
+ await fs.copyFile(srcPath, destPath);
87
+ }
88
+ }
89
+ }
90
+ /** True when the diff contains no added/removed/changed files. */
91
+ export function isContentTreeDiffEmpty(diff) {
92
+ return diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0;
93
+ }
94
+ async function readFileSafe(file) {
95
+ try {
96
+ return await fs.readFile(file);
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * Compare two content directories by file presence and exact content (byte comparison — robust to
104
+ * mtime differences, which `fs.copyFile` does not preserve). Missing directories list as empty.
105
+ */
106
+ export async function diffContentTrees(workingTreeDir, branchDir) {
107
+ const [wtFiles, brFiles] = await Promise.all([
108
+ listFilesRecursive(workingTreeDir),
109
+ listFilesRecursive(branchDir),
110
+ ]);
111
+ const wtSet = new Set(wtFiles);
112
+ const brSet = new Set(brFiles);
113
+ const added = wtFiles.filter((f) => !brSet.has(f)).sort();
114
+ const removed = brFiles.filter((f) => !wtSet.has(f)).sort();
115
+ const common = wtFiles.filter((f) => brSet.has(f));
116
+ const changed = [];
117
+ for (const rel of common) {
118
+ const [a, b] = await Promise.all([
119
+ readFileSafe(path.join(workingTreeDir, rel)),
120
+ readFileSafe(path.join(branchDir, rel)),
121
+ ]);
122
+ if (!a || !b)
123
+ continue;
124
+ if (!a.equals(b))
125
+ changed.push(rel);
126
+ }
127
+ changed.sort();
128
+ return { added, removed, changed };
129
+ }
130
+ /**
131
+ * Copy working-tree content into a branch workspace and commit it. Prompt-free — the interactive CLI
132
+ * (`canopycms sync push`) calls this for the actual copy + commit + tag step.
133
+ *
134
+ * Returns the number of changed files committed (0 when content was already up to date).
135
+ */
136
+ export async function pushContentToWorkspace(options) {
137
+ const { srcContentDir, branchPath, contentRoot, commitMessage, baseTag } = options;
138
+ if (!(await filePathExists(srcContentDir))) {
139
+ return { fileCount: 0 };
140
+ }
141
+ const wsContentDir = path.join(branchPath, contentRoot);
142
+ assertWithinDir(wsContentDir, branchPath, 'content-root');
143
+ // Copy into a temp dir, then atomically swap it into place.
144
+ const tmpDir = `${wsContentDir}.sync-tmp-${Date.now()}`;
145
+ try {
146
+ await copyDir(srcContentDir, tmpDir);
147
+ await safeReplaceDir(wsContentDir, tmpDir);
148
+ }
149
+ catch (err) {
150
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
151
+ throw err;
152
+ }
153
+ const wsGit = simpleGit({ baseDir: branchPath });
154
+ await wsGit.add('-A');
155
+ const postStatus = await wsGit.status();
156
+ if (postStatus.files.length === 0) {
157
+ if (baseTag)
158
+ await wsGit.tag(['-f', baseTag]);
159
+ return { fileCount: 0 };
160
+ }
161
+ await wsGit.commit(commitMessage ?? 'sync: update content from working tree');
162
+ if (baseTag)
163
+ await wsGit.tag(['-f', baseTag]);
164
+ return { fileCount: postStatus.files.length };
165
+ }
166
+ //# sourceMappingURL=sync-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-core.js","sourceRoot":"","sources":["../src/sync-core.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "@codemirror/language, @lezer/highlight: workaround — @mdxeditor/editor uses cm6-theme-basic-light which peer-requires these but mdxeditor doesn't declare them as dependencies",
3
3
  "name": "canopycms",
4
- "version": "0.0.39",
4
+ "version": "0.0.40",
5
5
  "description": "CanopyCMS core package: schema-driven content, branch-aware editing, and editor UI for Next.js.",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -61,6 +61,10 @@
61
61
  "import": "./dist/build/index.js",
62
62
  "types": "./dist/build/index.d.ts"
63
63
  },
64
+ "./utils/error": {
65
+ "import": "./dist/utils/error.js",
66
+ "types": "./dist/utils/error.d.ts"
67
+ },
64
68
  "./test-utils": {
65
69
  "import": "./dist/test-utils/index.js",
66
70
  "types": "./dist/test-utils/index.d.ts"