@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
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
@@ -2289,11 +2393,12 @@ var decodeScope = (query) => {
2289
2393
  };
2290
2394
  var DEFAULT_SCOPE_OPTIONS = {
2291
2395
  placeOfEvent: JurisdictionFilter.enum.all,
2292
- accessLevel: JurisdictionFilter.enum.all
2396
+ accessLevel: JurisdictionFilter.enum.all,
2397
+ registeredIn: JurisdictionFilter.enum.all,
2398
+ declaredIn: JurisdictionFilter.enum.all
2293
2399
  };
2294
2400
 
2295
2401
  // ../commons/src/scopes.deprecated.do-not-use.ts
2296
- var z4 = __toESM(require("zod/v4"));
2297
2402
  var rawConfigurableScopeRegex = /^([a-zA-Z][a-zA-Z0-9.-]*(?:\.[a-zA-Z0-9.-]+)*)\[((?:\w+=[\w.-]+(?:\|[\w.-]+)*)(?:,[\w]+=[\w.-]+(?:\|[\w.-]+)*)*)\]$/;
2298
2403
  var rawConfigurableScope = z4.string().regex(rawConfigurableScopeRegex);
2299
2404
  var SearchScope = z4.object({
@@ -2527,6 +2632,74 @@ var legacyScopeToV2Scope = (v1Scope) => {
2527
2632
  }
2528
2633
  throw new Error(`Unsupported V1 scope type: ${v1Scope}`);
2529
2634
  };
2635
+ var MERGEABLE_USER_SCOPE_TYPES = ["user.create", "user.edit"];
2636
+ function isMergeableUserScopeType(type) {
2637
+ return MERGEABLE_USER_SCOPE_TYPES.includes(type);
2638
+ }
2639
+ function decodeLegacyScope(legacy) {
2640
+ const encoded = legacyScopeToV2Scope(legacy);
2641
+ const scope = decodeScope(encoded);
2642
+ if (!scope) {
2643
+ throw new Error(
2644
+ `Could not decode migrated scope from legacy '${legacy}' (encoded: '${encoded}').`
2645
+ );
2646
+ }
2647
+ return { legacy, scope };
2648
+ }
2649
+ function groupByType(entries) {
2650
+ return entries.reduce(
2651
+ (groups, entry) => ({
2652
+ ...groups,
2653
+ [entry.scope.type]: [...groups[entry.scope.type] ?? [], entry]
2654
+ }),
2655
+ {}
2656
+ );
2657
+ }
2658
+ function deduplicateEntries(entries) {
2659
+ return entries.filter(
2660
+ (entry, index) => entries.findIndex((e) => (0, import_lodash.isEqual)(e.scope, entry.scope)) === index
2661
+ );
2662
+ }
2663
+ function mergeEntries(type, entries) {
2664
+ const optionsList = entries.map(
2665
+ (e) => "options" in e.scope && e.scope.options ? e.scope.options : {}
2666
+ );
2667
+ const allKeys = optionsList.flatMap(
2668
+ (opts) => Object.keys(opts).filter((k) => opts[k] !== void 0)
2669
+ );
2670
+ const conflictKey = allKeys.find((key, i) => allKeys.indexOf(key) !== i);
2671
+ if (conflictKey) {
2672
+ const conflicting = entries.filter(
2673
+ (e) => conflictKey in ("options" in e.scope && e.scope.options || {})
2674
+ ).map((e) => JSON.stringify(e.legacy));
2675
+ throw new Error(
2676
+ `Cannot auto-merge two '${type}' scopes that both define '${conflictKey}'. Conflicting legacy scopes: [${conflicting.join(", ")}]. Resolve manually in roles.ts and re-run.`
2677
+ );
2678
+ }
2679
+ const mergedOptions = Object.assign({}, ...optionsList);
2680
+ const merged = Object.keys(mergedOptions).length > 0 ? { type, options: mergedOptions } : { type };
2681
+ const parsed = Scope2.safeParse(merged);
2682
+ if (!parsed.success) {
2683
+ throw new Error(
2684
+ `Merged '${type}' scope failed schema validation: ${parsed.error.message}. Legacy scopes: [${entries.map((e) => JSON.stringify(e.legacy)).join(", ")}].`
2685
+ );
2686
+ }
2687
+ return parsed.data;
2688
+ }
2689
+ function migrateLegacyScopesArrayToV2Scopes(legacyScopes) {
2690
+ const groups = groupByType(legacyScopes.map(decodeLegacyScope));
2691
+ return Object.entries(groups).flatMap(([type, entries]) => {
2692
+ const deduped = deduplicateEntries(entries);
2693
+ if (deduped.length <= 1) return deduped.map((e) => e.scope);
2694
+ if (!isMergeableUserScopeType(type)) {
2695
+ console.warn(
2696
+ `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.`
2697
+ );
2698
+ return deduped.map((e) => e.scope);
2699
+ }
2700
+ return [mergeEntries(type, deduped)];
2701
+ });
2702
+ }
2530
2703
 
2531
2704
  // src/migrations/v2.0/migrate-scopes.ts
2532
2705
  var ROLES_FILE_RELATIVE_PATH = "src/data-seeding/roles/roles.ts";
@@ -2564,57 +2737,65 @@ function resolveLegacyScopeFromElement(element) {
2564
2737
  }
2565
2738
  function migrateScopesArrays(rolesFilePath, project) {
2566
2739
  const sourceFile = project.getSourceFile(rolesFilePath);
2567
- if (!sourceFile) return { migratedCount: 0, removedCount: 0 };
2740
+ if (!sourceFile) {
2741
+ return { migratedCount: 0, removedCount: 0, mergedCount: 0 };
2742
+ }
2568
2743
  let migratedCount = 0;
2569
2744
  let removedCount = 0;
2745
+ let mergedCount = 0;
2570
2746
  const scopesProperties = sourceFile.getDescendantsOfKind(import_ts_morph11.SyntaxKind.PropertyAssignment).filter((prop) => prop.getName() === SCOPES_PROPERTY_NAME);
2747
+ const relPath = import_path11.default.relative(process.cwd(), rolesFilePath);
2571
2748
  for (const scopesProp of scopesProperties) {
2572
2749
  const initializer = scopesProp.getInitializer();
2573
2750
  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;
2751
+ const rawTexts = [];
2752
+ const legacyStrings = [];
2753
+ let anyChange = false;
2754
+ for (const element of initializer.getElements()) {
2578
2755
  const scopeKey = resolveScopeKeyFromElementAccess(element);
2579
2756
  if (scopeKey && !(scopeKey in SCOPES)) {
2580
2757
  console.warn(
2581
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Removing unknown scope reference: SCOPES.${scopeKey}`
2758
+ ` [${relPath}] Removing unknown scope reference: SCOPES.${scopeKey}`
2582
2759
  );
2583
- initializer.removeElement(index);
2584
2760
  removedCount++;
2761
+ anyChange = true;
2585
2762
  continue;
2586
2763
  }
2587
2764
  const legacyScope = resolveLegacyScopeFromElement(element);
2588
2765
  if (!legacyScope) {
2589
2766
  console.warn(
2590
- ` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Skipping non-literal scope expression: ${element.getText()}`
2767
+ ` [${relPath}] Preserving non-literal scope expression verbatim: ${element.getText()}`
2591
2768
  );
2769
+ rawTexts.push(element.getText());
2592
2770
  continue;
2593
2771
  }
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
- }
2772
+ legacyStrings.push(legacyScope);
2773
+ anyChange = true;
2774
+ }
2775
+ if (!anyChange) continue;
2776
+ let mergedScopes;
2777
+ try {
2778
+ mergedScopes = migrateLegacyScopesArrayToV2Scopes(legacyStrings);
2779
+ } catch (error) {
2780
+ throw new Error(
2781
+ `[${relPath}] Failed to migrate scopes for role: ${error instanceof Error ? error.message : String(error)}`,
2782
+ { cause: error }
2783
+ );
2610
2784
  }
2785
+ migratedCount += legacyStrings.length;
2786
+ mergedCount += legacyStrings.length - mergedScopes.length;
2787
+ const outputElements = [
2788
+ ...rawTexts,
2789
+ ...mergedScopes.map((s) => JSON.stringify(s))
2790
+ ];
2791
+ scopesProp.setInitializer(`[${outputElements.join(", ")}]`);
2611
2792
  }
2612
2793
  if (removedCount > 0) {
2613
2794
  console.log(
2614
- ` Removed ${removedCount} unknown SCOPES reference(s) from '${import_path11.default.relative(process.cwd(), rolesFilePath)}'.`
2795
+ ` Removed ${removedCount} unknown SCOPES reference(s) from '${relPath}'.`
2615
2796
  );
2616
2797
  }
2617
- return { migratedCount, removedCount };
2798
+ return { migratedCount, removedCount, mergedCount };
2618
2799
  }
2619
2800
  function ensureScopesHelpersImport(rolesFilePath, project) {
2620
2801
  const sourceFile = project.getSourceFile(rolesFilePath);
@@ -2749,7 +2930,7 @@ async function main11() {
2749
2930
  return;
2750
2931
  }
2751
2932
  const importChanged = ensureScopesHelpersImport(rolesFilePath, project);
2752
- const { migratedCount, removedCount } = migrateScopesArrays(
2933
+ const { migratedCount, removedCount, mergedCount } = migrateScopesArrays(
2753
2934
  rolesFilePath,
2754
2935
  project
2755
2936
  );
@@ -2768,7 +2949,7 @@ async function main11() {
2768
2949
  }
2769
2950
  await sourceFile.save();
2770
2951
  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." : "."}`
2952
+ `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
2953
  );
2773
2954
  }
2774
2955
 
@@ -3574,6 +3755,10 @@ var ACTION_PROPERTY_NAME2 = "action";
3574
3755
  var CONDITIONALS_PROPERTY_NAME = "conditionals";
3575
3756
  var TYPE_PROPERTY_NAME7 = "type";
3576
3757
  var SLUG_PROPERTY_NAME = "slug";
3758
+ var READ_ACTION_TYPE = "READ";
3759
+ var ACTION_TYPE_IMPORT_NAME = "ActionType";
3760
+ var TOOLKIT_EVENTS_MODULE2 = "@opencrvs/toolkit/events";
3761
+ var READ_ACTION_FALLBACK_INITIALIZER = `{ type: ${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE} }`;
3577
3762
  var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
3578
3763
  "READ",
3579
3764
  "DELETE",
@@ -3621,91 +3806,137 @@ function isUnsupportedActionType(action) {
3621
3806
  }
3622
3807
  return !SUPPORTED_WORKQUEUE_ACTION_TYPES.has(typeName);
3623
3808
  }
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;
3809
+ function ensureValidAction(workqueue, relPath) {
3810
+ const workqueueLabel = getWorkqueueLabel(workqueue);
3811
+ const actionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3812
+ if (actionProperty && import_ts_morph17.Node.isPropertyAssignment(actionProperty)) {
3813
+ const initializer = actionProperty.getInitializer();
3814
+ if (initializer && import_ts_morph17.Node.isObjectLiteralExpression(initializer)) {
3815
+ if (!isUnsupportedActionType(initializer)) {
3816
+ return { changed: false, usedReadFallback: false };
3817
+ }
3818
+ actionProperty.set({
3819
+ name: ACTION_PROPERTY_NAME2,
3820
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3821
+ });
3822
+ console.log(
3823
+ ` [${relPath}] Replaced unsupported 'action' type with '${ACTION_TYPE_IMPORT_NAME}.${READ_ACTION_TYPE}' on workqueue '${workqueueLabel}'`
3824
+ );
3825
+ return { changed: true, usedReadFallback: true };
3826
+ }
3632
3827
  }
3828
+ workqueue.addPropertyAssignment({
3829
+ name: ACTION_PROPERTY_NAME2,
3830
+ initializer: READ_ACTION_FALLBACK_INITIALIZER
3831
+ });
3832
+ console.log(
3833
+ ` [${relPath}] Added default 'action: ${READ_ACTION_FALLBACK_INITIALIZER}' to workqueue '${workqueueLabel}'`
3834
+ );
3835
+ return { changed: true, usedReadFallback: true };
3836
+ }
3837
+ function migrateWorkqueueActions(workqueue, relPath) {
3633
3838
  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
3839
+ const actionsProperty = workqueue.getProperty(ACTIONS_PROPERTY_NAME5);
3840
+ let actionsArrayMigrated = false;
3841
+ if (actionsProperty && import_ts_morph17.Node.isPropertyAssignment(actionsProperty)) {
3842
+ const actionsInitializer = actionsProperty.getInitializer();
3843
+ if (actionsInitializer && import_ts_morph17.Node.isArrayLiteralExpression(actionsInitializer)) {
3844
+ const existingActionProperty = workqueue.getProperty(ACTION_PROPERTY_NAME2);
3845
+ if (existingActionProperty) {
3846
+ if (import_ts_morph17.Node.isPropertyAssignment(existingActionProperty)) {
3847
+ const existingActionInitializer = existingActionProperty.getInitializer();
3848
+ if (existingActionInitializer && import_ts_morph17.Node.isObjectLiteralExpression(existingActionInitializer)) {
3849
+ const conditionalsProperty = existingActionInitializer.getProperty(
3850
+ CONDITIONALS_PROPERTY_NAME
3851
+ );
3852
+ if (conditionalsProperty) {
3853
+ conditionalsProperty.remove();
3854
+ console.log(
3855
+ ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3856
+ );
3857
+ }
3858
+ }
3859
+ }
3860
+ actionsProperty.remove();
3861
+ console.log(
3862
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
3641
3863
  );
3642
- if (conditionalsProperty2) {
3643
- conditionalsProperty2.remove();
3864
+ actionsArrayMigrated = true;
3865
+ } else {
3866
+ const actionCandidates = actionsInitializer.getElements().filter(
3867
+ (element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
3868
+ );
3869
+ if (actionCandidates.length === 0) {
3870
+ actionsProperty.remove();
3644
3871
  console.log(
3645
- ` [${relPath}] Removed deprecated 'conditionals' from 'action' on workqueue '${workqueueLabel}'`
3872
+ ` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
3646
3873
  );
3647
- }
3648
- if (isUnsupportedActionType(existingActionInitializer)) {
3649
- existingActionProperty.remove();
3650
- console.log(
3651
- ` [${relPath}] Removed unsupported 'action' type on workqueue '${workqueueLabel}'`
3874
+ actionsArrayMigrated = true;
3875
+ } else {
3876
+ if (actionCandidates.length > 1) {
3877
+ const droppedCount = actionCandidates.length - 1;
3878
+ console.warn(
3879
+ ` [${relPath}] Workqueue '${workqueueLabel}' has ${actionCandidates.length} actions; keeping first as 'action' and dropping ${droppedCount} extra action(s)`
3880
+ );
3881
+ }
3882
+ const conditionalsProperty = actionCandidates[0].getProperty(
3883
+ CONDITIONALS_PROPERTY_NAME
3652
3884
  );
3885
+ if (conditionalsProperty) {
3886
+ conditionalsProperty.remove();
3887
+ console.log(
3888
+ ` [${relPath}] Removed deprecated 'conditionals' from migrated 'action' on workqueue '${workqueueLabel}'`
3889
+ );
3890
+ }
3891
+ if (isUnsupportedActionType(actionCandidates[0])) {
3892
+ actionsProperty.remove();
3893
+ console.log(
3894
+ ` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
3895
+ );
3896
+ actionsArrayMigrated = true;
3897
+ } else {
3898
+ const firstActionText = actionCandidates[0].getText();
3899
+ workqueue.addPropertyAssignment({
3900
+ name: ACTION_PROPERTY_NAME2,
3901
+ initializer: firstActionText
3902
+ });
3903
+ actionsProperty.remove();
3904
+ console.log(
3905
+ ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3906
+ );
3907
+ actionsArrayMigrated = true;
3908
+ }
3653
3909
  }
3654
3910
  }
3655
3911
  }
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
3912
  }
3678
- const conditionalsProperty = actionCandidates[0].getProperty(
3679
- CONDITIONALS_PROPERTY_NAME
3913
+ const fallback = ensureValidAction(workqueue, relPath);
3914
+ return {
3915
+ changed: actionsArrayMigrated || fallback.changed,
3916
+ usedReadFallback: fallback.usedReadFallback
3917
+ };
3918
+ }
3919
+ function ensureActionTypeImport(sourceFile) {
3920
+ const existingImport = sourceFile.getImportDeclaration(
3921
+ (decl) => decl.getModuleSpecifierValue() === TOOLKIT_EVENTS_MODULE2
3680
3922
  );
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;
3923
+ if (existingImport) {
3924
+ const alreadyImported = existingImport.getNamedImports().some((ni) => ni.getName() === ACTION_TYPE_IMPORT_NAME);
3925
+ if (!alreadyImported) {
3926
+ existingImport.addNamedImport(ACTION_TYPE_IMPORT_NAME);
3927
+ }
3928
+ return;
3693
3929
  }
3694
- const firstActionText = actionCandidates[0].getText();
3695
- workqueue.addPropertyAssignment({
3696
- name: ACTION_PROPERTY_NAME2,
3697
- initializer: firstActionText
3930
+ sourceFile.addImportDeclaration({
3931
+ moduleSpecifier: TOOLKIT_EVENTS_MODULE2,
3932
+ namedImports: [ACTION_TYPE_IMPORT_NAME]
3698
3933
  });
3699
- actionsProperty.remove();
3700
- console.log(
3701
- ` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
3702
- );
3703
- return 1;
3704
3934
  }
3705
3935
  function processFile13(filePath, project) {
3706
3936
  const sourceFile = project.getSourceFile(filePath);
3707
3937
  if (!sourceFile) return 0;
3708
3938
  let migratedWorkqueues = 0;
3939
+ let needsActionTypeImport = false;
3709
3940
  const relPath = import_path18.default.relative(process.cwd(), filePath);
3710
3941
  const callExpressions = sourceFile.getDescendantsOfKind(
3711
3942
  import_ts_morph17.SyntaxKind.CallExpression
@@ -3721,9 +3952,14 @@ function processFile13(filePath, project) {
3721
3952
  if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
3722
3953
  for (const element of workqueuesArg.getElements()) {
3723
3954
  if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
3724
- migratedWorkqueues += migrateWorkqueueActions(element, relPath);
3955
+ const result = migrateWorkqueueActions(element, relPath);
3956
+ if (result.changed) migratedWorkqueues++;
3957
+ if (result.usedReadFallback) needsActionTypeImport = true;
3725
3958
  }
3726
3959
  }
3960
+ if (needsActionTypeImport) {
3961
+ ensureActionTypeImport(sourceFile);
3962
+ }
3727
3963
  return migratedWorkqueues;
3728
3964
  }
3729
3965
  async function main18() {
@@ -4058,7 +4294,7 @@ Saved ${handlerRelPath}.`);
4058
4294
  // src/migrations/v2.0/checkout-upstream-files.ts
4059
4295
  var import_child_process = require("child_process");
4060
4296
  var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
4061
- var UPSTREAM_BRANCH = "develop";
4297
+ var UPSTREAM_BRANCH = "release-v2.0.0";
4062
4298
  var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
4063
4299
  var FILES_TO_CHECKOUT = [
4064
4300
  "src/analytics/analytics.ts",
@@ -4597,9 +4833,9 @@ var import_path24 = require("path");
4597
4833
  var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
4598
4834
  var INFRASTRUCTURE_DIR = "infrastructure";
4599
4835
  var BASE_BRANCH = "release-v1.9";
4600
- function gitShowBytes(ref, path23) {
4836
+ function gitShowBytes(ref, path24) {
4601
4837
  try {
4602
- const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path23}`], {
4838
+ const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path24}`], {
4603
4839
  cwd: process.cwd(),
4604
4840
  stdio: ["ignore", "pipe", "pipe"]
4605
4841
  });
@@ -4608,9 +4844,13 @@ function gitShowBytes(ref, path23) {
4608
4844
  return null;
4609
4845
  }
4610
4846
  }
4611
- async function main25() {
4847
+ async function main25({
4848
+ subdirs = []
4849
+ } = {}) {
4850
+ const pathspecs = subdirs.length > 0 ? subdirs.map((s) => `${INFRASTRUCTURE_DIR}/${s}/`) : [`${INFRASTRUCTURE_DIR}/`];
4851
+ const scopeLabel = pathspecs.map((p) => `'${p}'`).join(", ");
4612
4852
  console.log(
4613
- `Merging '${INFRASTRUCTURE_DIR}/' from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4853
+ `Merging ${scopeLabel} from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
4614
4854
  `
4615
4855
  );
4616
4856
  assertIsGitRepo();
@@ -4625,20 +4865,12 @@ async function main25() {
4625
4865
  const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
4626
4866
  const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
4627
4867
  const upstreamFiles = runGit(
4628
- [
4629
- "ls-tree",
4630
- "-r",
4631
- "--name-only",
4632
- "-z",
4633
- ref,
4634
- "--",
4635
- `${INFRASTRUCTURE_DIR}/`
4636
- ],
4868
+ ["ls-tree", "-r", "--name-only", "-z", ref, "--", ...pathspecs],
4637
4869
  { silent: true }
4638
4870
  ).split("\0").filter(Boolean);
4639
4871
  if (upstreamFiles.length === 0) {
4640
4872
  console.log(
4641
- ` No files found under '${INFRASTRUCTURE_DIR}/' on ${ref}. Nothing to merge.`
4873
+ ` No files found under ${scopeLabel} on ${ref}. Nothing to merge.`
4642
4874
  );
4643
4875
  return;
4644
4876
  }
@@ -4713,7 +4945,9 @@ async function main25() {
4713
4945
  var import_fs6 = require("fs");
4714
4946
  var import_path25 = require("path");
4715
4947
  var INFRASTRUCTURE_DIR2 = "infrastructure";
4716
- async function main26() {
4948
+ async function main26({
4949
+ keep = []
4950
+ } = {}) {
4717
4951
  const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
4718
4952
  if (!(0, import_fs6.existsSync)(fullPath)) {
4719
4953
  console.log(
@@ -4721,24 +4955,152 @@ async function main26() {
4721
4955
  );
4722
4956
  return;
4723
4957
  }
4724
- console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4725
- `);
4726
4958
  assertIsGitRepo();
4727
- runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4728
- silent: true
4959
+ if (keep.length === 0) {
4960
+ console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
4961
+ `);
4962
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", `${INFRASTRUCTURE_DIR2}/`], {
4963
+ silent: true
4964
+ });
4965
+ console.log(
4966
+ ` Staged deletion of tracked files under '${INFRASTRUCTURE_DIR2}/'.`
4967
+ );
4968
+ if ((0, import_fs6.existsSync)(fullPath)) {
4969
+ (0, import_fs6.rmSync)(fullPath, { recursive: true, force: true });
4970
+ console.log(
4971
+ ` Removed remaining untracked files under '${INFRASTRUCTURE_DIR2}/'.`
4972
+ );
4973
+ }
4974
+ console.log(
4975
+ `
4976
+ Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
4977
+ );
4978
+ return;
4979
+ }
4980
+ const keepSet = new Set(keep);
4981
+ console.log(
4982
+ `Deleting contents of '${INFRASTRUCTURE_DIR2}/' except: ${[...keepSet].map((name) => `'${name}'`).join(", ")}
4983
+ `
4984
+ );
4985
+ const topLevelEntries = (0, import_fs6.readdirSync)(fullPath, { withFileTypes: true });
4986
+ let deleted = 0;
4987
+ for (const entry of topLevelEntries) {
4988
+ if (keepSet.has(entry.name)) {
4989
+ console.log(` Keeping '${INFRASTRUCTURE_DIR2}/${entry.name}'.`);
4990
+ continue;
4991
+ }
4992
+ const relPath = `${INFRASTRUCTURE_DIR2}/${entry.name}${entry.isDirectory() ? "/" : ""}`;
4993
+ runGit(["rm", "-rf", "--ignore-unmatch", "--", relPath], { silent: true });
4994
+ const fullEntryPath = (0, import_path25.join)(fullPath, entry.name);
4995
+ if ((0, import_fs6.existsSync)(fullEntryPath)) {
4996
+ (0, import_fs6.rmSync)(fullEntryPath, { recursive: true, force: true });
4997
+ }
4998
+ console.log(` Deleted '${relPath}'.`);
4999
+ deleted++;
5000
+ }
5001
+ const presentNames = new Set(topLevelEntries.map((e) => e.name));
5002
+ const missingKeeps = [...keepSet].filter((name) => !presentNames.has(name));
5003
+ if (missingKeeps.length > 0) {
5004
+ console.warn(
5005
+ ` [warn] Requested to keep ${missingKeeps.map((n) => `'${n}'`).join(", ")} but they don't exist under '${INFRASTRUCTURE_DIR2}/'.`
5006
+ );
5007
+ }
5008
+ console.log(
5009
+ `
5010
+ Done. Deleted ${deleted} top-level entr${deleted === 1 ? "y" : "ies"}. Review the staged deletions with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5011
+ );
5012
+ }
5013
+
5014
+ // src/migrations/v2.0/fix-accept-requested-registration-function-type.ts
5015
+ var import_ts_morph22 = require("ts-morph");
5016
+ var import_path26 = __toESM(require("path"));
5017
+ var TARGET_FUNCTION_NAME = "acceptRequestedRegistration";
5018
+ var ACTION_PARAMETER_NAME = "action";
5019
+ var OLD_TYPE = "ActionInput";
5020
+ var NEW_TYPE = "Extract<ActionInput, { type?: 'REGISTER' }>";
5021
+ function isAcceptRequestedRegistrationParameter(parameter) {
5022
+ const functionDeclaration = parameter.getFirstAncestorByKind(
5023
+ import_ts_morph22.SyntaxKind.FunctionDeclaration
5024
+ );
5025
+ if (functionDeclaration?.getName() === TARGET_FUNCTION_NAME) {
5026
+ return true;
5027
+ }
5028
+ const arrowFunction = parameter.getFirstAncestorByKind(
5029
+ import_ts_morph22.SyntaxKind.ArrowFunction
5030
+ );
5031
+ const variableDeclaration = arrowFunction?.getFirstAncestorByKind(
5032
+ import_ts_morph22.SyntaxKind.VariableDeclaration
5033
+ );
5034
+ return variableDeclaration?.getName() === TARGET_FUNCTION_NAME;
5035
+ }
5036
+ function processFile14(filePath, project) {
5037
+ const sourceFile = project.getSourceFile(filePath);
5038
+ if (!sourceFile) return 0;
5039
+ let updatedCount = 0;
5040
+ for (const parameter of sourceFile.getDescendantsOfKind(
5041
+ import_ts_morph22.SyntaxKind.Parameter
5042
+ )) {
5043
+ if (parameter.getName() !== ACTION_PARAMETER_NAME) {
5044
+ continue;
5045
+ }
5046
+ if (!isAcceptRequestedRegistrationParameter(parameter)) {
5047
+ continue;
5048
+ }
5049
+ const typeNode = parameter.getTypeNode();
5050
+ if (!typeNode || typeNode.getText() !== OLD_TYPE) {
5051
+ continue;
5052
+ }
5053
+ parameter.setType(NEW_TYPE);
5054
+ updatedCount++;
5055
+ console.log(
5056
+ ` [${import_path26.default.relative(process.cwd(), filePath)}] Updated '${ACTION_PARAMETER_NAME}' in ${TARGET_FUNCTION_NAME} to ${NEW_TYPE}`
5057
+ );
5058
+ }
5059
+ return updatedCount;
5060
+ }
5061
+ async function main27() {
5062
+ const srcDir = import_path26.default.join(process.cwd(), "src");
5063
+ console.log(`Scanning for ${TARGET_FUNCTION_NAME} in: ${srcDir}
5064
+ `);
5065
+ const project = new import_ts_morph22.Project({
5066
+ tsConfigFilePath: import_path26.default.resolve(srcDir, "../tsconfig.json"),
5067
+ skipAddingFilesFromTsConfig: false
5068
+ });
5069
+ const sourceFiles = project.getSourceFiles().filter((sf) => {
5070
+ const filePath = sf.getFilePath();
5071
+ return filePath.includes("/src/") && !filePath.includes("/node_modules/");
4729
5072
  });
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}/'.`);
5073
+ let totalUpdated = 0;
5074
+ const modifiedFiles = [];
5075
+ for (const sourceFile of sourceFiles) {
5076
+ const filePath = sourceFile.getFilePath();
5077
+ const updated = processFile14(filePath, project);
5078
+ if (updated > 0) {
5079
+ totalUpdated += updated;
5080
+ modifiedFiles.push(filePath);
5081
+ }
5082
+ }
5083
+ if (modifiedFiles.length === 0) {
5084
+ console.log(
5085
+ `No '${ACTION_PARAMETER_NAME}: ${OLD_TYPE}' parameter in ${TARGET_FUNCTION_NAME} found. Nothing to do.`
5086
+ );
5087
+ return;
5088
+ }
5089
+ console.log(`
5090
+ Saving ${modifiedFiles.length} modified file(s)...`);
5091
+ for (const filePath of modifiedFiles) {
5092
+ const sourceFile = project.getSourceFileOrThrow(filePath);
5093
+ await sourceFile.save();
5094
+ console.log(` Saved: ${import_path26.default.relative(process.cwd(), filePath)}`);
4734
5095
  }
4735
5096
  console.log(
4736
5097
  `
4737
- Done. Review the staged deletion with: git diff --cached -- '${INFRASTRUCTURE_DIR2}/'`
5098
+ Done. Updated ${totalUpdated} parameter type annotation(s) in ${TARGET_FUNCTION_NAME}.`
4738
5099
  );
4739
5100
  }
4740
5101
 
4741
5102
  // src/migrations/v2.0/index.ts
5103
+ var NON_SWARM_INFRA_SUBDIRS = ["postgres", "metabase"];
4742
5104
  async function runUpgrade(dockerSwarm) {
4743
5105
  await main18();
4744
5106
  await main();
@@ -4763,11 +5125,13 @@ async function runUpgrade(dockerSwarm) {
4763
5125
  await main22();
4764
5126
  await main23();
4765
5127
  await main24();
5128
+ await main27();
4766
5129
  await main17();
4767
5130
  if (dockerSwarm) {
4768
5131
  await main25();
4769
5132
  } else {
4770
- await main26();
5133
+ await main26({ keep: NON_SWARM_INFRA_SUBDIRS });
5134
+ await main25({ subdirs: NON_SWARM_INFRA_SUBDIRS });
4771
5135
  }
4772
5136
  }
4773
5137
 
@@ -4796,7 +5160,7 @@ Options:
4796
5160
  'infrastructure/' directory is deleted (default).
4797
5161
  -h, --help Show this message.
4798
5162
  `.trim();
4799
- function main27() {
5163
+ function main28() {
4800
5164
  const command = args[0];
4801
5165
  if (!command || command === "--help" || command === "-h") {
4802
5166
  console.log(USAGE);
@@ -4871,4 +5235,4 @@ function handleCheckTranslations() {
4871
5235
  console.warn("This command is not implemented yet!");
4872
5236
  process.exit(1);
4873
5237
  }
4874
- main27();
5238
+ main28();