agoric 0.22.0-upgrade-14-dev-c8f9e7b.0 → 0.22.0-upgrade-16-dev-07b0130.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.
@@ -11,11 +11,6 @@ import { Offers } from '@agoric/inter-protocol/src/clientSupport.js';
11
11
  import { objectMap } from '@agoric/internal';
12
12
  import { M, matches } from '@agoric/store';
13
13
 
14
- // XXX scare away ambient type zombies to fix ScheduleNotification.activeStartTime etc.
15
- // https://github.com/Agoric/agoric-sdk/issues/6512
16
- // https://github.com/Agoric/agoric-sdk/issues/6343
17
- import '@agoric/inter-protocol/src/vaultFactory/types.js';
18
-
19
14
  import { normalizeAddressWithOptions, pollBlocks } from '../lib/chain.js';
20
15
  import {
21
16
  asBoardRemote,
@@ -38,16 +33,16 @@ const bidInvitationShape = harden({
38
33
  callPipe: [['makeBidInvitation', M.any()]],
39
34
  });
40
35
 
41
- /** @typedef {import('@agoric/vats/tools/board-utils.js').VBankAssetDetail } AssetDescriptor */
42
- /** @typedef {import('@agoric/smart-wallet/src/smartWallet').TryExitOfferAction } TryExitOfferAction */
43
- /** @typedef {import('@agoric/inter-protocol/src/auction/auctionBook.js').OfferSpec} BidSpec */
44
- /** @typedef {import('@agoric/inter-protocol/src/auction/scheduler.js').ScheduleNotification} ScheduleNotification */
45
- /** @typedef {import('@agoric/inter-protocol/src/auction/auctionBook.js').BookDataNotification} BookDataNotification */
36
+ /** @import {VBankAssetDetail} from '@agoric/vats/tools/board-utils.js'; */
37
+ /** @import {TryExitOfferAction} from '@agoric/smart-wallet/src/smartWallet.js'; */
38
+ /** @import {OfferSpec as BidSpec} from '@agoric/inter-protocol/src/auction/auctionBook.js' */
39
+ /** @import {ScheduleNotification} from '@agoric/inter-protocol/src/auction/scheduler.js' */
40
+ /** @import {BookDataNotification} from '@agoric/inter-protocol/src/auction/auctionBook.js' */
46
41
 
47
42
  /**
48
43
  * Format amounts, prices etc. based on brand board Ids, displayInfo
49
44
  *
50
- * @param {AssetDescriptor[]} assets
45
+ * @param {VBankAssetDetail[]} assets
51
46
  */
52
47
  const makeFormatters = assets => {
53
48
  const r4 = x => Math.round(x * 10_000) / 10_000;
@@ -70,12 +65,12 @@ const makeFormatters = assets => {
70
65
  r4(100 - (Number(r.numerator.value) / Number(r.denominator.value)) * 100);
71
66
 
72
67
  // XXX real TimeMath.absValue requires real Remotable timerBrand
73
- /** @param {import('@agoric/time/src/types.js').Timestamp} ts */
68
+ /** @param {import('@agoric/time').Timestamp} ts */
74
69
  const absValue = ts => (typeof ts === 'bigint' ? ts : ts.absValue);
75
70
 
76
- /** @param {import('@agoric/time/src/types.js').Timestamp} tr */
71
+ /** @param {import('@agoric/time').Timestamp} tr */
77
72
  const absTime = tr => new Date(Number(absValue(tr)) * 1000).toISOString();
78
- /** @param {import('@agoric/time/src/types.js').RelativeTimeRecord} tr */
73
+ /** @param {import('@agoric/time').RelativeTimeRecord} tr */
79
74
  const relTime = tr =>
80
75
  new Date(Number(tr.relValue) * 1000).toISOString().slice(11, 19);
81
76
 
@@ -87,7 +82,7 @@ const makeFormatters = assets => {
87
82
  * @param {(_: T) => string} f
88
83
  * @returns { (x: T | null | undefined ) => string | undefined }
89
84
  */
90
- const maybe = f => x => x ? f(x) : undefined;
85
+ const maybe = f => x => (x ? f(x) : undefined);
91
86
 
92
87
  return {
93
88
  amount,
@@ -107,7 +102,7 @@ const makeFormatters = assets => {
107
102
  * Dynamic check that an OfferStatus is also a BidSpec.
108
103
  *
109
104
  * @param {import('@agoric/smart-wallet/src/offers.js').OfferStatus} offerStatus
110
- * @param {Awaited<ReturnType<import('../lib/rpc').makeAgoricNames>>} agoricNames
105
+ * @param {import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes} agoricNames
111
106
  * @param {typeof console.warn} warn
112
107
  * returns null if offerStatus is not a BidSpec
113
108
  */
@@ -143,7 +138,7 @@ const coerceBid = (offerStatus, agoricNames, warn) => {
143
138
  *
144
139
  * @param {import('@agoric/smart-wallet/src/offers.js').OfferStatus &
145
140
  * { offerArgs: BidSpec}} bid
146
- * @param {import('agoric/src/lib/format.js').AssetDescriptor[]} assets
141
+ * @param {VBankAssetDetail[]} assets
147
142
  */
148
143
  export const fmtBid = (bid, assets) => {
149
144
  const fmt = makeFormatters(assets);
@@ -205,6 +200,10 @@ export const makeInterCommand = (
205
200
  const interCmd = createCommand('inter')
206
201
  .description('Inter Protocol commands for liquidation bidding etc.')
207
202
  .option('--home <dir>', 'agd CosmosSDK application home directory')
203
+ .option(
204
+ '--fees <amount>',
205
+ 'set fees for transaction broadcast (e.g. 5000ubld)',
206
+ )
208
207
  .option(
209
208
  '--keyring-backend <os|file|test>',
210
209
  `keyring's backend (os|file|test) (default "${
@@ -237,7 +236,6 @@ export const makeInterCommand = (
237
236
  try {
238
237
  // XXX pass fetch to getNetworkConfig() explicitly
239
238
  // await null above makes this await safe
240
- // eslint-disable-next-line @jessie.js/no-nested-await
241
239
  const networkConfig = await getNetworkConfig(env);
242
240
  return makeWalletUtils({ fetch, execFileSync, delay }, networkConfig);
243
241
  } catch (err) {
@@ -338,10 +336,10 @@ inter auction status
338
336
  const { networkConfig, agoricNames, pollOffer } = tools;
339
337
  const io = { ...networkConfig, execFileSync, delay, stdout };
340
338
 
341
- const { home, keyringBackend: backend } = interCmd.opts();
339
+ const { home, keyringBackend: backend, fees } = interCmd.opts();
342
340
  const result = await sendAction(
343
341
  { method: 'executeOffer', offer },
344
- { keyring: { home, backend }, from, verbose: false, dryRun, ...io },
342
+ { keyring: { home, backend }, from, fees, verbose: false, dryRun, ...io },
345
343
  );
346
344
  if (dryRun) {
347
345
  return;
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable @jessie.js/no-nested-await */
3
1
  // @ts-check
4
2
  /* eslint-disable func-names */
5
3
  /* global fetch, setTimeout, process */
@@ -9,6 +7,7 @@ import { Nat } from '@endo/nat';
9
7
  import { Command } from 'commander';
10
8
  import * as cp from 'child_process';
11
9
  import { inspect } from 'util';
10
+ import { oracleBrandFeedName } from '@agoric/inter-protocol/src/proposals/utils.js';
12
11
  import { normalizeAddressWithOptions } from '../lib/chain.js';
13
12
  import { getNetworkConfig, makeRpcUtils, storageHelper } from '../lib/rpc.js';
14
13
  import {
@@ -16,9 +15,12 @@ import {
16
15
  makeWalletUtils,
17
16
  outputAction,
18
17
  sendAction,
18
+ sendHint,
19
19
  } from '../lib/wallet.js';
20
20
  import { bigintReplacer } from '../lib/format.js';
21
21
 
22
+ /** @import {PriceAuthority, PriceDescription, PriceQuote, PriceQuoteValue, PriceQuery,} from '@agoric/zoe/tools/types.js'; */
23
+
22
24
  // XXX support other decimal places
23
25
  const COSMOS_UNIT = 1_000_000n;
24
26
  const scaleDecimals = num => BigInt(num * Number(COSMOS_UNIT));
@@ -82,7 +84,7 @@ export const makeOracleCommand = (logger, io = {}) => {
82
84
  const utils = await makeRpcUtils({ fetch });
83
85
 
84
86
  const lookupPriceAggregatorInstance = ([brandIn, brandOut]) => {
85
- const name = `${brandIn}-${brandOut} price feed`;
87
+ const name = oracleBrandFeedName(brandIn, brandOut);
86
88
  const instance = utils.agoricNames.instance[name];
87
89
  if (!instance) {
88
90
  logger.debug('known instances:', utils.agoricNames.instance);
@@ -129,7 +131,7 @@ export const makeOracleCommand = (logger, io = {}) => {
129
131
  offer,
130
132
  });
131
133
 
132
- console.warn('Now execute the prepared offer');
134
+ console.warn(sendHint);
133
135
  });
134
136
 
135
137
  oracle
@@ -164,7 +166,7 @@ export const makeOracleCommand = (logger, io = {}) => {
164
166
  offer,
165
167
  });
166
168
 
167
- console.warn('Now execute the prepared offer');
169
+ console.warn(sendHint);
168
170
  });
169
171
 
170
172
  const findOracleCap = async (instance, from, readLatestHead) => {
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable @jessie.js/no-nested-await */
3
1
  // @ts-check
4
2
  /* eslint-disable func-names */
5
3
  /* global process */
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable @jessie.js/no-nested-await */
3
1
  // @ts-check
4
2
  /* eslint-disable func-names */
5
3
  /* global fetch, process */
@@ -153,15 +151,17 @@ export const makePsmCommand = logger => {
153
151
  .option('--giveMinted <DOLLARS>', 'amount of minted tokens to give', Number)
154
152
  .option('--feePct [%]', 'Gas fee percentage', Number)
155
153
  .option('--offerId <string>', 'Offer id', String, `swap-${Date.now()}`)
156
- .action(async function (
157
- /** @type {Parameters<typeof Offers.psm.swap>[2]} */ opts,
158
- ) {
159
- console.warn('running with options', opts);
160
- const { agoricNames, lookupPsmInstance } = await rpcTools();
161
- const instance = await lookupPsmInstance(opts.pair);
162
- const offer = Offers.psm.swap(agoricNames, instance, opts);
163
- outputExecuteOfferAction(offer);
164
- });
154
+ .action(
155
+ async function (
156
+ /** @type {Parameters<typeof Offers.psm.swap>[2]} */ opts,
157
+ ) {
158
+ console.warn('running with options', opts);
159
+ const { agoricNames, lookupPsmInstance } = await rpcTools();
160
+ const instance = await lookupPsmInstance(opts.pair);
161
+ const offer = Offers.psm.swap(agoricNames, instance, opts);
162
+ outputExecuteOfferAction(offer);
163
+ },
164
+ );
165
165
 
166
166
  psm
167
167
  .command('proposePauseOffers')
@@ -216,8 +216,6 @@ export const makePsmCommand = logger => {
216
216
  };
217
217
 
218
218
  outputExecuteOfferAction(offer);
219
-
220
- console.warn('Now execute the prepared offer');
221
219
  });
222
220
 
223
221
  psm
@@ -272,8 +270,6 @@ export const makePsmCommand = logger => {
272
270
  };
273
271
 
274
272
  outputExecuteOfferAction(offer);
275
-
276
- console.warn('Now execute the prepared offer');
277
273
  });
278
274
 
279
275
  return psm;
@@ -1,4 +1,3 @@
1
- /* eslint-disable @jessie.js/no-nested-await */
2
1
  // @ts-check
3
2
  /* eslint-disable func-names */
4
3
  /* global fetch, process */
@@ -38,7 +38,6 @@ export const makeTestCommand = (
38
38
  try {
39
39
  // XXX pass fetch to getNetworkConfig() explicitly
40
40
  // await null above makes this await safe
41
- // eslint-disable-next-line @jessie.js/no-nested-await
42
41
  const networkConfig = await getNetworkConfig(env);
43
42
  return makeWalletUtils({ fetch, execFileSync, delay }, networkConfig);
44
43
  } catch (err) {
@@ -83,7 +82,13 @@ export const makeTestCommand = (
83
82
  publicInvitationMaker: 'makeInvitation',
84
83
  },
85
84
  proposal: {
86
- want: { Tokens: { brand: agoricNames.brand.GoodStuff, value: 32n } },
85
+ want: {
86
+ Tokens: {
87
+ // @ts-expect-error BoardRemote not a Brand object
88
+ brand: agoricNames.brand.GoodStuff,
89
+ value: 32n,
90
+ },
91
+ },
87
92
  },
88
93
  };
89
94
  const result = await sendAction(
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-await-in-loop */
2
- /* eslint-disable @jessie.js/no-nested-await */
3
1
  // @ts-check
4
2
  /* eslint-disable func-names */
5
3
  /* global fetch, process */
@@ -56,7 +54,7 @@ export const makeVaultsCommand = logger => {
56
54
 
57
55
  vaults
58
56
  .command('open')
59
- .description('open a new vault')
57
+ .description('Prepare an offer to open a new vault')
60
58
  .requiredOption('--giveCollateral <number>', 'Collateral to give', Number)
61
59
  .requiredOption('--wantMinted <number>', 'Minted wants', Number)
62
60
  .option('--offerId <string>', 'Offer id', String, `openVault-${Date.now()}`)
@@ -78,7 +76,7 @@ export const makeVaultsCommand = logger => {
78
76
 
79
77
  vaults
80
78
  .command('adjust')
81
- .description('adjust an existing vault')
79
+ .description('Prepare an offer to adjust an existing vault')
82
80
  .requiredOption(
83
81
  '--from <address>',
84
82
  'wallet address literal or name',
@@ -123,7 +121,7 @@ export const makeVaultsCommand = logger => {
123
121
 
124
122
  vaults
125
123
  .command('close')
126
- .description('close an existing vault')
124
+ .description('Prepare an offer to close an existing vault')
127
125
  .requiredOption(
128
126
  '--from <address>',
129
127
  'wallet address literal or name',
@@ -8,7 +8,6 @@ import {
8
8
  makeLeader,
9
9
  makeLeaderFromRpcAddresses,
10
10
  } from '@agoric/casting';
11
- import { Command } from 'commander';
12
11
  import fs from 'fs';
13
12
  import util from 'util';
14
13
  import { execFileSync } from 'child_process';
@@ -25,20 +24,31 @@ import { coalesceWalletState, getCurrent } from '../lib/wallet.js';
25
24
 
26
25
  const SLEEP_SECONDS = 3;
27
26
 
28
- export const makeWalletCommand = async () => {
29
- const wallet = new Command('wallet')
30
- .description('wallet commands')
31
- .option('--home <dir>', 'agd application home directory')
32
- .option(
33
- '--keyring-backend <os|file|test>',
34
- 'keyring\'s backend (os|file|test) (default "os")',
35
- );
27
+ /**
28
+ * @param {import('commander').Command['command']} command
29
+ * @returns {Promise<import('commander').Command>}
30
+ */
31
+ export const makeWalletCommand = async command => {
32
+ /**
33
+ * @param {import('commander').Command} baseCmd
34
+ */
35
+ const withSharedTxOptions = baseCmd =>
36
+ baseCmd
37
+ .option('--home <dir>', 'agd application home directory')
38
+ .option(
39
+ '--keyring-backend <os|file|test>',
40
+ 'keyring\'s backend (os|file|test) (default "os")',
41
+ );
42
+ /** @typedef {{home?: string, keyringBackend: 'os' | 'file' | 'test'}} SharedTxOptions */
43
+
44
+ const wallet = withSharedTxOptions(command('wallet')).description(
45
+ 'wallet commands',
46
+ );
36
47
 
37
48
  const normalizeAddress = literalOrName =>
38
49
  normalizeAddressWithOptions(literalOrName, wallet.opts());
39
50
 
40
- wallet
41
- .command('provision')
51
+ withSharedTxOptions(wallet.command('provision'))
42
52
  .description('provision a Smart Wallet')
43
53
  .requiredOption(
44
54
  '--account [address]',
@@ -48,8 +58,14 @@ export const makeWalletCommand = async () => {
48
58
  .option('--spend', 'confirm you want to spend')
49
59
  .option('--nickname <string>', 'nickname to use', 'my-wallet')
50
60
  .action(function (opts) {
51
- const { account, nickname, spend } = opts;
52
- const { home, keyringBackend: backend } = wallet.opts();
61
+ /** @typedef {{account: string, spend?: boolean, nickname: 'my-wallet' | string }} Opts */
62
+ const {
63
+ account,
64
+ nickname,
65
+ spend,
66
+ home,
67
+ keyringBackend: backend,
68
+ } = /** @type {SharedTxOptions & Opts} */ ({ ...wallet.opts(), ...opts });
53
69
  const tx = ['provision-one', nickname, account, 'SMART_WALLET'];
54
70
  if (spend) {
55
71
  execSwingsetTransaction(tx, {
@@ -107,8 +123,7 @@ export const makeWalletCommand = async () => {
107
123
  console.log(offerObj.offer.id);
108
124
  });
109
125
 
110
- wallet
111
- .command('send')
126
+ withSharedTxOptions(wallet.command('send'))
112
127
  .description('send a prepared offer')
113
128
  .requiredOption(
114
129
  '--from [address]',
@@ -118,8 +133,14 @@ export const makeWalletCommand = async () => {
118
133
  .requiredOption('--offer [filename]', 'path to file with prepared offer')
119
134
  .option('--dry-run', 'spit out the command instead of running it')
120
135
  .action(function (opts) {
121
- const { dryRun, from, offer } = opts;
122
- const { home, keyringBackend: backend } = wallet.opts();
136
+ /** @typedef {{ from: string, offer: string, dryRun: boolean }} Opts */
137
+ const {
138
+ dryRun,
139
+ from,
140
+ offer,
141
+ home,
142
+ keyringBackend: backend,
143
+ } = /** @type {SharedTxOptions & Opts} */ ({ ...wallet.opts(), ...opts });
123
144
 
124
145
  const offerBody = fs.readFileSync(offer).toString();
125
146
  execSwingsetTransaction(['wallet-action', '--allow-spend', offerBody], {
package/src/cosmos.js CHANGED
@@ -14,10 +14,10 @@ export default async function cosmosMain(progname, rawArgs, powers, opts) {
14
14
  const pspawnEnv = { ...process.env };
15
15
  if (popts.verbose > 1) {
16
16
  // Enable verbose logs.
17
- pspawnEnv.DEBUG = 'agoric';
17
+ pspawnEnv.DEBUG = 'agoric:info';
18
18
  } else if (!popts.verbose) {
19
19
  // Disable more logs.
20
- pspawnEnv.DEBUG = '';
20
+ pspawnEnv.DEBUG = 'agoric:none';
21
21
  }
22
22
 
23
23
  const pspawn = makePspawn({ env: pspawnEnv, log, spawn, chalk });
package/src/helpers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* global process */
2
2
  // @ts-check
3
3
 
4
- /** @typedef {import('child_process').ChildProcess} ChildProcess */
4
+ /** @import { ChildProcess } from 'child_process' */
5
5
 
6
6
  export const getSDKBinaries = ({
7
7
  jsPfx = '../..',
@@ -41,6 +41,7 @@ export const makePspawn = ({
41
41
  * @param {string} cmd command name to run
42
42
  * @param {Array<string>} cargs arguments to the command
43
43
  * @param {object} param2
44
+ * @param {string} [param2.cwd]
44
45
  * @param {string | [string, string, string]} [param2.stdio] standard IO
45
46
  * specification
46
47
  * @param {Record<string, string | undefined>} [param2.env] environment
package/src/init.js CHANGED
@@ -1,11 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { makePspawn } from './helpers.js';
3
3
 
4
- // Ambient types. Needed only for dev but this does a runtime import.
5
- // https://github.com/Agoric/agoric-sdk/issues/6512
6
- import '@endo/captp/src/types.js';
7
- import '@agoric/swingset-vat/exported.js';
8
- import '@agoric/swingset-vat/src/vats/network/types.js';
4
+ /// <reference types="@endo/captp/src/types.js" />
9
5
 
10
6
  // Use either an absolute template URL, or find it relative to DAPP_URL_BASE.
11
7
  const gitURL = (relativeOrAbsoluteURL, base) => {
@@ -61,7 +57,6 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
61
57
  const path = `${DIR}/${dir}package.json`;
62
58
  log('rewriting ', path);
63
59
 
64
- // eslint-disable-next-line no-await-in-loop
65
60
  const contents = await fs.readFile(path, 'utf-8');
66
61
  const pkg = JSON.parse(contents.replace(/@DIR@/g, DIR));
67
62
  if (dir === '') {
@@ -75,7 +70,6 @@ export default async function initMain(_progname, rawArgs, priv, opts) {
75
70
  pkg.name = `${DIR}${pkg.name.substr(topLevelName.length)}`;
76
71
  const json = JSON.stringify(pkg, undefined, 2);
77
72
 
78
- // eslint-disable-next-line no-await-in-loop
79
73
  await fs.writeFile(path, json);
80
74
  }
81
75
 
package/src/install.js CHANGED
@@ -1,4 +1,4 @@
1
- /* global process AggregateError Buffer */
1
+ /* global process Buffer */
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { makePspawn } from './helpers.js';
@@ -9,8 +9,7 @@ const REQUIRED_AGORIC_START_PACKAGES = [
9
9
  '@agoric/cosmic-swingset',
10
10
  ];
11
11
 
12
- const filename = new URL(import.meta.url).pathname;
13
- const dirname = path.dirname(filename);
12
+ const dirname = path.dirname(new URL(import.meta.url).pathname);
14
13
 
15
14
  export default async function installMain(progname, rawArgs, powers, opts) {
16
15
  const { anylogger, fs, spawn } = powers;
@@ -41,9 +40,9 @@ export default async function installMain(progname, rawArgs, powers, opts) {
41
40
  p.childProcess.stdout.on('data', out => stdout.push(out));
42
41
  await p;
43
42
  const d = JSON.parse(Buffer.concat(stdout).toString('utf-8'));
44
- Object.entries(d).forEach(([name, { location }]) =>
45
- map.set(name, path.resolve(cwd, location)),
46
- );
43
+ for (const [name, { location }] of Object.entries(d)) {
44
+ map.set(name, path.resolve(cwd, location));
45
+ }
47
46
  return map;
48
47
  }
49
48
 
@@ -61,7 +60,6 @@ export default async function installMain(progname, rawArgs, powers, opts) {
61
60
  const yarnInstallEachWorktree = async (phase, ...flags) => {
62
61
  for await (const workTree of workTrees) {
63
62
  log.info(`yarn install ${phase} in ${workTree}`);
64
- // eslint-disable-next-line no-await-in-loop
65
63
  const yarnInstall = await pspawn(
66
64
  'yarn',
67
65
  [...linkFlags, 'install', ...flags],
@@ -175,9 +173,6 @@ export default async function installMain(progname, rawArgs, powers, opts) {
175
173
  ({ status }) => status !== 'fulfilled',
176
174
  );
177
175
  if (failures.length) {
178
- if (typeof AggregateError !== 'function') {
179
- throw failures[0].reason;
180
- }
181
176
  throw AggregateError(
182
177
  failures.map(({ reason }) => reason),
183
178
  'Failed to prune',
@@ -269,7 +264,9 @@ export default async function installMain(progname, rawArgs, powers, opts) {
269
264
  };
270
265
  await Promise.all(subdirs.map(removeNodeModulesSymlinks));
271
266
  } else {
272
- DEFAULT_SDK_PACKAGE_NAMES.forEach(name => sdkPackageToPath.set(name, null));
267
+ for (const name of DEFAULT_SDK_PACKAGE_NAMES) {
268
+ sdkPackageToPath.set(name, null);
269
+ }
273
270
  }
274
271
 
275
272
  if (forceSdkVersion !== undefined) {
@@ -288,6 +285,7 @@ export default async function installMain(progname, rawArgs, powers, opts) {
288
285
  await Promise.all(
289
286
  [...sdkPackageToPath.entries()].map(async ([pjName, dir]) => {
290
287
  const SUBOPTIMAL = false;
288
+ await null;
291
289
  if (SUBOPTIMAL) {
292
290
  // This use of yarn is noisy and slow.
293
291
  await pspawn('yarn', [...linkFlags, 'unlink', pjName]);
@@ -0,0 +1,102 @@
1
+ // @ts-check
2
+
3
+ /* global Buffer */
4
+
5
+ import assert from 'node:assert/strict';
6
+ import fs from 'node:fs';
7
+ import { join } from 'node:path';
8
+
9
+ import { ZipReader } from '@endo/zip';
10
+
11
+ /** @import {Bundle} from '@agoric/swingset-vat'; */
12
+ /** @import {CoreEvalPlan} from '@agoric/deploy-script-support/src/writeCoreEvalParts.js' */
13
+
14
+ const PACKAGE_NAME_RE = /(?<packageName>.*-v[\d.]+)\//;
15
+
16
+ /**
17
+ * @typedef {{ name: string, label: string, location: string, modules: Record<string, {compartment: string, module: string}>}} Compartment
18
+ */
19
+
20
+ /**
21
+ * @typedef CompartmentMap
22
+ * @property {string[]} tags
23
+ * @property {{compartment: string, module: string}} entry
24
+ * @property {Record<string, Compartment>} compartments
25
+ */
26
+
27
+ /** @param {Bundle} bundleObj*/
28
+ export const extractBundleInfo = async bundleObj => {
29
+ if (bundleObj.moduleFormat !== 'endoZipBase64') {
30
+ throw new Error('only endoZipBase64 is supported');
31
+ }
32
+
33
+ const contents = Buffer.from(bundleObj.endoZipBase64, 'base64');
34
+
35
+ const zipReader = new ZipReader(contents);
36
+ const { files } = zipReader;
37
+
38
+ const cmapEntry = files.get('compartment-map.json');
39
+ /** @type {CompartmentMap} */
40
+ const compartmentMap = JSON.parse(Buffer.from(cmapEntry.content).toString());
41
+
42
+ // XXX mapIter better but requires SES
43
+ const fileSizes = Object.fromEntries(
44
+ Array.from(files.values()).map(f => [
45
+ f.name,
46
+ // bundle contents are not compressed
47
+ f.content.length,
48
+ ]),
49
+ );
50
+
51
+ return { compartmentMap, fileSizes };
52
+ };
53
+
54
+ // UNTIL https://github.com/endojs/endo/issues/1656
55
+ /** @param {string} bundleFilename */
56
+ export const statBundle = async bundleFilename => {
57
+ const bundle = fs.readFileSync(bundleFilename, 'utf8');
58
+ /** @type {Bundle} */
59
+ const bundleObj = JSON.parse(bundle);
60
+ console.log('\nBUNDLE', bundleObj.moduleFormat, bundleFilename);
61
+
62
+ const info = await extractBundleInfo(bundleObj);
63
+ assert(info, 'no bundle info');
64
+
65
+ /** @type {Record<string, number>} */
66
+ const byPackage = {};
67
+ let totalSize = 0;
68
+ for (const [filename, size] of Object.entries(info.fileSizes)) {
69
+ totalSize += size;
70
+ if (filename === 'compartment-map.json') {
71
+ continue;
72
+ }
73
+ const { packageName } = filename.match(PACKAGE_NAME_RE)?.groups ?? {};
74
+ assert(packageName, `invalid filename ${filename}`);
75
+ byPackage[packageName] ||= 0;
76
+ byPackage[packageName] += size;
77
+ }
78
+
79
+ console.log('Sum of file sizes in each package:');
80
+ console.table(byPackage);
81
+
82
+ console.log('total size:', totalSize);
83
+ console.log('\nTo explore the contents:\n');
84
+ console.log(
85
+ ` DIR=$(mktemp -d); cat ${bundleFilename} | jq -r .endoZipBase64 | base64 -d | tar xC $DIR; open $DIR`,
86
+ );
87
+ };
88
+
89
+ /** @param {string} path */
90
+ export const statPlans = async path => {
91
+ const files = await fs.promises.readdir(path);
92
+ const planfiles = files.filter(f => f.endsWith('plan.json'));
93
+
94
+ for (const planfile of planfiles) {
95
+ /** @type {CoreEvalPlan} */
96
+ const plan = JSON.parse(fs.readFileSync(join(path, planfile), 'utf8'));
97
+ console.log('\n**\nPLAN', plan.name);
98
+ for (const bundle of plan.bundles) {
99
+ await statBundle(bundle.fileName);
100
+ }
101
+ }
102
+ };