isomorphic-jj 1.0.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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +971 -0
  3. package/dist/index.cjs +12609 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.mjs +12600 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +90 -0
  8. package/src/api/repository.js +6885 -0
  9. package/src/backend/isomorphic-git-backend.js +687 -0
  10. package/src/backend/lazy-git-backend.js +180 -0
  11. package/src/browser/helpers.js +189 -0
  12. package/src/browser/index.d.ts +174 -0
  13. package/src/browser/index.js +12 -0
  14. package/src/core/background-ops.js +272 -0
  15. package/src/core/bookmark-store.js +232 -0
  16. package/src/core/change-graph.js +285 -0
  17. package/src/core/conflict-model.js +505 -0
  18. package/src/core/jj-checkout.js +78 -0
  19. package/src/core/jj-operation-store.js +120 -0
  20. package/src/core/jj-tree-state.js +94 -0
  21. package/src/core/jj-view-store.js +105 -0
  22. package/src/core/merge-driver-registry.js +432 -0
  23. package/src/core/operation-log.js +272 -0
  24. package/src/core/revset-engine.js +1081 -0
  25. package/src/core/storage-manager.js +207 -0
  26. package/src/core/tag-store.js +167 -0
  27. package/src/core/user-config.js +142 -0
  28. package/src/core/working-copy.js +314 -0
  29. package/src/core/workspace-manager.js +283 -0
  30. package/src/drivers/built-in-drivers.js +296 -0
  31. package/src/index.js +15 -0
  32. package/src/protos/local_working_copy.proto +76 -0
  33. package/src/protos/simple_op_store.proto +147 -0
  34. package/src/types.d.ts +1129 -0
  35. package/src/utils/errors.js +21 -0
  36. package/src/utils/id-generation.js +54 -0
  37. package/src/utils/validation.js +124 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * JJError - Custom error class for isomorphic-jj
