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

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 +305 -129
  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 +1084 -261
  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 +841 -160
  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.ff99998.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
package/dist/cli.js CHANGED
@@ -178,6 +178,8 @@ function ensureImports(sourceFile) {
178
178
  var DEFINE_CONFIG_NAME2 = "defineConfig";
179
179
  var ACTIONS_PROPERTY_NAME2 = "actions";
180
180
  var TYPE_PROPERTY_NAME2 = "type";
181
+ var ID_PROPERTY_NAME = "id";
182
+ var FLAGS_PROPERTY_NAME = "flags";
181
183
  var CUSTOM_ACTION_TYPE_PROPERTY_NAME = "customActionType";
182
184
  var AUDIT_HISTORY_LABEL_PROPERTY_NAME = "auditHistoryLabel";
183
185
  var DEDUPLICATION_PROPERTY_NAME = "deduplication";
@@ -187,9 +189,12 @@ var FORM_PROPERTY_NAME = "form";
187
189
  var ACTION_TYPE_ENUM_NAME = "ActionType";
188
190
  var VALIDATE_MEMBER_NAME = "VALIDATE";
189
191
  var CUSTOM_MEMBER_NAME = "CUSTOM";
192
+ var REGISTER_MEMBER_NAME2 = "REGISTER";
190
193
  var VALIDATE_STRING_LITERAL = "VALIDATE";
191
194
  var CUSTOM_STRING_LITERAL = "CUSTOM";
195
+ var REGISTER_STRING_LITERAL2 = "REGISTER";
192
196
  var VALIDATE_DECLARATION_VALUE = "VALIDATE_DECLARATION";
197
+ var VALIDATED_FLAG_VALUE = "validated";
193
198
  function isValidateType(typeInitializer) {
194
199
  if (typeInitializer.getKind() === import_ts_morph2.SyntaxKind.PropertyAccessExpression) {
195
200
  const propAccess = typeInitializer.asKindOrThrow(
@@ -313,6 +318,50 @@ function transformValidateActionToCustom(obj, eventType) {
313
318
  }
314
319
  return true;
315
320
  }
321
+ function isRegisterAction(action) {
322
+ const typeProperty = action.getProperty(TYPE_PROPERTY_NAME2);
323
+ if (!typeProperty || !import_ts_morph2.Node.isPropertyAssignment(typeProperty)) return false;
324
+ const typeInit = typeProperty.getInitializer();
325
+ if (!typeInit) return false;
326
+ if (import_ts_morph2.Node.isPropertyAccessExpression(typeInit)) {
327
+ const enumExpr = typeInit.getExpression();
328
+ return import_ts_morph2.Node.isIdentifier(enumExpr) && enumExpr.getText() === ACTION_TYPE_ENUM_NAME && typeInit.getName() === REGISTER_MEMBER_NAME2;
329
+ }
330
+ if (import_ts_morph2.Node.isStringLiteral(typeInit)) {
331
+ return typeInit.getLiteralValue() === REGISTER_STRING_LITERAL2;
332
+ }
333
+ return false;
334
+ }
335
+ function arrayHasElementWithId(array3, id) {
336
+ for (const el of array3.getElements()) {
337
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(el)) continue;
338
+ const idProp = el.getProperty(ID_PROPERTY_NAME);
339
+ if (!idProp || !import_ts_morph2.Node.isPropertyAssignment(idProp)) continue;
340
+ const idInit = idProp.getInitializer();
341
+ if (idInit && import_ts_morph2.Node.isStringLiteral(idInit) && idInit.getLiteralValue() === id) {
342
+ return true;
343
+ }
344
+ }
345
+ return false;
346
+ }
347
+ function ensureRegisterRemovesValidatedFlag(action) {
348
+ const entry = `{ id: '${VALIDATED_FLAG_VALUE}', operation: 'remove' }`;
349
+ const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME);
350
+ if (flagsProp && import_ts_morph2.Node.isPropertyAssignment(flagsProp)) {
351
+ const flagsInit = flagsProp.getInitializer();
352
+ if (flagsInit && import_ts_morph2.Node.isArrayLiteralExpression(flagsInit)) {
353
+ if (arrayHasElementWithId(flagsInit, VALIDATED_FLAG_VALUE)) return false;
354
+ flagsInit.addElement(entry);
355
+ return true;
356
+ }
357
+ return false;
358
+ }
359
+ action.addPropertyAssignment({
360
+ name: FLAGS_PROPERTY_NAME,
361
+ initializer: `[${entry}]`
362
+ });
363
+ return true;
364
+ }
316
365
  function processFile2(filePath, project) {
317
366
  const sourceFile = project.getSourceFile(filePath);
318
367
  if (!sourceFile) return 0;
@@ -378,6 +427,18 @@ function processFile2(filePath, project) {
378
427
  ` [${import_path2.default.relative(process.cwd(), filePath)}] Added top-level 'flags' array to defineConfig`
379
428
  );
380
429
  }
430
+ if (configHadValidateAction) {
431
+ for (const element of actionsInitializer.getElements()) {
432
+ if (!import_ts_morph2.Node.isObjectLiteralExpression(element)) continue;
433
+ if (!isRegisterAction(element)) continue;
434
+ if (ensureRegisterRemovesValidatedFlag(element)) {
435
+ transformedCount++;
436
+ console.log(
437
+ ` [${import_path2.default.relative(process.cwd(), filePath)}] Added '${VALIDATED_FLAG_VALUE}' remove-flag to REGISTER action`
438
+ );
439
+ }
440
+ }
441
+ }
381
442
  if (fileNeedsImports) {
382
443
  ensureImports(sourceFile);
383
444
  }
@@ -424,7 +485,7 @@ Saving ${modifiedFiles.length} modified file(s)...`);
424
485
  }
425
486
  console.log(
426
487
  `
427
- Done. Transformed ${totalTransformed} VALIDATE action(s) to CUSTOM with customActionType: '${VALIDATE_DECLARATION_VALUE}'.`
488
+ Done. Applied ${totalTransformed} change(s): VALIDATE \u2192 CUSTOM (${VALIDATE_DECLARATION_VALUE}) and '${VALIDATED_FLAG_VALUE}' remove-flag on matching REGISTER actions.`
428
489
  );
429
490
  }
430
491
 
@@ -667,14 +728,14 @@ var QUERY_PROPERTY_NAME = "query";
667
728
  var TYPE_PROPERTY_NAME5 = "type";
668
729
  var CLAUSES_PROPERTY_NAME = "clauses";
669
730
  var STATUS_PROPERTY_NAME = "status";
670
- var FLAGS_PROPERTY_NAME = "flags";
731
+ var FLAGS_PROPERTY_NAME2 = "flags";
671
732
  var TERMS_PROPERTY_NAME = "terms";
672
733
  var TERM_PROPERTY_NAME = "term";
673
734
  var ANY_OF_PROPERTY_NAME = "anyOf";
674
735
  var VALIDATED_STATUS_VALUE = "VALIDATED";
