sync-open-chrome-tab-replay-local.cjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. #!/usr/bin/env node
  2. 'use strict';
  3. const { spawnSync } = require('node:child_process');
  4. const path = require('node:path');
  5. function usage() {
  6. return [
  7. 'Usage: node scripts/sync-open-chrome-tab-replay-local.cjs --artifact <path> [options]',
  8. '',
  9. 'Options:',
  10. ' --artifact <path> Path to artifact.json produced by sync-open-chrome-tab-simulate',
  11. ' --client <n> Replay one client instance index only',
  12. ' --round <n> Replay one round only',
  13. ' --pretty Pretty-print JSON output',
  14. ' -h, --help Show this message',
  15. ].join('\n');
  16. }
  17. function parsePositiveInteger(value, flagName) {
  18. const parsed = Number.parseInt(value, 10);
  19. if (!Number.isInteger(parsed) || parsed <= 0) {
  20. throw new Error(`${flagName} must be a positive integer`);
  21. }
  22. return parsed;
  23. }
  24. function parseArgs(argv) {
  25. const result = {
  26. artifact: null,
  27. client: null,
  28. round: null,
  29. pretty: false,
  30. help: false,
  31. };
  32. for (let i = 0; i < argv.length; i += 1) {
  33. const arg = argv[i];
  34. if (arg === '--help' || arg === '-h') {
  35. result.help = true;
  36. continue;
  37. }
  38. if (arg === '--pretty') {
  39. result.pretty = true;
  40. continue;
  41. }
  42. const next = argv[i + 1];
  43. if (arg === '--artifact') {
  44. if (typeof next !== 'string' || next.length === 0) {
  45. throw new Error('--artifact must be a non-empty path');
  46. }
  47. result.artifact = next;
  48. i += 1;
  49. continue;
  50. }
  51. if (arg === '--client') {
  52. result.client = parsePositiveInteger(next, '--client');
  53. i += 1;
  54. continue;
  55. }
  56. if (arg === '--round') {
  57. result.round = parsePositiveInteger(next, '--round');
  58. i += 1;
  59. continue;
  60. }
  61. throw new Error(`Unknown argument: ${arg}`);
  62. }
  63. if (!result.help && !result.artifact) {
  64. throw new Error('--artifact is required');
  65. }
  66. return result;
  67. }
  68. function main() {
  69. let args;
  70. try {
  71. args = parseArgs(process.argv.slice(2));
  72. } catch (error) {
  73. console.error(error.message);
  74. console.error('\n' + usage());
  75. process.exit(1);
  76. return;
  77. }
  78. if (args.help) {
  79. console.log(usage());
  80. return;
  81. }
  82. const artifactPath = path.resolve(args.artifact);
  83. const dbDir = path.resolve(__dirname, '..', 'deps', 'db');
  84. const scriptPath = 'script/replay_sync_artifact.cljs';
  85. const commandArgs = [
  86. '-s',
  87. 'nbb-logseq',
  88. '-cp',
  89. 'src:script:../db-sync/src',
  90. scriptPath,
  91. '--artifact',
  92. artifactPath,
  93. ];
  94. if (args.client) {
  95. commandArgs.push('--client', String(args.client));
  96. }
  97. if (args.round) {
  98. commandArgs.push('--round', String(args.round));
  99. }
  100. if (args.pretty) {
  101. commandArgs.push('--pretty');
  102. }
  103. const result = spawnSync('yarn', commandArgs, {
  104. cwd: dbDir,
  105. stdio: ['ignore', 'pipe', 'pipe'],
  106. encoding: 'utf8',
  107. });
  108. if (result.status !== 0) {
  109. const stderr = (result.stderr || '').trim();
  110. const stdout = (result.stdout || '').trim();
  111. const detail = stderr || stdout;
  112. throw new Error(
  113. `Replay command failed (exit ${result.status ?? 'unknown'}): yarn ${commandArgs.join(' ')}` +
  114. (detail ? `\n${detail}` : '')
  115. );
  116. }
  117. const output = result.stdout || '';
  118. process.stdout.write(output);
  119. }
  120. if (require.main === module) {
  121. try {
  122. main();
  123. } catch (error) {
  124. console.error(error.stack || String(error));
  125. process.exit(1);
  126. }
  127. }
  128. module.exports = {
  129. parseArgs,
  130. };