@clarigen/cli 1.0.0-next.18 → 1.0.0-next.21
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/abi-types.ts.txt +119 -0
- package/dist/commands/index.js +750 -74
- package/dist/index.js +774 -87
- package/package.json +19 -21
- package/src/clarinet-config.ts +11 -8
- package/src/commands/index.ts +28 -23
- package/src/config.ts +7 -3
- package/src/generate/declaration.ts +3 -3
- package/src/generate/files.ts +0 -49
- package/src/generate/single.ts +12 -6
- package/src/utils.ts +0 -1
- package/dist/commands/index.d.ts +0 -16
- package/dist/commands/index.mjs +0 -92
- package/dist/index.d.ts +0 -64
- package/dist/index.mjs +0 -105
package/dist/commands/index.js
CHANGED
|
@@ -1,92 +1,768 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __objRest = (source, exclude) => {
|
|
25
|
+
var target = {};
|
|
26
|
+
for (var prop in source)
|
|
27
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
28
|
+
target[prop] = source[prop];
|
|
29
|
+
if (source != null && __getOwnPropSymbols)
|
|
30
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
31
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
32
|
+
target[prop] = source[prop];
|
|
33
|
+
}
|
|
34
|
+
return target;
|
|
35
|
+
};
|
|
36
|
+
var __export = (target, all) => {
|
|
37
|
+
for (var name in all)
|
|
38
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
39
|
+
};
|
|
40
|
+
var __copyProps = (to, from, except, desc) => {
|
|
41
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
42
|
+
for (let key of __getOwnPropNames(from))
|
|
43
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
44
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
45
|
+
}
|
|
46
|
+
return to;
|
|
47
|
+
};
|
|
48
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
|
49
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
50
|
+
|
|
51
|
+
// src/commands/index.ts
|
|
52
|
+
var commands_exports = {};
|
|
53
|
+
__export(commands_exports, {
|
|
54
|
+
Generate: () => Generate
|
|
55
|
+
});
|
|
56
|
+
module.exports = __toCommonJS(commands_exports);
|
|
57
|
+
var import_command2 = require("@oclif/command");
|
|
58
|
+
|
|
59
|
+
// src/generate/files.ts
|
|
60
|
+
var import_core2 = require("@clarigen/core");
|
|
61
|
+
var import_path3 = require("path");
|
|
62
|
+
|
|
63
|
+
// src/index.ts
|
|
64
|
+
var import_command = require("@oclif/command");
|
|
65
|
+
|
|
66
|
+
// src/config.ts
|
|
67
|
+
var import_path2 = require("path");
|
|
68
|
+
var import_promises2 = require("fs/promises");
|
|
69
|
+
var import_fs = require("fs");
|
|
70
|
+
|
|
71
|
+
// src/clarinet-config.ts
|
|
72
|
+
var import_j_toml = require("@ltd/j-toml");
|
|
73
|
+
var import_path = require("path");
|
|
74
|
+
var import_promises = require("fs/promises");
|
|
75
|
+
var import_wallet_sdk = require("micro-stacks/wallet-sdk");
|
|
76
|
+
var import_toposort = require("toposort");
|
|
77
|
+
var import_crypto = require("micro-stacks/crypto");
|
|
78
|
+
async function getClarinetDevConfig(folder) {
|
|
79
|
+
const baseConfigPath = (0, import_path.resolve)(folder, "settings", "Devnet.toml");
|
|
80
|
+
const configContents = await (0, import_promises.readFile)(baseConfigPath, { encoding: "utf-8" });
|
|
81
|
+
const config = (0, import_j_toml.parse)(configContents, 1, "\n", true, {
|
|
82
|
+
longer: true
|
|
83
|
+
});
|
|
84
|
+
return config;
|
|
85
|
+
}
|
|
86
|
+
async function getClarinetConfig(folder) {
|
|
87
|
+
const baseConfigPath = (0, import_path.resolve)(folder, "Clarinet.toml");
|
|
88
|
+
const configContents = await (0, import_promises.readFile)(baseConfigPath, { encoding: "utf-8" });
|
|
89
|
+
const config = (0, import_j_toml.parse)(configContents, 1, "\n", true);
|
|
90
|
+
return config;
|
|
91
|
+
}
|
|
92
|
+
function getContractsFromClarinet(clarinetConfig, accounts) {
|
|
93
|
+
const deployerAddress = accounts.deployer.address;
|
|
94
|
+
const sortedContracts = sortClarinetContracts(clarinetConfig.contracts);
|
|
95
|
+
const contracts = sortedContracts.map((contractName) => {
|
|
96
|
+
const info = clarinetConfig.contracts[contractName];
|
|
97
|
+
const file = info.path.replace(/^contracts\//, "");
|
|
98
|
+
return {
|
|
99
|
+
file,
|
|
100
|
+
address: deployerAddress,
|
|
101
|
+
name: contractName
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return contracts;
|
|
105
|
+
}
|
|
106
|
+
function sortClarinetContracts(contractsConfig) {
|
|
107
|
+
const edges = [];
|
|
108
|
+
const nodes = [];
|
|
109
|
+
Object.entries(contractsConfig).forEach(([contractName, info]) => {
|
|
110
|
+
nodes.push(contractName);
|
|
111
|
+
info.depends_on.forEach((dependency) => edges.push([contractName, dependency]));
|
|
112
|
+
});
|
|
113
|
+
const sorted = (0, import_toposort.array)(nodes, edges).reverse();
|
|
114
|
+
return sorted;
|
|
115
|
+
}
|
|
116
|
+
async function getClarinetAccounts(folder) {
|
|
117
|
+
const devConfig = await getClarinetDevConfig(folder);
|
|
118
|
+
const accountEntries = await Promise.all(Object.entries(devConfig.accounts).map(async ([key, info]) => {
|
|
119
|
+
const wallet = await (0, import_wallet_sdk.generateWallet)(info.mnemonic, "password");
|
|
120
|
+
const [account] = wallet.accounts;
|
|
121
|
+
const address = (0, import_wallet_sdk.getStxAddressFromAccount)(account, import_crypto.StacksNetworkVersion.testnetP2PKH);
|
|
122
|
+
return [
|
|
123
|
+
key,
|
|
124
|
+
__spreadProps(__spreadValues({}, info), {
|
|
125
|
+
address
|
|
126
|
+
})
|
|
127
|
+
];
|
|
128
|
+
}));
|
|
129
|
+
const accounts = Object.fromEntries(accountEntries);
|
|
130
|
+
return accounts;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/config.ts
|
|
134
|
+
var defaultConfigFile = {
|
|
135
|
+
outputDir: "src/clarigen",
|
|
136
|
+
clarinet: "."
|
|
137
|
+
};
|
|
138
|
+
function configFilePath(rootPath) {
|
|
139
|
+
return (0, import_path2.resolve)(rootPath, "clarigen.config.json");
|
|
140
|
+
}
|
|
141
|
+
async function configFileExists(configPath) {
|
|
142
|
+
try {
|
|
143
|
+
await (0, import_promises2.access)(configPath, import_fs.constants.R_OK);
|
|
144
|
+
return true;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function getConfigFile(rootPath) {
|
|
150
|
+
const fullPath = configFilePath(rootPath);
|
|
151
|
+
const exists = await configFileExists(fullPath);
|
|
152
|
+
if (exists) {
|
|
153
|
+
const configContents = await (0, import_promises2.readFile)(fullPath, { encoding: "utf-8" });
|
|
154
|
+
const configFile = JSON.parse(configContents);
|
|
155
|
+
return __spreadValues(__spreadValues({}, defaultConfigFile), configFile);
|
|
156
|
+
}
|
|
157
|
+
return defaultConfigFile;
|
|
158
|
+
}
|
|
159
|
+
async function getProjectConfig(rootPath) {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
const configFile = await getConfigFile(rootPath);
|
|
162
|
+
const clarinetPath = (0, import_path2.resolve)(rootPath, configFile.clarinet);
|
|
163
|
+
const clarinet = await getClarinetConfig(clarinetPath);
|
|
164
|
+
const accounts = await getClarinetAccounts(clarinetPath);
|
|
165
|
+
const contracts = getContractsFromClarinet(clarinet, accounts);
|
|
166
|
+
const contractsDir = (0, import_path2.relative)(process.cwd(), (0, import_path2.join)(configFile.clarinet, "contracts"));
|
|
167
|
+
return __spreadProps(__spreadValues({}, configFile), {
|
|
168
|
+
outputDir: ((_a = clarinet.clarigen) == null ? void 0 : _a.output_dir) || configFile.outputDir,
|
|
169
|
+
docs: ((_b = clarinet.clarigen) == null ? void 0 : _b.docs) || configFile.docs,
|
|
170
|
+
contracts,
|
|
171
|
+
contractsDir,
|
|
172
|
+
accounts,
|
|
173
|
+
clarinet: configFile.clarinet
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/generate/declaration.ts
|
|
178
|
+
var import_transactions = require("micro-stacks/transactions");
|
|
179
|
+
var import_core = require("@clarigen/core");
|
|
180
|
+
var import_reserved_words = require("reserved-words");
|
|
181
|
+
var jsTypeFromAbiType = (val, isArgument = false) => {
|
|
182
|
+
if ((0, import_transactions.isClarityAbiPrimitive)(val)) {
|
|
183
|
+
if (val === "uint128") {
|
|
184
|
+
if (isArgument)
|
|
185
|
+
return "number | bigint";
|
|
186
|
+
return "bigint";
|
|
187
|
+
} else if (val === "int128") {
|
|
188
|
+
if (isArgument)
|
|
189
|
+
return "number | bigint";
|
|
190
|
+
return "bigint";
|
|
191
|
+
} else if (val === "bool") {
|
|
192
|
+
return "boolean";
|
|
193
|
+
} else if (val === "principal") {
|
|
194
|
+
return "string";
|
|
195
|
+
} else if (val === "none") {
|
|
196
|
+
return "null";
|
|
197
|
+
} else if (val === "trait_reference") {
|
|
198
|
+
return "string";
|
|
199
|
+
} else {
|
|
200
|
+
throw new Error(`Unexpected Clarity ABI type primitive: ${JSON.stringify(val)}`);
|
|
201
|
+
}
|
|
202
|
+
} else if ((0, import_transactions.isClarityAbiBuffer)(val)) {
|
|
203
|
+
return "Uint8Array";
|
|
204
|
+
} else if ((0, import_transactions.isClarityAbiResponse)(val)) {
|
|
205
|
+
const ok = jsTypeFromAbiType(val.response.ok);
|
|
206
|
+
const err = jsTypeFromAbiType(val.response.error);
|
|
207
|
+
return `Response<${ok}, ${err}>`;
|
|
208
|
+
} else if ((0, import_transactions.isClarityAbiOptional)(val)) {
|
|
209
|
+
const innerType = jsTypeFromAbiType(val.optional);
|
|
210
|
+
return `${innerType} | null`;
|
|
211
|
+
} else if ((0, import_transactions.isClarityAbiTuple)(val)) {
|
|
212
|
+
const tupleDefs = [];
|
|
213
|
+
val.tuple.forEach(({ name, type }) => {
|
|
214
|
+
const innerType = jsTypeFromAbiType(type);
|
|
215
|
+
tupleDefs.push(`"${name}": ${innerType}`);
|
|
216
|
+
});
|
|
217
|
+
return `{
|
|
218
|
+
${tupleDefs.join(";\n ")}
|
|
219
|
+
}`;
|
|
220
|
+
} else if ((0, import_transactions.isClarityAbiList)(val)) {
|
|
221
|
+
const innerType = jsTypeFromAbiType(val.list.type);
|
|
222
|
+
return `${innerType}[]`;
|
|
223
|
+
} else if ((0, import_transactions.isClarityAbiStringAscii)(val)) {
|
|
224
|
+
return "string";
|
|
225
|
+
} else if ((0, import_transactions.isClarityAbiStringUtf8)(val)) {
|
|
226
|
+
return "string";
|
|
227
|
+
} else if (val === "trait_reference") {
|
|
228
|
+
return "string";
|
|
229
|
+
} else {
|
|
230
|
+
throw new Error(`Unexpected Clarity ABI type: ${JSON.stringify(val)}`);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
function getArgName(name) {
|
|
234
|
+
const camel = (0, import_core.toCamelCase)(name);
|
|
235
|
+
const prefix = (0, import_reserved_words.check)(camel, 6) ? "_" : "";
|
|
236
|
+
return `${prefix}${camel}`;
|
|
237
|
+
}
|
|
238
|
+
var accessToReturnType = {
|
|
239
|
+
public: "Public",
|
|
240
|
+
read_only: "ReadOnly",
|
|
241
|
+
private: "Private"
|
|
242
|
+
};
|
|
243
|
+
function makePureTypes(abi) {
|
|
244
|
+
let typings = "";
|
|
245
|
+
abi.functions.forEach((func, index) => {
|
|
246
|
+
let functionLine = `${(0, import_core.toCamelCase)(func.name)}: `;
|
|
247
|
+
const args = func.args.map((arg) => {
|
|
248
|
+
return `${getArgName(arg.name)}: ${jsTypeFromAbiType(arg.type, true)}`;
|
|
249
|
+
});
|
|
250
|
+
functionLine += `(${args.join(", ")}) => `;
|
|
251
|
+
const funcType = accessToReturnType[func.access];
|
|
252
|
+
functionLine += `ContractCalls.${funcType}<`;
|
|
253
|
+
if (func.access === "public") {
|
|
254
|
+
const { type } = func.outputs;
|
|
255
|
+
if (!(0, import_transactions.isClarityAbiResponse)(type))
|
|
256
|
+
throw new Error("Expected response type for public function");
|
|
257
|
+
const ok = jsTypeFromAbiType(type.response.ok);
|
|
258
|
+
const err = jsTypeFromAbiType(type.response.error);
|
|
259
|
+
functionLine += `${ok}, ${err}>;`;
|
|
260
|
+
} else {
|
|
261
|
+
const returnType = jsTypeFromAbiType(func.outputs.type);
|
|
262
|
+
functionLine += `${returnType}>;`;
|
|
263
|
+
}
|
|
264
|
+
typings += `${index === 0 ? "" : "\n"} ${functionLine}`;
|
|
265
|
+
});
|
|
266
|
+
abi.maps.forEach((map) => {
|
|
267
|
+
let functionLine = `${(0, import_core.toCamelCase)(map.name)}: `;
|
|
268
|
+
const keyType = jsTypeFromAbiType(map.key, true);
|
|
269
|
+
const arg = `key: ${keyType}`;
|
|
270
|
+
const valType = jsTypeFromAbiType(map.value);
|
|
271
|
+
functionLine += `(${arg}) => ContractCalls.Map<${keyType}, ${valType}>;`;
|
|
272
|
+
typings += `
|
|
273
|
+
${functionLine}`;
|
|
274
|
+
});
|
|
275
|
+
return typings;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/generate/files.ts
|
|
279
|
+
var generateInterfaceFile = ({
|
|
280
|
+
contractName,
|
|
281
|
+
abi
|
|
282
|
+
}) => {
|
|
283
|
+
const variableName = (0, import_core2.toCamelCase)(contractName, true);
|
|
284
|
+
const _a = abi, { clarity_version } = _a, rest = __objRest(_a, ["clarity_version"]);
|
|
285
|
+
const abiString = JSON.stringify(rest, null, 2);
|
|
286
|
+
const fileContents = `import { ClarityAbi } from '@clarigen/core';
|
|
9
287
|
|
|
10
288
|
// prettier-ignore
|
|
11
|
-
export const ${
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
289
|
+
export const ${variableName}Interface: ClarityAbi = ${abiString};
|
|
290
|
+
`;
|
|
291
|
+
return fileContents;
|
|
292
|
+
};
|
|
293
|
+
var generateIndexFile = ({
|
|
294
|
+
contractFile,
|
|
295
|
+
contractAddress,
|
|
296
|
+
contractName
|
|
297
|
+
}) => {
|
|
298
|
+
const contractTitle = (0, import_core2.toCamelCase)(contractName, true);
|
|
299
|
+
const varName = (0, import_core2.toCamelCase)(contractName);
|
|
300
|
+
const contractType = `${contractTitle}Contract`;
|
|
301
|
+
const interfaceVar = `${contractTitle}Interface`;
|
|
302
|
+
const fileContents = `import { pureProxy, Contract } from '@clarigen/core';
|
|
303
|
+
import type { ${contractType} } from './types';
|
|
304
|
+
import { ${interfaceVar} } from './abi';
|
|
305
|
+
export type { ${contractType} } from './types';
|
|
306
|
+
|
|
307
|
+
export function ${varName}Contract(contractAddress: string, contractName: string) {
|
|
308
|
+
return pureProxy<${contractType}>({
|
|
309
|
+
abi: ${interfaceVar},
|
|
20
310
|
contractAddress,
|
|
21
311
|
contractName,
|
|
22
312
|
});
|
|
23
313
|
}
|
|
24
314
|
|
|
25
|
-
export const ${
|
|
26
|
-
contract: ${
|
|
27
|
-
address: '${
|
|
28
|
-
contractFile: '${
|
|
29
|
-
name: '${
|
|
30
|
-
abi: ${
|
|
315
|
+
export const ${varName}Info: Contract<${contractType}> = {
|
|
316
|
+
contract: ${varName}Contract,
|
|
317
|
+
address: '${contractAddress}',
|
|
318
|
+
contractFile: '${contractFile}',
|
|
319
|
+
name: '${contractName}',
|
|
320
|
+
abi: ${interfaceVar},
|
|
321
|
+
};
|
|
322
|
+
`;
|
|
323
|
+
return fileContents;
|
|
31
324
|
};
|
|
32
|
-
|
|
325
|
+
var generateTypesFile = (abi, contractName) => {
|
|
326
|
+
const name = (0, import_core2.toCamelCase)(contractName, true);
|
|
327
|
+
const typings = makePureTypes(abi);
|
|
328
|
+
const fileContents = `import { Response, ContractCalls } from '@clarigen/core';
|
|
33
329
|
|
|
34
330
|
// prettier-ignore
|
|
35
|
-
export interface ${
|
|
36
|
-
${
|
|
331
|
+
export interface ${name}Contract {
|
|
332
|
+
${typings}
|
|
37
333
|
}
|
|
38
|
-
|
|
334
|
+
`;
|
|
335
|
+
return fileContents;
|
|
336
|
+
};
|
|
337
|
+
var generateProjectIndexFile = (config) => {
|
|
338
|
+
const imports = [
|
|
339
|
+
"import type { ContractInstances } from '@clarigen/core';"
|
|
340
|
+
];
|
|
341
|
+
const exports = [];
|
|
342
|
+
const contractMap = [];
|
|
343
|
+
let accounts = "";
|
|
344
|
+
if ("accounts" in config) {
|
|
345
|
+
const accountLines = Object.keys(config.accounts).map((key) => {
|
|
346
|
+
const account = config.accounts[key];
|
|
347
|
+
return `"${key}": {
|
|
348
|
+
mnemonic: "${account.mnemonic}",
|
|
349
|
+
balance: ${account.balance.toString()}n,
|
|
350
|
+
address: "${account.address}",
|
|
351
|
+
},`;
|
|
352
|
+
});
|
|
353
|
+
accounts = `
|
|
39
354
|
|
|
40
355
|
// prettier-ignore
|
|
41
356
|
export const accounts = {
|
|
42
|
-
${
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
357
|
+
${accountLines.join("\n ")}
|
|
358
|
+
};`;
|
|
359
|
+
}
|
|
360
|
+
config.contracts.forEach((contract) => {
|
|
361
|
+
const contractName = contract.name;
|
|
362
|
+
const contractVar = (0, import_core2.toCamelCase)(contractName);
|
|
363
|
+
const contractInfo = `${contractVar}Info`;
|
|
364
|
+
const contractInterface = `${(0, import_core2.toCamelCase)(contractName, true)}Contract`;
|
|
365
|
+
const dirName = (0, import_path3.dirname)(contract.file);
|
|
366
|
+
const importPath = `'./${(0, import_path3.join)(dirName || ".", contractName)}'`;
|
|
367
|
+
const _import = `import { ${contractInfo} } from ${importPath};`;
|
|
368
|
+
imports.push(_import);
|
|
369
|
+
const _export = `export type { ${contractInterface} } from ${importPath};`;
|
|
370
|
+
exports.push(_export);
|
|
371
|
+
const map = `${contractVar}: ${contractInfo},`;
|
|
372
|
+
contractMap.push(map);
|
|
373
|
+
});
|
|
374
|
+
const contractsType = `
|
|
49
375
|
export type Contracts = ContractInstances<typeof contracts>;
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
${
|
|
53
|
-
|
|
54
|
-
|
|
376
|
+
`;
|
|
377
|
+
const file = `${imports.join("\n")}
|
|
378
|
+
${exports.join("\n")}
|
|
379
|
+
${contractsType}
|
|
380
|
+
export const contracts = {
|
|
381
|
+
${contractMap.join("\n ")}
|
|
382
|
+
};${accounts}
|
|
383
|
+
`;
|
|
384
|
+
return file;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/utils.ts
|
|
388
|
+
var import_native_bin2 = require("@clarigen/native-bin");
|
|
389
|
+
var import_path7 = require("path");
|
|
390
|
+
var import_promises6 = require("fs/promises");
|
|
391
|
+
|
|
392
|
+
// src/docs.ts
|
|
393
|
+
var import_claridocs = require("@clarigen/claridocs");
|
|
394
|
+
var import_promises3 = require("fs/promises");
|
|
395
|
+
var import_path4 = require("path");
|
|
396
|
+
async function generateMarkdownDoc({
|
|
397
|
+
contractFile,
|
|
398
|
+
contractName,
|
|
399
|
+
docsPath,
|
|
400
|
+
abi,
|
|
401
|
+
dirName
|
|
402
|
+
}) {
|
|
403
|
+
const contractSrc = await (0, import_promises3.readFile)(contractFile, { encoding: "utf-8" });
|
|
404
|
+
const docs = (0, import_claridocs.createContractDocInfo)({ contractSrc, abi });
|
|
405
|
+
const folder = (0, import_path4.resolve)(process.cwd(), docsPath, dirName || ".");
|
|
406
|
+
const filePath = (0, import_path4.resolve)(folder, `${contractName}.md`);
|
|
407
|
+
const md = (0, import_claridocs.generateMarkdown)({
|
|
408
|
+
contract: docs,
|
|
409
|
+
contractFile: (0, import_path4.relative)(folder, contractFile),
|
|
410
|
+
contractName,
|
|
411
|
+
abi
|
|
412
|
+
});
|
|
413
|
+
await (0, import_promises3.mkdir)(folder, { recursive: true });
|
|
414
|
+
await (0, import_promises3.writeFile)(filePath, md);
|
|
415
|
+
}
|
|
416
|
+
async function generateDocsIndex(configFile) {
|
|
417
|
+
if (!configFile.docs)
|
|
418
|
+
return;
|
|
419
|
+
const contractLines = configFile.contracts.map((contract) => {
|
|
420
|
+
const fileName = contract.file.replace(".clar", ".md");
|
|
421
|
+
return `- [\`${contract.name}\`](${fileName})`;
|
|
422
|
+
});
|
|
423
|
+
const fileContents = `# Contracts
|
|
424
|
+
|
|
425
|
+
${contractLines.join("\n")}
|
|
426
|
+
`;
|
|
427
|
+
const filepath = (0, import_path4.resolve)(process.cwd(), configFile.docs, "README.md");
|
|
428
|
+
await (0, import_promises3.writeFile)(filepath, fileContents);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/generate/single.ts
|
|
432
|
+
var import_core3 = require("@clarigen/core");
|
|
433
|
+
var import_path5 = require("path");
|
|
434
|
+
var import_promises4 = require("fs/promises");
|
|
435
|
+
var import_util = require("util");
|
|
436
|
+
function generateContractMeta(contract) {
|
|
437
|
+
const { abi } = contract;
|
|
438
|
+
const functionLines = [];
|
|
439
|
+
const _a = abi, { functions, variables, maps } = _a, rest = __objRest(_a, ["functions", "variables", "maps"]);
|
|
440
|
+
functions.forEach((func) => {
|
|
441
|
+
let functionLine = `${(0, import_core3.toCamelCase)(func.name)}: `;
|
|
442
|
+
const args = func.args.map((arg) => {
|
|
443
|
+
return `${getArgName(arg.name)}: ${jsTypeFromAbiType(arg.type, true)}`;
|
|
444
|
+
});
|
|
445
|
+
const argsTuple = `[${args.join(", ")}]`;
|
|
446
|
+
const funcDef = JSON.stringify(func);
|
|
447
|
+
functionLine += funcDef;
|
|
448
|
+
const retType = jsTypeFromAbiType(func.outputs.type);
|
|
449
|
+
functionLine += ` as TypedAbiFunction<${argsTuple}, ${retType}>`;
|
|
450
|
+
functionLines.push(functionLine);
|
|
451
|
+
});
|
|
452
|
+
const variableLines = contract.variables.map((v) => {
|
|
453
|
+
let varLine = `${(0, import_core3.toCamelCase)(v.name)}: `;
|
|
454
|
+
const type = jsTypeFromAbiType(v.type);
|
|
455
|
+
const varJSON = serialize(v);
|
|
456
|
+
varLine += `${varJSON} as TypedAbiVariable<${type}>`;
|
|
457
|
+
return varLine;
|
|
458
|
+
});
|
|
459
|
+
const constants2 = contract.variables.filter((v) => v.access === "constant");
|
|
460
|
+
const constantLines = constants2.map((constant) => {
|
|
461
|
+
return `"${(0, import_core3.toCamelCase)(constant.name)}": ${serialize(constant.defaultValue)}`;
|
|
462
|
+
});
|
|
463
|
+
const mapLines = maps.map((map) => {
|
|
464
|
+
let mapLine = `${(0, import_core3.toCamelCase)(map.name)}: `;
|
|
465
|
+
const keyType = jsTypeFromAbiType(map.key);
|
|
466
|
+
const valType = jsTypeFromAbiType(map.value);
|
|
467
|
+
mapLine += JSON.stringify(map);
|
|
468
|
+
mapLine += ` as TypedAbiMap<${keyType}, ${valType}>`;
|
|
469
|
+
return mapLine;
|
|
470
|
+
});
|
|
471
|
+
const otherAbi = JSON.stringify(rest);
|
|
472
|
+
const contractFile = (0, import_path5.relative)(process.cwd(), contract.contractFile);
|
|
473
|
+
return `{
|
|
474
|
+
${serializeLines("functions", functionLines)}
|
|
475
|
+
${serializeLines("variables", variableLines)}
|
|
476
|
+
${serializeLines("maps", mapLines)}
|
|
477
|
+
${serializeLines("constants", constantLines)}
|
|
478
|
+
${otherAbi.slice(1, -1)},
|
|
479
|
+
contractName: '${contract.contractName}',
|
|
480
|
+
contractFile: '${contractFile}',
|
|
481
|
+
}`;
|
|
482
|
+
}
|
|
483
|
+
async function generateSingleFile(config, contracts) {
|
|
484
|
+
const contractDefs = contracts.map((contract) => {
|
|
485
|
+
const meta = generateContractMeta(contract);
|
|
486
|
+
const keyName = (0, import_core3.toCamelCase)(contract.contractName);
|
|
487
|
+
return `${keyName}: ${meta}`;
|
|
488
|
+
});
|
|
489
|
+
const types = await getSingleTypes();
|
|
490
|
+
const accounts = generateAccounts(config);
|
|
491
|
+
const file = `
|
|
492
|
+
${types}
|
|
493
|
+
|
|
55
494
|
export const contracts = {
|
|
56
|
-
${
|
|
57
|
-
`)}
|
|
58
|
-
};${o}
|
|
59
|
-
`};var K=require("@clarigen/native-bin"),g=require("path"),Tt=require("fs/promises");var J=require("@clarigen/claridocs"),T=require("fs/promises"),P=require("path");async function Ct({contractFile:t,contractName:e,docsPath:r,abi:n,dirName:o}){let i=await(0,T.readFile)(t,{encoding:"utf-8"}),a=(0,J.createContractDocInfo)({contractSrc:i,abi:n}),s=(0,P.resolve)(process.cwd(),r,o||"."),l=(0,P.resolve)(s,`${e}.md`),f=(0,J.generateMarkdown)({contract:a,contractFile:(0,P.relative)(s,t),contractName:e,abi:n});await(0,T.mkdir)(s,{recursive:!0}),await(0,T.writeFile)(l,f)}async function yt(t){if(!t.docs)return;let r=`# Contracts
|
|
60
|
-
|
|
61
|
-
${t.contracts.map(o=>{let i=o.file.replace(".clar",".md");return`- [\`${o.name}\`](${i})`}).join(`
|
|
62
|
-
`)}
|
|
63
|
-
`,n=(0,P.resolve)(process.cwd(),t.docs,"README.md");await(0,T.writeFile)(n,r)}var N=require("@clarigen/core");var U=require("path"),dt=require("fs/promises"),W=require("util");function zt(t){let{abi:e}=t,r=[],$=e,{functions:n,variables:o,maps:i}=$,a=V($,["functions","variables","maps"]);n.forEach(c=>{let u=`${(0,N.toCamelCase)(c.name)}: `,k=`[${c.args.map(tt=>`${Z(tt.name)}: ${p(tt.type,!0)}`).join(", ")}]`;u+=JSON.stringify(c);let Nt=p(c.outputs.type);u+=` as TypedAbiFunction<${k}, ${Nt}>`,r.push(u)});let s=t.variables.map(c=>{let u=`${(0,N.toCamelCase)(c.name)}: `,v=p(c.type);return u+=`${(0,W.inspect)(c,!1,null,!1)} as TypedAbiVariable<${v}>`,u}),f=t.variables.filter(c=>c.access==="constant").map(c=>`"${(0,N.toCamelCase)(c.name)}": ${qt(c.defaultValue)}`),b=i.map(c=>{let u=`${(0,N.toCamelCase)(c.name)}: `,v=p(c.key),k=p(c.value);return u+=JSON.stringify(c),u+=` as TypedAbiMap<${v}, ${k}>`,u}),A=JSON.stringify(a),C=(0,U.relative)(process.cwd(),t.contractFile);return`{
|
|
64
|
-
${R("functions",r)}
|
|
65
|
-
${R("variables",s)}
|
|
66
|
-
${R("maps",b)}
|
|
67
|
-
${R("constants",f)}
|
|
68
|
-
${A.slice(1,-1)},
|
|
69
|
-
contractName: '${t.contractName}',
|
|
70
|
-
contractFile: '${C}',
|
|
71
|
-
}`}async function $t(t,e){let r=e.map(a=>{let s=zt(a);return`${(0,N.toCamelCase)(a.contractName)}: ${s}`}),n=await Ht(),o=Kt(t);return`
|
|
72
|
-
${n}
|
|
73
|
-
|
|
74
|
-
export const contracts: Record<string, TypedAbi> = {
|
|
75
|
-
${r.join(`,
|
|
76
|
-
`)}
|
|
495
|
+
${contractDefs.join(",\n")}
|
|
77
496
|
} as const;
|
|
78
497
|
|
|
79
|
-
${
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
${
|
|
92
|
-
|
|
498
|
+
${accounts}
|
|
499
|
+
`;
|
|
500
|
+
return file;
|
|
501
|
+
}
|
|
502
|
+
function generateAccounts(config) {
|
|
503
|
+
let accounts = "";
|
|
504
|
+
if ("accounts" in config) {
|
|
505
|
+
const accountLines = Object.keys(config.accounts).map((key) => {
|
|
506
|
+
const account = config.accounts[key];
|
|
507
|
+
return `"${key}": {
|
|
508
|
+
mnemonic: "${account.mnemonic}",
|
|
509
|
+
balance: ${account.balance.toString()}n,
|
|
510
|
+
address: "${account.address}",
|
|
511
|
+
},`;
|
|
512
|
+
});
|
|
513
|
+
accounts = `export const accounts = {
|
|
514
|
+
${accountLines.join("\n ")}
|
|
515
|
+
} as const;`;
|
|
516
|
+
}
|
|
517
|
+
return accounts;
|
|
518
|
+
}
|
|
519
|
+
Uint8Array.prototype[import_util.inspect.custom] = function() {
|
|
520
|
+
return `Uint8Array.from([${this.join(",")}])`;
|
|
521
|
+
};
|
|
522
|
+
function serialize(obj) {
|
|
523
|
+
return (0, import_util.inspect)(obj, {
|
|
524
|
+
showHidden: false,
|
|
525
|
+
maxArrayLength: null,
|
|
526
|
+
maxStringLength: null,
|
|
527
|
+
depth: null,
|
|
528
|
+
colors: false
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function serializeLines(key, lines) {
|
|
532
|
+
return `"${key}": {
|
|
533
|
+
${lines.join(",\n ")}
|
|
534
|
+
},`;
|
|
535
|
+
}
|
|
536
|
+
async function getSingleTypes() {
|
|
537
|
+
const typesPath = (0, import_path5.resolve)(__dirname, "../../dist/abi-types.ts.txt");
|
|
538
|
+
const typesFile = await (0, import_promises4.readFile)(typesPath, { encoding: "utf-8" });
|
|
539
|
+
return typesFile;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/writer.ts
|
|
543
|
+
var import_promises5 = require("fs/promises");
|
|
544
|
+
var import_path6 = require("path");
|
|
545
|
+
var import_prettier = require("prettier");
|
|
546
|
+
var defaultPrettierConfig = {
|
|
547
|
+
printWidth: 80,
|
|
548
|
+
semi: true,
|
|
549
|
+
singleQuote: true,
|
|
550
|
+
trailingComma: "es5"
|
|
551
|
+
};
|
|
552
|
+
async function resolvePrettierConfig() {
|
|
553
|
+
try {
|
|
554
|
+
const local = await (0, import_prettier.resolveConfig)(process.cwd());
|
|
555
|
+
if (local)
|
|
556
|
+
return local;
|
|
557
|
+
} catch (error) {
|
|
558
|
+
}
|
|
559
|
+
return defaultPrettierConfig;
|
|
560
|
+
}
|
|
561
|
+
async function formatFile(contents, path) {
|
|
562
|
+
try {
|
|
563
|
+
const fileName = (0, import_path6.basename)(path);
|
|
564
|
+
const config = await resolvePrettierConfig();
|
|
565
|
+
const formatted = (0, import_prettier.format)(contents, __spreadProps(__spreadValues({}, config), {
|
|
566
|
+
filepath: fileName
|
|
567
|
+
}));
|
|
568
|
+
return formatted;
|
|
569
|
+
} catch (error) {
|
|
570
|
+
}
|
|
571
|
+
return contents;
|
|
572
|
+
}
|
|
573
|
+
async function writeFile2(path, contents) {
|
|
574
|
+
const formatted = await formatFile(contents, path);
|
|
575
|
+
await (0, import_promises5.writeFile)(path, formatted);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/generate/vars.ts
|
|
579
|
+
var import_core4 = require("@clarigen/core");
|
|
580
|
+
var import_native_bin = require("@clarigen/native-bin");
|
|
581
|
+
var import_clarity = require("micro-stacks/clarity");
|
|
582
|
+
async function getVariables({
|
|
583
|
+
abi,
|
|
584
|
+
contractIdentifier,
|
|
585
|
+
provider
|
|
586
|
+
}) {
|
|
587
|
+
const variableTransforms = abi.variables.map((variable) => {
|
|
588
|
+
return evalVariable({
|
|
589
|
+
variable,
|
|
590
|
+
provider,
|
|
591
|
+
contractIdentifier
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
const variables = await Promise.all(variableTransforms);
|
|
595
|
+
return variables;
|
|
596
|
+
}
|
|
597
|
+
async function evalVariable({
|
|
598
|
+
contractIdentifier,
|
|
599
|
+
variable,
|
|
600
|
+
provider
|
|
601
|
+
}) {
|
|
602
|
+
const code = getEvalCode(variable);
|
|
603
|
+
const result = await (0, import_native_bin.evalRaw)({
|
|
604
|
+
contractAddress: contractIdentifier,
|
|
605
|
+
code,
|
|
606
|
+
provider
|
|
607
|
+
});
|
|
608
|
+
const resultCV = (0, import_clarity.hexToCV)(result.output_serialized);
|
|
609
|
+
const value = (0, import_core4.cvToValue)(resultCV, true);
|
|
610
|
+
return __spreadProps(__spreadValues({}, variable), {
|
|
611
|
+
defaultValue: value
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function getEvalCode(variable) {
|
|
615
|
+
const { access: access2 } = variable;
|
|
616
|
+
if (access2 === "variable") {
|
|
617
|
+
return `(var-get ${variable.name})`;
|
|
618
|
+
}
|
|
619
|
+
return variable.name;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// src/utils.ts
|
|
623
|
+
var generateFilesForContract = async ({
|
|
624
|
+
contractFile: _contractFile,
|
|
625
|
+
outputFolder,
|
|
626
|
+
provider,
|
|
627
|
+
contractAddress,
|
|
628
|
+
dirName,
|
|
629
|
+
contractName,
|
|
630
|
+
docsPath
|
|
631
|
+
}) => {
|
|
632
|
+
const contractFile = (0, import_path7.resolve)(process.cwd(), _contractFile);
|
|
633
|
+
const contractIdentifier = `${contractAddress}.${contractName}`;
|
|
634
|
+
const abi = await (0, import_native_bin2.deployContract)({
|
|
635
|
+
contractIdentifier,
|
|
636
|
+
contractFilePath: contractFile,
|
|
637
|
+
provider
|
|
638
|
+
});
|
|
639
|
+
const variables = await getVariables({
|
|
640
|
+
abi,
|
|
641
|
+
contractIdentifier,
|
|
642
|
+
provider
|
|
643
|
+
});
|
|
644
|
+
const typesFile = generateTypesFile(abi, contractName);
|
|
645
|
+
const indexFile = generateIndexFile({
|
|
646
|
+
contractFile: (0, import_path7.relative)(process.cwd(), contractFile),
|
|
647
|
+
contractAddress,
|
|
648
|
+
contractName
|
|
649
|
+
});
|
|
650
|
+
const abiFile = generateInterfaceFile({ contractName, abi });
|
|
651
|
+
if (typeof docsPath !== "undefined") {
|
|
652
|
+
await generateMarkdownDoc({
|
|
653
|
+
contractFile,
|
|
654
|
+
contractName,
|
|
655
|
+
abi,
|
|
656
|
+
docsPath,
|
|
657
|
+
dirName
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const outputPath = (0, import_path7.resolve)(outputFolder, dirName || ".", contractName);
|
|
661
|
+
await (0, import_promises6.mkdir)(outputPath, { recursive: true });
|
|
662
|
+
await writeFile2((0, import_path7.resolve)(outputPath, "abi.ts"), abiFile);
|
|
663
|
+
await writeFile2((0, import_path7.resolve)(outputPath, "index.ts"), indexFile);
|
|
664
|
+
await writeFile2((0, import_path7.resolve)(outputPath, "types.ts"), typesFile);
|
|
665
|
+
return {
|
|
666
|
+
abi,
|
|
667
|
+
contractFile,
|
|
668
|
+
contractName,
|
|
669
|
+
dirName,
|
|
670
|
+
contractAddress,
|
|
671
|
+
variables
|
|
672
|
+
};
|
|
673
|
+
};
|
|
674
|
+
var generateProject = async (projectPath) => {
|
|
675
|
+
const configFile = await getProjectConfig(projectPath);
|
|
676
|
+
const { contractsDir, outputDir, contracts } = configFile;
|
|
677
|
+
const outputFolder = (0, import_path7.resolve)(projectPath, outputDir);
|
|
678
|
+
const provider = await (0, import_native_bin2.createClarityBin)();
|
|
679
|
+
const metas = [];
|
|
680
|
+
for (const contract of contracts) {
|
|
681
|
+
const contractFile = (0, import_path7.resolve)(projectPath, contractsDir, contract.file);
|
|
682
|
+
const dirName = (0, import_path7.dirname)(contract.file);
|
|
683
|
+
const meta = await generateFilesForContract({
|
|
684
|
+
contractFile,
|
|
685
|
+
outputFolder,
|
|
686
|
+
provider,
|
|
687
|
+
contractAddress: contract.address,
|
|
688
|
+
dirName,
|
|
689
|
+
contractName: contract.name,
|
|
690
|
+
docsPath: configFile.docs
|
|
691
|
+
});
|
|
692
|
+
metas.push(meta);
|
|
693
|
+
}
|
|
694
|
+
const indexFile = generateProjectIndexFile(configFile);
|
|
695
|
+
await generateDocsIndex(configFile);
|
|
696
|
+
const indexPath = (0, import_path7.resolve)(outputFolder, "index.ts");
|
|
697
|
+
await writeFile2(indexPath, indexFile);
|
|
698
|
+
const singleFile = await generateSingleFile(configFile, metas);
|
|
699
|
+
const singlePath = (0, import_path7.resolve)(outputFolder, "single.ts");
|
|
700
|
+
await writeFile2(singlePath, singleFile);
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/commands/index.ts
|
|
704
|
+
var import_chokidar = require("chokidar");
|
|
705
|
+
var import_path8 = require("path");
|
|
706
|
+
var import_chalk = require("chalk");
|
|
707
|
+
var import_ora = __toESM(require("ora"));
|
|
708
|
+
var _Generate = class extends import_command2.Command {
|
|
709
|
+
async run() {
|
|
710
|
+
const { flags: flags2 } = this.parse(_Generate);
|
|
711
|
+
const cwd = process.cwd();
|
|
712
|
+
if (flags2.watch) {
|
|
713
|
+
const spinner = (0, import_ora.default)("Generating files").start();
|
|
714
|
+
const { contractsDir } = await getProjectConfig(cwd);
|
|
715
|
+
const watcher = (0, import_chokidar.watch)([contractsDir], {
|
|
716
|
+
cwd
|
|
717
|
+
});
|
|
718
|
+
try {
|
|
719
|
+
await generateProject(cwd);
|
|
720
|
+
spinner.succeed(`Finished generating files. Watching for changes.`);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
spinner.fail(`Error generating files.
|
|
723
|
+
${String(error.message)}`);
|
|
724
|
+
}
|
|
725
|
+
watcher.on("change", (path) => {
|
|
726
|
+
const cb = async () => {
|
|
727
|
+
const file = (0, import_path8.basename)(path);
|
|
728
|
+
spinner.clear();
|
|
729
|
+
spinner.start(`Change detected for ${(0, import_chalk.green)(file)}, generating.`);
|
|
730
|
+
try {
|
|
731
|
+
await generateProject(cwd);
|
|
732
|
+
spinner.succeed(`Finished generating files for ${(0, import_chalk.green)(file)}. Watching for changes.`);
|
|
733
|
+
} catch (error) {
|
|
734
|
+
const msg = error.message;
|
|
735
|
+
spinner.fail(`Error after saving ${(0, import_chalk.red)(file)}.
|
|
736
|
+
${msg}`);
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
void cb();
|
|
740
|
+
});
|
|
741
|
+
process.on("SIGINT", () => {
|
|
742
|
+
async function cb() {
|
|
743
|
+
await watcher.close();
|
|
744
|
+
process.exit();
|
|
745
|
+
}
|
|
746
|
+
void cb();
|
|
747
|
+
});
|
|
748
|
+
} else {
|
|
749
|
+
await generateProject(cwd);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
var Generate = _Generate;
|
|
754
|
+
Generate.description = `Generate project files`;
|
|
755
|
+
Generate.strict = true;
|
|
756
|
+
Generate.hidden = false;
|
|
757
|
+
Generate.flags = {
|
|
758
|
+
help: import_command2.flags.help({ char: "h" }),
|
|
759
|
+
watch: import_command2.flags.boolean({
|
|
760
|
+
char: "w",
|
|
761
|
+
description: "Watch for changes to your contracts"
|
|
762
|
+
})
|
|
763
|
+
};
|
|
764
|
+
Generate.args = [];
|
|
765
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
766
|
+
0 && (module.exports = {
|
|
767
|
+
Generate
|
|
768
|
+
});
|