675
- var VALIDATED_FLAG_VALUE = "validated";
736
+ var VALIDATED_FLAG_VALUE2 = "validated";
676
737
  function addValidatedFlag(clause) {
677
- const flagsProperty = clause.getProperty(FLAGS_PROPERTY_NAME);
738
+ const flagsProperty = clause.getProperty(FLAGS_PROPERTY_NAME2);
678
739
  if (flagsProperty && import_ts_morph5.Node.isPropertyAssignment(flagsProperty)) {
679
740
  const flagsInit = flagsProperty.getInitializer();
680
741
  if (flagsInit && import_ts_morph5.Node.isObjectLiteralExpression(flagsInit)) {
@@ -682,19 +743,19 @@ function addValidatedFlag(clause) {
682
743
  if (anyOfProperty && import_ts_morph5.Node.isPropertyAssignment(anyOfProperty)) {
683
744
  const anyOfInit = anyOfProperty.getInitializer();
684
745
  if (anyOfInit && import_ts_morph5.Node.isArrayLiteralExpression(anyOfInit)) {
685
- anyOfInit.addElement(`'${VALIDATED_FLAG_VALUE}'`);
746
+ anyOfInit.addElement(`'${VALIDATED_FLAG_VALUE2}'`);
686
747
  }
687
748
  } else {
688
749
  flagsInit.addPropertyAssignment({
689
750
  name: ANY_OF_PROPERTY_NAME,
690
- initializer: `['${VALIDATED_FLAG_VALUE}']`
751
+ initializer: `['${VALIDATED_FLAG_VALUE2}']`
691
752
  });
692
753
  }
693
754
  }
694
755
  } else {
695
756
  clause.addPropertyAssignment({
696
- name: FLAGS_PROPERTY_NAME,
697
- initializer: `{ ${ANY_OF_PROPERTY_NAME}: ['${VALIDATED_FLAG_VALUE}'] }`
757
+ name: FLAGS_PROPERTY_NAME2,
758
+ initializer: `{ ${ANY_OF_PROPERTY_NAME}: ['${VALIDATED_FLAG_VALUE2}'] }`
698
759
  });
699
760
  }
700
761
  }
@@ -860,23 +921,26 @@ var DEFINE_CONFIG_NAME4 = "defineConfig";
860
921
  var DEFINE_WORKQUEUES_NAME2 = "defineWorkqueues";
861
922
  var ACTIONS_PROPERTY_NAME4 = "actions";
862
923
  var ACTION_PROPERTY_NAME = "action";
863
- var FLAGS_PROPERTY_NAME2 = "flags";
924
+ var FLAGS_PROPERTY_NAME3 = "flags";
864
925
  var QUERY_PROPERTY_NAME2 = "query";
865
926
  var TYPE_PROPERTY_NAME6 = "type";
927
+ var ID_PROPERTY_NAME2 = "id";
866
928
  var CLAUSES_PROPERTY_NAME2 = "clauses";
867
929
  var ANY_OF_PROPERTY_NAME2 = "anyOf";
868
930
  var FLAG_ID = "pending-first-certificate-issuance";
869
931
  var REGISTER_TYPE = "REGISTER";
870
932
  var PRINT_CERTIFICATE_TYPE = "PRINT_CERTIFICATE";
871
- var FLAG_DEFINITION_INITIALIZER = `{
933
+ function flagDefinitionInitializer(eventType) {
934
+ return `{
872
935
  id: '${FLAG_ID}',
873
936
  label: {
874
- id: 'event.birth.flag.${FLAG_ID}',
937
+ id: 'event.${eventType}.flag.${FLAG_ID}',
875
938
  defaultMessage: 'Pending first certificate issuance',
876
939
  description: 'Flag label for first certificate issuance'
877
940
  },
878
941
  requiresAction: true
879
942
  }`;
943
+ }
880
944
  function isActionOfType(obj, typeName) {
881
945
  const typeProperty = obj.getProperty(TYPE_PROPERTY_NAME6);
882
946
  if (!typeProperty || !import_ts_morph6.Node.isPropertyAssignment(typeProperty)) return false;
@@ -890,18 +954,20 @@ function isActionOfType(obj, typeName) {
890
954
  }
891
955
  return false;
892
956
  }
893
- function isBirthConfig(configArg) {
894
- const typeProperty = configArg.getProperty(TYPE_PROPERTY_NAME6);
895
- if (!typeProperty || !import_ts_morph6.Node.isPropertyAssignment(typeProperty)) return false;
896
- const initializer = typeProperty.getInitializer();
897
- if (!initializer) return false;
898
- if (import_ts_morph6.Node.isStringLiteral(initializer)) {
899
- return initializer.getLiteralValue().toLowerCase() === "birth";
900
- }
901
- if (import_ts_morph6.Node.isPropertyAccessExpression(initializer)) {
902
- return initializer.getName().toLowerCase() === "birth";
957
+ function getEventType(configArg) {
958
+ for (const propName of [TYPE_PROPERTY_NAME6, ID_PROPERTY_NAME2]) {
959
+ const prop = configArg.getProperty(propName);
960
+ if (!prop || !import_ts_morph6.Node.isPropertyAssignment(prop)) continue;
961
+ const initializer = prop.getInitializer();
962
+ if (!initializer) continue;
963
+ if (import_ts_morph6.Node.isStringLiteral(initializer)) {
964
+ return initializer.getLiteralValue().toLowerCase();
965
+ }
966
+ if (import_ts_morph6.Node.isPropertyAccessExpression(initializer)) {
967
+ return initializer.getName().toLowerCase();
968
+ }
903
969
  }
904
- return false;
970
+ return null;
905
971
  }
906
972
  function hasElementWithId(array3, id) {
907
973
  for (const el of array3.getElements()) {
@@ -915,27 +981,28 @@ function hasElementWithId(array3, id) {
915
981
  }
916
982
  return false;
917
983
  }
918
- function ensureTopLevelFlagDefinition(configArg) {
919
- const flagsProp = configArg.getProperty(FLAGS_PROPERTY_NAME2);
984
+ function ensureTopLevelFlagDefinition(configArg, eventType) {
985
+ const initializer = flagDefinitionInitializer(eventType);
986
+ const flagsProp = configArg.getProperty(FLAGS_PROPERTY_NAME3);
920
987
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
921
988
  const flagsInit = flagsProp.getInitializer();
922
989
  if (flagsInit && import_ts_morph6.Node.isArrayLiteralExpression(flagsInit)) {
923
990
  if (hasElementWithId(flagsInit, FLAG_ID)) return false;
924
- flagsInit.addElement(FLAG_DEFINITION_INITIALIZER);
991
+ flagsInit.addElement(initializer);
925
992
  return true;
926
993
  }
927
994
  return false;
928
995
  }
929
996
  configArg.addPropertyAssignment({
930
- name: FLAGS_PROPERTY_NAME2,
997
+ name: FLAGS_PROPERTY_NAME3,
931
998
  initializer: `[
932
- ${FLAG_DEFINITION_INITIALIZER}
999
+ ${initializer}
933
1000
  ]`
934
1001
  });
935
1002
  return true;
936
1003
  }
937
1004
  function ensureActionOperationFlag(action, operation) {
938
- const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME2);
1005
+ const flagsProp = action.getProperty(FLAGS_PROPERTY_NAME3);
939
1006
  const entry = `{ id: '${FLAG_ID}', operation: '${operation}' }`;
940
1007
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
941
1008
  const flagsInit = flagsProp.getInitializer();
@@ -947,16 +1014,18 @@ function ensureActionOperationFlag(action, operation) {
947
1014
  return false;
948
1015
  }
949
1016
  action.addPropertyAssignment({
950
- name: FLAGS_PROPERTY_NAME2,
1017
+ name: FLAGS_PROPERTY_NAME3,
951
1018
  initializer: `[${entry}]`
952
1019
  });
953
1020
  return true;
954
1021
  }
955
- function processBirthConfig(configArg, relPath) {
1022
+ function processConfig(configArg, eventType, relPath) {
956
1023
  let changes = 0;
957
- if (ensureTopLevelFlagDefinition(configArg)) {
1024
+ if (ensureTopLevelFlagDefinition(configArg, eventType)) {
958
1025
  changes++;
959
- console.log(` [${relPath}] Added '${FLAG_ID}' to top-level flags`);
1026
+ console.log(
1027
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' to top-level flags`
1028
+ );
960
1029
  }
