@opencrvs/toolkit 2.0.0-rc.fc7f7fc → 2.0.0-rc.fef9d21

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 (70) hide show
  1. package/dist/cli.js +1383 -142
  2. package/dist/commons/api/router.d.ts +42 -63
  3. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.d.ts.map +1 -1
  4. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.js +3 -4
  5. package/dist/migrations/v2.0/add-resend-invite-notification.d.ts +3 -0
  6. package/dist/migrations/v2.0/add-resend-invite-notification.d.ts.map +1 -0
  7. package/dist/migrations/v2.0/add-resend-invite-notification.js +373 -0
  8. package/dist/migrations/v2.0/checkout-upstream-files.d.ts +19 -0
  9. package/dist/migrations/v2.0/checkout-upstream-files.d.ts.map +1 -1
  10. package/dist/migrations/v2.0/checkout-upstream-files.js +17 -13
  11. package/dist/migrations/v2.0/convert-config-files-to-ts.d.ts.map +1 -1
  12. package/dist/migrations/v2.0/convert-config-files-to-ts.js +4 -5
  13. package/dist/migrations/v2.0/convert-paragraph-to-heading.d.ts.map +1 -1
  14. package/dist/migrations/v2.0/convert-paragraph-to-heading.js +4 -5
  15. package/dist/migrations/v2.0/create-events-index.d.ts.map +1 -1
  16. package/dist/migrations/v2.0/create-events-index.js +1 -2
  17. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts +3 -0
  18. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts.map +1 -0
  19. package/dist/migrations/v2.0/delete-infrastructure-directory.js +77 -0
  20. package/dist/migrations/v2.0/index.d.ts +8 -4
  21. package/dist/migrations/v2.0/index.d.ts.map +1 -1
  22. package/dist/migrations/v2.0/index.js +1351 -139
  23. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.d.ts.map +1 -1
  24. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.js +5 -6
  25. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts +3 -0
  26. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts.map +1 -0
  27. package/dist/migrations/v2.0/merge-infrastructure-directory.js +221 -0
  28. package/dist/migrations/v2.0/migrate-application-config-url.d.ts +3 -0
  29. package/dist/migrations/v2.0/migrate-application-config-url.d.ts.map +1 -0
  30. package/dist/migrations/v2.0/migrate-application-config-url.js +342 -0
  31. package/dist/migrations/v2.0/migrate-scopes.d.ts.map +1 -1
  32. package/dist/migrations/v2.0/migrate-scopes.js +6 -7
  33. package/dist/migrations/v2.0/migrate-validated-workqueue-status-to-flag.d.ts.map +1 -1
  34. package/dist/migrations/v2.0/migrate-validated-workqueue-status-to-flag.js +3 -4
  35. package/dist/migrations/v2.0/migrate-workqueue-configs.d.ts.map +1 -1
  36. package/dist/migrations/v2.0/migrate-workqueue-configs.js +3 -4
  37. package/dist/migrations/v2.0/remove-delete-actions.d.ts.map +1 -1
  38. package/dist/migrations/v2.0/remove-delete-actions.js +3 -4
  39. package/dist/migrations/v2.0/remove-demo-scope.d.ts.map +1 -1
  40. package/dist/migrations/v2.0/remove-demo-scope.js +2 -3
  41. package/dist/migrations/v2.0/remove-deprecated-imports.d.ts.map +1 -1
  42. package/dist/migrations/v2.0/remove-deprecated-imports.js +3 -4
  43. package/dist/migrations/v2.0/remove-fhir-url-helpers.d.ts +3 -0
  44. package/dist/migrations/v2.0/remove-fhir-url-helpers.d.ts.map +1 -0
  45. package/dist/migrations/v2.0/remove-fhir-url-helpers.js +163 -0
  46. package/dist/migrations/v2.0/remove-hearth-migrations.d.ts.map +1 -1
  47. package/dist/migrations/v2.0/remove-hearth-migrations.js +1 -2
  48. package/dist/migrations/v2.0/remove-old-statistics-service.d.ts +3 -0
  49. package/dist/migrations/v2.0/remove-old-statistics-service.d.ts.map +1 -0
  50. package/dist/migrations/v2.0/remove-old-statistics-service.js +356 -0
  51. package/dist/migrations/v2.0/remove-pending-certification-flag.d.ts.map +1 -1
  52. package/dist/migrations/v2.0/remove-pending-certification-flag.js +3 -4
  53. package/dist/migrations/v2.0/remove-review-from-register-action.d.ts.map +1 -1
  54. package/dist/migrations/v2.0/remove-review-from-register-action.js +3 -4
  55. package/dist/migrations/v2.0/remove-unused-environment-variables.d.ts +3 -0
  56. package/dist/migrations/v2.0/remove-unused-environment-variables.d.ts.map +1 -0
  57. package/dist/migrations/v2.0/remove-unused-environment-variables.js +186 -0
  58. package/dist/migrations/v2.0/rename-api-paths.d.ts.map +1 -1
  59. package/dist/migrations/v2.0/rename-api-paths.js +3 -4
  60. package/dist/migrations/v2.0/rename-location-parent-id.d.ts.map +1 -1
  61. package/dist/migrations/v2.0/rename-location-parent-id.js +3 -4
  62. package/dist/migrations/v2.0/simplify-analytics-precalculations.d.ts.map +1 -1
  63. package/dist/migrations/v2.0/simplify-analytics-precalculations.js +1 -2
  64. package/dist/migrations/v2.0/update-package-json.d.ts +3 -0
  65. package/dist/migrations/v2.0/update-package-json.d.ts.map +1 -0
  66. package/dist/migrations/v2.0/update-package-json.js +105 -0
  67. package/opencrvs-toolkit-2.0.0-rc.fef9d21.tgz +0 -0
  68. package/package.json +1 -1
  69. package/tsconfig.tsbuildinfo +1 -1
  70. package/opencrvs-toolkit-2.0.0-rc.fc7f7fc.tgz +0 -0
package/dist/cli.js CHANGED
@@ -91,7 +91,7 @@ function processFile(filePath, project) {
91
91
  if (wasRemoved) {
92
92
  removedCount++;
93
93
  console.log(
94
- ` [${import_path.default.relative(getCwd(), filePath)}] Removed 'review' from REGISTER action`
94
+ ` [${import_path.default.relative(process.cwd(), filePath)}] Removed 'review' from REGISTER action`
95
95
  );
96
96
  }
97
97
  }
@@ -99,7 +99,7 @@ function processFile(filePath, project) {
99
99
  return removedCount;
100
100
  }
