pagan-artifact 0.2.8 → 0.3.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/setup/index.js CHANGED
@@ -1,99 +1,133 @@
1
1
  /**
2
2
  * art - Modern version control.
3
- * Module: Setup (v0.2.8)
3
+ * Module: Setup (v0.3.0)
4
4
  */
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
 
9
9
  const pkg = require('../package.json');
10
- const { remote } = require('../contributions');
11
10
  const shouldIgnore = require('../utils/shouldIgnore');
12
11
 
13
12
  const ARTIFACT_HOST = pkg.artConfig.host || 'http://localhost:1337';
14
13
 
15
14
  /**
16
- * Initializes the local .art directory structure.
15
+ * Internal helper to create the .art directory tree.
17
16
  */
18
17
 
19
- function init (directoryPath = process.cwd()) {
20
- const artDirectory = path.join(directoryPath, '.art');
21
-
22
- const folders = [
23
- '',
24
- 'root',
25
- 'history',
26
- 'history/local',
27
- 'history/local/main',
28
- 'history/remote',
29
- 'history/remote/main'
30
- ];
31
-
32
- if (fs.existsSync(artDirectory)) {
33
- return `Reinitialized existing art repository in ${artDirectory}`;
34
- }
35
-
36
- for (const folder of folders) {
37
- const fullPath = path.join(artDirectory, folder);
38
-
39
- if (!fs.existsSync(fullPath)) {
40
- fs.mkdirSync(fullPath, { recursive: true });
41
- }
42
- }
43
-
44
- const files = fs.readdirSync(directoryPath, { recursive: true })
45
- .filter(f => {
46
- const isInternal = f === '.art' || f.startsWith('.art' + path.sep);
47
-
48
- return !isInternal && !shouldIgnore(f);
49
- });
50
-
51
- const rootManifest = { files: [] };
52
-
53
- for (const file of files) {
54
- const fullPath = path.join(directoryPath, file);
55
-
56
- if (fs.lstatSync(fullPath).isFile()) {
57
- rootManifest.files.push({
58
- path: file,
59
- content: fs.readFileSync(fullPath, 'utf8')
60
- });
61
- }
62
- }
63
-
64
- fs.writeFileSync(
65
- path.join(artDirectory, 'root/manifest.json'),
66
- JSON.stringify(rootManifest, null, 2)
67
- );
68
-
69
- fs.writeFileSync(
70
- path.join(artDirectory, 'history/local/main/manifest.json'),
71
- JSON.stringify({ commits: [] }, null, 2)
72
- );
73
-
74
- fs.writeFileSync(
75
- path.join(artDirectory, 'history/remote/main/manifest.json'),
76
- JSON.stringify({ commits: [] }, null, 2)
77
- );
78
-
79
- const artFile = {
80
- active: { branch: 'main', parent: null },
81
- remote: '',
82
- configuration: { handle: '', personalAccessToken: '' }
83
- };
84
-
85
- fs.writeFileSync(
86
- path.join(artDirectory, 'art.json'),
87
- JSON.stringify(artFile, null, 2)
88
- );
89
-
90
- return `Initialized empty art repository in ${artDirectory}`;
91
- }
18
+ function ensureDirStructure(artDirectory) {
19
+ const folders = [
20
+ '',
21
+ 'root',
22
+ 'history',
23
+ 'history/local',
24
+ 'history/local/main',
25
+ 'history/remote',
26
+ 'history/remote/main'
27
+ ];
28
+
29
+ for (const folder of folders) {
30
+ const fullPath = path.join(artDirectory, folder);
31
+
32
+ if (!fs.existsSync(fullPath)) {
33
+ fs.mkdirSync(fullPath, { recursive: true });
34
+ }
35
+ }
36
+ }
92
37
 
