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

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 (59) hide show
  1. package/create-countryconfig/index.js +103 -0
  2. package/create-countryconfig/package.json +21 -0
  3. package/dist/application-config/index.js +306 -130
  4. package/dist/cli.js +548 -178
  5. package/dist/commons/api/router.d.ts +3379 -239
  6. package/dist/commons/application-config/index.d.ts +17 -4
  7. package/dist/commons/conditionals/conditionals.d.ts +360 -10
  8. package/dist/commons/conditionals/validate.d.ts +52 -2
  9. package/dist/commons/events/ActionDocument.d.ts +6 -0
  10. package/dist/commons/events/ActionInput.d.ts +114 -0
  11. package/dist/commons/events/AdvancedSearchConfig.d.ts +342 -0
  12. package/dist/commons/events/Draft.d.ts +4 -2
  13. package/dist/commons/events/EventDocument.d.ts +3 -0
  14. package/dist/commons/events/EventIndex.d.ts +0 -3
  15. package/dist/commons/events/EventMetadata.d.ts +0 -7
  16. package/dist/commons/events/FieldConfig.d.ts +387 -50
  17. package/dist/commons/events/FieldValue.d.ts +6 -0
  18. package/dist/commons/events/WorkqueueConfig.d.ts +150 -150
  19. package/dist/commons/events/field.d.ts +53 -1
  20. package/dist/commons/events/index.d.ts +2 -0
  21. package/dist/commons/events/locations.d.ts +38 -1
  22. package/dist/commons/events/mocks.test.utils.d.ts +19 -0
  23. package/dist/commons/events/scopes.d.ts +24 -1
  24. package/dist/commons/events/state/flags.d.ts +16 -0
  25. package/dist/commons/events/state/index.d.ts +0 -4
  26. package/dist/commons/events/state/utils.d.ts +12 -289
  27. package/dist/commons/events/utils.d.ts +13 -2
  28. package/dist/conditionals/index.js +92 -9
  29. package/dist/events/index.js +1085 -262
  30. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.d.ts.map +1 -1
  31. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.js +60 -35
  32. package/dist/migrations/v2.0/checkout-upstream-files.d.ts +1 -1
  33. package/dist/migrations/v2.0/checkout-upstream-files.d.ts.map +1 -1
  34. package/dist/migrations/v2.0/checkout-upstream-files.js +1 -2
  35. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts +9 -1
  36. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts.map +1 -1
  37. package/dist/migrations/v2.0/delete-infrastructure-directory.js +66 -20
  38. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts +3 -0
  39. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts.map +1 -0
  40. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.js +110 -0
  41. package/dist/migrations/v2.0/index.d.ts +5 -3
  42. package/dist/migrations/v2.0/index.d.ts.map +1 -1
  43. package/dist/migrations/v2.0/index.js +546 -176
  44. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.d.ts.map +1 -1
  45. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.js +90 -1
  46. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts +11 -1
  47. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts.map +1 -1
  48. package/dist/migrations/v2.0/merge-infrastructure-directory.js +17 -16
  49. package/dist/migrations/v2.0/migrate-scopes.d.ts.map +1 -1
  50. package/dist/migrations/v2.0/migrate-scopes.js +54 -30
  51. package/dist/migrations/v2.0/migrate-workqueue-configs.d.ts.map +1 -1
  52. package/dist/migrations/v2.0/migrate-workqueue-configs.js +140 -59
  53. package/dist/notification/index.js +842 -161
  54. package/dist/scopes/index.d.ts +91 -16
  55. package/dist/scopes/index.js +55 -12
  56. package/opencrvs-toolkit-2.0.0-rc.ff777d6.tgz +0 -0
  57. package/package.json +6 -2
  58. package/tsconfig.tsbuildinfo +1 -1
  59. package/opencrvs-toolkit-2.0.0-rc.fef9d21.tgz +0 -0
@@ -190,6 +190,8 @@ function ensureImports(sourceFile) {
190
190
  var DEFINE_CONFIG_NAME2 = "defineConfig";
191
191
  var ACTIONS_PROPERTY_NAME2 = "actions";
192
192
  var TYPE_PROPERTY_NAME2 = "type";
193
+ var ID_PROPERTY_NAME = "id";
194
+ var FLAGS_PROPERTY_NAME = "flags";
193
195
  var CUSTOM_ACTION_TYPE_PROPERTY_NAME = "customActionType";
194
196
  var AUDIT_HISTORY_LABEL_PROPERTY_NAME = "auditHistoryLabel";
195
197
  var DEDUPLICATION_PROPERTY_NAME = "deduplication";
@@ -199,9 +201,12 @@ var FORM_PROPERTY_NAME = "form";
199
201
  var ACTION_TYPE_ENUM_NAME = "ActionType";
200
202
  var VALIDATE_MEMBER_NAME = "VALIDATE";
201
203
  var CUSTOM_MEMBER_NAME = "CUSTOM";
204
+ var REGISTER_MEMBER_NAME2 = "REGISTER";
202
205
  var VALIDATE_STRING_LITERAL = "VALIDATE";
203
206
  var CUSTOM_STRING_LITERAL = "CUSTOM";
207
+ var REGISTER_STRING_LITERAL2 = "REGISTER";
204
208
  var VALIDATE_DECLARATION_VALUE = "VALIDATE_DECLARATION";
209
+ var VALIDATED_FLAG_VALUE = "validated";
205
210
  function isValidateType(typeInitializer) {
206
211
  if (typeInitializer.getKind() === import_ts_morph2.SyntaxKind.PropertyAccessExpression) {
207
212
  const propAccess = typeInitializer.asKindOrThrow(
@@ -325,6 +330,50 @@ function transformValidateActionToCustom(obj, eventType) {
325
330
  }
326
331
  return true;
327
332
  }
333
+ function isRegisterAction(action) {
334
+ const typeProperty = action.getProperty(TYPE_PROPERTY_NAME2);
335
+ if (!typeProperty || !import_ts_morph2.Node.isPropertyAssignment(typeProperty)) return false;
336
+ const typeInit = typeProperty.getInitializer();
337
+ if (!typeInit) return false;
338
+ if (import_ts_morph2.Node.isPropertyAccessExpression(typeInit)) {
339
+ const enumExpr = typeInit.getExpression();
340
+ return import_ts_morph2.Node.isIdentifier(enumExpr) && enumExpr.getText() === ACTION_TYPE_ENUM_NAME && typeInit.getName() === REGISTER_MEMBER_NAME2;
341
+ }
342
+ if (import_ts_morph2.Node.isStringLiteral(typeInit)) {
343
+ return typeInit.getLiteralValue() === REGISTER_STRING_LITERAL2;
344
+ }
345
+ return false;
346
+ }
347
+ function arrayHasElementWithId(array3, id) {
348
+ for (const el of array3.getElements()) {
349
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(el)) continue;
350
+ const idProp = el.getProperty(ID_PROPERTY_NAME);
351
+ if (!idProp || !import_ts_morph2.Node.isPropertyAssignment(idProp)) continue;
352
+ const idInit = idProp.getInitializer();
353
+ if (idInit && import_ts_morph2.Node.isStringLiteral(idInit) && idInit.getLiteralValue() === id) {
354
+ return true;
355
+ }
356
+ }
357
+ return false;
358
+ }
359
+ function ensureRegisterRemovesValidatedFlag(action) {
360
+ const entry = `{ id: '${VALIDATED_FLAG_VALUE}', operation: 'remove' }`;
361
+ const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME);
362
+ if (flagsProp && import_ts_morph2.Node.isPropertyAssignment(flagsProp)) {
363
+ const flagsInit = flagsProp.getInitializer();
364
+ if (flagsInit && import_ts_morph2.Node.isArrayLiteralExpression(flagsInit)) {
365
+ if (arrayHasElementWithId(flagsInit, VALIDATED_FLAG_VALUE)) return false;
366
+ flagsInit.addElement(entry);
367
+ return true;
368
+ }
369
+ return false;
370
+ }
371
+ action.addPropertyAssignment({
372
+ name: FLAGS_PROPERTY_NAME,
373
+ initializer: `[${entry}]`
374
+ });
375
+ return true;
376
+ }
328
377
  function processFile2(filePath, project) {
329
378
  const sourceFile = project.getSourceFile(filePath);
330
379
  if (!sourceFile) return 0;
@@ -390,6 +439,18 @@ function processFile2(filePath, project) {
390
439
  ` [${import_path2.default.relative(process.cwd(), filePath)}] Added top-level 'flags' array to defineConfig`
391
440
  );
392
441
  }
442
+ if (configHadValidateAction) {
443
+ for (const element of actionsInitializer.getElements()) {
444
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(element)) continue;
445
+ if (!isRegisterAction(element)) continue;
446
+ if (ensureRegisterRemovesValidatedFlag(element)) {
447
+ transformedCount++;
448
+ console.log(
449
+ ` [${import_path2.default.relative(process.cwd(), filePath)}] Added '${VALIDATED_FLAG_VALUE}' remove-flag to REGISTER action`
450
+ );
451
+ }
452
+ }
453
+ }
393
454
  if (fileNeedsImports) {
394
455
  ensureImports(sourceFile);
395
456
  }
