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

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 (52) hide show
  1. package/create-countryconfig/index.js +73 -0
  2. package/create-countryconfig/package.json +11 -0
  3. package/dist/application-config/index.js +68 -39
  4. package/dist/cli.js +539 -175
  5. package/dist/commons/api/router.d.ts +3334 -271
  6. package/dist/commons/application-config/index.d.ts +16 -4
  7. package/dist/commons/conditionals/conditionals.d.ts +6 -6
  8. package/dist/commons/conditionals/validate.d.ts +1 -0
  9. package/dist/commons/events/ActionInput.d.ts +108 -0
  10. package/dist/commons/events/Draft.d.ts +3 -0
  11. package/dist/commons/events/EventIndex.d.ts +0 -3
  12. package/dist/commons/events/EventMetadata.d.ts +0 -7
  13. package/dist/commons/events/FieldConfig.d.ts +10 -0
  14. package/dist/commons/events/WorkqueueConfig.d.ts +146 -146
  15. package/dist/commons/events/locations.d.ts +38 -1
  16. package/dist/commons/events/mocks.test.utils.d.ts +19 -0
  17. package/dist/commons/events/scopes.d.ts +24 -1
  18. package/dist/commons/events/state/index.d.ts +0 -4
  19. package/dist/commons/events/state/utils.d.ts +0 -2
  20. package/dist/commons/events/utils.d.ts +1 -1
  21. package/dist/conditionals/index.js +40 -8
  22. package/dist/events/index.js +184 -83
  23. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.d.ts.map +1 -1
  24. package/dist/migrations/v2.0/add-birth-certificate-issuance-flag.js +60 -35
  25. package/dist/migrations/v2.0/checkout-upstream-files.d.ts +1 -1
  26. package/dist/migrations/v2.0/checkout-upstream-files.d.ts.map +1 -1
  27. package/dist/migrations/v2.0/checkout-upstream-files.js +1 -2
  28. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts +9 -1
  29. package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts.map +1 -1
  30. package/dist/migrations/v2.0/delete-infrastructure-directory.js +66 -20
  31. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts +3 -0
  32. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts.map +1 -0
  33. package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.js +110 -0
  34. package/dist/migrations/v2.0/index.d.ts +5 -3
  35. package/dist/migrations/v2.0/index.d.ts.map +1 -1
  36. package/dist/migrations/v2.0/index.js +537 -173
  37. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.d.ts.map +1 -1
  38. package/dist/migrations/v2.0/make-built-in-validate-actions-custom.js +90 -1
  39. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts +11 -1
  40. package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts.map +1 -1
  41. package/dist/migrations/v2.0/merge-infrastructure-directory.js +17 -16
  42. package/dist/migrations/v2.0/migrate-scopes.d.ts.map +1 -1
  43. package/dist/migrations/v2.0/migrate-scopes.js +54 -30
  44. package/dist/migrations/v2.0/migrate-workqueue-configs.d.ts.map +1 -1
  45. package/dist/migrations/v2.0/migrate-workqueue-configs.js +140 -59
  46. package/dist/notification/index.js +78 -53
  47. package/dist/scopes/index.d.ts +87 -10
  48. package/dist/scopes/index.js +46 -9
  49. package/opencrvs-toolkit-2.0.0-rc.ff04b30.tgz +0 -0
  50. package/package.json +1 -1
  51. package/tsconfig.tsbuildinfo +1 -1
  52. 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
@@ -2301,11 +2405,12 @@ var decodeScope = (query) => {
2301
2405
  };
2302
2406
  var DEFAULT_SCOPE_OPTIONS = {
2303
2407
  placeOfEvent: JurisdictionFilter.enum.all,
2304
- accessLevel: JurisdictionFilter.enum.all
2408
+ accessLevel: JurisdictionFilter.enum.all,
2409
+ registeredIn: JurisdictionFilter.enum.all,
2410
+ declaredIn: JurisdictionFilter.enum.all
2305
2411
  };
2306
2412
 
2307
2413
  // ../commons/src/scopes.deprecated.do-not-use.ts
