@0xobelisk/sui-common 1.2.0-pre.98 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +92 -0
- package/README.md +670 -1
- package/dist/index.d.ts +61 -27
- package/dist/index.js +1078 -461
- package/dist/index.js.map +1 -1
- package/package.json +15 -17
- package/src/codegen/debug.ts +0 -4
- package/src/codegen/types/index.ts +46 -10
- package/src/codegen/utils/config.ts +1 -1
- package/src/codegen/utils/format.ts +0 -6
- package/src/codegen/utils/formatAndWrite.ts +10 -31
- package/src/codegen/utils/generateLock.ts +122 -0
- package/src/codegen/utils/index.ts +4 -2
- package/src/codegen/utils/renderMove/{schemaGen.ts → codegen.ts} +40 -28
- package/src/codegen/utils/renderMove/common.ts +0 -65
- package/src/codegen/utils/renderMove/dapp.ts +2 -14
- package/src/codegen/utils/renderMove/generateDappKey.ts +33 -18
- package/src/codegen/utils/renderMove/generateError.ts +32 -15
- package/src/codegen/utils/renderMove/generateGenesis.ts +55 -22
- package/src/codegen/utils/renderMove/generateInitTest.ts +26 -14
- package/src/codegen/utils/renderMove/generateObjects.ts +377 -0
- package/src/codegen/utils/renderMove/generatePermits.ts +151 -0
- package/src/codegen/utils/renderMove/generateResources.ts +894 -242
- package/src/codegen/utils/renderMove/generateScenes.ts +467 -0
- package/src/codegen/utils/renderMove/generateScript.ts +18 -13
- package/src/codegen/utils/renderMove/generateSystem.ts +0 -2
- package/src/codegen/utils/renderMove/generateUserStorageInit.ts +37 -0
- package/src/codegen/utils/validateConfig.ts +237 -0
- package/src/index.ts +0 -1
- package/src/modules.d.ts +0 -10
- package/src/codegen/modules.d.ts +0 -1
- package/src/codegen/utils/posixPath.ts +0 -8
- package/src/codegen/utils/renderMove/generateComponents.ts +0 -802
- package/src/codegen/utils/renderMove/generateDefaultSchema.ts +0 -216
- package/src/codegen/utils/renderMove/generateEvent.ts +0 -99
- package/src/codegen/utils/renderMove/generateSchema.ts +0 -287
- package/src/codegen/utils/renderMove/generateSchemaHub.ts +0 -60
- package/src/parseData/index.ts +0 -1
- package/src/parseData/parser/index.ts +0 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xobelisk/sui-common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Common low level logic shared between packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sui",
|
|
@@ -31,19 +31,6 @@
|
|
|
31
31
|
]
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "pnpm run type-check && pnpm run build:js",
|
|
36
|
-
"build:js": "tsup && chmod +x ./dist/index.js",
|
|
37
|
-
"clean": "pnpm run clean:js",
|
|
38
|
-
"clean:js": "rimraf dist",
|
|
39
|
-
"dev": "tsup --watch",
|
|
40
|
-
"format": "prettier --write .",
|
|
41
|
-
"format:check": "prettier --check .",
|
|
42
|
-
"lint": "eslint . --ext .ts",
|
|
43
|
-
"test": "vitest",
|
|
44
|
-
"type-check": "tsc --noEmit",
|
|
45
|
-
"validate": "pnpm format:check && pnpm type-check"
|
|
46
|
-
},
|
|
47
34
|
"dependencies": {
|
|
48
35
|
"chalk": "^5.0.1",
|
|
49
36
|
"debug": "^4.3.4",
|
|
@@ -56,8 +43,6 @@
|
|
|
56
43
|
"path": "^0.12.7",
|
|
57
44
|
"prettier": "^3.1.1",
|
|
58
45
|
"prettier-plugin-move-js": "^0.0.5",
|
|
59
|
-
"prettier-plugin-rust": "^0.1.9",
|
|
60
|
-
"prettier-plugin-solidity": "^1.1.2",
|
|
61
46
|
"typescript": "^5.8.3",
|
|
62
47
|
"yargs": "^17.7.1",
|
|
63
48
|
"zod": "^3.22.3",
|
|
@@ -81,5 +66,18 @@
|
|
|
81
66
|
},
|
|
82
67
|
"publishConfig": {
|
|
83
68
|
"access": "public"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "pnpm run type-check && pnpm run build:js",
|
|
72
|
+
"build:js": "tsup && chmod +x ./dist/index.js",
|
|
73
|
+
"clean": "pnpm run clean:js",
|
|
74
|
+
"clean:js": "rimraf dist",
|
|
75
|
+
"dev": "tsup --watch",
|
|
76
|
+
"format": "prettier --write .",
|
|
77
|
+
"format:check": "prettier --check .",
|
|
78
|
+
"lint": "eslint . --ext .ts",
|
|
79
|
+
"test": "vitest",
|
|
80
|
+
"type-check": "tsc --noEmit",
|
|
81
|
+
"validate": "pnpm format:check && pnpm type-check"
|
|
84
82
|
}
|
|
85
|
-
}
|
|
83
|
+
}
|
package/src/codegen/debug.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { debug as parentDebug } from '../debug';
|
|
2
2
|
|
|
3
3
|
export const debug = parentDebug.extend('codegen');
|
|
4
|
-
export const error = parentDebug.extend('codegen');
|
|
5
4
|
|
|
6
5
|
// Pipe debug output to stdout instead of stderr
|
|
7
6
|
debug.log = console.debug.bind(console);
|
|
8
|
-
|
|
9
|
-
// Pipe error output to stderr
|
|
10
|
-
error.log = console.error.bind(console);
|
|
@@ -19,33 +19,69 @@ export type MoveType =
|
|
|
19
19
|
| 'vector<u256>'
|
|
20
20
|
| string;
|
|
21
21
|
|
|
22
|
-
// Define the type of Schema
|
|
23
22
|
export type Component = {
|
|
24
23
|
offchain?: boolean;
|
|
24
|
+
global?: boolean;
|
|
25
25
|
fields: Record<string, MoveType>;
|
|
26
26
|
keys?: string[];
|
|
27
|
+
// Storage extension annotations
|
|
28
|
+
reactive?: boolean; // generate _reactive cross-user write variants
|
|
29
|
+
fungible?: boolean; // generate add/sub instead of set
|
|
30
|
+
transferable?: boolean; // generate cross-layer transfer functions
|
|
31
|
+
listable?: boolean; // generate list/buy/cancel_listing/expire_listing
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
export type
|
|
34
|
+
/** Config for a DApp-owned named shared object (e.g. guild, boss). */
|
|
35
|
+
export type ObjectConfig = {
|
|
36
|
+
fields: Record<string, MoveType>;
|
|
37
|
+
/** Resources (from the `resources` section) this object accepts for transfers. */
|
|
38
|
+
accepts?: string[];
|
|
39
|
+
/** Other objects/scenes whose data can be transferred into this object. */
|
|
40
|
+
acceptsFrom?: string[];
|
|
41
|
+
/** If true, only the DApp admin can call create_<key>. */
|
|
42
|
+
adminOnly?: boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Config for a standalone ScenePermit (participant management only). */
|
|
46
|
+
export type PermitConfig = {
|
|
47
|
+
// Reserved for future permit-level options.
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type SceneAuthorization = { kind: 'permit'; permit: string } | { kind: 'system' };
|
|
51
|
+
|
|
52
|
+
/** Config for a SceneStorage object (pure data storage, symmetric to ObjectConfig). */
|
|
53
|
+
export type SceneConfig = {
|
|
54
|
+
fields: Record<string, MoveType>;
|
|
55
|
+
/** Resources this scene accepts for transfers. */
|
|
56
|
+
accepts?: string[];
|
|
57
|
+
/** Other objects/scenes whose data can be transferred into this scene. */
|
|
58
|
+
acceptsFrom?: string[];
|
|
59
|
+
/** Explicit write authorization model for this SceneStorage. */
|
|
60
|
+
authorization: SceneAuthorization;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ErrorDefinition = {
|
|
64
|
+
message: string;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type ErrorEntry = string | ErrorDefinition;
|
|
31
68
|
|
|
32
69
|
export type DubheConfig = {
|
|
33
70
|
name: string;
|
|
34
71
|
description: string;
|
|
35
72
|
enums?: Record<string, string[]>;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
resources?: Record<string, Component | MoveType>;
|
|
74
|
+
objects?: Record<string, ObjectConfig>;
|
|
75
|
+
permits?: Record<string, PermitConfig>;
|
|
76
|
+
scenes?: Record<string, SceneConfig>;
|
|
77
|
+
errors?: Record<string, ErrorEntry>;
|
|
39
78
|
};
|
|
40
79
|
|
|
41
80
|
export type DubheMetadata = {
|
|
42
|
-
components: Record<string, Component | MoveType | EmptyComponent>;
|
|
43
81
|
resources: Record<string, Component | MoveType>;
|
|
44
82
|
enums: Record<string, string[]>;
|
|
45
83
|
};
|
|
46
84
|
|
|
47
85
|
export type BaseType = any;
|
|
48
|
-
export type ErrorData =
|
|
86
|
+
export type ErrorData = Record<string, ErrorEntry>;
|
|
49
87
|
export type EventData = any;
|
|
50
|
-
export type SchemaData = any;
|
|
51
|
-
export type SchemaType = any;
|
|
@@ -34,7 +34,7 @@ export async function loadConfig(configPath?: string): Promise<unknown> {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
async function resolveConfigPath(configPath: string | undefined, toFileURL?: boolean) {
|
|
38
38
|
if (configPath === undefined) {
|
|
39
39
|
configPath = await getUserConfigPath();
|
|
40
40
|
} else {
|
|
@@ -28,9 +28,3 @@ export async function formatMove(content: string, prettierConfigPath?: string):
|
|
|
28
28
|
return content;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
export async function formatTypescript(content: string): Promise<string> {
|
|
33
|
-
return prettier.format(content, {
|
|
34
|
-
parser: 'typescript'
|
|
35
|
-
});
|
|
36
|
-
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { formatTypescript } from './format';
|
|
4
3
|
import { debug } from '../debug';
|
|
5
4
|
|
|
6
5
|
export async function formatAndWriteMove(
|
|
@@ -9,19 +8,18 @@ export async function formatAndWriteMove(
|
|
|
9
8
|
logPrefix?: string
|
|
10
9
|
): Promise<void> {
|
|
11
10
|
const formattedOutput = output;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#[allow(unused_use)]
|
|
15
|
-
|
|
16
|
-
/* Autogenerated file. Do not edit manually. */
|
|
17
|
-
|
|
18
|
-
`;
|
|
11
|
+
const fileHeader = `// Copyright (c) Obelisk Labs, Inc.
|
|
12
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
19
13
|
|
|
20
|
-
|
|
14
|
+
/* Autogenerated file. Do not edit manually. */
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
`;
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
const initPrefix = `#[test_only]\n`;
|
|
19
|
+
|
|
20
|
+
let code = fileHeader + formattedOutput;
|
|
21
|
+
|
|
22
|
+
const deployHookPrefix = `#[allow(lint(share_owned))]\n`;
|
|
25
23
|
|
|
26
24
|
if (
|
|
27
25
|
fullOutputPath.includes('.toml') ||
|
|
@@ -29,7 +27,7 @@ export async function formatAndWriteMove(
|
|
|
29
27
|
fullOutputPath.includes('migrate')
|
|
30
28
|
) {
|
|
31
29
|
code = formattedOutput;
|
|
32
|
-
} else if (fullOutputPath.includes('
|
|
30
|
+
} else if (fullOutputPath.includes('init_test')) {
|
|
33
31
|
code = initPrefix + formattedOutput;
|
|
34
32
|
} else if (fullOutputPath.includes('genesis')) {
|
|
35
33
|
code = deployHookPrefix + formattedOutput;
|
|
@@ -39,22 +37,3 @@ export async function formatAndWriteMove(
|
|
|
39
37
|
await fs.writeFile(fullOutputPath, code);
|
|
40
38
|
debug(`${logPrefix}: ${fullOutputPath}`);
|
|
41
39
|
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Formats typescript code using prettier and write it to a file
|
|
45
|
-
* @param output typescript code
|
|
46
|
-
* @param fullOutputPath full path to the output file
|
|
47
|
-
* @param logPrefix prefix for debug logs
|
|
48
|
-
*/
|
|
49
|
-
export async function formatAndWriteTypescript(
|
|
50
|
-
output: string,
|
|
51
|
-
fullOutputPath: string,
|
|
52
|
-
logPrefix: string
|
|
53
|
-
): Promise<void> {
|
|
54
|
-
const formattedOutput = await formatTypescript(output);
|
|
55
|
-
|
|
56
|
-
await fs.mkdir(path.dirname(fullOutputPath), { recursive: true });
|
|
57
|
-
|
|
58
|
-
await fs.writeFile(fullOutputPath, formattedOutput);
|
|
59
|
-
debug(`${logPrefix}: ${fullOutputPath}`);
|
|
60
|
-
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { DubheConfig, Component, MoveType } from '../types';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
/** Snapshot of field definitions for a single resource/object/scene. */
|
|
7
|
+
type FieldSnapshot = Record<string, string>;
|
|
8
|
+
|
|
9
|
+
/** Top-level structure of a dubhe.lock.json file. */
|
|
10
|
+
interface DubheLock {
|
|
11
|
+
version: 1;
|
|
12
|
+
resources: Record<string, FieldSnapshot>;
|
|
13
|
+
objects: Record<string, FieldSnapshot>;
|
|
14
|
+
scenes: Record<string, FieldSnapshot>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Extract the field map for a resource entry (handles both shorthand and full form). */
|
|
18
|
+
function resourceFields(entry: Component | MoveType): FieldSnapshot {
|
|
19
|
+
if (typeof entry === 'string') {
|
|
20
|
+
return { value: entry };
|
|
21
|
+
}
|
|
22
|
+
const fields: FieldSnapshot = {};
|
|
23
|
+
for (const [k, v] of Object.entries(entry.fields ?? {})) {
|
|
24
|
+
fields[k] = v as string;
|
|
25
|
+
}
|
|
26
|
+
return fields;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Extract the field map for an object or scene entry. */
|
|
30
|
+
function objectOrSceneFields(entry: { fields: Record<string, MoveType> }): FieldSnapshot {
|
|
31
|
+
const fields: FieldSnapshot = {};
|
|
32
|
+
for (const [k, v] of Object.entries(entry.fields)) {
|
|
33
|
+
fields[k] = v as string;
|
|
34
|
+
}
|
|
35
|
+
return fields;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Build a full DubheLock snapshot from a DubheConfig. */
|
|
39
|
+
function buildLock(config: DubheConfig): DubheLock {
|
|
40
|
+
const resources: Record<string, FieldSnapshot> = {};
|
|
41
|
+
for (const [name, entry] of Object.entries(config.resources ?? {})) {
|
|
42
|
+
resources[name] = resourceFields(entry);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const objects: Record<string, FieldSnapshot> = {};
|
|
46
|
+
for (const [name, entry] of Object.entries(config.objects ?? {})) {
|
|
47
|
+
objects[name] = objectOrSceneFields(entry);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const scenes: Record<string, FieldSnapshot> = {};
|
|
51
|
+
for (const [name, entry] of Object.entries(config.scenes ?? {})) {
|
|
52
|
+
scenes[name] = objectOrSceneFields(entry);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { version: 1, resources, objects, scenes };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type SectionName = 'resources' | 'objects' | 'scenes';
|
|
59
|
+
|
|
60
|
+
/** Compare old vs new field snapshots and throw on breaking changes. */
|
|
61
|
+
function checkSection(
|
|
62
|
+
section: SectionName,
|
|
63
|
+
oldSection: Record<string, FieldSnapshot>,
|
|
64
|
+
newSection: Record<string, FieldSnapshot>
|
|
65
|
+
): void {
|
|
66
|
+
for (const [name, newFields] of Object.entries(newSection)) {
|
|
67
|
+
const oldFields = oldSection[name];
|
|
68
|
+
if (!oldFields) continue; // New entry — allowed.
|
|
69
|
+
|
|
70
|
+
for (const [field, oldType] of Object.entries(oldFields)) {
|
|
71
|
+
if (!(field in newFields)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`[dubhe] Breaking change detected in ${section}.${name}:\n` +
|
|
74
|
+
` Field "${field}" was removed.\n\n` +
|
|
75
|
+
`Resources, objects, and scenes are stored as raw bytes on-chain.\n` +
|
|
76
|
+
`Removing fields corrupts existing data. Use a new name (e.g. "${name}_v2") for breaking changes.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (newFields[field] !== oldType) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`[dubhe] Breaking change detected in ${section}.${name}:\n` +
|
|
82
|
+
` Field "${field}" type changed from "${oldType}" to "${newFields[field]}".\n\n` +
|
|
83
|
+
`Resources, objects, and scenes are stored as raw bytes on-chain.\n` +
|
|
84
|
+
`Changing field types corrupts existing data. Use a new name (e.g. "${name}_v2") for breaking changes.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Read the existing lock file (if any), check for breaking changes against the
|
|
93
|
+
* current config, then write an updated lock file.
|
|
94
|
+
*
|
|
95
|
+
* Call this at the start of `codegen()` before any file generation begins.
|
|
96
|
+
*
|
|
97
|
+
* @param rootDir The project root directory (same dir passed to `codegen()`).
|
|
98
|
+
* @param config The parsed DubheConfig.
|
|
99
|
+
*/
|
|
100
|
+
export function checkAndUpdateLock(rootDir: string, config: DubheConfig): void {
|
|
101
|
+
const lockPath = path.join(rootDir, `${config.name}.lock.json`);
|
|
102
|
+
const newLock = buildLock(config);
|
|
103
|
+
|
|
104
|
+
if (existsSync(lockPath)) {
|
|
105
|
+
let oldLock: DubheLock;
|
|
106
|
+
try {
|
|
107
|
+
oldLock = JSON.parse(readFileSync(lockPath, 'utf-8')) as DubheLock;
|
|
108
|
+
} catch {
|
|
109
|
+
console.warn(
|
|
110
|
+
chalk.yellow('[dubhe]') + ` Could not parse ${chalk.bold(lockPath)}, skipping break-check.`
|
|
111
|
+
);
|
|
112
|
+
writeFileSync(lockPath, JSON.stringify(newLock, null, 2) + '\n', 'utf-8');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
checkSection('resources', oldLock.resources ?? {}, newLock.resources);
|
|
117
|
+
checkSection('objects', oldLock.objects ?? {}, newLock.objects);
|
|
118
|
+
checkSection('scenes', oldLock.scenes ?? {}, newLock.scenes);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
writeFileSync(lockPath, JSON.stringify(newLock, null, 2) + '\n', 'utf-8');
|
|
122
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from './format';
|
|
2
2
|
export * from './formatAndWrite';
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './renderMove/schemaGen';
|
|
3
|
+
export * from './renderMove/codegen';
|
|
5
4
|
export * from './renderMove/dapp';
|
|
5
|
+
export * from './renderMove/generateObjects';
|
|
6
|
+
export * from './renderMove/generateScenes';
|
|
6
7
|
export * from './config';
|
|
8
|
+
export * from './generateLock';
|
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import { DubheConfig } from '../../types';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
|
-
// import { rmdirSync } from 'fs'; // Unused
|
|
4
3
|
import { deleteFolderRecursive } from './common';
|
|
5
4
|
import { generateToml } from './generateToml';
|
|
6
|
-
// import { generateSchemaData, generateSchemaStructure } from './generateSchema';
|
|
7
5
|
import { generateDeployHook, generateMigrate } from './generateScript';
|
|
8
6
|
import { generateDappKey } from './generateDappKey';
|
|
9
|
-
// import { generateSchemaEvent } from // Unused './generateEvent';
|
|
10
7
|
import { generateSystemsAndTests } from './generateSystem';
|
|
11
|
-
|
|
12
|
-
import { generateSchemaError } from './generateError';
|
|
13
|
-
// import { generateDefaultSchema } from // Unused './generateDefaultSchema';
|
|
8
|
+
import { generateError } from './generateError';
|
|
14
9
|
import { generateInitTest } from './generateInitTest';
|
|
15
|
-
import { generateComponents } from './generateComponents';
|
|
16
10
|
import { generateGenesis } from './generateGenesis';
|
|
17
11
|
import { generateEnums } from './generateEnums';
|
|
18
12
|
import { generateResources } from './generateResources';
|
|
13
|
+
import { generateObjects } from './generateObjects';
|
|
14
|
+
import { generatePermits } from './generatePermits';
|
|
15
|
+
import { generateScenes } from './generateScenes';
|
|
16
|
+
import { generateUserStorageInit } from './generateUserStorageInit';
|
|
17
|
+
import { checkAndUpdateLock } from '../generateLock';
|
|
18
|
+
import { validateConfig } from '../validateConfig';
|
|
19
19
|
import path from 'node:path';
|
|
20
20
|
|
|
21
|
-
export async function
|
|
21
|
+
export async function codegen(
|
|
22
22
|
rootDir: string,
|
|
23
23
|
config: DubheConfig,
|
|
24
|
-
network?: 'mainnet' | 'testnet' | 'devnet' | 'localnet'
|
|
24
|
+
network?: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
|
|
25
|
+
initialMode: 0 | 1 = 1
|
|
25
26
|
) {
|
|
26
|
-
console.log('\n🚀 Starting
|
|
27
|
+
console.log('\n🚀 Starting Code Generation Process...');
|
|
27
28
|
console.log('📋 Project Configuration:');
|
|
28
29
|
console.log(` └─ Name: ${config.name}`);
|
|
29
30
|
console.log(` └─ Description: ${config.description || 'No description provided'}`);
|
|
30
31
|
console.log(` └─ Network: ${network || 'testnet'}`);
|
|
32
|
+
console.log(` └─ Settlement Mode: ${initialMode === 1 ? 'USER_PAYS' : 'DAPP_SUBSIDIZES'}`);
|
|
33
|
+
|
|
34
|
+
// Validate config once here — defineConfig also validates on load, but that
|
|
35
|
+
// is the same process call, so we suppress duplicates by making this the
|
|
36
|
+
// single codegen-phase validation point.
|
|
37
|
+
validateConfig(config);
|
|
38
|
+
|
|
39
|
+
// Check for breaking field changes and update the lock file.
|
|
40
|
+
checkAndUpdateLock(rootDir, config);
|
|
31
41
|
|
|
32
42
|
console.log(rootDir);
|
|
33
43
|
const projectDir = path.join(rootDir, 'src', config.name);
|
|
@@ -42,7 +52,7 @@ export async function schemaGen(
|
|
|
42
52
|
|
|
43
53
|
const genesisPath = path.join(projectDir, 'sources', 'codegen', 'genesis.move');
|
|
44
54
|
if (!existsSync(genesisPath)) {
|
|
45
|
-
await generateGenesis(config, genesisPath);
|
|
55
|
+
await generateGenesis(config, genesisPath, initialMode);
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
const initTestPath = path.join(projectDir, 'sources', 'codegen', 'init_test.move');
|
|
@@ -57,22 +67,20 @@ export async function schemaGen(
|
|
|
57
67
|
|
|
58
68
|
const deployHookPath = path.join(projectDir, 'sources', 'scripts', 'deploy_hook.move');
|
|
59
69
|
if (!existsSync(deployHookPath)) {
|
|
60
|
-
await generateDeployHook(config, deployHookPath);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const componentsPath = path.join(projectDir, 'sources', 'codegen', 'components');
|
|
64
|
-
if (!existsSync(componentsPath)) {
|
|
65
|
-
await generateComponents(config, componentsPath);
|
|
66
|
-
} else {
|
|
67
|
-
await generateComponents(config, componentsPath);
|
|
70
|
+
await generateDeployHook(config, deployHookPath, initialMode);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const resourcesPath = path.join(projectDir, 'sources', 'codegen', 'resources');
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
await generateResources(config, resourcesPath);
|
|
75
|
+
|
|
76
|
+
const objectsPath = path.join(projectDir, 'sources', 'codegen', 'objects');
|
|
77
|
+
await generateObjects(config, objectsPath);
|
|
78
|
+
|
|
79
|
+
const permitsPath = path.join(projectDir, 'sources', 'codegen', 'permits');
|
|
80
|
+
await generatePermits(config, permitsPath);
|
|
81
|
+
|
|
82
|
+
const scenesPath = path.join(projectDir, 'sources', 'codegen', 'scenes');
|
|
83
|
+
await generateScenes(config, scenesPath);
|
|
76
84
|
|
|
77
85
|
const enumsPath = path.join(projectDir, 'sources', 'codegen', 'enums');
|
|
78
86
|
if (!existsSync(enumsPath)) {
|
|
@@ -80,12 +88,16 @@ export async function schemaGen(
|
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
if (config.errors) {
|
|
83
|
-
await
|
|
91
|
+
await generateError(config.name, config.errors, rootDir);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
const userStorageInitPath = path.join(projectDir, 'sources', 'codegen', 'user_storage_init.move');
|
|
95
|
+
await generateUserStorageInit(config, userStorageInitPath);
|
|
96
|
+
|
|
88
97
|
await generateSystemsAndTests(config, rootDir);
|
|
89
98
|
await generateMigrate(config, rootDir);
|
|
90
|
-
console.log('\n✅
|
|
99
|
+
console.log('\n✅ Code Generation Complete!\n');
|
|
91
100
|
}
|
|
101
|
+
|
|
102
|
+
/** @deprecated Use `codegen` instead. */
|
|
103
|
+
export const schemaGen = codegen;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// import { MoveType } from '../../types'; // Unused
|
|
2
1
|
import fs from 'fs';
|
|
3
2
|
|
|
4
3
|
export function deleteFolderRecursive(path: string) {
|
|
@@ -14,67 +13,3 @@ export function deleteFolderRecursive(path: string) {
|
|
|
14
13
|
fs.rmdirSync(path);
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
export function capitalizeFirstLetter(input: string): string {
|
|
19
|
-
return input.charAt(0).toUpperCase() + input.slice(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
*
|
|
24
|
-
* @param values
|
|
25
|
-
* @param prefixArgs
|
|
26
|
-
* @return [ name, age, birth_time ]
|
|
27
|
-
*/
|
|
28
|
-
export function getStructAttrs(values: Record<string, string> | string): string {
|
|
29
|
-
return Object.entries(values)
|
|
30
|
-
.map(([key, _]) => `${key}`)
|
|
31
|
-
.join(',');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
35
|
-
function isAddress(str: string): boolean {
|
|
36
|
-
const regex = /^0x[a-fA-F0-9]+$/;
|
|
37
|
-
return regex.test(str);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
* @param values
|
|
43
|
-
* @return ( bool , u64 , u64)
|
|
44
|
-
*/
|
|
45
|
-
// export function getStructTypes(values: SchemaType): string {
|
|
46
|
-
export function getStructTypes(values: Record<string, string>): string {
|
|
47
|
-
return `(${Object.entries(values).map(([_, type]) => `${type}`)})`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
* @param values
|
|
53
|
-
* @return Attributes and types of the struct. [ name: string, age: u64 ]
|
|
54
|
-
*/
|
|
55
|
-
export function getStructAttrsWithType(values: Record<string, string>): string[] {
|
|
56
|
-
return Object.entries(values).map(([key, type]) => `${key}: ${type}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param values
|
|
61
|
-
* @return [ data.name, data.age ]
|
|
62
|
-
*/
|
|
63
|
-
export function getStructAttrsQuery(values: Record<string, string>): string[] {
|
|
64
|
-
return Object.entries(values).map(([key, _]) => `self.${key}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function containsString(obj: Record<string, any>, searchString: string): boolean {
|
|
68
|
-
for (const key in obj) {
|
|
69
|
-
if (obj.hasOwnProperty(key)) {
|
|
70
|
-
const value = obj[key];
|
|
71
|
-
if (
|
|
72
|
-
(typeof value === 'string' && value === searchString) ||
|
|
73
|
-
(typeof value === 'string' && value.includes(searchString) && value.includes('>'))
|
|
74
|
-
) {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
import { DubheConfig } from '../../types';
|
|
2
|
-
|
|
3
|
-
const checkDuplicateKeys = (config: DubheConfig): void => {
|
|
4
|
-
const componentKeys = Object.keys(config.components || {});
|
|
5
|
-
const resourceKeys = Object.keys(config.resources || {});
|
|
6
|
-
|
|
7
|
-
const duplicates = componentKeys.filter((key) => resourceKeys.includes(key));
|
|
8
|
-
|
|
9
|
-
if (duplicates.length > 0) {
|
|
10
|
-
throw new Error(
|
|
11
|
-
`Duplicate keys found between components and resources: ${duplicates.join(', ')}`
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
};
|
|
2
|
+
import { validateConfigErrors } from '../validateConfig';
|
|
15
3
|
|
|
16
4
|
export const defineConfig = (config: DubheConfig): DubheConfig => {
|
|
17
|
-
|
|
5
|
+
validateConfigErrors(config);
|
|
18
6
|
return config;
|
|
19
7
|
};
|
|
@@ -3,30 +3,45 @@ import { formatAndWriteMove } from '../formatAndWrite';
|
|
|
3
3
|
|
|
4
4
|
export async function generateDappKey(config: DubheConfig, path: string) {
|
|
5
5
|
let code = `module ${config.name}::dapp_key {
|
|
6
|
-
use std::type_name;
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
use std::type_name;
|
|
7
|
+
use sui::address;
|
|
8
|
+
use std::ascii::String;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/// DappKey — package-level authorization token for this DApp.
|
|
11
|
+
///
|
|
12
|
+
/// SECURITY: \`new()\` is intentionally \`public(package)\`.
|
|
13
|
+
/// Only code compiled into this package can construct a DappKey instance.
|
|
14
|
+
/// All framework write functions (\`set_record\`, \`set_field\`,
|
|
15
|
+
/// \`take_record\`, \`create_user_storage\`, …) require \`_auth: DappKey\`
|
|
16
|
+
/// as proof that the call originated from inside this package — an
|
|
17
|
+
/// external PTB cannot fabricate that proof.
|
|
18
|
+
///
|
|
19
|
+
/// NEVER change \`new()\` to \`public\`, and never accept a DappKey
|
|
20
|
+
/// value as a parameter from an external caller. Either mistake removes
|
|
21
|
+
/// every package-level access guard, allowing any PTB to write arbitrary
|
|
22
|
+
/// user data or register UserStorages without going through the DApp's
|
|
23
|
+
/// own entry functions.
|
|
11
24
|
|
|
12
|
-
|
|
25
|
+
public struct DappKey has copy, drop {}
|
|
13
26
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
/// Constructs an authorization token. Callable only from within this package.
|
|
28
|
+
/// Pass the result as \`_auth\` to any framework function that requires it.
|
|
29
|
+
public(package) fun new(): DappKey {
|
|
30
|
+
DappKey {}
|
|
31
|
+
}
|
|
17
32
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
public fun to_string(): String {
|
|
34
|
+
type_name::with_defining_ids<DappKey>().into_string()
|
|
35
|
+
}
|
|
21
36
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
public fun package_id(): address {
|
|
38
|
+
let package_id_str = type_name::with_defining_ids<DappKey>().address_string();
|
|
39
|
+
address::from_ascii_bytes(package_id_str.as_bytes())
|
|
40
|
+
}
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
public fun eq<DappKey1: copy + drop, DappKey2: copy + drop>(_: &DappKey1, _: &DappKey2): bool {
|
|
43
|
+
type_name::with_defining_ids<DappKey1>() == type_name::with_defining_ids<DappKey2>()
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
`;
|
|
32
47
|
await formatAndWriteMove(code, path, 'formatAndWriteMove');
|