dequanto 0.2.36 → 0.2.38
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/hardhat.config.js +10 -1
- package/lib/cjs/contracts/deploy/Deployments.js +33 -6
- package/lib/cjs/contracts/deploy/Deployments.js.map +1 -1
- package/lib/cjs/contracts/deploy/proxy/ProxyDeployment.js +37 -4
- package/lib/cjs/contracts/deploy/proxy/ProxyDeployment.js.map +1 -1
- package/lib/cjs/gen/Generator.js +15 -0
- package/lib/cjs/gen/Generator.js.map +1 -1
- package/lib/cjs/utils/$crypto.js +1 -1
- package/lib/cjs/utils/$crypto.js.map +1 -1
- package/lib/cjs/utils/$require.js +12 -5
- package/lib/cjs/utils/$require.js.map +1 -1
- package/lib/esm/contracts/deploy/Deployments.js.map +1 -1
- package/lib/esm/contracts/deploy/Deployments.mjs +33 -6
- package/lib/esm/contracts/deploy/proxy/ProxyDeployment.js.map +1 -1
- package/lib/esm/contracts/deploy/proxy/ProxyDeployment.mjs +37 -4
- package/lib/esm/gen/Generator.js.map +1 -1
- package/lib/esm/gen/Generator.mjs +15 -0
- package/lib/esm/utils/$crypto.js.map +1 -1
- package/lib/esm/utils/$crypto.mjs +1 -1
- package/lib/esm/utils/$require.js.map +1 -1
- package/lib/esm/utils/$require.mjs +12 -5
- package/lib/types/contracts/deploy/Deployments.d.ts +16 -10
- package/lib/types/contracts/deploy/proxy/ProxyDeployment.d.ts +2 -0
- package/lib/types/gen/Generator.d.ts +8 -0
- package/lib/types/utils/$require.d.ts +3 -1
- package/package.json +1 -1
- package/src/contracts/deploy/Deployments.ts +62 -18
- package/src/contracts/deploy/proxy/ProxyDeployment.ts +61 -16
- package/src/gen/Generator.ts +17 -0
- package/src/utils/$crypto.ts +1 -1
- package/src/utils/$require.ts +11 -5
|
@@ -7,8 +7,7 @@ import { TEth } from '@dequanto/models/TEth';
|
|
|
7
7
|
import { $abiUtils } from '@dequanto/utils/$abiUtils';
|
|
8
8
|
import { $contract } from '@dequanto/utils/$contract';
|
|
9
9
|
import { $require } from '@dequanto/utils/$require';
|
|
10
|
-
import { ParametersFromSecond } from '@dequanto/utils/types';
|
|
11
|
-
import { Constructor } from '@dequanto/utils/types';
|
|
10
|
+
import { Constructor, ParametersFromSecond } from '@dequanto/utils/types';
|
|
12
11
|
|
|
13
12
|
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
|
|
14
13
|
import { ContractVerifier } from '@dequanto/explorer/ContractVerifier';
|
|
@@ -16,13 +15,12 @@ import { HardhatWeb3Client } from '@dequanto/hardhat/HardhatWeb3Client';
|
|
|
16
15
|
import { LoggerService } from '@dequanto/loggers/LoggerService';
|
|
17
16
|
import { $is } from '@dequanto/utils/$is';
|
|
18
17
|
|
|
19
|
-
import {
|
|
20
|
-
import { DeploymentsStorage, IDeployment } from './storage/DeploymentsStorage';
|
|
18
|
+
import { $bytecode } from '@dequanto/evm/utils/$bytecode';
|
|
21
19
|
import { TAddress } from '@dequanto/models/TAddress';
|
|
22
|
-
import { $promise } from '@dequanto/utils/$promise';
|
|
23
20
|
import { l } from '@dequanto/utils/$logger';
|
|
24
|
-
import { $
|
|
25
|
-
import {
|
|
21
|
+
import { $promise } from '@dequanto/utils/$promise';
|
|
22
|
+
import { IBeacon, IBeaconProxy, IProxy, IProxyAdmin, ProxyDeployment } from './proxy/ProxyDeployment';
|
|
23
|
+
import { DeploymentsStorage, IDeployment } from './storage/DeploymentsStorage';
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
|
|
@@ -288,13 +286,11 @@ export class Deployments {
|
|
|
288
286
|
}
|
|
289
287
|
|
|
290
288
|
async ensureWithProxy<
|
|
291
|
-
T extends (TContract &
|
|
292
|
-
TInit extends
|
|
289
|
+
T extends (TContract & TInitializers<TInit>),
|
|
290
|
+
TInit extends TFunction
|
|
293
291
|
>(
|
|
294
292
|
CtorImpl: Constructor<T>,
|
|
295
|
-
opts?: TConstructorArgs<T> & TDeploymentOptions &
|
|
296
|
-
initialize?: ParametersFromSecond<T['initialize']>
|
|
297
|
-
}
|
|
293
|
+
opts?: TConstructorArgs<T> & TDeploymentOptions & TInitializerParams<T>
|
|
298
294
|
): Promise<{
|
|
299
295
|
// the Implementation Contract with the address set to Proxy
|
|
300
296
|
contract: T
|
|
@@ -322,7 +318,8 @@ export class Deployments {
|
|
|
322
318
|
deployment: opts?.deployment
|
|
323
319
|
});
|
|
324
320
|
|
|
325
|
-
let
|
|
321
|
+
let initData = serializeInitData(id, contractImpl, opts.initialize);
|
|
322
|
+
let { migrationData, migrationV } = serializeMigrationData(id, contractImpl, opts);
|
|
326
323
|
let implementationAddress = contractImplDeployment.implementation ?? contractImplDeployment.address;
|
|
327
324
|
|
|
328
325
|
let {
|
|
@@ -337,7 +334,9 @@ export class Deployments {
|
|
|
337
334
|
deployments: this,
|
|
338
335
|
implementation: {
|
|
339
336
|
address: implementationAddress,
|
|
340
|
-
initData
|
|
337
|
+
initData,
|
|
338
|
+
migrationData,
|
|
339
|
+
migrationV,
|
|
341
340
|
},
|
|
342
341
|
upgradeImplementation: opts.deployment?.upgradeProxy ?? this.opts?.whenUpgradeRequired !== 'ignore'
|
|
343
342
|
})
|
|
@@ -370,7 +369,7 @@ export class Deployments {
|
|
|
370
369
|
**/
|
|
371
370
|
async ensureWithBeacon<
|
|
372
371
|
T extends (TContract & { initialize?: TInit }),
|
|
373
|
-
TInit extends
|
|
372
|
+
TInit extends TFunction
|
|
374
373
|
>(
|
|
375
374
|
CtorImpl: Constructor<T>,
|
|
376
375
|
opts: TConstructorArgs<T> & TDeploymentOptions & {
|
|
@@ -426,7 +425,9 @@ export class Deployments {
|
|
|
426
425
|
deployments: this,
|
|
427
426
|
implementation: {
|
|
428
427
|
address: implementationAddress,
|
|
429
|
-
initData: data
|
|
428
|
+
initData: data,
|
|
429
|
+
// @TODO implement migrations for Beacons
|
|
430
|
+
migrationData: null
|
|
430
431
|
}
|
|
431
432
|
});
|
|
432
433
|
|
|
@@ -612,8 +613,26 @@ export class Deployments {
|
|
|
612
613
|
}
|
|
613
614
|
|
|
614
615
|
|
|
615
|
-
type
|
|
616
|
-
|
|
616
|
+
type TFunction = (...args: any[]) => any
|
|
617
|
+
type TInitializerName = 'initialize' | `initializeV${number}`
|
|
618
|
+
type TInitializers<TInit extends TFunction> = {
|
|
619
|
+
[K in TInitializerName]?: TInit
|
|
620
|
+
}
|
|
621
|
+
type TInitializerParams<T extends TInitializers<TFunction>> = {
|
|
622
|
+
initialize?: T['initialize'] extends TFunction
|
|
623
|
+
? ParametersFromSecond<T['initialize']>
|
|
624
|
+
: never
|
|
625
|
+
initializeV2?: T['initializeV2'] extends TFunction
|
|
626
|
+
? ParametersFromSecond<T['initializeV2']>
|
|
627
|
+
: never
|
|
628
|
+
initializeV3?: T['initializeV3'] extends TFunction
|
|
629
|
+
? ParametersFromSecond<T['initializeV3']>
|
|
630
|
+
: never
|
|
631
|
+
} & {
|
|
632
|
+
[K in Extract<keyof T, `initializeV${number}`>]?: T[K] extends TFunction
|
|
633
|
+
? ParametersFromSecond<T[K]>
|
|
634
|
+
: any
|
|
635
|
+
}
|
|
617
636
|
|
|
618
637
|
type TContract = ContractBase & { $constructor?: (...args: any[]) => any }
|
|
619
638
|
type TConstructorArgs<T extends TContract> = T['$constructor'] extends Function ? {
|
|
@@ -678,6 +697,31 @@ function serializeInitData(id: string, contract: ContractBase, initializeParams:
|
|
|
678
697
|
}
|
|
679
698
|
|
|
680
699
|
|
|
700
|
+
function serializeMigrationData(id: string, contract: ContractBase, opts: any) {
|
|
701
|
+
let rgx = /^initializeV(?<version>[\d+])$/;
|
|
702
|
+
let migrations = contract
|
|
703
|
+
.abi
|
|
704
|
+
.map(x => rgx.exec(x.name))
|
|
705
|
+
.filter(x => x != null)
|
|
706
|
+
.map(match => Number(match.groups.version))
|
|
707
|
+
;
|
|
708
|
+
if (migrations.length === 0) {
|
|
709
|
+
return { migrationData: null, migrationV: 1 };
|
|
710
|
+
}
|
|
711
|
+
let v = alot(migrations).max(x => x);
|
|
712
|
+
let key = `initializeV${v}`;
|
|
713
|
+
let migrationAbi = contract.abi.find(x => x.name === key);
|
|
714
|
+
let migrationParams = opts?.[key] ?? [];
|
|
715
|
+
if (migrationParams?.length !== migrationAbi.inputs.length) {
|
|
716
|
+
throw new Error(`Wrong number of arguments (${migrationParams?.length}) for initializer method (${migrationAbi.inputs.length}) in ${id}.`);
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
migrationData: $abiUtils.serializeMethodCallData(migrationAbi, migrationParams ?? []),
|
|
720
|
+
migrationV: v
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
|
|
681
725
|
/**
|
|
682
726
|
* Normalize the contract name by removing any versions from name
|
|
683
727
|
* "FooV1" is actually the "Foo" contract
|
|
@@ -15,6 +15,9 @@ import { $proxyDeploy } from './$proxyDeploy';
|
|
|
15
15
|
import { File } from 'atma-io';
|
|
16
16
|
import { IContractWrapped } from '@dequanto/contracts/ContractClassFactory';
|
|
17
17
|
import { HardhatProvider } from '@dequanto/hardhat/HardhatProvider';
|
|
18
|
+
import { $hex } from '@dequanto/utils/$hex';
|
|
19
|
+
import { SlotsStorage } from '@dequanto/solidity/SlotsStorage';
|
|
20
|
+
import { SlotsParser } from '@dequanto/solidity/SlotsParser';
|
|
18
21
|
|
|
19
22
|
export interface IProxy extends ContractBase {
|
|
20
23
|
changeAdmin?
|
|
@@ -24,14 +27,14 @@ export interface IProxyAdmin extends ContractBase {
|
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export interface IBeaconProxy extends ContractBase {
|
|
27
|
-
$constructor
|
|
30
|
+
$constructor(deployer: IAccount, beacon: TEth.Address, initData: TEth.Hex)
|
|
28
31
|
}
|
|
29
32
|
export interface IBeacon extends ContractBase {
|
|
30
|
-
$constructor
|
|
33
|
+
$constructor(deployer: IAccount, implementation: TEth.Address, initialOwner?: TEth.Address)
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
implementation(): Promise<TEth.Address>
|
|
34
|
-
upgradeTo
|
|
37
|
+
upgradeTo(sender: IAccount, newImplementation: TAddress)
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
interface IDeploymentCtx {
|
|
@@ -42,6 +45,8 @@ interface IDeploymentCtx {
|
|
|
42
45
|
implementation: {
|
|
43
46
|
address: TAddress
|
|
44
47
|
initData: TEth.Hex
|
|
48
|
+
migrationData: TEth.Hex
|
|
49
|
+
migrationV?: number
|
|
45
50
|
}
|
|
46
51
|
options?: {
|
|
47
52
|
skipStorageLayoutCheck?: boolean
|
|
@@ -117,7 +122,9 @@ export class ProxyDeployment {
|
|
|
117
122
|
} = deployments;
|
|
118
123
|
let {
|
|
119
124
|
address: implAddress,
|
|
120
|
-
initData
|
|
125
|
+
initData,
|
|
126
|
+
migrationData,
|
|
127
|
+
migrationV,
|
|
121
128
|
} = ctx.implementation;
|
|
122
129
|
let {
|
|
123
130
|
Proxy,
|
|
@@ -134,7 +141,7 @@ export class ProxyDeployment {
|
|
|
134
141
|
$require.notNull(ProxyAdmin, 'TransparentProxy.ProxyAdmin is required');
|
|
135
142
|
|
|
136
143
|
|
|
137
|
-
let proxyOpts = <Parameters<Deployments['ensure']>[1]>
|
|
144
|
+
let proxyOpts = <Parameters<Deployments['ensure']>[1]>{
|
|
138
145
|
id: proxyId,
|
|
139
146
|
// will not compare the contract updates, once deployed. As proxies normally not updated
|
|
140
147
|
latest: false,
|
|
@@ -150,8 +157,6 @@ export class ProxyDeployment {
|
|
|
150
157
|
let v = proxyAbi.some(x => x.name === 'upgradeToAndCall') || !proxyAbi.some(x => x.type === 'error') ? 'V4' : 'V5';
|
|
151
158
|
/** OpenZeppelin V5 hides admin/upgrade public methods and introduces "error" types*/
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
|
|
155
160
|
let hasProxy = await deployments.has(Proxy, proxyOpts);
|
|
156
161
|
let shouldUpdate = ctx.upgradeImplementation ?? true;
|
|
157
162
|
let {
|
|
@@ -206,6 +211,13 @@ export class ProxyDeployment {
|
|
|
206
211
|
if ($address.eq(address, implAddress) === false) {
|
|
207
212
|
if (shouldUpdate) {
|
|
208
213
|
await this.requireCompatibleStorageLayout(proxyId, ctx);
|
|
214
|
+
if ($hex.isEmpty(migrationData) === false) {
|
|
215
|
+
let version = await Interfaces.TransparentProxy[v].contractProxy.version(contractProxy);
|
|
216
|
+
if (migrationV <= version) {
|
|
217
|
+
// Clear migration call; This upgrade is raw implementation upgrade
|
|
218
|
+
migrationData = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
209
221
|
$logger.log(`Upgrading ProxyAdmin(${contractProxyAdmin.address}) to ${implAddress} (${v}) from ${address}`);
|
|
210
222
|
let receipt = await Interfaces.call(
|
|
211
223
|
ctx.owner ?? deployer,
|
|
@@ -213,7 +225,7 @@ export class ProxyDeployment {
|
|
|
213
225
|
Interfaces.TransparentProxy[v].contractProxyAdmin.upgradeAndCall,
|
|
214
226
|
contractProxy.address,
|
|
215
227
|
implAddress,
|
|
216
|
-
|
|
228
|
+
migrationData,
|
|
217
229
|
);
|
|
218
230
|
await this.saveStorageLayout(proxyId, ctx);
|
|
219
231
|
} else {
|
|
@@ -232,7 +244,7 @@ export class ProxyDeployment {
|
|
|
232
244
|
}
|
|
233
245
|
}
|
|
234
246
|
|
|
235
|
-
private async getOpenzeppelinUpgradable
|
|
247
|
+
private async getOpenzeppelinUpgradable(opts?: { proxy?: boolean, beacon?: boolean }) {
|
|
236
248
|
// We can't compile OpenZeppelin's contracts directly from node_modules folder, so create the wrappers
|
|
237
249
|
const baseSource = `./node_modules/@openzeppelin/contracts/proxy`;
|
|
238
250
|
const baseOutput = `./contracts/oz`;
|
|
@@ -250,7 +262,7 @@ export class ProxyDeployment {
|
|
|
250
262
|
import \"${deps.TransparentUpgradeableProxy}\";
|
|
251
263
|
`,
|
|
252
264
|
//install: `TransparentUpgradeableProxy,ProxyAdmin`,
|
|
253
|
-
contracts: [`TransparentUpgradeableProxy
|
|
265
|
+
contracts: [`TransparentUpgradeableProxy`, `ProxyAdmin`]
|
|
254
266
|
},
|
|
255
267
|
Beacon: {
|
|
256
268
|
source: `${baseSource}/beacon/UpgradeableBeacon.sol`,
|
|
@@ -260,7 +272,7 @@ export class ProxyDeployment {
|
|
|
260
272
|
import \"${deps.BeaconProxy}\";
|
|
261
273
|
`,
|
|
262
274
|
//install: `UpgradeableBeacon,BeaconProxy`,
|
|
263
|
-
contracts: [`UpgradeableBeacon
|
|
275
|
+
contracts: [`UpgradeableBeacon`, `BeaconProxy`],
|
|
264
276
|
}
|
|
265
277
|
};
|
|
266
278
|
|
|
@@ -271,7 +283,7 @@ export class ProxyDeployment {
|
|
|
271
283
|
delete paths.TransparentUpgradeableProxy;
|
|
272
284
|
}
|
|
273
285
|
|
|
274
|
-
function fmt
|
|
286
|
+
function fmt(template: string) {
|
|
275
287
|
let match = /^ +/m.exec(template);
|
|
276
288
|
return template.trim().replace(new RegExp(`^${match[0]}`, 'gm'), '');
|
|
277
289
|
}
|
|
@@ -342,12 +354,12 @@ export class ProxyDeployment {
|
|
|
342
354
|
? [
|
|
343
355
|
// address implementation
|
|
344
356
|
implAddress
|
|
345
|
-
] as [
|
|
357
|
+
] as [TEth.Address]
|
|
346
358
|
: [
|
|
347
359
|
// address implementation_, address initialOwner
|
|
348
360
|
implAddress,
|
|
349
361
|
deployer.address
|
|
350
|
-
] as [
|
|
362
|
+
] as [TEth.Address, TEth.Address]
|
|
351
363
|
};
|
|
352
364
|
let hasBeacon = await deployments.has(Beacon, beaconOpts);
|
|
353
365
|
let {
|
|
@@ -428,7 +440,7 @@ export class ProxyDeployment {
|
|
|
428
440
|
}
|
|
429
441
|
}
|
|
430
442
|
|
|
431
|
-
private getOzVersionByBeacon
|
|
443
|
+
private getOzVersionByBeacon(Beacon: Constructor<IBeacon>): 5 | 4 {
|
|
432
444
|
let $constructor = new Beacon().abi?.find(x => x.type === 'constructor');
|
|
433
445
|
$require.notNull($constructor, `Invalid Beacon contract: constructor not found`);
|
|
434
446
|
|
|
@@ -465,9 +477,31 @@ namespace Interfaces {
|
|
|
465
477
|
export const V4 = {
|
|
466
478
|
contractProxy: {
|
|
467
479
|
changeAdmin: 'changeAdmin(address newAdmin) external',
|
|
480
|
+
async version(contract: ContractBase) {
|
|
481
|
+
const slots = await SlotsParser.slotsFromAbi(`
|
|
482
|
+
(uint8 _version, bool initializing)
|
|
483
|
+
`);
|
|
484
|
+
const storage = SlotsStorage.createWithClient(
|
|
485
|
+
contract.client,
|
|
486
|
+
contract.address,
|
|
487
|
+
slots
|
|
488
|
+
);
|
|
489
|
+
let v = await storage.get('_version');
|
|
490
|
+
return v;
|
|
491
|
+
},
|
|
468
492
|
},
|
|
469
493
|
contractProxyAdmin: {
|
|
470
494
|
async upgradeAndCall(account, contract, proxyAddress, implementationAddress, data) {
|
|
495
|
+
if (data != null && $hex.isEmpty(data) === false) {
|
|
496
|
+
return call(
|
|
497
|
+
account,
|
|
498
|
+
contract,
|
|
499
|
+
'upgradeAndCall(address proxy, address implementation, bytes data) external',
|
|
500
|
+
proxyAddress,
|
|
501
|
+
implementationAddress,
|
|
502
|
+
data,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
471
505
|
return call(
|
|
472
506
|
account,
|
|
473
507
|
contract,
|
|
@@ -480,7 +514,18 @@ namespace Interfaces {
|
|
|
480
514
|
}
|
|
481
515
|
export const V5 = {
|
|
482
516
|
contractProxy: {
|
|
483
|
-
|
|
517
|
+
async version(contract: ContractBase) {
|
|
518
|
+
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
|
|
519
|
+
const position = '0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00';
|
|
520
|
+
const slots = await SlotsParser.slotsFromAbi(`
|
|
521
|
+
(uint8 initialized, bool initializing)
|
|
522
|
+
`);
|
|
523
|
+
const storage = SlotsStorage.createWithClient(contract.client, contract.address, slots, {
|
|
524
|
+
storageOffset: position
|
|
525
|
+
});
|
|
526
|
+
const v = await storage.get('initialized');
|
|
527
|
+
return v;
|
|
528
|
+
},
|
|
484
529
|
},
|
|
485
530
|
contractProxyAdmin: {
|
|
486
531
|
upgradeAndCall: 'upgradeAndCall(address proxy, address implementation, bytes memory data) external'
|
package/src/gen/Generator.ts
CHANGED
|
@@ -108,6 +108,23 @@ export class Generator {
|
|
|
108
108
|
return generator.generate();
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
static async generateFromJson (artifact: string) {
|
|
112
|
+
$require.True(await File.existsAsync(artifact), `${artifact} does not exist`);
|
|
113
|
+
let name = /(?<contractName>[^\\/]+).json$/.exec(artifact)?.groups?.contractName;
|
|
114
|
+
$require.notEmpty(name, `Contract name not resolved from the path ${artifact}`);
|
|
115
|
+
|
|
116
|
+
let generator = new Generator({
|
|
117
|
+
platform: 'hardhat',
|
|
118
|
+
name: name,
|
|
119
|
+
source: {
|
|
120
|
+
path: artifact
|
|
121
|
+
},
|
|
122
|
+
output: './0xc/hardhat/',
|
|
123
|
+
saveSources: false,
|
|
124
|
+
});
|
|
125
|
+
return generator.generate();
|
|
126
|
+
}
|
|
127
|
+
|
|
111
128
|
/**
|
|
112
129
|
* @deprecated Was possible to generate the Contract Class based on the meta information header in TS file
|
|
113
130
|
*/
|
package/src/utils/$crypto.ts
CHANGED
|
@@ -49,7 +49,7 @@ abstract class CryptoBase implements ICrypto {
|
|
|
49
49
|
const buffer = utils.toBuffer(mix);
|
|
50
50
|
$require.gt(buffer.length, 0, `Buffer to encrypt must be a non-empty`);
|
|
51
51
|
const secret = opts.secret;
|
|
52
|
-
$require.
|
|
52
|
+
$require.notEmpty(secret, `Secret must be a non-empty`);
|
|
53
53
|
const bufferSecret = await this.prepareSecret(secret);
|
|
54
54
|
|
|
55
55
|
let encrypted = await this.encryptInner(buffer, bufferSecret);
|
package/src/utils/$require.ts
CHANGED
|
@@ -74,14 +74,20 @@ export namespace $require {
|
|
|
74
74
|
}
|
|
75
75
|
return val;
|
|
76
76
|
}
|
|
77
|
-
export function notEmpty<T extends string | Array<any
|
|
77
|
+
export function notEmpty<T extends string | Array<any> | { length: number }> (val: T, message: string): T {
|
|
78
78
|
if (val == null) {
|
|
79
79
|
throw new Error(`Value is undefined. ${message}`);
|
|
80
80
|
}
|
|
81
|
-
if (typeof val === 'string'
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
if (typeof val === 'string') {
|
|
82
|
+
if (val.trim().length === 0) {
|
|
83
|
+
throw new Error(`Value is empty string. ${message}`);
|
|
84
|
+
}
|
|
85
|
+
} else if (typeof val ==='object' && 'length' in val) {
|
|
86
|
+
if (val.length === 0) {
|
|
87
|
+
throw new Error(`Value is empty array. ${message}`);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
throw new Error(`Invalid type for notEmpty check: ${typeof val}: ${val}`);
|
|
85
91
|
}
|
|
86
92
|
return val;
|
|
87
93
|
}
|