pagan-artifact 0.2.5

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,67 @@
1
+ /**
2
+ * art - Modern version control.
3
+ * Module: Utils (v0.2.5)
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ /**
10
+ * Helper to reconstruct file states at a specific commit hash.
11
+ */
12
+
13
+ module.exports = (branchName, targetHash) => {
14
+ const artPath = path.join(process.cwd(), '.art');
15
+ const rootPath = path.join(artPath, 'root/manifest.json');
16
+
17
+ if (!fs.existsSync(rootPath)) return {};
18
+
19
+ const rootManifest = JSON.parse(fs.readFileSync(rootPath, 'utf8'));
20
+ const branchPath = path.join(artPath, 'history/local', branchName);
21
+ const manifestPath = path.join(branchPath, 'manifest.json');
22
+
23
+ if (!fs.existsSync(manifestPath)) return {};
24
+
25
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
26
+
27
+ let state = {};
28
+
29
+ for (const file of rootManifest.files) {
30
+ state[file.path] = file.content;
31
+ }
32
+
33
+ for (const hash of manifest.commits) {
34
+ const commitPath = path.join(branchPath, `${hash}.json`);
35
+
36
+ if (!fs.existsSync(commitPath)) continue;
37
+
38
+ const commit = JSON.parse(fs.readFileSync(commitPath, 'utf8'));
39
+
40
+ for (const [filePath, changeSet] of Object.entries(commit.changes)) {
41
+ if (Array.isArray(changeSet)) {
42
+ let currentContent = state[filePath] || '';
43
+
44
+ for (const operation of changeSet) {
45
+ if (operation.type === 'insert') {
46
+ currentContent = `${currentContent.slice(0, operation.position)}${operation.content}${currentContent.slice(operation.position)}`;
47
+ } else if (operation.type === 'delete') {
48
+ currentContent = `${currentContent.slice(0, operation.position)}${currentContent.slice(operation.position + operation.length)}`;
49
+ }
50
+ }
51
+
52
+ state[filePath] = currentContent;
53
+
54
+ } else {
55
+ if (changeSet.type === 'createFile') {
56
+ state[filePath] = changeSet.content;
57
+ } else if (changeSet.type === 'deleteFile') {
58
+ delete state[filePath];
59
+ }
60
+ }
61
+ }
62
+
63
+ if (hash === targetHash) break;
64
+ }
65
+
66
+ return state;
67
+ };
@@ -0,0 +1,228 @@
1
+ /**
2
+ * art - Modern version control.
3
+ * Module: Workflow (v0.2.5)
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const crypto = require('crypto');
9
+
10
+ /**
11
+ * Compares the working directory against the last commit and pending stage.
12
+ */
13
+
14
+ function status () {
15
+ const root = process.cwd();
16
+ const artPath = path.join(root, '.art');
17
+ const artJsonPath = path.join(artPath, 'art.json');
18
+
19
+ if (!fs.existsSync(artJsonPath)) {
20
+ throw new Error('No art repository found.');
21
+ }
22
+
23
+ const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
24
+ const activeBranch = artJson.active.branch;
25
+ const stagePath = path.join(artPath, 'stage.json');
26
+
27
+ let stagedFiles = {};
28
+
29
+ if (fs.existsSync(stagePath)) {
30
+ stagedFiles = JSON.parse(fs.readFileSync(stagePath, 'utf8')).changes;
31
+ }
32
+
33
+ const getStateByHash = require('../utils/getStateByHash');
34
+ const activeState = getStateByHash(activeBranch, artJson.active.parent) || {};
35
+
36
+ const allWorkDirFiles = fs.readdirSync(root, { recursive: true })
37
+ .filter(f => !f.startsWith('.art') && !fs.statSync(path.join(root, f)).isDirectory());
38
+
39
+ const untracked = [];
40
+ const modified = [];
41
+
42
+ for (const file of allWorkDirFiles) {
43
+ const isStaged = !!stagedFiles[file];
44
+ const isActive = !!activeState[file];
45
+
46
+ if (!isStaged && !isActive) {
47
+ untracked.push(file);
48
+ } else if (!isStaged && isActive) {
49
+ const currentContent = fs.readFileSync(path.join(root, file), 'utf8');
50
+
51
+ if (currentContent !== activeState[file]) {
52
+ modified.push(file);
53
+ }
54
+ }
55
+ }
56
+
57
+ return {
58
+ activeBranch,
59
+ lastCommit: artJson.active.parent,
60
+ staged: Object.keys(stagedFiles),
61
+ modified,
62
+ untracked
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Updates or creates a JSON diff in the stage.json file.
68
+ * Implements character-precise position tracking.
69
+ */
70
+
71
+ function add (targetPath) {
72
+ const root = process.cwd();
73
+ const artPath = path.join(root, '.art');
74
+ const stagePath = path.join(artPath, 'stage.json');
75
+ const artJsonPath = path.join(artPath, 'art.json');
76
+ const fullPath = path.resolve(root, targetPath);
77
+
78
+ if (!fs.existsSync(fullPath)) {
79
+ throw new Error(`Path does not exist: ${targetPath}`);
80
+ }
81
+
82
+ let stage = { changes: {} };
83
+
84
+ if (fs.existsSync(stagePath)) {
85
+ stage = JSON.parse(fs.readFileSync(stagePath, 'utf8'));
86
+ }
87
+
88
+ const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
89
+ const getStateByHash = require('../utils/getStateByHash');
90
+ const activeState = getStateByHash(artJson.active.branch, artJson.active.parent) || {};
91
+
92
+ const stats = fs.statSync(fullPath);
93
+
94
+ let filesToProcess = [];
95
+
96
+ if (stats.isDirectory()) {
97
+ filesToProcess = fs.readdirSync(fullPath, { recursive: true })
98
+ .filter(f => {
99
+ const absoluteF = path.join(fullPath, f);
100
+
101
+ return !fs.statSync(absoluteF).isDirectory() && !absoluteF.includes('.art');
102
+ })
103
+ .map(f => path.relative(root, path.join(fullPath, f)));
104
+ } else {
105
+ filesToProcess = [path.relative(root, fullPath)];
106
+ }
107
+
108
+ for (const relPath of filesToProcess) {
109
+ const currentContent = fs.readFileSync(path.join(root, relPath), 'utf8');
110
+ const previousContent = activeState[relPath];
111
+
112
+ if (previousContent === undefined) {
113
+ stage.changes[relPath] = {
114
+ type: 'createFile',
115
+ content: currentContent
116
+ };
117
+
118
+ continue;
119
+ }
120
+
121
+ if (currentContent !== previousContent) {
122
+ const operations = [];
123
+
124
+ let start = 0;
125
+
126
+ while (start < previousContent.length && start < currentContent.length && previousContent[start] === currentContent[start]) {
127
+ start++;
128
+ }
129
+
130
+ let oldEnd = previousContent.length - 1;
131
+ let newEnd = currentContent.length - 1;
132
+
133
+ while (oldEnd >= start && newEnd >= start && previousContent[oldEnd] === currentContent[newEnd]) {
134
+ oldEnd--;
135
+ newEnd--;
136
+ }
137
+
138
+ const deletionLength = oldEnd - start + 1;
139
+
140
+ if (deletionLength > 0) {
141
+ operations.push({
142
+ type: 'delete',
143
+ position: start,
144
+ length: deletionLength
145
+ });
146
+ }
147
+
148
+ const insertionContent = currentContent.slice(start, newEnd + 1);
149
+
150
+ if (insertionContent.length > 0) {
151
+ operations.push({
152
+ type: 'insert',
153
+ position: start,
154
+ content: insertionContent
155
+ });
156
+ }
157
+
158
+ if (operations.length > 0) {
159
+ stage.changes[relPath] = operations;
160
+ }
161
+ }
162
+ }
163
+
164
+ fs.writeFileSync(stagePath, JSON.stringify(stage, null, 2));
165
+
166
+ return `Added ${filesToProcess.length} file(s) to stage.`;
167
+ }
168
+
169
+ /**
170
+ * Finalizes the stage into a commit file.
171
+ */
172
+
173
+ function commit (message) {
174
+ if (!message) {
175
+ throw new Error('A commit message is required.');
176
+ }
177
+
178
+ const artPath = path.join(process.cwd(), '.art');
179
+ const stagePath = path.join(artPath, 'stage.json');
180
+ const artJsonPath = path.join(artPath, 'art.json');
181
+
182
+ if (!fs.existsSync(stagePath)) {
183
+ throw new Error('Nothing to commit (stage is empty).');
184
+ }
185
+
186
+ const stage = JSON.parse(fs.readFileSync(stagePath, 'utf8'));
187
+ const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
188
+ const branch = artJson.active.branch;
189
+ const timestamp = Date.now();
190
+
191
+ const hash = crypto
192
+ .createHash('sha1')
193
+ .update(JSON.stringify(stage.changes) + timestamp + message)
194
+ .digest('hex');
195
+
196
+ const commitObject = {
197
+ hash,
198
+ message,
199
+ timestamp,
200
+ parent: artJson.active.parent,
201
+ changes: stage.changes
202
+ };
203
+
204
+ const branchHistoryDir = path.join(artPath, 'history', 'local', branch);
205
+ const commitFilePath = path.join(branchHistoryDir, `${hash}.json`);
206
+ const manifestPath = path.join(branchHistoryDir, 'manifest.json');
207
+
208
+ fs.writeFileSync(commitFilePath, JSON.stringify(commitObject, null, 2));
209
+
210
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
211
+
212
+ manifest.commits.push(hash);
213
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
214
+
215
+ artJson.active.parent = hash;
216
+ fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
217
+ fs.unlinkSync(stagePath);
218
+
219
+ return `[${branch} ${hash.slice(0, 7)}] ${message}`;
220
+ }
221
+
222
+ module.exports = {
223
+ __libraryVersion: '0.2.5',
224
+ __libraryAPIName: 'Workflow',
225
+ status,
226
+ add,
227
+ commit
228
+ };