2308
- var z4 = __toESM(require("zod/v4"));
2309
2414
  var rawConfigurableScopeRegex = /^([a-zA-Z][a-zA-Z0-9.-]*(?:\.[a-zA-Z0-9.-]+)*)\[((?:\w+=[\w.-]+(?:\|[\w.-]+)*)(?:,[\w]+=[\w.-]+(?:\|[\w.-]+)*)*)\]$/;
2310
2415
  var rawConfigurableScope = z4.string().regex(rawConfigurableScopeRegex);
2311
2416
  var SearchScope = z4.object({
@@ -2539,6 +2644,74 @@ var legacyScopeToV2Scope = (v1Scope) => {
2539
2644
  }
2540
2645
  throw new Error(`Unsupported V1 scope type: ${v1Scope}`);
2541
2646
  };
2647
+ var MERGEABLE_USER_SCOPE_TYPES = ["user.create", "user.edit"];
2648
+ function isMergeableUserScopeType(type) {
2649
+ return MERGEABLE_USER_SCOPE_TYPES.includes(type);
2650
+ }
2651
+ function decodeLegacyScope(legacy) {
2652
+ const encoded = legacyScopeToV2Scope(legacy);
2653
+ const scope = decodeScope(encoded);
2654
+ if (!scope) {
2655
+ throw new Error(
2656
+ `Could not decode migrated scope from legacy '${legacy}' (encoded: '${encoded}').`
2657
+ );
2658
+ }
2659
+ return { legacy, scope };
2660
+ }
2661
+ function groupByType(entries) {
2662
+ return entries.reduce(
2663
+ (groups, entry) => ({
2664
+ ...groups,
2665
+ [entry.scope.type]: [...groups[entry.scope.type] ?? [], entry]
2666
+ }),
2667
+ {}
2668
+ );
2669
+ }
2670
+ function deduplicateEntries(entries) {
2671
+ return entries.filter(
2672
+ (entry, index) => entries.findIndex((e) => (0, import_lodash.isEqual)(e.scope, entry.scope)) === index
2673
+ );
2674
+ }
2675
+ function mergeEntries(type, entries) {
2676
+ const optionsList = entries.map(
2677
+ (e) => "options" in e.scope && e.scope.options ? e.scope.options : {}
2678
+ );
2679
+ const allKeys = optionsList.flatMap(
2680
+ (opts) => Object.keys(opts).filter((k) => opts[k] !== void 0)
2681
+ );
2682
+ const conflictKey = allKeys.find((key, i) => allKeys.indexOf(key) !== i);
2683
+ if (conflictKey) {
2684
+ const conflicting = entries.filter(
2685
+ (e) => conflictKey in ("options" in e.scope && e.scope.options || {})
2686
+ ).map((e) => JSON.stringify(e.legacy));
2687
+ throw new Error(
2688
+ `Cannot auto-merge two '${type}' scopes that both define '${conflictKey}'. Conflicting legacy scopes: [${conflicting.join(", ")}]. Resolve manually in roles.ts and re-run.`
2689
+ );
2690
+ }
2691
+ const mergedOptions = Object.assign({}, ...optionsList);
2692
+ const merged = Object.keys(mergedOptions).length > 0 ? { type, options: mergedOptions } : { type };
2693
+ const parsed = Scope2.safeParse(merged);
2694
+ if (!parsed.success) {
2695
+ throw new Error(
2696
+ `Merged '${type}' scope failed schema validation: ${parsed.error.message}. Legacy scopes: [${entries.map((e) => JSON.stringify(e.legacy)).join(", ")}].`
2697
+ );
2698
+ }
2699
+ return parsed.data;
2700
+ }
2701
+ function migrateLegacyScopesArrayToV2Scopes(legacyScopes) {
2702
+ const groups = groupByType(legacyScopes.map(decodeLegacyScope));
2703
+ return Object.entries(groups).flatMap(([type, entries]) => {
2704
+ const deduped = deduplicateEntries(entries);
2705
+ if (deduped.length <= 1) return deduped.map((e) => e.scope);
2706
+ if (!isMergeableUserScopeType(type)) {
2707
+ console.warn(
2708
+ `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.`
2709
+ );
2710
+ return deduped.map((e) => e.scope);
2711
+ }
2712
+ return [mergeEntries(type, deduped)];
2713
+ });
2714
+ }
2542
2715
 
2543
2716
  // src/migrations/v2.0/migrate-scopes.ts
2544
2717
  var ROLES_FILE_RELATIVE_PATH = "src/data-seeding/roles/roles.ts";
@@ -2576,57 +2749,65 @@ function resolveLegacyScopeFromElement(element) {
2576
2749
  }
2577
2750
  function migrateScopesArrays(rolesFilePath, project) {
2578
2751
  const sourceFile = project.getSourceFile(rolesFilePath);
2579
- if (!sourceFile) return { migratedCount: 0, removedCount: 0 };
2752
+ if (!sourceFile) {
2753
+ return { migratedCount: 0, removedCount: 0, mergedCount: 0 };
2754
+ }
2580
2755
  let migratedCount = 0;
2581
2756
  let removedCount = 0;
2757
+ let mergedCount = 0;
2582
2758
  const scopesProperties = sourceFile.getDescendantsOfKind(import_ts_morph11.SyntaxKind.PropertyAssignment).filter((prop) => prop.getName() === SCOPES_PROPERTY_NAME);
2759
+ const relPath = import_path11.default.relative(process.cwd(), rolesFilePath);
2583
2760
  for (const scopesProp of scopesProperties) {
2584
2761
  const initializer = scopesProp.getInitializer();
2585
2762
  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;
2763
+ const rawTexts = [];
2764
+ const legacyStrings = [];
2765
+ let anyChange = false;
2766
+ for (const element of initializer.getElements()) {
2590
2767
  const scopeKey = resolveScopeKeyFromElementAccess(element);
2591
2768
  if (scopeKey && !(scopeKey in SCOPES)) {
2592
2769
  console.warn(
2593
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2770
+ ` [${relPath}] Removing unknown scope reference: SCOPES.${scopeKey}`
2594
2771
  );
2595
- initializer.removeElement(index);
2596
2772
  removedCount++;
2773
+ anyChange = true;
2597
2774
  continue;
2598
2775
  }
2599
2776
  const legacyScope = resolveLegacyScopeFromElement(element);
2600
2777
  if (!legacyScope) {
2601
2778
  console.warn(
2602
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2779
+ ` [${relPath}] Preserving non-literal scope expression verbatim: ${element.getText()}`
2603
2780
  );
2781
+ rawTexts.push(element.getText());
2604
2782
  continue;
2605
2783
  }
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
- }
2784
+ legacyStrings.push(legacyScope);
2785
+ anyChange = true;
2786
+ }
2787
+ if (!anyChange) continue;
2788
+ let mergedScopes;
2789
+ try {
2790
+ mergedScopes = migrateLegacyScopesArrayToV2Scopes(legacyStrings);
2791
+ } catch (error) {
2792
+ throw new Error(
2793
+ `[${relPath}] Failed to migrate scopes for role: ${error instanceof Error ? error.message : String(error)}`,
2794
+ { cause: error }
2795
+ );
2622
2796
  }
2797
+ migratedCount += legacyStrings.length;
2798
+ mergedCount += legacyStrings.length - mergedScopes.length;
2799
+ const outputElements = [
2800
+ ...rawTexts,
2801
+ ...mergedScopes.map((s) => JSON.stringify(s))
2802
+ ];
2803
+ scopesProp.setInitializer(`[${outputElements.join(", ")}]`);
2623
2804
  }
2624
2805
  if (removedCount > 0) {
2625
2806
  console.log(
2626
- ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(process.cwd(), rolesFilePath)}'.`
2807
+ ` Removed ${removedCount} unknown SCOPES reference(s) from '${relPath}'.`
2627
2808
  );
2628
2809
  }