@@ -436,7 +497,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
436
497
  }
437
498
  console.log(
438
499
  `
439
- Done. Transformed ${totalTransformed} VALIDATE action(s) to CUSTOM with customActionType: '${VALIDATE_DECLARATION_VALUE}'.`
500
+ Done. Applied ${totalTransformed} change(s): VALIDATE \u2192 CUSTOM (${VALIDATE_DECLARATION_VALUE}) and '${VALIDATED_FLAG_VALUE}' remove-flag on matching REGISTER actions.`
440
501
  );
441
502
  }
442
503
 
@@ -679,14 +740,14 @@ var QUERY_PROPERTY_NAME = "query";
679
740
  var TYPE_PROPERTY_NAME5 = "type";
680
741
  var CLAUSES_PROPERTY_NAME = "clauses";
681
742
  var STATUS_PROPERTY_NAME = "status";
682
- var FLAGS_PROPERTY_NAME = "flags";
743
+ var FLAGS_PROPERTY_NAME2 = "flags";
683
744
  var TERMS_PROPERTY_NAME = "terms";
684
745
  var TERM_PROPERTY_NAME = "term";
685
746
  var ANY_OF_PROPERTY_NAME = "anyOf";
686
747
  var VALIDATED_STATUS_VALUE = "VALIDATED";
687
- var VALIDATED_FLAG_VALUE = "validated";
748
+ var VALIDATED_FLAG_VALUE2 = "validated";
688
749
  function addValidatedFlag(clause) {
689
- const flagsProperty = clause.getProperty(FLAGS_PROPERTY_NAME);
750
+ const flagsProperty = clause.getProperty(FLAGS_PROPERTY_NAME2);
690
751
  if (flagsProperty && import_ts_morph5.Node.isPropertyAssignment(flagsProperty)) {
691
752
  const flagsInit = flagsProperty.getInitializer();
692
753
  if (flagsInit && import_ts_morph5.Node.isObjectLiteralExpression(flagsInit)) {
@@ -694,19 +755,19 @@ function addValidatedFlag(clause) {
694
755
  if (anyOfProperty && import_ts_morph5.Node.isPropertyAssignment(anyOfProperty)) {
695
756
  const anyOfInit = anyOfProperty.getInitializer();
696
757
  if (anyOfInit && import_ts_morph5.Node.isArrayLiteralExpression(anyOfInit)) {
697
- anyOfInit.addElement(`'${VALIDATED_FLAG_VALUE}'`);
758
+ anyOfInit.addElement(`'${VALIDATED_FLAG_VALUE2}'`);
698
759
  }
699
760
  } else {
700
761
  flagsInit.addPropertyAssignment({
701
762
  name: ANY_OF_PROPERTY_NAME,
702
- initializer: `['${VALIDATED_FLAG_VALUE}']`
763
+ initializer: `['${VALIDATED_FLAG_VALUE2}']`
703
764
  });
704
765
  }
705
766
  }
706
767
  } else {
707
768
  clause.addPropertyAssignment({
708
- name: FLAGS_PROPERTY_NAME,
709
- initializer: `{ ${ANY_OF_PROPERTY_NAME}: ['${VALIDATED_FLAG_VALUE}'] }`
769
+ name: FLAGS_PROPERTY_NAME2,
770
+ initializer: `{ ${ANY_OF_PROPERTY_NAME}: ['${VALIDATED_FLAG_VALUE2}'] }`
710
771
  });
711
772
  }
712
773
  }
@@ -872,23 +933,26 @@ var DEFINE_CONFIG_NAME4 = "defineConfig";
872
933
  var DEFINE_WORKQUEUES_NAME2 = "defineWorkqueues";
873
934
  var ACTIONS_PROPERTY_NAME4 = "actions";
874
935
  var ACTION_PROPERTY_NAME = "action";
875
- var FLAGS_PROPERTY_NAME2 = "flags";
936
+ var FLAGS_PROPERTY_NAME3 = "flags";
876
937
  var QUERY_PROPERTY_NAME2 = "query";
877
938
  var TYPE_PROPERTY_NAME6 = "type";
939
+ var ID_PROPERTY_NAME2 = "id";
878
940
  var CLAUSES_PROPERTY_NAME2 = "clauses";
879
941
  var ANY_OF_PROPERTY_NAME2 = "anyOf";
880
942
  var FLAG_ID = "pending-first-certificate-issuance";
881
943
  var REGISTER_TYPE = "REGISTER";
882
944
  var PRINT_CERTIFICATE_TYPE = "PRINT_CERTIFICATE";
883
- var FLAG_DEFINITION_INITIALIZER = `{
945
+ function flagDefinitionInitializer(eventType) {
946
+ return `{
884
947
  id: '${FLAG_ID}',
885
948
  label: {
886
- id: 'event.birth.flag.${FLAG_ID}',
949
+ id: 'event.${eventType}.flag.${FLAG_ID}',
887
950
  defaultMessage: 'Pending first certificate issuance',
888
951
  description: 'Flag label for first certificate issuance'
889
952
  },
890
953
  requiresAction: true
891
954
  }`;
955
+ }
892
956
  function isActionOfType(obj, typeName) {
893
957
  const typeProperty = obj.getProperty(TYPE_PROPERTY_NAME6);
894
958
  if (!typeProperty || !import_ts_morph6.Node.isPropertyAssignment(typeProperty)) return false;
@@ -902,18 +966,20 @@ function isActionOfType(obj, typeName) {
902
966
  }
903
967
  return false;
904
968
  }
905
- function isBirthConfig(configArg) {
906
- const typeProperty = configArg.getProperty(TYPE_PROPERTY_NAME6);
907
- if (!typeProperty || !import_ts_morph6.Node.isPropertyAssignment(typeProperty)) return false;
908
- const initializer = typeProperty.getInitializer();
909
- if (!initializer) return false;
910
- if (import_ts_morph6.Node.isStringLiteral(initializer)) {
911
- return initializer.getLiteralValue().toLowerCase() === "birth";
912
- }
913
- if (import_ts_morph6.Node.isPropertyAccessExpression(initializer)) {
914
- return initializer.getName().toLowerCase() === "birth";
969
+ function getEventType(configArg) {
970
+ for (const propName of [TYPE_PROPERTY_NAME6, ID_PROPERTY_NAME2]) {
971
+ const prop = configArg.getProperty(propName);
972
+ if (!prop || !import_ts_morph6.Node.isPropertyAssignment(prop)) continue;
973
+ const initializer = prop.getInitializer();
974
+ if (!initializer) continue;
975
+ if (import_ts_morph6.Node.isStringLiteral(initializer)) {
976
+ return initializer.getLiteralValue().toLowerCase();
977
+ }
978
+ if (import_ts_morph6.Node.isPropertyAccessExpression(initializer)) {
979
+ return initializer.getName().toLowerCase();
980
+ }
915
981
  }
916
- return false;
982
+ return null;
917
983
  }
918
984
  function hasElementWithId(array3, id) {
919
985
  for (const el of array3.getElements()) {
@@ -927,27 +993,28 @@ function hasElementWithId(array3, id) {
927
993
  }
928
994
  return false;
929
995
  }
930
- function ensureTopLevelFlagDefinition(configArg) {
931
- const flagsProp = configArg.getProperty(FLAGS_PROPERTY_NAME2);
996
+ function ensureTopLevelFlagDefinition(configArg, eventType) {
997
+ const initializer = flagDefinitionInitializer(eventType);
998
+ const flagsProp = configArg.getProperty(FLAGS_PROPERTY_NAME3);
932
999
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
933
1000
  const flagsInit = flagsProp.getInitializer();
934
1001
  if (flagsInit && import_ts_morph6.Node.isArrayLiteralExpression(flagsInit)) {
935
1002
  if (hasElementWithId(flagsInit, FLAG_ID)) return false;
936
- flagsInit.addElement(FLAG_DEFINITION_INITIALIZER);
1003
+ flagsInit.addElement(initializer);
937
1004
  return true;
938
1005
  }
939
1006
  return false;
940
1007
  }
941
1008
  configArg.addPropertyAssignment({
942
- name: FLAGS_PROPERTY_NAME2,
1009
+ name: FLAGS_PROPERTY_NAME3,
943
1010
  initializer: `[
944
- ${FLAG_DEFINITION_INITIALIZER}
1011
+ ${initializer}
945
1012
  ]`
946
1013
  });
947
1014
  return true;
948
1015
  }
949
1016
  function ensureActionOperationFlag(action, operation) {
950
- const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME2);
1017
+ const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME3);
951
1018
  const entry = `{ id: '${FLAG_ID}', operation: '${operation}' }`;
952
1019
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
953
1020
  const flagsInit = flagsProp.getInitializer();
@@ -959,16 +1026,18 @@ function ensureActionOperationFlag(action, operation) {
959
1026
  return false;
960
1027
  }
961
1028
  action.addPropertyAssignment({
962
- name: FLAGS_PROPERTY_NAME2,
1029
+ name: FLAGS_PROPERTY_NAME3,
963
1030
  initializer: `[${entry}]`
964
1031
  });
965
1032
  return true;
966
1033
  }
967
- function processBirthConfig(configArg, relPath) {
1034
+ function processConfig(configArg, eventType, relPath) {
968
1035
  let changes = 0;
969
- if (ensureTopLevelFlagDefinition(configArg)) {
1036
+ if (ensureTopLevelFlagDefinition(configArg, eventType)) {
970
1037
  changes++;
971
- console.log(` [${relPath}] Added '${FLAG_ID}' to top-level flags`);
1038
+ console.log(
1039
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' to top-level flags`
1040
+ );
972
1041
  }
973
1042
  const actionsProp = configArg.getProperty(ACTIONS_PROPERTY_NAME4);
974
1043
  if (!actionsProp || !import_ts_morph6.Node.isPropertyAssignment(actionsProp)) return changes;
@@ -982,7 +1051,7 @@ function processBirthConfig(configArg, relPath) {
982
1051
  if (ensureActionOperationFlag(element, "add")) {
983
1052
  changes++;
984
1053
  console.log(
985
- ` [${relPath}] Added '${FLAG_ID}' add-flag to REGISTER action`
1054
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' add-flag to REGISTER action`
986
1055
  );
987
1056
  }
988
1057
  }
@@ -990,7 +1059,7 @@ function processBirthConfig(configArg, relPath) {
990
1059
  if (ensureActionOperationFlag(element, "remove")) {
991
1060
  changes++;
992
1061
  console.log(
993
- ` [${relPath}] Added '${FLAG_ID}' remove-flag to PRINT_CERTIFICATE action`
1062
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' remove-flag to PRINT_CERTIFICATE action`
994
1063
  );
995
1064
  }
996
1065
  }
@@ -1019,7 +1088,7 @@ function hasPrintCertificateAction(workqueue) {
1019
1088
  return false;
1020
1089
  }
1021
1090
  function addFlagToQueryClause(clause) {
1022
- const flagsProp = clause.getProperty(FLAGS_PROPERTY_NAME2);
1091
+ const flagsProp = clause.getProperty(FLAGS_PROPERTY_NAME3);
1023
1092
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
1024
1093
  const flagsInit = flagsProp.getInitializer();
1025
1094
  if (flagsInit && import_ts_morph6.Node.isObjectLiteralExpression(flagsInit)) {
@@ -1044,7 +1113,7 @@ function addFlagToQueryClause(clause) {
1044
1113
  }
1045
1114
  } else {
1046
1115
  clause.addPropertyAssignment({
1047
- name: FLAGS_PROPERTY_NAME2,
1116
+ name: FLAGS_PROPERTY_NAME3,
1048
1117
  initializer: `{ ${ANY_OF_PROPERTY_NAME2}: ['${FLAG_ID}'] }`
1049
1118
  });
1050
1119
  return true;
@@ -1088,8 +1157,9 @@ function processFile6(filePath, project) {
1088
1157
  if (args.length === 0) continue;
1089
1158
  const configArg = args[0];
1090
1159
  if (!import_ts_morph6.Node.isObjectLiteralExpression(configArg)) continue;
1091
- if (!isBirthConfig(configArg)) continue;
1092
- totalChanges += processBirthConfig(configArg, relPath);
1160
+ const eventType = getEventType(configArg);
1161
+ if (!eventType) continue;
1162
+ totalChanges += processConfig(configArg, eventType, relPath);
1093
1163
  }
1094
1164
  if (calleeName === DEFINE_WORKQUEUES_NAME2) {
1095
1165
  const args = call.getArguments();
@@ -1118,7 +1188,7 @@ function processFile6(filePath, project) {
1118
1188
  async function main6() {
1119
1189
  const srcDir = import_path6.default.join(process.cwd(), "src");
1120
1190
  console.log(
1121
- `Scanning for birth defineConfig and PRINT_CERTIFICATE workqueues in: ${srcDir}
1191
+ `Scanning for defineConfig and PRINT_CERTIFICATE workqueues in: ${srcDir}
1122
1192
  `
1123
1193
  );
1124
1194
  const project = new import_ts_morph6.Project({
@@ -1143,7 +1213,7 @@ async function main6() {
1143
1213
  }
1144
1214
  if (modifiedFiles.length === 0) {
1145
1215
  console.log(
1146
- "No birth configs or PRINT_CERTIFICATE workqueues found. Nothing to do."
1216
+ "No event configs or PRINT_CERTIFICATE workqueues found. Nothing to do."
1147
1217
  );
1148
1218
  return;
1149
1219
  }
@@ -1825,9 +1895,9 @@ function updateRouteHandlers(sourceFiles, group, srcDir, relCwd) {
1825
1895
  }
1826
1896
  return replacedCount;
1827
1897
  }
1828
- function resolveConfigChain(configObj, path23) {
1898
+ function resolveConfigChain(configObj, path24) {
1829
1899
  let current = configObj;
1830
- for (const key of path23) {
1900
+ for (const key of path24) {
1831
1901
  if (!import_ts_morph10.Node.isObjectLiteralExpression(current)) return null;
1832
1902
  const prop = current.getProperty(key);
1833
1903
  if (!prop || !import_ts_morph10.Node.isPropertyAssignment(prop)) return null;
@@ -2062,6 +2132,10 @@ Done. Converted ${convertedCount} config file(s), replaced ${totalReplaced} rout
2062
2132
  var import_ts_morph11 = require("ts-morph");
2063
2133
  var import_path11 = __toESM(require("path"));
2064
2134
 
2135
+ // ../commons/src/scopes.deprecated.do-not-use.ts
2136
+ var z4 = __toESM(require("zod/v4"));
2137
+ var import_lodash = require("lodash");
2138
+
2065
2139
  // ../commons/src/scopes.ts
2066
2140
  var z3 = __toESM(require("zod/v4"));
2067
2141
  var qs = __toESM(require("qs"));
@@ -2122,6 +2196,10 @@ var scopeByEvent = z3.preprocess(
2122
2196
  (val) => val === void 0 ? void 0 : [val].flat(),
2123
2197
  z3.array(z3.string()).optional()
2124
2198
  ).describe("Event type, e.g. birth, death");
2199
+ var userRole = z3.preprocess(
2200
+ (val) => val === void 0 ? void 0 : [val].flat(),
2201
+ z3.array(z3.string()).optional()
2202
+ ).describe("User role, e.g. admin, field agent");
2125
2203
  var scopeOptionsPlaceEvent = z3.object({
2126
2204
  event: scopeByEvent,
2127
2205
  placeOfEvent: JurisdictionFilter.optional()
@@ -2145,6 +2223,9 @@ var CustomActionScopeOptions = AllRecordScopeOptions.extend({
2145
2223
  var AccessLevelOptions = z3.object({
2146
2224
  accessLevel: JurisdictionFilter.optional()
2147
2225
  });
2226
+ var AllUserScopeOptions = AccessLevelOptions.extend({
2227
+ role: userRole.optional()
2228
+ });
2148
2229
  var WorkqueueOrDashboardOptions = z3.object({
2149
2230
  ids: z3.preprocess(
2150
2231
  (val) => val === void 0 ? void 0 : [val].flat(),
@@ -2154,6 +2235,7 @@ var WorkqueueOrDashboardOptions = z3.object({
2154
2235
  var AllScopeOptions = z3.object({
2155
2236
  ...AllRecordScopeOptions.shape,
2156
2237
  ...AccessLevelOptions.shape,
2238
+ ...AllUserScopeOptions.shape,
2157
2239
  ...WorkqueueOrDashboardOptions.shape
2158
2240
  });
2159
2241
  var ScopeOptionKey = AllScopeOptions.keyof();
@@ -2226,6 +2308,30 @@ var RecordScopeV2 = z3.discriminatedUnion("type", [
2226
2308
  ]).describe(
2227
2309
  "Scopes used to check user's permission to perform actions on a record."
2228
2310
  );
2311
+ var SystemScopeType = z3.enum([
2312
+ "organisation.read-locations",
2313
+ "user.read",
2314
+ "user.create",
2315
+ "user.edit",
2316
+ "user.search"
2317
+ ]);
2318
+ var UserScopeType = SystemScopeType.extract([
2319
+ "user.read",
2320
+ "user.create",
2321
+ "user.edit",
2322
+ "user.search"
2323
+ ]);
2324
+ var ScopesWithRoleOption = UserScopeType.extract(["user.edit", "user.create"]);
2325
+ var UserScopeV2 = z3.discriminatedUnion("type", [
2326
+ z3.object({
2327
+ type: ScopesWithRoleOption,
2328
+ options: AllUserScopeOptions.optional()
2329
+ }),
2330
+ z3.object({
2331
+ type: UserScopeType.extract(["user.read", "user.search"]),
2332
+ options: AccessLevelOptions.optional()
2333
+ })
2334
+ ]);
2229
2335
  var ResolvedRecordScopeV2 = z3.discriminatedUnion("type", [
2230
2336
  z3.object({
2231
2337
  type: ScopesWithPlaceEventOptions,
@@ -2246,16 +2352,14 @@ var ResolvedRecordScopeV2 = z3.discriminatedUnion("type", [
2246
2352
  }).optional()
2247
2353
  })
2248
2354
  ]).describe("Resolved scope with location/user IDs instead of filters.");
2249
- var SystemScopeType = z3.enum([
2250
- "organisation.read-locations",
2251
- "user.read",
2252
- "user.create",
2253
- "user.edit"
2254
- ]);
2255
2355
  var Scope2 = z3.discriminatedUnion("type", [
2256
2356
  z3.object({ type: PlainScopeType }),
2257
2357
  ...RecordScopeV2.options,
2258
- z3.object({ type: SystemScopeType, options: AccessLevelOptions.optional() }),
2358
+ ...UserScopeV2.options,
2359
+ z3.object({
2360
+ type: z3.literal("organisation.read-locations"),
2361
+ options: AccessLevelOptions.optional()
2362
+ }),
2259
2363
  z3.object({
2260
2364
  type: z3.literal("workqueue"),
2261
2365
  options: WorkqueueOrDashboardOptions
@@ -2290,22 +2394,29 @@ var encodeScope = (scope) => {
2290
2394
  encode: false
2291
2395
  });
2292
2396
  };
2293
- var decodeScope = (query) => {
2294
- const scope = qs.parse(query, {
2397
+ var decodedScopeCache = /* @__PURE__ */ new Map();
2398
+ var decodeScope = (encodedScope) => {
2399
+ if (decodedScopeCache.has(encodedScope)) {
2400
+ return decodedScopeCache.get(encodedScope);
2401
+ }
2402
+ const scope = qs.parse(encodedScope, {
2295
2403
  ignoreQueryPrefix: true,
2296
2404
  comma: true,
2297
2405
  allowDots: true
2298
2406
  });
2299
2407
  const unflattenedScope = unflattenScope(scope);
2300
- return Scope2.safeParse(unflattenedScope)?.data;
2408
+ const result = Scope2.safeParse(unflattenedScope)?.data;
2409
+ decodedScopeCache.set(encodedScope, result);
2410
+ return result;
2301
2411
  };
2302
2412
  var DEFAULT_SCOPE_OPTIONS = {
2303
2413
  placeOfEvent: JurisdictionFilter.enum.all,
2304
- accessLevel: JurisdictionFilter.enum.all
2414
+ accessLevel: JurisdictionFilter.enum.all,
2415
+ registeredIn: JurisdictionFilter.enum.all,
2416
+ declaredIn: JurisdictionFilter.enum.all
2305
2417
  };
2306
2418
 
2307
2419
  // ../commons/src/scopes.deprecated.do-not-use.ts
2308
- var z4 = __toESM(require("zod/v4"));
2309
2420
  var rawConfigurableScopeRegex = /^([a-zA-Z][a-zA-Z0-9.-]*(?:\.[a-zA-Z0-9.-]+)*)\[((?:\w+=[\w.-]+(?:\|[\w.-]+)*)(?:,[\w]+=[\w.-]+(?:\|[\w.-]+)*)*)\]$/;
2310
2421
  var rawConfigurableScope = z4.string().regex(rawConfigurableScopeRegex);
2311
2422
  var SearchScope = z4.object({
@@ -2539,6 +2650,74 @@ var legacyScopeToV2Scope = (v1Scope) => {
2539
2650
  }
2540
2651
  throw new Error(`Unsupported V1 scope type: ${v1Scope}`);
2541
2652
  };
2653
+ var MERGEABLE_USER_SCOPE_TYPES = ["user.create", "user.edit"];
2654
+ function isMergeableUserScopeType(type) {
2655
+ return MERGEABLE_USER_SCOPE_TYPES.includes(type);
2656
+ }
2657
+ function decodeLegacyScope(legacy) {
2658
+ const encoded = legacyScopeToV2Scope(legacy);
2659
+ const scope = decodeScope(encoded);
2660
+ if (!scope) {
2661
+ throw new Error(
2662
+ `Could not decode migrated scope from legacy '${legacy}' (encoded: '${encoded}').`
2663
+ );
2664
+ }
2665
+ return { legacy, scope };
2666
+ }
2667
+ function groupByType(entries) {
2668
+ return entries.reduce(
2669
+ (groups, entry) => ({
2670
+ ...groups,
2671
+ [entry.scope.type]: [...groups[entry.scope.type] ?? [], entry]
2672
+ }),
2673
+ {}
2674
+ );
2675
+ }
2676
+ function deduplicateEntries(entries) {
2677
+ return entries.filter(
2678
+ (entry, index) => entries.findIndex((e) => (0, import_lodash.isEqual)(e.scope, entry.scope)) === index
2679
+ );
2680
+ }
2681
+ function mergeEntries(type, entries) {
2682
+ const optionsList = entries.map(
2683
+ (e) => "options" in e.scope && e.scope.options ? e.scope.options : {}
2684
+ );
2685
+ const allKeys = optionsList.flatMap(
2686
+ (opts) => Object.keys(opts).filter((k) => opts[k] !== void 0)
2687
+ );
2688
+ const conflictKey = allKeys.find((key, i) => allKeys.indexOf(key) !== i);
2689
+ if (conflictKey) {
2690
+ const conflicting = entries.filter(
2691
+ (e) => conflictKey in ("options" in e.scope && e.scope.options || {})
2692
+ ).map((e) => JSON.stringify(e.legacy));
2693
+ throw new Error(
2694
+ `Cannot auto-merge two '${type}' scopes that both define '${conflictKey}'. Conflicting legacy scopes: [${conflicting.join(", ")}]. Resolve manually in roles.ts and re-run.`
2695
+ );
2696
+ }
2697
+ const mergedOptions = Object.assign({}, ...optionsList);
2698
+ const merged = Object.keys(mergedOptions).length > 0 ? { type, options: mergedOptions } : { type };
2699
+ const parsed = Scope2.safeParse(merged);
2700
+ if (!parsed.success) {
2701
+ throw new Error(
2702
+ `Merged '${type}' scope failed schema validation: ${parsed.error.message}. Legacy scopes: [${entries.map((e) => JSON.stringify(e.legacy)).join(", ")}].`
2703
+ );
2704
+ }
2705
+ return parsed.data;
2706
+ }
2707
+ function migrateLegacyScopesArrayToV2Scopes(legacyScopes) {
2708
+ const groups = groupByType(legacyScopes.map(decodeLegacyScope));
2709
+ return Object.entries(groups).flatMap(([type, entries]) => {
2710
+ const deduped = deduplicateEntries(entries);
2711
+ if (deduped.length <= 1) return deduped.map((e) => e.scope);
2712
+ if (!isMergeableUserScopeType(type)) {
2713
+ console.warn(
2714
+ `Multiple non-mergeable '${type}' scopes detected after dedup \u2014 kept all as-is. Legacy scopes: [${deduped.map((e) => JSON.stringify(e.legacy)).join(", ")}]. Review whether the duplication is intentional.`
2715
+ );
2716
+ return deduped.map((e) => e.scope);
2717
+ }
2718
+ return [mergeEntries(type, deduped)];
2719
+ });
2720
+ }
2542
2721
 
2543
2722
  // src/migrations/v2.0/migrate-scopes.ts
2544
2723
  var ROLES_FILE_RELATIVE_PATH = "src/data-seeding/roles/roles.ts";
@@ -2576,57 +2755,65 @@ function resolveLegacyScopeFromElement(element) {
2576
2755
  }
2577
2756
  function migrateScopesArrays(rolesFilePath, project) {
2578
2757
  const sourceFile = project.getSourceFile(rolesFilePath);
2579
- if (!sourceFile) return { migratedCount: 0, removedCount: 0 };
2758
+ if (!sourceFile) {
2759
+ return { migratedCount: 0, removedCount: 0, mergedCount: 0 };
2760
+ }
2580
2761
  let migratedCount = 0;
2581
2762
  let removedCount = 0;
2763
+ let mergedCount = 0;
2582
2764
  const scopesProperties = sourceFile.getDescendantsOfKind(import_ts_morph11.SyntaxKind.PropertyAssignment).filter((prop) => prop.getName() === SCOPES_PROPERTY_NAME);
2765
+ const relPath = import_path11.default.relative(process.cwd(), rolesFilePath);
2583
2766
  for (const scopesProp of scopesProperties) {
2584
2767
  const initializer = scopesProp.getInitializer();
2585
2768
  if (!initializer || !import_ts_morph11.Node.isArrayLiteralExpression(initializer)) continue;
2586
- const elements = initializer.getElements();
2587
- for (let index = elements.length - 1; index >= 0; index--) {
2588
- const element = elements[index];
2589
- if (!import_ts_morph11.Node.isExpression(element)) continue;
2769
+ const rawTexts = [];
2770
+ const legacyStrings = [];
2771
+ let anyChange = false;
2772
+ for (const element of initializer.getElements()) {
2590
2773
  const scopeKey = resolveScopeKeyFromElementAccess(element);
2591
2774
  if (scopeKey && !(scopeKey in SCOPES)) {
2592
2775
  console.warn(
2593
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2776
+ ` [${relPath}] Removing unknown scope reference: SCOPES.${scopeKey}`
2594
2777
  );
2595
- initializer.removeElement(index);
2596
2778
  removedCount++;
2779
+ anyChange = true;
2597
2780
  continue;
2598
2781
  }
2599
2782
  const legacyScope = resolveLegacyScopeFromElement(element);
2600
2783
  if (!legacyScope) {
2601
2784
  console.warn(
2602
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2785
+ ` [${relPath}] Preserving non-literal scope expression verbatim: ${element.getText()}`
2603
2786
  );
2787
+ rawTexts.push(element.getText());
2604
2788
  continue;
2605
2789
  }
2606
- try {
2607
- const migratedScope = legacyScopeToV2Scope(legacyScope);
2608
- const scope = decodeScope(migratedScope);
2609
- if (!scope) {
2610
- console.warn(
2611
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not decode migrated scope '${migratedScope}'.`
2612
- );
2613
- continue;
2614
- }
2615
- element.replaceWithText(JSON.stringify(scope));
2616
- migratedCount++;
2617
- } catch (error) {
2618
- console.warn(
2619
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not migrate scope '${legacyScope}': ${error}`
2620
- );
2621
- }
2790
+ legacyStrings.push(legacyScope);
2791
+ anyChange = true;
2622
2792
  }
2793
+ if (!anyChange) continue;
2794
+ let mergedScopes;
2795
+ try {
2796
+ mergedScopes = migrateLegacyScopesArrayToV2Scopes(legacyStrings);
2797
+ } catch (error) {
2798
+ throw new Error(
2799
+ `[${relPath}] Failed to migrate scopes for role: ${error instanceof Error ? error.message : String(error)}`,
2800
+ { cause: error }
2801
+ );
2802
+ }
2803
+ migratedCount += legacyStrings.length;
2804
+ mergedCount += legacyStrings.length - mergedScopes.length;
2805
+ const outputElements = [
2806
+ ...rawTexts,
2807
+ ...mergedScopes.map((s) => JSON.stringify(s))
2808
+ ];
2809
+ scopesProp.setInitializer(`[${outputElements.join(", ")}]`);
2623
2810
  }
2624
2811
  if (removedCount > 0) {
2625
2812
  console.log(
2626
- ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(process.cwd(), rolesFilePath)}'.`
2813
+ ` Removed ${removedCount} unknown SCOPES reference(s) from '${relPath}'.`
2627
2814
  );
2628
2815
  }
2629
- return { migratedCount, removedCount };
2816
+ return { migratedCount, removedCount, mergedCount };
2630
2817
  }
2631
2818
  function ensureScopesHelpersImport(rolesFilePath, project) {
2632
2819
  const sourceFile = project.getSourceFile(rolesFilePath);
@@ -2761,7 +2948,7 @@ async function main11() {
2761
2948
  return;
2762
2949
  }
2763
2950
  const importChanged = ensureScopesHelpersImport(rolesFilePath, project);
2764
- const { migratedCount, removedCount } = migrateScopesArrays(
2951
+ const { migratedCount, removedCount, mergedCount } = migrateScopesArrays(
2765
2952
  rolesFilePath,
2766
2953
  project
2767
2954
  );
@@ -2780,7 +2967,7 @@ async function main11() {
2780
2967
  }
2781
2968
  await sourceFile.save();
2782
2969
  console.log(
2783
- `Done. Migrated ${migratedCount} legacy scope(s), removed ${removedCount} unknown SCOPES reference(s), wrapped ${wrappedScopesCount} scope array(s) with defineScopes()${wrappedRoles ? ", wrapped roles with defineRoles()" : ""}${removedRoleType ? ", and removed Role type definition" : ""}${importChanged ? ". Updated toolkit/scopes imports." : "."}`
2970
+ `Done. Migrated ${migratedCount} legacy scope(s), merged ${mergedCount} AND-paired scope(s) into single v2 objects (privilege-escalation fix, see issue #12489), removed ${removedCount} unknown SCOPES reference(s), wrapped ${wrappedScopesCount} scope array(s) with defineScopes()${wrappedRoles ? ", wrapped roles with defineRoles()" : ""}${removedRoleType ? ", and removed Role type definition" : ""}${importChanged ? ". Updated toolkit/scopes imports." : "."}`
2784
2971
  );
2785
2972
  }
2786
2973
 
@@ -3586,6 +3773,10 @@ var ACTION_PROPERTY_NAME2 = "action";
3586
3773
  var CONDITIONALS_PROPERTY_NAME = "conditionals";
3587
3774
  var TYPE_PROPERTY_NAME7 = "type";
3588
3775
  var SLUG_PROPERTY_NAME = "slug";
3776
+ var READ_ACTION_TYPE = "READ";
3777
+ var ACTION_TYPE_IMPORT_NAME = "ActionType";
3778
+ var TOOLKIT_EVENTS_MODULE2 = "@opencrvs/toolkit/events";
3779
+ var READ_ACTION_FALLBACK_INITIALIZER = `{ type: ${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE} }`;
3589
3780
  var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
3590
3781
  "READ",
3591
3782
  "DELETE",
@@ -3633,91 +3824,137 @@ function isUnsupportedActionType(action) {
3633
3824
  }
3634
3825
  return !SUPPORTED_WORKQUEUE_ACTION_TYPES.has(typeName);
3635
3826
  }
3636
- function migrateWorkqueueActions(workqueue, relPath) {
3637
- const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3638
- if (!actionsProperty || !import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3639
- return 0;
3640
- }
3641
- const actionsInitializer = actionsProperty.getInitializer();
3642
- if (!actionsInitializer || !import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3643
- return 0;
3827
+ function ensureValidAction(workqueue, relPath) {
3828
+ const workqueueLabel = getWorkqueueLabel(workqueue);
3829
+ const actionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3830
+ if (actionProperty && import_ts_morph17.Node.isPropertyAssignment(actionProperty)) {
3831
+ const initializer = actionProperty.getInitializer();
3832
+ if (initializer && import_ts_morph17.Node.isObjectLiteralExpression(initializer)) {
3833
+ if (!isUnsupportedActionType(initializer)) {
3834
+ return { changed: false, usedReadFallback: false };
3835
+ }
3836
+ actionProperty.set({
3837
+ name: ACTION_PROPERTY_NAME2,
3838
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3839
+ });
3840
+ console.log(
3841
+ ` [${relPath}] Replaced unsupported 'action' type with '${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE}' on workqueue '${workqueueLabel}'`
3842
+ );
3843
+ return { changed: true, usedReadFallback: true };
3844
+ }
3644
3845
  }
3846
+ workqueue.addPropertyAssignment({
3847
+ name: ACTION_PROPERTY_NAME2,
3848
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3849
+ });
3850
+ console.log(
3851
+ ` [${relPath}] Added default 'action: ${READ_ACTION_FALLBACK_INITIALIZER}' to workqueue '${workqueueLabel}'`
3852
+ );
3853
+ return { changed: true, usedReadFallback: true };
3854
+ }
3855
+ function migrateWorkqueueActions(workqueue, relPath) {
3645
3856
  const workqueueLabel = getWorkqueueLabel(workqueue);
3646
- const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3647
- if (existingActionProperty) {
3648
- if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3649
- const existingActionInitializer = existingActionProperty.getInitializer();
3650
- if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3651
- const conditionalsProperty2 = existingActionInitializer.getProperty(
3652
- CONDITIONALS_PROPERTY_NAME
3857
+ const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3858
+ let actionsArrayMigrated = false;
3859
+ if (actionsProperty && import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3860
+ const actionsInitializer = actionsProperty.getInitializer();
3861
+ if (actionsInitializer && import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3862
+ const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3863
+ if (existingActionProperty) {
3864
+ if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3865
+ const existingActionInitializer = existingActionProperty.getInitializer();
3866
+ if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3867
+ const conditionalsProperty = existingActionInitializer.getProperty(
3868
+ CONDITIONALS_PROPERTY_NAME
3869
+ );
3870
+ if (conditionalsProperty) {
3871
+ conditionalsProperty.remove();
3872
+ console.log(
3873
+ ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3874
+ );
3875
+ }
3876
+ }
3877
+ }
3878
+ actionsProperty.remove();
3879
+ console.log(
3880
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3881
+ );
3882
+ actionsArrayMigrated = true;
3883
+ } else {
3884
+ const actionCandidates = actionsInitializer.getElements().filter(
3885
+ (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3653
3886
  );
3654
- if (conditionalsProperty2) {
3655
- conditionalsProperty2.remove();
3887
+ if (actionCandidates.length === 0) {
3888
+ actionsProperty.remove();
3656
3889
  console.log(
3657
- ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3890
+ ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3658
3891
  );
3659
- }
3660
- if (isUnsupportedActionType(existingActionInitializer)) {
3661
- existingActionProperty.remove();
3662
- console.log(
3663
- ` [${relPath}] Removed unsupported 'action' type on workqueue '${workqueueLabel}'`
3892
+ actionsArrayMigrated = true;
3893
+ } else {
3894
+ if (actionCandidates.length > 1) {
3895
+ const droppedCount = actionCandidates.length - 1;
3896
+ console.warn(
3897
+ ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3898
+ );
3899
+ }
3900
+ const conditionalsProperty = actionCandidates[0].getProperty(
3901
+ CONDITIONALS_PROPERTY_NAME
3664
3902
  );
3903
+ if (conditionalsProperty) {
3904
+ conditionalsProperty.remove();
3905
+ console.log(
3906
+ ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3907
+ );
3908
+ }
3909
+ if (isUnsupportedActionType(actionCandidates[0])) {
3910
+ actionsProperty.remove();
3911
+ console.log(
3912
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3913
+ );
3914
+ actionsArrayMigrated = true;
3915
+ } else {
3916
+ const firstActionText = actionCandidates[0].getText();
3917
+ workqueue.addPropertyAssignment({
3918
+ name: ACTION_PROPERTY_NAME2,
3919
+ initializer: firstActionText
3920
+ });
3921
+ actionsProperty.remove();
3922
+ console.log(
3923
+ ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3924
+ );
3925
+ actionsArrayMigrated = true;
3926
+ }
3665
3927
  }
3666
3928
  }
3667
3929
  }
3668
- actionsProperty.remove();
3669
- console.log(
3670
- ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3671
- );
3672
- return 1;
3673
- }
3674
- const actionCandidates = actionsInitializer.getElements().filter(
3675
- (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3676
- );
3677
- if (actionCandidates.length === 0) {
3678
- actionsProperty.remove();
3679
- console.log(
3680
- ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3681
- );
3682
- return 1;
3683
- }
3684
- if (actionCandidates.length > 1) {
3685
- const droppedCount = actionCandidates.length - 1;
3686
- console.warn(
3687
- ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3688
- );
3689
3930
  }
3690
- const conditionalsProperty = actionCandidates[0].getProperty(
3691
- CONDITIONALS_PROPERTY_NAME
3931
+ const fallback = ensureValidAction(workqueue, relPath);
3932
+ return {
3933
+ changed: actionsArrayMigrated || fallback.changed,
3934
+ usedReadFallback: fallback.usedReadFallback
3935
+ };
3936
+ }
3937
+ function ensureActionTypeImport(sourceFile) {
3938
+ const existingImport = sourceFile.getImportDeclaration(
3939
+ (decl) => decl.getModuleSpecifierValue() === TOOLKIT_EVENTS_MODULE2
3692
3940
  );
3693
- if (conditionalsProperty) {
3694
- conditionalsProperty.remove();
3695
- console.log(
3696
- ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3697
- );
3698
- }
3699
- if (isUnsupportedActionType(actionCandidates[0])) {
3700
- actionsProperty.remove();
3701
- console.log(
3702
- ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3703
- );
3704
- return 1;
3941
+ if (existingImport) {
3942
+ const alreadyImported = existingImport.getNamedImports().some((ni) => ni.getName() === ACTION_TYPE_IMPORT_NAME);
3943
+ if (!alreadyImported) {
3944
+ existingImport.addNamedImport(ACTION_TYPE_IMPORT_NAME);
3945
+ }
3946
+ return;
3705
3947
  }
3706
- const firstActionText = actionCandidates[0].getText();
3707
- workqueue.addPropertyAssignment({
3708
- name: ACTION_PROPERTY_NAME2,
3709
- initializer: firstActionText
3948
+ sourceFile.addImportDeclaration({
3949
+ moduleSpecifier: TOOLKIT_EVENTS_MODULE2,
3950
+ namedImports: [ACTION_TYPE_IMPORT_NAME]
3710
3951
  });
3711
- actionsProperty.remove();
3712
- console.log(
3713
- ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3714
- );
3715
- return 1;
3716
3952
  }
3717
3953
  function processFile13(filePath, project) {
3718
3954
  const sourceFile = project.getSourceFile(filePath);
3719
3955
  if (!sourceFile) return 0;
3720
3956
  let migratedWorkqueues = 0;
3957
+ let needsActionTypeImport = false;
3721
3958
  const relPath = import_path18.default.relative(process.cwd(), filePath);
3722
3959
  const callExpressions = sourceFile.getDescendantsOfKind(
3723
3960
  import_ts_morph17.SyntaxKind.CallExpression
@@ -3733,9 +3970,14 @@ function processFile13(filePath, project) {
3733
3970
  if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
3734
3971
  for (const element of workqueuesArg.getElements()) {
3735
3972
  if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
3736
- migratedWorkqueues += migrateWorkqueueActions(element, relPath);
3973
+ const result = migrateWorkqueueActions(element, relPath);
3974
+ if (result.changed) migratedWorkqueues++;
3975
+ if (result.usedReadFallback) needsActionTypeImport = true;
3737
3976
  }
3738
3977
  }
3978
+ if (needsActionTypeImport) {
3979
+ ensureActionTypeImport(sourceFile);
3980
+ }
3739
3981
  return migratedWorkqueues;
3740
3982
  }
3741
3983
  async function main18() {
@@ -4070,7 +4312,7 @@ Saved ${handlerRelPath}.`);
4070
4312
  // src/migrations/v2.0/checkout-upstream-files.ts
4071
4313
  var import_child_process = require("child_process");
4072
4314
  var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
4073
- var UPSTREAM_BRANCH = "develop";
4315
+ var UPSTREAM_BRANCH = "release-v2.0.0";
4074
4316
  var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
4075
4317
  var FILES_TO_CHECKOUT = [
4076
4318
  "src/analytics/analytics.ts",
@@ -4609,9 +4851,9 @@ var import_path24 = require("path");
4609
4851
  var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
4610
4852
  var INFRASTRUCTURE_DIR = "infrastructure";
4611
4853
  var BASE_BRANCH = "release-v1.9";
4612
- function gitShowBytes(ref, path23) {
4854
+ function gitShowBytes(ref, path24) {
4613
4855
  try {
4614
- const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path23}`], {
4856
+ const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path24}`], {
4615
4857
  cwd: process.cwd(),
4616
4858
  stdio: ["ignore", "pipe", "pipe"]
4617
4859
  });
@@ -4620,9 +4862,13 @@ function gitShowBytes(ref, path23) {
4620
4862
  return null;
4621
4863
  }
4622
4864
  }
4623
- async function main25() {
4865
+ async function main25({
4866
+ subdirs = []
4867
+ } = {}) {
4868
+ const pathspecs = subdirs.length > 0 ? subdirs.map((s) => `${INFRASTRUCTURE_DIR}/${s}/`) : [`${INFRASTRUCTURE_DIR}/`];
4869
+ const scopeLabel = pathspecs.map((p) => `'${p}'`).join(", ");
4624
4870
  console.log(
4625
- `Merging '${INFRASTRUCTURE_DIR}/' from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4871
+ `Merging ${scopeLabel} from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4626
4872
  `
4627
4873
  );
4628
4874
  assertIsGitRepo();
@@ -4637,20 +4883,12 @@ async function main25() {
4637
4883
  const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
4638
4884
  const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
4639
4885
  const upstreamFiles = runGit(
4640
- [
4641
- "ls-tree",
4642
- "-r",
4643
- "--name-only",
4644
- "-z",
4645
- ref,
4646
- "--",
4647
- `${INFRASTRUCTURE_DIR}/`
4648
- ],
4886
+ ["ls-tree", "-r", "--name-only", "-z", ref, "--", ...pathspecs],
4649
4887
  { silent: true }
4650
4888
  ).split("\0").filter(Boolean);
4651
4889
  if (upstreamFiles.length === 0) {
4652
4890
  console.log(
4653
- ` No files found under '${INFRASTRUCTURE_DIR}/' on ${ref}. Nothing to merge.`
4891
+ ` No files found under ${scopeLabel} on ${ref}. Nothing to merge.`
4654
4892
  );
4655
4893
  return;
4656
4894
  }
@@ -4725,7 +4963,9 @@ async function main25() {
4725
4963
  var import_fs6 = require("fs");
4726
4964
  var import_path25 = require("path");
4727
4965
  var INFRASTRUCTURE_DIR2 = "infrastructure";
4728
- async function main26() {
4966
+ async function main26({
4967
+ keep = []
4968
+ } = {}) {
4729
4969
  const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
4730
4970
  if (!(0, import_fs6.existsSync)(fullPath)) {
4731
4971
  console.log(
@@ -4733,24 +4973,152 @@ async function main26() {
4733
4973
  );
4734
4974
  return;
4735
4975
  }
4736
- console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4737
- `);
4738
4976
  assertIsGitRepo();
4739
- runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4740
- silent: true
4977
+ if (keep.length === 0) {
4978
+ console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4979
+ `);
4980
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4981
+ silent: true
4982
+ });
4983
+ console.log(
4984
+ ` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`
4985
+ );
4986
+ if ((0, import_fs6.existsSync)(fullPath)) {
4987
+ (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4988
+ console.log(
4989
+ ` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`
4990
+ );
4991
+ }
4992
+ console.log(
4993
+ `
4994
+ Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
4995
+ );
4996
+ return;
4997
+ }
4998
+ const keepSet = new Set(keep);
4999
+ console.log(
5000
+ `Deleting contents of '${INFRASTRUCTURE_DIR2}/' except: ${[...keepSet].map((name) => `'${name}'`).join(", ")}
5001
+ `
5002
+ );
5003
+ const topLevelEntries = (0, import_fs6.readdirSync)(fullPath, { withFileTypes: true });
5004
+ let deleted = 0;
5005
+ for (const entry of topLevelEntries) {
5006
+ if (keepSet.has(entry.name)) {
5007
+ console.log(` Keeping '${INFRASTRUCTURE_DIR2}/${entry.name}'.`);
5008
+ continue;
5009
+ }
5010
+ const relPath = `${INFRASTRUCTURE_DIR2}/${entry.name}${entry.isDirectory() ? "/" : ""}`;
5011
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", relPath], { silent: true });
5012
+ const fullEntryPath = (0, import_path25.join)(fullPath, entry.name);
5013
+ if ((0, import_fs6.existsSync)(fullEntryPath)) {
5014
+ (0, import_fs6.rmSync)(fullEntryPath, { recursive: true, force: true });
5015
+ }
5016
+ console.log(` Deleted '${relPath}'.`);
5017
+ deleted++;
5018
+ }
5019
+ const presentNames = new Set(topLevelEntries.map((e) => e.name));
5020
+ const missingKeeps = [...keepSet].filter((name) => !presentNames.has(name));
5021
+ if (missingKeeps.length > 0) {
5022
+ console.warn(
5023
+ ` [warn] Requested to keep ${missingKeeps.map((n) => `'${n}'`).join(", ")} but they don't exist under '${INFRASTRUCTURE_DIR2}/'.`
5024
+ );
5025
+ }
5026
+ console.log(
5027
+ `
5028
+ Done. Deleted ${deleted} top-level entr${deleted === 1 ? "y" : "ies"}. Review the staged deletions with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5029
+ );
5030
+ }
5031
+
5032
+ // src/migrations/v2.0/fix-accept-requested-registration-function-type.ts
5033
+ var import_ts_morph22 = require("ts-morph");
5034
+ var import_path26 = __toESM(require("path"));
5035
+ var TARGET_FUNCTION_NAME = "acceptRequestedRegistration";
5036
+ var ACTION_PARAMETER_NAME = "action";
5037
+ var OLD_TYPE = "ActionInput";
5038
+ var NEW_TYPE = "Extract<ActionInput, { type?: 'REGISTER' }>";
5039
+ function isAcceptRequestedRegistrationParameter(parameter) {
5040
+ const functionDeclaration = parameter.getFirstAncestorByKind(
5041
+ import_ts_morph22.SyntaxKind.FunctionDeclaration
5042
+ );
5043
+ if (functionDeclaration?.getName() === TARGET_FUNCTION_NAME) {
5044
+ return true;
5045
+ }
5046
+ const arrowFunction = parameter.getFirstAncestorByKind(
5047
+ import_ts_morph22.SyntaxKind.ArrowFunction
5048
+ );
5049
+ const variableDeclaration = arrowFunction?.getFirstAncestorByKind(
5050
+ import_ts_morph22.SyntaxKind.VariableDeclaration
5051
+ );
5052
+ return variableDeclaration?.getName() === TARGET_FUNCTION_NAME;
5053
+ }
5054
+ function processFile14(filePath, project) {
5055
+ const sourceFile = project.getSourceFile(filePath);
5056
+ if (!sourceFile) return 0;
5057
+ let updatedCount = 0;
5058
+ for (const parameter of sourceFile.getDescendantsOfKind(
5059
+ import_ts_morph22.SyntaxKind.Parameter
5060
+ )) {
5061
+ if (parameter.getName() !== ACTION_PARAMETER_NAME) {
5062
+ continue;
5063
+ }
5064
+ if (!isAcceptRequestedRegistrationParameter(parameter)) {
5065
+ continue;
5066
+ }
5067
+ const typeNode = parameter.getTypeNode();
5068
+ if (!typeNode || typeNode.getText() !== OLD_TYPE) {
5069
+ continue;
5070
+ }
5071
+ parameter.setType(NEW_TYPE);
5072
+ updatedCount++;
5073
+ console.log(
5074
+ ` [${import_path26.default.relative(process.cwd(), filePath)}] Updated '${ACTION_PARAMETER_NAME}' in ${TARGET_FUNCTION_NAME} to ${NEW_TYPE}`
5075
+ );
5076
+ }
5077
+ return updatedCount;
5078
+ }
5079
+ async function main27() {
5080
+ const srcDir = import_path26.default.join(process.cwd(), "src");
5081
+ console.log(`Scanning for ${TARGET_FUNCTION_NAME} in: ${srcDir}
5082
+ `);
5083
+ const project = new import_ts_morph22.Project({
5084
+ tsConfigFilePath: import_path26.default.resolve(srcDir, "../tsconfig.json"),
5085
+ skipAddingFilesFromTsConfig: false
4741
5086
  });
4742
- console.log(` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`);
4743
- if ((0, import_fs6.existsSync)(fullPath)) {
4744
- (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4745
- console.log(` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`);
5087
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
5088
+ const filePath = sf.getFilePath();
5089
+ return filePath.includes("/src/") && !filePath.includes("/node_modules/");
5090
+ });
5091
+ let totalUpdated = 0;
5092
+ const modifiedFiles = [];
5093
+ for (const sourceFile of sourceFiles) {
5094
+ const filePath = sourceFile.getFilePath();
5095
+ const updated = processFile14(filePath, project);
5096
+ if (updated > 0) {
5097
+ totalUpdated += updated;
5098
+ modifiedFiles.push(filePath);
5099
+ }
5100
+ }
5101
+ if (modifiedFiles.length === 0) {
5102
+ console.log(
5103
+ `No '${ACTION_PARAMETER_NAME}: ${OLD_TYPE}' parameter in ${TARGET_FUNCTION_NAME} found. Nothing to do.`
5104
+ );
5105
+ return;
5106
+ }
5107
+ console.log(`
5108
+ Saving ${modifiedFiles.length} modified file(s)...`);
5109
+ for (const filePath of modifiedFiles) {
5110
+ const sourceFile = project.getSourceFileOrThrow(filePath);
5111
+ await sourceFile.save();
5112
+ console.log(` Saved: ${import_path26.default.relative(process.cwd(), filePath)}`);
4746
5113
  }
4747
5114
  console.log(
4748
5115
  `
4749
- Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5116
+ Done. Updated ${totalUpdated} parameter type annotation(s) in ${TARGET_FUNCTION_NAME}.`
4750
5117
  );
4751
5118
  }
4752
5119
 
4753
5120
  // src/migrations/v2.0/index.ts
5121
+ var NON_SWARM_INFRA_SUBDIRS = ["postgres", "metabase"];
4754
5122
  async function runUpgrade(dockerSwarm) {
4755
5123
  await main18();
4756
5124
  await main();
@@ -4775,10 +5143,12 @@ async function runUpgrade(dockerSwarm) {
4775
5143
  await main22();
4776
5144
  await main23();
4777
5145
  await main24();
5146
+ await main27();
4778
5147
  await main17();
4779
5148
  if (dockerSwarm) {
4780
5149
  await main25();
4781
5150
  } else {
4782
- await main26();
5151
+ await main26({ keep: NON_SWARM_INFRA_SUBDIRS });
5152
+ await main25({ subdirs: NON_SWARM_INFRA_SUBDIRS });
4783
5153
  }
4784
5154
  }