961
1030
  const actionsProp = configArg.getProperty(ACTIONS_PROPERTY_NAME4);
962
1031
  if (!actionsProp || !import_ts_morph6.Node.isPropertyAssignment(actionsProp)) return changes;
@@ -970,7 +1039,7 @@ function processBirthConfig(configArg, relPath) {
970
1039
  if (ensureActionOperationFlag(element, "add")) {
971
1040
  changes++;
972
1041
  console.log(
973
- ` [${relPath}] Added '${FLAG_ID}' add-flag to REGISTER action`
1042
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' add-flag to REGISTER action`
974
1043
  );
975
1044
  }
976
1045
  }
@@ -978,7 +1047,7 @@ function processBirthConfig(configArg, relPath) {
978
1047
  if (ensureActionOperationFlag(element, "remove")) {
979
1048
  changes++;
980
1049
  console.log(
981
- ` [${relPath}] Added '${FLAG_ID}' remove-flag to PRINT_CERTIFICATE action`
1050
+ ` [${relPath}] (${eventType}) Added '${FLAG_ID}' remove-flag to PRINT_CERTIFICATE action`
982
1051
  );
983
1052
  }
984
1053
  }
@@ -1007,7 +1076,7 @@ function hasPrintCertificateAction(workqueue) {
1007
1076
  return false;
1008
1077
  }
