@ldsg/cli 0.3.31 → 0.3.33
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/.rush/temp/package-deps_build.json +27 -27
- package/.rush/temp/shrinkwrap-deps.json +58 -3
- package/.tsbuildinfo +1 -1
- package/CHANGELOG.json +52 -0
- package/CHANGELOG.md +15 -1
- package/README.md +5 -2
- package/dist/__tests__/commands.test.js +104 -104
- package/dist/__tests__/formatter.test.js +66 -63
- package/dist/__tests__/formatter.test.js.map +1 -1
- package/dist/__tests__/json-file-mutator.test.js +48 -45
- package/dist/__tests__/json-file-mutator.test.js.map +1 -1
- package/dist/__tests__/source-resolver.test.js +39 -39
- package/dist/cli.js +89 -89
- package/dist/cli.js.map +1 -1
- package/dist/commands/resource/convert.d.ts +1 -1
- package/dist/commands/resource/convert.js +9 -7
- package/dist/commands/resource/convert.js.map +1 -1
- package/dist/commands/resource/create.d.ts +2 -2
- package/dist/commands/resource/create.js +5 -5
- package/dist/commands/resource/delete.d.ts +2 -2
- package/dist/commands/resource/delete.js.map +1 -1
- package/dist/commands/resource/diff.d.ts +2 -2
- package/dist/commands/resource/diff.js +8 -8
- package/dist/commands/resource/get.d.ts +1 -1
- package/dist/commands/resource/list.d.ts +2 -2
- package/dist/commands/resource/list.js +11 -11
- package/dist/commands/resource/list.js.map +1 -1
- package/dist/commands/resource/tree.d.ts +1 -1
- package/dist/commands/resource/update.d.ts +2 -2
- package/dist/commands/resource/update.js +1 -1
- package/dist/constants.js +2 -2
- package/dist/types/get-extend-app-data-params.d.ts +2 -2
- package/dist/types/resource-record.d.ts +2 -2
- package/dist/utils/formatter.d.ts +1 -1
- package/dist/utils/formatter.js +25 -25
- package/dist/utils/get-extend-app-data-params-using-npm-ldsg-module.d.ts +1 -1
- package/dist/utils/get-extend-app-data-params-using-npm-ldsg-module.js +12 -12
- package/dist/utils/get-extend-app-data-params-using-npm-ldsg-module.js.map +1 -1
- package/dist/utils/get-extend-app-data-params-using-workspace-ldsg-module/index.d.ts +1 -1
- package/dist/utils/get-extend-app-data-params-using-workspace-ldsg-module/index.js +20 -20
- package/dist/utils/get-extend-app-data-params-using-workspace-ldsg-module/index.js.map +1 -1
- package/dist/utils/json-file-mutator.d.ts +2 -2
- package/dist/utils/json-file-mutator.js +4 -4
- package/dist/utils/json-file-mutator.js.map +1 -1
- package/dist/utils/source-resolver.d.ts +1 -1
- package/dist/utils/source-resolver.js +15 -15
- package/dist/utils/source-resolver.js.map +1 -1
- package/jest.config.js +8 -8
- package/package.json +13 -10
- package/src/__tests__/commands.test.ts +116 -116
- package/src/__tests__/formatter.test.ts +68 -65
- package/src/__tests__/json-file-mutator.test.ts +57 -52
- package/src/__tests__/source-resolver.test.ts +43 -43
- package/src/cli.ts +132 -114
- package/src/commands/resource/convert.ts +13 -11
- package/src/commands/resource/create.ts +8 -8
- package/src/commands/resource/delete.ts +6 -4
- package/src/commands/resource/diff.ts +10 -10
- package/src/commands/resource/get.ts +2 -2
- package/src/commands/resource/list.ts +20 -18
- package/src/commands/resource/tree.ts +2 -2
- package/src/commands/resource/update.ts +4 -4
- package/src/constants.ts +2 -2
- package/src/types/get-extend-app-data-params.ts +5 -3
- package/src/types/resource-record.ts +2 -2
- package/src/utils/formatter.ts +27 -27
- package/src/utils/get-extend-app-data-params-using-npm-ldsg-module.ts +20 -16
- package/src/utils/get-extend-app-data-params-using-workspace-ldsg-module/index.ts +28 -26
- package/src/utils/json-file-mutator.ts +18 -12
- package/src/utils/source-resolver.ts +26 -21
package/src/cli.ts
CHANGED
|
@@ -1,51 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import { convertApaasResourceRecordsToResourceRecords } from
|
|
5
|
-
import { AppData } from
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { convertApaasResourceRecordsToResourceRecords } from "@ldsg/apaas-to-runtime";
|
|
5
|
+
import { AppData } from "@ldsg/types";
|
|
6
6
|
import {
|
|
7
7
|
extendAppData,
|
|
8
8
|
ExtendAppDataParams,
|
|
9
9
|
loadAppResourceRecords,
|
|
10
10
|
prepare,
|
|
11
|
-
ZipAppDataLayout
|
|
12
|
-
} from
|
|
13
|
-
import { Command } from
|
|
14
|
-
import { DEFAULT_OUTPUT_PATH, DEFAULT_SOURCE_FILE, DEFAULT_SOURCE_PATH } from
|
|
15
|
-
import { getExtendAppDataParamsUsingNpmLdsgModule } from
|
|
16
|
-
import { getExtendAppDataParamsUsingWorkspaceLdsgModule } from
|
|
17
|
-
import { resolveSource, isStdinSource, getSourceDescription } from
|
|
11
|
+
ZipAppDataLayout,
|
|
12
|
+
} from "@ldsg/utils";
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
import { DEFAULT_OUTPUT_PATH, DEFAULT_SOURCE_FILE, DEFAULT_SOURCE_PATH } from "./constants";
|
|
15
|
+
import { getExtendAppDataParamsUsingNpmLdsgModule } from "./utils/get-extend-app-data-params-using-npm-ldsg-module";
|
|
16
|
+
import { getExtendAppDataParamsUsingWorkspaceLdsgModule } from "./utils/get-extend-app-data-params-using-workspace-ldsg-module";
|
|
17
|
+
import { resolveSource, isStdinSource, getSourceDescription } from "./utils/source-resolver";
|
|
18
18
|
import {
|
|
19
19
|
formatSuccess,
|
|
20
20
|
formatError,
|
|
21
21
|
formatJson,
|
|
22
22
|
formatPrettyList,
|
|
23
23
|
formatPrettyTree,
|
|
24
|
-
formatPrettyDiff
|
|
25
|
-
} from
|
|
26
|
-
import { createResource } from
|
|
27
|
-
import { updateResource } from
|
|
28
|
-
import { deleteResource } from
|
|
29
|
-
import { listResources, getTree } from
|
|
30
|
-
import { getResource } from
|
|
31
|
-
import { convertResource } from
|
|
32
|
-
import { diffResource } from
|
|
33
|
-
|
|
34
|
-
const { version } = JSON.parse(readFileSync(join(__dirname,
|
|
24
|
+
formatPrettyDiff,
|
|
25
|
+
} from "./utils/formatter";
|
|
26
|
+
import { createResource } from "./commands/resource/create";
|
|
27
|
+
import { updateResource } from "./commands/resource/update";
|
|
28
|
+
import { deleteResource } from "./commands/resource/delete";
|
|
29
|
+
import { listResources, getTree } from "./commands/resource/list";
|
|
30
|
+
import { getResource } from "./commands/resource/get";
|
|
31
|
+
import { convertResource } from "./commands/resource/convert";
|
|
32
|
+
import { diffResource } from "./commands/resource/diff";
|
|
33
|
+
|
|
34
|
+
const { version } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
35
35
|
|
|
36
36
|
const program = new Command();
|
|
37
37
|
|
|
38
|
-
program.name(
|
|
38
|
+
program.name("ldsg").description("LDSG CLI tools").version(version);
|
|
39
39
|
|
|
40
40
|
program
|
|
41
|
-
.command(
|
|
42
|
-
.aliases([
|
|
43
|
-
.description(
|
|
44
|
-
.option(
|
|
45
|
-
.option(
|
|
46
|
-
.option(
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
.command("generate")
|
|
42
|
+
.aliases(["g", "gen"])
|
|
43
|
+
.description("Generate standalone application from resource records")
|
|
44
|
+
.option("--app-data-uri <uri>", "runtime app data URI")
|
|
45
|
+
.option("--apaas-app-data-uri <uri>", "aPaaS app data URI (GraphQL)")
|
|
46
|
+
.option(
|
|
47
|
+
"--apaas-app-data-expression <expression>",
|
|
48
|
+
"JSONata expression for aPaaS data extraction",
|
|
49
|
+
)
|
|
50
|
+
.option("--using-workspace-ldsg-module", "use workspace ldsg module mode")
|
|
51
|
+
.option(
|
|
52
|
+
"-o, --output <path>",
|
|
53
|
+
`output directory for prepared app code (default: ${DEFAULT_OUTPUT_PATH})`,
|
|
54
|
+
)
|
|
49
55
|
.action(async (options) => {
|
|
50
56
|
const outputPath = options.output || DEFAULT_OUTPUT_PATH;
|
|
51
57
|
|
|
@@ -54,14 +60,14 @@ program
|
|
|
54
60
|
const { resourceRecords, sourceType } = await loadAppResourceRecords({
|
|
55
61
|
appDataUri: options.appDataUri,
|
|
56
62
|
apaasAppDataUri: options.apaasAppDataUri,
|
|
57
|
-
apaasAppDataExpression: options.apaasAppDataExpression
|
|
63
|
+
apaasAppDataExpression: options.apaasAppDataExpression,
|
|
58
64
|
});
|
|
59
65
|
|
|
60
66
|
let appData: AppData;
|
|
61
67
|
|
|
62
|
-
if (sourceType ===
|
|
68
|
+
if (sourceType === "apaas") {
|
|
63
69
|
const { targetResourceRecords } = convertApaasResourceRecordsToResourceRecords({
|
|
64
|
-
resourceRecords
|
|
70
|
+
resourceRecords,
|
|
65
71
|
});
|
|
66
72
|
appData = { resourceRecords: targetResourceRecords };
|
|
67
73
|
} else {
|
|
@@ -81,20 +87,20 @@ program
|
|
|
81
87
|
await prepare({
|
|
82
88
|
...extendAppDataRes,
|
|
83
89
|
outputPath,
|
|
84
|
-
...(usingWorkspaceLdsgModule ? { layout: ZipAppDataLayout.FLAT } : {})
|
|
90
|
+
...(usingWorkspaceLdsgModule ? { layout: ZipAppDataLayout.FLAT } : {}),
|
|
85
91
|
});
|
|
86
92
|
});
|
|
87
93
|
|
|
88
|
-
const resource = program.command(
|
|
94
|
+
const resource = program.command("resource").aliases(["r"]).description("Manage resource records");
|
|
89
95
|
|
|
90
96
|
resource
|
|
91
|
-
.command(
|
|
92
|
-
.description(
|
|
93
|
-
.option(
|
|
94
|
-
.option(
|
|
95
|
-
.option(
|
|
96
|
-
.option(
|
|
97
|
-
.option(
|
|
97
|
+
.command("list")
|
|
98
|
+
.description("List resource records by filter")
|
|
99
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
100
|
+
.option("--kind <kind>", "filter by resource kind")
|
|
101
|
+
.option("--parent-id <id>", "filter by parent id")
|
|
102
|
+
.option("--root-id <id>", "filter by root resource id")
|
|
103
|
+
.option("--pretty", "human-readable output")
|
|
98
104
|
.action(async (options) => {
|
|
99
105
|
try {
|
|
100
106
|
const sourceDesc = getSourceDescription(options.source);
|
|
@@ -102,7 +108,7 @@ resource
|
|
|
102
108
|
const { total, resources } = await listResources(sourceData.resourceRecords, {
|
|
103
109
|
kind: options.kind,
|
|
104
110
|
parentId: options.parentId,
|
|
105
|
-
rootId: options.rootId
|
|
111
|
+
rootId: options.rootId,
|
|
106
112
|
});
|
|
107
113
|
|
|
108
114
|
if (options.pretty) {
|
|
@@ -112,37 +118,37 @@ resource
|
|
|
112
118
|
id: r.id,
|
|
113
119
|
kind: r.kind,
|
|
114
120
|
parentId: r.parentId,
|
|
115
|
-
title: (r.settings as Record<string, unknown>)?.title as string | undefined
|
|
116
|
-
}))
|
|
117
|
-
)
|
|
121
|
+
title: (r.settings as Record<string, unknown>)?.title as string | undefined,
|
|
122
|
+
})),
|
|
123
|
+
),
|
|
118
124
|
);
|
|
119
125
|
} else {
|
|
120
126
|
console.log(formatJson(formatSuccess({ source: sourceDesc, total, resources })));
|
|
121
127
|
}
|
|
122
128
|
} catch (error) {
|
|
123
|
-
console.error(formatJson(formatError(
|
|
129
|
+
console.error(formatJson(formatError("LIST_ERROR", (error as Error).message)));
|
|
124
130
|
process.exit(1);
|
|
125
131
|
}
|
|
126
132
|
});
|
|
127
133
|
|
|
128
134
|
resource
|
|
129
|
-
.command(
|
|
130
|
-
.description(
|
|
131
|
-
.option(
|
|
132
|
-
.option(
|
|
133
|
-
.option(
|
|
134
|
-
.option(
|
|
135
|
+
.command("get <id>")
|
|
136
|
+
.description("Get a resource record by id")
|
|
137
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
138
|
+
.option("--children", "include direct children")
|
|
139
|
+
.option("--tree", "include full subtree")
|
|
140
|
+
.option("--pretty", "human-readable output")
|
|
135
141
|
.action(async (id, options) => {
|
|
136
142
|
try {
|
|
137
143
|
const sourceDesc = getSourceDescription(options.source);
|
|
138
144
|
const sourceData = await resolveSource(options.source);
|
|
139
145
|
const record = await getResource(sourceData.resourceRecords, id, {
|
|
140
146
|
children: options.children,
|
|
141
|
-
tree: options.tree
|
|
147
|
+
tree: options.tree,
|
|
142
148
|
});
|
|
143
149
|
|
|
144
150
|
if (!record) {
|
|
145
|
-
console.error(formatJson(formatError(
|
|
151
|
+
console.error(formatJson(formatError("NOT_FOUND", `Resource with id "${id}" not found`)));
|
|
146
152
|
process.exit(1);
|
|
147
153
|
}
|
|
148
154
|
|
|
@@ -152,17 +158,17 @@ resource
|
|
|
152
158
|
console.log(formatJson(formatSuccess({ source: sourceDesc, record })));
|
|
153
159
|
}
|
|
154
160
|
} catch (error) {
|
|
155
|
-
console.error(formatJson(formatError(
|
|
161
|
+
console.error(formatJson(formatError("GET_ERROR", (error as Error).message)));
|
|
156
162
|
process.exit(1);
|
|
157
163
|
}
|
|
158
164
|
});
|
|
159
165
|
|
|
160
166
|
resource
|
|
161
|
-
.command(
|
|
162
|
-
.description(
|
|
163
|
-
.option(
|
|
164
|
-
.option(
|
|
165
|
-
.option(
|
|
167
|
+
.command("tree [root-id]")
|
|
168
|
+
.description("Display resource tree")
|
|
169
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
170
|
+
.option("--show-kind", "show resource kind prefix")
|
|
171
|
+
.option("--pretty", "human-readable output")
|
|
166
172
|
.action(async (rootId, options) => {
|
|
167
173
|
try {
|
|
168
174
|
const sourceDesc = getSourceDescription(options.source);
|
|
@@ -175,26 +181,28 @@ resource
|
|
|
175
181
|
console.log(formatJson(formatSuccess({ source: sourceDesc, ...result })));
|
|
176
182
|
}
|
|
177
183
|
} catch (error) {
|
|
178
|
-
console.error(formatJson(formatError(
|
|
184
|
+
console.error(formatJson(formatError("TREE_ERROR", (error as Error).message)));
|
|
179
185
|
process.exit(1);
|
|
180
186
|
}
|
|
181
187
|
});
|
|
182
188
|
|
|
183
189
|
resource
|
|
184
|
-
.command(
|
|
185
|
-
.description(
|
|
186
|
-
.option(
|
|
187
|
-
.option(
|
|
188
|
-
.option(
|
|
189
|
-
.option(
|
|
190
|
-
.option(
|
|
191
|
-
.option(
|
|
192
|
-
.option(
|
|
190
|
+
.command("create")
|
|
191
|
+
.description("Create a new resource record")
|
|
192
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
193
|
+
.option("--kind <kind>", "resource kind")
|
|
194
|
+
.option("--parent-id <id>", "parent resource id")
|
|
195
|
+
.option("--settings <json>", "resource settings as JSON string")
|
|
196
|
+
.option("--from-file <path>", "read full resource record from JSON file")
|
|
197
|
+
.option("--from-stdin", "read full resource record from stdin")
|
|
198
|
+
.option("--dry-run", "preview without writing")
|
|
193
199
|
.action(async (options) => {
|
|
194
200
|
try {
|
|
195
201
|
if (isStdinSource(options.source)) {
|
|
196
202
|
console.error(
|
|
197
|
-
formatJson(
|
|
203
|
+
formatJson(
|
|
204
|
+
formatError("STDIN_NOT_WRITABLE", "stdin source does not support write operations"),
|
|
205
|
+
),
|
|
198
206
|
);
|
|
199
207
|
process.exit(1);
|
|
200
208
|
}
|
|
@@ -204,15 +212,15 @@ resource
|
|
|
204
212
|
let fromFileRecord: Record<string, unknown> | undefined;
|
|
205
213
|
|
|
206
214
|
if (options.fromFile) {
|
|
207
|
-
const fs = await import(
|
|
215
|
+
const fs = await import("fs-extra");
|
|
208
216
|
fromFileRecord = await fs.readJson(options.fromFile);
|
|
209
217
|
} else if (options.fromStdin) {
|
|
210
218
|
const chunks: Buffer[] = [];
|
|
211
|
-
process.stdin.setEncoding(
|
|
219
|
+
process.stdin.setEncoding("utf-8");
|
|
212
220
|
for await (const chunk of process.stdin) {
|
|
213
221
|
chunks.push(Buffer.from(chunk));
|
|
214
222
|
}
|
|
215
|
-
const raw = Buffer.concat(chunks).toString(
|
|
223
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
216
224
|
fromFileRecord = JSON.parse(raw);
|
|
217
225
|
}
|
|
218
226
|
|
|
@@ -227,27 +235,29 @@ resource
|
|
|
227
235
|
parentId: options.parentId,
|
|
228
236
|
settings,
|
|
229
237
|
fromFileRecord: fromFileRecord as never,
|
|
230
|
-
dryRun: options.dryRun
|
|
238
|
+
dryRun: options.dryRun,
|
|
231
239
|
});
|
|
232
240
|
|
|
233
241
|
console.log(formatJson(formatSuccess({ record, dryRun: options.dryRun ?? false })));
|
|
234
242
|
} catch (error) {
|
|
235
|
-
console.error(formatJson(formatError(
|
|
243
|
+
console.error(formatJson(formatError("CREATE_ERROR", (error as Error).message)));
|
|
236
244
|
process.exit(1);
|
|
237
245
|
}
|
|
238
246
|
});
|
|
239
247
|
|
|
240
248
|
resource
|
|
241
|
-
.command(
|
|
242
|
-
.description(
|
|
243
|
-
.option(
|
|
244
|
-
.option(
|
|
245
|
-
.option(
|
|
249
|
+
.command("update <id>")
|
|
250
|
+
.description("Update a resource record")
|
|
251
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
252
|
+
.option("--settings <json>", "resource settings to merge (JSON string, required)")
|
|
253
|
+
.option("--dry-run", "preview without writing")
|
|
246
254
|
.action(async (id, options) => {
|
|
247
255
|
try {
|
|
248
256
|
if (isStdinSource(options.source)) {
|
|
249
257
|
console.error(
|
|
250
|
-
formatJson(
|
|
258
|
+
formatJson(
|
|
259
|
+
formatError("STDIN_NOT_WRITABLE", "stdin source does not support write operations"),
|
|
260
|
+
),
|
|
251
261
|
);
|
|
252
262
|
process.exit(1);
|
|
253
263
|
}
|
|
@@ -255,7 +265,9 @@ resource
|
|
|
255
265
|
const sourceData = await resolveSource(options.source);
|
|
256
266
|
|
|
257
267
|
if (!options.settings) {
|
|
258
|
-
console.error(
|
|
268
|
+
console.error(
|
|
269
|
+
formatJson(formatError("MISSING_SETTINGS", "Required option: --settings <json>")),
|
|
270
|
+
);
|
|
259
271
|
process.exit(1);
|
|
260
272
|
}
|
|
261
273
|
|
|
@@ -264,26 +276,28 @@ resource
|
|
|
264
276
|
sourceData,
|
|
265
277
|
id,
|
|
266
278
|
settings,
|
|
267
|
-
dryRun: options.dryRun
|
|
279
|
+
dryRun: options.dryRun,
|
|
268
280
|
});
|
|
269
281
|
|
|
270
282
|
console.log(formatJson(formatSuccess({ record, dryRun: options.dryRun ?? false })));
|
|
271
283
|
} catch (error) {
|
|
272
|
-
console.error(formatJson(formatError(
|
|
284
|
+
console.error(formatJson(formatError("UPDATE_ERROR", (error as Error).message)));
|
|
273
285
|
process.exit(1);
|
|
274
286
|
}
|
|
275
287
|
});
|
|
276
288
|
|
|
277
289
|
resource
|
|
278
|
-
.command(
|
|
279
|
-
.description(
|
|
280
|
-
.option(
|
|
281
|
-
.option(
|
|
290
|
+
.command("delete <id>")
|
|
291
|
+
.description("Delete a resource record and its children")
|
|
292
|
+
.option("--source <path>", `data source (default: ${DEFAULT_SOURCE_PATH})`)
|
|
293
|
+
.option("--dry-run", "preview deletion scope without writing")
|
|
282
294
|
.action(async (id, options) => {
|
|
283
295
|
try {
|
|
284
296
|
if (isStdinSource(options.source)) {
|
|
285
297
|
console.error(
|
|
286
|
-
formatJson(
|
|
298
|
+
formatJson(
|
|
299
|
+
formatError("STDIN_NOT_WRITABLE", "stdin source does not support write operations"),
|
|
300
|
+
),
|
|
287
301
|
);
|
|
288
302
|
process.exit(1);
|
|
289
303
|
}
|
|
@@ -292,7 +306,7 @@ resource
|
|
|
292
306
|
const { removed } = await deleteResource({
|
|
293
307
|
sourceData,
|
|
294
308
|
id,
|
|
295
|
-
dryRun: options.dryRun
|
|
309
|
+
dryRun: options.dryRun,
|
|
296
310
|
});
|
|
297
311
|
|
|
298
312
|
console.log(
|
|
@@ -300,27 +314,29 @@ resource
|
|
|
300
314
|
formatSuccess({
|
|
301
315
|
dryRun: options.dryRun ?? false,
|
|
302
316
|
removedCount: removed.length,
|
|
303
|
-
removed: removed.map((r) => ({ id: r.id, kind: r.kind }))
|
|
304
|
-
})
|
|
305
|
-
)
|
|
317
|
+
removed: removed.map((r) => ({ id: r.id, kind: r.kind })),
|
|
318
|
+
}),
|
|
319
|
+
),
|
|
306
320
|
);
|
|
307
321
|
} catch (error) {
|
|
308
|
-
console.error(formatJson(formatError(
|
|
322
|
+
console.error(formatJson(formatError("DELETE_ERROR", (error as Error).message)));
|
|
309
323
|
process.exit(1);
|
|
310
324
|
}
|
|
311
325
|
});
|
|
312
326
|
|
|
313
327
|
resource
|
|
314
|
-
.command(
|
|
315
|
-
.description(
|
|
316
|
-
.option(
|
|
317
|
-
.option(
|
|
318
|
-
.option(
|
|
319
|
-
.option(
|
|
328
|
+
.command("convert")
|
|
329
|
+
.description("Convert resource records between aPaaS and Runtime formats")
|
|
330
|
+
.option("--from <format>", "source format (apaas or runtime)")
|
|
331
|
+
.option("--to <format>", "target format (apaas or runtime)")
|
|
332
|
+
.option("--input <path>", "input file path")
|
|
333
|
+
.option("--output <path>", "output file path (omit for stdout)")
|
|
320
334
|
.action(async (options) => {
|
|
321
335
|
try {
|
|
322
336
|
if (!options.from || !options.to || !options.input) {
|
|
323
|
-
console.error(
|
|
337
|
+
console.error(
|
|
338
|
+
formatJson(formatError("MISSING_ARGS", "Required options: --from, --to, --input")),
|
|
339
|
+
);
|
|
324
340
|
process.exit(1);
|
|
325
341
|
}
|
|
326
342
|
|
|
@@ -328,7 +344,7 @@ resource
|
|
|
328
344
|
from: options.from,
|
|
329
345
|
to: options.to,
|
|
330
346
|
input: options.input,
|
|
331
|
-
output: options.output
|
|
347
|
+
output: options.output,
|
|
332
348
|
});
|
|
333
349
|
|
|
334
350
|
if (result.outputPath) {
|
|
@@ -337,21 +353,23 @@ resource
|
|
|
337
353
|
console.log(formatJson(formatSuccess({ data: result.data })));
|
|
338
354
|
}
|
|
339
355
|
} catch (error) {
|
|
340
|
-
console.error(formatJson(formatError(
|
|
356
|
+
console.error(formatJson(formatError("CONVERT_ERROR", (error as Error).message)));
|
|
341
357
|
process.exit(1);
|
|
342
358
|
}
|
|
343
359
|
});
|
|
344
360
|
|
|
345
361
|
resource
|
|
346
|
-
.command(
|
|
347
|
-
.description(
|
|
348
|
-
.option(
|
|
349
|
-
.option(
|
|
350
|
-
.option(
|
|
362
|
+
.command("diff <id>")
|
|
363
|
+
.description("Diff a resource between two sources")
|
|
364
|
+
.option("--source-a <path>", "first source file (required)")
|
|
365
|
+
.option("--source-b <path>", "second source file (required)")
|
|
366
|
+
.option("--pretty", "human-readable output")
|
|
351
367
|
.action(async (id, options) => {
|
|
352
368
|
try {
|
|
353
369
|
if (!options.sourceA || !options.sourceB) {
|
|
354
|
-
console.error(
|
|
370
|
+
console.error(
|
|
371
|
+
formatJson(formatError("MISSING_ARGS", "Required options: --source-a, --source-b")),
|
|
372
|
+
);
|
|
355
373
|
process.exit(1);
|
|
356
374
|
}
|
|
357
375
|
|
|
@@ -363,7 +381,7 @@ resource
|
|
|
363
381
|
sourceB: getSourceDescription(options.sourceB),
|
|
364
382
|
id,
|
|
365
383
|
recordsA: sourceDataA.resourceRecords,
|
|
366
|
-
recordsB: sourceDataB.resourceRecords
|
|
384
|
+
recordsB: sourceDataB.resourceRecords,
|
|
367
385
|
});
|
|
368
386
|
|
|
369
387
|
if (options.pretty) {
|
|
@@ -372,7 +390,7 @@ resource
|
|
|
372
390
|
console.log(formatJson(formatSuccess(result)));
|
|
373
391
|
}
|
|
374
392
|
} catch (error) {
|
|
375
|
-
console.error(formatJson(formatError(
|
|
393
|
+
console.error(formatJson(formatError("DIFF_ERROR", (error as Error).message)));
|
|
376
394
|
process.exit(1);
|
|
377
395
|
}
|
|
378
396
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import { convertApaasResourceRecordsToResourceRecords } from
|
|
3
|
-
import { ResourceRecords } from
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import { convertApaasResourceRecordsToResourceRecords } from "@ldsg/apaas-to-runtime";
|
|
3
|
+
import { ResourceRecords } from "@ldsg/types";
|
|
4
4
|
|
|
5
5
|
export interface ConvertOptions {
|
|
6
6
|
from: string;
|
|
@@ -10,15 +10,15 @@ export interface ConvertOptions {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export async function convertResource(
|
|
13
|
-
options: ConvertOptions
|
|
13
|
+
options: ConvertOptions,
|
|
14
14
|
): Promise<{ outputPath?: string; data: ResourceRecords }> {
|
|
15
15
|
const { from, to, input, output } = options;
|
|
16
16
|
|
|
17
|
-
if (from ===
|
|
18
|
-
throw new Error(
|
|
17
|
+
if (from === "runtime" && to === "apaas") {
|
|
18
|
+
throw new Error("Runtime → aPaaS conversion is not supported yet");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
if (from !==
|
|
21
|
+
if (from !== "apaas" || to !== "runtime") {
|
|
22
22
|
throw new Error(`Unsupported conversion: ${from} → ${to}`);
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -31,19 +31,21 @@ export async function convertResource(
|
|
|
31
31
|
|
|
32
32
|
if (Array.isArray(rawData)) {
|
|
33
33
|
resourceRecords = rawData as ResourceRecords;
|
|
34
|
-
} else if (rawData && typeof rawData ===
|
|
34
|
+
} else if (rawData && typeof rawData === "object" && Array.isArray(rawData.resourceRecords)) {
|
|
35
35
|
resourceRecords = rawData.resourceRecords as ResourceRecords;
|
|
36
36
|
} else {
|
|
37
|
-
throw new Error(
|
|
37
|
+
throw new Error("Invalid input format: expected an array or { resourceRecords: [] }");
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const { targetResourceRecords } = convertApaasResourceRecordsToResourceRecords({
|
|
41
|
-
resourceRecords
|
|
41
|
+
resourceRecords,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
if (output) {
|
|
45
45
|
const isArrayInput = Array.isArray(rawData);
|
|
46
|
-
const outputData = isArrayInput
|
|
46
|
+
const outputData = isArrayInput
|
|
47
|
+
? targetResourceRecords
|
|
48
|
+
: { resourceRecords: targetResourceRecords };
|
|
47
49
|
await fs.writeJson(output, outputData, { spaces: 2 });
|
|
48
50
|
return { outputPath: output, data: targetResourceRecords };
|
|
49
51
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ResourceRecord } from
|
|
2
|
-
import { SourceData } from
|
|
3
|
-
import { appendRecord, generateId } from
|
|
1
|
+
import { ResourceRecord } from "@ldsg/types";
|
|
2
|
+
import { SourceData } from "../../types/resource-record";
|
|
3
|
+
import { appendRecord, generateId } from "../../utils/json-file-mutator";
|
|
4
4
|
|
|
5
5
|
export interface CreateOptions {
|
|
6
6
|
sourceData: SourceData;
|
|
@@ -20,27 +20,27 @@ export async function createResource(options: CreateOptions): Promise<{ record:
|
|
|
20
20
|
if (options.fromFileRecord) {
|
|
21
21
|
const partial = options.fromFileRecord;
|
|
22
22
|
if (!partial.kind) {
|
|
23
|
-
throw new Error(
|
|
23
|
+
throw new Error("Missing required field: kind");
|
|
24
24
|
}
|
|
25
25
|
record = {
|
|
26
26
|
id: partial.id || generateId(),
|
|
27
27
|
kind: partial.kind,
|
|
28
28
|
parentId: partial.parentId ?? sourceData.resourceRecords[0]?.parentId ?? null,
|
|
29
|
-
settings: (partial.settings || {}) as Record<string, unknown
|
|
29
|
+
settings: (partial.settings || {}) as Record<string, unknown>,
|
|
30
30
|
};
|
|
31
31
|
} else {
|
|
32
32
|
if (!options.kind) {
|
|
33
|
-
throw new Error(
|
|
33
|
+
throw new Error("Missing required option: --kind");
|
|
34
34
|
}
|
|
35
35
|
if (options.parentId === undefined) {
|
|
36
|
-
throw new Error(
|
|
36
|
+
throw new Error("Missing required option: --parent-id");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
record = {
|
|
40
40
|
id: options.id || generateId(),
|
|
41
41
|
kind: options.kind,
|
|
42
42
|
parentId: options.parentId ?? null,
|
|
43
|
-
settings: options.settings || {}
|
|
43
|
+
settings: options.settings || {},
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ResourceRecord } from
|
|
2
|
-
import { SourceData } from
|
|
3
|
-
import { removeRecord } from
|
|
1
|
+
import { ResourceRecord } from "@ldsg/types";
|
|
2
|
+
import { SourceData } from "../../types/resource-record";
|
|
3
|
+
import { removeRecord } from "../../utils/json-file-mutator";
|
|
4
4
|
|
|
5
5
|
export interface DeleteOptions {
|
|
6
6
|
sourceData: SourceData;
|
|
@@ -8,7 +8,9 @@ export interface DeleteOptions {
|
|
|
8
8
|
dryRun?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export async function deleteResource(
|
|
11
|
+
export async function deleteResource(
|
|
12
|
+
options: DeleteOptions,
|
|
13
|
+
): Promise<{ removed: ResourceRecord[] }> {
|
|
12
14
|
const { sourceData, id, dryRun } = options;
|
|
13
15
|
const result = await removeRecord(sourceData, id, dryRun);
|
|
14
16
|
return { removed: result.removed };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ResourceRecords } from
|
|
2
|
-
import { DiffResult } from
|
|
1
|
+
import { ResourceRecords } from "@ldsg/types";
|
|
2
|
+
import { DiffResult } from "../../types/resource-record";
|
|
3
3
|
|
|
4
4
|
function isObject(value: unknown): value is Record<string, unknown> {
|
|
5
|
-
return value !== null && typeof value ===
|
|
5
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
interface DiffEntry {
|
|
@@ -15,7 +15,7 @@ interface DiffEntry {
|
|
|
15
15
|
function deepDiff(
|
|
16
16
|
objA: Record<string, unknown>,
|
|
17
17
|
objB: Record<string, unknown>,
|
|
18
|
-
basePath =
|
|
18
|
+
basePath = "",
|
|
19
19
|
): {
|
|
20
20
|
changed: DiffEntry[];
|
|
21
21
|
added: DiffEntry[];
|
|
@@ -65,14 +65,14 @@ export async function diffResource(options: DiffOptions): Promise<DiffResult> {
|
|
|
65
65
|
|
|
66
66
|
const result: DiffResult = {
|
|
67
67
|
id,
|
|
68
|
-
kind: (recordA || recordB)?.kind ||
|
|
68
|
+
kind: (recordA || recordB)?.kind || "",
|
|
69
69
|
sourceA,
|
|
70
70
|
sourceB,
|
|
71
71
|
changed: [],
|
|
72
72
|
added: [],
|
|
73
73
|
removed: [],
|
|
74
74
|
sideA: recordA,
|
|
75
|
-
sideB: recordB
|
|
75
|
+
sideB: recordB,
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
if (!recordA || !recordB) {
|
|
@@ -81,17 +81,17 @@ export async function diffResource(options: DiffOptions): Promise<DiffResult> {
|
|
|
81
81
|
|
|
82
82
|
if (recordA.kind !== recordB.kind) {
|
|
83
83
|
result.changed.push({
|
|
84
|
-
path:
|
|
84
|
+
path: "kind",
|
|
85
85
|
from: recordA.kind,
|
|
86
|
-
to: recordB.kind
|
|
86
|
+
to: recordB.kind,
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
if (recordA.parentId !== recordB.parentId) {
|
|
91
91
|
result.changed.push({
|
|
92
|
-
path:
|
|
92
|
+
path: "parentId",
|
|
93
93
|
from: recordA.parentId || null,
|
|
94
|
-
to: recordB.parentId || null
|
|
94
|
+
to: recordB.parentId || null,
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|