101
101
  async function main() {
102
- const srcDir = import_path.default.join(getCwd(), "src");
102
+ const srcDir = import_path.default.join(process.cwd(), "src");
103
103
  console.log(`Scanning for defineConfig calls in: ${srcDir}
104
104
  `);
105
105
  const project = new import_ts_morph.Project({
@@ -135,7 +135,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
135
135
  for (const filePath of modifiedFiles) {
136
136
  const sourceFile = project.getSourceFileOrThrow(filePath);
137
137
  await sourceFile.save();
138
- console.log(` Saved: ${import_path.default.relative(getCwd(), filePath)}`);
138
+ console.log(` Saved: ${import_path.default.relative(process.cwd(), filePath)}`);
139
139
  }
140
140
  console.log(
141
141
  `
@@ -338,7 +338,7 @@ function processFile2(filePath, project) {
338
338
  const eventType = resolveEventType(configArg);
339
339
  if (!eventType) {
340
340
  console.warn(
341
- ` [${import_path2.default.relative(getCwd(), configArg.getSourceFile().getFilePath())}] Could not resolve root 'type' from defineConfig \u2014 skipping auditHistoryLabel id generation`
341
+ ` [${import_path2.default.relative(process.cwd(), configArg.getSourceFile().getFilePath())}] Could not resolve root 'type' from defineConfig \u2014 skipping auditHistoryLabel id generation`
342
342
  );
343
343
  }
344
344
  let fileNeedsImports = false;
@@ -354,7 +354,7 @@ function processFile2(filePath, project) {
354
354
  fileNeedsImports = true;
355
355
  configHadValidateAction = true;
356
356
  console.log(
357
- ` [${import_path2.default.relative(getCwd(), filePath)}] Transformed VALIDATE action to CUSTOM with customActionType: '${VALIDATE_DECLARATION_VALUE}'`
357
+ ` [${import_path2.default.relative(process.cwd(), filePath)}] Transformed VALIDATE action to CUSTOM with customActionType: '${VALIDATE_DECLARATION_VALUE}'`
358
358
  );
359
359
  }
360
360
  }
@@ -375,7 +375,7 @@ function processFile2(filePath, project) {
375
375
  ]`
376
376
  });
377
377
  console.log(
378
- ` [${import_path2.default.relative(getCwd(), filePath)}] Added top-level 'flags' array to defineConfig`
378
+ ` [${import_path2.default.relative(process.cwd(), filePath)}] Added top-level 'flags' array to defineConfig`
379
379
  );
380
380
  }
381
381
  if (fileNeedsImports) {
@@ -385,7 +385,7 @@ function processFile2(filePath, project) {
385
385
  return transformedCount;
386
386
  }
387
387
  async function main2() {
388
- const srcDir = import_path2.default.join(getCwd(), "src");
388
+ const srcDir = import_path2.default.join(process.cwd(), "src");
389
389
  console.log(`Scanning for defineConfig calls in: ${srcDir}
390
390
  `);
391
391
  const project = new import_ts_morph2.Project({
@@ -420,7 +420,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
420
420
  for (const filePath of modifiedFiles) {
421
421
  const sourceFile = project.getSourceFileOrThrow(filePath);
422
422
  await sourceFile.save();
423
- console.log(` Saved: ${import_path2.default.relative(getCwd(), filePath)}`);
423
+ console.log(` Saved: ${import_path2.default.relative(process.cwd(), filePath)}`);
424
424
  }
425
425
  console.log(
426
426
  `
@@ -491,14 +491,14 @@ function processFile3(filePath, project) {
491
491
  actionsInitializer.removeElement(index);
492
492
  removedCount++;
493
493
  console.log(
494
- ` [${import_path3.default.relative(getCwd(), filePath)}] Removed DELETE action at index ${index}`
494
+ ` [${import_path3.default.relative(process.cwd(), filePath)}] Removed DELETE action at index ${index}`
495
495
  );
496
496
  }
497
497
  }
498
498
  return removedCount;
499
499
  }
500
500
  async function main3() {
501
- const srcDir = import_path3.default.join(getCwd(), "src");
501
+ const srcDir = import_path3.default.join(process.cwd(), "src");
502
502
  console.log(`Scanning for defineConfig calls in: ${srcDir}
503
503
  `);
504
504
  const project = new import_ts_morph3.Project({
@@ -533,7 +533,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
533
533
  for (const filePath of modifiedFiles) {
534
534
  const sourceFile = project.getSourceFileOrThrow(filePath);
535
535
  await sourceFile.save();
536
- console.log(` Saved: ${import_path3.default.relative(getCwd(), filePath)}`);
536
+ console.log(` Saved: ${import_path3.default.relative(process.cwd(), filePath)}`);
537
537
  }
538
538
  console.log(
539
539
  `
@@ -607,19 +607,19 @@ function processFile4(filePath, project) {
607
607
  if (result === "converted-to-heading") {
608
608
  changedCount++;
609
609
  console.log(
610
- ` [${import_path4.default.relative(getCwd(), filePath)}] Converted PARAGRAPH with heading fontVariant to HEADING`
610
+ ` [${import_path4.default.relative(process.cwd(), filePath)}] Converted PARAGRAPH with heading fontVariant to HEADING`
611
611
  );
612
612
  } else if (result === "removed-font-variant") {
613
613
  changedCount++;
614
614
  console.log(
615
- ` [${import_path4.default.relative(getCwd(), filePath)}] Removed non-heading fontVariant from PARAGRAPH`
615
+ ` [${import_path4.default.relative(process.cwd(), filePath)}] Removed non-heading fontVariant from PARAGRAPH`
616
616
  );
617
617
  }
618
618
  }
619
619
  return changedCount;
620
620
  }
621
621
  async function main4() {
622
- const srcDir = import_path4.default.join(getCwd(), "src");
622
+ const srcDir = import_path4.default.join(process.cwd(), "src");
623
623
  console.log(`Scanning for FieldType.PARAGRAPH fields in: ${srcDir}
624
624
  `);
625
625
  const project = new import_ts_morph4.Project({
@@ -653,7 +653,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
653
653
  for (const filePath of modifiedFiles) {
654
654
  const sourceFile = project.getSourceFileOrThrow(filePath);
655
655
  await sourceFile.save();
656
- console.log(` Saved: ${import_path4.default.relative(getCwd(), filePath)}`);
656
+ console.log(` Saved: ${import_path4.default.relative(process.cwd(), filePath)}`);
657
657
  }
658
658
  console.log(`
659
659
  Done. Processed ${totalChanged} FieldType.PARAGRAPH field(s).`);
@@ -803,7 +803,7 @@ function processFile5(filePath, project) {
803
803
  if (changed > 0) {
804
804
  migratedCount += changed;
805
805
  console.log(
806
- ` [${import_path5.default.relative(getCwd(), filePath)}] Migrated ${changed} clause(s): VALIDATED status \u2192 flags.anyOf`
806
+ ` [${import_path5.default.relative(process.cwd(), filePath)}] Migrated ${changed} clause(s): VALIDATED status \u2192 flags.anyOf`
807
807
  );
808
808
  }
809
809
  }
@@ -811,7 +811,7 @@ function processFile5(filePath, project) {
811
811
  return migratedCount;
812
812
  }
813
813
  async function main5() {
814
- const srcDir = import_path5.default.join(getCwd(), "src");
814
+ const srcDir = import_path5.default.join(process.cwd(), "src");
815
815
  console.log(`Scanning for defineWorkqueues calls in: ${srcDir}
816
816
  `);
817
817
  const project = new import_ts_morph5.Project({
@@ -845,7 +845,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
845
845
  for (const filePath of modifiedFiles) {
846
846
  const sourceFile = project.getSourceFileOrThrow(filePath);
847
847
  await sourceFile.save();
848
- console.log(` Saved: ${import_path5.default.relative(getCwd(), filePath)}`);
848
+ console.log(` Saved: ${import_path5.default.relative(process.cwd(), filePath)}`);
849
849
  }
850
850
  console.log(
851
851
  `
@@ -1063,7 +1063,7 @@ function processFile6(filePath, project) {
1063
1063
  const sourceFile = project.getSourceFile(filePath);
1064
1064
  if (!sourceFile) return 0;
1065
1065
  let totalChanges = 0;
1066
- const relPath = import_path6.default.relative(getCwd(), filePath);
1066
+ const relPath = import_path6.default.relative(process.cwd(), filePath);
1067
1067
  const callExpressions = sourceFile.getDescendantsOfKind(
1068
1068
  import_ts_morph6.SyntaxKind.CallExpression
1069
1069
  );
@@ -1104,7 +1104,7 @@ function processFile6(filePath, project) {
1104
1104
  return totalChanges;
1105
1105
  }
1106
1106
  async function main6() {
1107
- const srcDir = import_path6.default.join(getCwd(), "src");
1107
+ const srcDir = import_path6.default.join(process.cwd(), "src");
1108
1108
  console.log(
1109
1109
  `Scanning for birth defineConfig and PRINT_CERTIFICATE workqueues in: ${srcDir}
1110
1110
  `
@@ -1140,7 +1140,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
1140
1140
  for (const filePath of modifiedFiles) {
1141
1141
  const sourceFile = project.getSourceFileOrThrow(filePath);
1142
1142
  await sourceFile.save();
1143
- console.log(` Saved: ${import_path6.default.relative(getCwd(), filePath)}`);
1143
+ console.log(` Saved: ${import_path6.default.relative(process.cwd(), filePath)}`);
1144
1144
  }
1145
1145
  console.log(
1146
1146
  `
@@ -1177,14 +1177,14 @@ function processFile7(filePath, project) {
1177
1177
  array3.removeElement(index);
1178
1178
  removedCount++;
1179
1179
  console.log(
1180
- ` [${import_path7.default.relative(getCwd(), filePath)}] Removed InherentFlags.PENDING_CERTIFICATION from array`
1180
+ ` [${import_path7.default.relative(process.cwd(), filePath)}] Removed InherentFlags.PENDING_CERTIFICATION from array`
1181
1181
  );
1182
1182
  }
1183
1183
  }
1184
1184
  return removedCount;
1185
1185
  }
1186
1186
  async function main7() {
1187
- const srcDir = import_path7.default.join(getCwd(), "src");
1187
+ const srcDir = import_path7.default.join(process.cwd(), "src");
1188
1188
  console.log(
1189
1189
  `Scanning for InherentFlags.PENDING_CERTIFICATION array elements in: ${srcDir}
1190
1190
  `
@@ -1220,7 +1220,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
1220
1220
  for (const filePath of modifiedFiles) {
1221
1221
  const sourceFile = project.getSourceFileOrThrow(filePath);
1222
1222
  await sourceFile.save();
1223
- console.log(` Saved: ${import_path7.default.relative(getCwd(), filePath)}`);
1223
+ console.log(` Saved: ${import_path7.default.relative(process.cwd(), filePath)}`);
1224
1224
  }
1225
1225
  console.log(
1226
1226
  `
@@ -1278,13 +1278,13 @@ function processFile8(filePath, project) {
1278
1278
  propAccess.getNameNode().replaceWithText(NEW_PROPERTY_NAME);
1279
1279
  renamedCount++;
1280
1280
  console.log(
1281
- ` [${import_path8.default.relative(getCwd(), filePath)}] Renamed Location.${OLD_PROPERTY_NAME} \u2192 Location.${NEW_PROPERTY_NAME}`
1281
+ ` [${import_path8.default.relative(process.cwd(), filePath)}] Renamed Location.${OLD_PROPERTY_NAME} \u2192 Location.${NEW_PROPERTY_NAME}`
1282
1282
  );
1283
1283
  }
1284
1284
  return renamedCount;
1285
1285
  }
1286
1286
  async function main8() {
1287
- const srcDir = import_path8.default.join(getCwd(), "src");
1287
+ const srcDir = import_path8.default.join(process.cwd(), "src");
1288
1288
  console.log(
1289
1289
  `Scanning for Location.${OLD_PROPERTY_NAME} references in: ${srcDir}
1290
1290
  `
@@ -1320,7 +1320,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
1320
1320
  for (const filePath of modifiedFiles) {
1321
1321
  const sourceFile = project.getSourceFileOrThrow(filePath);
1322
1322
  await sourceFile.save();
1323
- console.log(` Saved: ${import_path8.default.relative(getCwd(), filePath)}`);
1323
+ console.log(` Saved: ${import_path8.default.relative(process.cwd(), filePath)}`);
1324
1324
  }
1325
1325
  console.log(
1326
1326
  `
@@ -1387,7 +1387,7 @@ function processFile9(filePath, project) {
1387
1387
  const sourceFile = project.getSourceFile(filePath);
1388
1388
  if (!sourceFile) return 0;
1389
1389
  let renamedCount = 0;
1390
- const relPath = import_path9.default.relative(getCwd(), filePath);
1390
+ const relPath = import_path9.default.relative(process.cwd(), filePath);
1391
1391
  const candidates = sourceFile.getDescendantsOfKind(
1392
1392
  import_ts_morph9.SyntaxKind.ObjectLiteralExpression
1393
1393
  );
@@ -1402,7 +1402,7 @@ function processFile9(filePath, project) {
1402
1402
  return renamedCount;
1403
1403
  }
1404
1404
  async function main9() {
1405
- const srcDir = import_path9.default.join(getCwd(), "src");
1405
+ const srcDir = import_path9.default.join(process.cwd(), "src");
1406
1406
  console.log(`Scanning for Hapi route configs in: ${srcDir}
1407
1407
  `);
1408
1408
  console.log("Active path renames:");
@@ -1439,7 +1439,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
1439
1439
  for (const filePath of modifiedFiles) {
1440
1440
  const sourceFile = project.getSourceFileOrThrow(filePath);
1441
1441
  await sourceFile.save();
1442
- console.log(` Saved: ${import_path9.default.relative(getCwd(), filePath)}`);
1442
+ console.log(` Saved: ${import_path9.default.relative(process.cwd(), filePath)}`);
1443
1443
  }
1444
1444
  console.log(`
1445
1445
  Done. Renamed ${totalRenamed} route path(s).`);
@@ -1813,9 +1813,9 @@ function updateRouteHandlers(sourceFiles, group, srcDir, relCwd) {
1813
1813
  }
1814
1814
  return replacedCount;
1815
1815
  }
1816
- function resolveConfigChain(configObj, path17) {
1816
+ function resolveConfigChain(configObj, path23) {
1817
1817
  let current = configObj;
1818
- for (const key of path17) {
1818
+ for (const key of path23) {
1819
1819
  if (!import_ts_morph10.Node.isObjectLiteralExpression(current)) return null;
1820
1820
  const prop = current.getProperty(key);
1821
1821
  if (!prop || !import_ts_morph10.Node.isPropertyAssignment(prop)) return null;
@@ -1860,8 +1860,8 @@ function ensureJoinImport(sf) {
1860
1860
  });
1861
1861
  }
1862
1862
  async function main10() {
1863
- const srcDir = import_path10.default.join(getCwd(), "src");
1864
- const relCwd = (fp) => import_path10.default.relative(getCwd(), fp);
1863
+ const srcDir = import_path10.default.join(process.cwd(), "src");
1864
+ const relCwd = (fp) => import_path10.default.relative(process.cwd(), fp);
1865
1865
  console.log("Phase 1: Converting JS config files to TypeScript...\n");
1866
1866
  let convertedCount = 0;
1867
1867
  for (const group of CONFIG_GROUPS) {
@@ -1949,7 +1949,7 @@ async function main10() {
1949
1949
  ensureJoinImport(sf);
1950
1950
  }
1951
1951
  console.log(
1952
- ` Inlined applicationConfig.${accessPath.join(".")} \u2192 ${adjustedValue} in ${import_path10.default.relative(getCwd(), sf.getFilePath())}`
1952
+ ` Inlined applicationConfig.${accessPath.join(".")} \u2192 ${adjustedValue} in ${import_path10.default.relative(process.cwd(), sf.getFilePath())}`
1953
1953
  );
1954
1954
  filesToSaveAfterInlining.add(sf);
1955
1955
  madeReplacement = true;
@@ -1961,7 +1961,7 @@ async function main10() {
1961
1961
  for (const sf of Array.from(filesToSaveAfterInlining)) {
1962
1962
  await sf.save();
1963
1963
  console.log(
1964
- ` Saved (inlined): ${import_path10.default.relative(getCwd(), sf.getFilePath())}`
1964
+ ` Saved (inlined): ${import_path10.default.relative(process.cwd(), sf.getFilePath())}`
1965
1965
  );
1966
1966
  }
1967
1967
  for (const prop of [...propsToRemove].reverse()) {
@@ -2578,7 +2578,7 @@ function migrateScopesArrays(rolesFilePath, project) {
2578
2578
  const scopeKey = resolveScopeKeyFromElementAccess(element);
2579
2579
  if (scopeKey && !(scopeKey in SCOPES)) {
2580
2580
  console.warn(
2581
- ` [${import_path11.default.relative(getCwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2581
+ ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2582
2582
  );
2583
2583
  initializer.removeElement(index);
2584
2584
  removedCount++;
@@ -2587,7 +2587,7 @@ function migrateScopesArrays(rolesFilePath, project) {
2587
2587
  const legacyScope = resolveLegacyScopeFromElement(element);
2588
2588
  if (!legacyScope) {
2589
2589
  console.warn(
2590
- ` [${import_path11.default.relative(getCwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2590
+ ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2591
2591
  );
2592
2592
  continue;
2593
2593
  }
@@ -2596,7 +2596,7 @@ function migrateScopesArrays(rolesFilePath, project) {
2596
2596
  const scope = decodeScope(migratedScope);
2597
2597
  if (!scope) {
2598
2598
  console.warn(
2599
- ` [${import_path11.default.relative(getCwd(), rolesFilePath)}] Could not decode migrated scope '${migratedScope}'.`
2599
+ ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not decode migrated scope '${migratedScope}'.`
2600
2600
  );
2601
2601
  continue;
2602
2602
  }
@@ -2604,14 +2604,14 @@ function migrateScopesArrays(rolesFilePath, project) {
2604
2604
  migratedCount++;
2605
2605
  } catch (error) {
2606
2606
  console.warn(
2607
- ` [${import_path11.default.relative(getCwd(), rolesFilePath)}] Could not migrate scope '${legacyScope}': ${error}`
2607
+ ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not migrate scope '${legacyScope}': ${error}`
2608
2608
  );
2609
2609
  }
2610
2610
  }
2611
2611
  }
2612
2612
  if (removedCount > 0) {
2613
2613
  console.log(
2614
- ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(getCwd(), rolesFilePath)}'.`
2614
+ ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(process.cwd(), rolesFilePath)}'.`
2615
2615
  );
2616
2616
  }
2617
2617
  return { migratedCount, removedCount };
@@ -2733,9 +2733,9 @@ function removeRoleTypeDefinitionAndUnusedImports(rolesFilePath, project) {
2733
2733
  return removed;
2734
2734
  }
2735
2735
  async function main11() {
2736
- const cwd2 = getCwd();
2737
- const srcDir = import_path11.default.join(cwd2, "src");
2738
- const rolesFilePath = import_path11.default.join(cwd2, ROLES_FILE_RELATIVE_PATH);
2736
+ const cwd = process.cwd();
2737
+ const srcDir = import_path11.default.join(cwd, "src");
2738
+ const rolesFilePath = import_path11.default.join(cwd, ROLES_FILE_RELATIVE_PATH);
2739
2739
  console.log(`Scanning roles scopes in: ${rolesFilePath}`);
2740
2740
  const project = new import_ts_morph11.Project({
2741
2741
  tsConfigFilePath: import_path11.default.resolve(srcDir, "../tsconfig.json"),
@@ -2799,13 +2799,13 @@ function processFile10(filePath, project) {
2799
2799
  decl.remove();
2800
2800
  }
2801
2801
  console.log(
2802
- ` [${import_path12.default.relative(getCwd(), filePath)}] Removed ${matchingNamedImports.length} deprecated '${DEPRECATED_IMPORT}' import(s) from '${decl.getModuleSpecifierValue()}'`
2802
+ ` [${import_path12.default.relative(process.cwd(), filePath)}] Removed ${matchingNamedImports.length} deprecated '${DEPRECATED_IMPORT}' import(s) from '${decl.getModuleSpecifierValue()}'`
2803
2803
  );
2804
2804
  }
2805
2805
  return removedCount;
2806
2806
  }
2807
2807
  async function main12() {
2808
- const srcDir = import_path12.default.join(getCwd(), "src");
2808
+ const srcDir = import_path12.default.join(process.cwd(), "src");
2809
2809
  console.log(`Scanning for deprecated imports in: ${srcDir}
2810
2810
  `);
2811
2811
  const project = new import_ts_morph12.Project({
@@ -2836,15 +2836,738 @@ async function main12() {
2836
2836
  Saving ${modifiedFiles.length} modified file(s)...`);
2837
2837
  for (const filePath of modifiedFiles) {
2838
2838
  await project.getSourceFileOrThrow(filePath).save();
2839
- console.log(` Saved: ${import_path12.default.relative(getCwd(), filePath)}`);
2839
+ console.log(` Saved: ${import_path12.default.relative(process.cwd(), filePath)}`);
2840
2840
  }
2841
2841
  console.log(`
2842
2842
  Done. Removed ${totalRemoved} deprecated import(s).`);
2843
2843
  }
2844
2844
 
2845
- // src/migrations/v2.0/migrate-workqueue-configs.ts
2846
- var import_ts_morph13 = require("ts-morph");
2845
+ // src/migrations/v2.0/remove-unused-environment-variables.ts
2847
2846
  var import_path13 = __toESM(require("path"));
2847
+ var import_ts_morph13 = require("ts-morph");
2848
+ var UNUSED_ENV_VARS = /* @__PURE__ */ new Set([
2849
+ "APPLICATION_CONFIG_URL",
2850
+ "FHIR_URL",
2851
+ "CONFIRM_REGISTRATION_URL"
2852
+ ]);
2853
+ var ENVIRONMENT_FILE_RELATIVE = "src/environment.ts";
2854
+ var CONSTANTS_FILE_RELATIVE = "src/constants.ts";
2855
+ function cleanEnvironmentFile(project, filePath) {
2856
+ const sourceFile = project.getSourceFile(filePath);
2857
+ if (!sourceFile) return [];
2858
+ const removed = [];
2859
+ const cleanEnvCall = sourceFile.getDescendantsOfKind(import_ts_morph13.SyntaxKind.CallExpression).find((call) => {
2860
+ const expr = call.getExpression();
2861
+ return import_ts_morph13.Node.isIdentifier(expr) && expr.getText() === "cleanEnv";
2862
+ });
2863
+ if (!cleanEnvCall) return removed;
2864
+ const schemaArg = cleanEnvCall.getArguments()[1]?.asKind(import_ts_morph13.SyntaxKind.ObjectLiteralExpression);
2865
+ if (!schemaArg) return removed;
2866
+ for (const name of UNUSED_ENV_VARS) {
2867
+ const prop = schemaArg.getProperty(name);
2868
+ if (!prop) continue;
2869
+ if (prop.isKind(import_ts_morph13.SyntaxKind.PropertyAssignment) || prop.isKind(import_ts_morph13.SyntaxKind.ShorthandPropertyAssignment)) {
2870
+ prop.remove();
2871
+ removed.push(name);
2872
+ }
2873
+ }
2874
+ return removed;
2875
+ }
2876
+ function extractEnvAccess(initText) {
2877
+ if (!initText) return null;
2878
+ const match = /\benv\.([A-Z][A-Z0-9_]*)\b/.exec(initText);
2879
+ return match ? match[1] : null;
2880
+ }
2881
+ function cleanConstantsFile(project, filePath) {
2882
+ const sourceFile = project.getSourceFile(filePath);
2883
+ if (!sourceFile) return [];
2884
+ const removed = [];
2885
+ for (const stmt of [...sourceFile.getVariableStatements()]) {
2886
+ const declarations = [...stmt.getDeclarations()];
2887
+ const declarationsToRemove = declarations.filter((decl) => {
2888
+ const name = decl.getName();
2889
+ const accessedEnvVar = extractEnvAccess(decl.getInitializer()?.getText());
2890
+ return UNUSED_ENV_VARS.has(name) || accessedEnvVar !== null && UNUSED_ENV_VARS.has(accessedEnvVar);
2891
+ });
2892
+ if (declarationsToRemove.length === 0) continue;
2893
+ const namesToRemove = declarationsToRemove.map((decl) => decl.getName());
2894
+ removed.push(...namesToRemove);
2895
+ if (declarationsToRemove.length === declarations.length) {
2896
+ stmt.remove();
2897
+ } else {
2898
+ for (const decl of declarationsToRemove) {
2899
+ decl.remove();
2900
+ }
2901
+ }
2902
+ }
2903
+ return removed;
2904
+ }
2905
+ async function main13() {
2906
+ const project = new import_ts_morph13.Project({
2907
+ tsConfigFilePath: import_path13.default.resolve(process.cwd(), "tsconfig.json"),
2908
+ skipAddingFilesFromTsConfig: false
2909
+ });
2910
+ console.log(
2911
+ `Removing unused environment variable(s) (${[...UNUSED_ENV_VARS].join(", ")})...
2912
+ `
2913
+ );
2914
+ const envFilePath = import_path13.default.join(process.cwd(), ENVIRONMENT_FILE_RELATIVE);
2915
+ const constsFilePath = import_path13.default.join(process.cwd(), CONSTANTS_FILE_RELATIVE);
2916
+ const envRemoved = cleanEnvironmentFile(project, envFilePath);
2917
+ if (envRemoved.length > 0) {
2918
+ await project.getSourceFileOrThrow(envFilePath).save();
2919
+ console.log(
2920
+ ` [${ENVIRONMENT_FILE_RELATIVE}] Removed schema entr${envRemoved.length === 1 ? "y" : "ies"}: ${envRemoved.join(", ")}`
2921
+ );
2922
+ } else if (project.getSourceFile(envFilePath)) {
2923
+ console.log(
2924
+ ` [${ENVIRONMENT_FILE_RELATIVE}] No matching schema entries \u2014 nothing to remove.`
2925
+ );
2926
+ } else {
2927
+ console.log(
2928
+ ` [${ENVIRONMENT_FILE_RELATIVE}] File not found \u2014 skipping.`
2929
+ );
2930
+ }
2931
+ const constsRemoved = cleanConstantsFile(project, constsFilePath);
2932
+ if (constsRemoved.length > 0) {
2933
+ await project.getSourceFileOrThrow(constsFilePath).save();
2934
+ console.log(
2935
+ ` [${CONSTANTS_FILE_RELATIVE}] Removed declaration(s): ${constsRemoved.join(", ")}`
2936
+ );
2937
+ } else if (project.getSourceFile(constsFilePath)) {
2938
+ console.log(
2939
+ ` [${CONSTANTS_FILE_RELATIVE}] No matching declarations \u2014 nothing to remove.`
2940
+ );
2941
+ } else {
2942
+ console.log(` [${CONSTANTS_FILE_RELATIVE}] File not found \u2014 skipping.`);
2943
+ }
2944
+ if (envRemoved.length === 0 && constsRemoved.length === 0) {
2945
+ console.log("\nNothing to do.");
2946
+ return;
2947
+ }
2948
+ console.log(
2949
+ `
2950
+ Done. NOTE: usages of these constants elsewhere in 'src/' may now produce TypeScript errors and need manual review.`
2951
+ );
2952
+ }
2953
+
2954
+ // src/migrations/v2.0/remove-fhir-url-helpers.ts
2955
+ var import_path14 = __toESM(require("path"));
2956
+ var import_ts_morph14 = require("ts-morph");
2957
+ var FHIR_IMPORT = "FHIR_URL";
2958
+ var HELPER_NAMES = /* @__PURE__ */ new Set([
2959
+ "getFromFhir",
2960
+ "updateResourceFromHearth",
2961
+ "updateResourceInHearth"
2962
+ ]);
2963
+ function removeHelpers(sourceFilePath, project) {
2964
+ const sourceFile = project.getSourceFile(sourceFilePath);
2965
+ if (!sourceFile) {
2966
+ return {
2967
+ removedHelpers: [],
2968
+ removedImportCount: 0,
2969
+ remainingFhirReferences: 0
2970
+ };
2971
+ }
2972
+ const removedHelpers = [];
2973
+ let removedImportCount = 0;
2974
+ for (const fn of sourceFile.getFunctions()) {
2975
+ const name = fn.getName();
2976
+ if (!name || !HELPER_NAMES.has(name)) continue;
2977
+ removedHelpers.push(name);
2978
+ fn.remove();
2979
+ }
2980
+ for (const stmt of [...sourceFile.getVariableStatements()]) {
2981
+ const declarations = [...stmt.getDeclarations()];
2982
+ const toRemove = declarations.filter((decl) => HELPER_NAMES.has(decl.getName()));
2983
+ if (toRemove.length === 0) continue;
2984
+ removedHelpers.push(...toRemove.map((decl) => decl.getName()));
2985
+ if (toRemove.length === declarations.length) {
2986
+ stmt.remove();
2987
+ } else {
2988
+ for (const decl of toRemove) decl.remove();
2989
+ }
2990
+ }
2991
+ for (const decl of sourceFile.getImportDeclarations()) {
2992
+ const matches = decl.getNamedImports().filter((ni) => ni.getName() === FHIR_IMPORT);
2993
+ if (matches.length === 0) continue;
2994
+ for (const named of matches) {
2995
+ named.remove();
2996
+ removedImportCount++;
2997
+ }
2998
+ const hasAnyNamedImportsLeft = decl.getNamedImports().length > 0;
2999
+ const hasDefaultImport = !!decl.getDefaultImport();
3000
+ const hasNamespaceImport = !!decl.getNamespaceImport();
3001
+ if (!hasAnyNamedImportsLeft && !hasDefaultImport && !hasNamespaceImport) {
3002
+ decl.remove();
3003
+ }
3004
+ }
3005
+ const remainingFhirReferences = sourceFile.getDescendantsOfKind(import_ts_morph14.SyntaxKind.Identifier).filter((id) => id.getText() === FHIR_IMPORT).filter((id) => !id.getFirstAncestorByKind(import_ts_morph14.SyntaxKind.ImportDeclaration)).length;
3006
+ return { removedHelpers, removedImportCount, remainingFhirReferences };
3007
+ }
3008
+ async function main14() {
3009
+ const project = new import_ts_morph14.Project({
3010
+ tsConfigFilePath: import_path14.default.resolve(process.cwd(), "tsconfig.json"),
3011
+ skipAddingFilesFromTsConfig: false
3012
+ });
3013
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
3014
+ const fp = sf.getFilePath();
3015
+ return fp.includes("/src/") && !fp.includes("/node_modules/");
3016
+ });
3017
+ console.log(`Removing legacy FHIR_URL helpers in ${sourceFiles.length} source file(s)...
3018
+ `);
3019
+ let totalRemovedImports = 0;
3020
+ let totalRemovedHelpers = 0;
3021
+ const modifiedFiles = [];
3022
+ const warnings = [];
3023
+ for (const sourceFile of sourceFiles) {
3024
+ const filePath = sourceFile.getFilePath();
3025
+ const rel = import_path14.default.relative(process.cwd(), filePath);
3026
+ const result = removeHelpers(filePath, project);
3027
+ const changed = result.removedImportCount > 0 || result.removedHelpers.length > 0;
3028
+ if (changed) {
3029
+ modifiedFiles.push(filePath);
3030
+ totalRemovedImports += result.removedImportCount;
3031
+ totalRemovedHelpers += result.removedHelpers.length;
3032
+ if (result.removedHelpers.length > 0) {
3033
+ console.log(
3034
+ ` [${rel}] Removed helper(s): ${result.removedHelpers.join(", ")}`
3035
+ );
3036
+ }
3037
+ if (result.removedImportCount > 0) {
3038
+ console.log(
3039
+ ` [${rel}] Removed ${result.removedImportCount} '${FHIR_IMPORT}' import(s)`
3040
+ );
3041
+ }
3042
+ }
3043
+ if (result.remainingFhirReferences > 0) {
3044
+ warnings.push(
3045
+ `${rel}: ${result.remainingFhirReferences} remaining '${FHIR_IMPORT}' reference(s)`
3046
+ );
3047
+ }
3048
+ }
3049
+ if (modifiedFiles.length === 0 && warnings.length === 0) {
3050
+ console.log("No FHIR_URL imports/helpers found. Nothing to do.");
3051
+ return;
3052
+ }
3053
+ if (modifiedFiles.length > 0) {
3054
+ console.log(`
3055
+ Saving ${modifiedFiles.length} modified file(s)...`);
3056
+ for (const filePath of modifiedFiles) {
3057
+ await project.getSourceFileOrThrow(filePath).save();
3058
+ console.log(` Saved: ${import_path14.default.relative(process.cwd(), filePath)}`);
3059
+ }
3060
+ }
3061
+ if (warnings.length > 0) {
3062
+ console.warn(
3063
+ `
3064
+ WARNING: Some '${FHIR_IMPORT}' usages remain and need manual cleanup:
3065
+ ` + warnings.map((line) => ` - ${line}`).join("\n")
3066
+ );
3067
+ }
3068
+ console.log(
3069
+ `
3070
+ Done. Removed ${totalRemovedHelpers} helper declaration(s) and ${totalRemovedImports} '${FHIR_IMPORT}' import(s).`
3071
+ );
3072
+ }
3073
+
3074
+ // src/migrations/v2.0/migrate-application-config-url.ts
3075
+ var import_path15 = __toESM(require("path"));
3076
+ var import_ts_morph15 = require("ts-morph");
3077
+ var TARGET_IDENTIFIER = "APPLICATION_CONFIG_URL";
3078
+ var REPLACEMENT_IDENTIFIER = "applicationConfig";
3079
+ var REPLACEMENT_MODULE = "@countryconfig/api/application/application-config";
3080
+ var FUNCTION_LIKE_KINDS = [
3081
+ import_ts_morph15.SyntaxKind.FunctionDeclaration,
3082
+ import_ts_morph15.SyntaxKind.FunctionExpression,
3083
+ import_ts_morph15.SyntaxKind.ArrowFunction,
3084
+ import_ts_morph15.SyntaxKind.MethodDeclaration
3085
+ ];
3086
+ function describeFunction(node) {
3087
+ if (import_ts_morph15.Node.isFunctionDeclaration(node) || import_ts_morph15.Node.isMethodDeclaration(node)) {
3088
+ return node.getName() ?? "<anonymous>";
3089
+ }
3090
+ const parent = node.getParent();
3091
+ if (parent && import_ts_morph15.Node.isVariableDeclaration(parent)) {
3092
+ return parent.getName();
3093
+ }
3094
+ if (parent && import_ts_morph15.Node.isPropertyAssignment(parent)) {
3095
+ return parent.getName();
3096
+ }
3097
+ if (import_ts_morph15.Node.isFunctionExpression(node) && node.getNameNode() !== void 0) {
3098
+ return node.getNameNode()?.getText() ?? "<anonymous>";
3099
+ }
3100
+ return "<anonymous>";
3101
+ }
3102
+ function hasBlockBody(node) {
3103
+ if (import_ts_morph15.Node.isFunctionDeclaration(node) || import_ts_morph15.Node.isFunctionExpression(node) || import_ts_morph15.Node.isMethodDeclaration(node)) {
3104
+ return node.getBody() !== void 0;
3105
+ }
3106
+ if (import_ts_morph15.Node.isArrowFunction(node)) {
3107
+ return node.getBody().getKind() === import_ts_morph15.SyntaxKind.Block;
3108
+ }
3109
+ return false;
3110
+ }
3111
+ function isConfigFetcher(node) {
3112
+ let referencesTarget = false;
3113
+ let callsFetch = false;
3114
+ for (const id of node.getDescendantsOfKind(import_ts_morph15.SyntaxKind.Identifier)) {
3115
+ const text = id.getText();
3116
+ if (!referencesTarget && text === TARGET_IDENTIFIER) referencesTarget = true;
3117
+ if (!callsFetch && text === "fetch") {
3118
+ const parent = id.getParent();
3119
+ if (import_ts_morph15.Node.isCallExpression(parent) && parent.getExpression() === id) {
3120
+ callsFetch = true;
3121
+ }
3122
+ }
3123
+ if (referencesTarget && callsFetch) return true;
3124
+ }
3125
+ return false;
3126
+ }
3127
+ function processFile11(sourceFile) {
3128
+ const importsWithTarget = sourceFile.getImportDeclarations().filter(
3129
+ (decl) => decl.getNamedImports().some((ni) => ni.getName() === TARGET_IDENTIFIER)
3130
+ );
3131
+ if (importsWithTarget.length === 0) {
3132
+ return { changed: false, rewrittenFunctions: [], renamedReferences: 0 };
3133
+ }
3134
+ const collidingLocals = sourceFile.getDescendantsOfKind(import_ts_morph15.SyntaxKind.Identifier).filter((id) => id.getText() === REPLACEMENT_IDENTIFIER).filter((id) => {
3135
+ const parent = id.getParent();
3136
+ const kind = parent.getKind();
3137
+ return kind === import_ts_morph15.SyntaxKind.VariableDeclaration || kind === import_ts_morph15.SyntaxKind.FunctionDeclaration || kind === import_ts_morph15.SyntaxKind.ClassDeclaration || kind === import_ts_morph15.SyntaxKind.Parameter;
3138
+ });
3139
+ if (collidingLocals.length > 0) {
3140
+ return {
3141
+ changed: false,
3142
+ rewrittenFunctions: [],
3143
+ renamedReferences: 0,
3144
+ skippedReason: `local declaration named '${REPLACEMENT_IDENTIFIER}' would collide`
3145
+ };
3146
+ }
3147
+ const candidateFunctions = FUNCTION_LIKE_KINDS.flatMap(
3148
+ (kind) => sourceFile.getDescendantsOfKind(kind)
3149
+ );
3150
+ candidateFunctions.sort((a, b) => a.getStart() - b.getStart());
3151
+ const rewrittenFunctions = [];
3152
+ const rewrittenSet = /* @__PURE__ */ new Set();
3153
+ for (const fn of candidateFunctions) {
3154
+ if (!hasBlockBody(fn)) continue;
3155
+ if (!isConfigFetcher(fn)) continue;
3156
+ let inRewrittenAncestor = false;
3157
+ let cursor = fn.getParent();
3158
+ while (cursor) {
3159
+ if (rewrittenSet.has(cursor)) {
3160
+ inRewrittenAncestor = true;
3161
+ break;
3162
+ }
3163
+ cursor = cursor.getParent();
3164
+ }
3165
+ if (inRewrittenAncestor) continue;
3166
+ const start = fn.getStart();
3167
+ const line = sourceFile.getLineAndColumnAtPos(start).line;
3168
+ const name = describeFunction(fn);
3169
+ if (import_ts_morph15.Node.isFunctionDeclaration(fn) || import_ts_morph15.Node.isFunctionExpression(fn) || import_ts_morph15.Node.isMethodDeclaration(fn) || import_ts_morph15.Node.isArrowFunction(fn)) {
3170
+ fn.setBodyText(`return ${REPLACEMENT_IDENTIFIER}`);
3171
+ }
3172
+ rewrittenFunctions.push({ name, line });
3173
+ rewrittenSet.add(fn);
3174
+ }
3175
+ for (const decl of importsWithTarget) {
3176
+ const targetNamed = decl.getNamedImports().find((ni) => ni.getName() === TARGET_IDENTIFIER);
3177
+ targetNamed?.remove();
3178
+ const isNowEmpty = decl.getNamedImports().length === 0 && !decl.getDefaultImport() && !decl.getNamespaceImport();
3179
+ if (isNowEmpty) decl.remove();
3180
+ }
3181
+ const existing = sourceFile.getImportDeclaration(
3182
+ (decl) => decl.getModuleSpecifierValue() === REPLACEMENT_MODULE
3183
+ );
3184
+ if (existing) {
3185
+ const alreadyImported = existing.getNamedImports().some((ni) => ni.getName() === REPLACEMENT_IDENTIFIER);
3186
+ if (!alreadyImported) existing.addNamedImport(REPLACEMENT_IDENTIFIER);
3187
+ } else {
3188
+ sourceFile.addImportDeclaration({
3189
+ moduleSpecifier: REPLACEMENT_MODULE,
3190
+ namedImports: [REPLACEMENT_IDENTIFIER]
3191
+ });
3192
+ }
3193
+ const renameTargets = sourceFile.getDescendantsOfKind(import_ts_morph15.SyntaxKind.Identifier).filter((id) => id.getText() === TARGET_IDENTIFIER).filter((id) => !id.getFirstAncestorByKind(import_ts_morph15.SyntaxKind.ImportDeclaration)).filter((id) => {
3194
+ const parent = id.getParent();
3195
+ const parentKind = parent.getKind();
3196
+ if (parentKind === import_ts_morph15.SyntaxKind.PropertyAccessExpression) {
3197
+ const pae = parent.asKind(import_ts_morph15.SyntaxKind.PropertyAccessExpression);
3198
+ if (pae.getNameNode() === id) return false;
3199
+ }
3200
+ if (parentKind === import_ts_morph15.SyntaxKind.PropertyAssignment) {
3201
+ const pa = parent.asKind(import_ts_morph15.SyntaxKind.PropertyAssignment);
3202
+ if (pa.getNameNode() === id) return false;
3203
+ }
3204
+ if (parentKind === import_ts_morph15.SyntaxKind.ShorthandPropertyAssignment) {
3205
+ return false;
3206
+ }
3207
+ return true;
3208
+ }).sort((a, b) => b.getPos() - a.getPos());
3209
+ for (const id of renameTargets) {
3210
+ id.replaceWithText(REPLACEMENT_IDENTIFIER);
3211
+ }
3212
+ return {
3213
+ changed: true,
3214
+ rewrittenFunctions,
3215
+ renamedReferences: renameTargets.length
3216
+ };
3217
+ }
3218
+ async function main15() {
3219
+ const project = new import_ts_morph15.Project({
3220
+ tsConfigFilePath: import_path15.default.resolve(process.cwd(), "tsconfig.json"),
3221
+ skipAddingFilesFromTsConfig: false
3222
+ });
3223
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
3224
+ const fp = sf.getFilePath();
3225
+ return fp.includes("/src/") && !fp.includes("/node_modules/");
3226
+ });
3227
+ console.log(
3228
+ `Migrating '${TARGET_IDENTIFIER}' \u2192 '${REPLACEMENT_IDENTIFIER}' across ${sourceFiles.length} source file(s)...
3229
+ `
3230
+ );
3231
+ let changedFiles = 0;
3232
+ let totalRewrittenFunctions = 0;
3233
+ let totalRenamedReferences = 0;
3234
+ const skipped = [];
3235
+ for (const sourceFile of sourceFiles) {
3236
+ const filePath = sourceFile.getFilePath();
3237
+ const rel = import_path15.default.relative(process.cwd(), filePath);
3238
+ const result = processFile11(sourceFile);
3239
+ if (result.skippedReason) {
3240
+ skipped.push(`${rel} (${result.skippedReason})`);
3241
+ continue;
3242
+ }
3243
+ if (!result.changed) continue;
3244
+ await sourceFile.save();
3245
+ changedFiles++;
3246
+ totalRewrittenFunctions += result.rewrittenFunctions.length;
3247
+ totalRenamedReferences += result.renamedReferences;
3248
+ for (const fn of result.rewrittenFunctions) {
3249
+ console.log(
3250
+ ` [${rel}:${fn.line}] Rewrote '${fn.name}' body \u2192 'return ${REPLACEMENT_IDENTIFIER}' (config fetch eliminated)`
3251
+ );
3252
+ }
3253
+ if (result.renamedReferences > 0) {
3254
+ console.log(
3255
+ ` [${rel}] Renamed ${result.renamedReferences} stray reference(s) ${TARGET_IDENTIFIER} \u2192 ${REPLACEMENT_IDENTIFIER}`
3256
+ );
3257
+ }
3258
+ }
3259
+ if (skipped.length > 0) {
3260
+ console.warn(
3261
+ `
3262
+ ${skipped.length} file(s) skipped:
3263
+ ` + skipped.map((s) => ` - ${s}`).join("\n")
3264
+ );
3265
+ }
3266
+ if (changedFiles === 0) {
3267
+ console.log(`
3268
+ No files reference '${TARGET_IDENTIFIER}'. Nothing to do.`);
3269
+ return;
3270
+ }
3271
+ console.log(
3272
+ `
3273
+ Done. Updated ${changedFiles} file(s): ${totalRewrittenFunctions} fetch function(s) rewritten, ${totalRenamedReferences} bare reference(s) renamed.`
3274
+ );
3275
+ if (totalRewrittenFunctions > 0) {
3276
+ console.log(
3277
+ `NOTE: rewriting fetch functions discards any custom logic that lived inside them. Review the diff and re-apply any country-specific adjustments on top of '${REPLACEMENT_IDENTIFIER}'.`
3278
+ );
3279
+ }
3280
+ }
3281
+
3282
+ // src/migrations/v2.0/remove-old-statistics-service.ts
3283
+ var import_path16 = __toESM(require("path"));
3284
+ var import_ts_morph16 = require("ts-morph");
3285
+ var ROUTE_PATH = "/statistics";
3286
+ var HANDLER_NAME = "statisticsHandler";
3287
+ function isStatisticsRouteObject(obj) {
3288
+ const pathProp = obj.getProperty("path");
3289
+ if (!pathProp || !import_ts_morph16.Node.isPropertyAssignment(pathProp)) return false;
3290
+ const init = pathProp.getInitializer();
3291
+ if (!init) return false;
3292
+ if (import_ts_morph16.Node.isStringLiteral(init) || import_ts_morph16.Node.isNoSubstitutionTemplateLiteral(init)) {
3293
+ return init.getLiteralValue() === ROUTE_PATH;
3294
+ }
3295
+ return false;
3296
+ }
3297
+ function removeStatisticsRoutes(sourceFile) {
3298
+ let removed = 0;
3299
+ const callExpressions = [
3300
+ ...sourceFile.getDescendantsOfKind(import_ts_morph16.SyntaxKind.CallExpression)
3301
+ ];
3302
+ for (const call of callExpressions) {
3303
+ if (call.wasForgotten()) continue;
3304
+ const expr = call.getExpression();
3305
+ if (!import_ts_morph16.Node.isPropertyAccessExpression(expr)) continue;
3306
+ if (expr.getName() !== "route") continue;
3307
+ const args2 = call.getArguments();
3308
+ if (args2.length === 0) continue;
3309
+ const firstArg = args2[0];
3310
+ if (import_ts_morph16.Node.isObjectLiteralExpression(firstArg)) {
3311
+ if (!isStatisticsRouteObject(firstArg)) continue;
3312
+ const stmt = call.getFirstAncestorByKind(import_ts_morph16.SyntaxKind.ExpressionStatement);
3313
+ if (stmt) {
3314
+ stmt.remove();
3315
+ removed++;
3316
+ } else {
3317
+ call.replaceWithText("");
3318
+ removed++;
3319
+ }
3320
+ continue;
3321
+ }
3322
+ if (import_ts_morph16.Node.isArrayLiteralExpression(firstArg)) {
3323
+ const matchingIndexes = firstArg.getElements().map((el, idx) => ({ el, idx })).filter(
3324
+ ({ el }) => import_ts_morph16.Node.isObjectLiteralExpression(el) && isStatisticsRouteObject(el)
3325
+ ).map(({ idx }) => idx);
3326
+ matchingIndexes.sort((a, b) => b - a).forEach((idx) => {
3327
+ firstArg.removeElement(idx);
3328
+ removed++;
3329
+ });
3330
+ }
3331
+ }
3332
+ return removed;
3333
+ }
3334
+ function removeStatisticsHandlerImports(sourceFile) {
3335
+ let changed = false;
3336
+ for (const decl of sourceFile.getImportDeclarations()) {
3337
+ const targetNamed = decl.getNamedImports().find((ni) => ni.getName() === HANDLER_NAME);
3338
+ if (!targetNamed) continue;
3339
+ targetNamed.remove();
3340
+ changed = true;
3341
+ const isNowEmpty = decl.getNamedImports().length === 0 && !decl.getDefaultImport() && !decl.getNamespaceImport();
3342
+ if (isNowEmpty) decl.remove();
3343
+ }
3344
+ return changed;
3345
+ }
3346
+ function processFile12(sourceFile) {
3347
+ const routesRemoved = removeStatisticsRoutes(sourceFile);
3348
+ const importsCleaned = removeStatisticsHandlerImports(sourceFile);
3349
+ return { routesRemoved, importsCleaned };
3350
+ }
3351
+ function collectLocalImportsInSubtree(sourceFile, subtreeRoot) {
3352
+ const result = [];
3353
+ for (const decl of sourceFile.getImportDeclarations()) {
3354
+ const moduleSf = decl.getModuleSpecifierSourceFile();
3355
+ if (!moduleSf) continue;
3356
+ const fp = moduleSf.getFilePath();
3357
+ if (!fp.startsWith(subtreeRoot)) continue;
3358
+ result.push(fp);
3359
+ }
3360
+ return result;
3361
+ }
3362
+ function deleteOrphanedDescendants(project, startingFile, subtreeRoot) {
3363
+ const initialCandidates = collectLocalImportsInSubtree(
3364
+ startingFile,
3365
+ subtreeRoot
3366
+ );
3367
+ const startingPath = startingFile.getFilePath();
3368
+ const deleted = /* @__PURE__ */ new Set([startingPath]);
3369
+ const seen = /* @__PURE__ */ new Set();
3370
+ const worklist = [...initialCandidates];
3371
+ const orphans = [];
3372
+ while (worklist.length > 0) {
3373
+ const candidate = worklist.shift();
3374
+ if (seen.has(candidate)) continue;
3375
+ seen.add(candidate);
3376
+ if (deleted.has(candidate)) continue;
3377
+ if (!candidate.startsWith(subtreeRoot)) continue;
3378
+ const candidateSf = project.getSourceFile(candidate);
3379
+ if (!candidateSf) continue;
3380
+ const stillImported = project.getSourceFiles().some((sf) => {
3381
+ const sfPath = sf.getFilePath();
3382
+ if (deleted.has(sfPath)) return false;
3383
+ if (sfPath === candidate) return false;
3384
+ return sf.getImportDeclarations().some(
3385
+ (decl) => decl.getModuleSpecifierSourceFile()?.getFilePath() === candidate
3386
+ );
3387
+ });
3388
+ if (stillImported) continue;
3389
+ const next = collectLocalImportsInSubtree(candidateSf, subtreeRoot);
3390
+ worklist.push(...next);
3391
+ candidateSf.delete();
3392
+ deleted.add(candidate);
3393
+ orphans.push(candidate);
3394
+ }
3395
+ return orphans;
3396
+ }
3397
+ function processHandlerFile(project) {
3398
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
3399
+ const fp = sf.getFilePath();
3400
+ return fp.includes("/src/") && !fp.includes("/node_modules/");
3401
+ });
3402
+ for (const sf of sourceFiles) {
3403
+ const fn = sf.getFunction(HANDLER_NAME);
3404
+ if (!fn) continue;
3405
+ const filePath = sf.getFilePath();
3406
+ const exportsByName = [...sf.getExportedDeclarations().keys()];
3407
+ const otherExports = exportsByName.filter((name) => name !== HANDLER_NAME);
3408
+ if (otherExports.length === 0) {
3409
+ const subtreeRoot = import_path16.default.dirname(filePath) + import_path16.default.sep;
3410
+ const orphansDeleted = deleteOrphanedDescendants(
3411
+ project,
3412
+ sf,
3413
+ subtreeRoot
3414
+ );
3415
+ sf.delete();
3416
+ return { filePath, action: "deleted", orphansDeleted };
3417
+ }
3418
+ fn.remove();
3419
+ return {
3420
+ filePath,
3421
+ action: "function-removed",
3422
+ remainingExports: otherExports,
3423
+ orphansDeleted: []
3424
+ };
3425
+ }
3426
+ return { filePath: "", action: "not-found", orphansDeleted: [] };
3427
+ }
3428
+ async function main16() {
3429
+ const project = new import_ts_morph16.Project({
3430
+ tsConfigFilePath: import_path16.default.resolve(process.cwd(), "tsconfig.json"),
3431
+ skipAddingFilesFromTsConfig: false
3432
+ });
3433
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
3434
+ const fp = sf.getFilePath();
3435
+ return fp.includes("/src/") && !fp.includes("/node_modules/");
3436
+ });
3437
+ console.log(
3438
+ `Removing legacy 'GET ${ROUTE_PATH}' route and '${HANDLER_NAME}' across ${sourceFiles.length} source file(s)...
3439
+ `
3440
+ );
3441
+ let totalRoutesRemoved = 0;
3442
+ let totalFilesWithImportsCleaned = 0;
3443
+ let totalFilesWithRoutesRemoved = 0;
3444
+ for (const sourceFile of sourceFiles) {
3445
+ const filePath = sourceFile.getFilePath();
3446
+ const rel = import_path16.default.relative(process.cwd(), filePath);
3447
+ const result = processFile12(sourceFile);
3448
+ if (result.routesRemoved > 0) {
3449
+ console.log(
3450
+ ` [${rel}] Removed ${result.routesRemoved} '${ROUTE_PATH}' route(s)`
3451
+ );
3452
+ totalRoutesRemoved += result.routesRemoved;
3453
+ totalFilesWithRoutesRemoved++;
3454
+ }
3455
+ if (result.importsCleaned) {
3456
+ console.log(` [${rel}] Cleaned up '${HANDLER_NAME}' import`);
3457
+ totalFilesWithImportsCleaned++;
3458
+ }
3459
+ if (result.routesRemoved > 0 || result.importsCleaned) {
3460
+ await sourceFile.save();
3461
+ }
3462
+ }
3463
+ const handlerResult = processHandlerFile(project);
3464
+ switch (handlerResult.action) {
3465
+ case "deleted": {
3466
+ const rel = import_path16.default.relative(process.cwd(), handlerResult.filePath);
3467
+ console.log(
3468
+ ` [${rel}] Deleted handler file ('${HANDLER_NAME}' was the sole export).`
3469
+ );
3470
+ for (const orphanPath of handlerResult.orphansDeleted) {
3471
+ const orphanRel = import_path16.default.relative(process.cwd(), orphanPath);
3472
+ console.log(
3473
+ ` [${orphanRel}] Deleted (no remaining importers in the project after handler removal).`
3474
+ );
3475
+ }
3476
+ await project.save();
3477
+ break;
3478
+ }
3479
+ case "function-removed": {
3480
+ const rel = import_path16.default.relative(process.cwd(), handlerResult.filePath);
3481
+ console.warn(
3482
+ ` [${rel}] Removed '${HANDLER_NAME}' function but kept the file (other exports remain: ${handlerResult.remainingExports?.join(", ")}). Review whether the file is still needed.`
3483
+ );
3484
+ const sf = project.getSourceFile(handlerResult.filePath);
3485
+ if (sf) await sf.save();
3486
+ break;
3487
+ }
3488
+ case "not-found":
3489
+ break;
3490
+ }
3491
+ if (totalRoutesRemoved === 0 && totalFilesWithImportsCleaned === 0 && handlerResult.action === "not-found") {
3492
+ console.log(`
3493
+ No legacy '${ROUTE_PATH}' route or handler found. Nothing to do.`);
3494
+ return;
3495
+ }
3496
+ const orphansSummary = handlerResult.orphansDeleted.length > 0 ? ` and ${handlerResult.orphansDeleted.length} orphaned helper file(s)` : "";
3497
+ console.log(
3498
+ `
3499
+ Done. Removed ${totalRoutesRemoved} route(s) across ${totalFilesWithRoutesRemoved} file(s); cleaned ${HANDLER_NAME} import in ${totalFilesWithImportsCleaned} file(s)${orphansSummary}.`
3500
+ );
3501
+ }
3502
+
3503
+ // src/migrations/v2.0/update-package-json.ts
3504
+ var import_fs = require("fs");
3505
+ var import_path17 = __toESM(require("path"));
3506
+ var PACKAGE_JSON_RELATIVE = "package.json";
3507
+ var TARGET_VERSION = "2.0.0";
3508
+ var TARGET_ZOD_RANGE = "^4.2.1";
3509
+ async function main17() {
3510
+ const packageJsonPath = import_path17.default.join(process.cwd(), PACKAGE_JSON_RELATIVE);
3511
+ if (!(0, import_fs.existsSync)(packageJsonPath)) {
3512
+ console.log(`'${PACKAGE_JSON_RELATIVE}' not found in ${process.cwd()} \u2014 skipping.`);
3513
+ return;
3514
+ }
3515
+ const raw = (0, import_fs.readFileSync)(packageJsonPath, "utf8");
3516
+ let parsed;
3517
+ try {
3518
+ parsed = JSON.parse(raw);
3519
+ } catch (error) {
3520
+ console.warn(
3521
+ ` [${PACKAGE_JSON_RELATIVE}] Failed to parse JSON \u2014 skipping update. ${error.message}`
3522
+ );
3523
+ return;
3524
+ }
3525
+ console.log(`Updating '${PACKAGE_JSON_RELATIVE}'...`);
3526
+ const previousVersion = parsed.version;
3527
+ const versionChanged = previousVersion !== TARGET_VERSION;
3528
+ parsed.version = TARGET_VERSION;
3529
+ if (versionChanged) {
3530
+ console.log(
3531
+ ` Set version: ${previousVersion ?? "<unset>"} \u2192 ${TARGET_VERSION}`
3532
+ );
3533
+ } else {
3534
+ console.log(` Version already ${TARGET_VERSION} \u2014 no change.`);
3535
+ }
3536
+ let zodBucket = "dependencies";
3537
+ if (parsed.devDependencies && "zod" in parsed.devDependencies) {
3538
+ zodBucket = "devDependencies";
3539
+ }
3540
+ if (!parsed[zodBucket]) {
3541
+ parsed[zodBucket] = {};
3542
+ }
3543
+ const bucket = parsed[zodBucket];
3544
+ const previousZod = bucket.zod;
3545
+ const zodChanged = previousZod !== TARGET_ZOD_RANGE;
3546
+ bucket.zod = TARGET_ZOD_RANGE;
3547
+ if (zodChanged) {
3548
+ console.log(
3549
+ ` Set ${zodBucket}.zod: ${previousZod ?? "<absent>"} \u2192 ${TARGET_ZOD_RANGE}`
3550
+ );
3551
+ } else {
3552
+ console.log(` ${zodBucket}.zod already ${TARGET_ZOD_RANGE} \u2014 no change.`);
3553
+ }
3554
+ const trailingNewline = raw.endsWith("\n") ? "\n" : "";
3555
+ const next = JSON.stringify(parsed, null, 2) + trailingNewline;
3556
+ if (next === raw) {
3557
+ console.log("\n No changes to write.");
3558
+ return;
3559
+ }
3560
+ (0, import_fs.writeFileSync)(packageJsonPath, next, "utf8");
3561
+ console.log(`
3562
+ Saved '${PACKAGE_JSON_RELATIVE}'.`);
3563
+ console.log(
3564
+ ` NOTE: run \`yarn install\` (or your package manager equivalent) to install the updated dependency versions.`
3565
+ );
3566
+ }
3567
+
3568
+ // src/migrations/v2.0/migrate-workqueue-configs.ts
3569
+ var import_ts_morph17 = require("ts-morph");
3570
+ var import_path18 = __toESM(require("path"));
2848
3571
  var DEFINE_WORKQUEUES_NAME3 = "defineWorkqueues";
2849
3572
  var ACTIONS_PROPERTY_NAME5 = "actions";
2850
3573
  var ACTION_PROPERTY_NAME2 = "action";
@@ -2865,28 +3588,28 @@ var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
2865
3588
  ]);
2866
3589
  function getWorkqueueLabel(workqueue) {
2867
3590
  const slugProperty = workqueue.getProperty(SLUG_PROPERTY_NAME);
2868
- if (!slugProperty || !import_ts_morph13.Node.isPropertyAssignment(slugProperty)) {
3591
+ if (!slugProperty || !import_ts_morph17.Node.isPropertyAssignment(slugProperty)) {
2869
3592
  return "(unknown slug)";
2870
3593
  }
2871
3594
  const slugInitializer = slugProperty.getInitializer();
2872
- if (!slugInitializer || !import_ts_morph13.Node.isStringLiteral(slugInitializer)) {
3595
+ if (!slugInitializer || !import_ts_morph17.Node.isStringLiteral(slugInitializer)) {
2873
3596
  return "(unknown slug)";
2874
3597
  }
2875
3598
  return slugInitializer.getLiteralValue();
2876
3599
  }
2877
3600
  function getActionTypeName(action) {
2878
3601
  const typeProperty = action.getProperty(TYPE_PROPERTY_NAME7);
2879
- if (!typeProperty || !import_ts_morph13.Node.isPropertyAssignment(typeProperty)) {
3602
+ if (!typeProperty || !import_ts_morph17.Node.isPropertyAssignment(typeProperty)) {
2880
3603
  return null;
2881
3604
  }
2882
3605
  const typeInitializer = typeProperty.getInitializer();
2883
3606
  if (!typeInitializer) {
2884
3607
  return null;
2885
3608
  }
2886
- if (import_ts_morph13.Node.isStringLiteral(typeInitializer)) {
3609
+ if (import_ts_morph17.Node.isStringLiteral(typeInitializer)) {
2887
3610
  return typeInitializer.getLiteralValue();
2888
3611
  }
2889
- if (import_ts_morph13.Node.isPropertyAccessExpression(typeInitializer)) {
3612
+ if (import_ts_morph17.Node.isPropertyAccessExpression(typeInitializer)) {
2890
3613
  return typeInitializer.getName();
2891
3614
  }
2892
3615
  return null;
@@ -2900,19 +3623,19 @@ function isUnsupportedActionType(action) {
2900
3623
  }
2901
3624
  function migrateWorkqueueActions(workqueue, relPath) {
2902
3625
  const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
2903
- if (!actionsProperty || !import_ts_morph13.Node.isPropertyAssignment(actionsProperty)) {
3626
+ if (!actionsProperty || !import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
2904
3627
  return 0;
2905
3628
  }
2906
3629
  const actionsInitializer = actionsProperty.getInitializer();
2907
- if (!actionsInitializer || !import_ts_morph13.Node.isArrayLiteralExpression(actionsInitializer)) {
3630
+ if (!actionsInitializer || !import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
2908
3631
  return 0;
2909
3632
  }
2910
3633
  const workqueueLabel = getWorkqueueLabel(workqueue);
2911
3634
  const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
2912
3635
  if (existingActionProperty) {
2913
- if (import_ts_morph13.Node.isPropertyAssignment(existingActionProperty)) {
3636
+ if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
2914
3637
  const existingActionInitializer = existingActionProperty.getInitializer();
2915
- if (existingActionInitializer && import_ts_morph13.Node.isObjectLiteralExpression(existingActionInitializer)) {
3638
+ if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
2916
3639
  const conditionalsProperty2 = existingActionInitializer.getProperty(
2917
3640
  CONDITIONALS_PROPERTY_NAME
2918
3641
  );
@@ -2937,7 +3660,7 @@ function migrateWorkqueueActions(workqueue, relPath) {
2937
3660
  return 1;
2938
3661
  }
2939
3662
  const actionCandidates = actionsInitializer.getElements().filter(
2940
- (element) => import_ts_morph13.Node.isObjectLiteralExpression(element)
3663
+ (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
2941
3664
  );
2942
3665
  if (actionCandidates.length === 0) {
2943
3666
  actionsProperty.remove();
@@ -2979,36 +3702,36 @@ function migrateWorkqueueActions(workqueue, relPath) {
2979
3702
  );
2980
3703
  return 1;
2981
3704
  }
2982
- function processFile11(filePath, project) {
3705
+ function processFile13(filePath, project) {
2983
3706
  const sourceFile = project.getSourceFile(filePath);
2984
3707
  if (!sourceFile) return 0;
2985
3708
  let migratedWorkqueues = 0;
2986
- const relPath = import_path13.default.relative(getCwd(), filePath);
3709
+ const relPath = import_path18.default.relative(process.cwd(), filePath);
2987
3710
  const callExpressions = sourceFile.getDescendantsOfKind(
2988
- import_ts_morph13.SyntaxKind.CallExpression
3711
+ import_ts_morph17.SyntaxKind.CallExpression
2989
3712
  );
2990
3713
  for (const call of callExpressions) {
2991
3714
  const expression = call.getExpression();
2992
- if (!import_ts_morph13.Node.isIdentifier(expression) || expression.getText() !== DEFINE_WORKQUEUES_NAME3) {
3715
+ if (!import_ts_morph17.Node.isIdentifier(expression) || expression.getText() !== DEFINE_WORKQUEUES_NAME3) {
2993
3716
  continue;
2994
3717
  }
2995
3718
  const args2 = call.getArguments();
2996
3719
  if (args2.length === 0) continue;
2997
3720
  const workqueuesArg = args2[0];
2998
- if (!import_ts_morph13.Node.isArrayLiteralExpression(workqueuesArg)) continue;
3721
+ if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
2999
3722
  for (const element of workqueuesArg.getElements()) {
3000
- if (!import_ts_morph13.Node.isObjectLiteralExpression(element)) continue;
3723
+ if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
3001
3724
  migratedWorkqueues += migrateWorkqueueActions(element, relPath);
3002
3725
  }
3003
3726
  }
3004
3727
  return migratedWorkqueues;
3005
3728
  }
3006
- async function main13() {
3007
- const srcDir = import_path13.default.join(getCwd(), "src");
3729
+ async function main18() {
3730
+ const srcDir = import_path18.default.join(process.cwd(), "src");
3008
3731
  console.log(`Scanning for defineWorkqueues calls in: ${srcDir}
3009
3732
  `);
3010
- const project = new import_ts_morph13.Project({
3011
- tsConfigFilePath: import_path13.default.resolve(srcDir, "../tsconfig.json"),
3733
+ const project = new import_ts_morph17.Project({
3734
+ tsConfigFilePath: import_path18.default.resolve(srcDir, "../tsconfig.json"),
3012
3735
  skipAddingFilesFromTsConfig: false
3013
3736
  });
3014
3737
  const sourceFiles = project.getSourceFiles().filter((sf) => {
@@ -3021,7 +3744,7 @@ async function main13() {
3021
3744
  const modifiedFiles = [];
3022
3745
  for (const sourceFile of sourceFiles) {
3023
3746
  const filePath = sourceFile.getFilePath();
3024
- const migrated = processFile11(filePath, project);
3747
+ const migrated = processFile13(filePath, project);
3025
3748
  if (migrated > 0) {
3026
3749
  totalMigrated += migrated;
3027
3750
  modifiedFiles.push(filePath);
@@ -3038,7 +3761,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
3038
3761
  for (const filePath of modifiedFiles) {
3039
3762
  const sourceFile = project.getSourceFileOrThrow(filePath);
3040
3763
  await sourceFile.save();
3041
- console.log(` Saved: ${import_path13.default.relative(getCwd(), filePath)}`);
3764
+ console.log(` Saved: ${import_path18.default.relative(process.cwd(), filePath)}`);
3042
3765
  }
3043
3766
  console.log(
3044
3767
  `
@@ -3047,13 +3770,13 @@ Done. Migrated ${totalMigrated} deprecated workqueue 'actions' definition(s) to
3047
3770
  }
3048
3771
 
3049
3772
  // src/migrations/v2.0/remove-demo-scope.ts
3050
- var import_path14 = __toESM(require("path"));
3051
- var import_ts_morph14 = require("ts-morph");
3773
+ var import_path19 = __toESM(require("path"));
3774
+ var import_ts_morph18 = require("ts-morph");
3052
3775
  var ROLES_HANDLER_RELATIVE_PATH = "src/data-seeding/roles/handler.ts";
3053
- async function main14() {
3054
- const rolesHandlerPath = import_path14.default.join(getCwd(), ROLES_HANDLER_RELATIVE_PATH);
3055
- const project = new import_ts_morph14.Project({
3056
- tsConfigFilePath: import_path14.default.resolve(getCwd(), "tsconfig.json"),
3776
+ async function main19() {
3777
+ const rolesHandlerPath = import_path19.default.join(process.cwd(), ROLES_HANDLER_RELATIVE_PATH);
3778
+ const project = new import_ts_morph18.Project({
3779
+ tsConfigFilePath: import_path19.default.resolve(process.cwd(), "tsconfig.json"),
3057
3780
  skipAddingFilesFromTsConfig: false
3058
3781
  });
3059
3782
  const sourceFile = project.getSourceFile(rolesHandlerPath);
@@ -3102,7 +3825,7 @@ async function main14() {
3102
3825
 
3103
3826
  // src/migrations/v2.0/remove-hearth-migrations.ts
3104
3827
  var fs2 = __toESM(require("fs"));
3105
- var import_path15 = __toESM(require("path"));
3828
+ var import_path20 = __toESM(require("path"));
3106
3829
  var PATHS_TO_REMOVE = [
3107
3830
  {
3108
3831
  relativePath: "src/migrations/hearth",
@@ -3114,9 +3837,9 @@ var PATHS_TO_REMOVE = [
3114
3837
  },
3115
3838
  { relativePath: "src/utils/hearth-helpers.ts", label: "Hearth helpers file" }
3116
3839
  ];
3117
- async function main15() {
3840
+ async function main20() {
3118
3841
  for (const { relativePath, label } of PATHS_TO_REMOVE) {
3119
- const absolutePath = import_path15.default.join(getCwd(), relativePath);
3842
+ const absolutePath = import_path20.default.join(process.cwd(), relativePath);
3120
3843
  if (fs2.existsSync(absolutePath)) {
3121
3844
  fs2.rmSync(absolutePath, { recursive: true, force: true });
3122
3845
  console.log(`Removed ${label} '${relativePath}'.`);
@@ -3127,9 +3850,9 @@ async function main15() {
3127
3850
  }
3128
3851
 
3129
3852
  // src/migrations/v2.0/create-events-index.ts
3130
- var import_ts_morph15 = require("ts-morph");
3131
- var import_fs = __toESM(require("fs"));
3132
- var import_path16 = __toESM(require("path"));
3853
+ var import_ts_morph19 = require("ts-morph");
3854
+ var import_fs2 = __toESM(require("fs"));
3855
+ var import_path21 = __toESM(require("path"));
3133
3856
  var DEFINE_CONFIG_NAME5 = "defineConfig";
3134
3857
  var HANDLER_FUNCTION_NAMES = /* @__PURE__ */ new Set([
3135
3858
  "getCustomEventsHandler",
@@ -3151,7 +3874,7 @@ var LICENSE_HEADER = `/*
3151
3874
  `;
3152
3875
  function findEventConfigExports(project, srcDir) {
3153
3876
  const found = [];
3154
- const eventsIndexAbs = import_path16.default.join(srcDir, "events", "index.ts");
3877
+ const eventsIndexAbs = import_path21.default.join(srcDir, "events", "index.ts");
3155
3878
  for (const sourceFile of project.getSourceFiles()) {
3156
3879
  const filePath = sourceFile.getFilePath();
3157
3880
  if (!filePath.startsWith(srcDir) || filePath.includes("/node_modules/")) {
@@ -3163,10 +3886,10 @@ function findEventConfigExports(project, srcDir) {
3163
3886
  for (const decl of statement.getDeclarations()) {
3164
3887
  const initializer = decl.getInitializer();
3165
3888
  if (!initializer) continue;
3166
- const unwrapped = import_ts_morph15.Node.isAsExpression(initializer) ? initializer.getExpression() : initializer;
3167
- if (!import_ts_morph15.Node.isCallExpression(unwrapped)) continue;
3889
+ const unwrapped = import_ts_morph19.Node.isAsExpression(initializer) ? initializer.getExpression() : initializer;
3890
+ if (!import_ts_morph19.Node.isCallExpression(unwrapped)) continue;
3168
3891
  const callee = unwrapped.getExpression();
3169
- if (!import_ts_morph15.Node.isIdentifier(callee)) continue;
3892
+ if (!import_ts_morph19.Node.isIdentifier(callee)) continue;
3170
3893
  if (callee.getText() !== DEFINE_CONFIG_NAME5) continue;
3171
3894
  found.push({ name: decl.getName(), filePath });
3172
3895
  }
@@ -3175,8 +3898,8 @@ function findEventConfigExports(project, srcDir) {
3175
3898
  return found;
3176
3899
  }
3177
3900
  function buildIndexImportSpecifier(eventConfigFilePath, srcDir) {
3178
- const eventsIndexDir = import_path16.default.join(srcDir, "events");
3179
- let rel = import_path16.default.relative(eventsIndexDir, eventConfigFilePath).split(import_path16.default.sep).join("/");
3901
+ const eventsIndexDir = import_path21.default.join(srcDir, "events");
3902
+ let rel = import_path21.default.relative(eventsIndexDir, eventConfigFilePath).split(import_path21.default.sep).join("/");
3180
3903
  if (rel.endsWith(".ts")) rel = rel.slice(0, -".ts".length);
3181
3904
  if (rel.endsWith("/index")) rel = rel.slice(0, -"/index".length);
3182
3905
  if (!rel.startsWith(".")) rel = "./" + rel;
@@ -3253,17 +3976,17 @@ function modifyHandler(sourceFile, exportNames) {
3253
3976
  );
3254
3977
  }
3255
3978
  for (const call of sourceFile.getDescendantsOfKind(
3256
- import_ts_morph15.SyntaxKind.CallExpression
3979
+ import_ts_morph19.SyntaxKind.CallExpression
3257
3980
  )) {
3258
3981
  const callee = call.getExpression();
3259
- if (!import_ts_morph15.Node.isPropertyAccessExpression(callee)) continue;
3982
+ if (!import_ts_morph19.Node.isPropertyAccessExpression(callee)) continue;
3260
3983
  if (callee.getName() !== RESPONSE_METHOD_NAME) continue;
3261
3984
  const args2 = call.getArguments();
3262
3985
  if (args2.length !== 1) continue;
3263
3986
  const [arg] = args2;
3264
- if (!import_ts_morph15.Node.isArrayLiteralExpression(arg)) continue;
3987
+ if (!import_ts_morph19.Node.isArrayLiteralExpression(arg)) continue;
3265
3988
  if (arg.getElements().length === 0) continue;
3266
- if (!arg.getElements().every((el) => import_ts_morph15.Node.isIdentifier(el))) continue;
3989
+ if (!arg.getElements().every((el) => import_ts_morph19.Node.isIdentifier(el))) continue;
3267
3990
  arg.replaceWithText(EVENT_CONFIGS_IDENTIFIER);
3268
3991
  changes.push(
3269
3992
  `rewrote h.response([...]) \u2192 h.response(${EVENT_CONFIGS_IDENTIFIER})`
@@ -3271,13 +3994,13 @@ function modifyHandler(sourceFile, exportNames) {
3271
3994
  }
3272
3995
  return changes;
3273
3996
  }
3274
- async function main16() {
3275
- const cwd2 = getCwd();
3276
- const srcDir = import_path16.default.join(cwd2, "src");
3997
+ async function main21() {
3998
+ const cwd = process.cwd();
3999
+ const srcDir = import_path21.default.join(cwd, "src");
3277
4000
  console.log(`Scanning for defineConfig event exports in: ${srcDir}
3278
4001
  `);
3279
- const project = new import_ts_morph15.Project({
3280
- tsConfigFilePath: import_path16.default.resolve(srcDir, "../tsconfig.json"),
4002
+ const project = new import_ts_morph19.Project({
4003
+ tsConfigFilePath: import_path21.default.resolve(srcDir, "../tsconfig.json"),
3281
4004
  skipAddingFilesFromTsConfig: false
3282
4005
  });
3283
4006
  const eventConfigExports = findEventConfigExports(project, srcDir);
@@ -3289,15 +4012,15 @@ async function main16() {
3289
4012
  }
3290
4013
  console.log(`Found ${eventConfigExports.length} event config export(s):`);
3291
4014
  for (const e of eventConfigExports) {
3292
- console.log(` ${e.name} \u2190 ${import_path16.default.relative(cwd2, e.filePath)}`);
4015
+ console.log(` ${e.name} \u2190 ${import_path21.default.relative(cwd, e.filePath)}`);
3293
4016
  }
3294
- const eventsIndexAbs = import_path16.default.join(srcDir, "events", "index.ts");
3295
- const eventsIndexRel = import_path16.default.relative(cwd2, eventsIndexAbs);
3296
- if (import_fs.default.existsSync(eventsIndexAbs)) {
4017
+ const eventsIndexAbs = import_path21.default.join(srcDir, "events", "index.ts");
4018
+ const eventsIndexRel = import_path21.default.relative(cwd, eventsIndexAbs);
4019
+ if (import_fs2.default.existsSync(eventsIndexAbs)) {
3297
4020
  console.log(`
3298
4021
  [${eventsIndexRel}] already exists \u2014 leaving untouched.`);
3299
4022
  } else {
3300
- import_fs.default.mkdirSync(import_path16.default.dirname(eventsIndexAbs), { recursive: true });
4023
+ import_fs2.default.mkdirSync(import_path21.default.dirname(eventsIndexAbs), { recursive: true });
3301
4024
  const source = buildEventsIndexSource(eventConfigExports, srcDir);
3302
4025
  const eventsIndexFile = project.createSourceFile(eventsIndexAbs, source, {
3303
4026
  overwrite: false
@@ -3318,7 +4041,7 @@ No event-config handler file found (looked for exported function(s): ${Array.fro
3318
4041
  );
3319
4042
  return;
3320
4043
  }
3321
- const handlerRelPath = import_path16.default.relative(cwd2, handler.getFilePath());
4044
+ const handlerRelPath = import_path21.default.relative(cwd, handler.getFilePath());
3322
4045
  const exportNameSet = new Set(eventConfigExports.map((e) => e.name));
3323
4046
  const changes = modifyHandler(handler, exportNameSet);
3324
4047
  if (changes.length === 0) {
@@ -3338,12 +4061,13 @@ var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
3338
4061
  var UPSTREAM_BRANCH = "develop";
3339
4062
  var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
3340
4063
  var FILES_TO_CHECKOUT = [
3341
- "infrastructure/postgres/setup-analytics.sh",
3342
- "src/analytics/analytics.ts"
4064
+ "src/analytics/analytics.ts",
4065
+ "Dockerfile",
4066
+ "Dockerfile.assets"
3343
4067
  ];
3344
4068
  function runGit(args2, { silent = false } = {}) {
3345
4069
  return (0, import_child_process.execFileSync)("git", args2, {
3346
- cwd: getCwd(),
4070
+ cwd: process.cwd(),
3347
4071
  encoding: "utf8",
3348
4072
  stdio: silent ? ["ignore", "pipe", "pipe"] : ["ignore", "pipe", "inherit"]
3349
4073
  });
@@ -3359,11 +4083,11 @@ function tryGit(args2) {
3359
4083
  function assertIsGitRepo() {
3360
4084
  if (!tryGit(["rev-parse", "--is-inside-work-tree"])) {
3361
4085
  throw new Error(
3362
- `checkout-upstream-files: ${getCwd()} is not a git repository. Initialise git (or run the upgrade inside a git checkout) before re-running.`
4086
+ `${process.cwd()} is not a git repository. Initialise git (or run the upgrade inside a git checkout) before re-running.`
3363
4087
  );
3364
4088
  }
3365
4089
  }
3366
- async function main17() {
4090
+ async function main22() {
3367
4091
  console.log(
3368
4092
  `Checking out ${FILES_TO_CHECKOUT.length} file(s) from ${UPSTREAM_URL}@${UPSTREAM_BRANCH}...
3369
4093
  `
@@ -3411,10 +4135,356 @@ async function main17() {
3411
4135
  }
3412
4136
  }
3413
4137
 
4138
+ // src/migrations/v2.0/add-resend-invite-notification.ts
4139
+ var import_fs3 = require("fs");
4140
+ var import_path22 = __toESM(require("path"));
4141
+ var import_ts_morph20 = require("ts-morph");
4142
+ var TS_MORPH_PROJECT_OPTIONS = {
4143
+ skipAddingFilesFromTsConfig: true,
4144
+ manipulationSettings: {
4145
+ indentationText: import_ts_morph20.IndentationText.TwoSpaces
4146
+ }
4147
+ };
4148
+ var EMAIL_TEMPLATES_INDEX = "src/api/notification/email-templates/index.ts";
4149
+ var NOTIFICATION_HANDLER = "src/api/notification/handler.ts";
4150
+ var USER_NOTIFICATION_ROUTES = "src/config/routes/userNotificationRoutes.ts";
4151
+ var SMS_SERVICE = "src/api/notification/sms-service.ts";
4152
+ var NOTIFICATION_CSV = "src/translations/notification.csv";
4153
+ var RESEND_INVITE_HTML = "src/api/notification/email-templates/other/resend-invite.html";
4154
+ var RESEND_INVITE_HTML_CONTENT = `<!doctype html>
4155
+ <html>
4156
+ <head>
4157
+ <link
4158
+ href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap"
4159
+ rel="stylesheet"
4160
+ />
4161
+ <style type="text/css">
4162
+ body {
4163
+ font-family: 'Noto Sans', sans-serif;
4164
+ color: #222;
4165
+ padding: 16px 24px;
4166
+ }
4167
+
4168
+ h1 {
4169
+ font-size: 21px;
4170
+ margin-top: 32px;
4171
+ margin-bottom: 36px;
4172
+ font-weight: 700;
4173
+ }
4174
+
4175
+ p {
4176
+ font-weight: 400;
4177
+ font-size: 16px;
4178
+ line-height: 1.8;
4179
+ margin-bottom: 24px;
4180
+ }
4181
+
4182
+ i {
4183
+ color: #666;
4184
+ font-weight: 400;
4185
+ font-size: 16px;
4186
+ }
4187
+ </style>
4188
+ </head>
4189
+
4190
+ <body>
4191
+ <img
4192
+ src="{{countryLogo}}"
4193
+ alt="country_logo"
4194
+ style="max-height: 88px; max-width: 100%"
4195
+ />
4196
+ <h1>Your {{applicationName}} account invitation</h1>
4197
+ <p>Hello {{firstname}},</p>
4198
+ <p>Your account invitation has been resent. Please use the following credentials to access your account:</p>
4199
+ <p>
4200
+ Username: <strong>{{username}}</strong><br />
4201
+ Password: <strong>{{temporaryPassword}}</strong>
4202
+ </p>
4203
+ <p>
4204
+ Please click the link below to complete your account setup and start using
4205
+ {{applicationName}}.
4206
+ </p>
4207
+
4208
+ <div style="margin-top: 32px; margin-bottom: 32px">
4209
+ <a href="{{loginURL}}">Complete setup</a>
4210
+ </div>
4211
+ <br />
4212
+ <p>
4213
+ Best regards, <br />
4214
+ {{applicationName}} Team
4215
+ </p>
4216
+ </body>
4217
+ </html>
4218
+ `;
4219
+ var CSV_ROW = "resendInviteNotification,The SMS message that is sent when a user invitation is resent,Your OpenCRVS account invitation was resent. Please login with your username: {{username}} and temporary password: {{temporaryPassword}},Votre invitation de compte OpenCRVS a \xE9t\xE9 renvoy\xE9e. Veuillez vous connecter avec votre nom d'utilisateur : {{username}} et votre mot de passe temporaire : {{temporaryPassword}}.\n";
4220
+ function getObjectLiteralFromConst(declaration) {
4221
+ if (!declaration) {
4222
+ return;
4223
+ }
4224
+ const initializer = declaration.getInitializer();
4225
+ if (!initializer) {
4226
+ return;
4227
+ }
4228
+ if (import_ts_morph20.Node.isObjectLiteralExpression(initializer)) {
4229
+ return initializer;
4230
+ }
4231
+ if (import_ts_morph20.Node.isAsExpression(initializer)) {
4232
+ const expression = initializer.getExpression();
4233
+ if (import_ts_morph20.Node.isObjectLiteralExpression(expression)) {
4234
+ return expression;
4235
+ }
4236
+ }
4237
+ if (import_ts_morph20.Node.isSatisfiesExpression(initializer)) {
4238
+ const expression = initializer.getExpression();
4239
+ if (import_ts_morph20.Node.isObjectLiteralExpression(expression)) {
4240
+ return expression;
4241
+ }
4242
+ }
4243
+ }
4244
+ function patchEmailTemplatesIndex() {
4245
+ const full = import_path22.default.join(process.cwd(), EMAIL_TEMPLATES_INDEX);
4246
+ if (!(0, import_fs3.existsSync)(full)) {
4247
+ console.log(` '${EMAIL_TEMPLATES_INDEX}' missing \u2014 skipping.`);
4248
+ return false;
4249
+ }
4250
+ const project = new import_ts_morph20.Project(TS_MORPH_PROJECT_OPTIONS);
4251
+ const sourceFile = project.addSourceFileAtPath(full);
4252
+ let changed = false;
4253
+ const triggerVariableObject = getObjectLiteralFromConst(
4254
+ sourceFile.getVariableDeclaration("TriggerVariable")
4255
+ );
4256
+ if (triggerVariableObject) {
4257
+ const hasResendInviteVariable = triggerVariableObject.getProperties().some((prop) => prop.getText().includes("[TriggerEvent.RESEND_INVITE]"));
4258
+ if (!hasResendInviteVariable) {
4259
+ const properties = triggerVariableObject.getProperties();
4260
+ const lastProperty = properties[properties.length - 1];
4261
+ if (lastProperty) {
4262
+ sourceFile.insertText(
4263
+ lastProperty.getEnd(),
4264
+ `,
4265
+ [TriggerEvent.RESEND_INVITE]: z.object({
4266
+ firstname: z.string(),
4267
+ username: z.string(),
4268
+ temporaryPassword: z.string(),
4269
+ completeSetupUrl: z.string(),
4270
+ loginURL: z.string()
4271
+ })`
4272
+ );
4273
+ changed = true;
4274
+ }
4275
+ }
4276
+ }
4277
+ const templatesObject = getObjectLiteralFromConst(
4278
+ sourceFile.getVariableDeclaration("templates")
4279
+ );
4280
+ if (templatesObject) {
4281
+ const hasResendInviteTemplate = templatesObject.getProperties().some(
4282
+ (prop) => prop.getText().includes("readOtherTemplate<TriggerVariable['resend-invite']>")
4283
+ );
4284
+ if (!hasResendInviteTemplate) {
4285
+ const userCreatedProp = templatesObject.getProperties().find((prop) => prop.getText().includes("[TriggerEvent.USER_CREATED]"));
4286
+ const anchor = userCreatedProp ?? templatesObject.getProperties()[templatesObject.getProperties().length - 1];
4287
+ if (anchor) {
4288
+ sourceFile.insertText(
4289
+ anchor.getEnd(),
4290
+ `,
4291
+ [TriggerEvent.RESEND_INVITE]: {
4292
+ type: 'resend-invite',
4293
+ subject: 'Your OpenCRVS account invitation',
4294
+ template:
4295
+ readOtherTemplate<TriggerVariable['resend-invite']>('resend-invite')
4296
+ }`
4297
+ );
4298
+ changed = true;
4299
+ }
4300
+ }
4301
+ }
4302
+ if (changed) {
4303
+ sourceFile.saveSync();
4304
+ }
4305
+ return changed;
4306
+ }
4307
+ function patchNotificationHandler() {
4308
+ const full = import_path22.default.join(process.cwd(), NOTIFICATION_HANDLER);
4309
+ if (!(0, import_fs3.existsSync)(full)) {
4310
+ return false;
4311
+ }
4312
+ const project = new import_ts_morph20.Project(TS_MORPH_PROJECT_OPTIONS);
4313
+ const sourceFile = project.addSourceFileAtPath(full);
4314
+ const convertFn = sourceFile.getFunction("convertPayloadToVariable");
4315
+ if (!convertFn) {
4316
+ return false;
4317
+ }
4318
+ const switchStmt = convertFn.getFirstDescendantByKind(
4319
+ import_ts_morph20.SyntaxKind.SwitchStatement
4320
+ );
4321
+ if (!switchStmt) {
4322
+ return false;
4323
+ }
4324
+ const clauses = switchStmt.getCaseBlock().getClauses();
4325
+ const hasResendInvite = clauses.some(
4326
+ (clause) => import_ts_morph20.Node.isCaseClause(clause) && clause.getExpression().getText() === "TriggerEvent.RESEND_INVITE"
4327
+ );
4328
+ if (hasResendInvite) {
4329
+ return false;
4330
+ }
4331
+ const userCreatedClauseIndex = clauses.findIndex(
4332
+ (clause) => import_ts_morph20.Node.isCaseClause(clause) && clause.getExpression().getText() === "TriggerEvent.USER_CREATED"
4333
+ );
4334
+ if (userCreatedClauseIndex < 0) {
4335
+ return false;
4336
+ }
4337
+ const userCreatedClause = clauses[userCreatedClauseIndex];
4338
+ sourceFile.insertText(
4339
+ userCreatedClause.getEnd(),
4340
+ `
4341
+
4342
+ case TriggerEvent.RESEND_INVITE:
4343
+ return {
4344
+ firstname,
4345
+ username: payload.username,
4346
+ temporaryPassword: payload.temporaryPassword,
4347
+ completeSetupUrl: LOGIN_URL,
4348
+ loginURL: LOGIN_URL
4349
+ }`
4350
+ );
4351
+ sourceFile.saveSync();
4352
+ return true;
4353
+ }
4354
+ function getReturnedArrayLiteral(functionFilePath, fnName) {
4355
+ const project = new import_ts_morph20.Project(TS_MORPH_PROJECT_OPTIONS);
4356
+ const sourceFile = project.addSourceFileAtPath(functionFilePath);
4357
+ const fn = sourceFile.getFunction(fnName);
4358
+ const returnStmt = fn?.getFirstDescendantByKind(import_ts_morph20.SyntaxKind.ReturnStatement);
4359
+ const expr = returnStmt?.getExpression();
4360
+ if (!expr || !import_ts_morph20.Node.isArrayLiteralExpression(expr)) {
4361
+ return { sourceFile, arrayLiteral: void 0 };
4362
+ }
4363
+ return { sourceFile, arrayLiteral: expr };
4364
+ }
4365
+ function patchUserNotificationRoutes() {
4366
+ const full = import_path22.default.join(process.cwd(), USER_NOTIFICATION_ROUTES);
4367
+ if (!(0, import_fs3.existsSync)(full)) {
4368
+ return false;
4369
+ }
4370
+ const { sourceFile, arrayLiteral } = getReturnedArrayLiteral(
4371
+ full,
4372
+ "getUserNotificationRoutes"
4373
+ );
4374
+ if (!arrayLiteral) {
4375
+ return false;
4376
+ }
4377
+ const elements = arrayLiteral.getElements();
4378
+ const routeExists = elements.some(
4379
+ (element) => element.getText().includes(`path: '/triggers/user/resend-invite'`)
4380
+ );
4381
+ if (routeExists) {
4382
+ return false;
4383
+ }
4384
+ const anchor = elements.find(
4385
+ (element) => element.getText().includes(`path: '/triggers/user/user-created'`)
4386
+ ) ?? elements[elements.length - 1];
4387
+ if (!anchor) {
4388
+ return false;
4389
+ }
4390
+ sourceFile.insertText(
4391
+ anchor.getEnd(),
4392
+ `,
4393
+ {
4394
+ method: 'POST',
4395
+ path: '/triggers/user/resend-invite',
4396
+ handler: makeNotificationHandler('resend-invite'),
4397
+ options: {
4398
+ auth: false,
4399
+ tags: ['api'],
4400
+ description: 'Handles notification for resent user invite'
4401
+ }
4402
+ }`
4403
+ );
4404
+ sourceFile.saveSync();
4405
+ return true;
4406
+ }
4407
+ function patchSmsService() {
4408
+ const full = import_path22.default.join(process.cwd(), SMS_SERVICE);
4409
+ if (!(0, import_fs3.existsSync)(full)) {
4410
+ return false;
4411
+ }
4412
+ const project = new import_ts_morph20.Project(TS_MORPH_PROJECT_OPTIONS);
4413
+ const sourceFile = project.addSourceFileAtPath(full);
4414
+ let changed = false;
4415
+ const otherTemplatesObject = getObjectLiteralFromConst(
4416
+ sourceFile.getVariableDeclaration("otherTemplates")
4417
+ );
4418
+ if (otherTemplatesObject) {
4419
+ const hasResendTemplate = otherTemplatesObject.getProperties().some((prop) => prop.getText().includes("resendInviteNotification"));
4420
+ if (!hasResendTemplate) {
4421
+ const userCredentialsIndex = otherTemplatesObject.getProperties().findIndex((prop) => prop.getText().includes("userCredentialsNotification"));
4422
+ otherTemplatesObject.insertPropertyAssignment(
4423
+ userCredentialsIndex >= 0 ? userCredentialsIndex + 1 : otherTemplatesObject.getProperties().length,
4424
+ {
4425
+ name: "resendInviteNotification",
4426
+ initializer: `'resendInviteNotification'`
4427
+ }
4428
+ );
4429
+ changed = true;
4430
+ }
4431
+ }
4432
+ const triggerToSmsTemplateObject = getObjectLiteralFromConst(
4433
+ sourceFile.getVariableDeclaration("TriggerToSMSTemplate")
4434
+ );
4435
+ if (triggerToSmsTemplateObject) {
4436
+ const hasResendInviteMap = triggerToSmsTemplateObject.getProperties().some((prop) => prop.getText().includes(`['resend-invite']`));
4437
+ if (!hasResendInviteMap) {
4438
+ const userCreatedIndex = triggerToSmsTemplateObject.getProperties().findIndex((prop) => prop.getText().includes(`['user-created']`));
4439
+ triggerToSmsTemplateObject.insertPropertyAssignment(
4440
+ userCreatedIndex >= 0 ? userCreatedIndex + 1 : triggerToSmsTemplateObject.getProperties().length,
4441
+ {
4442
+ name: `['resend-invite']`,
4443
+ initializer: `'resendInviteNotification'`
4444
+ }
4445
+ );
4446
+ changed = true;
4447
+ }
4448
+ }
4449
+ if (changed) {
4450
+ sourceFile.saveSync();
4451
+ }
4452
+ return changed;
4453
+ }
4454
+ async function main23() {
4455
+ console.log("Adding RESEND_INVITE notification wiring\u2026");
4456
+ const touched = [];
4457
+ if (patchEmailTemplatesIndex()) touched.push(EMAIL_TEMPLATES_INDEX);
4458
+ if (patchNotificationHandler()) touched.push(NOTIFICATION_HANDLER);
4459
+ if (patchUserNotificationRoutes()) touched.push(USER_NOTIFICATION_ROUTES);
4460
+ if (patchSmsService()) touched.push(SMS_SERVICE);
4461
+ const csvPath = import_path22.default.join(process.cwd(), NOTIFICATION_CSV);
4462
+ if ((0, import_fs3.existsSync)(csvPath)) {
4463
+ let t = (0, import_fs3.readFileSync)(csvPath, "utf8");
4464
+ if (!/^resendInviteNotification,/m.test(t)) {
4465
+ if (!t.endsWith("\n")) t += "\n";
4466
+ (0, import_fs3.writeFileSync)(csvPath, t + CSV_ROW);
4467
+ touched.push(NOTIFICATION_CSV);
4468
+ }
4469
+ }
4470
+ const htmlPath = import_path22.default.join(process.cwd(), RESEND_INVITE_HTML);
4471
+ (0, import_fs3.mkdirSync)(import_path22.default.dirname(htmlPath), { recursive: true });
4472
+ if (!(0, import_fs3.existsSync)(htmlPath)) {
4473
+ (0, import_fs3.writeFileSync)(htmlPath, RESEND_INVITE_HTML_CONTENT);
4474
+ touched.push(RESEND_INVITE_HTML);
4475
+ }
4476
+ if (touched.length === 0) {
4477
+ console.log(" Nothing to update (already present or anchors not found).");
4478
+ return;
4479
+ }
4480
+ console.log(` Updated:
4481
+ ${touched.map((f) => ` - ${f}`).join("\n")}`);
4482
+ }
4483
+
3414
4484
  // src/migrations/v2.0/simplify-analytics-precalculations.ts
3415
- var import_ts_morph16 = require("ts-morph");
3416
- var import_fs2 = __toESM(require("fs"));
3417
- var import_path17 = __toESM(require("path"));
4485
+ var import_ts_morph21 = require("ts-morph");
4486
+ var import_fs4 = __toESM(require("fs"));
4487
+ var import_path23 = __toESM(require("path"));
3418
4488
  var ANALYTICS_FILE_REL = "src/analytics/analytics.ts";
3419
4489
  var FUNCTION_NAME = "precalculateAdditionalAnalytics";
3420
4490
  var IMPORT_NAMES_TO_REMOVE = /* @__PURE__ */ new Set(["Event", "precalculateBirthEvent"]);
@@ -3450,10 +4520,10 @@ function findPrecalculateFunction(sourceFile) {
3450
4520
  return void 0;
3451
4521
  }
3452
4522
  function getFunctionBodyBlock(fn) {
3453
- if (import_ts_morph16.Node.isFunctionDeclaration(fn)) return fn.getBody();
4523
+ if (import_ts_morph21.Node.isFunctionDeclaration(fn)) return fn.getBody();
3454
4524
  const initializer = fn.getInitializer();
3455
4525
  if (!initializer) return void 0;
3456
- if (!import_ts_morph16.Node.isArrowFunction(initializer) && !import_ts_morph16.Node.isFunctionExpression(initializer)) {
4526
+ if (!import_ts_morph21.Node.isArrowFunction(initializer) && !import_ts_morph21.Node.isFunctionExpression(initializer)) {
3457
4527
  return void 0;
3458
4528
  }
3459
4529
  return initializer.getBody();
@@ -3463,9 +4533,9 @@ function simplifyPrecalculateFunction(sourceFile) {
3463
4533
  const fn = findPrecalculateFunction(sourceFile);
3464
4534
  if (!fn) return log;
3465
4535
  const body = getFunctionBodyBlock(fn);
3466
- if (!body || !import_ts_morph16.Node.isBlock(body)) return log;
4536
+ if (!body || !import_ts_morph21.Node.isBlock(body)) return log;
3467
4537
  for (const statement of [...body.getStatements()]) {
3468
- const references = statement.getDescendantsOfKind(import_ts_morph16.SyntaxKind.Identifier).some((id) => IMPORT_NAMES_TO_REMOVE.has(id.getText()));
4538
+ const references = statement.getDescendantsOfKind(import_ts_morph21.SyntaxKind.Identifier).some((id) => IMPORT_NAMES_TO_REMOVE.has(id.getText()));
3469
4539
  if (references) {
3470
4540
  statement.remove();
3471
4541
  log.push(`removed statement in ${FUNCTION_NAME} referencing removed imports`);
@@ -3473,7 +4543,7 @@ function simplifyPrecalculateFunction(sourceFile) {
3473
4543
  }
3474
4544
  const remaining = body.getStatements();
3475
4545
  const last = remaining[remaining.length - 1];
3476
- const alreadyCorrect = last && import_ts_morph16.Node.isReturnStatement(last) && last.getExpression()?.getText() === RETURN_EXPRESSION_TEXT;
4546
+ const alreadyCorrect = last && import_ts_morph21.Node.isReturnStatement(last) && last.getExpression()?.getText() === RETURN_EXPRESSION_TEXT;
3477
4547
  if (!alreadyCorrect) {
3478
4548
  body.addStatements(`return ${RETURN_EXPRESSION_TEXT}`);
3479
4549
  log.push(
@@ -3482,17 +4552,17 @@ function simplifyPrecalculateFunction(sourceFile) {
3482
4552
  }
3483
4553
  return log;
3484
4554
  }
3485
- async function main18() {
3486
- const cwd2 = getCwd();
3487
- const absPath = import_path17.default.join(cwd2, ANALYTICS_FILE_REL);
3488
- if (!import_fs2.default.existsSync(absPath)) {
4555
+ async function main24() {
4556
+ const cwd = process.cwd();
4557
+ const absPath = import_path23.default.join(cwd, ANALYTICS_FILE_REL);
4558
+ if (!import_fs4.default.existsSync(absPath)) {
3489
4559
  console.log(
3490
4560
  `[${ANALYTICS_FILE_REL}] does not exist \u2014 nothing to clean up.`
3491
4561
  );
3492
4562
  return;
3493
4563
  }
3494
- const project = new import_ts_morph16.Project({
3495
- tsConfigFilePath: import_path17.default.resolve(cwd2, "tsconfig.json"),
4564
+ const project = new import_ts_morph21.Project({
4565
+ tsConfigFilePath: import_path23.default.resolve(cwd, "tsconfig.json"),
3496
4566
  skipAddingFilesFromTsConfig: false
3497
4567
  });
3498
4568
  const sourceFile = project.getSourceFile(absPath);
@@ -3519,26 +4589,158 @@ async function main18() {
3519
4589
  Saved ${ANALYTICS_FILE_REL}.`);
3520
4590
  }
3521
4591
 
3522
- // src/migrations/v2.0/index.ts
3523
- var cwd;
3524
- function getCwd() {
3525
- if (!cwd) {
3526
- throw new Error("Country config working directory not set.");
4592
+ // src/migrations/v2.0/merge-infrastructure-directory.ts
4593
+ var import_child_process2 = require("child_process");
4594
+ var import_fs5 = require("fs");
4595
+ var import_os = require("os");
4596
+ var import_path24 = require("path");
4597
+ var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
4598
+ var INFRASTRUCTURE_DIR = "infrastructure";
4599
+ var BASE_BRANCH = "release-v1.9";
4600
+ function gitShowBytes(ref, path23) {
4601
+ try {
4602
+ const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path23}`], {
4603
+ cwd: process.cwd(),
4604
+ stdio: ["ignore", "pipe", "pipe"]
4605
+ });
4606
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
4607
+ } catch {
4608
+ return null;
3527
4609
  }
3528
- return cwd;
3529
4610
  }
3530
- async function runUpgrade(ccwd) {
3531
- if (ccwd) {
3532
- console.log("Country config working directory:", ccwd);
3533
- cwd = ccwd;
4611
+ async function main25() {
4612
+ console.log(
4613
+ `Merging '${INFRASTRUCTURE_DIR}/' from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4614
+ `
4615
+ );
4616
+ assertIsGitRepo();
4617
+ tryGit(["remote", "remove", TEMP_REMOTE2]);
4618
+ try {
4619
+ console.log(` Adding temporary remote '${TEMP_REMOTE2}' \u2192 ${UPSTREAM_URL}`);
4620
+ runGit(["remote", "add", TEMP_REMOTE2, UPSTREAM_URL], { silent: true });
4621
+ console.log(
4622
+ ` Fetching ${TEMP_REMOTE2}/{${UPSTREAM_BRANCH},${BASE_BRANCH}} (depth 1)...`
4623
+ );
4624
+ runGit(["fetch", "--depth=1", TEMP_REMOTE2, UPSTREAM_BRANCH, BASE_BRANCH]);
4625
+ const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
4626
+ const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
4627
+ const upstreamFiles = runGit(
4628
+ [
4629
+ "ls-tree",
4630
+ "-r",
4631
+ "--name-only",
4632
+ "-z",
4633
+ ref,
4634
+ "--",
4635
+ `${INFRASTRUCTURE_DIR}/`
4636
+ ],
4637
+ { silent: true }
4638
+ ).split("\0").filter(Boolean);
4639
+ if (upstreamFiles.length === 0) {
4640
+ console.log(
4641
+ ` No files found under '${INFRASTRUCTURE_DIR}/' on ${ref}. Nothing to merge.`
4642
+ );
4643
+ return;
4644
+ }
4645
+ const tmpDir = (0, import_fs5.mkdtempSync)((0, import_path24.join)((0, import_os.tmpdir)(), "opencrvs-merge-infra-"));
4646
+ const baseTmp = (0, import_path24.join)(tmpDir, "base");
4647
+ const theirsTmp = (0, import_path24.join)(tmpDir, "theirs");
4648
+ let added = 0;
4649
+ let merged = 0;
4650
+ const conflictedFiles = [];
4651
+ for (const file of upstreamFiles) {
4652
+ const fullPath = (0, import_path24.join)(process.cwd(), file);
4653
+ if (!(0, import_fs5.existsSync)(fullPath)) {
4654
+ if (tryGit(["checkout", ref, "--", file])) {
4655
+ console.log(` + ${file} (new from upstream)`);
4656
+ added++;
4657
+ } else {
4658
+ console.warn(` [warn] failed to add new file ${file}`);
4659
+ }
4660
+ continue;
4661
+ }
4662
+ const theirContents = gitShowBytes(ref, file);
4663
+ if (theirContents === null) {
4664
+ console.warn(` [warn] could not read ${file} from ${ref} \u2014 skipping.`);
4665
+ continue;
4666
+ }
4667
+ (0, import_fs5.writeFileSync)(theirsTmp, theirContents);
4668
+ const baseContents = gitShowBytes(baseRef, file);
4669
+ (0, import_fs5.writeFileSync)(baseTmp, baseContents ?? new Uint8Array(0));
4670
+ try {
4671
+ (0, import_child_process2.execFileSync)("git", ["merge-file", fullPath, baseTmp, theirsTmp], {
4672
+ cwd: process.cwd(),
4673
+ stdio: ["ignore", "pipe", "pipe"]
4674
+ });
4675
+ runGit(["add", "--", file], { silent: true });
4676
+ merged++;
4677
+ } catch (err) {
4678
+ const status = err.status;
4679
+ if (typeof status === "number" && status > 0) {
4680
+ conflictedFiles.push(file);
4681
+ console.warn(
4682
+ ` ! ${file} (${status} conflict${status > 1 ? "s" : ""})`
4683
+ );
4684
+ } else {
4685
+ conflictedFiles.push(file);
4686
+ console.warn(
4687
+ ` [warn] merge failed for ${file} (likely binary or unreadable) \u2014 left untouched.`
4688
+ );
4689
+ }
4690
+ }
4691
+ }
4692
+ console.log(
4693
+ `
4694
+ Done. ${added} added, ${merged} merged cleanly, ${conflictedFiles.length} with conflicts.`
4695
+ );
4696
+ if (conflictedFiles.length > 0) {
4697
+ console.warn(
4698
+ `
4699
+ NOTE: ${conflictedFiles.length} file(s) under '${INFRASTRUCTURE_DIR}/' have merge conflicts that you must resolve manually.
4700
+ Look for the standard conflict markers (<<<<<<<, =======, >>>>>>>) in:
4701
+ ` + conflictedFiles.map((f) => ` - ${f}`).join("\n") + `
4702
+ After resolving, stage the fixes with: git add ${INFRASTRUCTURE_DIR}/`
4703
+ );
4704
+ }
4705
+ } finally {
4706
+ if (tryGit(["remote", "remove", TEMP_REMOTE2])) {
4707
+ console.log(` Removed temporary remote '${TEMP_REMOTE2}'.`);
4708
+ }
3534
4709
  }
3535
- if (!ccwd) {
4710
+ }
4711
+
4712
+ // src/migrations/v2.0/delete-infrastructure-directory.ts
4713
+ var import_fs6 = require("fs");
4714
+ var import_path25 = require("path");
4715
+ var INFRASTRUCTURE_DIR2 = "infrastructure";
4716
+ async function main26() {
4717
+ const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
4718
+ if (!(0, import_fs6.existsSync)(fullPath)) {
3536
4719
  console.log(
3537
- "Will use current working directory as country config working directory."
4720
+ `'${INFRASTRUCTURE_DIR2}/' not found in ${process.cwd()} \u2014 nothing to delete.`
3538
4721
  );
3539
- cwd = process.cwd();
4722
+ return;
3540
4723
  }
3541
- await main13();
4724
+ console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4725
+ `);
4726
+ assertIsGitRepo();
4727
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4728
+ silent: true
4729
+ });
4730
+ console.log(` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`);
4731
+ if ((0, import_fs6.existsSync)(fullPath)) {
4732
+ (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4733
+ console.log(` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`);
4734
+ }
4735
+ console.log(
4736
+ `
4737
+ Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
4738
+ );
4739
+ }
4740
+
4741
+ // src/migrations/v2.0/index.ts
4742
+ async function runUpgrade(dockerSwarm) {
4743
+ await main18();
3542
4744
  await main();
3543
4745
  await main2();
3544
4746
  await main3();
@@ -3551,11 +4753,22 @@ async function runUpgrade(ccwd) {
3551
4753
  await main10();
3552
4754
  await main11();
3553
4755
  await main12();
4756
+ await main13();
3554
4757
  await main14();
3555
4758
  await main15();
3556
4759
  await main16();
4760
+ await main19();
4761
+ await main20();
4762
+ await main21();
4763
+ await main22();
4764
+ await main23();
4765
+ await main24();
3557
4766
  await main17();
3558
- await main18();
4767
+ if (dockerSwarm) {
4768
+ await main25();
4769
+ } else {
4770
+ await main26();
4771
+ }
3559
4772
  }
3560
4773
 
3561
4774
  // src/cli.ts
@@ -3570,7 +4783,20 @@ Commands:
3570
4783
 
3571
4784
  Run 'opencrvs <command> --help' for more information on a command.
3572
4785
  `.trim();
3573
- function main19() {
4786
+ var UPGRADE_USAGE = `
4787
+ Usage: opencrvs upgrade [options]
4788
+
4789
+ Upgrade the country config in the current working directory to the next
4790
+ major version of OpenCRVS.
4791
+
4792
+ Options:
4793
+ --docker-swarm Keep and merge the 'infrastructure/' directory with
4794
+ upstream changes. Use this if your country deploys
4795
+ OpenCRVS via Docker Swarm. When omitted, the
4796
+ 'infrastructure/' directory is deleted (default).
4797
+ -h, --help Show this message.
4798
+ `.trim();
4799
+ function main27() {
3574
4800
  const command = args[0];
3575
4801
  if (!command || command === "--help" || command === "-h") {
3576
4802
  console.log(USAGE);
@@ -3615,10 +4841,25 @@ Subcommands:
3615
4841
  }
3616
4842
  }
3617
4843
  async function handleUpgrade() {
4844
+ const upgradeArgs = args.slice(1);
4845
+ if (upgradeArgs.includes("--help") || upgradeArgs.includes("-h")) {
4846
+ console.log(UPGRADE_USAGE);
4847
+ process.exit(0);
4848
+ }
4849
+ const KNOWN_FLAGS = /* @__PURE__ */ new Set(["--docker-swarm"]);
4850
+ const unknownFlags = upgradeArgs.filter(
4851
+ (arg) => arg.startsWith("-") && !KNOWN_FLAGS.has(arg)
4852
+ );
4853
+ if (unknownFlags.length > 0) {
4854
+ console.error(`Unknown option(s): ${unknownFlags.join(", ")}
4855
+ `);
4856
+ console.log(UPGRADE_USAGE);
4857
+ process.exit(1);
4858
+ }
4859
+ const dockerSwarm = upgradeArgs.includes("--docker-swarm");
3618
4860
  console.log("Initiating upgrade...");
3619
4861
  try {
3620
- const pathToCountryconfig = args[1];
3621
- await runUpgrade(pathToCountryconfig);
4862
+ await runUpgrade(dockerSwarm);
3622
4863
  console.log("Upgrade completed successfully!");
3623
4864
  } catch (error) {
3624
4865
  console.error("Upgrade failed:", error);
@@ -3630,4 +4871,4 @@ function handleCheckTranslations() {
3630
4871
  console.warn("This command is not implemented yet!");
3631
4872
  process.exit(1);
3632
4873
  }
3633
- main19();
4874
+ main27();