93
38
  /**
94
- * Clones a repository by fetching manifests and commits via POST.
95
- * @param {string} repoSlug - The handle/repo identifier.
96
- * @param {string} providedToken - Optional token for authentication.
39
+ * Initializes the local .art directory structure and indexes current files.
40
+ */
41
+
42
+ function init (directoryPath = process.cwd()) {
43
+ const artDirectory = path.join(directoryPath, '.art');
44
+
45
+ if (fs.existsSync(artDirectory)) {
46
+ return `Reinitialized existing art repository in ${artDirectory}`;
47
+ }
48
+
49
+ ensureDirStructure(artDirectory);
50
+
51
+ const files = fs.readdirSync(directoryPath, { recursive: true })
52
+ .filter(f => {
53
+ const isInternal = f === '.art' || f.startsWith('.art' + path.sep);
54
+ return !isInternal && !shouldIgnore(f);
55
+ });
56
+
57
+ const rootMasterManifest = { parts: [] };
58
+ let currentPartFiles = [];
59
+ let currentPartChars = 0;
60
+ const MAX_PART_CHARS = 32000000;
61
+
62
+ const saveManifestPart = () => {
63
+ if (currentPartFiles.length === 0) return;
64
+
65
+ const partIndex = rootMasterManifest.parts.length;
66
+ const partName = `manifest.part.${Date.now()}.${partIndex}.json`;
67
+ const partPath = path.join(artDirectory, 'root', partName);
68
+
69
+ fs.writeFileSync(
70
+ partPath,
71
+ JSON.stringify({ files: currentPartFiles }, null, 2)
72
+ );
73
+
74
+ rootMasterManifest.parts.push(partName);
75
+ currentPartFiles = [];
76
+ currentPartChars = 0;
77
+ };
78
+
79
+ for (const file of files) {
80
+ const fullPath = path.join(directoryPath, file);
81
+
82
+ if (fs.lstatSync(fullPath).isFile()) {
83
+ const content = fs.readFileSync(fullPath, 'utf8');
84
+
85
+ if (currentPartChars + content.length > MAX_PART_CHARS && currentPartFiles.length > 0) {
86
+ saveManifestPart();
87
+ }
88
+
89
+ currentPartFiles.push({
90
+ path: file,
91
+ content: content
92
+ });
93
+
94
+ currentPartChars += content.length;
95
+ }
96
+ }
97
+
98
+ saveManifestPart();
99
+
100
+ fs.writeFileSync(
101
+ path.join(artDirectory, 'root/manifest.json'),
102
+ JSON.stringify(rootMasterManifest, null, 2)
103
+ );
104
+
105
+ fs.writeFileSync(
106
+ path.join(artDirectory, 'history/local/main/manifest.json'),
107
+ JSON.stringify({ commits: [] }, null, 2)
108
+ );
109
+
110
+ fs.writeFileSync(
111
+ path.join(artDirectory, 'history/remote/main/manifest.json'),
112
+ JSON.stringify({ commits: [] }, null, 2)
113
+ );
114
+
115
+ const artFile = {
116
+ active: { branch: 'main', parent: null },
117
+ remote: '',
118
+ configuration: { handle: '', personalAccessToken: '' }
119
+ };
120
+
121
+ fs.writeFileSync(
122
+ path.join(artDirectory, 'art.json'),
123
+ JSON.stringify(artFile, null, 2)
124
+ );
125
+
126
+ return `Initialized empty art repository in ${artDirectory}.`;
127
+ }
128
+
129
+ /**
130
+ * Clone a repository and replay history.
97
131
  */
98
132
 
99
133
  async function clone (repoSlug, providedToken = null) {
@@ -110,19 +144,20 @@ async function clone (repoSlug, providedToken = null) {
110
144
  }
111
145
 
112
146
  fs.mkdirSync(targetPath, { recursive: true });
113
- init(targetPath);
147
+
148
+ const artPath = path.join(targetPath, '.art');
149
+
150
+ ensureDirStructure(artPath);
114
151
  process.chdir(targetPath);
115
152
 
116
153
  try {
117
- const artPath = path.join(targetPath, '.art');
118
154
  const artJsonPath = path.join(artPath, 'art.json');
119
- const artJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
120
155
 
121
- if (providedToken) {
122
- artJson.configuration.personalAccessToken = providedToken;
123
- }
124
-
125
- artJson.remote = `${ARTIFACT_HOST}/${handle}/${repo}`;
156
+ const artJson = {
157
+ active: { branch: 'main', parent: null },
158
+ remote: `${ARTIFACT_HOST}/${handle}/${repo}`,
159
+ configuration: { handle: '', personalAccessToken: providedToken || '' }
160
+ };
126
161
 
127
162
  fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
128
163
 
@@ -136,26 +171,26 @@ async function clone (repoSlug, providedToken = null) {
136
171
  handle,
137
172
  repo,
138
173
  branch: 'main',
139
-
140
174
  ...(token && { personalAccessToken: token })
141
175
  })
142
176
  });
143
177
 
144
- if (!rootRes.ok) {
145
- throw new Error(`Failed to fetch root: ${rootRes.statusText}`);
146
- }
178
+ const masterManifest = await rootRes.json();
147
179
 