1009
1078
  function addFlagToQueryClause(clause) {
1010
- const flagsProp = clause.getProperty(FLAGS_PROPERTY_NAME2);
1079
+ const flagsProp = clause.getProperty(FLAGS_PROPERTY_NAME3);
1011
1080
  if (flagsProp && import_ts_morph6.Node.isPropertyAssignment(flagsProp)) {
1012
1081
  const flagsInit = flagsProp.getInitializer();
1013
1082
  if (flagsInit && import_ts_morph6.Node.isObjectLiteralExpression(flagsInit)) {
@@ -1032,7 +1101,7 @@ function addFlagToQueryClause(clause) {
1032
1101
  }
1033
1102
  } else {
1034
1103
  clause.addPropertyAssignment({
1035
- name: FLAGS_PROPERTY_NAME2,
1104
+ name: FLAGS_PROPERTY_NAME3,
1036
1105
  initializer: `{ ${ANY_OF_PROPERTY_NAME2}: ['${FLAG_ID}'] }`
1037
1106
  });
1038
1107
  return true;
@@ -1076,8 +1145,9 @@ function processFile6(filePath, project) {
1076
1145
  if (args2.length === 0) continue;
1077
1146
  const configArg = args2[0];
1078
1147
  if (!import_ts_morph6.Node.isObjectLiteralExpression(configArg)) continue;
1079
- if (!isBirthConfig(configArg)) continue;
1080
- totalChanges += processBirthConfig(configArg, relPath);
1148
+ const eventType = getEventType(configArg);
1149
+ if (!eventType) continue;
1150
+ totalChanges += processConfig(configArg, eventType, relPath);
1081
1151
  }
1082
1152
  if (calleeName === DEFINE_WORKQUEUES_NAME2) {
1083
1153
  const args2 = call.getArguments();
@@ -1106,7 +1176,7 @@ function processFile6(filePath, project) {
1106
1176
  async function main6() {
1107
1177
  const srcDir = import_path6.default.join(process.cwd(), "src");
1108
1178
  console.log(
1109
- `Scanning for birth defineConfig and PRINT_CERTIFICATE workqueues in: ${srcDir}
1179
+ `Scanning for defineConfig and PRINT_CERTIFICATE workqueues in: ${srcDir}
1110
1180
  `
1111
1181
  );
1112
1182
  const project = new import_ts_morph6.Project({
@@ -1131,7 +1201,7 @@ async function main6() {
1131
1201
  }
1132
1202
  if (modifiedFiles.length === 0) {
1133
1203
  console.log(
1134
- "No birth configs or PRINT_CERTIFICATE workqueues found. Nothing to do."
1204
+ "No event configs or PRINT_CERTIFICATE workqueues found. Nothing to do."
1135
1205
  );
1136
1206
  return;
1137
1207
  }
@@ -1813,9 +1883,9 @@ function updateRouteHandlers(sourceFiles, group, srcDir, relCwd) {
1813
1883
  }
1814
1884
  return replacedCount;
1815
1885
  }
1816
- function resolveConfigChain(configObj, path23) {
1886
+ function resolveConfigChain(configObj, path24) {
1817
1887
  let current = configObj;
1818
- for (const key of path23) {
1888
+ for (const key of path24) {
1819
1889
  if (!import_ts_morph10.Node.isObjectLiteralExpression(current)) return null;
1820
1890
  const prop = current.getProperty(key);
1821
1891
  if (!prop || !import_ts_morph10.Node.isPropertyAssignment(prop)) return null;
@@ -2050,6 +2120,10 @@ Done. Converted ${convertedCount} config file(s), replaced ${totalReplaced} rout
2050
2120
  var import_ts_morph11 = require("ts-morph");
2051
2121
  var import_path11 = __toESM(require("path"));
2052
2122
 
2123
+ // ../commons/src/scopes.deprecated.do-not-use.ts
2124
+ var z4 = __toESM(require("zod/v4"));
2125
+ var import_lodash = require("lodash");
2126
+
2053
2127
  // ../commons/src/scopes.ts
2054
2128
  var z3 = __toESM(require("zod/v4"));
2055
2129
  var qs = __toESM(require("qs"));
@@ -2110,6 +2184,10 @@ var scopeByEvent = z3.preprocess(
2110
2184
  (val) => val === void 0 ? void 0 : [val].flat(),
2111
2185
  z3.array(z3.string()).optional()
2112
2186
  ).describe("Event type, e.g. birth, death");
2187
+ var userRole = z3.preprocess(
2188
+ (val) => val === void 0 ? void 0 : [val].flat(),
2189
+ z3.array(z3.string()).optional()
2190
+ ).describe("User role, e.g. admin, field agent");
2113
2191
  var scopeOptionsPlaceEvent = z3.object({
2114
2192
  event: scopeByEvent,
2115
2193
  placeOfEvent: JurisdictionFilter.optional()
@@ -2133,6 +2211,9 @@ var CustomActionScopeOptions = AllRecordScopeOptions.extend({
2133
2211
  var AccessLevelOptions = z3.object({
2134
2212
  accessLevel: JurisdictionFilter.optional()
2135
2213
  });
2214
+ var AllUserScopeOptions = AccessLevelOptions.extend({
2215
+ role: userRole.optional()
2216
+ });
2136
2217
  var WorkqueueOrDashboardOptions = z3.object({
2137
2218
  ids: z3.preprocess(
2138
2219
  (val) => val === void 0 ? void 0 : [val].flat(),
@@ -2142,6 +2223,7 @@ var WorkqueueOrDashboardOptions = z3.object({
2142
2223
  var AllScopeOptions = z3.object({
2143
2224
  ...AllRecordScopeOptions.shape,
2144
2225
  ...AccessLevelOptions.shape,
2226
+ ...AllUserScopeOptions.shape,
2145
2227
  ...WorkqueueOrDashboardOptions.shape
2146
2228
  });
2147
2229
  var ScopeOptionKey = AllScopeOptions.keyof();
@@ -2214,6 +2296,30 @@ var RecordScopeV2 = z3.discriminatedUnion("type", [
2214
2296
  ]).describe(
2215
2297
  "Scopes used to check user's permission to perform actions on a record."
2216
2298
  );
2299
+ var SystemScopeType = z3.enum([
2300
+ "organisation.read-locations",
2301
+ "user.read",
2302
+ "user.create",
2303
+ "user.edit",
2304
+ "user.search"
2305
+ ]);
2306
+ var UserScopeType = SystemScopeType.extract([
2307
+ "user.read",
2308
+ "user.create",
2309
+ "user.edit",
2310
+ "user.search"
2311
+ ]);
2312
+ var ScopesWithRoleOption = UserScopeType.extract(["user.edit", "user.create"]);
2313
+ var UserScopeV2 = z3.discriminatedUnion("type", [
2314
+ z3.object({
2315
+ type: ScopesWithRoleOption,
2316
+ options: AllUserScopeOptions.optional()
2317
+ }),
2318
+ z3.object({
2319
+ type: UserScopeType.extract(["user.read", "user.search"]),
2320
+ options: AccessLevelOptions.optional()
2321
+ })
2322
+ ]);
2217
2323
  var ResolvedRecordScopeV2 = z3.discriminatedUnion("type", [
2218
2324
  z3.object({
2219
2325
  type: ScopesWithPlaceEventOptions,
@@ -2234,16 +2340,14 @@ var ResolvedRecordScopeV2 = z3.discriminatedUnion("type", [
2234
2340
  }).optional()
2235
2341
  })
2236
2342
  ]).describe("Resolved scope with location/user IDs instead of filters.");
2237
- var SystemScopeType = z3.enum([
2238
- "organisation.read-locations",
2239
- "user.read",
2240
- "user.create",
2241
- "user.edit"
2242
- ]);
2243
2343
  var Scope2 = z3.discriminatedUnion("type", [
2244
2344
  z3.object({ type: PlainScopeType }),
2245
2345
  ...RecordScopeV2.options,
2246
- z3.object({ type: SystemScopeType, options: AccessLevelOptions.optional() }),
2346
+ ...UserScopeV2.options,
2347
+ z3.object({
2348
+ type: z3.literal("organisation.read-locations"),
2349
+ options: AccessLevelOptions.optional()
2350
+ }),
2247
2351
  z3.object({
2248
2352
  type: z3.literal("workqueue"),
2249
2353
  options: WorkqueueOrDashboardOptions
@@ -2278,22 +2382,29 @@ var encodeScope = (scope) => {
2278
2382
  encode: false
2279
2383
  });
2280
2384
  };
2281
- var decodeScope = (query) => {
2282
- const scope = qs.parse(query, {
2385
+ var decodedScopeCache = /* @__PURE__ */ new Map();
2386
+ var decodeScope = (encodedScope) => {
2387
+ if (decodedScopeCache.has(encodedScope)) {
2388
+ return decodedScopeCache.get(encodedScope);
2389
+ }
2390
+ const scope = qs.parse(encodedScope, {
2283
2391
  ignoreQueryPrefix: true,
2284
2392
  comma: true,
2285
2393
  allowDots: true
2286
2394
  });
2287
2395
  const unflattenedScope = unflattenScope(scope);
2288
- return Scope2.safeParse(unflattenedScope)?.data;
2396
+ const result = Scope2.safeParse(unflattenedScope)?.data;
2397
+ decodedScopeCache.set(encodedScope, result);
2398
+ return result;
2289
2399
  };
2290
2400
  var DEFAULT_SCOPE_OPTIONS = {
2291
2401
  placeOfEvent: JurisdictionFilter.enum.all,
2292
- accessLevel: JurisdictionFilter.enum.all
2402
+ accessLevel: JurisdictionFilter.enum.all,
2403
+ registeredIn: JurisdictionFilter.enum.all,
2404
+ declaredIn: JurisdictionFilter.enum.all
2293
2405
  };
2294
2406
 
2295
2407
  // ../commons/src/scopes.deprecated.do-not-use.ts
2296
- var z4 = __toESM(require("zod/v4"));
2297
2408
  var rawConfigurableScopeRegex = /^([a-zA-Z][a-zA-Z0-9.-]*(?:\.[a-zA-Z0-9.-]+)*)\[((?:\w+=[\w.-]+(?:\|[\w.-]+)*)(?:,[\w]+=[\w.-]+(?:\|[\w.-]+)*)*)\]$/;
2298
2409
  var rawConfigurableScope = z4.string().regex(rawConfigurableScopeRegex);
2299
2410
  var SearchScope = z4.object({
@@ -2527,6 +2638,74 @@ var legacyScopeToV2Scope = (v1Scope) => {
2527
2638
  }
2528
2639
  throw new Error(`Unsupported V1 scope type: ${v1Scope}`);
2529
2640
  };
2641
+ var MERGEABLE_USER_SCOPE_TYPES = ["user.create", "user.edit"];
2642
+ function isMergeableUserScopeType(type) {
2643
+ return MERGEABLE_USER_SCOPE_TYPES.includes(type);
2644
+ }
2645
+ function decodeLegacyScope(legacy) {
2646
+ const encoded = legacyScopeToV2Scope(legacy);
2647
+ const scope = decodeScope(encoded);
2648
+ if (!scope) {
2649
+ throw new Error(
2650
+ `Could not decode migrated scope from legacy '${legacy}' (encoded: '${encoded}').`
2651
+ );
2652
+ }
2653
+ return { legacy, scope };
2654
+ }
2655
+ function groupByType(entries) {
2656
+ return entries.reduce(
2657
+ (groups, entry) => ({
2658
+ ...groups,
2659
+ [entry.scope.type]: [...groups[entry.scope.type] ?? [], entry]
2660
+ }),
2661
+ {}
2662
+ );
2663
+ }
2664
+ function deduplicateEntries(entries) {
2665
+ return entries.filter(
2666
+ (entry, index) => entries.findIndex((e) => (0, import_lodash.isEqual)(e.scope, entry.scope)) === index
2667
+ );
2668
+ }
2669
+ function mergeEntries(type, entries) {
2670
+ const optionsList = entries.map(
2671
+ (e) => "options" in e.scope && e.scope.options ? e.scope.options : {}
2672
+ );
2673
+ const allKeys = optionsList.flatMap(
2674
+ (opts) => Object.keys(opts).filter((k) => opts[k] !== void 0)
2675
+ );
2676
+ const conflictKey = allKeys.find((key, i) => allKeys.indexOf(key) !== i);
2677
+ if (conflictKey) {
2678
+ const conflicting = entries.filter(
2679
+ (e) => conflictKey in ("options" in e.scope && e.scope.options || {})
2680
+ ).map((e) => JSON.stringify(e.legacy));
2681
+ throw new Error(
2682
+ `Cannot auto-merge two '${type}' scopes that both define '${conflictKey}'. Conflicting legacy scopes: [${conflicting.join(", ")}]. Resolve manually in roles.ts and re-run.`
2683
+ );
2684
+ }
2685
+ const mergedOptions = Object.assign({}, ...optionsList);
2686
+ const merged = Object.keys(mergedOptions).length > 0 ? { type, options: mergedOptions } : { type };
2687
+ const parsed = Scope2.safeParse(merged);
2688
+ if (!parsed.success) {
2689
+ throw new Error(
2690
+ `Merged '${type}' scope failed schema validation: ${parsed.error.message}. Legacy scopes: [${entries.map((e) => JSON.stringify(e.legacy)).join(", ")}].`
2691
+ );
2692
+ }
2693
+ return parsed.data;
2694
+ }
2695
+ function migrateLegacyScopesArrayToV2Scopes(legacyScopes) {
2696
+ const groups = groupByType(legacyScopes.map(decodeLegacyScope));
2697
+ return Object.entries(groups).flatMap(([type, entries]) => {
2698
+ const deduped = deduplicateEntries(entries);
2699
+ if (deduped.length <= 1) return deduped.map((e) => e.scope);
2700
+ if (!isMergeableUserScopeType(type)) {
2701
+ console.warn(
2702
+ `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.`
2703
+ );
2704
+ return deduped.map((e) => e.scope);
2705
+ }
2706
+ return [mergeEntries(type, deduped)];
2707
+ });
2708
+ }
2530
2709
 
2531
2710
  // src/migrations/v2.0/migrate-scopes.ts
2532
2711
  var ROLES_FILE_RELATIVE_PATH = "src/data-seeding/roles/roles.ts";
@@ -2564,57 +2743,65 @@ function resolveLegacyScopeFromElement(element) {
2564
2743
  }
2565
2744
  function migrateScopesArrays(rolesFilePath, project) {
2566
2745
  const sourceFile = project.getSourceFile(rolesFilePath);
2567
- if (!sourceFile) return { migratedCount: 0, removedCount: 0 };
2746
+ if (!sourceFile) {
2747
+ return { migratedCount: 0, removedCount: 0, mergedCount: 0 };
2748
+ }
2568
2749
  let migratedCount = 0;
2569
2750
  let removedCount = 0;
2751
+ let mergedCount = 0;
2570
2752
  const scopesProperties = sourceFile.getDescendantsOfKind(import_ts_morph11.SyntaxKind.PropertyAssignment).filter((prop) => prop.getName() === SCOPES_PROPERTY_NAME);
2753
+ const relPath = import_path11.default.relative(process.cwd(), rolesFilePath);
2571
2754
  for (const scopesProp of scopesProperties) {
2572
2755
  const initializer = scopesProp.getInitializer();
2573
2756
  if (!initializer || !import_ts_morph11.Node.isArrayLiteralExpression(initializer)) continue;
2574
- const elements = initializer.getElements();
2575
- for (let index = elements.length - 1; index >= 0; index--) {
2576
- const element = elements[index];
2577
- if (!import_ts_morph11.Node.isExpression(element)) continue;
2757
+ const rawTexts = [];
2758
+ const legacyStrings = [];
2759
+ let anyChange = false;
2760
+ for (const element of initializer.getElements()) {
2578
2761
  const scopeKey = resolveScopeKeyFromElementAccess(element);
2579
2762
  if (scopeKey && !(scopeKey in SCOPES)) {
2580
2763
  console.warn(
2581
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2764
+ ` [${relPath}] Removing unknown scope reference: SCOPES.${scopeKey}`
2582
2765
  );
2583
- initializer.removeElement(index);
2584
2766
  removedCount++;
2767
+ anyChange = true;
2585
2768
  continue;
2586
2769
  }
2587
2770
  const legacyScope = resolveLegacyScopeFromElement(element);
2588
2771
  if (!legacyScope) {
2589
2772
  console.warn(
2590
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2773
+ ` [${relPath}] Preserving non-literal scope expression verbatim: ${element.getText()}`
2591
2774
  );
2775
+ rawTexts.push(element.getText());
2592
2776
  continue;
2593
2777
  }
2594
- try {
2595
- const migratedScope = legacyScopeToV2Scope(legacyScope);
2596
- const scope = decodeScope(migratedScope);
2597
- if (!scope) {
2598
- console.warn(
2599
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not decode migrated scope '${migratedScope}'.`
2600
- );
2601
- continue;
2602
- }
2603
- element.replaceWithText(JSON.stringify(scope));
2604
- migratedCount++;
2605
- } catch (error) {
2606
- console.warn(
2607
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not migrate scope '${legacyScope}': ${error}`
2608
- );
2609
- }
2778
+ legacyStrings.push(legacyScope);
2779
+ anyChange = true;
2610
2780
  }
2781
+ if (!anyChange) continue;
2782
+ let mergedScopes;
2783
+ try {
2784
+ mergedScopes = migrateLegacyScopesArrayToV2Scopes(legacyStrings);
2785
+ } catch (error) {
2786
+ throw new Error(
2787
+ `[${relPath}] Failed to migrate scopes for role: ${error instanceof Error ? error.message : String(error)}`,
2788
+ { cause: error }
2789
+ );
2790
+ }
2791
+ migratedCount += legacyStrings.length;
2792
+ mergedCount += legacyStrings.length - mergedScopes.length;
2793
+ const outputElements = [
2794
+ ...rawTexts,
2795
+ ...mergedScopes.map((s) => JSON.stringify(s))
2796
+ ];
2797
+ scopesProp.setInitializer(`[${outputElements.join(", ")}]`);
2611
2798
  }
2612
2799
  if (removedCount > 0) {
2613
2800
  console.log(
2614
- ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(process.cwd(), rolesFilePath)}'.`
2801
+ ` Removed ${removedCount} unknown SCOPES reference(s) from '${relPath}'.`
2615
2802
  );
2616
2803
  }
2617
- return { migratedCount, removedCount };
2804
+ return { migratedCount, removedCount, mergedCount };
2618
2805
  }
2619
2806
  function ensureScopesHelpersImport(rolesFilePath, project) {
2620
2807
  const sourceFile = project.getSourceFile(rolesFilePath);
@@ -2749,7 +2936,7 @@ async function main11() {
2749
2936
  return;
2750
2937
  }
2751
2938
  const importChanged = ensureScopesHelpersImport(rolesFilePath, project);
2752
- const { migratedCount, removedCount } = migrateScopesArrays(
2939
+ const { migratedCount, removedCount, mergedCount } = migrateScopesArrays(
2753
2940
  rolesFilePath,
2754
2941
  project
2755
2942
  );
@@ -2768,7 +2955,7 @@ async function main11() {
2768
2955
  }
2769
2956
  await sourceFile.save();
2770
2957
  console.log(
2771
- `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." : "."}`
2958
+ `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." : "."}`
2772
2959
  );
2773
2960
  }
2774
2961
 
@@ -3574,6 +3761,10 @@ var ACTION_PROPERTY_NAME2 = "action";
3574
3761
  var CONDITIONALS_PROPERTY_NAME = "conditionals";
3575
3762
  var TYPE_PROPERTY_NAME7 = "type";
3576
3763
  var SLUG_PROPERTY_NAME = "slug";
3764
+ var READ_ACTION_TYPE = "READ";
3765
+ var ACTION_TYPE_IMPORT_NAME = "ActionType";
3766
+ var TOOLKIT_EVENTS_MODULE2 = "@opencrvs/toolkit/events";
3767
+ var READ_ACTION_FALLBACK_INITIALIZER = `{ type: ${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE} }`;
3577
3768
  var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
3578
3769
  "READ",
3579
3770
  "DELETE",
@@ -3621,91 +3812,137 @@ function isUnsupportedActionType(action) {
3621
3812
  }
3622
3813
  return !SUPPORTED_WORKQUEUE_ACTION_TYPES.has(typeName);
3623
3814
  }
3624
- function migrateWorkqueueActions(workqueue, relPath) {
3625
- const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3626
- if (!actionsProperty || !import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3627
- return 0;
3628
- }
3629
- const actionsInitializer = actionsProperty.getInitializer();
3630
- if (!actionsInitializer || !import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3631
- return 0;
3815
+ function ensureValidAction(workqueue, relPath) {
3816
+ const workqueueLabel = getWorkqueueLabel(workqueue);
3817
+ const actionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3818
+ if (actionProperty && import_ts_morph17.Node.isPropertyAssignment(actionProperty)) {
3819
+ const initializer = actionProperty.getInitializer();
3820
+ if (initializer && import_ts_morph17.Node.isObjectLiteralExpression(initializer)) {
3821
+ if (!isUnsupportedActionType(initializer)) {
3822
+ return { changed: false, usedReadFallback: false };
3823
+ }
3824
+ actionProperty.set({
3825
+ name: ACTION_PROPERTY_NAME2,
3826
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3827
+ });
3828
+ console.log(
3829
+ ` [${relPath}] Replaced unsupported 'action' type with '${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE}' on workqueue '${workqueueLabel}'`
3830
+ );
3831
+ return { changed: true, usedReadFallback: true };
3832
+ }
3632
3833
  }
3834
+ workqueue.addPropertyAssignment({
3835
+ name: ACTION_PROPERTY_NAME2,
3836
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3837
+ });
3838
+ console.log(
3839
+ ` [${relPath}] Added default 'action: ${READ_ACTION_FALLBACK_INITIALIZER}' to workqueue '${workqueueLabel}'`
3840
+ );
3841
+ return { changed: true, usedReadFallback: true };
3842
+ }
3843
+ function migrateWorkqueueActions(workqueue, relPath) {
3633
3844
  const workqueueLabel = getWorkqueueLabel(workqueue);
3634
- const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3635
- if (existingActionProperty) {
3636
- if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3637
- const existingActionInitializer = existingActionProperty.getInitializer();
3638
- if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3639
- const conditionalsProperty2 = existingActionInitializer.getProperty(
3640
- CONDITIONALS_PROPERTY_NAME
3845
+ const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3846
+ let actionsArrayMigrated = false;
3847
+ if (actionsProperty && import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3848
+ const actionsInitializer = actionsProperty.getInitializer();
3849
+ if (actionsInitializer && import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3850
+ const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3851
+ if (existingActionProperty) {
3852
+ if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3853
+ const existingActionInitializer = existingActionProperty.getInitializer();
3854
+ if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3855
+ const conditionalsProperty = existingActionInitializer.getProperty(
3856
+ CONDITIONALS_PROPERTY_NAME
3857
+ );
3858
+ if (conditionalsProperty) {
3859
+ conditionalsProperty.remove();
3860
+ console.log(
3861
+ ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3862
+ );
3863
+ }
3864
+ }
3865
+ }
3866
+ actionsProperty.remove();
3867
+ console.log(
3868
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3869
+ );
3870
+ actionsArrayMigrated = true;
3871
+ } else {
3872
+ const actionCandidates = actionsInitializer.getElements().filter(
3873
+ (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3641
3874
  );
3642
- if (conditionalsProperty2) {
3643
- conditionalsProperty2.remove();
3875
+ if (actionCandidates.length === 0) {
3876
+ actionsProperty.remove();
3644
3877
  console.log(
3645
- ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3878
+ ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3646
3879
  );
3647
- }
3648
- if (isUnsupportedActionType(existingActionInitializer)) {
3649
- existingActionProperty.remove();
3650
- console.log(
3651
- ` [${relPath}] Removed unsupported 'action' type on workqueue '${workqueueLabel}'`
3880
+ actionsArrayMigrated = true;
3881
+ } else {
3882
+ if (actionCandidates.length > 1) {
3883
+ const droppedCount = actionCandidates.length - 1;
3884
+ console.warn(
3885
+ ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3886
+ );
3887
+ }
3888
+ const conditionalsProperty = actionCandidates[0].getProperty(
3889
+ CONDITIONALS_PROPERTY_NAME
3652
3890
  );
3891
+ if (conditionalsProperty) {
3892
+ conditionalsProperty.remove();
3893
+ console.log(
3894
+ ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3895
+ );
3896
+ }
3897
+ if (isUnsupportedActionType(actionCandidates[0])) {
3898
+ actionsProperty.remove();
3899
+ console.log(
3900
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3901
+ );
3902
+ actionsArrayMigrated = true;
3903
+ } else {
3904
+ const firstActionText = actionCandidates[0].getText();
3905
+ workqueue.addPropertyAssignment({
3906
+ name: ACTION_PROPERTY_NAME2,
3907
+ initializer: firstActionText
3908
+ });
3909
+ actionsProperty.remove();
3910
+ console.log(
3911
+ ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3912
+ );
3913
+ actionsArrayMigrated = true;
3914
+ }
3653
3915
  }
3654
3916
  }
3655
3917
  }
3656
- actionsProperty.remove();
3657
- console.log(
3658
- ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3659
- );
3660
- return 1;
3661
- }
3662
- const actionCandidates = actionsInitializer.getElements().filter(
3663
- (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3664
- );
3665
- if (actionCandidates.length === 0) {
3666
- actionsProperty.remove();
3667
- console.log(
3668
- ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3669
- );
3670
- return 1;
3671
- }
3672
- if (actionCandidates.length > 1) {
3673
- const droppedCount = actionCandidates.length - 1;
3674
- console.warn(
3675
- ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3676
- );
3677
3918
  }
3678
- const conditionalsProperty = actionCandidates[0].getProperty(
3679
- CONDITIONALS_PROPERTY_NAME
3919
+ const fallback = ensureValidAction(workqueue, relPath);
3920
+ return {
3921
+ changed: actionsArrayMigrated || fallback.changed,
3922
+ usedReadFallback: fallback.usedReadFallback
3923
+ };
3924
+ }
3925
+ function ensureActionTypeImport(sourceFile) {
3926
+ const existingImport = sourceFile.getImportDeclaration(
3927
+ (decl) => decl.getModuleSpecifierValue() === TOOLKIT_EVENTS_MODULE2
3680
3928
  );
3681
- if (conditionalsProperty) {
3682
- conditionalsProperty.remove();
3683
- console.log(
3684
- ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3685
- );
3686
- }
3687
- if (isUnsupportedActionType(actionCandidates[0])) {
3688
- actionsProperty.remove();
3689
- console.log(
3690
- ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3691
- );
3692
- return 1;
3929
+ if (existingImport) {
3930
+ const alreadyImported = existingImport.getNamedImports().some((ni) => ni.getName() === ACTION_TYPE_IMPORT_NAME);
3931
+ if (!alreadyImported) {
3932
+ existingImport.addNamedImport(ACTION_TYPE_IMPORT_NAME);
3933
+ }
3934
+ return;
3693
3935
  }
3694
- const firstActionText = actionCandidates[0].getText();
3695
- workqueue.addPropertyAssignment({
3696
- name: ACTION_PROPERTY_NAME2,
3697
- initializer: firstActionText
3936
+ sourceFile.addImportDeclaration({
3937
+ moduleSpecifier: TOOLKIT_EVENTS_MODULE2,
3938
+ namedImports: [ACTION_TYPE_IMPORT_NAME]
3698
3939
  });
3699
- actionsProperty.remove();
3700
- console.log(
3701
- ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3702
- );
3703
- return 1;
3704
3940
  }
3705
3941
  function processFile13(filePath, project) {
3706
3942
  const sourceFile = project.getSourceFile(filePath);
3707
3943
  if (!sourceFile) return 0;
3708
3944
  let migratedWorkqueues = 0;
3945
+ let needsActionTypeImport = false;
3709
3946
  const relPath = import_path18.default.relative(process.cwd(), filePath);
3710
3947
  const callExpressions = sourceFile.getDescendantsOfKind(
3711
3948
  import_ts_morph17.SyntaxKind.CallExpression
@@ -3721,9 +3958,14 @@ function processFile13(filePath, project) {
3721
3958
  if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
3722
3959
  for (const element of workqueuesArg.getElements()) {
3723
3960
  if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
3724
- migratedWorkqueues += migrateWorkqueueActions(element, relPath);
3961
+ const result = migrateWorkqueueActions(element, relPath);
3962
+ if (result.changed) migratedWorkqueues++;
3963
+ if (result.usedReadFallback) needsActionTypeImport = true;
3725
3964
  }
3726
3965
  }
3966
+ if (needsActionTypeImport) {
3967
+ ensureActionTypeImport(sourceFile);
3968
+ }
3727
3969
  return migratedWorkqueues;
3728
3970
  }
3729
3971
  async function main18() {
@@ -4058,7 +4300,7 @@ Saved ${handlerRelPath}.`);
4058
4300
  // src/migrations/v2.0/checkout-upstream-files.ts
4059
4301
  var import_child_process = require("child_process");
4060
4302
  var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
4061
- var UPSTREAM_BRANCH = "develop";
4303
+ var UPSTREAM_BRANCH = "release-v2.0.0";
4062
4304
  var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
4063
4305
  var FILES_TO_CHECKOUT = [
4064
4306
  "src/analytics/analytics.ts",
@@ -4597,9 +4839,9 @@ var import_path24 = require("path");
4597
4839
  var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
4598
4840
  var INFRASTRUCTURE_DIR = "infrastructure";
4599
4841
  var BASE_BRANCH = "release-v1.9";
4600
- function gitShowBytes(ref, path23) {
4842
+ function gitShowBytes(ref, path24) {
4601
4843
  try {
4602
- const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path23}`], {
4844
+ const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path24}`], {
4603
4845
  cwd: process.cwd(),
4604
4846
  stdio: ["ignore", "pipe", "pipe"]
4605
4847
  });
@@ -4608,9 +4850,13 @@ function gitShowBytes(ref, path23) {
4608
4850
  return null;
4609
4851
  }
4610
4852
  }
4611
- async function main25() {
4853
+ async function main25({
4854
+ subdirs = []
4855
+ } = {}) {
4856
+ const pathspecs = subdirs.length > 0 ? subdirs.map((s) => `${INFRASTRUCTURE_DIR}/${s}/`) : [`${INFRASTRUCTURE_DIR}/`];
4857
+ const scopeLabel = pathspecs.map((p) => `'${p}'`).join(", ");
4612
4858
  console.log(
4613
- `Merging '${INFRASTRUCTURE_DIR}/' from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4859
+ `Merging ${scopeLabel} from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4614
4860
  `
4615
4861
  );
4616
4862
  assertIsGitRepo();
@@ -4625,20 +4871,12 @@ async function main25() {
4625
4871
  const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
4626
4872
  const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
4627
4873
  const upstreamFiles = runGit(
4628
- [
4629
- "ls-tree",
4630
- "-r",
4631
- "--name-only",
4632
- "-z",
4633
- ref,
4634
- "--",
4635
- `${INFRASTRUCTURE_DIR}/`
4636
- ],
4874
+ ["ls-tree", "-r", "--name-only", "-z", ref, "--", ...pathspecs],
4637
4875
  { silent: true }
4638
4876
  ).split("\0").filter(Boolean);
4639
4877
  if (upstreamFiles.length === 0) {
4640
4878
  console.log(
4641
- ` No files found under '${INFRASTRUCTURE_DIR}/' on ${ref}. Nothing to merge.`
4879
+ ` No files found under ${scopeLabel} on ${ref}. Nothing to merge.`
4642
4880
  );
4643
4881
  return;
4644
4882
  }
@@ -4713,7 +4951,9 @@ async function main25() {
4713
4951
  var import_fs6 = require("fs");
4714
4952
  var import_path25 = require("path");
4715
4953
  var INFRASTRUCTURE_DIR2 = "infrastructure";
4716
- async function main26() {
4954
+ async function main26({
4955
+ keep = []
4956
+ } = {}) {
4717
4957
  const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
4718
4958
  if (!(0, import_fs6.existsSync)(fullPath)) {
4719
4959
  console.log(
@@ -4721,24 +4961,152 @@ async function main26() {
4721
4961
  );
4722
4962
  return;
4723
4963
  }
4724
- console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4725
- `);
4726
4964
  assertIsGitRepo();
4727
- runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4728
- silent: true
4965
+ if (keep.length === 0) {
4966
+ console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4967
+ `);
4968
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4969
+ silent: true
4970
+ });
4971
+ console.log(
4972
+ ` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`
4973
+ );
4974
+ if ((0, import_fs6.existsSync)(fullPath)) {
4975
+ (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4976
+ console.log(
4977
+ ` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`
4978
+ );
4979
+ }
4980
+ console.log(
4981
+ `
4982
+ Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
4983
+ );
4984
+ return;
4985
+ }
4986
+ const keepSet = new Set(keep);
4987
+ console.log(
4988
+ `Deleting contents of '${INFRASTRUCTURE_DIR2}/' except: ${[...keepSet].map((name) => `'${name}'`).join(", ")}
4989
+ `
4990
+ );
4991
+ const topLevelEntries = (0, import_fs6.readdirSync)(fullPath, { withFileTypes: true });
4992
+ let deleted = 0;
4993
+ for (const entry of topLevelEntries) {
4994
+ if (keepSet.has(entry.name)) {
4995
+ console.log(` Keeping '${INFRASTRUCTURE_DIR2}/${entry.name}'.`);
4996
+ continue;
4997
+ }
4998
+ const relPath = `${INFRASTRUCTURE_DIR2}/${entry.name}${entry.isDirectory() ? "/" : ""}`;
4999
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", relPath], { silent: true });
5000
+ const fullEntryPath = (0, import_path25.join)(fullPath, entry.name);
5001
+ if ((0, import_fs6.existsSync)(fullEntryPath)) {
5002
+ (0, import_fs6.rmSync)(fullEntryPath, { recursive: true, force: true });
5003
+ }
5004
+ console.log(` Deleted '${relPath}'.`);
5005
+ deleted++;
5006
+ }
5007
+ const presentNames = new Set(topLevelEntries.map((e) => e.name));
5008
+ const missingKeeps = [...keepSet].filter((name) => !presentNames.has(name));
5009
+ if (missingKeeps.length > 0) {
5010
+ console.warn(
5011
+ ` [warn] Requested to keep ${missingKeeps.map((n) => `'${n}'`).join(", ")} but they don't exist under '${INFRASTRUCTURE_DIR2}/'.`
5012
+ );
5013
+ }
5014
+ console.log(
5015
+ `
5016
+ Done. Deleted ${deleted} top-level entr${deleted === 1 ? "y" : "ies"}. Review the staged deletions with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5017
+ );
5018
+ }
5019
+
5020
+ // src/migrations/v2.0/fix-accept-requested-registration-function-type.ts
5021
+ var import_ts_morph22 = require("ts-morph");
5022
+ var import_path26 = __toESM(require("path"));
5023
+ var TARGET_FUNCTION_NAME = "acceptRequestedRegistration";
5024
+ var ACTION_PARAMETER_NAME = "action";
5025
+ var OLD_TYPE = "ActionInput";
5026
+ var NEW_TYPE = "Extract<ActionInput, { type?: 'REGISTER' }>";
5027
+ function isAcceptRequestedRegistrationParameter(parameter) {
5028
+ const functionDeclaration = parameter.getFirstAncestorByKind(
5029
+ import_ts_morph22.SyntaxKind.FunctionDeclaration
5030
+ );
5031
+ if (functionDeclaration?.getName() === TARGET_FUNCTION_NAME) {
5032
+ return true;
5033
+ }
5034
+ const arrowFunction = parameter.getFirstAncestorByKind(
5035
+ import_ts_morph22.SyntaxKind.ArrowFunction
5036
+ );
5037
+ const variableDeclaration = arrowFunction?.getFirstAncestorByKind(
5038
+ import_ts_morph22.SyntaxKind.VariableDeclaration
5039
+ );
5040
+ return variableDeclaration?.getName() === TARGET_FUNCTION_NAME;
5041
+ }
5042
+ function processFile14(filePath, project) {
5043
+ const sourceFile = project.getSourceFile(filePath);
5044
+ if (!sourceFile) return 0;
5045
+ let updatedCount = 0;
5046
+ for (const parameter of sourceFile.getDescendantsOfKind(
5047
+ import_ts_morph22.SyntaxKind.Parameter
5048
+ )) {
5049
+ if (parameter.getName() !== ACTION_PARAMETER_NAME) {
5050
+ continue;
5051
+ }
5052
+ if (!isAcceptRequestedRegistrationParameter(parameter)) {
5053
+ continue;
5054
+ }
5055
+ const typeNode = parameter.getTypeNode();
5056
+ if (!typeNode || typeNode.getText() !== OLD_TYPE) {
5057
+ continue;
5058
+ }
5059
+ parameter.setType(NEW_TYPE);
5060
+ updatedCount++;
5061
+ console.log(
5062
+ ` [${import_path26.default.relative(process.cwd(), filePath)}] Updated '${ACTION_PARAMETER_NAME}' in ${TARGET_FUNCTION_NAME} to ${NEW_TYPE}`
5063
+ );
5064
+ }
5065
+ return updatedCount;
5066
+ }
5067
+ async function main27() {
5068
+ const srcDir = import_path26.default.join(process.cwd(), "src");
5069
+ console.log(`Scanning for ${TARGET_FUNCTION_NAME} in: ${srcDir}
5070
+ `);
5071
+ const project = new import_ts_morph22.Project({
5072
+ tsConfigFilePath: import_path26.default.resolve(srcDir, "../tsconfig.json"),
5073
+ skipAddingFilesFromTsConfig: false
4729
5074
  });
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}/'.`);
5075
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
5076
+ const filePath = sf.getFilePath();
5077
+ return filePath.includes("/src/") && !filePath.includes("/node_modules/");
5078
+ });
5079
+ let totalUpdated = 0;
5080
+ const modifiedFiles = [];
5081
+ for (const sourceFile of sourceFiles) {
5082
+ const filePath = sourceFile.getFilePath();
5083
+ const updated = processFile14(filePath, project);
5084
+ if (updated > 0) {
5085
+ totalUpdated += updated;
5086
+ modifiedFiles.push(filePath);
5087
+ }
5088
+ }
5089
+ if (modifiedFiles.length === 0) {
5090
+ console.log(
5091
+ `No '${ACTION_PARAMETER_NAME}: ${OLD_TYPE}' parameter in ${TARGET_FUNCTION_NAME} found. Nothing to do.`
5092
+ );
5093
+ return;
5094
+ }
5095
+ console.log(`
5096
+ Saving ${modifiedFiles.length} modified file(s)...`);
5097
+ for (const filePath of modifiedFiles) {
5098
+ const sourceFile = project.getSourceFileOrThrow(filePath);
5099
+ await sourceFile.save();
5100
+ console.log(` Saved: ${import_path26.default.relative(process.cwd(), filePath)}`);
4734
5101
  }
4735
5102
  console.log(
4736
5103
  `
4737
- Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5104
+ Done. Updated ${totalUpdated} parameter type annotation(s) in ${TARGET_FUNCTION_NAME}.`
4738
5105
  );
4739
5106
  }
4740
5107
 
4741
5108
  // src/migrations/v2.0/index.ts
5109
+ var NON_SWARM_INFRA_SUBDIRS = ["postgres", "metabase"];
4742
5110
  async function runUpgrade(dockerSwarm) {
4743
5111
  await main18();
4744
5112
  await main();
@@ -4763,11 +5131,13 @@ async function runUpgrade(dockerSwarm) {
4763
5131
  await main22();
4764
5132
  await main23();
4765
5133
  await main24();
5134
+ await main27();
4766
5135
  await main17();
4767
5136
  if (dockerSwarm) {
4768
5137
  await main25();
4769
5138
  } else {
4770
- await main26();
5139
+ await main26({ keep: NON_SWARM_INFRA_SUBDIRS });
5140
+ await main25({ subdirs: NON_SWARM_INFRA_SUBDIRS });
4771
5141
  }
4772
5142
  }
4773
5143
 
@@ -4796,7 +5166,7 @@ Options:
4796
5166
  'infrastructure/' directory is deleted (default).
4797
5167
  -h, --help Show this message.
4798
5168
  `.trim();
4799
- function main27() {
5169
+ function main28() {
4800
5170
  const command = args[0];
4801
5171
  if (!command || command === "--help" || command === "-h") {
4802
5172
  console.log(USAGE);
@@ -4871,4 +5241,4 @@ function handleCheckTranslations() {
4871
5241
  console.warn("This command is not implemented yet!");
4872
5242
  process.exit(1);
4873
5243
  }
4874
- main27();
5244
+ main28();