@canton-network/wallet-gateway-remote 0.27.0 → 1.1.0
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/dist/example-config.d.ts +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/ledger/wallet-sync-service.d.ts +2 -1
- package/dist/ledger/wallet-sync-service.d.ts.map +1 -1
- package/dist/ledger/wallet-sync-service.js +63 -47
- package/dist/ledger/wallet-sync-service.test.js +169 -50
- package/dist/user-api/controller.d.ts.map +1 -1
- package/dist/user-api/controller.js +34 -5
- package/dist/user-api/rpc-gen/typings.d.ts +16 -1
- package/dist/user-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/web/frontend/404/index.html +2 -2
- package/dist/web/frontend/activities/index.html +3 -3
- package/dist/web/frontend/approve/index.html +5 -5
- package/dist/web/frontend/assets/404-BCjBS7OU.js +5 -0
- package/dist/web/frontend/assets/{activities-CPXS4OC3.js → activities-4fnnetzm.js} +1 -1
- package/dist/web/frontend/assets/{addIdentityProvider-BJjhu--M.js → addIdentityProvider-uy13wDEx.js} +2 -2
- package/dist/web/frontend/assets/{addNetwork-B0olMmyv.js → addNetwork-B9yARLXq.js} +2 -2
- package/dist/web/frontend/assets/{addParty-PUjps7Ie.js → addParty-4qt8FiJv.js} +1 -1
- package/dist/web/frontend/assets/{approve-CARzty9R.js → approve-DUxX18Xm.js} +1 -1
- package/dist/web/frontend/assets/{callback-BqE26RlP.js → callback-BGMELOxy.js} +1 -1
- package/dist/web/frontend/assets/{identityProviders-Cnx8VIsD.js → identityProviders-ByQNvF7K.js} +1 -1
- package/dist/web/frontend/assets/{index-nOmkqkbK.js → index-DcZcHMS1.js} +1 -1
- package/dist/web/frontend/assets/{index-CgsIALyR.js → index-DvQio60y.js} +1 -1
- package/dist/web/frontend/assets/{index-UdyYJ_nU.js → index-lfC4c213.js} +83 -83
- package/dist/web/frontend/assets/{login-CkpIib74.js → login-BtNyAQqL.js} +2 -2
- package/dist/web/frontend/assets/{networks-D7iP_lGh.js → networks-B8bwNVkz.js} +1 -1
- package/dist/web/frontend/assets/{reviewIdentityProvider-BRb38sGH.js → reviewIdentityProvider-CWDFtb4t.js} +3 -3
- package/dist/web/frontend/assets/{reviewNetwork-BVdYGugz.js → reviewNetwork-ZgS_rpXv.js} +2 -2
- package/dist/web/frontend/assets/{settings-CxEU64oG.js → settings-BiWW1vj2.js} +1 -1
- package/dist/web/frontend/assets/{state-CypMU0cY.js → state-BpQr_22x.js} +1 -1
- package/dist/web/frontend/assets/{utils-V1qa66Nv.js → utils-BNCwV3PT.js} +1 -1
- package/dist/web/frontend/callback/index.html +2 -2
- package/dist/web/frontend/identity-providers/add/index.html +3 -3
- package/dist/web/frontend/identity-providers/index.html +3 -3
- package/dist/web/frontend/identity-providers/review/index.html +3 -3
- package/dist/web/frontend/index.html +1 -1
- package/dist/web/frontend/login/index.html +4 -4
- package/dist/web/frontend/networks/add/index.html +3 -3
- package/dist/web/frontend/networks/index.html +3 -3
- package/dist/web/frontend/networks/review/index.html +3 -3
- package/dist/web/frontend/parties/add/index.html +5 -5
- package/dist/web/frontend/parties/index.html +4 -4
- package/dist/web/frontend/settings/index.html +3 -3
- package/package.json +20 -20
- package/dist/web/frontend/assets/404-CEXKCUlr.js +0 -5
package/dist/example-config.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ declare const _default: {
|
|
|
34
34
|
id: string;
|
|
35
35
|
type: "self_signed";
|
|
36
36
|
issuer: string;
|
|
37
|
-
configUrl?:
|
|
37
|
+
configUrl?: undefined;
|
|
38
38
|
} | {
|
|
39
39
|
id: string;
|
|
40
40
|
type: "oauth";
|
|
@@ -61,7 +61,7 @@ declare const _default: {
|
|
|
61
61
|
audience: string;
|
|
62
62
|
clientId: string;
|
|
63
63
|
clientSecret: string;
|
|
64
|
-
clientSecretEnv?:
|
|
64
|
+
clientSecretEnv?: undefined;
|
|
65
65
|
};
|
|
66
66
|
ledgerApi: {
|
|
67
67
|
baseUrl: string;
|
|
@@ -76,8 +76,8 @@ declare const _default: {
|
|
|
76
76
|
clientId: string;
|
|
77
77
|
scope: string;
|
|
78
78
|
audience: string;
|
|
79
|
-
issuer?:
|
|
80
|
-
clientSecret?:
|
|
79
|
+
issuer?: undefined;
|
|
80
|
+
clientSecret?: undefined;
|
|
81
81
|
};
|
|
82
82
|
adminAuth: {
|
|
83
83
|
method: "client_credentials";
|
|
@@ -85,8 +85,8 @@ declare const _default: {
|
|
|
85
85
|
audience: string;
|
|
86
86
|
clientId: string;
|
|
87
87
|
clientSecretEnv: string;
|
|
88
|
-
issuer?:
|
|
89
|
-
clientSecret?:
|
|
88
|
+
issuer?: undefined;
|
|
89
|
+
clientSecret?: undefined;
|
|
90
90
|
};
|
|
91
91
|
ledgerApi: {
|
|
92
92
|
baseUrl: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,9 @@ declare const options: {
|
|
|
3
3
|
config: string;
|
|
4
4
|
configSchema: boolean;
|
|
5
5
|
configExample: boolean;
|
|
6
|
-
port?: string | true;
|
|
7
|
-
logFormat?: "json" | "pretty";
|
|
8
|
-
logLevel?: "error" | "trace" | "debug" | "info" | "warn" | "fatal";
|
|
6
|
+
port?: string | true | undefined;
|
|
7
|
+
logFormat?: "json" | "pretty" | undefined;
|
|
8
|
+
logLevel?: "error" | "trace" | "debug" | "info" | "warn" | "fatal" | undefined;
|
|
9
9
|
};
|
|
10
10
|
export type CliOptions = typeof options;
|
|
11
11
|
export {};
|
|
@@ -16,7 +16,8 @@ export declare class WalletSyncService {
|
|
|
16
16
|
private static readonly EMPTY_RIGHTS;
|
|
17
17
|
private sameRights;
|
|
18
18
|
run(timeoutMs: number): Promise<void>;
|
|
19
|
-
|
|
19
|
+
private getParticipantNamespace;
|
|
20
|
+
protected resolveSigningProvider(partyNamespace: string, participantNamespace: string): Promise<{
|
|
20
21
|
signingProviderId: SigningProvider.PARTICIPANT;
|
|
21
22
|
matched: boolean;
|
|
22
23
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet-sync-service.d.ts","sourceRoot":"","sources":["../../src/ledger/wallet-sync-service.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,YAAY,EAEf,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,EACH,KAAK,EAIR,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAGlE,qBAAa,iBAAiB;IAEtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IAGtB,OAAO,CAAC,cAAc;gBAPd,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,OAAO,CAC3B,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,YAAK,EACE,cAAc,EAAE,sBAAsB;IAGlD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAwB;IAE5D,OAAO,CAAC,UAAU;IAUZ,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"wallet-sync-service.d.ts","sourceRoot":"","sources":["../../src/ledger/wallet-sync-service.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,YAAY,EAEf,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,EACH,KAAK,EAIR,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAGlE,qBAAa,iBAAiB;IAEtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IAGtB,OAAO,CAAC,cAAc;gBAPd,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,OAAO,CAC3B,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,YAAK,EACE,cAAc,EAAE,sBAAsB;IAGlD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAwB;IAE5D,OAAO,CAAC,UAAU;IAUZ,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAU7B,uBAAuB;cAkBrB,sBAAsB,CAClC,cAAc,EAAE,MAAM,EACtB,oBAAoB,EAAE,MAAM,GAC7B,OAAO,CACJ;QACI,iBAAiB,EAAE,eAAe,CAAC,WAAW,CAAA;QAC9C,OAAO,EAAE,OAAO,CAAA;KACnB,GACD;QACI,iBAAiB,EAAE,OAAO,CACtB,eAAe,EACf,eAAe,CAAC,WAAW,CAC9B,CAAA;QACD,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CACN;YAwGa,iBAAiB;IA8DzB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;YA8D9B,yBAAyB;YA2EzB,0BAA0B;YAoD1B,mBAAmB;IAsB3B,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAgHlD"}
|
|
@@ -28,27 +28,23 @@ export class WalletSyncService {
|
|
|
28
28
|
await new Promise((res) => setTimeout(res, timeoutMs));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
async
|
|
31
|
+
async getParticipantNamespace() {
|
|
32
|
+
const { participantId } = await this.ledgerClient.getWithRetry('/v2/parties/participant-id', defaultRetryableOptions);
|
|
33
|
+
// Extract the namespace part from participantId
|
|
34
|
+
// Format is hint::namespace
|
|
35
|
+
const [, extractedNamespace] = participantId.split('::');
|
|
36
|
+
if (extractedNamespace) {
|
|
37
|
+
return extractedNamespace;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new Error(`Invalid participantId format: expected "hint::namespace", got "${participantId}"`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Protected for tests
|
|
44
|
+
async resolveSigningProvider(partyNamespace, participantNamespace) {
|
|
32
45
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
let participantNamespace;
|
|
36
|
-
try {
|
|
37
|
-
const { participantId } = await this.ledgerClient.getWithRetry('/v2/parties/participant-id', defaultRetryableOptions);
|
|
38
|
-
// Extract the namespace part from participantId
|
|
39
|
-
// Format is hint::namespace
|
|
40
|
-
const [, extractedNamespace] = participantId.split('::');
|
|
41
|
-
if (extractedNamespace) {
|
|
42
|
-
participantNamespace = extractedNamespace;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
this.logger.warn({ participantId }, `Invalid participantId format: expected "hint::namespace", got "${participantId}"`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
this.logger.warn({ err }, 'Failed to get participant namespace');
|
|
50
|
-
}
|
|
51
|
-
if (participantNamespace && namespace === participantNamespace) {
|
|
46
|
+
if (participantNamespace &&
|
|
47
|
+
partyNamespace === participantNamespace) {
|
|
52
48
|
return {
|
|
53
49
|
signingProviderId: SigningProvider.PARTICIPANT,
|
|
54
50
|
matched: true,
|
|
@@ -82,9 +78,9 @@ export class WalletSyncService {
|
|
|
82
78
|
if (!normalizedKey)
|
|
83
79
|
continue;
|
|
84
80
|
const keyNamespace = this.partyAllocator.createFingerprintFromKey(normalizedKey);
|
|
85
|
-
if (keyNamespace ===
|
|
81
|
+
if (keyNamespace === partyNamespace) {
|
|
86
82
|
this.logger.info({
|
|
87
|
-
namespace,
|
|
83
|
+
namespace: partyNamespace,
|
|
88
84
|
providerId,
|
|
89
85
|
keyId: key.id,
|
|
90
86
|
publicKey: key.publicKey,
|
|
@@ -104,14 +100,14 @@ export class WalletSyncService {
|
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
// No match found - use participant as default provider
|
|
107
|
-
this.logger.warn({ namespace }, 'No signing provider match found for namespace, using participant as default and marking wallet as unmatched (disabled)');
|
|
103
|
+
this.logger.warn({ namespace: partyNamespace }, 'No signing provider match found for namespace, using participant as default and marking wallet as unmatched (disabled)');
|
|
108
104
|
return {
|
|
109
105
|
signingProviderId: SigningProvider.PARTICIPANT,
|
|
110
106
|
matched: false,
|
|
111
107
|
};
|
|
112
108
|
}
|
|
113
109
|
catch (err) {
|
|
114
|
-
this.logger.error({ err, namespace }, 'Error resolving signing provider, using participant as default and marking wallet as unmatched (disabled)');
|
|
110
|
+
this.logger.error({ err, namespace: partyNamespace }, 'Error resolving signing provider, using participant as default and marking wallet as unmatched (disabled)');
|
|
115
111
|
// On error, use participant as default but mark as unmatched
|
|
116
112
|
return {
|
|
117
113
|
signingProviderId: SigningProvider.PARTICIPANT,
|
|
@@ -217,12 +213,12 @@ export class WalletSyncService {
|
|
|
217
213
|
}
|
|
218
214
|
// Participant wallets: disable when party not on ledger (participant node reset, namespace changed).
|
|
219
215
|
// Other wallets: mark as initialized so user can re-allocate (e.g. after external signing).
|
|
220
|
-
async handleWalletsWithoutParty(
|
|
221
|
-
const walletsWithoutParty =
|
|
222
|
-
const
|
|
223
|
-
const
|
|
216
|
+
async handleWalletsWithoutParty(allocatedWallets, partiesWithRights) {
|
|
217
|
+
const walletsWithoutParty = allocatedWallets.filter((wallet) => !partiesWithRights.has(wallet.partyId));
|
|
218
|
+
const updatedToInitialized = [];
|
|
219
|
+
const updatedToDisabled = [];
|
|
224
220
|
for (const wallet of walletsWithoutParty) {
|
|
225
|
-
if (wallet.status !== 'allocated')
|
|
221
|
+
if (wallet.status !== 'allocated' || wallet.disabled)
|
|
226
222
|
continue;
|
|
227
223
|
try {
|
|
228
224
|
if (wallet.signingProviderId === SigningProvider.PARTICIPANT) {
|
|
@@ -230,6 +226,12 @@ export class WalletSyncService {
|
|
|
230
226
|
partyId: wallet.partyId,
|
|
231
227
|
signingProviderId: wallet.signingProviderId,
|
|
232
228
|
}, 'Participant wallet party not on ledger, disabling (participant namespace changed)');
|
|
229
|
+
const disabledWallet = {
|
|
230
|
+
...wallet,
|
|
231
|
+
disabled: true,
|
|
232
|
+
reason: WALLET_DISABLED_REASON.PARTICIPANT_NAMESPACE_CHANGED,
|
|
233
|
+
...(wallet.primary && { primary: false }),
|
|
234
|
+
};
|
|
233
235
|
await this.store.updateWallet({
|
|
234
236
|
partyId: wallet.partyId,
|
|
235
237
|
networkId: wallet.networkId,
|
|
@@ -237,7 +239,7 @@ export class WalletSyncService {
|
|
|
237
239
|
reason: WALLET_DISABLED_REASON.PARTICIPANT_NAMESPACE_CHANGED,
|
|
238
240
|
...(wallet.primary && { primary: false }),
|
|
239
241
|
});
|
|
240
|
-
|
|
242
|
+
updatedToDisabled.push(disabledWallet);
|
|
241
243
|
}
|
|
242
244
|
else {
|
|
243
245
|
this.logger.info({
|
|
@@ -250,7 +252,12 @@ export class WalletSyncService {
|
|
|
250
252
|
status: 'initialized',
|
|
251
253
|
...(wallet.primary && { primary: false }),
|
|
252
254
|
});
|
|
253
|
-
|
|
255
|
+
const reinitialized = {
|
|
256
|
+
...wallet,
|
|
257
|
+
status: 'initialized',
|
|
258
|
+
...(wallet.primary && { primary: false }),
|
|
259
|
+
};
|
|
260
|
+
updatedToInitialized.push(reinitialized);
|
|
254
261
|
}
|
|
255
262
|
}
|
|
256
263
|
catch (err) {
|
|
@@ -258,16 +265,15 @@ export class WalletSyncService {
|
|
|
258
265
|
}
|
|
259
266
|
}
|
|
260
267
|
return {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
disabledExistingWallets,
|
|
268
|
+
updatedToInitialized,
|
|
269
|
+
updatedToDisabled,
|
|
264
270
|
};
|
|
265
271
|
}
|
|
266
272
|
// Creates wallets for parties user has rights to
|
|
267
|
-
async handlePartiesWithoutWallet(newParties, networkId, rightsByParty) {
|
|
273
|
+
async handlePartiesWithoutWallet(newParties, networkId, rightsByParty, participantNamespace) {
|
|
268
274
|
return await Promise.all(newParties.map(async (partyId) => {
|
|
269
275
|
const [hint, namespace] = partyId.split('::');
|
|
270
|
-
const resolvedSigningProvider = await this.resolveSigningProvider(namespace);
|
|
276
|
+
const resolvedSigningProvider = await this.resolveSigningProvider(namespace, participantNamespace);
|
|
271
277
|
const isMatched = resolvedSigningProvider.matched;
|
|
272
278
|
const walletPublicKey = resolvedSigningProvider.signingProviderId ===
|
|
273
279
|
SigningProvider.PARTICIPANT
|
|
@@ -316,6 +322,7 @@ export class WalletSyncService {
|
|
|
316
322
|
async syncWallets() {
|
|
317
323
|
this.logger.info('Starting wallet sync...');
|
|
318
324
|
try {
|
|
325
|
+
const participantNamespace = await this.getParticipantNamespace();
|
|
319
326
|
const network = await this.store.getCurrentNetwork();
|
|
320
327
|
this.logger.info(network, 'Current network');
|
|
321
328
|
const { rightsByParty, rightsByUser } = await this.getRightsSnapshot();
|
|
@@ -332,13 +339,14 @@ export class WalletSyncService {
|
|
|
332
339
|
const newParties = partiesWithRights.filter((party) => !existingPartiesOnNetwork.has(`${party}:${network.id}`)
|
|
333
340
|
// todo: filter on idp id
|
|
334
341
|
);
|
|
335
|
-
const {
|
|
342
|
+
const { updatedToInitialized, updatedToDisabled } = await this.handleWalletsWithoutParty(existingAllocatedWallets, new Set(partiesWithRights));
|
|
336
343
|
const rightsUpdatedWallets = await this.handleRightsUpdates(existingAllocatedWallets, rightsByParty);
|
|
337
344
|
this.logger.info({
|
|
338
345
|
newParties,
|
|
339
|
-
|
|
346
|
+
updatedToInitialized: updatedToInitialized.map((w) => w.partyId),
|
|
347
|
+
updatedToDisabled: updatedToDisabled.map((w) => w.partyId),
|
|
340
348
|
}, 'Wallets without parties');
|
|
341
|
-
const newParticipantWallets = await this.handlePartiesWithoutWallet(newParties, network.id, rightsByParty);
|
|
349
|
+
const newParticipantWallets = await this.handlePartiesWithoutWallet(newParties, network.id, rightsByParty, participantNamespace);
|
|
342
350
|
// Set primary wallet if none exists, or if primary is on an initialized wallet
|
|
343
351
|
const networkWallets = await this.store.getWallets();
|
|
344
352
|
const primaryWallet = networkWallets.find((w) => w.primary);
|
|
@@ -349,19 +357,27 @@ export class WalletSyncService {
|
|
|
349
357
|
await this.store.setPrimaryWallet(allocatedWallets[0].partyId);
|
|
350
358
|
this.logger.info(`Set ${allocatedWallets[0].partyId} as primary wallet in network ${network.id}`);
|
|
351
359
|
}
|
|
360
|
+
const newWallets = newParticipantWallets;
|
|
361
|
+
const updatedRaw = [
|
|
362
|
+
...updatedToInitialized,
|
|
363
|
+
...rightsUpdatedWallets,
|
|
364
|
+
];
|
|
365
|
+
const added = newWallets.filter((wallet) => !wallet.disabled);
|
|
366
|
+
const updated = updatedRaw.filter((wallet) => !wallet.disabled);
|
|
352
367
|
const disabled = [
|
|
353
|
-
...
|
|
354
|
-
...
|
|
368
|
+
...newWallets.filter((wallet) => wallet.disabled),
|
|
369
|
+
...updatedRaw.filter((wallet) => wallet.disabled),
|
|
370
|
+
...updatedToDisabled,
|
|
355
371
|
];
|
|
356
372
|
this.logger.info({
|
|
357
|
-
added
|
|
358
|
-
updated
|
|
359
|
-
disabled
|
|
373
|
+
added,
|
|
374
|
+
updated,
|
|
375
|
+
disabled,
|
|
360
376
|
}, 'Wallet sync completed.');
|
|
361
377
|
return {
|
|
362
|
-
added
|
|
363
|
-
updated
|
|
364
|
-
disabled
|
|
378
|
+
added,
|
|
379
|
+
updated,
|
|
380
|
+
disabled,
|
|
365
381
|
};
|
|
366
382
|
}
|
|
367
383
|
catch (err) {
|