@opencrvs/toolkit 2.0.0-rc.fdc585a → 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.
- package/create-countryconfig/index.js +73 -0
- package/create-countryconfig/package.json +11 -0
- package/dist/application-config/index.js +27 -28
- package/dist/cli.js +392 -128
- package/dist/commons/api/router.d.ts +3301 -247
- package/dist/commons/conditionals/validate.d.ts +1 -0
- package/dist/commons/events/ActionInput.d.ts +108 -0
- package/dist/commons/events/Draft.d.ts +3 -0
- package/dist/commons/events/EventIndex.d.ts +0 -3
- package/dist/commons/events/EventMetadata.d.ts +0 -7
- package/dist/commons/events/WorkqueueConfig.d.ts +146 -146
- package/dist/commons/events/locations.d.ts +21 -0
- package/dist/commons/events/mocks.test.utils.d.ts +19 -0
- package/dist/commons/events/scopes.d.ts +24 -1
- package/dist/commons/events/state/index.d.ts +0 -4
- package/dist/commons/events/state/utils.d.ts +0 -2
- package/dist/commons/events/utils.d.ts +1 -1
- package/dist/conditionals/index.js +3 -1
- package/dist/events/index.js +109 -76
- package/dist/migrations/v2.0/checkout-upstream-files.d.ts +1 -1
- package/dist/migrations/v2.0/checkout-upstream-files.d.ts.map +1 -1
- package/dist/migrations/v2.0/checkout-upstream-files.js +1 -2
- package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts +9 -1
- package/dist/migrations/v2.0/delete-infrastructure-directory.d.ts.map +1 -1
- package/dist/migrations/v2.0/delete-infrastructure-directory.js +66 -20
- package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts +3 -0
- package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.d.ts.map +1 -0
- package/dist/migrations/v2.0/fix-accept-requested-registration-function-type.js +110 -0
- package/dist/migrations/v2.0/index.d.ts +5 -3
- package/dist/migrations/v2.0/index.d.ts.map +1 -1
- package/dist/migrations/v2.0/index.js +390 -126
- package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts +11 -1
- package/dist/migrations/v2.0/merge-infrastructure-directory.d.ts.map +1 -1
- package/dist/migrations/v2.0/merge-infrastructure-directory.js +17 -16
- package/dist/migrations/v2.0/migrate-scopes.d.ts.map +1 -1
- package/dist/migrations/v2.0/migrate-scopes.js +54 -30
- package/dist/migrations/v2.0/migrate-workqueue-configs.d.ts.map +1 -1
- package/dist/migrations/v2.0/migrate-workqueue-configs.js +140 -59
- package/dist/notification/index.js +41 -46
- package/dist/scopes/index.js +3 -1
- package/opencrvs-toolkit-2.0.0-rc.ff04b30.tgz +0 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/opencrvs-toolkit-2.0.0-rc.fdc585a.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -1883,9 +1883,9 @@ function updateRouteHandlers(sourceFiles, group, srcDir, relCwd) {
|
|
|
1883
1883
|
}
|
|
1884
1884
|
return replacedCount;
|
|
1885
1885
|
}
|
|
1886
|
-
function resolveConfigChain(configObj,
|
|
1886
|
+
function resolveConfigChain(configObj, path24) {
|
|
1887
1887
|
let current = configObj;
|
|
1888
|
-
for (const key of
|
|
1888
|
+
for (const key of path24) {
|
|
1889
1889
|
if (!import_ts_morph10.Node.isObjectLiteralExpression(current)) return null;
|
|
1890
1890
|
const prop = current.getProperty(key);
|
|
1891
1891
|
if (!prop || !import_ts_morph10.Node.isPropertyAssignment(prop)) return null;
|
|
@@ -2120,6 +2120,10 @@ Done. Converted ${convertedCount} config file(s), replaced ${totalReplaced} rout
|
|
|
2120
2120
|
var import_ts_morph11 = require("ts-morph");
|
|
2121
2121
|
var import_path11 = __toESM(require("path"));
|
|
2122
2122
|
|
|
2123
|
+
// ../commons/src/scopes.deprecated.do-not-use.ts
|
|
2124
|
+
var z4 = __toESM(require("zod/v4"));
|
|
2125
|
+
var import_lodash = require("lodash");
|
|
2126
|
+
|
|
2123
2127
|
// ../commons/src/scopes.ts
|
|
2124
2128
|
var z3 = __toESM(require("zod/v4"));
|
|
2125
2129
|
var qs = __toESM(require("qs"));
|
|
@@ -2389,11 +2393,12 @@ var decodeScope = (query) => {
|
|
|
2389
2393
|
};
|
|
2390
2394
|
var DEFAULT_SCOPE_OPTIONS = {
|
|
2391
2395
|
placeOfEvent: JurisdictionFilter.enum.all,
|
|
2392
|
-
accessLevel: JurisdictionFilter.enum.all
|
|
2396
|
+
accessLevel: JurisdictionFilter.enum.all,
|
|
2397
|
+
registeredIn: JurisdictionFilter.enum.all,
|
|
2398
|
+
declaredIn: JurisdictionFilter.enum.all
|
|
2393
2399
|
};
|
|
2394
2400
|
|
|
2395
2401
|
// ../commons/src/scopes.deprecated.do-not-use.ts
|
|
2396
|
-
var z4 = __toESM(require("zod/v4"));
|
|
2397
2402
|
var rawConfigurableScopeRegex = /^([a-zA-Z][a-zA-Z0-9.-]*(?:\.[a-zA-Z0-9.-]+)*)\[((?:\w+=[\w.-]+(?:\|[\w.-]+)*)(?:,[\w]+=[\w.-]+(?:\|[\w.-]+)*)*)\]$/;
|
|
2398
2403
|
var rawConfigurableScope = z4.string().regex(rawConfigurableScopeRegex);
|
|
2399
2404
|
var SearchScope = z4.object({
|
|
@@ -2627,6 +2632,74 @@ var legacyScopeToV2Scope = (v1Scope) => {
|
|
|
2627
2632
|
}
|
|
2628
2633
|
throw new Error(`Unsupported V1 scope type: ${v1Scope}`);
|
|
2629
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
|
+
}
|
|
2630
2703
|
|
|
2631
2704
|
// src/migrations/v2.0/migrate-scopes.ts
|
|
2632
2705
|
var ROLES_FILE_RELATIVE_PATH = "src/data-seeding/roles/roles.ts";
|
|
@@ -2664,57 +2737,65 @@ function resolveLegacyScopeFromElement(element) {
|
|
|
2664
2737
|
}
|
|
2665
2738
|
function migrateScopesArrays(rolesFilePath, project) {
|
|
2666
2739
|
const sourceFile = project.getSourceFile(rolesFilePath);
|
|
2667
|
-
if (!sourceFile)
|
|
2740
|
+
if (!sourceFile) {
|
|
2741
|
+
return { migratedCount: 0, removedCount: 0, mergedCount: 0 };
|
|
2742
|
+
}
|
|
2668
2743
|
let migratedCount = 0;
|
|
2669
2744
|
let removedCount = 0;
|
|
2745
|
+
let mergedCount = 0;
|
|
2670
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);
|
|
2671
2748
|
for (const scopesProp of scopesProperties) {
|
|
2672
2749
|
const initializer = scopesProp.getInitializer();
|
|
2673
2750
|
if (!initializer || !import_ts_morph11.Node.isArrayLiteralExpression(initializer)) continue;
|
|
2674
|
-
const
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2751
|
+
const rawTexts = [];
|
|
2752
|
+
const legacyStrings = [];
|
|
2753
|
+
let anyChange = false;
|
|
2754
|
+
for (const element of initializer.getElements()) {
|
|
2678
2755
|
const scopeKey = resolveScopeKeyFromElementAccess(element);
|
|
2679
2756
|
if (scopeKey && !(scopeKey in SCOPES)) {
|
|
2680
2757
|
console.warn(
|
|
2681
|
-
` [${
|
|
2758
|
+
` [${relPath}] Removing unknown scope reference: SCOPES.${scopeKey}`
|
|
2682
2759
|
);
|
|
2683
|
-
initializer.removeElement(index);
|
|
2684
2760
|
removedCount++;
|
|
2761
|
+
anyChange = true;
|
|
2685
2762
|
continue;
|
|
2686
2763
|
}
|
|
2687
2764
|
const legacyScope = resolveLegacyScopeFromElement(element);
|
|
2688
2765
|
if (!legacyScope) {
|
|
2689
2766
|
console.warn(
|
|
2690
|
-
` [${
|
|
2767
|
+
` [${relPath}] Preserving non-literal scope expression verbatim: ${element.getText()}`
|
|
2691
2768
|
);
|
|
2769
|
+
rawTexts.push(element.getText());
|
|
2692
2770
|
continue;
|
|
2693
2771
|
}
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
console.warn(
|
|
2707
|
-
` [${import_path11.default.relative(process.cwd(), rolesFilePath)}] Could not migrate scope '${legacyScope}': ${error}`
|
|
2708
|
-
);
|
|
2709
|
-
}
|
|
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
|
+
);
|
|
2710
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(", ")}]`);
|
|
2711
2792
|
}
|
|
2712
2793
|
if (removedCount > 0) {
|
|
2713
2794
|
console.log(
|
|
2714
|
-
` Removed ${removedCount} unknown SCOPES reference(s) from '${
|
|
2795
|
+
` Removed ${removedCount} unknown SCOPES reference(s) from '${relPath}'.`
|
|
2715
2796
|
);
|
|
2716
2797
|
}
|
|
2717
|
-
return { migratedCount, removedCount };
|
|
2798
|
+
return { migratedCount, removedCount, mergedCount };
|
|
2718
2799
|
}
|
|
2719
2800
|
function ensureScopesHelpersImport(rolesFilePath, project) {
|
|
2720
2801
|
const sourceFile = project.getSourceFile(rolesFilePath);
|
|
@@ -2849,7 +2930,7 @@ async function main11() {
|
|
|
2849
2930
|
return;
|
|
2850
2931
|
}
|
|
2851
2932
|
const importChanged = ensureScopesHelpersImport(rolesFilePath, project);
|
|
2852
|
-
const { migratedCount, removedCount } = migrateScopesArrays(
|
|
2933
|
+
const { migratedCount, removedCount, mergedCount } = migrateScopesArrays(
|
|
2853
2934
|
rolesFilePath,
|
|
2854
2935
|
project
|
|
2855
2936
|
);
|
|
@@ -2868,7 +2949,7 @@ async function main11() {
|
|
|
2868
2949
|
}
|
|
2869
2950
|
await sourceFile.save();
|
|
2870
2951
|
console.log(
|
|
2871
|
-
`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." : "."}`
|
|
2872
2953
|
);
|
|
2873
2954
|
}
|
|
2874
2955
|
|
|
@@ -3674,6 +3755,10 @@ var ACTION_PROPERTY_NAME2 = "action";
|
|
|
3674
3755
|
var CONDITIONALS_PROPERTY_NAME = "conditionals";
|
|
3675
3756
|
var TYPE_PROPERTY_NAME7 = "type";
|
|
3676
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} }`;
|
|
3677
3762
|
var SUPPORTED_WORKQUEUE_ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
3678
3763
|
"READ",
|
|
3679
3764
|
"DELETE",
|
|
@@ -3721,91 +3806,137 @@ function isUnsupportedActionType(action) {
|
|
|
3721
3806
|
}
|
|
3722
3807
|
return !SUPPORTED_WORKQUEUE_ACTION_TYPES.has(typeName);
|
|
3723
3808
|
}
|
|
3724
|
-
function
|
|
3725
|
-
const
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
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
|
+
}
|
|
3732
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) {
|
|
3733
3838
|
const workqueueLabel = getWorkqueueLabel(workqueue);
|
|
3734
|
-
const
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
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)`
|
|
3863
|
+
);
|
|
3864
|
+
actionsArrayMigrated = true;
|
|
3865
|
+
} else {
|
|
3866
|
+
const actionCandidates = actionsInitializer.getElements().filter(
|
|
3867
|
+
(element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
|
|
3741
3868
|
);
|
|
3742
|
-
if (
|
|
3743
|
-
|
|
3869
|
+
if (actionCandidates.length === 0) {
|
|
3870
|
+
actionsProperty.remove();
|
|
3744
3871
|
console.log(
|
|
3745
|
-
` [${relPath}] Removed
|
|
3872
|
+
` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
|
|
3746
3873
|
);
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
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
|
|
3752
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
|
+
}
|
|
3753
3909
|
}
|
|
3754
3910
|
}
|
|
3755
3911
|
}
|
|
3756
|
-
actionsProperty.remove();
|
|
3757
|
-
console.log(
|
|
3758
|
-
` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' (existing 'action' kept)`
|
|
3759
|
-
);
|
|
3760
|
-
return 1;
|
|
3761
|
-
}
|
|
3762
|
-
const actionCandidates = actionsInitializer.getElements().filter(
|
|
3763
|
-
(element) => import_ts_morph17.Node.isObjectLiteralExpression(element)
|
|
3764
|
-
);
|
|
3765
|
-
if (actionCandidates.length === 0) {
|
|
3766
|
-
actionsProperty.remove();
|
|
3767
|
-
console.log(
|
|
3768
|
-
` [${relPath}] Removed empty 'actions' from workqueue '${workqueueLabel}'`
|
|
3769
|
-
);
|
|
3770
|
-
return 1;
|
|
3771
3912
|
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
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
|
|
3780
3922
|
);
|
|
3781
|
-
if (
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
if (isUnsupportedActionType(actionCandidates[0])) {
|
|
3788
|
-
actionsProperty.remove();
|
|
3789
|
-
console.log(
|
|
3790
|
-
` [${relPath}] Removed deprecated 'actions' from workqueue '${workqueueLabel}' because action type is unsupported`
|
|
3791
|
-
);
|
|
3792
|
-
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;
|
|
3793
3929
|
}
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
initializer: firstActionText
|
|
3930
|
+
sourceFile.addImportDeclaration({
|
|
3931
|
+
moduleSpecifier: TOOLKIT_EVENTS_MODULE2,
|
|
3932
|
+
namedImports: [ACTION_TYPE_IMPORT_NAME]
|
|
3798
3933
|
});
|
|
3799
|
-
actionsProperty.remove();
|
|
3800
|
-
console.log(
|
|
3801
|
-
` [${relPath}] Replaced 'actions' with 'action' on workqueue '${workqueueLabel}'`
|
|
3802
|
-
);
|
|
3803
|
-
return 1;
|
|
3804
3934
|
}
|
|
3805
3935
|
function processFile13(filePath, project) {
|
|
3806
3936
|
const sourceFile = project.getSourceFile(filePath);
|
|
3807
3937
|
if (!sourceFile) return 0;
|
|
3808
3938
|
let migratedWorkqueues = 0;
|
|
3939
|
+
let needsActionTypeImport = false;
|
|
3809
3940
|
const relPath = import_path18.default.relative(process.cwd(), filePath);
|
|
3810
3941
|
const callExpressions = sourceFile.getDescendantsOfKind(
|
|
3811
3942
|
import_ts_morph17.SyntaxKind.CallExpression
|
|
@@ -3821,9 +3952,14 @@ function processFile13(filePath, project) {
|
|
|
3821
3952
|
if (!import_ts_morph17.Node.isArrayLiteralExpression(workqueuesArg)) continue;
|
|
3822
3953
|
for (const element of workqueuesArg.getElements()) {
|
|
3823
3954
|
if (!import_ts_morph17.Node.isObjectLiteralExpression(element)) continue;
|
|
3824
|
-
|
|
3955
|
+
const result = migrateWorkqueueActions(element, relPath);
|
|
3956
|
+
if (result.changed) migratedWorkqueues++;
|
|
3957
|
+
if (result.usedReadFallback) needsActionTypeImport = true;
|
|
3825
3958
|
}
|
|
3826
3959
|
}
|
|
3960
|
+
if (needsActionTypeImport) {
|
|
3961
|
+
ensureActionTypeImport(sourceFile);
|
|
3962
|
+
}
|
|
3827
3963
|
return migratedWorkqueues;
|
|
3828
3964
|
}
|
|
3829
3965
|
async function main18() {
|
|
@@ -4158,7 +4294,7 @@ Saved ${handlerRelPath}.`);
|
|
|
4158
4294
|
// src/migrations/v2.0/checkout-upstream-files.ts
|
|
4159
4295
|
var import_child_process = require("child_process");
|
|
4160
4296
|
var UPSTREAM_URL = "https://github.com/opencrvs/opencrvs-countryconfig.git";
|
|
4161
|
-
var UPSTREAM_BRANCH = "
|
|
4297
|
+
var UPSTREAM_BRANCH = "release-v2.0.0";
|
|
4162
4298
|
var TEMP_REMOTE = "opencrvs-upgrade-v19-v20-codemod";
|
|
4163
4299
|
var FILES_TO_CHECKOUT = [
|
|
4164
4300
|
"src/analytics/analytics.ts",
|
|
@@ -4697,9 +4833,9 @@ var import_path24 = require("path");
|
|
|
4697
4833
|
var TEMP_REMOTE2 = "opencrvs-upgrade-v19-v20-codemod-merge-infra";
|
|
4698
4834
|
var INFRASTRUCTURE_DIR = "infrastructure";
|
|
4699
4835
|
var BASE_BRANCH = "release-v1.9";
|
|
4700
|
-
function gitShowBytes(ref,
|
|
4836
|
+
function gitShowBytes(ref, path24) {
|
|
4701
4837
|
try {
|
|
4702
|
-
const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${
|
|
4838
|
+
const buf = (0, import_child_process2.execFileSync)("git", ["show", `${ref}:${path24}`], {
|
|
4703
4839
|
cwd: process.cwd(),
|
|
4704
4840
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4705
4841
|
});
|
|
@@ -4708,9 +4844,13 @@ function gitShowBytes(ref, path23) {
|
|
|
4708
4844
|
return null;
|
|
4709
4845
|
}
|
|
4710
4846
|
}
|
|
4711
|
-
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(", ");
|
|
4712
4852
|
console.log(
|
|
4713
|
-
`Merging
|
|
4853
|
+
`Merging ${scopeLabel} from ${UPSTREAM_URL}@${UPSTREAM_BRANCH} (base: ${BASE_BRANCH})...
|
|
4714
4854
|
`
|
|
4715
4855
|
);
|
|
4716
4856
|
assertIsGitRepo();
|
|
@@ -4725,20 +4865,12 @@ async function main25() {
|
|
|
4725
4865
|
const ref = `${TEMP_REMOTE2}/${UPSTREAM_BRANCH}`;
|
|
4726
4866
|
const baseRef = `${TEMP_REMOTE2}/${BASE_BRANCH}`;
|
|
4727
4867
|
const upstreamFiles = runGit(
|
|
4728
|
-
[
|
|
4729
|
-
"ls-tree",
|
|
4730
|
-
"-r",
|
|
4731
|
-
"--name-only",
|
|
4732
|
-
"-z",
|
|
4733
|
-
ref,
|
|
4734
|
-
"--",
|
|
4735
|
-
`${INFRASTRUCTURE_DIR}/`
|
|
4736
|
-
],
|
|
4868
|
+
["ls-tree", "-r", "--name-only", "-z", ref, "--", ...pathspecs],
|
|
4737
4869
|
{ silent: true }
|
|
4738
4870
|
).split("\0").filter(Boolean);
|
|
4739
4871
|
if (upstreamFiles.length === 0) {
|
|
4740
4872
|
console.log(
|
|
4741
|
-
` No files found under
|
|
4873
|
+
` No files found under ${scopeLabel} on ${ref}. Nothing to merge.`
|
|
4742
4874
|
);
|
|
4743
4875
|
return;
|
|
4744
4876
|
}
|
|
@@ -4813,7 +4945,9 @@ async function main25() {
|
|
|
4813
4945
|
var import_fs6 = require("fs");
|
|
4814
4946
|
var import_path25 = require("path");
|
|
4815
4947
|
var INFRASTRUCTURE_DIR2 = "infrastructure";
|
|
4816
|
-
async function main26(
|
|
4948
|
+
async function main26({
|
|
4949
|
+
keep = []
|
|
4950
|
+
} = {}) {
|
|
4817
4951
|
const fullPath = (0, import_path25.join)(process.cwd(), INFRASTRUCTURE_DIR2);
|
|
4818
4952
|
if (!(0, import_fs6.existsSync)(fullPath)) {
|
|
4819
4953
|
console.log(
|
|
@@ -4821,24 +4955,152 @@ async function main26() {
|
|
|
4821
4955
|
);
|
|
4822
4956
|
return;
|
|
4823
4957
|
}
|
|
4824
|
-
console.log(`Deleting '${INFRASTRUCTURE_DIR2}/'...
|
|
4825
|
-
`);
|
|
4826
4958
|
assertIsGitRepo();
|
|
4827
|
-
|
|
4828
|
-
|
|
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
|
|
4829
5068
|
});
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
5069
|
+
const sourceFiles = project.getSourceFiles().filter((sf) => {
|
|
5070
|
+
const filePath = sf.getFilePath();
|
|
5071
|
+
return filePath.includes("/src/") && !filePath.includes("/node_modules/");
|
|
5072
|
+
});
|
|
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)}`);
|
|
4834
5095
|
}
|
|
4835
5096
|
console.log(
|
|
4836
5097
|
`
|
|
4837
|
-
|
|
5098
|
+
Done. Updated ${totalUpdated} parameter type annotation(s) in ${TARGET_FUNCTION_NAME}.`
|
|
4838
5099
|
);
|
|
4839
5100
|
}
|
|
4840
5101
|
|
|
4841
5102
|
// src/migrations/v2.0/index.ts
|
|
5103
|
+
var NON_SWARM_INFRA_SUBDIRS = ["postgres", "metabase"];
|
|
4842
5104
|
async function runUpgrade(dockerSwarm) {
|
|
4843
5105
|
await main18();
|
|
4844
5106
|
await main();
|
|
@@ -4863,11 +5125,13 @@ async function runUpgrade(dockerSwarm) {
|
|
|
4863
5125
|
await main22();
|
|
4864
5126
|
await main23();
|
|
4865
5127
|
await main24();
|
|
5128
|
+
await main27();
|
|
4866
5129
|
await main17();
|
|
4867
5130
|
if (dockerSwarm) {
|
|
4868
5131
|
await main25();
|
|
4869
5132
|
} else {
|
|
4870
|
-
await main26();
|
|
5133
|
+
await main26({ keep: NON_SWARM_INFRA_SUBDIRS });
|
|
5134
|
+
await main25({ subdirs: NON_SWARM_INFRA_SUBDIRS });
|
|
4871
5135
|
}
|
|
4872
5136
|
}
|
|
4873
5137
|
|
|
@@ -4896,7 +5160,7 @@ Options:
|
|
|
4896
5160
|
'infrastructure/' directory is deleted (default).
|
|
4897
5161
|
-h, --help Show this message.
|
|
4898
5162
|
`.trim();
|
|
4899
|
-
function
|
|
5163
|
+
function main28() {
|
|
4900
5164
|
const command = args[0];
|
|
4901
5165
|
if (!command || command === "--help" || command === "-h") {
|
|
4902
5166
|
console.log(USAGE);
|
|
@@ -4971,4 +5235,4 @@ function handleCheckTranslations() {
|
|
|
4971
5235
|
console.warn("This command is not implemented yet!");
|
|
4972
5236
|
process.exit(1);
|
|
4973
5237
|
}
|
|
4974
|
-
|
|
5238
|
+
main28();
|