2629
- return { migratedCount, removedCount };
2810
+ return { migratedCount, removedCount, mergedCount };
2630
2811
  }
2631
2812
  function ensureScopesHelpersImport(rolesFilePath, project) {
2632
2813
  const sourceFile = project.getSourceFile(rolesFilePath);
@@ -2761,7 +2942,7 @@ async function main11() {
2761
2942
  return;
2762
2943
  }
2763
2944
  const importChanged = ensureScopesHelpersImport(rolesFilePath, project);
2764
- const { migratedCount, removedCount } = migrateScopesArrays(
2945
+ const { migratedCount, removedCount, mergedCount } = migrateScopesArrays(
2765
2946
  rolesFilePath,
2766
2947
  project
2767
2948
  );
@@ -2780,7 +2961,7 @@ async function main11() {
2780
2961
  }
2781
2962
  await sourceFile.save();
2782
2963
  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." : "."}`
2964
+ `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
2965
  );
2785
2966
  }
2786
2967
 
@@ -3586,6 +3767,10 @@ var ACTION_PROPERTY_NAME2 = "action";
3586
3767
  var CONDITIONALS_PROPERTY_NAME = "conditionals";
3587
3768
  var TYPE_PROPERTY_NAME7 = "type";
3588
3769
  var SLUG_PROPERTY_NAME = "slug";
3770
+ var READ_ACTION_TYPE = "READ";
3771
+ var ACTION_TYPE_IMPORT_NAME = "ActionType";
3772
+ var TOOLKIT_EVENTS_MODULE2 = "@opencrvs/toolkit/events";
3773
+ var READ_ACTION_FALLBACK_INITIALIZER = `{ type: ${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE} }`;
3589
3774
  var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
3590
3775
  "READ",
3591
3776
  "DELETE",
@@ -3633,91 +3818,137 @@ function isUnsupportedActionType(action) {
3633
3818
  }
3634
3819
  return !SUPPORTED_WORKQUEUE_ACTION_TYPES.has(typeName);
3635
3820
  }
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;
3821
+ function ensureValidAction(workqueue, relPath) {
3822
+ const workqueueLabel = getWorkqueueLabel(workqueue);
3823
+ const actionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3824
+ if (actionProperty && import_ts_morph17.Node.isPropertyAssignment(actionProperty)) {
3825
+ const initializer = actionProperty.getInitializer();
3826
+ if (initializer && import_ts_morph17.Node.isObjectLiteralExpression(initializer)) {
3827
+ if (!isUnsupportedActionType(initializer)) {
3828
+ return { changed: false, usedReadFallback: false };
3829
+ }
3830
+ actionProperty.set({
3831
+ name: ACTION_PROPERTY_NAME2,
3832
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3833
+ });
3834
+ console.log(
3835
+ ` [${relPath}] Replaced unsupported 'action' type with '${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE}' on workqueue '${workqueueLabel}'`
3836
+ );
3837
+ return { changed: true, usedReadFallback: true };
3838
+ }
3644
3839
  }
3840
+ workqueue.addPropertyAssignment({
3841
+ name: ACTION_PROPERTY_NAME2,
3842
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3843
+ });
3844
+ console.log(
3845
+ ` [${relPath}] Added default 'action: ${READ_ACTION_FALLBACK_INITIALIZER}' to workqueue '${workqueueLabel}'`
3846
+ );
3847
+ return { changed: true, usedReadFallback: true };
3848
+ }
3849
+ function migrateWorkqueueActions(workqueue, relPath) {
3645
3850
  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
3851
+ const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3852
+ let actionsArrayMigrated = false;
3853
+ if (actionsProperty && import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3854
+ const actionsInitializer = actionsProperty.getInitializer();
3855
+ if (actionsInitializer && import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3856
+ const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3857
+ if (existingActionProperty) {
3858
+ if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3859
+ const existingActionInitializer = existingActionProperty.getInitializer();
3860
+ if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3861
+ const conditionalsProperty = existingActionInitializer.getProperty(
3862
+ CONDITIONALS_PROPERTY_NAME
3863
+ );
3864
+ if (conditionalsProperty) {
3865
+ conditionalsProperty.remove();
3866
+ console.log(
3867
+ ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3868
+ );
3869
+ }
3870
+ }
3871
+ }
3872
+ actionsProperty.remove();
3873
+ console.log(
3874
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3653
3875
  );
3654
- if (conditionalsProperty2) {
3655
- conditionalsProperty2.remove();
3876
+ actionsArrayMigrated = true;
3877
+ } else {
3878
+ const actionCandidates = actionsInitializer.getElements().filter(
3879
+ (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3880
+ );
3881
+ if (actionCandidates.length === 0) {
3882
+ actionsProperty.remove();
3656
3883
  console.log(
3657
- ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3884
+ ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3658
3885
  );
3659
- }
3660
- if (isUnsupportedActionType(existingActionInitializer)) {
3661
- existingActionProperty.remove();
3662
- console.log(
3663
- ` [${relPath}] Removed unsupported 'action' type on workqueue '${workqueueLabel}'`
3886
+ actionsArrayMigrated = true;
3887
+ } else {
3888
+ if (actionCandidates.length > 1) {
3889
+ const droppedCount = actionCandidates.length - 1;
3890
+ console.warn(
3891
+ ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3892
+ );
3893
+ }
3894
+ const conditionalsProperty = actionCandidates[0].getProperty(
3895
+ CONDITIONALS_PROPERTY_NAME
3664
3896
  );
3897
+ if (conditionalsProperty) {
3898
+ conditionalsProperty.remove();
3899
+ console.log(
3900
+ ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3901
+ );
3902
+ }
3903
+ if (isUnsupportedActionType(actionCandidates[0])) {
3904
+ actionsProperty.remove();
3905
+ console.log(
3906
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3907
+ );
3908
+ actionsArrayMigrated = true;
3909
+ } else {
3910
+ const firstActionText = actionCandidates[0].getText();
3911
+ workqueue.addPropertyAssignment({
3912
+ name: ACTION_PROPERTY_NAME2,
3913
+ initializer: firstActionText
3914
+ });
3915
+ actionsProperty.remove();
3916
+ console.log(
3917
+ ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3918
+ );
3919
+ actionsArrayMigrated = true;
3920
+ }
3665
3921
  }
3666
3922
  }
3667
3923
  }
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
3924
  }
3690
- const conditionalsProperty = actionCandidates[0].getProperty(
3691
- CONDITIONALS_PROPERTY_NAME
3925
+ const fallback = ensureValidAction(workqueue, relPath);
3926
+ return {
3927
+ changed: actionsArrayMigrated || fallback.changed,
3928
+ usedReadFallback: fallback.usedReadFallback
3929
+ };
3930
+ }
3931
+ function ensureActionTypeImport(sourceFile) {
3932
+ const existingImport = sourceFile.getImportDeclaration(
3933
+ (decl) => decl.getModuleSpecifierValue() === TOOLKIT_EVENTS_MODULE2
3692
3934
  );
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;
3935
+ if (existingImport) {
3936
+ const alreadyImported = existingImport.getNamedImports().some((ni) => ni.getName() === ACTION_TYPE_IMPORT_NAME);
3937
+ if (!alreadyImported) {
3938
+ existingImport.addNamedImport(ACTION_TYPE_IMPORT_NAME);
3939
+ }
3940
+ return;
3705
3941
  }
3706
- const firstActionText = actionCandidates[0].getText();
3707
- workqueue.addPropertyAssignment({
3708
- name: ACTION_PROPERTY_NAME2,
3709
- initializer: firstActionText
3942
+ sourceFile.addImportDeclaration({
3943
+ moduleSpecifier: TOOLKIT_EVENTS_MODULE2,
3944
+ namedImports: [ACTION_TYPE_IMPORT_NAME]
3710
3945
  });
3711
- actionsProperty.remove();
3712
- console.log(
3713
- ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3714
- );
3715
- return 1;
3716
3946
  }
3717
3947
  function processFile13(filePath, project) {
3718
3948
  const sourceFile = project.getSourceFile(filePath);
3719
3949
  if (!sourceFile) return 0;
3720
3950
  let migratedWorkqueues = 0;
3951
+ let needsActionTypeImport = false;
3721
3952
  const relPath = import_path18.default.relative(process.cwd(), filePath);
3722
3953
  const callExpressions = sourceFile.getDescendantsOfKind(
3723
3954
  import_ts_morph17.SyntaxKind.CallExpression
@@ -3733,9 +3964,14 @@ function processFile13(filePath, project) {
3733
3964
  if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
3734
3965
  for (const element of workqueuesArg.getElements()) {
3735
3966
  if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
3736
- migratedWorkqueues += migrateWorkqueueActions(element, relPath);
3967
+ const result = migrateWorkqueueActions(element, relPath);
3968
+ if (result.changed) migratedWorkqueues++;
3969
+ if (result.usedReadFallback) needsActionTypeImport = true;
3737
3970
  }
3738
3971
  }
3972
+ if (needsActionTypeImport) {
3973
+ ensureActionTypeImport(sourceFile);
3974
+ }
3739
3975
  return migratedWorkqueues;
3740
3976
  }
3741
3977
  async function main18() {
@@ -4070,7 +4306,7 @@ Saved ${handlerRelPath}.`);
4070
4306
  // src/migrations/v2.0/checkout-upstream-files.ts
4071
4307
  var import_child_process = require("child_process");
4072
4308
  var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
4073
- var UPSTREAM_BRANCH = "develop";
4309
+ var UPSTREAM_BRANCH = "release-v2.0.0";
4074
4310
  var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
4075
4311
  var FILES_TO_CHECKOUT = [
4076
4312
  "src/analytics/analytics.ts",
@@ -4609,9 +4845,9 @@ var import_path24 = require("path");
4609
4845
  var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
4610
4846
  var INFRASTRUCTURE_DIR = "infrastructure";
4611
4847
  var BASE_BRANCH = "release-v1.9";
4612
- function gitShowBytes(ref, path23) {
4848
+ function gitShowBytes(ref, path24) {
4613
4849
  try {
4614
- const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path23}`], {
4850
+ const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path24}`], {
4615
4851
  cwd: process.cwd(),
4616
4852
  stdio: ["ignore", "pipe", "pipe"]
4617
4853
  });
@@ -4620,9 +4856,13 @@ function gitShowBytes(ref, path23) {
4620
4856
  return null;
4621
4857
  }
4622
4858
  }
4623
- async function main25() {
4859
+ async function main25({
4860
+ subdirs = []
4861
+ } = {}) {
4862
+ const pathspecs = subdirs.length > 0 ? subdirs.map((s) => `${INFRASTRUCTURE_DIR}/${s}/`) : [`${INFRASTRUCTURE_DIR}/`];
4863
+ const scopeLabel = pathspecs.map((p) => `'${p}'`).join(", ");
4624
4864
  console.log(
4625
- `Merging '${INFRASTRUCTURE_DIR}/' from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4865
+ `Merging ${scopeLabel} from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4626
4866
  `
4627
4867
  );
4628
4868
  assertIsGitRepo();
@@ -4637,20 +4877,12 @@ async function main25() {
4637
4877
  const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
4638
4878
  const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
4639
4879
  const upstreamFiles = runGit(
4640
- [
4641
- "ls-tree",
4642
- "-r",
4643
- "--name-only",
4644
- "-z",
4645
- ref,
4646
- "--",
4647
- `${INFRASTRUCTURE_DIR}/`
4648
- ],
4880
+ ["ls-tree", "-r", "--name-only", "-z", ref, "--", ...pathspecs],
4649
4881
  { silent: true }
4650
4882
  ).split("\0").filter(Boolean);
4651
4883
  if (upstreamFiles.length === 0) {
4652
4884
  console.log(
4653
- ` No files found under '${INFRASTRUCTURE_DIR}/' on ${ref}. Nothing to merge.`
4885
+ ` No files found under ${scopeLabel} on ${ref}. Nothing to merge.`
4654
4886
  );
4655
4887
  return;
4656
4888
  }
@@ -4725,7 +4957,9 @@ async function main25() {
4725
4957
  var import_fs6 = require("fs");
4726
4958
  var import_path25 = require("path");
4727
4959
  var INFRASTRUCTURE_DIR2 = "infrastructure";
4728
- async function main26() {
4960
+ async function main26({
4961
+ keep = []
4962
+ } = {}) {
4729
4963
  const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
4730
4964
  if (!(0, import_fs6.existsSync)(fullPath)) {
4731
4965
  console.log(
@@ -4733,24 +4967,152 @@ async function main26() {
4733
4967
  );
4734
4968
  return;
4735
4969
  }
4736
- console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4737
- `);
4738
4970
  assertIsGitRepo();
4739
- runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4740
- silent: true
4971
+ if (keep.length === 0) {
4972
+ console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4973
+ `);
4974
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4975
+ silent: true
4976
+ });
4977
+ console.log(
4978
+ ` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`
4979
+ );
4980
+ if ((0, import_fs6.existsSync)(fullPath)) {
4981
+ (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4982
+ console.log(
4983
+ ` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`
4984
+ );
4985
+ }
4986
+ console.log(
4987
+ `
4988
+ Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
4989
+ );
4990
+ return;
4991
+ }
4992
+ const keepSet = new Set(keep);
4993
+ console.log(
4994
+ `Deleting contents of '${INFRASTRUCTURE_DIR2}/' except: ${[...keepSet].map((name) => `'${name}'`).join(", ")}
4995
+ `
4996
+ );
4997
+ const topLevelEntries = (0, import_fs6.readdirSync)(fullPath, { withFileTypes: true });
4998
+ let deleted = 0;
4999
+ for (const entry of topLevelEntries) {
5000
+ if (keepSet.has(entry.name)) {
5001
+ console.log(` Keeping '${INFRASTRUCTURE_DIR2}/${entry.name}'.`);
5002
+ continue;
5003
+ }
5004
+ const relPath = `${INFRASTRUCTURE_DIR2}/${entry.name}${entry.isDirectory() ? "/" : ""}`;
5005
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", relPath], { silent: true });
5006
+ const fullEntryPath = (0, import_path25.join)(fullPath, entry.name);
5007
+ if ((0, import_fs6.existsSync)(fullEntryPath)) {
5008
+ (0, import_fs6.rmSync)(fullEntryPath, { recursive: true, force: true });
5009
+ }
5010
+ console.log(` Deleted '${relPath}'.`);
5011
+ deleted++;
5012
+ }
5013
+ const presentNames = new Set(topLevelEntries.map((e) => e.name));
5014
+ const missingKeeps = [...keepSet].filter((name) => !presentNames.has(name));
5015
+ if (missingKeeps.length > 0) {
5016
+ console.warn(
5017
+ ` [warn] Requested to keep ${missingKeeps.map((n) => `'${n}'`).join(", ")} but they don't exist under '${INFRASTRUCTURE_DIR2}/'.`
5018
+ );
5019
+ }
5020
+ console.log(
5021
+ `
5022
+ Done. Deleted ${deleted} top-level entr${deleted === 1 ? "y" : "ies"}. Review the staged deletions with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5023
+ );
5024
+ }
5025
+
5026
+ // src/migrations/v2.0/fix-accept-requested-registration-function-type.ts
5027
+ var import_ts_morph22 = require("ts-morph");
5028
+ var import_path26 = __toESM(require("path"));
5029
+ var TARGET_FUNCTION_NAME = "acceptRequestedRegistration";
5030
+ var ACTION_PARAMETER_NAME = "action";
5031
+ var OLD_TYPE = "ActionInput";
5032
+ var NEW_TYPE = "Extract<ActionInput, { type?: 'REGISTER' }>";
5033
+ function isAcceptRequestedRegistrationParameter(parameter) {
5034
+ const functionDeclaration = parameter.getFirstAncestorByKind(
5035
+ import_ts_morph22.SyntaxKind.FunctionDeclaration
5036
+ );
5037
+ if (functionDeclaration?.getName() === TARGET_FUNCTION_NAME) {
5038
+ return true;
5039
+ }
5040
+ const arrowFunction = parameter.getFirstAncestorByKind(
5041
+ import_ts_morph22.SyntaxKind.ArrowFunction
5042
+ );
5043
+ const variableDeclaration = arrowFunction?.getFirstAncestorByKind(
5044
+ import_ts_morph22.SyntaxKind.VariableDeclaration
5045
+ );
5046
+ return variableDeclaration?.getName() === TARGET_FUNCTION_NAME;
5047
+ }
5048
+ function processFile14(filePath, project) {
5049
+ const sourceFile = project.getSourceFile(filePath);
5050
+ if (!sourceFile) return 0;
5051
+ let updatedCount = 0;
5052
+ for (const parameter of sourceFile.getDescendantsOfKind(
5053
+ import_ts_morph22.SyntaxKind.Parameter
5054
+ )) {
5055
+ if (parameter.getName() !== ACTION_PARAMETER_NAME) {
5056
+ continue;
5057
+ }
5058
+ if (!isAcceptRequestedRegistrationParameter(parameter)) {
5059
+ continue;
5060
+ }
5061
+ const typeNode = parameter.getTypeNode();
5062
+ if (!typeNode || typeNode.getText() !== OLD_TYPE) {
5063
+ continue;
5064
+ }
5065
+ parameter.setType(NEW_TYPE);
5066
+ updatedCount++;
5067
+ console.log(
5068
+ ` [${import_path26.default.relative(process.cwd(), filePath)}] Updated '${ACTION_PARAMETER_NAME}' in ${TARGET_FUNCTION_NAME} to ${NEW_TYPE}`
5069
+ );
5070
+ }
5071
+ return updatedCount;
5072
+ }
5073
+ async function main27() {
5074
+ const srcDir = import_path26.default.join(process.cwd(), "src");
5075
+ console.log(`Scanning for ${TARGET_FUNCTION_NAME} in: ${srcDir}
5076
+ `);
5077
+ const project = new import_ts_morph22.Project({
5078
+ tsConfigFilePath: import_path26.default.resolve(srcDir, "../tsconfig.json"),
5079
+ skipAddingFilesFromTsConfig: false
5080
+ });
5081
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
5082
+ const filePath = sf.getFilePath();
5083
+ return filePath.includes("/src/") && !filePath.includes("/node_modules/");
4741
5084
  });
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}/'.`);
5085
+ let totalUpdated = 0;
5086
+ const modifiedFiles = [];
5087
+ for (const sourceFile of sourceFiles) {
5088
+ const filePath = sourceFile.getFilePath();
5089
+ const updated = processFile14(filePath, project);
5090
+ if (updated > 0) {
5091
+ totalUpdated += updated;
5092
+ modifiedFiles.push(filePath);
5093
+ }
5094
+ }
5095
+ if (modifiedFiles.length === 0) {
5096
+ console.log(
5097
+ `No '${ACTION_PARAMETER_NAME}: ${OLD_TYPE}' parameter in ${TARGET_FUNCTION_NAME} found. Nothing to do.`
5098
+ );
5099
+ return;
5100
+ }
5101
+ console.log(`
5102
+ Saving ${modifiedFiles.length} modified file(s)...`);
5103
+ for (const filePath of modifiedFiles) {
5104
+ const sourceFile = project.getSourceFileOrThrow(filePath);
5105
+ await sourceFile.save();
5106
+ console.log(` Saved: ${import_path26.default.relative(process.cwd(), filePath)}`);
4746
5107
  }
4747
5108
  console.log(
4748
5109
  `
4749
- Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5110
+ Done. Updated ${totalUpdated} parameter type annotation(s) in ${TARGET_FUNCTION_NAME}.`
4750
5111
  );
4751
5112
  }
4752
5113
 
4753
5114
  // src/migrations/v2.0/index.ts
5115
+ var NON_SWARM_INFRA_SUBDIRS = ["postgres", "metabase"];
4754
5116
  async function runUpgrade(dockerSwarm) {
4755
5117
  await main18();
4756
5118
  await main();
@@ -4775,10 +5137,12 @@ async function runUpgrade(dockerSwarm) {
4775
5137
  await main22();
4776
5138
  await main23();
4777
5139
  await main24();
5140
+ await main27();
4778
5141
  await main17();
4779
5142
  if (dockerSwarm) {
4780
5143
  await main25();
4781
5144
  } else {
4782
- await main26();
5145
+ await main26({ keep: NON_SWARM_INFRA_SUBDIRS });
5146
+ await main25({ subdirs: NON_SWARM_INFRA_SUBDIRS });
4783
5147
  }
4784
5148
  }