@git-stunts/git-warp 10.1.1
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/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +480 -0
- package/SECURITY.md +30 -0
- package/bin/git-warp +24 -0
- package/bin/warp-graph.js +1574 -0
- package/index.d.ts +2366 -0
- package/index.js +180 -0
- package/package.json +129 -0
- package/scripts/install-git-warp.sh +258 -0
- package/scripts/uninstall-git-warp.sh +139 -0
- package/src/domain/WarpGraph.js +3157 -0
- package/src/domain/crdt/Dot.js +160 -0
- package/src/domain/crdt/LWW.js +154 -0
- package/src/domain/crdt/ORSet.js +371 -0
- package/src/domain/crdt/VersionVector.js +222 -0
- package/src/domain/entities/GraphNode.js +60 -0
- package/src/domain/errors/EmptyMessageError.js +47 -0
- package/src/domain/errors/ForkError.js +30 -0
- package/src/domain/errors/IndexError.js +23 -0
- package/src/domain/errors/OperationAbortedError.js +22 -0
- package/src/domain/errors/QueryError.js +39 -0
- package/src/domain/errors/SchemaUnsupportedError.js +17 -0
- package/src/domain/errors/ShardCorruptionError.js +56 -0
- package/src/domain/errors/ShardLoadError.js +57 -0
- package/src/domain/errors/ShardValidationError.js +61 -0
- package/src/domain/errors/StorageError.js +57 -0
- package/src/domain/errors/SyncError.js +30 -0
- package/src/domain/errors/TraversalError.js +23 -0
- package/src/domain/errors/WarpError.js +31 -0
- package/src/domain/errors/WormholeError.js +28 -0
- package/src/domain/errors/WriterError.js +39 -0
- package/src/domain/errors/index.js +21 -0
- package/src/domain/services/AnchorMessageCodec.js +99 -0
- package/src/domain/services/BitmapIndexBuilder.js +225 -0
- package/src/domain/services/BitmapIndexReader.js +435 -0
- package/src/domain/services/BoundaryTransitionRecord.js +463 -0
- package/src/domain/services/CheckpointMessageCodec.js +147 -0
- package/src/domain/services/CheckpointSerializerV5.js +281 -0
- package/src/domain/services/CheckpointService.js +384 -0
- package/src/domain/services/CommitDagTraversalService.js +156 -0
- package/src/domain/services/DagPathFinding.js +712 -0
- package/src/domain/services/DagTopology.js +239 -0
- package/src/domain/services/DagTraversal.js +245 -0
- package/src/domain/services/Frontier.js +108 -0
- package/src/domain/services/GCMetrics.js +101 -0
- package/src/domain/services/GCPolicy.js +122 -0
- package/src/domain/services/GitLogParser.js +205 -0
- package/src/domain/services/HealthCheckService.js +246 -0
- package/src/domain/services/HookInstaller.js +326 -0
- package/src/domain/services/HttpSyncServer.js +262 -0
- package/src/domain/services/IndexRebuildService.js +426 -0
- package/src/domain/services/IndexStalenessChecker.js +103 -0
- package/src/domain/services/JoinReducer.js +582 -0
- package/src/domain/services/KeyCodec.js +113 -0
- package/src/domain/services/LegacyAnchorDetector.js +67 -0
- package/src/domain/services/LogicalTraversal.js +351 -0
- package/src/domain/services/MessageCodecInternal.js +132 -0
- package/src/domain/services/MessageSchemaDetector.js +145 -0
- package/src/domain/services/MigrationService.js +55 -0
- package/src/domain/services/ObserverView.js +265 -0
- package/src/domain/services/PatchBuilderV2.js +669 -0
- package/src/domain/services/PatchMessageCodec.js +140 -0
- package/src/domain/services/ProvenanceIndex.js +337 -0
- package/src/domain/services/ProvenancePayload.js +242 -0
- package/src/domain/services/QueryBuilder.js +835 -0
- package/src/domain/services/StateDiff.js +300 -0
- package/src/domain/services/StateSerializerV5.js +156 -0
- package/src/domain/services/StreamingBitmapIndexBuilder.js +709 -0
- package/src/domain/services/SyncProtocol.js +593 -0
- package/src/domain/services/TemporalQuery.js +201 -0
- package/src/domain/services/TranslationCost.js +221 -0
- package/src/domain/services/TraversalService.js +8 -0
- package/src/domain/services/WarpMessageCodec.js +29 -0
- package/src/domain/services/WarpStateIndexBuilder.js +127 -0
- package/src/domain/services/WormholeService.js +353 -0
- package/src/domain/types/TickReceipt.js +285 -0
- package/src/domain/types/WarpTypes.js +209 -0
- package/src/domain/types/WarpTypesV2.js +200 -0
- package/src/domain/utils/CachedValue.js +140 -0
- package/src/domain/utils/EventId.js +89 -0
- package/src/domain/utils/LRUCache.js +112 -0
- package/src/domain/utils/MinHeap.js +114 -0
- package/src/domain/utils/RefLayout.js +280 -0
- package/src/domain/utils/WriterId.js +205 -0
- package/src/domain/utils/cancellation.js +33 -0
- package/src/domain/utils/canonicalStringify.js +42 -0
- package/src/domain/utils/defaultClock.js +20 -0
- package/src/domain/utils/defaultCodec.js +51 -0
- package/src/domain/utils/nullLogger.js +21 -0
- package/src/domain/utils/roaring.js +181 -0
- package/src/domain/utils/shardVersion.js +9 -0
- package/src/domain/warp/PatchSession.js +217 -0
- package/src/domain/warp/Writer.js +181 -0
- package/src/hooks/post-merge.sh +60 -0
- package/src/infrastructure/adapters/BunHttpAdapter.js +225 -0
- package/src/infrastructure/adapters/ClockAdapter.js +57 -0
- package/src/infrastructure/adapters/ConsoleLogger.js +150 -0
- package/src/infrastructure/adapters/DenoHttpAdapter.js +230 -0
- package/src/infrastructure/adapters/GitGraphAdapter.js +787 -0
- package/src/infrastructure/adapters/GlobalClockAdapter.js +5 -0
- package/src/infrastructure/adapters/NoOpLogger.js +62 -0
- package/src/infrastructure/adapters/NodeCryptoAdapter.js +32 -0
- package/src/infrastructure/adapters/NodeHttpAdapter.js +98 -0
- package/src/infrastructure/adapters/PerformanceClockAdapter.js +5 -0
- package/src/infrastructure/adapters/WebCryptoAdapter.js +121 -0
- package/src/infrastructure/codecs/CborCodec.js +384 -0
- package/src/ports/BlobPort.js +30 -0
- package/src/ports/ClockPort.js +25 -0
- package/src/ports/CodecPort.js +25 -0
- package/src/ports/CommitPort.js +114 -0
- package/src/ports/ConfigPort.js +31 -0
- package/src/ports/CryptoPort.js +38 -0
- package/src/ports/GraphPersistencePort.js +57 -0
- package/src/ports/HttpServerPort.js +25 -0
- package/src/ports/IndexStoragePort.js +39 -0
- package/src/ports/LoggerPort.js +68 -0
- package/src/ports/RefPort.js +51 -0
- package/src/ports/TreePort.js +51 -0
- package/src/visualization/index.js +26 -0
- package/src/visualization/layouts/converters.js +75 -0
- package/src/visualization/layouts/elkAdapter.js +86 -0
- package/src/visualization/layouts/elkLayout.js +95 -0
- package/src/visualization/layouts/index.js +29 -0
- package/src/visualization/renderers/ascii/box.js +16 -0
- package/src/visualization/renderers/ascii/check.js +271 -0
- package/src/visualization/renderers/ascii/colors.js +13 -0
- package/src/visualization/renderers/ascii/formatters.js +73 -0
- package/src/visualization/renderers/ascii/graph.js +344 -0
- package/src/visualization/renderers/ascii/history.js +335 -0
- package/src/visualization/renderers/ascii/index.js +14 -0
- package/src/visualization/renderers/ascii/info.js +245 -0
- package/src/visualization/renderers/ascii/materialize.js +255 -0
- package/src/visualization/renderers/ascii/path.js +240 -0
- package/src/visualization/renderers/ascii/progress.js +32 -0
- package/src/visualization/renderers/ascii/symbols.js +33 -0
- package/src/visualization/renderers/ascii/table.js +19 -0
- package/src/visualization/renderers/browser/index.js +1 -0
- package/src/visualization/renderers/svg/index.js +159 -0
- package/src/visualization/utils/ansi.js +14 -0
- package/src/visualization/utils/time.js +40 -0
- package/src/visualization/utils/truncate.js +40 -0
- package/src/visualization/utils/unicode.js +52 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import LoggerPort from '../../ports/LoggerPort.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* No-operation logger adapter.
|
|
5
|
+
*
|
|
6
|
+
* Provides a zero-overhead implementation of LoggerPort that discards
|
|
7
|
+
* all log messages. Useful as the default logger when logging is not
|
|
8
|
+
* needed, or for testing scenarios where log output is not relevant.
|
|
9
|
+
*
|
|
10
|
+
* All methods are no-ops that return immediately without side effects.
|
|
11
|
+
*/
|
|
12
|
+
export default class NoOpLogger extends LoggerPort {
|
|
13
|
+
/**
|
|
14
|
+
* No-op debug log.
|
|
15
|
+
* @param {string} _message - Ignored
|
|
16
|
+
* @param {Record<string, unknown>} [_context] - Ignored
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
debug(_message, _context) {
|
|
20
|
+
// Intentionally empty
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* No-op info log.
|
|
25
|
+
* @param {string} _message - Ignored
|
|
26
|
+
* @param {Record<string, unknown>} [_context] - Ignored
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
info(_message, _context) {
|
|
30
|
+
// Intentionally empty
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* No-op warning log.
|
|
35
|
+
* @param {string} _message - Ignored
|
|
36
|
+
* @param {Record<string, unknown>} [_context] - Ignored
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
warn(_message, _context) {
|
|
40
|
+
// Intentionally empty
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* No-op error log.
|
|
45
|
+
* @param {string} _message - Ignored
|
|
46
|
+
* @param {Record<string, unknown>} [_context] - Ignored
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
error(_message, _context) {
|
|
50
|
+
// Intentionally empty
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns a new NoOpLogger instance.
|
|
55
|
+
* Context is ignored since no logging occurs.
|
|
56
|
+
* @param {Record<string, unknown>} [_context] - Ignored
|
|
57
|
+
* @returns {NoOpLogger} A new NoOpLogger instance
|
|
58
|
+
*/
|
|
59
|
+
child(_context) {
|
|
60
|
+
return new NoOpLogger();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import CryptoPort from '../../ports/CryptoPort.js';
|
|
2
|
+
import {
|
|
3
|
+
createHash,
|
|
4
|
+
createHmac,
|
|
5
|
+
timingSafeEqual as nodeTimingSafeEqual,
|
|
6
|
+
} from 'node:crypto';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Node.js crypto adapter implementing CryptoPort.
|
|
10
|
+
*
|
|
11
|
+
* This is the only file that imports node:crypto.
|
|
12
|
+
*
|
|
13
|
+
* @extends CryptoPort
|
|
14
|
+
*/
|
|
15
|
+
export default class NodeCryptoAdapter extends CryptoPort {
|
|
16
|
+
/** @inheritdoc */
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- async ensures sync throws become rejected promises
|
|
18
|
+
async hash(algorithm, data) {
|
|
19
|
+
return createHash(algorithm).update(data).digest('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @inheritdoc */
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/require-await -- async ensures sync throws become rejected promises
|
|
24
|
+
async hmac(algorithm, key, data) {
|
|
25
|
+
return createHmac(algorithm, key).update(data).digest();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @inheritdoc */
|
|
29
|
+
timingSafeEqual(a, b) {
|
|
30
|
+
return nodeTimingSafeEqual(a, b);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import HttpServerPort from '../../ports/HttpServerPort.js';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
|
|
4
|
+
/** Absolute streaming body limit (10 MB). */
|
|
5
|
+
const MAX_BODY_BYTES = 10 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Collects the request body and dispatches to the handler, returning
|
|
9
|
+
* a 500 response if the handler throws.
|
|
10
|
+
*/
|
|
11
|
+
async function dispatch(req, res, { handler, logger }) {
|
|
12
|
+
try {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
let totalBytes = 0;
|
|
15
|
+
for await (const chunk of req) {
|
|
16
|
+
totalBytes += chunk.length;
|
|
17
|
+
if (totalBytes > MAX_BODY_BYTES) {
|
|
18
|
+
res.writeHead(413, { 'Content-Type': 'text/plain' });
|
|
19
|
+
res.end('Payload Too Large');
|
|
20
|
+
req.destroy();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
chunks.push(chunk);
|
|
24
|
+
}
|
|
25
|
+
const body = Buffer.concat(chunks);
|
|
26
|
+
|
|
27
|
+
const response = await handler({
|
|
28
|
+
method: req.method,
|
|
29
|
+
url: req.url,
|
|
30
|
+
headers: req.headers,
|
|
31
|
+
body: body.length > 0 ? body : undefined,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
res.writeHead(response.status || 200, response.headers || {});
|
|
35
|
+
res.end(response.body);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
logger.error('[NodeHttpAdapter] dispatch error:', err);
|
|
38
|
+
if (!res.headersSent) {
|
|
39
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
40
|
+
}
|
|
41
|
+
res.end('Internal Server Error');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const noopLogger = { error() {} };
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Node.js HTTP adapter implementing HttpServerPort.
|
|
49
|
+
*
|
|
50
|
+
* This is the only file that imports node:http for server creation.
|
|
51
|
+
*
|
|
52
|
+
* @extends HttpServerPort
|
|
53
|
+
*/
|
|
54
|
+
export default class NodeHttpAdapter extends HttpServerPort {
|
|
55
|
+
/**
|
|
56
|
+
* @param {{ logger?: { error: Function } }} [options]
|
|
57
|
+
*/
|
|
58
|
+
constructor({ logger } = {}) {
|
|
59
|
+
super();
|
|
60
|
+
this._logger = logger || noopLogger;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @inheritdoc */
|
|
64
|
+
createServer(requestHandler) {
|
|
65
|
+
const logger = this._logger;
|
|
66
|
+
const server = createServer((req, res) => {
|
|
67
|
+
dispatch(req, res, { handler: requestHandler, logger }).catch((err) => {
|
|
68
|
+
logger.error('[NodeHttpAdapter] unhandled dispatch error:', err);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
listen(port, host, callback) {
|
|
74
|
+
const cb = typeof host === 'function' ? host : callback;
|
|
75
|
+
const bindHost = typeof host === 'string' ? host : undefined;
|
|
76
|
+
const onError = (err) => {
|
|
77
|
+
if (cb) {
|
|
78
|
+
cb(err);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
server.once('error', onError);
|
|
82
|
+
const args = bindHost !== undefined ? [port, bindHost] : [port];
|
|
83
|
+
server.listen(...args, () => {
|
|
84
|
+
server.removeListener('error', onError);
|
|
85
|
+
if (cb) {
|
|
86
|
+
cb(null);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
close(callback) {
|
|
91
|
+
server.close(callback);
|
|
92
|
+
},
|
|
93
|
+
address() {
|
|
94
|
+
return server.address();
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import CryptoPort from '../../ports/CryptoPort.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Map of common algorithm names to Web Crypto API algorithm identifiers.
|
|
5
|
+
* @const {Object<string, string>}
|
|
6
|
+
*/
|
|
7
|
+
const ALGO_MAP = {
|
|
8
|
+
'sha-1': 'SHA-1',
|
|
9
|
+
'sha1': 'SHA-1',
|
|
10
|
+
'sha-256': 'SHA-256',
|
|
11
|
+
'sha256': 'SHA-256',
|
|
12
|
+
'sha-384': 'SHA-384',
|
|
13
|
+
'sha384': 'SHA-384',
|
|
14
|
+
'sha-512': 'SHA-512',
|
|
15
|
+
'sha512': 'SHA-512',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts a common algorithm name to the Web Crypto API identifier.
|
|
20
|
+
* @param {string} algorithm - Algorithm name (e.g. 'sha256', 'sha-256')
|
|
21
|
+
* @returns {string} Web Crypto API algorithm identifier (e.g. 'SHA-256')
|
|
22
|
+
* @throws {Error} If the algorithm is not supported
|
|
23
|
+
*/
|
|
24
|
+
function toWebCryptoAlgo(algorithm) {
|
|
25
|
+
const mapped = ALGO_MAP[algorithm.toLowerCase()];
|
|
26
|
+
if (!mapped) {
|
|
27
|
+
throw new Error(`WebCryptoAdapter: unsupported algorithm "${algorithm}"`);
|
|
28
|
+
}
|
|
29
|
+
return mapped;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts input data to a Uint8Array for Web Crypto API consumption.
|
|
34
|
+
* @param {string|Buffer|Uint8Array} data - Input data
|
|
35
|
+
* @returns {Uint8Array} Data as Uint8Array
|
|
36
|
+
* @throws {Error} If data type is not supported
|
|
37
|
+
*/
|
|
38
|
+
function toUint8Array(data) {
|
|
39
|
+
if (data instanceof Uint8Array) { return data; }
|
|
40
|
+
if (typeof data === 'string') { return new TextEncoder().encode(data); }
|
|
41
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(data)) {
|
|
42
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
43
|
+
}
|
|
44
|
+
throw new Error('WebCryptoAdapter: data must be string, Buffer, or Uint8Array');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Converts an ArrayBuffer to a hex string.
|
|
49
|
+
* @param {ArrayBuffer} buf - ArrayBuffer to convert
|
|
50
|
+
* @returns {string} Hex-encoded string
|
|
51
|
+
*/
|
|
52
|
+
function bufToHex(buf) {
|
|
53
|
+
return Array.from(new Uint8Array(buf))
|
|
54
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
55
|
+
.join('');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Web Crypto API adapter implementing CryptoPort.
|
|
60
|
+
*
|
|
61
|
+
* Uses the standard Web Crypto API (globalThis.crypto.subtle) which is
|
|
62
|
+
* available in browsers, Deno, Bun, and Node.js 20+.
|
|
63
|
+
*
|
|
64
|
+
* All hash and HMAC operations are async because the Web Crypto API
|
|
65
|
+
* is inherently promise-based.
|
|
66
|
+
*
|
|
67
|
+
* @extends CryptoPort
|
|
68
|
+
*/
|
|
69
|
+
export default class WebCryptoAdapter extends CryptoPort {
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new WebCryptoAdapter.
|
|
72
|
+
* @param {Object} [options] - Configuration options
|
|
73
|
+
* @param {SubtleCrypto} [options.subtle] - SubtleCrypto instance (defaults to globalThis.crypto.subtle)
|
|
74
|
+
*/
|
|
75
|
+
constructor({ subtle } = {}) {
|
|
76
|
+
super();
|
|
77
|
+
this._subtle = subtle || globalThis.crypto.subtle;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @inheritdoc */
|
|
81
|
+
async hash(algorithm, data) {
|
|
82
|
+
const digest = await this._subtle.digest(
|
|
83
|
+
toWebCryptoAlgo(algorithm),
|
|
84
|
+
toUint8Array(data),
|
|
85
|
+
);
|
|
86
|
+
return bufToHex(digest);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @inheritdoc */
|
|
90
|
+
async hmac(algorithm, key, data) {
|
|
91
|
+
const keyBytes = toUint8Array(key);
|
|
92
|
+
const cryptoKey = await this._subtle.importKey(
|
|
93
|
+
'raw',
|
|
94
|
+
keyBytes,
|
|
95
|
+
{ name: 'HMAC', hash: toWebCryptoAlgo(algorithm) },
|
|
96
|
+
false,
|
|
97
|
+
['sign'],
|
|
98
|
+
);
|
|
99
|
+
const signature = await this._subtle.sign('HMAC', cryptoKey, toUint8Array(data));
|
|
100
|
+
return new Uint8Array(signature);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Constant-time comparison of two buffers.
|
|
105
|
+
*
|
|
106
|
+
* Uses XOR accumulation with no early exit to prevent timing attacks.
|
|
107
|
+
* This is the standard approach when crypto.timingSafeEqual is unavailable.
|
|
108
|
+
*
|
|
109
|
+
* @param {Buffer|Uint8Array} a - First buffer
|
|
110
|
+
* @param {Buffer|Uint8Array} b - Second buffer
|
|
111
|
+
* @returns {boolean} True if buffers are equal
|
|
112
|
+
*/
|
|
113
|
+
timingSafeEqual(a, b) {
|
|
114
|
+
if (a.length !== b.length) { return false; }
|
|
115
|
+
let result = 0;
|
|
116
|
+
for (let i = 0; i < a.length; i++) {
|
|
117
|
+
result |= a[i] ^ b[i];
|
|
118
|
+
}
|
|
119
|
+
return result === 0;
|
|
120
|
+
}
|
|
121
|
+
}
|