@everyprotocol/every-cli 0.1.6 → 0.1.8
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/cmdgen.js +10 -4
- package/dist/cmds/kind.js +124 -6
- package/dist/cmds/matter.js +22 -9
- package/dist/cmds/pick.js +27 -8
- package/dist/commander-patch.js +3 -4
- package/dist/logger.js +1 -1
- package/dist/utils.js +23 -0
- package/package.json +1 -1
package/dist/cmdgen.js
CHANGED
|
@@ -10,7 +10,7 @@ const getReadAction = (config, funName, abiFunc, abi) => async function readActi
|
|
|
10
10
|
const opts = this.opts();
|
|
11
11
|
const conf = FromOpts.getUniverseConfig(opts);
|
|
12
12
|
const address = await config.getContract(conf, this.processedArgs, abiFunc);
|
|
13
|
-
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.processedArgs, abiFunc);
|
|
13
|
+
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.processedArgs, abiFunc, opts);
|
|
14
14
|
const console = new Logger(opts);
|
|
15
15
|
const publicClient = createPublicClient({ transport: http(conf.rpc) });
|
|
16
16
|
const result = await publicClient.readContract({ address, abi, functionName: funName, args });
|
|
@@ -20,7 +20,7 @@ const getReadAction = (config, funName, abiFunc, abi) => async function readActi
|
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
21
|
const getWriteAction = (config, funcName, abiFunc, abi) => async function writeAction() {
|
|
22
22
|
const opts = this.opts();
|
|
23
|
-
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.args, abiFunc);
|
|
23
|
+
const args = (config.getFuncArgs ?? CommandGenDefaults.getFuncArgs)(this.args, abiFunc, opts);
|
|
24
24
|
const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
|
|
25
25
|
const address = config.getContract(conf, this.args, abiFunc);
|
|
26
26
|
const account = walletClient.account;
|
|
@@ -36,7 +36,8 @@ export const getCommandGen = (config) => function genCmd(cmdName) {
|
|
|
36
36
|
const abiFuncDoc = abiFuncs[0];
|
|
37
37
|
const description = abiFuncDoc._metadata?.notice || "";
|
|
38
38
|
const isRead = abiFuncDoc.stateMutability == "view" || abiFuncDoc.stateMutability == "pure";
|
|
39
|
-
const options = isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
|
|
39
|
+
// const options = isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
|
|
40
|
+
const options = (config.getCmdOpts ?? CommandGenDefaults.getCmdOpts)(abiFuncDoc);
|
|
40
41
|
const args = (config.getCmdArgs ?? CommandGenDefaults.getCmdArgs)(abiFuncDoc);
|
|
41
42
|
const abiContract = [...abiFuncs, ...abiNonFuncs];
|
|
42
43
|
const action = isRead
|
|
@@ -48,7 +49,12 @@ export function makeFuncName(cmdName, prefix) {
|
|
|
48
49
|
return `${prefix}${cmdName[0].toUpperCase()}${cmdName.slice(1)}`;
|
|
49
50
|
}
|
|
50
51
|
export const CommandGenDefaults = {
|
|
51
|
-
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
|
|
53
|
+
getFuncArgs: (args, abiFunc, opts) => args.map((arg, i) => coerceValue(arg, abiFunc.inputs[i])),
|
|
54
|
+
getCmdOpts: (abiFunc) => {
|
|
55
|
+
const isRead = abiFunc.stateMutability == "view" || abiFunc.stateMutability == "pure";
|
|
56
|
+
return isRead ? [universe, ...outputOptions] : [...writeOptions, ...outputOptions];
|
|
57
|
+
},
|
|
52
58
|
getCmdArgs: (abiFunc) => abiFunc.inputs.map((input) => {
|
|
53
59
|
const desc = abiFunc._metadata?.params?.[input.name] || `${input.type} parameter`;
|
|
54
60
|
return new Argument(`<${input.name}>`, desc);
|
package/dist/cmds/kind.js
CHANGED
|
@@ -1,14 +1,132 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
1
|
+
import { Argument, Command, Option } from "commander";
|
|
2
2
|
import { abi } from "../abi.js";
|
|
3
3
|
import { getCommandGen, makeFuncName } from "../cmdgen.js";
|
|
4
|
-
|
|
4
|
+
import { toHex } from "viem";
|
|
5
|
+
import { coerceValue, readKindElements, toElementType, toRelationId } from "../utils.js";
|
|
6
|
+
import { outputOptions, writeOptions } from "../commander-patch.js";
|
|
7
|
+
import { submitSimulation } from "../ethereum.js";
|
|
8
|
+
import { Logger } from "../logger.js";
|
|
9
|
+
import { FromOpts } from "../from-opts.js";
|
|
10
|
+
const genKindCmd = getCommandGen({
|
|
5
11
|
getFuncName: (cmdName) => makeFuncName(cmdName, `kind`),
|
|
6
12
|
getAbiFuncs: (funcName) => abi.funcs.kindRegistry.filter((i) => i.name == funcName),
|
|
7
13
|
// eslint-disable-next-line
|
|
8
14
|
getAbiNonFuncs: (funcName) => abi.nonFuncs.kindRegistry,
|
|
9
15
|
// eslint-disable-next-line
|
|
10
16
|
getContract: (conf, args, abiFunc) => conf.contracts.KindRegistry,
|
|
11
|
-
};
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
17
|
+
});
|
|
18
|
+
const registerCmd = genRegisterCmd();
|
|
19
|
+
const updateCmd = genUpdateCmd();
|
|
20
|
+
const otherCmds = "upgrade,touch,transfer,owner,descriptor,snapshot".split(",").map(genKindCmd);
|
|
21
|
+
export const kindCmd = new Command("kind")
|
|
22
|
+
.description("manage kinds")
|
|
23
|
+
.addCommand(registerCmd)
|
|
24
|
+
.addCommand(updateCmd)
|
|
25
|
+
.addCommands(otherCmds);
|
|
26
|
+
function genRegisterCmd() {
|
|
27
|
+
const funcName = "kindRegister";
|
|
28
|
+
const abiFuncs = abi.funcs.kindRegistry.filter((i) => i.name == funcName);
|
|
29
|
+
const abiNonFuncs = abi.nonFuncs.kindRegistry;
|
|
30
|
+
const code = new Argument("<code>", "Matter hash of the kind code");
|
|
31
|
+
const data = new Argument("<data>", "Matter hash of the kind data");
|
|
32
|
+
const etys = new Option("--elements <ety...>", "Element types");
|
|
33
|
+
const rels = new Option("--relations <rel...>", "IDs of relations supported");
|
|
34
|
+
const options = [etys, rels, ...writeOptions, ...outputOptions];
|
|
35
|
+
const args = [code, data];
|
|
36
|
+
function resolveElementTypes(args) {
|
|
37
|
+
return args.length == 0
|
|
38
|
+
? []
|
|
39
|
+
: args.length == 1 && /\.wasm$/i.test(args[0])
|
|
40
|
+
? readKindElements(args[0])
|
|
41
|
+
: args.map(toElementType);
|
|
42
|
+
}
|
|
43
|
+
function getFuncArgs(cmd) {
|
|
44
|
+
const opts = cmd.opts();
|
|
45
|
+
const etys = resolveElementTypes(opts.elements ?? []);
|
|
46
|
+
const rels = (opts.relations ?? []).map(toRelationId);
|
|
47
|
+
const args = cmd.args.map((arg, i) => coerceValue(arg, abiFuncs[0].inputs[i]));
|
|
48
|
+
args.push(etys, rels);
|
|
49
|
+
return args;
|
|
50
|
+
}
|
|
51
|
+
async function getCmdAction(cmd) {
|
|
52
|
+
const opts = cmd.opts();
|
|
53
|
+
const args = getFuncArgs(cmd);
|
|
54
|
+
const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
|
|
55
|
+
const address = conf.contracts.KindRegistry;
|
|
56
|
+
const account = walletClient.account;
|
|
57
|
+
const simulation = {
|
|
58
|
+
address,
|
|
59
|
+
abi: [...abiFuncs, ...abiNonFuncs],
|
|
60
|
+
functionName: funcName,
|
|
61
|
+
args,
|
|
62
|
+
account,
|
|
63
|
+
};
|
|
64
|
+
await submitSimulation(simulation, publicClient, walletClient, new Logger(opts));
|
|
65
|
+
}
|
|
66
|
+
return new Command("register")
|
|
67
|
+
.description("Register a new kind")
|
|
68
|
+
.addOptions(options)
|
|
69
|
+
.addArguments(args)
|
|
70
|
+
.action(getCmdAction);
|
|
71
|
+
}
|
|
72
|
+
function genUpdateCmd() {
|
|
73
|
+
const funcName = "kindUpdate";
|
|
74
|
+
const abiFuncs = abi.funcs.kindRegistry.filter((i) => i.name == funcName);
|
|
75
|
+
const abiNonFuncs = abi.nonFuncs.kindRegistry;
|
|
76
|
+
const code = new Option("--code <code>", "Matter hash of the kind code");
|
|
77
|
+
const data = new Option("--data <data>", "Matter hash of the kind data");
|
|
78
|
+
const rels = new Option("--relations [rel...]", "IDs of relations supported");
|
|
79
|
+
const id = new Argument(`<id>`, "Kind id");
|
|
80
|
+
const options = [code, data, rels, ...writeOptions, ...outputOptions];
|
|
81
|
+
const args = [id];
|
|
82
|
+
function getFuncArgs(cmd) {
|
|
83
|
+
const opts = cmd.opts();
|
|
84
|
+
const args = [cmd.args[0]];
|
|
85
|
+
const ZERO32 = toHex(0, { size: 32 });
|
|
86
|
+
let sig;
|
|
87
|
+
if (opts.relations) {
|
|
88
|
+
if (opts.code || opts.data) {
|
|
89
|
+
sig = "kindUpdate(uint64,bytes32,bytes32,uint64[])";
|
|
90
|
+
args.push(opts.code ?? ZERO32);
|
|
91
|
+
args.push(opts.data ?? ZERO32);
|
|
92
|
+
args.push(typeof opts.relations == "boolean" ? [] : opts.relations);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
sig = "kindUpdate(uint64,uint64[])";
|
|
96
|
+
args.push(typeof opts.relations == "boolean" ? [] : opts.relations);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (opts.code || opts.data) {
|
|
101
|
+
sig = "kindUpdate(uint64,bytes32,bytes32)";
|
|
102
|
+
args.push(opts.code ?? ZERO32);
|
|
103
|
+
args.push(opts.data ?? ZERO32);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
throw new Error("");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const abiFunc = abiFuncs.filter((f) => f._metadata.signature == sig)[0];
|
|
110
|
+
return [args, abiFunc];
|
|
111
|
+
}
|
|
112
|
+
async function getCmdAction(cmd) {
|
|
113
|
+
const opts = cmd.opts();
|
|
114
|
+
const [args, abiFunc] = getFuncArgs(cmd);
|
|
115
|
+
const { publicClient, walletClient, conf } = await FromOpts.toWriteEthereum(opts);
|
|
116
|
+
const address = conf.contracts.KindRegistry;
|
|
117
|
+
const account = walletClient.account;
|
|
118
|
+
const simulation = {
|
|
119
|
+
address,
|
|
120
|
+
abi: [abiFunc, ...abiNonFuncs],
|
|
121
|
+
functionName: "kindUpdate",
|
|
122
|
+
args,
|
|
123
|
+
account,
|
|
124
|
+
};
|
|
125
|
+
await submitSimulation(simulation, publicClient, walletClient, new Logger(opts));
|
|
126
|
+
}
|
|
127
|
+
return new Command("update")
|
|
128
|
+
.description("Update an existing kind")
|
|
129
|
+
.addOptions(options)
|
|
130
|
+
.addArguments(args)
|
|
131
|
+
.action(getCmdAction);
|
|
132
|
+
}
|
package/dist/cmds/matter.js
CHANGED
|
@@ -229,13 +229,24 @@ async function compileEnumMatter(file) {
|
|
|
229
229
|
throw new Error(`Invalid column element type: ${h}`);
|
|
230
230
|
}
|
|
231
231
|
};
|
|
232
|
+
function resolvePath(v) {
|
|
233
|
+
if (path.isAbsolute(v))
|
|
234
|
+
return v;
|
|
235
|
+
const dir = path.dirname(file);
|
|
236
|
+
if (path.isAbsolute(file)) {
|
|
237
|
+
return path.resolve(dir, v);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
return path.normalize(path.join(dir, v));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
232
243
|
const mapValues = ({ header, value }) => {
|
|
233
244
|
const v = value.trim();
|
|
234
245
|
if (v.startsWith("0x")) {
|
|
235
246
|
return fromHex(pad(v, { size: 32, dir: "left" }), "bytes");
|
|
236
247
|
}
|
|
237
248
|
else if (header == "IMAGE" || header == "JSON") {
|
|
238
|
-
const { hash, mime, form, path } = computeHashMemo(v);
|
|
249
|
+
const { hash, mime, form, path } = computeHashMemo(resolvePath(v));
|
|
239
250
|
if (!deps.has(path)) {
|
|
240
251
|
deps.set(path, { hash, mime, form, path });
|
|
241
252
|
}
|
|
@@ -261,7 +272,7 @@ async function compileEnumMatter(file) {
|
|
|
261
272
|
.on("end", () => {
|
|
262
273
|
const auxTypes = Object.keys(aux ?? {});
|
|
263
274
|
const auxValues = Object.values(aux ?? {});
|
|
264
|
-
const enumHeader = buildEnumHeader(auxTypes, colTypes, rows);
|
|
275
|
+
const enumHeader = buildEnumHeader(auxTypes ?? [], colTypes ?? [], rows);
|
|
265
276
|
resolve(Buffer.concat([enumHeader, ...auxValues, ...content]));
|
|
266
277
|
})
|
|
267
278
|
.on("error", (e /* eslint-disable-line */) => {
|
|
@@ -318,22 +329,24 @@ function computeHashFile(file) {
|
|
|
318
329
|
* [16..31] = col_types [u8; 16]
|
|
319
330
|
*/
|
|
320
331
|
function buildEnumHeader(auxTypes, colTypes, rows = 0) {
|
|
321
|
-
|
|
332
|
+
const colTypesLen = colTypes.length;
|
|
333
|
+
const auxTypesLen = auxTypes.length;
|
|
334
|
+
if (colTypesLen + auxTypesLen == 0) {
|
|
322
335
|
throw new Error("Empty header list");
|
|
323
336
|
}
|
|
324
|
-
if (
|
|
325
|
-
throw new Error(`Too many aux types (max 8): ${
|
|
337
|
+
if (auxTypesLen > 8) {
|
|
338
|
+
throw new Error(`Too many aux types (max 8): ${auxTypesLen}`);
|
|
326
339
|
}
|
|
327
|
-
if (
|
|
328
|
-
throw new Error(`Too many column types (max 16): ${
|
|
340
|
+
if (colTypesLen > 16) {
|
|
341
|
+
throw new Error(`Too many column types (max 16): ${colTypesLen}`);
|
|
329
342
|
}
|
|
330
343
|
if (rows < 0 || rows > 0xffff) {
|
|
331
344
|
throw new Error(`Rows out of range: ${rows}`);
|
|
332
345
|
}
|
|
333
346
|
const buf = new Uint8Array(32);
|
|
334
347
|
buf.set([0x45, 0x4e, 0x55, 0x4d], 0); // magic "ENUM"
|
|
335
|
-
buf[4] = (0x01 << 4) | (
|
|
336
|
-
buf[5] =
|
|
348
|
+
buf[4] = (0x01 << 4) | (auxTypesLen & 0x0f); // version(hi4), aux(lo4)
|
|
349
|
+
buf[5] = colTypesLen; // cols
|
|
337
350
|
buf[6] = rows & 0xff; // rows.lo
|
|
338
351
|
buf[7] = (rows >>> 8) & 0xff; // rows.hi
|
|
339
352
|
auxTypes.forEach((t, i) => (buf[8 + i] = formByName(t))); // aux types
|
package/dist/cmds/pick.js
CHANGED
|
@@ -3,14 +3,22 @@ import path from "node:path";
|
|
|
3
3
|
import jp from "jsonpath";
|
|
4
4
|
import * as _ from "lodash-es";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
+
import { loadMergedConfig } from "../config.js";
|
|
6
7
|
const DEFAULT_DIR = "./register";
|
|
8
|
+
const DEFAULT_UNIVERSE = "anvil";
|
|
7
9
|
const STATIC_RULES = {
|
|
10
|
+
// config
|
|
11
|
+
"universe.id": { file: "config://universe.json", root: "$", field: "id" },
|
|
12
|
+
"universe.sreg": { file: "config://universe.json", root: "$", field: "contracts.SetRegistry" },
|
|
13
|
+
"universe.oreg": { file: "config://universe.json", root: "$", field: "contracts.OmniRegistry" },
|
|
14
|
+
"universe.kreg": { file: "config://universe.json", root: "$", field: "contracts.KindRegistry" },
|
|
15
|
+
"universe.ereg": { file: "config://universe.json", root: "$", field: "contracts.ElementRegistry" },
|
|
16
|
+
"universe.minter": { file: "config://universe.json", root: "$", field: "contracts.ObjectMinter" },
|
|
17
|
+
"universe.rpc": { file: "config://universe.json", root: "$", field: "rpc" },
|
|
18
|
+
"universe.observer": { file: "config://universe.json", root: "$", field: "observer" },
|
|
8
19
|
// contract
|
|
9
|
-
"deploy.addr": {
|
|
10
|
-
|
|
11
|
-
root: "$",
|
|
12
|
-
field: "deployedTo",
|
|
13
|
-
},
|
|
20
|
+
"deploy.addr": { file: "deploy.json", root: "$", field: "deployedTo" },
|
|
21
|
+
"contract.addr": { file: "contract.json", root: "$", field: "deployedTo" },
|
|
14
22
|
// kind
|
|
15
23
|
"kind.id": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data", field: "id" },
|
|
16
24
|
"kind.rev": { file: "kind.json", root: "$.events[?(@.name=='KindRegistered')].data.desc", field: "rev" },
|
|
@@ -99,13 +107,24 @@ export const pickCmd = new Command("pick")
|
|
|
99
107
|
.description("Pick values from outputs")
|
|
100
108
|
.argument("<keys...>", "Keys to resolve")
|
|
101
109
|
.option("--from <dir>", "Output directory", DEFAULT_DIR)
|
|
110
|
+
.option("-u, --universe <name>", "Universe name", DEFAULT_UNIVERSE)
|
|
102
111
|
.action(function (keys) {
|
|
103
|
-
const { from } = this.opts();
|
|
104
|
-
const dir = from || DEFAULT_DIR;
|
|
112
|
+
const { from, universe } = this.opts();
|
|
105
113
|
const loadJsonCache = _.memoize((rel) => {
|
|
106
|
-
|
|
114
|
+
if (rel == "config://universe.json") {
|
|
115
|
+
return loadUniverseConfig(universe);
|
|
116
|
+
}
|
|
117
|
+
const full = path.join(from, rel);
|
|
107
118
|
return loadJson(full);
|
|
108
119
|
});
|
|
109
120
|
const outputs = keys.map((key) => pickOnce(loadJsonCache, key));
|
|
110
121
|
console.log(outputs.join(" "));
|
|
111
122
|
});
|
|
123
|
+
function loadUniverseConfig(universe) {
|
|
124
|
+
const config = loadMergedConfig();
|
|
125
|
+
const uniConf = config.universes?.[universe];
|
|
126
|
+
if (!uniConf) {
|
|
127
|
+
throw new Error(`config for universe ${universe} not found`);
|
|
128
|
+
}
|
|
129
|
+
return uniConf;
|
|
130
|
+
}
|
package/dist/commander-patch.js
CHANGED
|
@@ -7,11 +7,10 @@ export const privateKey = new Option("-k, --private-key <key>", "Private key to
|
|
|
7
7
|
export const foundry = new Option("-f, --foundry", "Use foundry keystores (~/.foundry/keystores)");
|
|
8
8
|
export const universe = new Option("-u, --universe <universe>", "Universe name").default("anvil");
|
|
9
9
|
export const network = new Option("-n, --network <network>", "Network name").default("dev");
|
|
10
|
-
export const json = new Option("-j, --json [file]", "Output result as JSON to stdout or file
|
|
10
|
+
export const json = new Option("-j, --json [file]", "Output result as JSON to stdout or file");
|
|
11
11
|
export const quiet = new Option("-q, --quiet", "Suppress info messages");
|
|
12
|
-
export const noQuiet = new Option("--no-quiet", "Force info messages even when --json is set");
|
|
13
12
|
export const keystoreOptions = [account, password, passwordFile, foundry];
|
|
14
|
-
export const outputOptions = [json, quiet
|
|
13
|
+
export const outputOptions = [json, quiet];
|
|
15
14
|
export const writeOptions = [universe, account, password, passwordFile, foundry];
|
|
16
15
|
Command.prototype.addCommands = function (commands) {
|
|
17
16
|
commands.forEach((cmd) => this.addCommand(cmd));
|
|
@@ -29,7 +28,7 @@ Command.prototype.addKeystoreOptions = function () {
|
|
|
29
28
|
return this.addOptions([account, password, passwordFile, foundry]);
|
|
30
29
|
};
|
|
31
30
|
Command.prototype.addOutputOptions = function () {
|
|
32
|
-
return this.addOptions([json, quiet
|
|
31
|
+
return this.addOptions([json, quiet]);
|
|
33
32
|
};
|
|
34
33
|
Command.prototype.addWriteOptions = function () {
|
|
35
34
|
return this.addKeystoreOptions().addOption(universe);
|
package/dist/logger.js
CHANGED
package/dist/utils.js
CHANGED
|
@@ -167,3 +167,26 @@ export function loadBinary(file) {
|
|
|
167
167
|
const buf = fs.readFileSync(file);
|
|
168
168
|
return buf;
|
|
169
169
|
}
|
|
170
|
+
export function readKindElements(file) {
|
|
171
|
+
const buf = fs.readFileSync(file);
|
|
172
|
+
const mod = new WebAssembly.Module(buf); // sync
|
|
173
|
+
const [section] = WebAssembly.Module.customSections(mod, "kindelms");
|
|
174
|
+
if (!section)
|
|
175
|
+
throw new Error(`Section "kindelms" not found in ${file}`);
|
|
176
|
+
return Array.from(new Uint8Array(section));
|
|
177
|
+
}
|
|
178
|
+
export function toElementType(v) {
|
|
179
|
+
const n = typeof v === "number" ? v : v.startsWith("0x") ? Number.parseInt(v.slice(2), 16) : Number.parseInt(v, 10);
|
|
180
|
+
if (!Number.isInteger(n) || n < 1 || n > 255) {
|
|
181
|
+
throw new Error(`Invalid element type: ${v} (must be 1–255)`);
|
|
182
|
+
}
|
|
183
|
+
return n;
|
|
184
|
+
}
|
|
185
|
+
export function toRelationId(v) {
|
|
186
|
+
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
187
|
+
const U48_MAX = (1n << 48n) - 1n;
|
|
188
|
+
if (n < 1n || n > U48_MAX) {
|
|
189
|
+
throw new Error(`Invalid relation id: ${String(v)} (must be between 1 and ${U48_MAX})`);
|
|
190
|
+
}
|
|
191
|
+
return n;
|
|
192
|
+
}
|