@nocobase/cli 2.1.0-alpha.3 → 2.1.0-alpha.30

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.
Files changed (182) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +379 -19
  3. package/README.zh-CN.md +329 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +131 -0
  6. package/dist/commands/api/resource/create.js +15 -0
  7. package/dist/commands/api/resource/destroy.js +15 -0
  8. package/dist/commands/api/resource/get.js +15 -0
  9. package/dist/commands/api/resource/index.js +20 -0
  10. package/dist/commands/api/resource/list.js +16 -0
  11. package/dist/commands/api/resource/query.js +15 -0
  12. package/dist/commands/api/resource/update.js +15 -0
  13. package/dist/commands/app/down.js +266 -0
  14. package/dist/commands/app/logs.js +98 -0
  15. package/dist/commands/app/restart.js +75 -0
  16. package/dist/commands/app/start.js +253 -0
  17. package/dist/commands/app/stop.js +99 -0
  18. package/dist/commands/app/upgrade.js +582 -0
  19. package/{src/cli.js → dist/commands/build.js} +4 -11
  20. package/dist/commands/config/delete.js +30 -0
  21. package/dist/commands/config/get.js +29 -0
  22. package/dist/commands/config/index.js +20 -0
  23. package/dist/commands/config/list.js +29 -0
  24. package/dist/commands/config/set.js +35 -0
  25. package/dist/commands/db/check.js +238 -0
  26. package/dist/commands/db/logs.js +85 -0
  27. package/dist/commands/db/ps.js +60 -0
  28. package/dist/commands/db/shared.js +96 -0
  29. package/dist/commands/db/start.js +71 -0
  30. package/dist/commands/db/stop.js +71 -0
  31. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  32. package/{src/index.js → dist/commands/down.js} +4 -6
  33. package/{src/commands/locale/react-js-cron/index.js → dist/commands/download.js} +4 -8
  34. package/dist/commands/env/add.js +312 -0
  35. package/dist/commands/env/auth.js +55 -0
  36. package/dist/commands/env/info.js +156 -0
  37. package/dist/commands/env/list.js +50 -0
  38. package/dist/commands/env/remove.js +59 -0
  39. package/dist/commands/env/shared.js +158 -0
  40. package/dist/commands/env/update.js +67 -0
  41. package/dist/commands/env/use.js +28 -0
  42. package/dist/commands/examples/prompts-stages.js +150 -0
  43. package/dist/commands/examples/prompts-test.js +181 -0
  44. package/dist/commands/init.js +1027 -0
  45. package/dist/commands/install.js +2206 -0
  46. package/dist/commands/license/activate.js +360 -0
  47. package/dist/commands/license/env.js +94 -0
  48. package/dist/commands/license/generate-id.js +108 -0
  49. package/dist/commands/license/id.js +56 -0
  50. package/dist/commands/license/index.js +20 -0
  51. package/dist/commands/license/plugins/clean.js +101 -0
  52. package/dist/commands/license/plugins/index.js +20 -0
  53. package/dist/commands/license/plugins/list.js +50 -0
  54. package/dist/commands/license/plugins/shared.js +325 -0
  55. package/dist/commands/license/plugins/sync.js +269 -0
  56. package/dist/commands/license/shared.js +414 -0
  57. package/dist/commands/license/status.js +50 -0
  58. package/dist/commands/logs.js +12 -0
  59. package/dist/commands/plugin/disable.js +66 -0
  60. package/dist/commands/plugin/enable.js +66 -0
  61. package/dist/commands/plugin/list.js +62 -0
  62. package/dist/commands/pm/disable.js +12 -0
  63. package/dist/commands/pm/enable.js +12 -0
  64. package/dist/commands/pm/list.js +12 -0
  65. package/dist/commands/restart.js +12 -0
  66. package/dist/commands/scaffold/migration.js +38 -0
  67. package/dist/commands/scaffold/plugin.js +37 -0
  68. package/dist/commands/self/check.js +71 -0
  69. package/dist/commands/self/index.js +20 -0
  70. package/dist/commands/self/update.js +86 -0
  71. package/dist/commands/skills/check.js +69 -0
  72. package/dist/commands/skills/index.js +20 -0
  73. package/dist/commands/skills/install.js +71 -0
  74. package/dist/commands/skills/remove.js +71 -0
  75. package/dist/commands/skills/update.js +78 -0
  76. package/dist/commands/source/build.js +58 -0
  77. package/dist/commands/source/dev.js +158 -0
  78. package/dist/commands/source/download.js +866 -0
  79. package/dist/commands/source/test.js +467 -0
  80. package/dist/commands/start.js +12 -0
  81. package/dist/commands/stop.js +12 -0
  82. package/dist/commands/test.js +12 -0
  83. package/dist/commands/upgrade.js +12 -0
  84. package/dist/generated/command-registry.js +133 -0
  85. package/dist/help/runtime-help.js +23 -0
  86. package/dist/lib/api-client.js +329 -0
  87. package/dist/lib/app-health.js +126 -0
  88. package/dist/lib/app-managed-resources.js +268 -0
  89. package/dist/lib/app-runtime.js +171 -0
  90. package/dist/lib/auth-store.js +328 -0
  91. package/dist/lib/bootstrap.js +384 -0
  92. package/dist/lib/build-config.js +18 -0
  93. package/dist/lib/builtin-db.js +86 -0
  94. package/dist/lib/cli-config.js +176 -0
  95. package/dist/lib/cli-home.js +47 -0
  96. package/dist/lib/cli-locale.js +129 -0
  97. package/dist/lib/command-discovery.js +39 -0
  98. package/dist/lib/db-connection-check.js +178 -0
  99. package/dist/lib/env-auth.js +872 -0
  100. package/dist/lib/env-config.js +87 -0
  101. package/dist/lib/generated-command.js +171 -0
  102. package/dist/lib/http-request.js +49 -0
  103. package/dist/lib/naming.js +70 -0
  104. package/dist/lib/openapi.js +62 -0
  105. package/dist/lib/plugin-storage.js +127 -0
  106. package/dist/lib/post-processors.js +23 -0
  107. package/dist/lib/prompt-catalog.js +581 -0
  108. package/dist/lib/prompt-validators.js +185 -0
  109. package/dist/lib/prompt-web-ui.js +2103 -0
  110. package/dist/lib/resource-command.js +343 -0
  111. package/dist/lib/resource-request.js +104 -0
  112. package/dist/lib/run-npm.js +250 -0
  113. package/dist/lib/runtime-env-vars.js +32 -0
  114. package/dist/lib/runtime-generator.js +498 -0
  115. package/dist/lib/runtime-store.js +56 -0
  116. package/dist/lib/self-manager.js +301 -0
  117. package/dist/lib/skills-manager.js +296 -0
  118. package/dist/lib/startup-update.js +281 -0
  119. package/dist/lib/ui.js +178 -0
  120. package/dist/locale/en-US.json +339 -0
  121. package/dist/locale/zh-CN.json +339 -0
  122. package/dist/post-processors/data-modeling.js +66 -0
  123. package/dist/post-processors/data-source-manager.js +114 -0
  124. package/dist/post-processors/index.js +19 -0
  125. package/nocobase-ctl.config.json +369 -0
  126. package/package.json +95 -26
  127. package/LICENSE +0 -661
  128. package/bin/index.js +0 -39
  129. package/nocobase.conf.tpl +0 -95
  130. package/src/commands/benchmark.js +0 -73
  131. package/src/commands/build.js +0 -49
  132. package/src/commands/clean.js +0 -30
  133. package/src/commands/client.js +0 -166
  134. package/src/commands/create-nginx-conf.js +0 -37
  135. package/src/commands/create-plugin.js +0 -33
  136. package/src/commands/dev.js +0 -200
  137. package/src/commands/doc.js +0 -76
  138. package/src/commands/e2e.js +0 -265
  139. package/src/commands/global.js +0 -43
  140. package/src/commands/index.js +0 -45
  141. package/src/commands/instance-id.js +0 -47
  142. package/src/commands/locale/cronstrue.js +0 -122
  143. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  144. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  145. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  146. package/src/commands/locale.js +0 -81
  147. package/src/commands/p-test.js +0 -88
  148. package/src/commands/perf.js +0 -63
  149. package/src/commands/pkg.js +0 -321
  150. package/src/commands/pm2.js +0 -37
  151. package/src/commands/postinstall.js +0 -88
  152. package/src/commands/start.js +0 -148
  153. package/src/commands/tar.js +0 -36
  154. package/src/commands/test-coverage.js +0 -55
  155. package/src/commands/test.js +0 -107
  156. package/src/commands/umi.js +0 -33
  157. package/src/commands/update-deps.js +0 -72
  158. package/src/commands/upgrade.js +0 -47
  159. package/src/commands/view-license-key.js +0 -44
  160. package/src/license.js +0 -76
  161. package/src/logger.js +0 -75
  162. package/src/plugin-generator.js +0 -80
  163. package/src/util.js +0 -517
  164. package/templates/bundle-status.html +0 -338
  165. package/templates/create-app-package.json +0 -39
  166. package/templates/plugin/.npmignore.tpl +0 -2
  167. package/templates/plugin/README.md.tpl +0 -1
  168. package/templates/plugin/client.d.ts +0 -2
  169. package/templates/plugin/client.js +0 -1
  170. package/templates/plugin/package.json.tpl +0 -11
  171. package/templates/plugin/server.d.ts +0 -2
  172. package/templates/plugin/server.js +0 -1
  173. package/templates/plugin/src/client/client.d.ts +0 -249
  174. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  175. package/templates/plugin/src/client/locale.ts +0 -21
  176. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  177. package/templates/plugin/src/index.ts +0 -2
  178. package/templates/plugin/src/locale/en-US.json +0 -1
  179. package/templates/plugin/src/locale/zh-CN.json +0 -1
  180. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  181. package/templates/plugin/src/server/index.ts.tpl +0 -1
  182. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,87 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ const STRING_ENV_CONFIG_KEYS = [
10
+ 'source',
11
+ 'downloadVersion',
12
+ 'dockerRegistry',
13
+ 'dockerPlatform',
14
+ 'gitUrl',
15
+ 'npmRegistry',
16
+ 'appRootPath',
17
+ 'storagePath',
18
+ 'appPort',
19
+ 'appKey',
20
+ 'timezone',
21
+ 'dbDialect',
22
+ 'builtinDbImage',
23
+ 'dbHost',
24
+ 'dbPort',
25
+ 'dbDatabase',
26
+ 'dbUser',
27
+ 'dbPassword',
28
+ 'rootUsername',
29
+ 'rootEmail',
30
+ 'rootPassword',
31
+ 'rootNickname',
32
+ ];
33
+ const BOOLEAN_ENV_CONFIG_KEYS = [
34
+ 'builtinDb',
35
+ 'devDependencies',
36
+ 'build',
37
+ 'buildDts',
38
+ ];
39
+ function trimConfigValue(value) {
40
+ const text = String(value ?? '').trim();
41
+ return text || undefined;
42
+ }
43
+ function resolveEnvKind(input) {
44
+ const source = trimConfigValue(input.source);
45
+ const appRootPath = trimConfigValue(input.appRootPath);
46
+ if (source === 'docker') {
47
+ return 'docker';
48
+ }
49
+ if (source === 'npm' || source === 'git' || source === 'local' || appRootPath) {
50
+ return 'local';
51
+ }
52
+ return 'http';
53
+ }
54
+ export function buildStoredEnvConfig(input) {
55
+ const envConfig = {
56
+ kind: resolveEnvKind(input),
57
+ apiBaseUrl: trimConfigValue(input.apiBaseUrl) ?? '',
58
+ };
59
+ for (const key of STRING_ENV_CONFIG_KEYS) {
60
+ const value = trimConfigValue(input[key]);
61
+ if (value) {
62
+ envConfig[key] = value;
63
+ }
64
+ }
65
+ for (const key of BOOLEAN_ENV_CONFIG_KEYS) {
66
+ const value = input[key];
67
+ if (typeof value === 'boolean') {
68
+ envConfig[key] = value;
69
+ }
70
+ }
71
+ if (input.builtinDb === false) {
72
+ envConfig.builtinDbImage = undefined;
73
+ }
74
+ if (input.builtinDb === true) {
75
+ delete envConfig.dbHost;
76
+ const source = trimConfigValue(input.source);
77
+ if (source === 'docker') {
78
+ delete envConfig.dbPort;
79
+ }
80
+ }
81
+ const authType = trimConfigValue(input.authType);
82
+ const accessToken = trimConfigValue(input.accessToken);
83
+ if (authType === 'token' && accessToken) {
84
+ envConfig.accessToken = accessToken;
85
+ }
86
+ return envConfig;
87
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ /**
10
+ * This file is part of the NocoBase (R) project.
11
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
12
+ * Authors: NocoBase Team.
13
+ *
14
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
15
+ * For more information, please refer to: https://www.nocobase.com/agreement.
16
+ */
17
+ import { Command, Flags } from '@oclif/core';
18
+ import { executeApiRequest } from './api-client.js';
19
+ import { applyPostProcessor } from './post-processors.js';
20
+ import { registerPostProcessors } from '../post-processors/index.js';
21
+ function buildParameterFlag(parameter, options) {
22
+ const hints = [parameter.in];
23
+ if (parameter.isFile) {
24
+ hints.push('file path');
25
+ }
26
+ else if (parameter.type === 'object' || parameter.type === 'array' || parameter.jsonEncoded) {
27
+ hints.push('JSON');
28
+ }
29
+ else if (parameter.isArray) {
30
+ hints.push('repeatable');
31
+ }
32
+ else if (parameter.type) {
33
+ hints.push(parameter.type);
34
+ }
35
+ const description = [
36
+ `${parameter.description ?? ''}${parameter.description ? ' ' : ''}[${hints.join(', ')}]`.trim(),
37
+ parameter.jsonShape ? `Shape: ${parameter.jsonShape}` : undefined,
38
+ ]
39
+ .filter(Boolean)
40
+ .join('\n');
41
+ const required = options?.required ?? parameter.required;
42
+ const helpGroup = parameter.in === 'body'
43
+ ? 'Body Field'
44
+ : parameter.in === 'path'
45
+ ? 'Path Parameter'
46
+ : parameter.in === 'query'
47
+ ? 'Query Parameter'
48
+ : parameter.in === 'header'
49
+ ? 'Header Parameter'
50
+ : parameter.in === 'cookie'
51
+ ? 'Cookie Parameter'
52
+ : undefined;
53
+ if (parameter.type === 'boolean') {
54
+ return Flags.boolean({
55
+ description,
56
+ allowNo: true,
57
+ ...(helpGroup ? { helpGroup } : {}),
58
+ ...(required ? { required: true } : {}),
59
+ });
60
+ }
61
+ if (parameter.isArray && !parameter.jsonEncoded) {
62
+ return Flags.string({
63
+ description,
64
+ multiple: true,
65
+ ...(helpGroup ? { helpGroup } : {}),
66
+ ...(required ? { required: true } : {}),
67
+ });
68
+ }
69
+ return Flags.string({
70
+ description,
71
+ ...(helpGroup ? { helpGroup } : {}),
72
+ ...(required ? { required: true } : {}),
73
+ });
74
+ }
75
+ export function createGeneratedFlags(operation) {
76
+ const flags = {};
77
+ for (const parameter of operation.parameters) {
78
+ flags[parameter.flagName] = buildParameterFlag(parameter, {
79
+ // Body flags are an alternative authoring path to --body/--body-file.
80
+ // Enforce required body semantics later in parseBody(), after we know
81
+ // which input mode the user chose.
82
+ required: parameter.in === 'body' && !parameter.isFile ? false : parameter.required,
83
+ });
84
+ }
85
+ if (operation.hasBody && operation.requestContentType !== 'multipart/form-data') {
86
+ flags.body = Flags.string({
87
+ description: 'Full JSON request body string. Do not combine with body field flags.',
88
+ helpGroup: 'Raw JSON Body',
89
+ exclusive: ['body-file'],
90
+ });
91
+ flags['body-file'] = Flags.string({
92
+ description: 'Path to a JSON file containing the full request body. Do not combine with body field flags.',
93
+ helpGroup: 'Raw JSON Body',
94
+ exclusive: ['body'],
95
+ });
96
+ }
97
+ if (operation.responseType === 'binary') {
98
+ flags.output = Flags.string({
99
+ description: 'Path where the downloaded response should be written.',
100
+ helpGroup: 'Output',
101
+ required: true,
102
+ });
103
+ }
104
+ flags['api-base-url'] = Flags.string({
105
+ description: 'NocoBase API base URL, for example http://localhost:13000/api',
106
+ helpGroup: 'Global',
107
+ });
108
+ flags.verbose = Flags.boolean({
109
+ description: 'Show detailed progress output',
110
+ default: false,
111
+ helpGroup: 'Global',
112
+ });
113
+ flags.env = Flags.string({
114
+ char: 'e',
115
+ description: 'Environment name',
116
+ helpGroup: 'Global',
117
+ });
118
+ flags.role = Flags.string({
119
+ description: 'Role override, sent as X-Role',
120
+ helpGroup: 'Global',
121
+ });
122
+ flags.token = Flags.string({
123
+ char: 't',
124
+ description: 'API key override',
125
+ helpGroup: 'Global',
126
+ });
127
+ flags['json-output'] = Flags.boolean({
128
+ char: 'j',
129
+ description: 'Print raw JSON response',
130
+ default: true,
131
+ allowNo: true,
132
+ helpGroup: 'Global',
133
+ });
134
+ return flags;
135
+ }
136
+ export class GeneratedApiCommand extends Command {
137
+ static operation;
138
+ async run() {
139
+ registerPostProcessors();
140
+ const ctor = this.constructor;
141
+ const { flags } = await this.parse(ctor);
142
+ const response = await executeApiRequest({
143
+ envName: flags.env,
144
+ baseUrl: flags['api-base-url'],
145
+ role: flags.role,
146
+ token: flags.token,
147
+ flags,
148
+ operation: {
149
+ method: ctor.operation.method,
150
+ pathTemplate: ctor.operation.pathTemplate,
151
+ parameters: ctor.operation.parameters,
152
+ hasBody: ctor.operation.hasBody,
153
+ bodyRequired: ctor.operation.bodyRequired,
154
+ requestContentType: ctor.operation.requestContentType,
155
+ responseType: ctor.operation.responseType,
156
+ },
157
+ });
158
+ if (!response.ok) {
159
+ this.error(`Request failed with status ${response.status}\n${JSON.stringify(response.data, null, 2)}`);
160
+ }
161
+ const processedData = await applyPostProcessor(response.data, {
162
+ flags,
163
+ operation: ctor.operation,
164
+ });
165
+ if (flags['json-output']) {
166
+ this.log(JSON.stringify(processedData, null, 2));
167
+ return;
168
+ }
169
+ this.log(`HTTP ${response.status}`);
170
+ }
171
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ function normalizeLocationUrl(location, currentUrl) {
10
+ try {
11
+ return new URL(location, currentUrl).toString();
12
+ }
13
+ catch (_error) {
14
+ return undefined;
15
+ }
16
+ }
17
+ function shouldPreserveAuthorizationRedirect(fromUrl, toUrl) {
18
+ try {
19
+ const from = new URL(fromUrl);
20
+ const to = new URL(toUrl);
21
+ return (from.hostname === to.hostname &&
22
+ from.port === to.port &&
23
+ from.pathname === to.pathname &&
24
+ from.search === to.search &&
25
+ from.protocol === 'http:' &&
26
+ to.protocol === 'https:');
27
+ }
28
+ catch (_error) {
29
+ return false;
30
+ }
31
+ }
32
+ export async function fetchWithPreservedAuthRedirect(url, init = {}) {
33
+ const response = await fetch(url, {
34
+ ...init,
35
+ redirect: 'manual',
36
+ });
37
+ const location = response.headers.get('location');
38
+ if (!location || ![301, 302, 307, 308].includes(response.status)) {
39
+ return response;
40
+ }
41
+ const nextUrl = normalizeLocationUrl(location, url);
42
+ if (!nextUrl || !shouldPreserveAuthorizationRedirect(url, nextUrl)) {
43
+ return response;
44
+ }
45
+ return fetch(nextUrl, {
46
+ ...init,
47
+ redirect: 'manual',
48
+ });
49
+ }
@@ -0,0 +1,70 @@
1
+ import path from 'node:path';
2
+ export function toKebabCase(value) {
3
+ return value
4
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
5
+ .replace(/[^a-zA-Z0-9]+/g, '-')
6
+ .replace(/-+/g, '-')
7
+ .replace(/^-|-$/g, '')
8
+ .toLowerCase();
9
+ }
10
+ export function splitPathAction(pathTemplate) {
11
+ const normalizedPath = pathTemplate.replace(/^\/+/, '');
12
+ const separatorIndex = normalizedPath.lastIndexOf(':');
13
+ if (separatorIndex === -1) {
14
+ return {
15
+ resourcePath: normalizedPath,
16
+ action: 'call',
17
+ };
18
+ }
19
+ return {
20
+ resourcePath: normalizedPath.slice(0, separatorIndex),
21
+ action: normalizedPath.slice(separatorIndex + 1),
22
+ };
23
+ }
24
+ export function toLogicalResourceName(pathTemplate) {
25
+ const { resourcePath } = splitPathAction(pathTemplate);
26
+ return resourcePath
27
+ .split('/')
28
+ .filter(Boolean)
29
+ .filter((segment) => !segment.startsWith('{'))
30
+ .map((segment) => toKebabCase(segment))
31
+ .join('.');
32
+ }
33
+ export function toLogicalActionName(pathTemplate) {
34
+ return toKebabCase(splitPathAction(pathTemplate).action);
35
+ }
36
+ export function toResourceSegments(pathTemplate, options) {
37
+ const { resourcePath, action } = splitPathAction(pathTemplate);
38
+ const pathSegments = resourcePath
39
+ .split('/')
40
+ .filter(Boolean)
41
+ .flatMap((segment) => {
42
+ if (!segment.startsWith('{')) {
43
+ return [toKebabCase(segment)];
44
+ }
45
+ if (!options?.includeParams) {
46
+ return [];
47
+ }
48
+ return [`by-${toKebabCase(segment.slice(1, -1))}`];
49
+ });
50
+ return [...pathSegments, toKebabCase(action)].filter(Boolean);
51
+ }
52
+ export function toCommandSegments(moduleName, pathTemplate, options) {
53
+ const resourceSegments = toResourceSegments(pathTemplate, options);
54
+ const segments = [options?.omitModule ? '' : toKebabCase(moduleName), ...resourceSegments].filter(Boolean);
55
+ return segments.length ? segments : [toKebabCase(moduleName), 'call'];
56
+ }
57
+ export function toClassName(segments) {
58
+ return segments
59
+ .map((segment) => segment.replace(/(^\w|-\w)/g, (token) => token.replace('-', '').toUpperCase()))
60
+ .join('');
61
+ }
62
+ export function toOutputFile(outputRoot, segments) {
63
+ const folder = path.join(outputRoot, ...segments.slice(0, -1));
64
+ const filePath = path.join(folder, `${segments.at(-1)}.ts`);
65
+ return filePath;
66
+ }
67
+ export function toImportPath(fromFile, targetFile) {
68
+ const relative = path.relative(path.dirname(fromFile), targetFile).replace(/\\/g, '/');
69
+ return relative.startsWith('.') ? relative : `./${relative}`;
70
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
10
+ function resolveLocalRef(document, ref) {
11
+ if (!ref.startsWith('#/')) {
12
+ return undefined;
13
+ }
14
+ return ref
15
+ .slice(2)
16
+ .split('/')
17
+ .reduce((current, segment) => current?.[segment], document);
18
+ }
19
+ function dereferenceNode(node, document, seen = new Set()) {
20
+ if (Array.isArray(node)) {
21
+ return node.map((item) => dereferenceNode(item, document, seen));
22
+ }
23
+ if (!node || typeof node !== 'object') {
24
+ return node;
25
+ }
26
+ const ref = node.$ref;
27
+ if (typeof ref === 'string') {
28
+ if (seen.has(ref)) {
29
+ return {};
30
+ }
31
+ const resolved = resolveLocalRef(document, ref);
32
+ if (!resolved) {
33
+ return node;
34
+ }
35
+ return dereferenceNode(resolved, document, new Set([...seen, ref]));
36
+ }
37
+ return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, dereferenceNode(value, document, seen)]));
38
+ }
39
+ export function collectOperations(document) {
40
+ const operations = [];
41
+ for (const [pathTemplate, pathItem] of Object.entries(document.paths ?? {})) {
42
+ for (const method of HTTP_METHODS) {
43
+ const operation = pathItem?.[method];
44
+ if (!operation || '$ref' in operation) {
45
+ continue;
46
+ }
47
+ const parameters = [...(pathItem.parameters ?? []), ...(operation.parameters ?? [])]
48
+ .map((parameter) => dereferenceNode(parameter, document))
49
+ .filter((parameter) => Boolean(parameter && !('$ref' in parameter)));
50
+ operations.push({
51
+ method,
52
+ pathTemplate,
53
+ operation: {
54
+ ...operation,
55
+ parameters,
56
+ requestBody: operation.requestBody ? dereferenceNode(operation.requestBody, document) : undefined,
57
+ },
58
+ });
59
+ }
60
+ }
61
+ return operations;
62
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import path from 'node:path';
10
+ import { access, lstat, mkdir, readdir, readlink, realpath, rm, stat, symlink } from 'node:fs/promises';
11
+ async function pathExists(target) {
12
+ try {
13
+ await access(target);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ export function resolvePluginStoragePath(storagePath) {
21
+ const root = String(storagePath ?? process.env.STORAGE_PATH ?? '').trim();
22
+ if (root) {
23
+ return path.join(path.isAbsolute(root) ? root : path.resolve(process.cwd(), root), 'plugins');
24
+ }
25
+ const configured = String(process.env.PLUGIN_STORAGE_PATH ?? '').trim();
26
+ if (configured) {
27
+ return path.isAbsolute(configured) ? configured : path.resolve(process.cwd(), configured);
28
+ }
29
+ return path.resolve(process.cwd(), 'storage', 'plugins');
30
+ }
31
+ async function getStoragePluginNames(target) {
32
+ const plugins = [];
33
+ const items = await readdir(target);
34
+ for (const item of items) {
35
+ const itemPath = path.resolve(target, item);
36
+ if (item.startsWith('@')) {
37
+ const statResult = await stat(itemPath);
38
+ if (!statResult.isDirectory()) {
39
+ continue;
40
+ }
41
+ const children = await getStoragePluginNames(itemPath);
42
+ plugins.push(...children.map((child) => `${item}/${child}`));
43
+ continue;
44
+ }
45
+ if (await pathExists(path.resolve(itemPath, 'package.json'))) {
46
+ plugins.push(item);
47
+ }
48
+ }
49
+ return plugins;
50
+ }
51
+ async function ensureOrgDirectory(nodeModulesPath, pluginName) {
52
+ if (!pluginName.startsWith('@')) {
53
+ return;
54
+ }
55
+ const [orgName] = pluginName.split('/');
56
+ await mkdir(path.resolve(nodeModulesPath, orgName), { recursive: true });
57
+ }
58
+ async function isSymlinkValid(linkPath, targetPath) {
59
+ try {
60
+ if (await pathExists(linkPath)) {
61
+ const realPath = await realpath(linkPath);
62
+ return realPath === targetPath;
63
+ }
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ return false;
69
+ }
70
+ async function createStoragePluginSymlink(storagePluginsPath, nodeModulesPath, pluginName) {
71
+ const targetPath = path.resolve(storagePluginsPath, pluginName);
72
+ if (!(await pathExists(targetPath))) {
73
+ return;
74
+ }
75
+ await ensureOrgDirectory(nodeModulesPath, pluginName);
76
+ const linkPath = path.resolve(nodeModulesPath, pluginName);
77
+ if (await isSymlinkValid(linkPath, targetPath)) {
78
+ return;
79
+ }
80
+ await rm(linkPath, { recursive: true, force: true });
81
+ await symlink(targetPath, linkPath, 'dir');
82
+ }
83
+ export async function createStoragePluginsSymlink(storagePath, nodeModulesPath = String(process.env.NODE_MODULES_PATH ?? '').trim()) {
84
+ if (!nodeModulesPath) {
85
+ return;
86
+ }
87
+ const storagePluginsPath = resolvePluginStoragePath(storagePath);
88
+ if (!(await pathExists(storagePluginsPath))) {
89
+ return;
90
+ }
91
+ const pluginNames = await getStoragePluginNames(storagePluginsPath);
92
+ await Promise.all(pluginNames.map(async (pluginName) => await createStoragePluginSymlink(storagePluginsPath, nodeModulesPath, pluginName)));
93
+ }
94
+ export async function removeStoragePluginSymlink(pluginName, storagePath, nodeModulesPath = String(process.env.NODE_MODULES_PATH ?? '').trim()) {
95
+ if (!nodeModulesPath) {
96
+ return false;
97
+ }
98
+ const storagePluginsPath = resolvePluginStoragePath(storagePath);
99
+ const targetPath = path.resolve(storagePluginsPath, pluginName);
100
+ const linkPath = path.resolve(nodeModulesPath, pluginName);
101
+ if (!(await pathExists(linkPath))) {
102
+ return false;
103
+ }
104
+ let statResult;
105
+ try {
106
+ statResult = await lstat(linkPath);
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ if (!statResult.isSymbolicLink()) {
112
+ return false;
113
+ }
114
+ let resolvedLinkTarget = '';
115
+ try {
116
+ const linkTarget = await readlink(linkPath);
117
+ resolvedLinkTarget = path.resolve(path.dirname(linkPath), linkTarget);
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ if (resolvedLinkTarget !== targetPath) {
123
+ return false;
124
+ }
125
+ await rm(linkPath, { recursive: true, force: true });
126
+ return true;
127
+ }
@@ -0,0 +1,23 @@
1
+ function buildKey(resource, action) {
2
+ return `${resource}:${action}`;
3
+ }
4
+ class PostProcessorRegistry {
5
+ processors = new Map();
6
+ register(resource, action, processor) {
7
+ this.processors.set(buildKey(resource, action), processor);
8
+ }
9
+ resolve(resource, action) {
10
+ if (!resource || !action) {
11
+ return undefined;
12
+ }
13
+ return this.processors.get(buildKey(resource, action));
14
+ }
15
+ }
16
+ export const postProcessorRegistry = new PostProcessorRegistry();
17
+ export async function applyPostProcessor(result, context) {
18
+ const processor = postProcessorRegistry.resolve(context.operation.logicalResourceName, context.operation.actionName);
19
+ if (!processor) {
20
+ return result;
21
+ }
22
+ return processor(result, context);
23
+ }