@abapify/adt-plugin-gcts 0.4.0 → 0.4.3
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.d.mts +166 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1291 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +6 -3
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1291 @@
|
|
|
1
|
+
import { createPlugin, registerFormatPlugin } from "@abapify/adt-plugin";
|
|
2
|
+
import { AdkClass, AdkDataElement, AdkDomain, AdkFunctionGroup, AdkInterface, AdkPackage, AdkProgram, AdkTable, AdkTableType, createAdk, getMainType, getTypeForKind } from "@abapify/adk";
|
|
3
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/lib/format/filename.ts
|
|
7
|
+
/**
|
|
8
|
+
* Extension used for the source-file part of each object type.
|
|
9
|
+
*
|
|
10
|
+
* Order of precedence: CDS types use AFF's `.asddls` / `.asdcls`; everything
|
|
11
|
+
* else uses `.abap`. Metadata is always `.json`.
|
|
12
|
+
*/
|
|
13
|
+
const TYPE_TO_SOURCE_EXT = {
|
|
14
|
+
CLAS: "abap",
|
|
15
|
+
INTF: "abap",
|
|
16
|
+
PROG: "abap",
|
|
17
|
+
FUGR: "abap",
|
|
18
|
+
FUNC: "abap",
|
|
19
|
+
DDLS: "asddls",
|
|
20
|
+
DCLS: "asdcls",
|
|
21
|
+
BDEF: "abdl",
|
|
22
|
+
SRVD: "acds"
|
|
23
|
+
};
|
|
24
|
+
/** Metadata extension for all AFF/gCTS objects. */
|
|
25
|
+
const METADATA_EXTENSION = "json";
|
|
26
|
+
/** Fixed filename for package metadata (AFF / gCTS convention). */
|
|
27
|
+
const PACKAGE_FILENAME = "package.devc.json";
|
|
28
|
+
/**
|
|
29
|
+
* Build the AFF/gCTS on-disk filename for an object.
|
|
30
|
+
*
|
|
31
|
+
* @param name Object name (case preserved — AFF normalises to lowercase)
|
|
32
|
+
* @param type ABAP object type code (CLAS, INTF, ...)
|
|
33
|
+
* @param kind `"metadata"` (default) or `"source"`.
|
|
34
|
+
* @param suffix Optional sub-include suffix (e.g. `locals_def` for CLAS).
|
|
35
|
+
*/
|
|
36
|
+
function gctsFilename(name, type, kind = "metadata", suffix) {
|
|
37
|
+
const base = name.toLowerCase();
|
|
38
|
+
const typeLower = mainType(type).toLowerCase();
|
|
39
|
+
if (kind === "metadata") return `${base}.${typeLower}.${METADATA_EXTENSION}`;
|
|
40
|
+
const ext = TYPE_TO_SOURCE_EXT[mainType(type).toUpperCase()] ?? "abap";
|
|
41
|
+
return suffix ? `${base}.${typeLower}.${suffix}.${ext}` : `${base}.${typeLower}.${ext}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse an AFF/gCTS filename back into its components.
|
|
45
|
+
*
|
|
46
|
+
* Returns `undefined` if the filename does not look like a known AFF/gCTS
|
|
47
|
+
* artefact — callers should skip unknown files (e.g. `README.md`).
|
|
48
|
+
*/
|
|
49
|
+
function parseGctsFilename(filename) {
|
|
50
|
+
if (filename === "package.devc.json") return {
|
|
51
|
+
name: "",
|
|
52
|
+
type: "DEVC",
|
|
53
|
+
extension: METADATA_EXTENSION
|
|
54
|
+
};
|
|
55
|
+
const parts = filename.split(".");
|
|
56
|
+
if (parts.length < 3) return;
|
|
57
|
+
const extension = parts.at(-1);
|
|
58
|
+
const nameParts = (parts.length >= 4 ? parts.at(-2) : void 0) ? parts.slice(0, -3) : parts.slice(0, -2);
|
|
59
|
+
if (nameParts.length === 0) return;
|
|
60
|
+
if (extension === "json") return {
|
|
61
|
+
name: nameParts.length === 0 ? "" : parts.slice(0, -2).join("."),
|
|
62
|
+
type: parts.at(-2).toUpperCase(),
|
|
63
|
+
extension
|
|
64
|
+
};
|
|
65
|
+
if (!(/* @__PURE__ */ new Set([
|
|
66
|
+
"abap",
|
|
67
|
+
"asddls",
|
|
68
|
+
"asdcls",
|
|
69
|
+
"abdl",
|
|
70
|
+
"acds",
|
|
71
|
+
"properties"
|
|
72
|
+
])).has(extension)) return;
|
|
73
|
+
if (parts.length >= 4) return {
|
|
74
|
+
name: parts.slice(0, -3).join("."),
|
|
75
|
+
type: parts.at(-3).toUpperCase(),
|
|
76
|
+
suffix: parts.at(-2),
|
|
77
|
+
extension
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
name: parts.slice(0, -2).join("."),
|
|
81
|
+
type: parts.at(-2).toUpperCase(),
|
|
82
|
+
extension
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Translate an ADT object URI (e.g. `/sap/bc/adt/oo/classes/zcl_foo`) to
|
|
87
|
+
* the AFF/gCTS on-disk path (e.g. `zcl_foo/zcl_foo.clas.json`).
|
|
88
|
+
*
|
|
89
|
+
* Matches the `adtUriToAbapGitPath` helper in the abapGit plugin.
|
|
90
|
+
*/
|
|
91
|
+
function adtUriToGctsPath(uri) {
|
|
92
|
+
if (!uri.startsWith("/sap/bc/adt/")) return void 0;
|
|
93
|
+
const tail = uri.slice(12);
|
|
94
|
+
if (tail.length === 0) return void 0;
|
|
95
|
+
for (const [pattern, type] of [
|
|
96
|
+
[/^oo\/classes\/([^/]+)$/, "CLAS"],
|
|
97
|
+
[/^oo\/interfaces\/([^/]+)$/, "INTF"],
|
|
98
|
+
[/^programs\/programs\/([^/]+)$/, "PROG"],
|
|
99
|
+
[/^functions\/groups\/([^/]+)$/, "FUGR"],
|
|
100
|
+
[/^packages\/([^/]+)$/, "DEVC"],
|
|
101
|
+
[/^ddic\/domains\/([^/]+)$/, "DOMA"],
|
|
102
|
+
[/^ddic\/dataelements\/([^/]+)$/, "DTEL"],
|
|
103
|
+
[/^ddic\/structures\/([^/]+)$/, "TABL"],
|
|
104
|
+
[/^ddic\/tables\/([^/]+)$/, "TABL"],
|
|
105
|
+
[/^ddic\/tabletypes\/([^/]+)$/, "TTYP"],
|
|
106
|
+
[/^ddic\/ddl\/sources\/([^/]+)$/, "DDLS"],
|
|
107
|
+
[/^acm\/dcls\/sources\/([^/]+)$/, "DCLS"]
|
|
108
|
+
]) {
|
|
109
|
+
const m = pattern.exec(tail);
|
|
110
|
+
if (m) {
|
|
111
|
+
const name = m[1];
|
|
112
|
+
if (type === "DEVC") return `${name.toLowerCase()}/${PACKAGE_FILENAME}`;
|
|
113
|
+
return `${name.toLowerCase()}/${gctsFilename(name, type)}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function mainType(type) {
|
|
118
|
+
return type.split("/")[0];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/lib/handlers/base.ts
|
|
123
|
+
const handlerRegistry = /* @__PURE__ */ new Map();
|
|
124
|
+
/** Look up a handler (full-type first, then main-type fallback). */
|
|
125
|
+
function getHandler(type) {
|
|
126
|
+
return handlerRegistry.get(type) ?? handlerRegistry.get(getMainType(type));
|
|
127
|
+
}
|
|
128
|
+
/** `true` iff the given object type has a registered handler. */
|
|
129
|
+
function isSupported(type) {
|
|
130
|
+
return handlerRegistry.has(type) || handlerRegistry.has(getMainType(type));
|
|
131
|
+
}
|
|
132
|
+
/** Snapshot of all registered object types. */
|
|
133
|
+
function getSupportedTypes() {
|
|
134
|
+
return [...handlerRegistry.keys()];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Build a JSON metadata file for the object. JSON is pretty-printed (2-space
|
|
138
|
+
* indent) so the output is diff-friendly and matches AFF's own tooling.
|
|
139
|
+
*/
|
|
140
|
+
function buildMetadataFile(meta) {
|
|
141
|
+
return JSON.stringify(meta, null, 2) + "\n";
|
|
142
|
+
}
|
|
143
|
+
function createHandler(adkClassOrType, definition) {
|
|
144
|
+
let type;
|
|
145
|
+
if (typeof adkClassOrType === "string") type = adkClassOrType;
|
|
146
|
+
else {
|
|
147
|
+
const kind = adkClassOrType.kind;
|
|
148
|
+
const derived = getTypeForKind(kind);
|
|
149
|
+
if (!derived) throw new Error(`Unknown ADK kind: ${kind}. Ensure the ADK class has a static 'kind' property.`);
|
|
150
|
+
type = derived;
|
|
151
|
+
}
|
|
152
|
+
const fileExtension = getMainType(type).toLowerCase();
|
|
153
|
+
async function resolveSource(entry) {
|
|
154
|
+
return await (typeof entry.content === "function" ? entry.content() : entry.content);
|
|
155
|
+
}
|
|
156
|
+
const handler = {
|
|
157
|
+
type,
|
|
158
|
+
fileExtension,
|
|
159
|
+
schema: {
|
|
160
|
+
parse: (raw) => JSON.parse(raw),
|
|
161
|
+
build: (data) => JSON.stringify(data, null, 2) + "\n"
|
|
162
|
+
},
|
|
163
|
+
suffixToSourceKey: definition.suffixToSourceKey,
|
|
164
|
+
async serialize(object) {
|
|
165
|
+
const files = [];
|
|
166
|
+
const objectName = (object.name ?? "").toLowerCase();
|
|
167
|
+
if (definition.getSources) for (const entry of definition.getSources(object)) {
|
|
168
|
+
const content = await resolveSource(entry);
|
|
169
|
+
if (!content) continue;
|
|
170
|
+
files.push({
|
|
171
|
+
path: gctsFilename(objectName, type, "source", entry.suffix),
|
|
172
|
+
content
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else if (definition.getSource) {
|
|
176
|
+
const content = await definition.getSource(object);
|
|
177
|
+
if (content) files.push({
|
|
178
|
+
path: gctsFilename(objectName, type, "source"),
|
|
179
|
+
content
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const metadata = definition.toMetadata(object);
|
|
183
|
+
const metadataFile = typeof definition.metadataFileName === "function" ? definition.metadataFileName(object) : definition.metadataFileName ?? gctsFilename(objectName, type, "metadata");
|
|
184
|
+
files.push({
|
|
185
|
+
path: metadataFile,
|
|
186
|
+
content: buildMetadataFile(metadata)
|
|
187
|
+
});
|
|
188
|
+
if (definition.getExtraFiles) {
|
|
189
|
+
const extras = await definition.getExtraFiles(object);
|
|
190
|
+
for (const extra of extras) {
|
|
191
|
+
const content = await resolveSource(extra);
|
|
192
|
+
if (!content) continue;
|
|
193
|
+
files.push({
|
|
194
|
+
path: extra.path,
|
|
195
|
+
content
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return files;
|
|
200
|
+
},
|
|
201
|
+
fromAbapGit: definition.fromMetadata ? (values) => definition.fromMetadata(values) : void 0,
|
|
202
|
+
setSources: definition.setSources
|
|
203
|
+
};
|
|
204
|
+
handlerRegistry.set(type, handler);
|
|
205
|
+
return handler;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/lib/handlers/objects/clas.ts
|
|
210
|
+
/**
|
|
211
|
+
* CLAS handler for gCTS / AFF format.
|
|
212
|
+
*
|
|
213
|
+
* Projects ADK class data to the AFF clas-v1.json schema shape:
|
|
214
|
+
*
|
|
215
|
+
* {
|
|
216
|
+
* formatVersion: "1",
|
|
217
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
218
|
+
* category?,
|
|
219
|
+
* fixPointArithmetic?,
|
|
220
|
+
* messageClass?,
|
|
221
|
+
* descriptions?: { types?, attributes?, events?, methods? }
|
|
222
|
+
* }
|
|
223
|
+
*
|
|
224
|
+
* Sources are emitted per include (main, definitions, implementations, ...).
|
|
225
|
+
* Abapgit suffix convention is reused for consistency — AFF does not
|
|
226
|
+
* standardise include filenames, but using the same suffixes as abapGit
|
|
227
|
+
* means downstream tooling (diff, round-trip) can treat both formats with
|
|
228
|
+
* the same suffix map.
|
|
229
|
+
*/
|
|
230
|
+
const SUFFIX = {
|
|
231
|
+
main: void 0,
|
|
232
|
+
definitions: "locals_def",
|
|
233
|
+
implementations: "locals_imp",
|
|
234
|
+
localtypes: "locals_types",
|
|
235
|
+
macros: "macros",
|
|
236
|
+
testclasses: "testclasses"
|
|
237
|
+
};
|
|
238
|
+
const SUFFIX_TO_SOURCE_KEY = Object.fromEntries(Object.entries(SUFFIX).filter(([, v]) => v !== void 0).map(([k, v]) => [v, k]));
|
|
239
|
+
/**
|
|
240
|
+
* ADK category enum → AFF category enum.
|
|
241
|
+
* AFF uses a different, finer-grained set of category names.
|
|
242
|
+
*/
|
|
243
|
+
const CATEGORY_TO_AFF = {
|
|
244
|
+
generalObjectType: "generalObjectType",
|
|
245
|
+
exitClass: "exitClass",
|
|
246
|
+
testClass: "testclassAbapUnit",
|
|
247
|
+
behaviorPool: "behaviorClass",
|
|
248
|
+
entityEventHandler: "entityEventHandler",
|
|
249
|
+
persistentClass: "persistentClass",
|
|
250
|
+
factoryClass: "factoryForPersistentClass",
|
|
251
|
+
rfcProxyClass: "rfcProxyClass",
|
|
252
|
+
communicationConnectionClass: "communicationConnectionClass",
|
|
253
|
+
exceptionClass: "exceptionClass",
|
|
254
|
+
areaClass: "areaClassSharedObjects",
|
|
255
|
+
bspClass: "bspApplicationClass"
|
|
256
|
+
};
|
|
257
|
+
const CATEGORY_FROM_AFF = Object.fromEntries(Object.entries(CATEGORY_TO_AFF).map(([k, v]) => [v, k]));
|
|
258
|
+
const classHandler = createHandler(AdkClass, {
|
|
259
|
+
suffixToSourceKey: SUFFIX_TO_SOURCE_KEY,
|
|
260
|
+
toMetadata(cls) {
|
|
261
|
+
const data = cls.dataSync;
|
|
262
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
263
|
+
return {
|
|
264
|
+
formatVersion: "1",
|
|
265
|
+
header: {
|
|
266
|
+
description: cls.description ?? data.description ?? "",
|
|
267
|
+
originalLanguage: lang,
|
|
268
|
+
...data.abapLanguageVersion && data.abapLanguageVersion !== "standard" ? { abapLanguageVersion: data.abapLanguageVersion } : {}
|
|
269
|
+
},
|
|
270
|
+
...data.category ? { category: CATEGORY_TO_AFF[data.category] ?? "generalObjectType" } : {},
|
|
271
|
+
...typeof data.fixPointArithmetic === "boolean" ? { fixPointArithmetic: data.fixPointArithmetic } : {}
|
|
272
|
+
};
|
|
273
|
+
},
|
|
274
|
+
getSources: (cls) => {
|
|
275
|
+
return (cls.dataSync.include ?? []).map((inc) => ({
|
|
276
|
+
suffix: SUFFIX[String(inc.includeType ?? "main")],
|
|
277
|
+
content: () => cls.getIncludeSource(String(inc.includeType ?? "main"))
|
|
278
|
+
}));
|
|
279
|
+
},
|
|
280
|
+
fromMetadata: (meta) => ({
|
|
281
|
+
name: "",
|
|
282
|
+
type: "CLAS/OC",
|
|
283
|
+
description: meta.header.description,
|
|
284
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
285
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
286
|
+
category: meta.category ? CATEGORY_FROM_AFF[meta.category] ?? meta.category : void 0,
|
|
287
|
+
abapLanguageVersion: meta.header.abapLanguageVersion,
|
|
288
|
+
fixPointArithmetic: meta.fixPointArithmetic
|
|
289
|
+
}),
|
|
290
|
+
setSources: (cls, sources) => {
|
|
291
|
+
cls._pendingSources = sources;
|
|
292
|
+
if (sources.main) cls._pendingSource = sources.main;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/lib/handlers/objects/intf.ts
|
|
298
|
+
/**
|
|
299
|
+
* INTF handler for gCTS / AFF format.
|
|
300
|
+
*
|
|
301
|
+
* Projects ADK interface data to the AFF intf-v1.json schema shape:
|
|
302
|
+
*
|
|
303
|
+
* {
|
|
304
|
+
* formatVersion: "1",
|
|
305
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
306
|
+
* category?,
|
|
307
|
+
* proxy?,
|
|
308
|
+
* descriptions?
|
|
309
|
+
* }
|
|
310
|
+
*/
|
|
311
|
+
const interfaceHandler = createHandler(AdkInterface, {
|
|
312
|
+
toMetadata(intf) {
|
|
313
|
+
const data = intf.dataSync;
|
|
314
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
315
|
+
return {
|
|
316
|
+
formatVersion: "1",
|
|
317
|
+
header: {
|
|
318
|
+
description: intf.description ?? data.description ?? "",
|
|
319
|
+
originalLanguage: lang,
|
|
320
|
+
...data.abapLanguageVersion && data.abapLanguageVersion !== "standard" ? { abapLanguageVersion: data.abapLanguageVersion } : {}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
},
|
|
324
|
+
getSource: (obj) => obj.getSource(),
|
|
325
|
+
fromMetadata: (meta) => ({
|
|
326
|
+
name: "",
|
|
327
|
+
type: "INTF/OI",
|
|
328
|
+
description: meta.header.description,
|
|
329
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
330
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
331
|
+
abapLanguageVersion: meta.header.abapLanguageVersion
|
|
332
|
+
}),
|
|
333
|
+
setSources: (intf, sources) => {
|
|
334
|
+
if (sources.main) intf._pendingSource = sources.main;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/lib/handlers/objects/prog.ts
|
|
340
|
+
/**
|
|
341
|
+
* PROG handler for gCTS / AFF format.
|
|
342
|
+
*
|
|
343
|
+
* Projects ADK program data to the AFF prog-v1.json schema shape:
|
|
344
|
+
*
|
|
345
|
+
* {
|
|
346
|
+
* formatVersion: "1",
|
|
347
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
348
|
+
* generalInformation: { programType, programStatus, fixPointArithmetic?, ... }
|
|
349
|
+
* }
|
|
350
|
+
*/
|
|
351
|
+
/** ADK sourceObjectStatus → AFF programStatus */
|
|
352
|
+
const STATUS_TO_AFF = {
|
|
353
|
+
SAPStandardProduction: "sapProductionProgram",
|
|
354
|
+
customerProduction: "customerProductionProgram",
|
|
355
|
+
system: "systemProgram",
|
|
356
|
+
test: "testProgram"
|
|
357
|
+
};
|
|
358
|
+
const STATUS_FROM_AFF = Object.fromEntries(Object.entries(STATUS_TO_AFF).map(([k, v]) => [v, k]));
|
|
359
|
+
const programHandler = createHandler(AdkProgram, {
|
|
360
|
+
toMetadata(prog) {
|
|
361
|
+
const data = prog.dataSync;
|
|
362
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
363
|
+
const generalInformation = {
|
|
364
|
+
programType: data.programType ?? "executableProgram",
|
|
365
|
+
programStatus: STATUS_TO_AFF[data.sourceObjectStatus ?? ""] ?? "systemProgram"
|
|
366
|
+
};
|
|
367
|
+
if (typeof data.fixPointArithmetic === "boolean") generalInformation.fixPointArithmetic = data.fixPointArithmetic;
|
|
368
|
+
return {
|
|
369
|
+
formatVersion: "1",
|
|
370
|
+
header: {
|
|
371
|
+
description: prog.description ?? data.description ?? "",
|
|
372
|
+
originalLanguage: lang,
|
|
373
|
+
...data.abapLanguageVersion && data.abapLanguageVersion !== "standard" ? { abapLanguageVersion: data.abapLanguageVersion } : {}
|
|
374
|
+
},
|
|
375
|
+
generalInformation
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
getSource: (obj) => obj.getSource(),
|
|
379
|
+
fromMetadata: (meta) => ({
|
|
380
|
+
name: "",
|
|
381
|
+
type: "PROG/P",
|
|
382
|
+
description: meta.header.description,
|
|
383
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
384
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
385
|
+
programType: meta.generalInformation?.programType,
|
|
386
|
+
sourceObjectStatus: meta.generalInformation?.programStatus ? STATUS_FROM_AFF[meta.generalInformation.programStatus] : void 0,
|
|
387
|
+
fixPointArithmetic: meta.generalInformation?.fixPointArithmetic,
|
|
388
|
+
abapLanguageVersion: meta.header.abapLanguageVersion
|
|
389
|
+
}),
|
|
390
|
+
setSources: (prog, sources) => {
|
|
391
|
+
if (sources.main) prog._pendingSource = sources.main;
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/lib/handlers/objects/devc.ts
|
|
397
|
+
/**
|
|
398
|
+
* DEVC (package) handler for gCTS / AFF format.
|
|
399
|
+
*
|
|
400
|
+
* Packages use a fixed filename (`package.devc.json`) — same convention as
|
|
401
|
+
* abapGit's `package.devc.xml`.
|
|
402
|
+
*
|
|
403
|
+
* Note: DEVC has no AFF schema in SAP/abap-file-formats — this handler
|
|
404
|
+
* uses a gCTS-compatible shape with formatVersion "1". If AFF adds a DEVC
|
|
405
|
+
* schema later, align here.
|
|
406
|
+
*/
|
|
407
|
+
const packageHandler = createHandler(AdkPackage, {
|
|
408
|
+
metadataFileName: PACKAGE_FILENAME,
|
|
409
|
+
toMetadata(pkg) {
|
|
410
|
+
return {
|
|
411
|
+
formatVersion: "1",
|
|
412
|
+
header: {
|
|
413
|
+
description: pkg.description ?? "",
|
|
414
|
+
originalLanguage: (pkg.dataSync?.language ?? pkg.dataSync?.masterLanguage ?? "").toLowerCase(),
|
|
415
|
+
...pkg.dataSync?.abapLanguageVersion && pkg.dataSync.abapLanguageVersion !== "standard" ? { abapLanguageVersion: pkg.dataSync.abapLanguageVersion } : {}
|
|
416
|
+
},
|
|
417
|
+
package: {
|
|
418
|
+
softwareComponent: pkg.dataSync?.transport?.softwareComponent?.name,
|
|
419
|
+
applicationComponent: pkg.dataSync?.applicationComponent?.name,
|
|
420
|
+
packageType: pkg.dataSync?.attributes?.packageType
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
fromMetadata: (meta) => ({
|
|
425
|
+
name: "",
|
|
426
|
+
description: meta?.header?.description,
|
|
427
|
+
language: meta?.header?.originalLanguage?.toUpperCase(),
|
|
428
|
+
masterLanguage: meta?.header?.originalLanguage?.toUpperCase(),
|
|
429
|
+
abapLanguageVersion: meta?.header?.abapLanguageVersion
|
|
430
|
+
})
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/lib/handlers/objects/doma.ts
|
|
435
|
+
/**
|
|
436
|
+
* DOMA (domain) handler for gCTS / AFF format.
|
|
437
|
+
*
|
|
438
|
+
* Projects ADK domain data to the AFF doma-v1.json schema shape:
|
|
439
|
+
*
|
|
440
|
+
* {
|
|
441
|
+
* formatVersion: "1",
|
|
442
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
443
|
+
* format: { dataType, length, decimals? } (required),
|
|
444
|
+
* outputCharacteristics?: { length?, caseSensitive?, conversionRoutine?, ... },
|
|
445
|
+
* valueTable?: { name }
|
|
446
|
+
* }
|
|
447
|
+
*/
|
|
448
|
+
const domainHandler = createHandler(AdkDomain, {
|
|
449
|
+
toMetadata(doma) {
|
|
450
|
+
const data = doma.dataSync;
|
|
451
|
+
const content = data.content ?? {};
|
|
452
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
453
|
+
const ti = content.typeInformation ?? {};
|
|
454
|
+
const oi = content.outputInformation ?? {};
|
|
455
|
+
const vi = content.valueInformation ?? {};
|
|
456
|
+
const format = {
|
|
457
|
+
dataType: ti.datatype ?? "CHAR",
|
|
458
|
+
length: ti.length ?? 1
|
|
459
|
+
};
|
|
460
|
+
if (typeof ti.decimals === "number") format.decimals = ti.decimals;
|
|
461
|
+
const result = {
|
|
462
|
+
formatVersion: "1",
|
|
463
|
+
header: {
|
|
464
|
+
description: doma.description ?? "",
|
|
465
|
+
originalLanguage: lang
|
|
466
|
+
},
|
|
467
|
+
format
|
|
468
|
+
};
|
|
469
|
+
const outChars = {};
|
|
470
|
+
if (typeof oi.length === "number") outChars.length = oi.length;
|
|
471
|
+
if (typeof oi.lowercase === "boolean") outChars.caseSensitive = !oi.lowercase;
|
|
472
|
+
if (oi.conversionExit) outChars.conversionRoutine = oi.conversionExit;
|
|
473
|
+
if (Object.keys(outChars).length > 0) result.outputCharacteristics = outChars;
|
|
474
|
+
const vtRef = vi.valueTableRef ?? {};
|
|
475
|
+
if (vtRef.name) result.valueTable = { name: vtRef.name };
|
|
476
|
+
return result;
|
|
477
|
+
},
|
|
478
|
+
fromMetadata: (meta) => {
|
|
479
|
+
const result = {
|
|
480
|
+
name: "",
|
|
481
|
+
description: meta.header.description,
|
|
482
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
483
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase()
|
|
484
|
+
};
|
|
485
|
+
const content = { typeInformation: {
|
|
486
|
+
datatype: meta.format.dataType,
|
|
487
|
+
length: meta.format.length,
|
|
488
|
+
decimals: meta.format.decimals ?? 0
|
|
489
|
+
} };
|
|
490
|
+
if (meta.outputCharacteristics) content.outputInformation = {
|
|
491
|
+
length: meta.outputCharacteristics.length,
|
|
492
|
+
lowercase: meta.outputCharacteristics.caseSensitive ? false : void 0,
|
|
493
|
+
conversionExit: meta.outputCharacteristics.conversionRoutine
|
|
494
|
+
};
|
|
495
|
+
if (meta.valueTable?.name) content.valueInformation = { valueTableRef: { name: meta.valueTable.name } };
|
|
496
|
+
result.content = content;
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region src/lib/handlers/objects/dtel.ts
|
|
503
|
+
/**
|
|
504
|
+
* DTEL (data element) handler for gCTS / AFF format.
|
|
505
|
+
*
|
|
506
|
+
* Projects ADK data element data to the AFF dtel-v1.json schema shape:
|
|
507
|
+
*
|
|
508
|
+
* {
|
|
509
|
+
* formatVersion: "1",
|
|
510
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
511
|
+
* dataTypeInformation: { domainName? } | { predefinedType: { dataType, length?, decimals? } },
|
|
512
|
+
* fieldLabels?: { short, shortLength, medium, mediumLength, long, longLength, heading, headingLength }
|
|
513
|
+
* }
|
|
514
|
+
*/
|
|
515
|
+
function buildDataTypeInformation(data) {
|
|
516
|
+
if (data.typeKind === "domain" && data.typeName) return { domainName: data.typeName };
|
|
517
|
+
if (data.dataType) {
|
|
518
|
+
const predefinedType = { dataType: data.dataType };
|
|
519
|
+
if (typeof data.dataTypeLength === "number") predefinedType.length = data.dataTypeLength;
|
|
520
|
+
if (typeof data.dataTypeDecimals === "number") predefinedType.decimals = data.dataTypeDecimals;
|
|
521
|
+
return { predefinedType };
|
|
522
|
+
}
|
|
523
|
+
return {};
|
|
524
|
+
}
|
|
525
|
+
function buildFieldLabels(data) {
|
|
526
|
+
const labels = {};
|
|
527
|
+
for (const [src, dst, lenKey] of [
|
|
528
|
+
[
|
|
529
|
+
"shortFieldLabel",
|
|
530
|
+
"short",
|
|
531
|
+
"shortFieldLength"
|
|
532
|
+
],
|
|
533
|
+
[
|
|
534
|
+
"mediumFieldLabel",
|
|
535
|
+
"medium",
|
|
536
|
+
"mediumFieldLength"
|
|
537
|
+
],
|
|
538
|
+
[
|
|
539
|
+
"longFieldLabel",
|
|
540
|
+
"long",
|
|
541
|
+
"longFieldLength"
|
|
542
|
+
],
|
|
543
|
+
[
|
|
544
|
+
"headingFieldLabel",
|
|
545
|
+
"heading",
|
|
546
|
+
"headingFieldLength"
|
|
547
|
+
]
|
|
548
|
+
]) if (data[src]) {
|
|
549
|
+
labels[dst] = data[src];
|
|
550
|
+
if (typeof data[lenKey] === "number") labels[`${dst}Length`] = data[lenKey];
|
|
551
|
+
}
|
|
552
|
+
return labels;
|
|
553
|
+
}
|
|
554
|
+
const dataElementHandler = createHandler(AdkDataElement, {
|
|
555
|
+
toMetadata(dtel) {
|
|
556
|
+
const data = dtel.dataSync;
|
|
557
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
558
|
+
const result = {
|
|
559
|
+
formatVersion: "1",
|
|
560
|
+
header: {
|
|
561
|
+
description: dtel.description ?? "",
|
|
562
|
+
originalLanguage: lang
|
|
563
|
+
},
|
|
564
|
+
dataTypeInformation: buildDataTypeInformation(data)
|
|
565
|
+
};
|
|
566
|
+
const fieldLabels = buildFieldLabels(data);
|
|
567
|
+
if (Object.keys(fieldLabels).length > 0) result.fieldLabels = fieldLabels;
|
|
568
|
+
return result;
|
|
569
|
+
},
|
|
570
|
+
fromMetadata: (meta) => {
|
|
571
|
+
const dti = meta.dataTypeInformation ?? {};
|
|
572
|
+
const result = {
|
|
573
|
+
name: "",
|
|
574
|
+
description: meta.header.description,
|
|
575
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
576
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase()
|
|
577
|
+
};
|
|
578
|
+
if (dti.domainName) {
|
|
579
|
+
result.typeKind = "domain";
|
|
580
|
+
result.typeName = dti.domainName;
|
|
581
|
+
} else if (dti.predefinedType) {
|
|
582
|
+
result.typeKind = "predefinedAbapType";
|
|
583
|
+
result.dataType = dti.predefinedType.dataType;
|
|
584
|
+
result.dataTypeLength = dti.predefinedType.length;
|
|
585
|
+
result.dataTypeDecimals = dti.predefinedType.decimals;
|
|
586
|
+
}
|
|
587
|
+
const fl = meta.fieldLabels;
|
|
588
|
+
if (fl) {
|
|
589
|
+
result.shortFieldLabel = fl.short;
|
|
590
|
+
result.shortFieldLength = fl.shortLength;
|
|
591
|
+
result.mediumFieldLabel = fl.medium;
|
|
592
|
+
result.mediumFieldLength = fl.mediumLength;
|
|
593
|
+
result.longFieldLabel = fl.long;
|
|
594
|
+
result.longFieldLength = fl.longLength;
|
|
595
|
+
result.headingFieldLabel = fl.heading;
|
|
596
|
+
result.headingFieldLength = fl.headingLength;
|
|
597
|
+
}
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
//#endregion
|
|
603
|
+
//#region src/lib/handlers/objects/tabl.ts
|
|
604
|
+
/**
|
|
605
|
+
* TABL (table / structure) handler for gCTS / AFF format.
|
|
606
|
+
*
|
|
607
|
+
* Note: TABL has no AFF schema in SAP/abap-file-formats — this handler
|
|
608
|
+
* uses a gCTS-compatible shape with formatVersion "1". If AFF adds a TABL
|
|
609
|
+
* schema later, align here.
|
|
610
|
+
*/
|
|
611
|
+
const tableHandler = createHandler(AdkTable, {
|
|
612
|
+
toMetadata(tabl) {
|
|
613
|
+
const data = tabl.dataSync;
|
|
614
|
+
return {
|
|
615
|
+
formatVersion: "1",
|
|
616
|
+
header: {
|
|
617
|
+
description: tabl.description ?? "",
|
|
618
|
+
originalLanguage: (data.language ?? data.masterLanguage ?? "").toLowerCase()
|
|
619
|
+
},
|
|
620
|
+
table: {
|
|
621
|
+
tableCategory: data.tableCategory,
|
|
622
|
+
deliveryClass: data.deliveryClass,
|
|
623
|
+
dataMaintenance: data.dataMaintenance,
|
|
624
|
+
fields: data.fields
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
},
|
|
628
|
+
fromMetadata: (meta) => ({
|
|
629
|
+
name: "",
|
|
630
|
+
description: meta?.header?.description,
|
|
631
|
+
language: meta?.header?.originalLanguage?.toUpperCase(),
|
|
632
|
+
masterLanguage: meta?.header?.originalLanguage?.toUpperCase(),
|
|
633
|
+
tableCategory: meta?.table?.tableCategory,
|
|
634
|
+
deliveryClass: meta?.table?.deliveryClass,
|
|
635
|
+
dataMaintenance: meta?.table?.dataMaintenance,
|
|
636
|
+
fields: meta?.table?.fields
|
|
637
|
+
})
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
//#endregion
|
|
641
|
+
//#region src/lib/handlers/objects/ttyp.ts
|
|
642
|
+
/**
|
|
643
|
+
* TTYP (table type) handler for gCTS / AFF format.
|
|
644
|
+
*
|
|
645
|
+
* Note: TTYP has no AFF schema in SAP/abap-file-formats — this handler
|
|
646
|
+
* uses a gCTS-compatible shape with formatVersion "1". If AFF adds a TTYP
|
|
647
|
+
* schema later, align here.
|
|
648
|
+
*/
|
|
649
|
+
const tableTypeHandler = createHandler(AdkTableType, {
|
|
650
|
+
toMetadata(ttyp) {
|
|
651
|
+
const data = ttyp.dataSync;
|
|
652
|
+
return {
|
|
653
|
+
formatVersion: "1",
|
|
654
|
+
header: {
|
|
655
|
+
description: ttyp.description ?? "",
|
|
656
|
+
originalLanguage: (data.language ?? data.masterLanguage ?? "").toLowerCase()
|
|
657
|
+
},
|
|
658
|
+
tableType: {
|
|
659
|
+
accessMode: data.accessMode,
|
|
660
|
+
rowType: data.rowTypeRef?.name,
|
|
661
|
+
primaryKey: data.primaryKey,
|
|
662
|
+
secondaryKeys: data.secondaryKeys
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
},
|
|
666
|
+
fromMetadata: (meta) => ({
|
|
667
|
+
name: "",
|
|
668
|
+
description: meta?.header?.description,
|
|
669
|
+
language: meta?.header?.originalLanguage?.toUpperCase()
|
|
670
|
+
})
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
//#endregion
|
|
674
|
+
//#region src/lib/handlers/objects/fugr.ts
|
|
675
|
+
/**
|
|
676
|
+
* FUGR (function group) handler for gCTS / AFF format.
|
|
677
|
+
*
|
|
678
|
+
* Projects ADK function group data to the AFF fugr-v1.json schema shape:
|
|
679
|
+
*
|
|
680
|
+
* {
|
|
681
|
+
* formatVersion: "1",
|
|
682
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
683
|
+
* fixPointArithmetic: boolean (required),
|
|
684
|
+
* status?
|
|
685
|
+
* }
|
|
686
|
+
*
|
|
687
|
+
* AFF's FUGR representation nests function modules under the group
|
|
688
|
+
* directory. This handler emits the group metadata plus per-function-module
|
|
689
|
+
* files when function module data is available:
|
|
690
|
+
* <fmname>.func.json — FM metadata (func-v1.json schema)
|
|
691
|
+
* <fmname>.func.abap — FM source
|
|
692
|
+
*/
|
|
693
|
+
function buildFuncMetadata(fm) {
|
|
694
|
+
const meta = {
|
|
695
|
+
formatVersion: "1",
|
|
696
|
+
header: { description: fm.description ?? fm.name ?? "" },
|
|
697
|
+
processingType: fm.processingType ?? "normal",
|
|
698
|
+
includeNumber: fm.includeNumber ?? "0000"
|
|
699
|
+
};
|
|
700
|
+
return JSON.stringify(meta, null, 2) + "\n";
|
|
701
|
+
}
|
|
702
|
+
const functionGroupHandler = createHandler(AdkFunctionGroup, {
|
|
703
|
+
toMetadata(fugr) {
|
|
704
|
+
const data = fugr.dataSync ?? {};
|
|
705
|
+
const lang = (data.language ?? data.masterLanguage ?? "").toLowerCase();
|
|
706
|
+
return {
|
|
707
|
+
formatVersion: "1",
|
|
708
|
+
header: {
|
|
709
|
+
description: fugr.description ?? "",
|
|
710
|
+
originalLanguage: lang
|
|
711
|
+
},
|
|
712
|
+
fixPointArithmetic: data.fixPointArithmetic === true
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
getSource: (fugr) => typeof fugr.getSource === "function" ? fugr.getSource() : "",
|
|
716
|
+
getExtraFiles(fugr) {
|
|
717
|
+
const fms = fugr.functionModules ?? [];
|
|
718
|
+
const files = [];
|
|
719
|
+
for (const fm of fms) {
|
|
720
|
+
const fmName = fm.name.toLowerCase();
|
|
721
|
+
files.push({
|
|
722
|
+
path: `${fmName}.func.json`,
|
|
723
|
+
content: buildFuncMetadata(fm)
|
|
724
|
+
});
|
|
725
|
+
if (fm.source) files.push({
|
|
726
|
+
path: `${fmName}.func.abap`,
|
|
727
|
+
content: fm.source
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
return files;
|
|
731
|
+
},
|
|
732
|
+
fromMetadata: (meta) => ({
|
|
733
|
+
name: "",
|
|
734
|
+
description: meta.header.description,
|
|
735
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
736
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
737
|
+
fixPointArithmetic: meta.fixPointArithmetic
|
|
738
|
+
})
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
//#endregion
|
|
742
|
+
//#region src/lib/handlers/objects/ddls.ts
|
|
743
|
+
/**
|
|
744
|
+
* DDLS (CDS view) handler for gCTS / AFF format.
|
|
745
|
+
*
|
|
746
|
+
* Source-driven: emits `<name>.ddls.asddls` (source) + `<name>.ddls.json`
|
|
747
|
+
* (metadata per ddls-v1.json schema).
|
|
748
|
+
*
|
|
749
|
+
* {
|
|
750
|
+
* formatVersion: "1",
|
|
751
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
752
|
+
* sourceOrigin: "abapDevelopmentTools" (required),
|
|
753
|
+
* sourceType: "unknown" (required),
|
|
754
|
+
* parentName?: string
|
|
755
|
+
* }
|
|
756
|
+
*/
|
|
757
|
+
const ddlSourceHandler = createHandler("DDLS", {
|
|
758
|
+
toMetadata(obj) {
|
|
759
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
760
|
+
return {
|
|
761
|
+
formatVersion: "1",
|
|
762
|
+
header: {
|
|
763
|
+
description: obj.description ?? obj.name ?? "",
|
|
764
|
+
originalLanguage: lang,
|
|
765
|
+
...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
|
|
766
|
+
},
|
|
767
|
+
sourceOrigin: obj.sourceOrigin ?? "abapDevelopmentTools",
|
|
768
|
+
sourceType: obj.sourceType ?? "unknown"
|
|
769
|
+
};
|
|
770
|
+
},
|
|
771
|
+
getSource: (obj) => typeof obj.getSource === "function" ? obj.getSource() : "",
|
|
772
|
+
fromMetadata: (meta) => ({
|
|
773
|
+
name: "",
|
|
774
|
+
description: meta.header.description,
|
|
775
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
776
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
777
|
+
sourceOrigin: meta.sourceOrigin,
|
|
778
|
+
sourceType: meta.sourceType
|
|
779
|
+
})
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
//#endregion
|
|
783
|
+
//#region src/lib/handlers/objects/dcls.ts
|
|
784
|
+
/**
|
|
785
|
+
* DCLS (CDS access control) handler for gCTS / AFF format.
|
|
786
|
+
*
|
|
787
|
+
* Source-driven: emits `<name>.dcls.asdcls` (source) + `<name>.dcls.json`
|
|
788
|
+
* (metadata per dcls-v1.json schema).
|
|
789
|
+
*
|
|
790
|
+
* {
|
|
791
|
+
* formatVersion: "1",
|
|
792
|
+
* header: { description, originalLanguage, abapLanguageVersion? }
|
|
793
|
+
* }
|
|
794
|
+
*/
|
|
795
|
+
const dclSourceHandler = createHandler("DCLS", {
|
|
796
|
+
toMetadata(obj) {
|
|
797
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
798
|
+
return {
|
|
799
|
+
formatVersion: "1",
|
|
800
|
+
header: {
|
|
801
|
+
description: obj.description ?? obj.name ?? "",
|
|
802
|
+
originalLanguage: lang,
|
|
803
|
+
...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
},
|
|
807
|
+
getSource: (obj) => typeof obj.getSource === "function" ? obj.getSource() : "",
|
|
808
|
+
fromMetadata: (meta) => ({
|
|
809
|
+
name: "",
|
|
810
|
+
description: meta.header.description,
|
|
811
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
812
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase()
|
|
813
|
+
})
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
//#endregion
|
|
817
|
+
//#region src/lib/handlers/objects/bdef.ts
|
|
818
|
+
/**
|
|
819
|
+
* BDEF (RAP behavior definition) handler for gCTS / AFF format.
|
|
820
|
+
*
|
|
821
|
+
* Source-driven: emits `<name>.bdef.abdl` (source) + `<name>.bdef.json`
|
|
822
|
+
* (metadata per bdef-v1.json schema).
|
|
823
|
+
*
|
|
824
|
+
* {
|
|
825
|
+
* formatVersion: "1",
|
|
826
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
827
|
+
* extendedBehaviorDefintion?: { name?: string }
|
|
828
|
+
* }
|
|
829
|
+
*/
|
|
830
|
+
const behaviorDefinitionHandler = createHandler("BDEF", {
|
|
831
|
+
toMetadata(obj) {
|
|
832
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
833
|
+
return {
|
|
834
|
+
formatVersion: "1",
|
|
835
|
+
header: {
|
|
836
|
+
description: obj.description ?? obj.name ?? "",
|
|
837
|
+
originalLanguage: lang,
|
|
838
|
+
...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
},
|
|
842
|
+
getSource: (obj) => typeof obj.getSource === "function" ? obj.getSource() : "",
|
|
843
|
+
fromMetadata: (meta) => ({
|
|
844
|
+
name: "",
|
|
845
|
+
description: meta.header.description,
|
|
846
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
847
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase()
|
|
848
|
+
})
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
//#endregion
|
|
852
|
+
//#region src/lib/handlers/objects/srvd.ts
|
|
853
|
+
/**
|
|
854
|
+
* SRVD (RAP service definition) handler for gCTS / AFF format.
|
|
855
|
+
*
|
|
856
|
+
* Source-driven: emits `<name>.srvd.acds` (source) + `<name>.srvd.json`
|
|
857
|
+
* (metadata per srvd-v1.json schema).
|
|
858
|
+
*
|
|
859
|
+
* {
|
|
860
|
+
* formatVersion: "1",
|
|
861
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
862
|
+
* generalInformation: { sourceOrigin, sourceType }
|
|
863
|
+
* }
|
|
864
|
+
*/
|
|
865
|
+
const serviceDefinitionHandler = createHandler("SRVD", {
|
|
866
|
+
toMetadata(obj) {
|
|
867
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
868
|
+
return {
|
|
869
|
+
formatVersion: "1",
|
|
870
|
+
header: {
|
|
871
|
+
description: obj.description ?? obj.name ?? "",
|
|
872
|
+
originalLanguage: lang,
|
|
873
|
+
...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
|
|
874
|
+
},
|
|
875
|
+
generalInformation: {
|
|
876
|
+
sourceOrigin: obj.sourceOrigin ?? "abapDevelopmentTools",
|
|
877
|
+
sourceType: obj.sourceType ?? "definition"
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
},
|
|
881
|
+
getSource: (obj) => typeof obj.getSource === "function" ? obj.getSource() : "",
|
|
882
|
+
fromMetadata: (meta) => ({
|
|
883
|
+
name: "",
|
|
884
|
+
description: meta.header.description,
|
|
885
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
886
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
887
|
+
sourceOrigin: meta.generalInformation.sourceOrigin,
|
|
888
|
+
sourceType: meta.generalInformation.sourceType
|
|
889
|
+
})
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
//#endregion
|
|
893
|
+
//#region src/lib/handlers/objects/srvb.ts
|
|
894
|
+
/**
|
|
895
|
+
* SRVB (RAP service binding) handler for gCTS / AFF format.
|
|
896
|
+
*
|
|
897
|
+
* Metadata-only (no source): emits `<name>.srvb.json` only
|
|
898
|
+
* (metadata per srvb-v1.json schema).
|
|
899
|
+
*
|
|
900
|
+
* {
|
|
901
|
+
* formatVersion: "1",
|
|
902
|
+
* header: { description, originalLanguage, abapLanguageVersion? },
|
|
903
|
+
* bindingType: string (required),
|
|
904
|
+
* bindingTypeCategory: "ui" | "webApi" (required),
|
|
905
|
+
* services: [{ name, versions: [{ serviceVersion, serviceDefinition }] }]
|
|
906
|
+
* }
|
|
907
|
+
*/
|
|
908
|
+
const serviceBindingHandler = createHandler("SRVB", {
|
|
909
|
+
toMetadata(obj) {
|
|
910
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
911
|
+
return {
|
|
912
|
+
formatVersion: "1",
|
|
913
|
+
header: {
|
|
914
|
+
description: obj.description ?? obj.name ?? "",
|
|
915
|
+
originalLanguage: lang,
|
|
916
|
+
...obj.abapLanguageVersion ? { abapLanguageVersion: obj.abapLanguageVersion } : {}
|
|
917
|
+
},
|
|
918
|
+
bindingType: obj.bindingType ?? "",
|
|
919
|
+
bindingTypeCategory: obj.bindingTypeCategory ?? "ui",
|
|
920
|
+
services: obj.services ?? []
|
|
921
|
+
};
|
|
922
|
+
},
|
|
923
|
+
fromMetadata: (meta) => ({
|
|
924
|
+
name: "",
|
|
925
|
+
description: meta.header.description,
|
|
926
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
927
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
928
|
+
bindingType: meta.bindingType,
|
|
929
|
+
bindingTypeCategory: meta.bindingTypeCategory,
|
|
930
|
+
services: meta.services
|
|
931
|
+
})
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
//#endregion
|
|
935
|
+
//#region src/lib/handlers/objects/msag.ts
|
|
936
|
+
/**
|
|
937
|
+
* MSAG (message class) handler for gCTS / AFF format.
|
|
938
|
+
*
|
|
939
|
+
* Metadata-only (no source): emits `<name>.msag.json` only
|
|
940
|
+
* (metadata per msag-v1.json schema).
|
|
941
|
+
*
|
|
942
|
+
* {
|
|
943
|
+
* formatVersion: "1",
|
|
944
|
+
* header: { description, originalLanguage },
|
|
945
|
+
* messages: [{ number?: string, text?: string }]
|
|
946
|
+
* }
|
|
947
|
+
*/
|
|
948
|
+
const messageClassHandler = createHandler("MSAG", {
|
|
949
|
+
toMetadata(obj) {
|
|
950
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
951
|
+
return {
|
|
952
|
+
formatVersion: "1",
|
|
953
|
+
header: {
|
|
954
|
+
description: obj.description ?? obj.name ?? "",
|
|
955
|
+
originalLanguage: lang
|
|
956
|
+
},
|
|
957
|
+
messages: obj.messages ?? []
|
|
958
|
+
};
|
|
959
|
+
},
|
|
960
|
+
fromMetadata: (meta) => ({
|
|
961
|
+
name: "",
|
|
962
|
+
description: meta.header.description,
|
|
963
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
964
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase(),
|
|
965
|
+
messages: meta.messages
|
|
966
|
+
})
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
//#endregion
|
|
970
|
+
//#region src/lib/handlers/objects/wave2-generic.ts
|
|
971
|
+
/**
|
|
972
|
+
* Generic AFF handler stubs for the remaining object types.
|
|
973
|
+
*
|
|
974
|
+
* Wave 2 mass-generates handlers for all AFF types that have JSON schemas
|
|
975
|
+
* but no dedicated handler. Each stub emits the minimal AFF contract:
|
|
976
|
+
* { formatVersion: "1", header: { description, originalLanguage } }
|
|
977
|
+
*
|
|
978
|
+
* Type-specific fields can be added later by promoting a stub to a
|
|
979
|
+
* dedicated handler file (like clas.ts, ddls.ts, etc.) when the ADK
|
|
980
|
+
* object model exposes richer data for that type.
|
|
981
|
+
*
|
|
982
|
+
* All types here are registered via the string-type form of
|
|
983
|
+
* `createHandler` — no ADK class required.
|
|
984
|
+
*/
|
|
985
|
+
function toMetadata(obj) {
|
|
986
|
+
const lang = (obj.originalLanguage ?? "").toLowerCase();
|
|
987
|
+
const meta = {
|
|
988
|
+
formatVersion: "1",
|
|
989
|
+
header: {
|
|
990
|
+
description: obj.description ?? obj.name ?? "",
|
|
991
|
+
originalLanguage: lang
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
if (obj.abapLanguageVersion) meta.header.abapLanguageVersion = obj.abapLanguageVersion;
|
|
995
|
+
return meta;
|
|
996
|
+
}
|
|
997
|
+
function fromMetadata(meta) {
|
|
998
|
+
return {
|
|
999
|
+
name: "",
|
|
1000
|
+
description: meta.header.description,
|
|
1001
|
+
language: meta.header.originalLanguage?.toUpperCase(),
|
|
1002
|
+
masterLanguage: meta.header.originalLanguage?.toUpperCase()
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function getSource(obj) {
|
|
1006
|
+
return typeof obj.getSource === "function" ? obj.getSource() : "";
|
|
1007
|
+
}
|
|
1008
|
+
for (const type of [
|
|
1009
|
+
"ADVC",
|
|
1010
|
+
"AIFA",
|
|
1011
|
+
"AIFD",
|
|
1012
|
+
"AIFF",
|
|
1013
|
+
"AIFI",
|
|
1014
|
+
"AIFN",
|
|
1015
|
+
"AIFP",
|
|
1016
|
+
"AIFR",
|
|
1017
|
+
"AOBJ",
|
|
1018
|
+
"APIC",
|
|
1019
|
+
"APLO",
|
|
1020
|
+
"APOB",
|
|
1021
|
+
"BGQC",
|
|
1022
|
+
"CDBO",
|
|
1023
|
+
"CFDF",
|
|
1024
|
+
"CHDO",
|
|
1025
|
+
"CHKC",
|
|
1026
|
+
"CHKO",
|
|
1027
|
+
"CHKV",
|
|
1028
|
+
"COTA",
|
|
1029
|
+
"CSNM",
|
|
1030
|
+
"DCAT",
|
|
1031
|
+
"DDLA",
|
|
1032
|
+
"DDLX",
|
|
1033
|
+
"DESD",
|
|
1034
|
+
"DMON",
|
|
1035
|
+
"DOBJ",
|
|
1036
|
+
"DRAS",
|
|
1037
|
+
"DRTY",
|
|
1038
|
+
"DSFD",
|
|
1039
|
+
"DSFI",
|
|
1040
|
+
"DTDC",
|
|
1041
|
+
"DTEB",
|
|
1042
|
+
"DTIX",
|
|
1043
|
+
"DTSC",
|
|
1044
|
+
"EDCC",
|
|
1045
|
+
"EDCK",
|
|
1046
|
+
"EDCR",
|
|
1047
|
+
"EDOI",
|
|
1048
|
+
"EEEC",
|
|
1049
|
+
"ENHO",
|
|
1050
|
+
"ENHS",
|
|
1051
|
+
"ENQU",
|
|
1052
|
+
"EVTB",
|
|
1053
|
+
"EVTO",
|
|
1054
|
+
"GSMP",
|
|
1055
|
+
"HTTP",
|
|
1056
|
+
"ILMB",
|
|
1057
|
+
"INTM",
|
|
1058
|
+
"INTS",
|
|
1059
|
+
"IWNG",
|
|
1060
|
+
"NONT",
|
|
1061
|
+
"NROB",
|
|
1062
|
+
"NTTA",
|
|
1063
|
+
"NTTY",
|
|
1064
|
+
"RONT",
|
|
1065
|
+
"RVBC",
|
|
1066
|
+
"SAIA",
|
|
1067
|
+
"SAJC",
|
|
1068
|
+
"SAJT",
|
|
1069
|
+
"SCP1",
|
|
1070
|
+
"SFPF",
|
|
1071
|
+
"SIAD",
|
|
1072
|
+
"SITO",
|
|
1073
|
+
"SMBC",
|
|
1074
|
+
"SMTG",
|
|
1075
|
+
"SPRV",
|
|
1076
|
+
"SRVC",
|
|
1077
|
+
"SUCO",
|
|
1078
|
+
"SUSI",
|
|
1079
|
+
"SWCR",
|
|
1080
|
+
"SXTG",
|
|
1081
|
+
"UIAD",
|
|
1082
|
+
"UIPG",
|
|
1083
|
+
"UIST"
|
|
1084
|
+
]) createHandler(type, {
|
|
1085
|
+
toMetadata,
|
|
1086
|
+
getSource,
|
|
1087
|
+
fromMetadata
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region src/lib/format/gcts-format.ts
|
|
1092
|
+
const gctsFormatPlugin = {
|
|
1093
|
+
id: "gcts",
|
|
1094
|
+
description: "gCTS / AFF (abap-file-formats) serialization format (JSON metadata + .abap/.acds sources)",
|
|
1095
|
+
get supportedTypes() {
|
|
1096
|
+
return getSupportedTypes();
|
|
1097
|
+
},
|
|
1098
|
+
getHandler(type) {
|
|
1099
|
+
return getHandler(type);
|
|
1100
|
+
},
|
|
1101
|
+
parseFilename(filename) {
|
|
1102
|
+
return parseGctsFilename(filename);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
//#endregion
|
|
1107
|
+
//#region src/lib/deserializer.ts
|
|
1108
|
+
/** Source-file extensions emitted by gCTS handlers. */
|
|
1109
|
+
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1110
|
+
"abap",
|
|
1111
|
+
"asddls",
|
|
1112
|
+
"asdcls"
|
|
1113
|
+
]);
|
|
1114
|
+
/**
|
|
1115
|
+
* Map numeric abapLanguageVersion codes (`'5'` etc.) to the descriptive
|
|
1116
|
+
* string the ADT REST API expects (`'cloudDevelopment'`). Mirrors the
|
|
1117
|
+
* helper in `adt-plugin-abapgit` — kept local so this package does not
|
|
1118
|
+
* depend on its sibling.
|
|
1119
|
+
*/
|
|
1120
|
+
const ABAP_LANG_VER_TO_ADT = {
|
|
1121
|
+
"2": "keyUser",
|
|
1122
|
+
"5": "cloudDevelopment"
|
|
1123
|
+
};
|
|
1124
|
+
function abapLangVerToAdt(code) {
|
|
1125
|
+
if (!code) return void 0;
|
|
1126
|
+
return ABAP_LANG_VER_TO_ADT[code] ?? code;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Deserialise a gCTS/AFF file tree into `AdkObject` instances.
|
|
1130
|
+
*
|
|
1131
|
+
* @param fileTree Virtual FS abstraction (checkin supplies a real-fs impl).
|
|
1132
|
+
* @param client `AdtClient` — required by `createAdk` to build ADK objects.
|
|
1133
|
+
* @param options Export options. `rootPackage` is assigned as packageRef
|
|
1134
|
+
* to every discovered object that lacks one, mirroring the
|
|
1135
|
+
* abapGit behaviour when `.abapgit.xml` is absent.
|
|
1136
|
+
*/
|
|
1137
|
+
async function* deserialize(fileTree, client, options) {
|
|
1138
|
+
const adk = createAdk(client);
|
|
1139
|
+
const jsonFiles = await fileTree.glob("**/*.json");
|
|
1140
|
+
const supportedTypes = new Set(getSupportedTypes().map((t) => t.toLowerCase()));
|
|
1141
|
+
const objectMap = /* @__PURE__ */ new Map();
|
|
1142
|
+
for (const jsonPath of jsonFiles) {
|
|
1143
|
+
const filename = jsonPath.split("/").pop();
|
|
1144
|
+
if (filename === "package.devc.json") continue;
|
|
1145
|
+
const parsed = parseGctsFilename(filename);
|
|
1146
|
+
if (!parsed) continue;
|
|
1147
|
+
if (parsed.extension !== "json") continue;
|
|
1148
|
+
if (!supportedTypes.has(parsed.type.toLowerCase())) continue;
|
|
1149
|
+
const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
|
|
1150
|
+
if (!objectMap.has(key)) objectMap.set(key, {
|
|
1151
|
+
name: parsed.name.toUpperCase(),
|
|
1152
|
+
type: parsed.type.toUpperCase(),
|
|
1153
|
+
sourceFiles: []
|
|
1154
|
+
});
|
|
1155
|
+
objectMap.get(key).metadataFile = jsonPath;
|
|
1156
|
+
}
|
|
1157
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
1158
|
+
const paths = await fileTree.glob(`**/*.${ext}`);
|
|
1159
|
+
for (const path of paths) {
|
|
1160
|
+
const filename = path.split("/").pop();
|
|
1161
|
+
const parsed = parseGctsFilename(filename);
|
|
1162
|
+
if (!parsed) continue;
|
|
1163
|
+
const key = `${parsed.name.toUpperCase()}:${parsed.type.toUpperCase()}`;
|
|
1164
|
+
const obj = objectMap.get(key);
|
|
1165
|
+
if (!obj) continue;
|
|
1166
|
+
obj.sourceFiles.push({
|
|
1167
|
+
path,
|
|
1168
|
+
suffix: parsed.suffix
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
for (const [, objFiles] of objectMap) {
|
|
1173
|
+
if (!objFiles.metadataFile) continue;
|
|
1174
|
+
const handler = getHandler(objFiles.type);
|
|
1175
|
+
if (!handler) continue;
|
|
1176
|
+
try {
|
|
1177
|
+
const raw = await fileTree.read(objFiles.metadataFile);
|
|
1178
|
+
const metadata = handler.schema.parse(raw);
|
|
1179
|
+
const sources = {};
|
|
1180
|
+
for (const { path, suffix } of objFiles.sourceFiles) {
|
|
1181
|
+
const content = await fileTree.read(path);
|
|
1182
|
+
const sourceKey = suffix ? handler.suffixToSourceKey?.[suffix] ?? suffix : "main";
|
|
1183
|
+
sources[sourceKey] = content;
|
|
1184
|
+
}
|
|
1185
|
+
const payload = handler.fromAbapGit ? handler.fromAbapGit(metadata) : { name: objFiles.name };
|
|
1186
|
+
const objectName = payload.name || objFiles.name;
|
|
1187
|
+
const fullData = {
|
|
1188
|
+
...payload,
|
|
1189
|
+
name: objectName
|
|
1190
|
+
};
|
|
1191
|
+
const adkType = typeof payload.type === "string" ? payload.type : objFiles.type;
|
|
1192
|
+
const adkObject = adk.getWithData(fullData, adkType);
|
|
1193
|
+
if (Object.keys(sources).length > 0) {
|
|
1194
|
+
if (handler.setSources) handler.setSources(adkObject, sources);
|
|
1195
|
+
else if (sources.main) adkObject._pendingSource = sources.main;
|
|
1196
|
+
}
|
|
1197
|
+
if (payload.description) adkObject._pendingDescription = payload.description;
|
|
1198
|
+
adkObject._relDir = objFiles.metadataFile.split("/").slice(0, -1).join("/");
|
|
1199
|
+
if (options?.rootPackage) {
|
|
1200
|
+
const data = adkObject._data;
|
|
1201
|
+
if (data && !data.packageRef) data.packageRef = { name: options.rootPackage };
|
|
1202
|
+
}
|
|
1203
|
+
if (options?.abapLanguageVersion) {
|
|
1204
|
+
const data = adkObject._data;
|
|
1205
|
+
if (data && !data.abapLanguageVersion) data.abapLanguageVersion = abapLangVerToAdt(options.abapLanguageVersion) ?? options.abapLanguageVersion;
|
|
1206
|
+
}
|
|
1207
|
+
yield adkObject;
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
console.error(`Failed to deserialize ${objFiles.type} ${objFiles.name}:`, error);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
//#endregion
|
|
1215
|
+
//#region src/lib/gcts-plugin.ts
|
|
1216
|
+
/**
|
|
1217
|
+
* gCTS AdtPlugin.
|
|
1218
|
+
*
|
|
1219
|
+
* The `FormatPlugin` in `format/gcts-format.ts` provides the declarative
|
|
1220
|
+
* contract (used by registry consumers); this `AdtPlugin` provides the
|
|
1221
|
+
* behaviour expected by `adt-cli`'s higher-level import/export services.
|
|
1222
|
+
*
|
|
1223
|
+
* SAP → disk ("import") serialises ADK objects to AFF JSON + source files.
|
|
1224
|
+
* Disk → SAP ("export" in adt-cli terminology — Git → SAP direction) is
|
|
1225
|
+
* delegated to `./deserializer.ts`, which yields AdkObjects for the
|
|
1226
|
+
* `CheckinService` apply pipeline (E08). Together they close the
|
|
1227
|
+
* round-trip that the E06 v0.1 follow-ups called out.
|
|
1228
|
+
*/
|
|
1229
|
+
const gctsPlugin = createPlugin({
|
|
1230
|
+
name: "gcts",
|
|
1231
|
+
version: "0.1.0",
|
|
1232
|
+
description: "gCTS / AFF format plugin — JSON metadata + .abap/.acds sources",
|
|
1233
|
+
registry: {
|
|
1234
|
+
isSupported,
|
|
1235
|
+
getSupportedTypes
|
|
1236
|
+
},
|
|
1237
|
+
format: {
|
|
1238
|
+
async import(object, targetPath, context) {
|
|
1239
|
+
try {
|
|
1240
|
+
const objPackage = object.type?.startsWith("DEVC") ? object.name : object.package || object.name || "ROOT";
|
|
1241
|
+
const packageDir = (await context.resolvePackagePath(objPackage)).map((p) => p.toLowerCase()).join("/");
|
|
1242
|
+
const handler = getHandler(object.type);
|
|
1243
|
+
if (!handler) return {
|
|
1244
|
+
success: false,
|
|
1245
|
+
filesCreated: [],
|
|
1246
|
+
errors: [`No gCTS handler registered for object type: ${object.type}`]
|
|
1247
|
+
};
|
|
1248
|
+
const files = await handler.serialize(object);
|
|
1249
|
+
const baseDir = join(targetPath, "src", packageDir);
|
|
1250
|
+
mkdirSync(baseDir, { recursive: true });
|
|
1251
|
+
const written = [];
|
|
1252
|
+
for (const f of files) {
|
|
1253
|
+
const full = join(baseDir, f.path);
|
|
1254
|
+
mkdirSync(dirname(full), { recursive: true });
|
|
1255
|
+
writeFileSync(full, f.content, f.encoding ?? "utf8");
|
|
1256
|
+
written.push(full);
|
|
1257
|
+
}
|
|
1258
|
+
return {
|
|
1259
|
+
success: true,
|
|
1260
|
+
filesCreated: written
|
|
1261
|
+
};
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
return {
|
|
1264
|
+
success: false,
|
|
1265
|
+
filesCreated: [],
|
|
1266
|
+
errors: [error instanceof Error ? error.message : String(error)]
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
1270
|
+
export: (fileTree, client, options) => deserialize(fileTree, client, options)
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
//#endregion
|
|
1275
|
+
//#region src/index.ts
|
|
1276
|
+
/**
|
|
1277
|
+
* @abapify/adt-plugin-gcts — gCTS / AFF format plugin entry point.
|
|
1278
|
+
*
|
|
1279
|
+
* Importing this module self-registers the plugin into the global
|
|
1280
|
+
* `FormatPlugin` registry so that `--format gcts` becomes available
|
|
1281
|
+
* throughout the `adt` CLI.
|
|
1282
|
+
*/
|
|
1283
|
+
registerFormatPlugin(gctsFormatPlugin);
|
|
1284
|
+
registerFormatPlugin({
|
|
1285
|
+
...gctsFormatPlugin,
|
|
1286
|
+
id: "aff"
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
//#endregion
|
|
1290
|
+
export { gctsPlugin as GctsPlugin, gctsPlugin as default, gctsPlugin, METADATA_EXTENSION, PACKAGE_FILENAME, adtUriToGctsPath, createHandler, gctsFilename, gctsFormatPlugin, getHandler, getSupportedTypes, isSupported, parseGctsFilename };
|
|
1291
|
+
//# sourceMappingURL=index.mjs.map
|