148
- const rootManifest = await rootRes.json();
180
+ fs.writeFileSync(path.join(artPath, 'root/manifest.json'), JSON.stringify(masterManifest, null, 2));
149
181
 
150
- if (rootManifest.files) {
151
- fs.writeFileSync(
152
- path.join(artPath, 'root/manifest.json'),
153
- JSON.stringify(rootManifest, null, 2)
154
- );
182
+ for (const partName of masterManifest.parts) {
183
+ const partRes = await fetch(`${ARTIFACT_HOST}/part`, {
184
+ method: 'POST',
185
+ headers: { 'Content-Type': 'application/json' },
186
+ body: JSON.stringify({ handle, repo, partName, ...(token && { personalAccessToken: token }) })
187
+ });
155
188
 
156
- for (const file of rootManifest.files) {
157
- const workingPath = path.join(targetPath, file.path);
189
+ const partData = await partRes.json();
190
+ fs.writeFileSync(path.join(artPath, 'root', partName), JSON.stringify(partData, null, 2));
158
191
 
192
+ for (const file of partData.files) {
193
+ const workingPath = path.join(targetPath, file.path);
159
194
  fs.mkdirSync(path.dirname(workingPath), { recursive: true });
160
195
  fs.writeFileSync(workingPath, file.content);
161
196
  }
@@ -164,79 +199,85 @@ async function clone (repoSlug, providedToken = null) {
164
199
  const historyRes = await fetch(`${ARTIFACT_HOST}/manifest`, {
165
200
  method: 'POST',
166
201
  headers: { 'Content-Type': 'application/json' },
167
- body: JSON.stringify({
168
- type: 'history',
169
- handle,
170
- repo,
171
- branch: 'main',
172
-
173
- ...(token && { personalAccessToken: token })
174
- })
202
+ body: JSON.stringify({ type: 'history', handle, repo, branch: 'main', ...(token && { personalAccessToken: token }) })
175
203
  });
176
204
 
177
205
  const historyManifest = await historyRes.json();
178
- const localManifest = { commits: [] };
179
206
  const localHistoryDir = path.join(artPath, 'history/local/main');
180
207
  const remoteHistoryDir = path.join(artPath, 'history/remote/main');
181
208
 
182
- if (historyManifest.commits) {
183
- for (const commitHash of historyManifest.commits) {
184
- const commitRes = await fetch(`${ARTIFACT_HOST}/commit`, {
185
- method: 'POST',
186
- headers: { 'Content-Type': 'application/json' },
187
- body: JSON.stringify({
188
- handle,
189
- repo,
190
- branch: 'main',
191
- hash: commitHash,
192
-
193
- ...(token && { personalAccessToken: token })
209
+ for (const commitHash of historyManifest.commits) {
210
+ const commitRes = await fetch(`${ARTIFACT_HOST}/commit`, {
211
+ method: 'POST',
212
+ headers: { 'Content-Type': 'application/json' },
213
+ body: JSON.stringify({
214
+ handle,
215
+ repo,
216
+ branch: 'main',
217
+ hash: commitHash,
218
+
219
+ ...(token && { personalAccessToken: token
194
220
  })
195
- });
221
+ })
222
+ });
196
223
 
197
- const commitDiff = await commitRes.json();
224
+ const commitMaster = await commitRes.json();
198
225
 
199
- for (const filePath of Object.keys(commitDiff.changes)) {
200
- const fullPath = path.join(targetPath, filePath);
201
- const changeSet = commitDiff.changes[filePath];
226
+ let fullChanges = {};
202
227
 
203
- if (Array.isArray(changeSet)) {
204
- let content = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : '';
228
+ if (commitMaster.parts && commitMaster.parts.length > 0) {
229
+ for (const partName of commitMaster.parts) {
230
+ const partRes = await fetch(`${ARTIFACT_HOST}/commit/part`, {
231
+ method: 'POST',
232
+ headers: { 'Content-Type': 'application/json' },
233
+ body: JSON.stringify({ handle, repo, branch: 'main', partName, ...(token && { personalAccessToken: token }) })
234
+ });
205
235
 
206
- for (const operation of changeSet) {
207
- if (operation.type === 'insert') {
208
- content = `${content.slice(0, operation.position)}${operation.content}${content.slice(operation.position)}`;
209
- } else if (operation.type === 'delete') {
210
- content = `${content.slice(0, operation.position)}${content.slice(operation.position + operation.length)}`;
211
- }
212
- }
236
+ const partData = await partRes.json();
237
+ fs.writeFileSync(path.join(localHistoryDir, partName), JSON.stringify(partData, null, 2));
238
+ fs.writeFileSync(path.join(remoteHistoryDir, partName), JSON.stringify(partData, null, 2));
213
239
 
214
- fs.writeFileSync(fullPath, content);
215
- } else if (changeSet.type === 'createFile') {
216
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
217
- fs.writeFileSync(fullPath, changeSet.content || '');
218
- } else if (changeSet.type === 'deleteFile' && fs.existsSync(fullPath)) {
219
- fs.unlinkSync(fullPath);
220
- }
221
- }
240
+ Object.assign(fullChanges, partData.changes);
241
+ }
242
+ } else if (commitMaster.changes) {
243
+ fullChanges = commitMaster.changes;
244
+ }
245
+
246
+ const masterContent = JSON.stringify(commitMaster, null, 2);
222
247
 
223
- const commitContent = JSON.stringify(commitDiff, null, 2);
248
+ fs.writeFileSync(path.join(localHistoryDir, `${commitHash}.json`), masterContent);
249
+ fs.writeFileSync(path.join(remoteHistoryDir, `${commitHash}.json`), masterContent);
224
250
 
225
- fs.writeFileSync(path.join(localHistoryDir, `${commitHash}.json`), commitContent);
226
- fs.writeFileSync(path.join(remoteHistoryDir, `${commitHash}.json`), commitContent);
251
+ for (const [filePath, changeSet] of Object.entries(fullChanges)) {
252
+ const fullPath = path.join(targetPath, filePath);
227
253
 
228
- localManifest.commits.push(commitHash);
229
- }
254
+ if (Array.isArray(changeSet)) {
255
+ let content = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : '';
256
+ for (const op of changeSet) {
257
+ if (op.type === 'insert') {
258
+ content = content.slice(0, op.position) + op.content + content.slice(op.position);
259
+ } else if (op.type === 'delete') {
260
+ content = content.slice(0, op.position) + content.slice(op.position + op.length);
261
+ }
262
+ }
263
+ fs.writeFileSync(fullPath, content);
264
+ } else if (changeSet.type === 'createFile') {
265
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
266
+ fs.writeFileSync(fullPath, changeSet.content || '');
267
+ } else if (changeSet.type === 'deleteFile' && fs.existsSync(fullPath)) {
268
+ fs.unlinkSync(fullPath);
269
+ }
270
+ }
230
271
  }
231
272
 
232
- fs.writeFileSync(path.join(localHistoryDir, 'manifest.json'), JSON.stringify(localManifest, null, 2));
233
- fs.writeFileSync(path.join(remoteHistoryDir, 'manifest.json'), JSON.stringify(localManifest, null, 2));
273
+ const manifestJson = JSON.stringify({ commits: historyManifest.commits }, null, 2);
234
274
 
235
- const updatedDepJson = JSON.parse(fs.readFileSync(artJsonPath, 'utf8'));
275
+ fs.writeFileSync(path.join(localHistoryDir, 'manifest.json'), manifestJson);
276
+ fs.writeFileSync(path.join(remoteHistoryDir, 'manifest.json'), manifestJson);
236
277
 
237
- if (localManifest.commits.length > 0) {
238
- updatedDepJson.active.parent = localManifest.commits[localManifest.commits.length - 1];
239
- fs.writeFileSync(artJsonPath, JSON.stringify(updatedDepJson, null, 2));
278
+ if (historyManifest.commits.length > 0) {
279
+ artJson.active.parent = historyManifest.commits[historyManifest.commits.length - 1];
280
+ fs.writeFileSync(artJsonPath, JSON.stringify(artJson, null, 2));
240
281
  }
241
282
 
242
283
  return `Successfully cloned and replayed ${repoSlug}.`;
@@ -250,8 +291,7 @@ async function clone (repoSlug, providedToken = null) {
250
291
  /**
251
292
  * Updates the configuration in art.json.
252
293
  */
253
-
254
- function config (key, value) {
294
+ function config(key, value) {
255
295
  const manifestPath = path.join(process.cwd(), '.art', 'art.json');
256
296
 
257
297
  if (!fs.existsSync(manifestPath)) {
@@ -262,7 +302,6 @@ function config (key, value) {
262
302
 
263
303
  if (key && value !== undefined) {
264
304
  manifest.configuration[key] = value;
265
-
266
305
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
267
306
  }
268
307
 
@@ -274,5 +313,6 @@ module.exports = {
274
313
  __libraryAPIName: 'Setup',
275
314
  init,
276
315
  clone,
277
- config
316
+ config,
317
+ ensureDirStructure
278
318
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * art - Modern version control.
3
- * Module: Utils (v0.2.8)
3
+ * Module: Utils (v0.3.0)
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -10,58 +10,78 @@ const path = require('path');
10
10
  * Helper to reconstruct file states at a specific commit hash.
11
11
  */
12
12
 
13
- module.exports = (branchName, targetHash) => {
14
- const artPath = path.join(process.cwd(), '.art');
15
- const rootPath = path.join(artPath, 'root/manifest.json');
13
+ module.exports = (branchName, targetHash) => {
14
+ const artPath = path.join(process.cwd(), '.art');
15
+ const rootPath = path.join(artPath, 'root/manifest.json');
16
16
 
17
- if (!fs.existsSync(rootPath)) return {};
17
+ if (!fs.existsSync(rootPath)) return {};
18
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');
19
+ const rootMaster = JSON.parse(fs.readFileSync(rootPath, 'utf8'));
20
+ const branchPath = path.join(artPath, 'history/local', branchName);
21
+ const manifestPath = path.join(branchPath, 'manifest.json');
22
22
 
23
- if (!fs.existsSync(manifestPath)) return {};
23
+ if (!fs.existsSync(manifestPath)) return {};
24
24
 
25
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
25
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
26
26
 
27
- let state = {};
27
+ let state = {};
28
28
 
29
- for (const file of rootManifest.files) {
30
- state[file.path] = file.content;
31
- }
29
+ for (const partName of rootMaster.parts) {
30
+ const partPath = path.join(artPath, 'root', partName);
31
+ const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
32
32
 
33
- for (const hash of manifest.commits) {
34
- const commitPath = path.join(branchPath, `${hash}.json`);
33
+ for (const file of partData.files) {
34
+ state[file.path] = file.content;
35
+ }
36
+ }
35
37
 
36
- if (!fs.existsSync(commitPath)) continue;
38
+ if (!targetHash) return state;
37
39
 
38
- const commit = JSON.parse(fs.readFileSync(commitPath, 'utf8'));
40
+ for (const hash of manifest.commits) {
41
+ const commitPath = path.join(branchPath, `${hash}.json`);
39
42
 
40
- for (const [filePath, changeSet] of Object.entries(commit.changes)) {
41
- if (Array.isArray(changeSet)) {
42
- let currentContent = state[filePath] || '';
43
+ if (!fs.existsSync(commitPath)) continue;
43
44
 
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
- }
45
+ const commitMaster = JSON.parse(fs.readFileSync(commitPath, 'utf8'));
51
46
 
52
- state[filePath] = currentContent;
47
+ let fullChanges = {};
53
48
 
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
- }
49
+ if (commitMaster.parts && Array.isArray(commitMaster.parts)) {
50
+ for (const partName of commitMaster.parts) {
51
+ const partPath = path.join(branchPath, partName);
52
+ if (fs.existsSync(partPath)) {
53
+ const partData = JSON.parse(fs.readFileSync(partPath, 'utf8'));
54
+ Object.assign(fullChanges, partData.changes);
55
+ }
56
+ }
57
+ } else if (commitMaster.changes) {
58
+ fullChanges = commitMaster.changes;
59
+ }
62
60
 
63
- if (hash === targetHash) break;
64
- }
61
+ for (const [filePath, changeSet] of Object.entries(fullChanges)) {
62
+ if (Array.isArray(changeSet)) {
63
+ let currentContent = state[filePath] || '';
65
64
 
66
- return state;
67
- };
65
+ for (const operation of changeSet) {
66
+ if (operation.type === 'insert') {
67
+ currentContent = `${currentContent.slice(0, operation.position)}${operation.content}${currentContent.slice(operation.position)}`;
68
+ } else if (operation.type === 'delete') {
69
+ currentContent = `${currentContent.slice(0, operation.position)}${currentContent.slice(operation.position + operation.length)}`;
70
+ }
71
+ }
72
+
73
+ state[filePath] = currentContent;
74
+ } else {
75
+ if (changeSet.type === 'createFile') {
76
+ state[filePath] = changeSet.content;
77
+ } else if (changeSet.type === 'deleteFile') {
78
+ delete state[filePath];
79
+ }
80
+ }
81
+ }
82
+
83
+ if (hash === targetHash) break;
84
+ }
85
+
86
+ return state;
87
+ };