3
+ *
4
+ * Provides structured errors with error codes, context, and user-actionable suggestions.
5
+ */
6
+ export class JJError extends Error {
7
+ /**
8
+ * @param {string} code - Error code (e.g., 'CHANGE_NOT_FOUND')
9
+ * @param {string} message - Human-readable error message
10
+ * @param {Object} [context={}] - Additional error context
11
+ * @param {string} [context.suggestion] - User-actionable suggestion
12
+ */
13
+ constructor(code, message, context = {}) {
14
+ super(message);
15
+ this.name = 'JJError';
16
+ this.code = code;
17
+ this.context = context;
18
+ this.details = context; // Alias for better ergonomics
19
+ this.suggestion = context.suggestion;
20
+ }
21
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * ID Generation utilities for isomorphic-jj
3
+ *
4
+ * Generates stable change IDs and operation IDs using cryptographically secure random values.
5
+ */
6
+
7
+ /**
8
+ * Generate a stable change ID
9
+ *
10
+ * Uses 128-bit cryptographically random value encoded as 32-character hex string.
11
+ * Collision probability is negligible (2^128 space).
12
+ *
13
+ * @returns {string} 32-character lowercase hex string
14
+ */
15
+ export function generateChangeId() {
16
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
17
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
18
+ }
19
+
20
+ /**
21
+ * Generate an operation ID from operation content
22
+ *
23
+ * Uses SHA-256 hash of operation content for integrity verification.
24
+ *
25
+ * @param {Object} operation - Operation data
26
+ * @returns {Promise<string>} 64-character lowercase hex string (SHA-256)
27
+ */
28
+ export async function generateOperationId(operation) {
29
+ const content = JSON.stringify({
30
+ timestamp: operation.timestamp,
31
+ user: operation.user,
32
+ description: operation.description,
33
+ parents: operation.parents,
34
+ view: operation.view,
35
+ });
36
+
37
+ const encoder = new TextEncoder();
38
+ const data = encoder.encode(content);
39
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
40
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
41
+ return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
42
+ }
43
+
44
+ /**
45
+ * Generate a generic ID with optional prefix
46
+ *
47
+ * @param {string} prefix - Optional prefix (e.g., 'conflict', 'bookmark')
48
+ * @returns {string} ID string
49
+ */
50
+ export function generateId(prefix = '') {
51
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
52
+ const id = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
53
+ return prefix ? `${prefix}-${id}` : id;
54
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Validation utilities for isomorphic-jj
3
+ *
4
+ * Provides validation functions for change IDs, paths, bookmark names, etc.
5
+ */
6
+
7
+ import { JJError } from './errors.js';
8
+
9
+ /**
10
+ * Validate a change ID
11
+ *
12
+ * @param {string} changeId - Change ID to validate
13
+ * @throws {JJError} If change ID is invalid
14
+ */
15
+ export function validateChangeId(changeId) {
16
+ if (typeof changeId !== 'string') {
17
+ throw new JJError('INVALID_CHANGE_ID', 'Change ID must be a string', {
18
+ changeId,
19
+ suggestion: 'Provide a valid 32-character hex string',
20
+ });
21
+ }
22
+
23
+ if (!/^[0-9a-f]{32}$/.test(changeId)) {
24
+ throw new JJError(
25
+ 'INVALID_CHANGE_ID',
26
+ `Change ID must be 32 hexadecimal characters, got "${changeId}"`,
27
+ {
28
+ changeId,
29
+ suggestion: 'Use a valid change ID from log() output or use a revset expression',
30
+ }
31
+ );
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Validate a file path
37
+ *
38
+ * @param {string} path - File path to validate
39
+ * @throws {JJError} If path is invalid or contains path traversal
40
+ */
41
+ export function validatePath(path) {
42
+ if (typeof path !== 'string') {
43
+ throw new JJError('INVALID_PATH', 'Path must be a string', {
44
+ path,
45
+ });
46
+ }
47
+
48
+ if (path.includes('..')) {
49
+ throw new JJError('INVALID_PATH', 'Path traversal (..) not allowed', {
50
+ path,
51
+ suggestion: 'Use relative paths without .. components',
52
+ });
53
+ }
54
+
55
+ if (path.startsWith('/')) {
56
+ throw new JJError('INVALID_PATH', 'Absolute paths not allowed', {
57
+ path,
58
+ suggestion: 'Use relative paths from repository root',
59
+ });
60
+ }
61
+
62
+ if (path.length > 4096) {
63
+ throw new JJError('INVALID_PATH', 'Path exceeds maximum length', {
64
+ path,
65
+ maxLength: 4096,
66
+ suggestion: 'Use shorter file paths',
67
+ });
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Validate a bookmark name (follows Git ref name rules)
73
+ *
74
+ * @param {string} name - Bookmark name to validate
75
+ * @throws {JJError} If bookmark name is invalid
76
+ */
77
+ export function validateBookmarkName(name) {
78
+ if (typeof name !== 'string' || name.length === 0) {
79
+ throw new JJError('INVALID_BOOKMARK_NAME', 'Bookmark name must be a non-empty string', {
80
+ name,
81
+ });
82
+ }
83
+
84
+ // Git ref name validation rules
85
+ const invalidChars = /[\s*~^:?[\]\\]/;
86
+ if (invalidChars.test(name)) {
87
+ throw new JJError(
88
+ 'INVALID_BOOKMARK_NAME',
89
+ 'Bookmark name contains invalid characters (spaces, *, ~, ^, :, ?, [, ], \\)',
90
+ {
91
+ name,
92
+ suggestion: 'Use alphanumeric characters, hyphens, and forward slashes',
93
+ }
94
+ );
95
+ }
96
+
97
+ if (name.includes('..')) {
98
+ throw new JJError('INVALID_BOOKMARK_NAME', 'Bookmark name cannot contain ".."', {
99
+ name,
100
+ });
101
+ }
102
+
103
+ if (name.startsWith('.') || name.endsWith('.')) {
104
+ throw new JJError(
105
+ 'INVALID_BOOKMARK_NAME',
106
+ 'Bookmark name cannot start or end with "."',
107
+ {
108
+ name,
109
+ }
110
+ );
111
+ }
112
+
113
+ if (name.endsWith('.lock')) {
114
+ throw new JJError('INVALID_BOOKMARK_NAME', 'Bookmark name cannot end with ".lock"', {
115
+ name,
116
+ });
117
+ }
118
+
119
+ if (name.includes('//')) {
120
+ throw new JJError('INVALID_BOOKMARK_NAME', 'Bookmark name cannot contain "//"', {
121
+ name,
122
+ });
123
+ }
124
+ }