isomorphic-jj 1.2.0 → 1.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/dist/index.cjs +134 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +134 -135
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/repository.js +134 -135
- package/src/types.d.ts +165 -2
package/dist/index.mjs
CHANGED
|
@@ -6291,69 +6291,116 @@ async function createJJ(options) {
|
|
|
6291
6291
|
* @param {string[]} [args.paths] - Paths to move between changes (for history operations)
|
|
6292
6292
|
* @returns {Promise<Object>} Returns change object for history operations, file info for file operations
|
|
6293
6293
|
*/
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6294
|
+
/**
|
|
6295
|
+
* Move a file in the working copy
|
|
6296
|
+
*
|
|
6297
|
+
* @param {Object} args - Move file arguments
|
|
6298
|
+
* @param {string} args.from - Source file path
|
|
6299
|
+
* @param {string} args.to - Destination file path
|
|
6300
|
+
* @returns {Promise<Object>} Operation result including from, to paths and file stats
|
|
6301
|
+
*/
|
|
6302
|
+
async moveFile(args) {
|
|
6303
|
+
if (!args.from || typeof args.from !== 'string') {
|
|
6304
|
+
throw new JJError('INVALID_ARGUMENT', 'Missing or invalid from path', {
|
|
6305
|
+
suggestion: 'Provide a valid source file path',
|
|
6306
|
+
provided: { from: args.from, type: typeof args.from },
|
|
6298
6307
|
});
|
|
6299
6308
|
}
|
|
6300
6309
|
|
|
6301
|
-
|
|
6302
|
-
|
|
6310
|
+
if (!args.to || typeof args.to !== 'string') {
|
|
6311
|
+
throw new JJError('INVALID_ARGUMENT', 'Missing or invalid to path', {
|
|
6312
|
+
suggestion: 'Provide a valid destination file path',
|
|
6313
|
+
provided: { to: args.to, type: typeof args.to },
|
|
6314
|
+
});
|
|
6315
|
+
}
|
|
6303
6316
|
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6317
|
+
// Prevent moving to the same location
|
|
6318
|
+
if (args.from === args.to) {
|
|
6319
|
+
throw new JJError('INVALID_OPERATION', 'Source and destination paths are the same', {
|
|
6320
|
+
path: args.from,
|
|
6321
|
+
suggestion: 'Specify a different destination path',
|
|
6322
|
+
});
|
|
6308
6323
|
}
|
|
6309
|
-
},
|
|
6310
6324
|
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
*/
|
|
6318
|
-
_detectHistoryOperation(args) {
|
|
6319
|
-
// Explicit history operation indicators
|
|
6320
|
-
if (args.paths || args.changeId || args.newParent) {
|
|
6321
|
-
return true;
|
|
6325
|
+
// Prevent absolute paths
|
|
6326
|
+
if (args.from.startsWith('/') || args.to.startsWith('/')) {
|
|
6327
|
+
throw new JJError('INVALID_PATH', 'Absolute paths are not allowed', {
|
|
6328
|
+
suggestion: 'Use paths relative to the repository root',
|
|
6329
|
+
provided: { from: args.from, to: args.to },
|
|
6330
|
+
});
|
|
6322
6331
|
}
|
|
6323
6332
|
|
|
6324
|
-
//
|
|
6325
|
-
if (args.from
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6333
|
+
// Prevent parent directory traversal
|
|
6334
|
+
if (args.from.includes('..') || args.to.includes('..')) {
|
|
6335
|
+
throw new JJError('INVALID_PATH', 'Parent directory traversal (..) is not allowed', {
|
|
6336
|
+
suggestion: 'Use paths within the repository',
|
|
6337
|
+
provided: { from: args.from, to: args.to },
|
|
6338
|
+
});
|
|
6339
|
+
}
|
|
6330
6340
|
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
// Users should use explicit { changeId, newParent } for clarity
|
|
6334
|
-
if (fromLooksLikeChangeId && toLooksLikeChangeId) {
|
|
6335
|
-
// This is ambiguous - throw a helpful error
|
|
6336
|
-
throw new JJError('AMBIGUOUS_OPERATION',
|
|
6337
|
-
'Ambiguous move operation: arguments look like change IDs', {
|
|
6338
|
-
suggestion: 'Use { changeId, newParent } for history operations or ensure file paths don\'t match change ID pattern',
|
|
6339
|
-
args,
|
|
6340
|
-
});
|
|
6341
|
-
}
|
|
6341
|
+
const fromPath = `${dir}/${args.from}`;
|
|
6342
|
+
const toPath = `${dir}/${args.to}`;
|
|
6342
6343
|
|
|
6343
|
-
|
|
6344
|
+
// Check if source file exists
|
|
6345
|
+
try {
|
|
6346
|
+
await fs.promises.stat(fromPath);
|
|
6347
|
+
} catch (error) {
|
|
6348
|
+
throw new JJError('FILE_NOT_FOUND', `Source file not found: ${args.from}`, {
|
|
6349
|
+
path: args.from,
|
|
6350
|
+
suggestion: 'Check that the source file exists',
|
|
6351
|
+
});
|
|
6344
6352
|
}
|
|
6345
6353
|
|
|
6346
|
-
|
|
6354
|
+
// Ensure destination directory exists
|
|
6355
|
+
const toDir = toPath.substring(0, toPath.lastIndexOf('/'));
|
|
6356
|
+
try {
|
|
6357
|
+
await fs.promises.mkdir(toDir, { recursive: true });
|
|
6358
|
+
} catch (error) {
|
|
6359
|
+
throw new JJError('FILE_SYSTEM_ERROR', `Failed to create destination directory: ${error.message}`, {
|
|
6360
|
+
path: toDir,
|
|
6361
|
+
error: error.message,
|
|
6362
|
+
});
|
|
6363
|
+
}
|
|
6364
|
+
|
|
6365
|
+
// Move the file
|
|
6366
|
+
try {
|
|
6367
|
+
await fs.promises.rename(fromPath, toPath);
|
|
6368
|
+
} catch (error) {
|
|
6369
|
+
throw new JJError('FILE_SYSTEM_ERROR', `Failed to move file: ${error.message}`, {
|
|
6370
|
+
from: args.from,
|
|
6371
|
+
to: args.to,
|
|
6372
|
+
error: error.message,
|
|
6373
|
+
});
|
|
6374
|
+
}
|
|
6375
|
+
|
|
6376
|
+
// Update working copy tracking
|
|
6377
|
+
await workingCopy.load();
|
|
6378
|
+
await workingCopy.untrackFile(args.from);
|
|
6379
|
+
const stats = await fs.promises.stat(toPath);
|
|
6380
|
+
await workingCopy.trackFile(args.to, {
|
|
6381
|
+
mtime: stats.mtime,
|
|
6382
|
+
size: stats.size,
|
|
6383
|
+
mode: stats.mode,
|
|
6384
|
+
});
|
|
6385
|
+
|
|
6386
|
+
return {
|
|
6387
|
+
from: args.from,
|
|
6388
|
+
to: args.to,
|
|
6389
|
+
size: stats.size,
|
|
6390
|
+
mode: stats.mode,
|
|
6391
|
+
mtime: stats.mtime,
|
|
6392
|
+
};
|
|
6347
6393
|
},
|
|
6348
6394
|
|
|
6349
6395
|
/**
|
|
6350
|
-
* Move
|
|
6396
|
+
* Move (rebase) a change to a new parent
|
|
6351
6397
|
*
|
|
6352
|
-
* @
|
|
6353
|
-
* @param {
|
|
6398
|
+
* @param {Object} args - Move change arguments
|
|
6399
|
+
* @param {string} args.changeId - Change ID to move (also accepts 'from' or 'source')
|
|
6400
|
+
* @param {string} args.newParent - New parent change ID (also accepts 'to' or 'destination')
|
|
6354
6401
|
* @returns {Promise<Object>} Updated change object
|
|
6355
6402
|
*/
|
|
6356
|
-
async
|
|
6403
|
+
async moveChange(args) {
|
|
6357
6404
|
await graph.load();
|
|
6358
6405
|
|
|
6359
6406
|
// Support multiple parameter names for flexibility
|
|
@@ -6441,110 +6488,62 @@ async function createJJ(options) {
|
|
|
6441
6488
|
},
|
|
6442
6489
|
|
|
6443
6490
|
/**
|
|
6444
|
-
*
|
|
6445
|
-
*
|
|
6446
|
-
* @private
|
|
6447
|
-
* @param {Object} args - Move arguments
|
|
6448
|
-
* @returns {Promise<Object>} Operation result including from, to paths and file stats
|
|
6491
|
+
* @deprecated Use moveFile() for files or moveChange() for history operations
|
|
6492
|
+
* Move operation - delegates to moveFile or moveChange based on arguments
|
|
6449
6493
|
*/
|
|
6450
|
-
async
|
|
6451
|
-
if (!args
|
|
6452
|
-
throw new JJError('INVALID_ARGUMENT', 'Missing or invalid
|
|
6453
|
-
suggestion: '
|
|
6454
|
-
provided: { from: args.from, type: typeof args.from },
|
|
6455
|
-
});
|
|
6456
|
-
}
|
|
6457
|
-
|
|
6458
|
-
if (!args.to || typeof args.to !== 'string') {
|
|
6459
|
-
throw new JJError('INVALID_ARGUMENT', 'Missing or invalid to path', {
|
|
6460
|
-
suggestion: 'Provide a valid destination file path',
|
|
6461
|
-
provided: { to: args.to, type: typeof args.to },
|
|
6494
|
+
async move(args) {
|
|
6495
|
+
if (!args || typeof args !== 'object') {
|
|
6496
|
+
throw new JJError('INVALID_ARGUMENT', 'Missing or invalid arguments', {
|
|
6497
|
+
suggestion: 'Use moveFile({ from, to }) for files or moveChange({ changeId, newParent }) for history',
|
|
6462
6498
|
});
|
|
6463
6499
|
}
|
|
6464
6500
|
|
|
6465
|
-
//
|
|
6466
|
-
|
|
6467
|
-
throw new JJError('INVALID_OPERATION', 'Source and destination paths are the same', {
|
|
6468
|
-
path: args.from,
|
|
6469
|
-
suggestion: 'Specify a different destination path',
|
|
6470
|
-
});
|
|
6471
|
-
}
|
|
6501
|
+
// Detect operation type based on arguments
|
|
6502
|
+
const isHistoryOp = this._detectHistoryOperation(args);
|
|
6472
6503
|
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
provided: { from: args.from, to: args.to },
|
|
6478
|
-
});
|
|
6504
|
+
if (isHistoryOp) {
|
|
6505
|
+
return await this.moveChange(args);
|
|
6506
|
+
} else {
|
|
6507
|
+
return await this.moveFile(args);
|
|
6479
6508
|
}
|
|
6509
|
+
},
|
|
6480
6510
|
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6511
|
+
/**
|
|
6512
|
+
* Detect if move operation is for history (rebase) or files
|
|
6513
|
+
*
|
|
6514
|
+
* @private
|
|
6515
|
+
* @param {Object} args - Move arguments
|
|
6516
|
+
* @returns {boolean} True if history operation, false if file operation
|
|
6517
|
+
*/
|
|
6518
|
+
_detectHistoryOperation(args) {
|
|
6519
|
+
// Explicit history operation indicators
|
|
6520
|
+
if (args.paths || args.changeId || args.newParent) {
|
|
6521
|
+
return true;
|
|
6487
6522
|
}
|
|
6488
6523
|
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
} catch (error) {
|
|
6496
|
-
throw new JJError('FILE_NOT_FOUND', `Source file not found: ${args.from}`, {
|
|
6497
|
-
path: args.from,
|
|
6498
|
-
fullPath: fromPath,
|
|
6499
|
-
suggestion: 'Check that the file exists',
|
|
6500
|
-
});
|
|
6501
|
-
}
|
|
6524
|
+
// If only from/to are present, check if they look like change IDs
|
|
6525
|
+
if (args.from && args.to && !args.changeId && !args.newParent && !args.paths) {
|
|
6526
|
+
// Change IDs are 32-character hex strings
|
|
6527
|
+
const changeIdPattern = /^[0-9a-f]{32}$/;
|
|
6528
|
+
const fromLooksLikeChangeId = changeIdPattern.test(args.from);
|
|
6529
|
+
const toLooksLikeChangeId = changeIdPattern.test(args.to);
|
|
6502
6530
|
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
`Failed to create destination directory: ${dirPath}`, {
|
|
6513
|
-
directory: dirPath,
|
|
6514
|
-
originalError: error.message,
|
|
6531
|
+
// If both look like change IDs, it's likely a history operation
|
|
6532
|
+
// However, we err on the side of file operations to be safe
|
|
6533
|
+
// Users should use explicit { changeId, newParent } for clarity
|
|
6534
|
+
if (fromLooksLikeChangeId && toLooksLikeChangeId) {
|
|
6535
|
+
// This is ambiguous - throw a helpful error
|
|
6536
|
+
throw new JJError('AMBIGUOUS_OPERATION',
|
|
6537
|
+
'Ambiguous move operation: arguments look like change IDs', {
|
|
6538
|
+
suggestion: 'Use { changeId, newParent } for history operations or ensure file paths don\'t match change ID pattern',
|
|
6539
|
+
args,
|
|
6515
6540
|
});
|
|
6516
6541
|
}
|
|
6517
|
-
}
|
|
6518
6542
|
|
|
6519
|
-
|
|
6520
|
-
try {
|
|
6521
|
-
await fs.promises.rename(fromPath, toPath);
|
|
6522
|
-
} catch (error) {
|
|
6523
|
-
throw new JJError('FILE_MOVE_FAILED', `Failed to move file: ${error.message}`, {
|
|
6524
|
-
from: args.from,
|
|
6525
|
-
to: args.to,
|
|
6526
|
-
originalError: error.message,
|
|
6527
|
-
});
|
|
6543
|
+
return false; // Treat as file operation
|
|
6528
6544
|
}
|
|
6529
6545
|
|
|
6530
|
-
|
|
6531
|
-
await workingCopy.load();
|
|
6532
|
-
await workingCopy.untrackFile(args.from);
|
|
6533
|
-
const stats = await fs.promises.stat(toPath);
|
|
6534
|
-
await workingCopy.trackFile(args.to, {
|
|
6535
|
-
mtime: stats.mtime,
|
|
6536
|
-
size: stats.size,
|
|
6537
|
-
mode: stats.mode,
|
|
6538
|
-
});
|
|
6539
|
-
|
|
6540
|
-
// Return useful information about the move operation
|
|
6541
|
-
return {
|
|
6542
|
-
from: args.from,
|
|
6543
|
-
to: args.to,
|
|
6544
|
-
size: stats.size,
|
|
6545
|
-
mode: stats.mode,
|
|
6546
|
-
mtime: stats.mtime,
|
|
6547
|
-
};
|
|
6546
|
+
return false;
|
|
6548
6547
|
},
|
|
6549
6548
|
|
|
6550
6549
|
/**
|
|
@@ -8051,7 +8050,7 @@ async function createJJ(options) {
|
|
|
8051
8050
|
* });
|
|
8052
8051
|
*/
|
|
8053
8052
|
async rebase(args) {
|
|
8054
|
-
return await this.
|
|
8053
|
+
return await this.moveChange(args);
|
|
8055
8054
|
},
|
|
8056
8055
|
|
|
8057
8056
|
/**
|