lan-low-code-cli 0.0.0-stage → 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/dist/index.js ADDED
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.program = void 0;
4
+ const commander_1 = require("commander");
5
+ const app_1 = require("./commands/app");
6
+ const config_1 = require("./commands/config");
7
+ const control_1 = require("./commands/control");
8
+ const field_1 = require("./commands/field");
9
+ const form_1 = require("./commands/form");
10
+ const locale_1 = require("./commands/locale");
11
+ const logger_1 = require("./utils/logger");
12
+ const VERSION = '2.0.0';
13
+ const program = new commander_1.Command();
14
+ exports.program = program;
15
+ program
16
+ .name('llc')
17
+ .description('低代码表单引擎命令行工具:创建表单 / 获取可用字段 / 自动完善表单字段')
18
+ .version(VERSION, '-v, --version', '查看版本号')
19
+ .helpOption('-h, --help', '显示帮助信息')
20
+ .addHelpCommand(false)
21
+ .option('--base-url <url>', '服务端基址(全局覆盖,等同各子命令参数)')
22
+ .option('--token <token>', '鉴权 Token(全局覆盖)');
23
+ // 将全局选项透传到子命令
24
+ program.hook('preAction', (thisCommand, actionCommand) => {
25
+ const globals = thisCommand.opts();
26
+ if (globals.baseUrl && actionCommand.getOptionValue('baseUrl') === undefined) {
27
+ actionCommand.setOptionValue('baseUrl', globals.baseUrl);
28
+ }
29
+ if (globals.token && actionCommand.getOptionValue('token') === undefined) {
30
+ actionCommand.setOptionValue('token', globals.token);
31
+ }
32
+ });
33
+ (0, config_1.registerConfigCommand)(program);
34
+ (0, app_1.registerAppCommand)(program);
35
+ (0, form_1.registerFormCommand)(program);
36
+ (0, field_1.registerFieldCommand)(program);
37
+ (0, locale_1.registerLocaleCommand)(program);
38
+ (0, control_1.registerControlCommand)(program);
39
+ /**
40
+ * 子命令组不再生成内置的 `help [command]`(英文文案),
41
+ * 统一由根部的 `llc help [command...]` 提供。
42
+ */
43
+ function disableBuiltinHelpCommands(cmd) {
44
+ cmd.addHelpCommand(false);
45
+ cmd.commands.forEach(disableBuiltinHelpCommands);
46
+ }
47
+ disableBuiltinHelpCommands(program);
48
+ /**
49
+ * `llc help [command...]`:显示帮助。
50
+ *
51
+ * 支持多级路径(`llc help form create`),commander 内置的 `help [command]`
52
+ * 只能解析一级,因此这里自行按段向下查找命令树。
53
+ */
54
+ program
55
+ .command('help [command...]')
56
+ .description('显示帮助信息(支持多级,如 help form create)')
57
+ .action((parts = []) => {
58
+ let target = program;
59
+ for (const part of parts) {
60
+ const next = target.commands.find((cmd) => cmd.name() === part || cmd.aliases().includes(part));
61
+ if (!next) {
62
+ logger_1.logger.error(`未知命令:llc ${parts.join(' ')}(在 "${target.name()}" 下找不到 "${part}")`);
63
+ process.exit(1);
64
+ }
65
+ target = next;
66
+ }
67
+ target.outputHelp();
68
+ });
69
+ program.addHelpText('after', `
70
+ ${logger_1.color.bold('从零到一')}
71
+ ${logger_1.color.gray('# 1. 配置服务端与 Token(一次性,也可用环境变量)')}
72
+ llc config set --base-url http://host/data --token <X-AUTH-TOKEN>
73
+
74
+ ${logger_1.color.gray('# 2. 创建应用(返回 appId,后端会同时创建默认首页)')}
75
+ llc app create --name 客户管理 --code crm
76
+
77
+ ${logger_1.color.gray('# 3. 查看环境可用控件(--add-field 第三段的取值依据)')}
78
+ llc control list
79
+ llc control show --type moneytext
80
+
81
+ ${logger_1.color.gray('# 4. 创建复杂表单并发布')}
82
+ llc form create --name 客户信息 --app-id <appId> --publish \\
83
+ --add-field "fd_col_name:客户名称" \\
84
+ --add-field "fd_col_level:客户等级:select" \\
85
+ --option "fd_col_level=A级,B级,C级" \\
86
+ --detail "联系人明细=fd_pname:姓名,fd_pphone:电话:phoneNumber" \\
87
+ --layout tabs --tabs "基本信息:fd_col_name,fd_col_level" --tab-layout grid --grid-columns 3 \\
88
+ --required "fd_col_name" --readonly "fd_col_code" --hidden "fd_col_inner" \\
89
+ --locale all
90
+
91
+ ${logger_1.color.gray('# 5. 校验与查看')}
92
+ llc app forms --id <appId>
93
+ llc form get --id <fdId>
94
+ llc field list --form-id <fdId>
95
+
96
+ ${logger_1.color.gray('# 6. 迭代调整(布局默认保持不动;--layout 才会重建布局)')}
97
+ llc form autofill --id <fdId> --apply --publish --locale all
98
+
99
+ ${logger_1.color.gray('# 7. 反悔 / 清理(均需 --yes 确认)')}
100
+ llc form delete --id <fdId> --yes
101
+ llc app delete --id <appId> --yes
102
+
103
+ ${logger_1.color.bold('常用查询')}
104
+ llc app list [--keyword 名称] [--no-auth] 应用列表(fdId 即 --app-id)
105
+ llc app forms --id <appId> 应用下表单列表
106
+ llc locale list 环境提供了哪些语言译文
107
+ llc field library [--keyword 关键词] 字段库
108
+
109
+ 环境变量:
110
+ LLC_BASE_URL 服务端基址
111
+ LLC_TOKEN 鉴权 Token
112
+
113
+ 更多信息:llc <command> --help(如 llc form create --help),或查阅 README.md
114
+ `);
115
+ async function main() {
116
+ try {
117
+ await program.parseAsync(process.argv);
118
+ // 显式退出:避免未关闭的 keep-alive 连接令 Windows 下的 Node 触发
119
+ // libuv 断言(Assertion failed: !(handle->flags & UV_HANDLE_CLOSING))
120
+ // 而以非零退出码结束,干扰脚本判断。
121
+ process.exit(0);
122
+ }
123
+ catch (err) {
124
+ // 显示帮助/版本属于正常行为,退出码应为 0(commander 默认抛 1)
125
+ if (err instanceof commander_1.CommanderError &&
126
+ (err.code === 'commander.helpDisplayed' || err.code === 'commander.version')) {
127
+ process.exit(0);
128
+ }
129
+ logger_1.logger.error(err.message || String(err));
130
+ if (process.env.LLC_DEBUG) {
131
+ console.error(err);
132
+ }
133
+ process.exit(1);
134
+ }
135
+ }
136
+ main();
package/dist/types.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * 低代码表单引擎 - 公共类型定义
4
+ *
5
+ * 注意:此文件为 Node 端独立实现,不依赖任何前端包(@ekp-infra/*、@ekp-runtime/*)。
6
+ * 类型依据后端 mk-sys-xform 的 DTO 与 sys-xform 前端的持久化结构整理而来。
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.FORM_TYPE_LABEL = exports.FORM_TYPE_ALIAS = exports.EFormDefineType = void 0;
10
+ /**
11
+ * 表单类型(对应后端 com.landray.sys.xform.constant.FormDefineType)
12
+ */
13
+ var EFormDefineType;
14
+ (function (EFormDefineType) {
15
+ /** 普通表单 */
16
+ EFormDefineType[EFormDefineType["Common"] = 1] = "Common";
17
+ /** 审批意见表单 */
18
+ EFormDefineType[EFormDefineType["General"] = 2] = "General";
19
+ /** 全局配置项表单 */
20
+ EFormDefineType[EFormDefineType["Config"] = 3] = "Config";
21
+ /** 弹窗表单 */
22
+ EFormDefineType[EFormDefineType["Modal"] = 4] = "Modal";
23
+ /** 通用表单 */
24
+ EFormDefineType[EFormDefineType["Template"] = 5] = "Template";
25
+ /** 引用者表单 */
26
+ EFormDefineType[EFormDefineType["Refer"] = 6] = "Refer";
27
+ })(EFormDefineType || (exports.EFormDefineType = EFormDefineType = {}));
28
+ exports.FORM_TYPE_ALIAS = {
29
+ common: EFormDefineType.Common,
30
+ '普通表单': EFormDefineType.Common,
31
+ general: EFormDefineType.General,
32
+ '审批意见表单': EFormDefineType.General,
33
+ config: EFormDefineType.Config,
34
+ '全局配置项表单': EFormDefineType.Config,
35
+ modal: EFormDefineType.Modal,
36
+ '弹窗表单': EFormDefineType.Modal,
37
+ template: EFormDefineType.Template,
38
+ '通用表单': EFormDefineType.Template,
39
+ refer: EFormDefineType.Refer,
40
+ reference: EFormDefineType.Refer,
41
+ '引用者表单': EFormDefineType.Refer,
42
+ };
43
+ exports.FORM_TYPE_LABEL = {
44
+ [EFormDefineType.Common]: '普通表单',
45
+ [EFormDefineType.General]: '审批意见表单',
46
+ [EFormDefineType.Config]: '全局配置项表单',
47
+ [EFormDefineType.Modal]: '弹窗表单',
48
+ [EFormDefineType.Template]: '通用表单',
49
+ [EFormDefineType.Refer]: '引用者表单',
50
+ };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeIcon = normalizeIcon;
4
+ const constants_1 = require("../constants");
5
+ /**
6
+ * 规范化图标输入:始终输出合法 JSON 字符串。
7
+ *
8
+ * 用户传图标名时补全为标准结构;传 JSON 时原样规范化;不传则用内置默认图标。
9
+ * 表单(`fdIcon`)与应用(`fdIcon`)共用 —— sys-modeling 编辑页存在
10
+ * `JSON.parse(\`${fdIcon}\` || '{}')` 的写法,undefined 会变成真值字符串
11
+ * "undefined" 并抛 `SyntaxError`,导致编辑器无法进入。
12
+ */
13
+ function normalizeIcon(input) {
14
+ const raw = input?.trim();
15
+ if (!raw)
16
+ return constants_1.DEFAULT_FORM_ICON_JSON;
17
+ if (raw.startsWith('{')) {
18
+ try {
19
+ return JSON.stringify(JSON.parse(raw));
20
+ }
21
+ catch {
22
+ /* 非合法 JSON,按图标名处理 */
23
+ }
24
+ }
25
+ return JSON.stringify({ ...constants_1.DEFAULT_FORM_ICON, name: raw });
26
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toLocaleCode = toLocaleCode;
4
+ exports.normalizeLocale = normalizeLocale;
5
+ exports.normalizeLangMap = normalizeLangMap;
6
+ const constants_1 = require("../constants");
7
+ /**
8
+ * 原始语言码 → 前端语言码。
9
+ *
10
+ * 与前端 `sys-xform/src/util/pureUtils.ts#getLocale` 完全一致:
11
+ * 取 `-` 后的第二段并首字母大写。
12
+ *
13
+ * ```ts
14
+ * toLocaleCode('zh-cn') // 'Cn'
15
+ * toLocaleCode('en-us') // 'Us'
16
+ * toLocaleCode('zh-tw') // 'Tw'
17
+ * toLocaleCode('zh') // '' (无 `-`,前端同样返回 undefined)
18
+ * ```
19
+ */
20
+ function toLocaleCode(raw) {
21
+ if (!raw)
22
+ return '';
23
+ const parts = String(raw).split('-');
24
+ if (parts.length < 2)
25
+ return '';
26
+ const tail = parts[1].toLowerCase();
27
+ return tail.charAt(0).toUpperCase() + tail.slice(1);
28
+ }
29
+ /** 归一化用户传入的语言码:`cn` / `US` → `Cn` / `Us`;非法则抛错 */
30
+ function normalizeLocale(input) {
31
+ const raw = input.trim();
32
+ if (!constants_1.LOCALE_PATTERN.test(raw)) {
33
+ throw new Error(`非法的语言码:${input}(应为 zh-cn/zh-tw 的第二段,如 Cn、Us、Tw)`);
34
+ }
35
+ return raw[0].toUpperCase() + raw.slice(1).toLowerCase();
36
+ }
37
+ /**
38
+ * 把接口返回的「原始语言码 → 文案」映射归一为「前端语言码 → 文案」。
39
+ *
40
+ * ```ts
41
+ * normalizeLangMap({ 'zh-cn': '请输入', 'en-us': 'Please enter' })
42
+ * // { Cn: '请输入', Us: 'Please enter' }
43
+ * ```
44
+ */
45
+ function normalizeLangMap(raw) {
46
+ const result = {};
47
+ for (const [code, value] of Object.entries(raw)) {
48
+ const locale = toLocaleCode(code);
49
+ if (locale)
50
+ result[locale] = value;
51
+ }
52
+ return result;
53
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * 极简终端输出工具(零依赖,自行实现 ANSI 着色)。
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = exports.color = void 0;
7
+ const noColor = process.env.NO_COLOR !== undefined || process.env.LLC_NO_COLOR !== undefined;
8
+ const wrap = (code) => (text) => (noColor ? text : `\u001b[${code}m${text}\u001b[0m`);
9
+ exports.color = {
10
+ red: wrap(31),
11
+ green: wrap(32),
12
+ yellow: wrap(33),
13
+ blue: wrap(34),
14
+ magenta: wrap(35),
15
+ cyan: wrap(36),
16
+ gray: wrap(90),
17
+ bold: wrap(1),
18
+ };
19
+ /**
20
+ * 说明:除 `json()` 外全部输出到 **stderr**,
21
+ * 以便 `--json` 时可直接 `> out.json` 得到纯 JSON。
22
+ */
23
+ exports.logger = {
24
+ info(msg) {
25
+ console.error(msg);
26
+ },
27
+ step(msg) {
28
+ console.error(`${exports.color.cyan('›')} ${msg}`);
29
+ },
30
+ success(msg) {
31
+ console.error(`${exports.color.green('✔')} ${msg}`);
32
+ },
33
+ warn(msg) {
34
+ console.error(`${exports.color.yellow('!')} ${msg}`);
35
+ },
36
+ error(msg) {
37
+ console.error(`${exports.color.red('✖')} ${msg}`);
38
+ },
39
+ title(msg) {
40
+ console.error(exports.color.bold(msg));
41
+ },
42
+ /** 输出 JSON(stdout) */
43
+ json(data) {
44
+ console.log(JSON.stringify(data, null, 2));
45
+ },
46
+ /** 输出表格(左对齐文本表格,stderr) */
47
+ table(rows, columns) {
48
+ if (!rows || rows.length === 0) {
49
+ console.error(exports.color.gray('(无数据)'));
50
+ return;
51
+ }
52
+ const cols = columns ?? Object.keys(rows[0]);
53
+ const widths = cols.map((c) => Math.max(displayWidth(c), ...rows.map((r) => displayWidth(formatCell(r[c])))));
54
+ const line = (cells) => cells.map((cell, i) => padRight(cell, widths[i])).join(' ');
55
+ console.error(exports.color.gray(line(cols)));
56
+ console.error(exports.color.gray(widths.map((w) => '─'.repeat(w)).join('──')));
57
+ rows.forEach((r) => console.error(line(cols.map((c) => formatCell(r[c])))));
58
+ },
59
+ };
60
+ function formatCell(value) {
61
+ if (value === null || value === undefined)
62
+ return '';
63
+ if (typeof value === 'object')
64
+ return JSON.stringify(value);
65
+ return String(value);
66
+ }
67
+ /** 粗略计算显示宽度(中日韩字符按 2 计) */
68
+ function displayWidth(text) {
69
+ let width = 0;
70
+ for (const ch of text) {
71
+ width += /[\u1100-\u115F\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE30-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]/.test(ch) ? 2 : 1;
72
+ }
73
+ return width;
74
+ }
75
+ function padRight(text, width) {
76
+ const diff = width - displayWidth(text);
77
+ return diff > 0 ? text + ' '.repeat(diff) : text;
78
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCmptKey = generateCmptKey;
4
+ exports.generateTemplateCode = generateTemplateCode;
5
+ exports.generateColId = generateColId;
6
+ exports.uuid = uuid;
7
+ exports.isMessageKey = isMessageKey;
8
+ exports.generateMessageKey = generateMessageKey;
9
+ exports.generateFieldName = generateFieldName;
10
+ exports.cloneDeep = cloneDeep;
11
+ exports.safeJsonParse = safeJsonParse;
12
+ exports.toSnakeCase = toSnakeCase;
13
+ exports.sanitizeIdentifier = sanitizeIdentifier;
14
+ /** 与前端 generateCmptKey 一致的随机短串:type~xxxxxx */
15
+ function generateCmptKey(type) {
16
+ return `${type}~${Math.random().toString(36).slice(-6)}`;
17
+ }
18
+ /** 生成模板编码 template_xxxxxx */
19
+ function generateTemplateCode() {
20
+ return `template_${Date.now().toString(36).slice(-6)}`;
21
+ }
22
+ /** 生成列编码 fd_col_xxxxxx */
23
+ function generateColId() {
24
+ return `fd_col_${Math.random().toString(36).slice(-6)}`;
25
+ }
26
+ /**
27
+ * 生成随机的多语言 messageKey 主体。
28
+ *
29
+ * 必须与前端 `sys-xform/src/util/runtimeUtil.ts#uuid` 完全一致:
30
+ * Date.now().toString(36) + Math.random().toString(36).slice(2)
31
+ *
32
+ * ⚠️ 只能是 `[0-9a-z]`(base36),**不能含连字符**。
33
+ * 因为前端识别多语言标记的正则是 `/\!\{([\w\.]*)\}/gi`
34
+ * (见 `store/i18n/syncApi.ts#isCustomI18n`、`components/runtime/utils.ts`),
35
+ * `\w` 不包含 `-`,用 RFC4122 UUID 会导致 key 无法被识别,界面上会原样显示 `!{...}`。
36
+ */
37
+ function uuid() {
38
+ return Date.now().toString(36) + Math.random().toString(36).slice(2);
39
+ }
40
+ /** 合法的多语言 messageKey:!{仅含 [\w.]} */
41
+ const MESSAGE_KEY_PATTERN = /^!\{[\w.]+\}$/;
42
+ /** 判断是否为前端可识别的多语言 messageKey */
43
+ function isMessageKey(value) {
44
+ return !!value && MESSAGE_KEY_PATTERN.test(value);
45
+ }
46
+ /** 生成多语言 messageKey(!{xxxx}) */
47
+ function generateMessageKey() {
48
+ return `!{${uuid()}}`;
49
+ }
50
+ /** 字段物理名兜底:fd_xxxxxx */
51
+ function generateFieldName(prefix = 'fd') {
52
+ return `${prefix}_${Math.random().toString(36).slice(-6)}`;
53
+ }
54
+ /** 深度克隆(JSON 安全) */
55
+ function cloneDeep(value) {
56
+ return value === undefined ? value : JSON.parse(JSON.stringify(value));
57
+ }
58
+ /** 安全 JSON 解析 */
59
+ function safeJsonParse(text, fallback) {
60
+ if (!text)
61
+ return fallback;
62
+ try {
63
+ return JSON.parse(text);
64
+ }
65
+ catch {
66
+ return fallback;
67
+ }
68
+ }
69
+ /** 驼峰/下划线转小写下划线 */
70
+ function toSnakeCase(input) {
71
+ return input
72
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
73
+ .replace(/[-\s]+/g, '_')
74
+ .toLowerCase();
75
+ }
76
+ /** 生成合法的表名前缀(不含系统模块前缀) */
77
+ function sanitizeIdentifier(input) {
78
+ return input.replace(/[^a-zA-Z0-9_]/g, '_');
79
+ }