@abapify/adt-plugin-abapgit 0.4.1 → 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.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import { createPlugin, registerFormatPlugin } from "@abapify/adt-plugin";
1
+ import { FormatMaterializationError, createPlugin, registerFormatPlugin } from "@abapify/adt-plugin";
2
+ import { basename, dirname, isAbsolute, join, posix, relative, sep } from "node:path";
2
3
  import { AdkClass, AdkDataElement, AdkDomain, AdkFunctionGroup, AdkInterface, AdkPackage, AdkProgram, AdkStructure, AdkTable, AdkTableType, createAdk, getMainType, getTypeForKind } from "@abapify/adk";
3
4
  import { typedSchema } from "@abapify/ts-xsd";
4
5
  import { parse } from "@abapify/acds";
5
6
  import { tablesettings } from "@abapify/adt-schemas";
6
- import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
7
- import { basename, join, relative } from "path";
8
- import { createAbapGitResolver as createFindingResolver } from "@abapify/adt-atc";
7
+ import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
8
+ import { adtUriToAbapGitPath, createAbapGitResolver as createFindingResolver } from "@abapify/adt-atc";
9
9
 
10
10
  //#region src/lib/handlers/xml-format.ts
11
11
  /**
@@ -190,7 +190,8 @@ function createHandler(adkClassOrType, definition) {
190
190
  serializer: definition.serializer,
191
191
  serializer_version: definition.serializer_version
192
192
  };
193
- return formatAbapGitXml(definition.schema.build(fullPayload, { pretty: true }));
193
+ const xml = definition.schema.build(fullPayload, { pretty: true });
194
+ return formatAbapGitXml(xml);
194
195
  },
195
196
  createFile(path, content, encoding) {
196
197
  return {
@@ -212,37 +213,67 @@ function createHandler(adkClassOrType, definition) {
212
213
  };
213
214
  }
214
215
  };
215
- const defaultSerialize = async (object) => {
216
+ function resolveXmlFileName(object) {
217
+ if (definition.xmlFileName) return typeof definition.xmlFileName === "function" ? definition.xmlFileName(object, ctx) : definition.xmlFileName;
218
+ return `${ctx.getObjectName(object)}.${fileExtension}.xml`;
219
+ }
220
+ function createExplicitSourceFile(sourceKey, content, suffixBySourceKey, objectName) {
221
+ if (content === void 0) return void 0;
222
+ if (!suffixBySourceKey.has(sourceKey)) throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", `The ${type} format handler cannot map source component "${sourceKey}".`);
223
+ return ctx.createAbapFile(objectName, content, suffixBySourceKey.get(sourceKey));
224
+ }
225
+ function buildExplicitSourceFiles(object, sources) {
226
+ const sourceKeys = Object.keys(sources);
227
+ if (sourceKeys.length === 0) return [];
228
+ if (!definition.getSource && !definition.getSources) throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", `The ${type} format handler does not serialize source components.`);
229
+ const suffixBySourceKey = new Map([["main", void 0], ...Object.entries(definition.suffixToSourceKey ?? {}).map(([suffix, sourceKey]) => [sourceKey, suffix])]);
216
230
  const objectName = ctx.getObjectName(object);
217
231
  const files = [];
232
+ for (const sourceKey of sourceKeys.sort((left, right) => left.localeCompare(right, "en-US"))) {
233
+ const file = createExplicitSourceFile(sourceKey, sources[sourceKey], suffixBySourceKey, objectName);
234
+ if (file) files.push(file);
235
+ }
236
+ return files;
237
+ }
238
+ async function buildMutableSourceFiles(object) {
239
+ const objectName = ctx.getObjectName(object);
218
240
  if (definition.getSources) {
219
241
  const rawSources = definition.getSources(object);
220
- const sources = await Promise.all(rawSources.map(async ({ suffix, content }) => ({
242
+ return (await Promise.all(rawSources.map(async ({ suffix, content }) => ({
221
243
  suffix,
222
244
  content: await content
223
- })));
224
- for (const { suffix, content } of sources) if (content) files.push(ctx.createAbapFile(objectName, content, suffix));
225
- } else if (definition.getSource) {
245
+ })))).filter(({ content }) => content).map(({ suffix, content }) => ctx.createAbapFile(objectName, content, suffix));
246
+ }
247
+ if (definition.getSource) {
226
248
  const source = await definition.getSource(object);
227
- files.push(ctx.createAbapFile(objectName, source));
249
+ return source ? [ctx.createAbapFile(objectName, source)] : [];
228
250
  }
251
+ return [];
252
+ }
253
+ const defaultSerialize = async (object, options) => {
254
+ const sourceFiles = options?.sources !== void 0 ? buildExplicitSourceFiles(object, options.sources) : await buildMutableSourceFiles(object);
255
+ const xmlFileName = resolveXmlFileName(object);
229
256
  const xmlContent = ctx.toAbapGitXml(object);
230
- let xmlFileName;
231
- if (definition.xmlFileName) xmlFileName = typeof definition.xmlFileName === "function" ? definition.xmlFileName(object, ctx) : definition.xmlFileName;
232
- else xmlFileName = `${objectName}.${fileExtension}.xml`;
233
- files.push(ctx.createFile(xmlFileName, xmlContent));
234
- return files;
257
+ return [...sourceFiles, ctx.createFile(xmlFileName, xmlContent)];
235
258
  };
236
259
  const defaultFromAbapGit = definition.fromAbapGit ? (values) => {
237
260
  return definition.fromAbapGit(values);
238
261
  } : void 0;
262
+ const explicitSupports = definition.supportsExplicitSources;
263
+ const serializeAcceptsOptions = explicitSupports ?? (definition.serialize !== void 0 && definition.serialize.length >= 3);
264
+ const supportsExplicitSources = explicitSupports ?? (definition.getSource !== void 0 || definition.getSources !== void 0 || serializeAcceptsOptions);
239
265
  const handler = {
240
266
  type,
241
267
  fileExtension,
242
268
  schema: definition.schema,
243
269
  suffixToSourceKey: definition.suffixToSourceKey,
244
- serialize: definition.serialize ? (object) => definition.serialize(object, ctx) : defaultSerialize,
270
+ supportsExplicitSources,
271
+ serialize: definition.serialize ? async (object, options) => {
272
+ if (serializeAcceptsOptions) return await definition.serialize(object, ctx, options);
273
+ return await definition.serialize(object, ctx);
274
+ } : defaultSerialize,
245
275
  fromAbapGit: defaultFromAbapGit,
276
+ fromAffJson: definition.fromAffJson,
246
277
  setSources: definition.setSources
247
278
  };
248
279
  handlerRegistry.set(type, handler);
@@ -2358,89 +2389,1718 @@ var srvb_default = {
2358
2389
  };
2359
2390
 
2360
2391
  //#endregion
2361
- //#region src/schemas/generated/index.ts
2392
+ //#region src/schemas/generated/schemas/dcls.ts
2362
2393
  /**
2363
- * AbapGit Typed Schemas
2364
- *
2365
- * Auto-generated by ts-xsd codegen - DO NOT EDIT
2366
- *
2367
- * Each schema provides:
2368
- * - _type: Full AbapGitType (for XML build/parse)
2369
- * - _values: Inner values type (for handler mapping)
2370
- * - parse(xml) → fully typed object
2371
- * - build(data) → XML string
2372
- * - schema → raw schema literal
2394
+ * Auto-generated schema from XSD
2395
+ *
2396
+ * DO NOT EDIT - Generated by ts-xsd codegen
2397
+ * Source: abapgit/dcls.xsd
2373
2398
  */
2374
- const clas = abapGitSchema(clas_default);
2375
- const devc = abapGitSchema(devc_default);
2376
- const doma = abapGitSchema(doma_default);
2377
- const dtel = abapGitSchema(dtel_default);
2378
- const intf = abapGitSchema(intf_default);
2379
- const prog = abapGitSchema(prog_default);
2380
- const fugr = abapGitSchema(fugr_default);
2381
- const tabl = abapGitSchema(tabl_default);
2382
- const ttyp = abapGitSchema(ttyp_default);
2383
- const bdef = abapGitSchema(bdef_default);
2384
- const srvd = abapGitSchema(srvd_default);
2385
- const srvb = abapGitSchema(srvb_default);
2399
+ var dcls_default = {
2400
+ $xmlns: {
2401
+ xs: "http://www.w3.org/2001/XMLSchema",
2402
+ asx: "http://www.sap.com/abapxml"
2403
+ },
2404
+ elementFormDefault: "unqualified",
2405
+ element: [{
2406
+ name: "abapGit",
2407
+ complexType: {
2408
+ sequence: { element: [{
2409
+ name: "abap",
2410
+ type: "DclsAbapType"
2411
+ }] },
2412
+ attribute: [
2413
+ {
2414
+ name: "version",
2415
+ type: "xs:string",
2416
+ use: "required"
2417
+ },
2418
+ {
2419
+ name: "serializer",
2420
+ type: "xs:string",
2421
+ use: "required"
2422
+ },
2423
+ {
2424
+ name: "serializer_version",
2425
+ type: "xs:string",
2426
+ use: "required"
2427
+ }
2428
+ ]
2429
+ }
2430
+ }, {
2431
+ name: "Schema",
2432
+ abstract: true
2433
+ }],
2434
+ complexType: [
2435
+ {
2436
+ name: "SkeyType",
2437
+ all: { element: [{
2438
+ name: "TYPE",
2439
+ type: "xs:string",
2440
+ minOccurs: "0"
2441
+ }, {
2442
+ name: "NAME",
2443
+ type: "xs:string",
2444
+ minOccurs: "0"
2445
+ }] }
2446
+ },
2447
+ {
2448
+ name: "DclsValuesType",
2449
+ all: { element: [{
2450
+ name: "SKEY",
2451
+ type: "SkeyType",
2452
+ minOccurs: "0"
2453
+ }] }
2454
+ },
2455
+ {
2456
+ name: "DclsAbapType",
2457
+ sequence: { element: [{
2458
+ name: "values",
2459
+ type: "DclsValuesType"
2460
+ }] },
2461
+ attribute: [{
2462
+ name: "version",
2463
+ type: "xs:string",
2464
+ "default": "1.0"
2465
+ }]
2466
+ },
2467
+ {
2468
+ name: "AbapValuesType",
2469
+ sequence: { element: [{
2470
+ ref: "asx:Schema",
2471
+ minOccurs: "0",
2472
+ maxOccurs: "unbounded"
2473
+ }] }
2474
+ },
2475
+ {
2476
+ name: "AbapType",
2477
+ sequence: { element: [{ ref: "asx:values" }] },
2478
+ attribute: [{
2479
+ name: "version",
2480
+ type: "xs:string",
2481
+ "default": "1.0"
2482
+ }]
2483
+ }
2484
+ ]
2485
+ };
2386
2486
 
2387
2487
  //#endregion
2388
- //#region src/lib/handlers/lang.ts
2488
+ //#region src/schemas/generated/schemas/ddls.ts
2389
2489
  /**
2390
- * SAP 1-character language code ↔ ISO 639-1 mapping
2490
+ * Auto-generated schema from XSD
2391
2491
  *
2392
- * abapGit stores SAP internal 1-char codes (e.g. "E" for English).
2393
- * SAP ADT REST API expects ISO 639-1 codes (e.g. "EN").
2492
+ * DO NOT EDIT - Generated by ts-xsd codegen
2493
+ * Source: abapgit/ddls.xsd
2394
2494
  */
2395
- const SAP_TO_ISO = {
2396
- E: "EN",
2397
- D: "DE",
2398
- F: "FR",
2399
- I: "IT",
2400
- S: "ES",
2401
- P: "PT",
2402
- J: "JA",
2403
- K: "KO",
2404
- "1": "ZH",
2405
- W: "RU",
2406
- T: "TR",
2407
- N: "NL",
2408
- H: "HU",
2409
- L: "PL",
2410
- Q: "CS",
2411
- R: "SK",
2412
- U: "SV",
2413
- V: "DA",
2414
- Y: "FI",
2415
- X: "NO",
2416
- G: "EL",
2417
- B: "HE",
2418
- A: "AR",
2419
- "3": "TH",
2420
- M: "UK",
2421
- "6": "HR",
2422
- O: "SL",
2423
- "7": "BG",
2424
- "8": "SR",
2425
- C: "RO"
2495
+ var ddls_default = {
2496
+ $xmlns: {
2497
+ xs: "http://www.w3.org/2001/XMLSchema",
2498
+ asx: "http://www.sap.com/abapxml"
2499
+ },
2500
+ elementFormDefault: "unqualified",
2501
+ element: [{
2502
+ name: "abapGit",
2503
+ complexType: {
2504
+ sequence: { element: [{
2505
+ name: "abap",
2506
+ type: "DdlsAbapType"
2507
+ }] },
2508
+ attribute: [
2509
+ {
2510
+ name: "version",
2511
+ type: "xs:string",
2512
+ use: "required"
2513
+ },
2514
+ {
2515
+ name: "serializer",
2516
+ type: "xs:string",
2517
+ use: "required"
2518
+ },
2519
+ {
2520
+ name: "serializer_version",
2521
+ type: "xs:string",
2522
+ use: "required"
2523
+ }
2524
+ ]
2525
+ }
2526
+ }, {
2527
+ name: "Schema",
2528
+ abstract: true
2529
+ }],
2530
+ complexType: [
2531
+ {
2532
+ name: "SkeyType",
2533
+ all: { element: [{
2534
+ name: "TYPE",
2535
+ type: "xs:string",
2536
+ minOccurs: "0"
2537
+ }, {
2538
+ name: "NAME",
2539
+ type: "xs:string",
2540
+ minOccurs: "0"
2541
+ }] }
2542
+ },
2543
+ {
2544
+ name: "DdlsValuesType",
2545
+ all: { element: [{
2546
+ name: "SKEY",
2547
+ type: "SkeyType",
2548
+ minOccurs: "0"
2549
+ }] }
2550
+ },
2551
+ {
2552
+ name: "DdlsAbapType",
2553
+ sequence: { element: [{
2554
+ name: "values",
2555
+ type: "DdlsValuesType"
2556
+ }] },
2557
+ attribute: [{
2558
+ name: "version",
2559
+ type: "xs:string",
2560
+ "default": "1.0"
2561
+ }]
2562
+ },
2563
+ {
2564
+ name: "AbapValuesType",
2565
+ sequence: { element: [{
2566
+ ref: "asx:Schema",
2567
+ minOccurs: "0",
2568
+ maxOccurs: "unbounded"
2569
+ }] }
2570
+ },
2571
+ {
2572
+ name: "AbapType",
2573
+ sequence: { element: [{ ref: "asx:values" }] },
2574
+ attribute: [{
2575
+ name: "version",
2576
+ type: "xs:string",
2577
+ "default": "1.0"
2578
+ }]
2579
+ }
2580
+ ]
2426
2581
  };
2427
- const ISO_TO_SAP = Object.fromEntries(Object.entries(SAP_TO_ISO).map(([k, v]) => [v, k]));
2428
- /**
2429
- * Convert SAP 1-char language code to ISO 639-1.
2430
- * Returns the input uppercased if no mapping exists (may already be ISO).
2431
- */
2432
- function sapLangToIso(sapLang) {
2433
- if (!sapLang) return "EN";
2434
- const upper = sapLang.toUpperCase();
2435
- if (upper.length >= 2) return upper;
2436
- return SAP_TO_ISO[upper] ?? upper;
2437
- }
2582
+
2583
+ //#endregion
2584
+ //#region src/schemas/generated/schemas/ddlx.ts
2438
2585
  /**
2439
- * Convert ISO 639-1 language code to SAP 1-char code.
2440
- * Returns "E" (English) if no mapping exists.
2586
+ * Auto-generated schema from XSD
2587
+ *
2588
+ * DO NOT EDIT - Generated by ts-xsd codegen
2589
+ * Source: abapgit/ddlx.xsd
2441
2590
  */
2442
- function isoToSapLang(isoLang) {
2443
- if (!isoLang) return "E";
2591
+ var ddlx_default = {
2592
+ $xmlns: {
2593
+ xs: "http://www.w3.org/2001/XMLSchema",
2594
+ asx: "http://www.sap.com/abapxml"
2595
+ },
2596
+ elementFormDefault: "unqualified",
2597
+ element: [{
2598
+ name: "abapGit",
2599
+ complexType: {
2600
+ sequence: { element: [{
2601
+ name: "abap",
2602
+ type: "DdlxAbapType"
2603
+ }] },
2604
+ attribute: [
2605
+ {
2606
+ name: "version",
2607
+ type: "xs:string",
2608
+ use: "required"
2609
+ },
2610
+ {
2611
+ name: "serializer",
2612
+ type: "xs:string",
2613
+ use: "required"
2614
+ },
2615
+ {
2616
+ name: "serializer_version",
2617
+ type: "xs:string",
2618
+ use: "required"
2619
+ }
2620
+ ]
2621
+ }
2622
+ }, {
2623
+ name: "Schema",
2624
+ abstract: true
2625
+ }],
2626
+ complexType: [
2627
+ {
2628
+ name: "SkeyType",
2629
+ all: { element: [{
2630
+ name: "TYPE",
2631
+ type: "xs:string",
2632
+ minOccurs: "0"
2633
+ }, {
2634
+ name: "NAME",
2635
+ type: "xs:string",
2636
+ minOccurs: "0"
2637
+ }] }
2638
+ },
2639
+ {
2640
+ name: "DdlxValuesType",
2641
+ all: { element: [{
2642
+ name: "SKEY",
2643
+ type: "SkeyType",
2644
+ minOccurs: "0"
2645
+ }] }
2646
+ },
2647
+ {
2648
+ name: "DdlxAbapType",
2649
+ sequence: { element: [{
2650
+ name: "values",
2651
+ type: "DdlxValuesType"
2652
+ }] },
2653
+ attribute: [{
2654
+ name: "version",
2655
+ type: "xs:string",
2656
+ "default": "1.0"
2657
+ }]
2658
+ },
2659
+ {
2660
+ name: "AbapValuesType",
2661
+ sequence: { element: [{
2662
+ ref: "asx:Schema",
2663
+ minOccurs: "0",
2664
+ maxOccurs: "unbounded"
2665
+ }] }
2666
+ },
2667
+ {
2668
+ name: "AbapType",
2669
+ sequence: { element: [{ ref: "asx:values" }] },
2670
+ attribute: [{
2671
+ name: "version",
2672
+ type: "xs:string",
2673
+ "default": "1.0"
2674
+ }]
2675
+ }
2676
+ ]
2677
+ };
2678
+
2679
+ //#endregion
2680
+ //#region src/schemas/generated/schemas/msag.ts
2681
+ /**
2682
+ * Auto-generated schema from XSD
2683
+ *
2684
+ * DO NOT EDIT - Generated by ts-xsd codegen
2685
+ * Source: abapgit/msag.xsd
2686
+ */
2687
+ var msag_default = {
2688
+ $xmlns: {
2689
+ xs: "http://www.w3.org/2001/XMLSchema",
2690
+ asx: "http://www.sap.com/abapxml"
2691
+ },
2692
+ targetNamespace: "http://www.sap.com/abapxml",
2693
+ elementFormDefault: "unqualified",
2694
+ element: [
2695
+ {
2696
+ name: "abapGit",
2697
+ complexType: {
2698
+ sequence: { element: [{ ref: "asx:abap" }] },
2699
+ attribute: [
2700
+ {
2701
+ name: "version",
2702
+ type: "xs:string",
2703
+ use: "required"
2704
+ },
2705
+ {
2706
+ name: "serializer",
2707
+ type: "xs:string",
2708
+ use: "required"
2709
+ },
2710
+ {
2711
+ name: "serializer_version",
2712
+ type: "xs:string",
2713
+ use: "required"
2714
+ }
2715
+ ]
2716
+ }
2717
+ },
2718
+ {
2719
+ name: "Schema",
2720
+ abstract: true
2721
+ },
2722
+ {
2723
+ name: "values",
2724
+ type: "asx:AbapValuesType"
2725
+ },
2726
+ {
2727
+ name: "abap",
2728
+ type: "asx:AbapType"
2729
+ }
2730
+ ],
2731
+ complexType: [
2732
+ {
2733
+ name: "AbapValuesType",
2734
+ all: { element: [{
2735
+ name: "T100A",
2736
+ type: "asx:T100aType",
2737
+ minOccurs: "0"
2738
+ }, {
2739
+ name: "T100",
2740
+ type: "asx:T100TabType",
2741
+ minOccurs: "0"
2742
+ }] }
2743
+ },
2744
+ {
2745
+ name: "T100aType",
2746
+ all: { element: [
2747
+ {
2748
+ name: "ARBGB",
2749
+ type: "xs:string"
2750
+ },
2751
+ {
2752
+ name: "MASTERLANG",
2753
+ type: "xs:string",
2754
+ minOccurs: "0"
2755
+ },
2756
+ {
2757
+ name: "STEXT",
2758
+ type: "xs:string",
2759
+ minOccurs: "0"
2760
+ }
2761
+ ] }
2762
+ },
2763
+ {
2764
+ name: "T100Type",
2765
+ sequence: { element: [
2766
+ {
2767
+ name: "SPRSL",
2768
+ type: "xs:string",
2769
+ minOccurs: "0"
2770
+ },
2771
+ {
2772
+ name: "ARBGB",
2773
+ type: "xs:string",
2774
+ minOccurs: "0"
2775
+ },
2776
+ {
2777
+ name: "MSGNR",
2778
+ type: "xs:string",
2779
+ minOccurs: "0"
2780
+ },
2781
+ {
2782
+ name: "TEXT",
2783
+ type: "xs:string",
2784
+ minOccurs: "0"
2785
+ }
2786
+ ] }
2787
+ },
2788
+ {
2789
+ name: "T100TabType",
2790
+ sequence: { element: [{
2791
+ name: "item",
2792
+ type: "T100Type",
2793
+ minOccurs: "0",
2794
+ maxOccurs: "unbounded"
2795
+ }] }
2796
+ },
2797
+ {
2798
+ name: "AbapType",
2799
+ sequence: { element: [{ ref: "asx:values" }] },
2800
+ attribute: [{
2801
+ name: "version",
2802
+ type: "xs:string",
2803
+ "default": "1.0"
2804
+ }]
2805
+ }
2806
+ ]
2807
+ };
2808
+
2809
+ //#endregion
2810
+ //#region src/schemas/generated/schemas/view.ts
2811
+ /**
2812
+ * Auto-generated schema from XSD
2813
+ *
2814
+ * DO NOT EDIT - Generated by ts-xsd codegen
2815
+ * Source: abapgit/view.xsd
2816
+ */
2817
+ var view_default = {
2818
+ $xmlns: {
2819
+ xs: "http://www.w3.org/2001/XMLSchema",
2820
+ asx: "http://www.sap.com/abapxml"
2821
+ },
2822
+ targetNamespace: "http://www.sap.com/abapxml",
2823
+ elementFormDefault: "unqualified",
2824
+ element: [
2825
+ {
2826
+ name: "abapGit",
2827
+ complexType: {
2828
+ sequence: { element: [{ ref: "asx:abap" }] },
2829
+ attribute: [
2830
+ {
2831
+ name: "version",
2832
+ type: "xs:string",
2833
+ use: "required"
2834
+ },
2835
+ {
2836
+ name: "serializer",
2837
+ type: "xs:string",
2838
+ use: "required"
2839
+ },
2840
+ {
2841
+ name: "serializer_version",
2842
+ type: "xs:string",
2843
+ use: "required"
2844
+ }
2845
+ ]
2846
+ }
2847
+ },
2848
+ {
2849
+ name: "Schema",
2850
+ abstract: true
2851
+ },
2852
+ {
2853
+ name: "values",
2854
+ type: "asx:AbapValuesType"
2855
+ },
2856
+ {
2857
+ name: "abap",
2858
+ type: "asx:AbapType"
2859
+ }
2860
+ ],
2861
+ complexType: [
2862
+ {
2863
+ name: "AbapValuesType",
2864
+ all: { element: [
2865
+ {
2866
+ name: "DD25V",
2867
+ type: "asx:Dd25vType",
2868
+ minOccurs: "0"
2869
+ },
2870
+ {
2871
+ name: "DD26V_TABLE",
2872
+ type: "asx:Dd26vTabType",
2873
+ minOccurs: "0"
2874
+ },
2875
+ {
2876
+ name: "DD27P_TABLE",
2877
+ type: "asx:Dd27pTabType",
2878
+ minOccurs: "0"
2879
+ },
2880
+ {
2881
+ name: "DD28V_TABLE",
2882
+ type: "asx:Dd28vTabType",
2883
+ minOccurs: "0"
2884
+ }
2885
+ ] }
2886
+ },
2887
+ {
2888
+ name: "Dd25vType",
2889
+ all: { element: [
2890
+ {
2891
+ name: "VIEWNAME",
2892
+ type: "xs:string"
2893
+ },
2894
+ {
2895
+ name: "DDLANGUAGE",
2896
+ type: "xs:string",
2897
+ minOccurs: "0"
2898
+ },
2899
+ {
2900
+ name: "DDTEXT",
2901
+ type: "xs:string",
2902
+ minOccurs: "0"
2903
+ },
2904
+ {
2905
+ name: "VIEWCLASS",
2906
+ type: "xs:string",
2907
+ minOccurs: "0"
2908
+ },
2909
+ {
2910
+ name: "ROOTTAB",
2911
+ type: "xs:string",
2912
+ minOccurs: "0"
2913
+ },
2914
+ {
2915
+ name: "ROOTFIELD",
2916
+ type: "xs:string",
2917
+ minOccurs: "0"
2918
+ },
2919
+ {
2920
+ name: "ACTFLAG",
2921
+ type: "xs:string",
2922
+ minOccurs: "0"
2923
+ },
2924
+ {
2925
+ name: "MULTITAB",
2926
+ type: "xs:string",
2927
+ minOccurs: "0"
2928
+ }
2929
+ ] }
2930
+ },
2931
+ {
2932
+ name: "Dd26vType",
2933
+ sequence: { element: [
2934
+ {
2935
+ name: "VIEWNAME",
2936
+ type: "xs:string",
2937
+ minOccurs: "0"
2938
+ },
2939
+ {
2940
+ name: "TABNAME",
2941
+ type: "xs:string",
2942
+ minOccurs: "0"
2943
+ },
2944
+ {
2945
+ name: "TABPOS",
2946
+ type: "xs:string",
2947
+ minOccurs: "0"
2948
+ },
2949
+ {
2950
+ name: "FVIEWNAME",
2951
+ type: "xs:string",
2952
+ minOccurs: "0"
2953
+ },
2954
+ {
2955
+ name: "FFIELDNAME",
2956
+ type: "xs:string",
2957
+ minOccurs: "0"
2958
+ },
2959
+ {
2960
+ name: "READONLY",
2961
+ type: "xs:string",
2962
+ minOccurs: "0"
2963
+ }
2964
+ ] }
2965
+ },
2966
+ {
2967
+ name: "Dd26vTabType",
2968
+ sequence: { element: [{
2969
+ name: "item",
2970
+ type: "Dd26vType",
2971
+ minOccurs: "0",
2972
+ maxOccurs: "unbounded"
2973
+ }] }
2974
+ },
2975
+ {
2976
+ name: "Dd27pType",
2977
+ sequence: { element: [
2978
+ {
2979
+ name: "VIEWNAME",
2980
+ type: "xs:string",
2981
+ minOccurs: "0"
2982
+ },
2983
+ {
2984
+ name: "VIEWFIELD",
2985
+ type: "xs:string",
2986
+ minOccurs: "0"
2987
+ },
2988
+ {
2989
+ name: "TABNAME",
2990
+ type: "xs:string",
2991
+ minOccurs: "0"
2992
+ },
2993
+ {
2994
+ name: "FIELDNAME",
2995
+ type: "xs:string",
2996
+ minOccurs: "0"
2997
+ },
2998
+ {
2999
+ name: "KEYFLAG",
3000
+ type: "xs:string",
3001
+ minOccurs: "0"
3002
+ },
3003
+ {
3004
+ name: "READONLY",
3005
+ type: "xs:string",
3006
+ minOccurs: "0"
3007
+ },
3008
+ {
3009
+ name: "ROLLNAME",
3010
+ type: "xs:string",
3011
+ minOccurs: "0"
3012
+ },
3013
+ {
3014
+ name: "CHECKTABLE",
3015
+ type: "xs:string",
3016
+ minOccurs: "0"
3017
+ },
3018
+ {
3019
+ name: "LOGFLAG",
3020
+ type: "xs:string",
3021
+ minOccurs: "0"
3022
+ },
3023
+ {
3024
+ name: "REFTABLE",
3025
+ type: "xs:string",
3026
+ minOccurs: "0"
3027
+ },
3028
+ {
3029
+ name: "REFFIELD",
3030
+ type: "xs:string",
3031
+ minOccurs: "0"
3032
+ },
3033
+ {
3034
+ name: "OUTPUTLEN",
3035
+ type: "xs:string",
3036
+ minOccurs: "0"
3037
+ },
3038
+ {
3039
+ name: "CONVEXIT",
3040
+ type: "xs:string",
3041
+ minOccurs: "0"
3042
+ },
3043
+ {
3044
+ name: "DDTEXT",
3045
+ type: "xs:string",
3046
+ minOccurs: "0"
3047
+ }
3048
+ ] }
3049
+ },
3050
+ {
3051
+ name: "Dd27pTabType",
3052
+ sequence: { element: [{
3053
+ name: "item",
3054
+ type: "Dd27pType",
3055
+ minOccurs: "0",
3056
+ maxOccurs: "unbounded"
3057
+ }] }
3058
+ },
3059
+ {
3060
+ name: "Dd28vType",
3061
+ sequence: { element: [
3062
+ {
3063
+ name: "VIEWNAME",
3064
+ type: "xs:string",
3065
+ minOccurs: "0"
3066
+ },
3067
+ {
3068
+ name: "ANDOR",
3069
+ type: "xs:string",
3070
+ minOccurs: "0"
3071
+ },
3072
+ {
3073
+ name: "LEFTBRACKET",
3074
+ type: "xs:string",
3075
+ minOccurs: "0"
3076
+ },
3077
+ {
3078
+ name: "FIELDNAME",
3079
+ type: "xs:string",
3080
+ minOccurs: "0"
3081
+ },
3082
+ {
3083
+ name: "OPERATOR",
3084
+ type: "xs:string",
3085
+ minOccurs: "0"
3086
+ },
3087
+ {
3088
+ name: "CONST_VALUE",
3089
+ type: "xs:string",
3090
+ minOccurs: "0"
3091
+ },
3092
+ {
3093
+ name: "RIGHTBRACKET",
3094
+ type: "xs:string",
3095
+ minOccurs: "0"
3096
+ }
3097
+ ] }
3098
+ },
3099
+ {
3100
+ name: "Dd28vTabType",
3101
+ sequence: { element: [{
3102
+ name: "item",
3103
+ type: "Dd28vType",
3104
+ minOccurs: "0",
3105
+ maxOccurs: "unbounded"
3106
+ }] }
3107
+ },
3108
+ {
3109
+ name: "AbapType",
3110
+ sequence: { element: [{ ref: "asx:values" }] },
3111
+ attribute: [{
3112
+ name: "version",
3113
+ type: "xs:string",
3114
+ "default": "1.0"
3115
+ }]
3116
+ }
3117
+ ]
3118
+ };
3119
+
3120
+ //#endregion
3121
+ //#region src/schemas/generated/schemas/enqu.ts
3122
+ /**
3123
+ * Auto-generated schema from XSD
3124
+ *
3125
+ * DO NOT EDIT - Generated by ts-xsd codegen
3126
+ * Source: abapgit/enqu.xsd
3127
+ */
3128
+ var enqu_default = {
3129
+ $xmlns: {
3130
+ xs: "http://www.w3.org/2001/XMLSchema",
3131
+ asx: "http://www.sap.com/abapxml"
3132
+ },
3133
+ targetNamespace: "http://www.sap.com/abapxml",
3134
+ elementFormDefault: "unqualified",
3135
+ element: [
3136
+ {
3137
+ name: "abapGit",
3138
+ complexType: {
3139
+ sequence: { element: [{ ref: "asx:abap" }] },
3140
+ attribute: [
3141
+ {
3142
+ name: "version",
3143
+ type: "xs:string",
3144
+ use: "required"
3145
+ },
3146
+ {
3147
+ name: "serializer",
3148
+ type: "xs:string",
3149
+ use: "required"
3150
+ },
3151
+ {
3152
+ name: "serializer_version",
3153
+ type: "xs:string",
3154
+ use: "required"
3155
+ }
3156
+ ]
3157
+ }
3158
+ },
3159
+ {
3160
+ name: "Schema",
3161
+ abstract: true
3162
+ },
3163
+ {
3164
+ name: "values",
3165
+ type: "asx:AbapValuesType"
3166
+ },
3167
+ {
3168
+ name: "abap",
3169
+ type: "asx:AbapType"
3170
+ }
3171
+ ],
3172
+ complexType: [
3173
+ {
3174
+ name: "AbapValuesType",
3175
+ all: { element: [
3176
+ {
3177
+ name: "DD25V",
3178
+ type: "asx:Dd25vType",
3179
+ minOccurs: "0"
3180
+ },
3181
+ {
3182
+ name: "DD26V_TABLE",
3183
+ type: "asx:Dd26vTabType",
3184
+ minOccurs: "0"
3185
+ },
3186
+ {
3187
+ name: "DD27P_TABLE",
3188
+ type: "asx:Dd27pTabType",
3189
+ minOccurs: "0"
3190
+ }
3191
+ ] }
3192
+ },
3193
+ {
3194
+ name: "Dd25vType",
3195
+ all: { element: [
3196
+ {
3197
+ name: "VIEWNAME",
3198
+ type: "xs:string"
3199
+ },
3200
+ {
3201
+ name: "DDLANGUAGE",
3202
+ type: "xs:string",
3203
+ minOccurs: "0"
3204
+ },
3205
+ {
3206
+ name: "DDTEXT",
3207
+ type: "xs:string",
3208
+ minOccurs: "0"
3209
+ },
3210
+ {
3211
+ name: "VIEWCLASS",
3212
+ type: "xs:string",
3213
+ minOccurs: "0"
3214
+ },
3215
+ {
3216
+ name: "ROOTTAB",
3217
+ type: "xs:string",
3218
+ minOccurs: "0"
3219
+ },
3220
+ {
3221
+ name: "ROOTFIELD",
3222
+ type: "xs:string",
3223
+ minOccurs: "0"
3224
+ },
3225
+ {
3226
+ name: "ACTFLAG",
3227
+ type: "xs:string",
3228
+ minOccurs: "0"
3229
+ },
3230
+ {
3231
+ name: "MULTITAB",
3232
+ type: "xs:string",
3233
+ minOccurs: "0"
3234
+ }
3235
+ ] }
3236
+ },
3237
+ {
3238
+ name: "Dd26vType",
3239
+ sequence: { element: [
3240
+ {
3241
+ name: "VIEWNAME",
3242
+ type: "xs:string",
3243
+ minOccurs: "0"
3244
+ },
3245
+ {
3246
+ name: "TABNAME",
3247
+ type: "xs:string",
3248
+ minOccurs: "0"
3249
+ },
3250
+ {
3251
+ name: "TABPOS",
3252
+ type: "xs:string",
3253
+ minOccurs: "0"
3254
+ },
3255
+ {
3256
+ name: "FVIEWNAME",
3257
+ type: "xs:string",
3258
+ minOccurs: "0"
3259
+ },
3260
+ {
3261
+ name: "FFIELDNAME",
3262
+ type: "xs:string",
3263
+ minOccurs: "0"
3264
+ },
3265
+ {
3266
+ name: "READONLY",
3267
+ type: "xs:string",
3268
+ minOccurs: "0"
3269
+ }
3270
+ ] }
3271
+ },
3272
+ {
3273
+ name: "Dd26vTabType",
3274
+ sequence: { element: [{
3275
+ name: "item",
3276
+ type: "Dd26vType",
3277
+ minOccurs: "0",
3278
+ maxOccurs: "unbounded"
3279
+ }] }
3280
+ },
3281
+ {
3282
+ name: "Dd27pType",
3283
+ sequence: { element: [
3284
+ {
3285
+ name: "VIEWNAME",
3286
+ type: "xs:string",
3287
+ minOccurs: "0"
3288
+ },
3289
+ {
3290
+ name: "VIEWFIELD",
3291
+ type: "xs:string",
3292
+ minOccurs: "0"
3293
+ },
3294
+ {
3295
+ name: "TABNAME",
3296
+ type: "xs:string",
3297
+ minOccurs: "0"
3298
+ },
3299
+ {
3300
+ name: "FIELDNAME",
3301
+ type: "xs:string",
3302
+ minOccurs: "0"
3303
+ },
3304
+ {
3305
+ name: "KEYFLAG",
3306
+ type: "xs:string",
3307
+ minOccurs: "0"
3308
+ },
3309
+ {
3310
+ name: "READONLY",
3311
+ type: "xs:string",
3312
+ minOccurs: "0"
3313
+ },
3314
+ {
3315
+ name: "ROLLNAME",
3316
+ type: "xs:string",
3317
+ minOccurs: "0"
3318
+ },
3319
+ {
3320
+ name: "CHECKTABLE",
3321
+ type: "xs:string",
3322
+ minOccurs: "0"
3323
+ },
3324
+ {
3325
+ name: "LOGFLAG",
3326
+ type: "xs:string",
3327
+ minOccurs: "0"
3328
+ },
3329
+ {
3330
+ name: "REFTABLE",
3331
+ type: "xs:string",
3332
+ minOccurs: "0"
3333
+ },
3334
+ {
3335
+ name: "REFFIELD",
3336
+ type: "xs:string",
3337
+ minOccurs: "0"
3338
+ },
3339
+ {
3340
+ name: "OUTPUTLEN",
3341
+ type: "xs:string",
3342
+ minOccurs: "0"
3343
+ },
3344
+ {
3345
+ name: "CONVEXIT",
3346
+ type: "xs:string",
3347
+ minOccurs: "0"
3348
+ },
3349
+ {
3350
+ name: "DDTEXT",
3351
+ type: "xs:string",
3352
+ minOccurs: "0"
3353
+ }
3354
+ ] }
3355
+ },
3356
+ {
3357
+ name: "Dd27pTabType",
3358
+ sequence: { element: [{
3359
+ name: "item",
3360
+ type: "Dd27pType",
3361
+ minOccurs: "0",
3362
+ maxOccurs: "unbounded"
3363
+ }] }
3364
+ },
3365
+ {
3366
+ name: "AbapType",
3367
+ sequence: { element: [{ ref: "asx:values" }] },
3368
+ attribute: [{
3369
+ name: "version",
3370
+ type: "xs:string",
3371
+ "default": "1.0"
3372
+ }]
3373
+ }
3374
+ ]
3375
+ };
3376
+
3377
+ //#endregion
3378
+ //#region src/schemas/generated/schemas/shlp.ts
3379
+ /**
3380
+ * Auto-generated schema from XSD
3381
+ *
3382
+ * DO NOT EDIT - Generated by ts-xsd codegen
3383
+ * Source: abapgit/shlp.xsd
3384
+ */
3385
+ var shlp_default = {
3386
+ $xmlns: {
3387
+ xs: "http://www.w3.org/2001/XMLSchema",
3388
+ asx: "http://www.sap.com/abapxml"
3389
+ },
3390
+ targetNamespace: "http://www.sap.com/abapxml",
3391
+ elementFormDefault: "unqualified",
3392
+ element: [
3393
+ {
3394
+ name: "abapGit",
3395
+ complexType: {
3396
+ sequence: { element: [{ ref: "asx:abap" }] },
3397
+ attribute: [
3398
+ {
3399
+ name: "version",
3400
+ type: "xs:string",
3401
+ use: "required"
3402
+ },
3403
+ {
3404
+ name: "serializer",
3405
+ type: "xs:string",
3406
+ use: "required"
3407
+ },
3408
+ {
3409
+ name: "serializer_version",
3410
+ type: "xs:string",
3411
+ use: "required"
3412
+ }
3413
+ ]
3414
+ }
3415
+ },
3416
+ {
3417
+ name: "Schema",
3418
+ abstract: true
3419
+ },
3420
+ {
3421
+ name: "values",
3422
+ type: "asx:AbapValuesType"
3423
+ },
3424
+ {
3425
+ name: "abap",
3426
+ type: "asx:AbapType"
3427
+ }
3428
+ ],
3429
+ complexType: [
3430
+ {
3431
+ name: "AbapValuesType",
3432
+ all: { element: [
3433
+ {
3434
+ name: "DD30V",
3435
+ type: "asx:Dd30vType",
3436
+ minOccurs: "0"
3437
+ },
3438
+ {
3439
+ name: "DD31V",
3440
+ type: "asx:Dd31vType",
3441
+ minOccurs: "0"
3442
+ },
3443
+ {
3444
+ name: "DD32P_TABLE",
3445
+ type: "asx:Dd32pTabType",
3446
+ minOccurs: "0"
3447
+ },
3448
+ {
3449
+ name: "DD33V_TABLE",
3450
+ type: "asx:Dd33vTabType",
3451
+ minOccurs: "0"
3452
+ }
3453
+ ] }
3454
+ },
3455
+ {
3456
+ name: "Dd30vType",
3457
+ all: { element: [
3458
+ {
3459
+ name: "SHLPNAME",
3460
+ type: "xs:string"
3461
+ },
3462
+ {
3463
+ name: "DDLANGUAGE",
3464
+ type: "xs:string",
3465
+ minOccurs: "0"
3466
+ },
3467
+ {
3468
+ name: "DDTEXT",
3469
+ type: "xs:string",
3470
+ minOccurs: "0"
3471
+ },
3472
+ {
3473
+ name: "SHLPTYPE",
3474
+ type: "xs:string",
3475
+ minOccurs: "0"
3476
+ },
3477
+ {
3478
+ name: "SHLPINPUT",
3479
+ type: "xs:string",
3480
+ minOccurs: "0"
3481
+ },
3482
+ {
3483
+ name: "DEFLOOKUP",
3484
+ type: "xs:string",
3485
+ minOccurs: "0"
3486
+ },
3487
+ {
3488
+ name: "DIALOGTYPE",
3489
+ type: "xs:string",
3490
+ minOccurs: "0"
3491
+ },
3492
+ {
3493
+ name: "SELEXT",
3494
+ type: "xs:string",
3495
+ minOccurs: "0"
3496
+ },
3497
+ {
3498
+ name: "ACTMETHOD",
3499
+ type: "xs:string",
3500
+ minOccurs: "0"
3501
+ },
3502
+ {
3503
+ name: "HOTKEY",
3504
+ type: "xs:string",
3505
+ minOccurs: "0"
3506
+ }
3507
+ ] }
3508
+ },
3509
+ {
3510
+ name: "Dd31vType",
3511
+ all: { element: [
3512
+ {
3513
+ name: "SHLPNAME",
3514
+ type: "xs:string",
3515
+ minOccurs: "0"
3516
+ },
3517
+ {
3518
+ name: "TABNAME",
3519
+ type: "xs:string",
3520
+ minOccurs: "0"
3521
+ },
3522
+ {
3523
+ name: "FIELDNAME",
3524
+ type: "xs:string",
3525
+ minOccurs: "0"
3526
+ },
3527
+ {
3528
+ name: "FLDLOCATION",
3529
+ type: "xs:string",
3530
+ minOccurs: "0"
3531
+ },
3532
+ {
3533
+ name: "ROLLNAME",
3534
+ type: "xs:string",
3535
+ minOccurs: "0"
3536
+ }
3537
+ ] }
3538
+ },
3539
+ {
3540
+ name: "Dd32pType",
3541
+ sequence: { element: [
3542
+ {
3543
+ name: "SHLPNAME",
3544
+ type: "xs:string",
3545
+ minOccurs: "0"
3546
+ },
3547
+ {
3548
+ name: "FIELDNAME",
3549
+ type: "xs:string",
3550
+ minOccurs: "0"
3551
+ },
3552
+ {
3553
+ name: "SHLPINPUT",
3554
+ type: "xs:string",
3555
+ minOccurs: "0"
3556
+ },
3557
+ {
3558
+ name: "SHLPOUTPUT",
3559
+ type: "xs:string",
3560
+ minOccurs: "0"
3561
+ },
3562
+ {
3563
+ name: "SHLPSELPOS",
3564
+ type: "xs:string",
3565
+ minOccurs: "0"
3566
+ },
3567
+ {
3568
+ name: "SHLPLISPOS",
3569
+ type: "xs:string",
3570
+ minOccurs: "0"
3571
+ },
3572
+ {
3573
+ name: "DEFAULTVAL",
3574
+ type: "xs:string",
3575
+ minOccurs: "0"
3576
+ },
3577
+ {
3578
+ name: "ROLLNAME",
3579
+ type: "xs:string",
3580
+ minOccurs: "0"
3581
+ },
3582
+ {
3583
+ name: "DATATYPE",
3584
+ type: "xs:string",
3585
+ minOccurs: "0"
3586
+ },
3587
+ {
3588
+ name: "LENG",
3589
+ type: "xs:string",
3590
+ minOccurs: "0"
3591
+ },
3592
+ {
3593
+ name: "DECIMALS",
3594
+ type: "xs:string",
3595
+ minOccurs: "0"
3596
+ },
3597
+ {
3598
+ name: "DDTEXT",
3599
+ type: "xs:string",
3600
+ minOccurs: "0"
3601
+ }
3602
+ ] }
3603
+ },
3604
+ {
3605
+ name: "Dd32pTabType",
3606
+ sequence: { element: [{
3607
+ name: "item",
3608
+ type: "Dd32pType",
3609
+ minOccurs: "0",
3610
+ maxOccurs: "unbounded"
3611
+ }] }
3612
+ },
3613
+ {
3614
+ name: "Dd33vType",
3615
+ sequence: { element: [
3616
+ {
3617
+ name: "SHLPNAME",
3618
+ type: "xs:string",
3619
+ minOccurs: "0"
3620
+ },
3621
+ {
3622
+ name: "SHLPFIELD",
3623
+ type: "xs:string",
3624
+ minOccurs: "0"
3625
+ },
3626
+ {
3627
+ name: "TABNAME",
3628
+ type: "xs:string",
3629
+ minOccurs: "0"
3630
+ },
3631
+ {
3632
+ name: "FIELDNAME",
3633
+ type: "xs:string",
3634
+ minOccurs: "0"
3635
+ },
3636
+ {
3637
+ name: "SHLPINPUT",
3638
+ type: "xs:string",
3639
+ minOccurs: "0"
3640
+ },
3641
+ {
3642
+ name: "SHLPOUTPUT",
3643
+ type: "xs:string",
3644
+ minOccurs: "0"
3645
+ }
3646
+ ] }
3647
+ },
3648
+ {
3649
+ name: "Dd33vTabType",
3650
+ sequence: { element: [{
3651
+ name: "item",
3652
+ type: "Dd33vType",
3653
+ minOccurs: "0",
3654
+ maxOccurs: "unbounded"
3655
+ }] }
3656
+ },
3657
+ {
3658
+ name: "AbapType",
3659
+ sequence: { element: [{ ref: "asx:values" }] },
3660
+ attribute: [{
3661
+ name: "version",
3662
+ type: "xs:string",
3663
+ "default": "1.0"
3664
+ }]
3665
+ }
3666
+ ]
3667
+ };
3668
+
3669
+ //#endregion
3670
+ //#region src/schemas/generated/schemas/tran.ts
3671
+ /**
3672
+ * Auto-generated schema from XSD
3673
+ *
3674
+ * DO NOT EDIT - Generated by ts-xsd codegen
3675
+ * Source: abapgit/tran.xsd
3676
+ */
3677
+ var tran_default = {
3678
+ $xmlns: {
3679
+ xs: "http://www.w3.org/2001/XMLSchema",
3680
+ asx: "http://www.sap.com/abapxml"
3681
+ },
3682
+ targetNamespace: "http://www.sap.com/abapxml",
3683
+ elementFormDefault: "unqualified",
3684
+ element: [
3685
+ {
3686
+ name: "abapGit",
3687
+ complexType: {
3688
+ sequence: { element: [{ ref: "asx:abap" }] },
3689
+ attribute: [
3690
+ {
3691
+ name: "version",
3692
+ type: "xs:string",
3693
+ use: "required"
3694
+ },
3695
+ {
3696
+ name: "serializer",
3697
+ type: "xs:string",
3698
+ use: "required"
3699
+ },
3700
+ {
3701
+ name: "serializer_version",
3702
+ type: "xs:string",
3703
+ use: "required"
3704
+ }
3705
+ ]
3706
+ }
3707
+ },
3708
+ {
3709
+ name: "Schema",
3710
+ abstract: true
3711
+ },
3712
+ {
3713
+ name: "values",
3714
+ type: "asx:AbapValuesType"
3715
+ },
3716
+ {
3717
+ name: "abap",
3718
+ type: "asx:AbapType"
3719
+ }
3720
+ ],
3721
+ complexType: [
3722
+ {
3723
+ name: "AbapValuesType",
3724
+ all: { element: [
3725
+ {
3726
+ name: "TSTC",
3727
+ type: "asx:TstcType",
3728
+ minOccurs: "0"
3729
+ },
3730
+ {
3731
+ name: "TSTCC",
3732
+ type: "asx:TstccType",
3733
+ minOccurs: "0"
3734
+ },
3735
+ {
3736
+ name: "TSTCT",
3737
+ type: "asx:TstctTabType",
3738
+ minOccurs: "0"
3739
+ }
3740
+ ] }
3741
+ },
3742
+ {
3743
+ name: "TstcType",
3744
+ all: { element: [
3745
+ {
3746
+ name: "TCODE",
3747
+ type: "xs:string"
3748
+ },
3749
+ {
3750
+ name: "PGMNA",
3751
+ type: "xs:string",
3752
+ minOccurs: "0"
3753
+ },
3754
+ {
3755
+ name: "DYPNO",
3756
+ type: "xs:string",
3757
+ minOccurs: "0"
3758
+ },
3759
+ {
3760
+ name: "TYPE",
3761
+ type: "xs:string",
3762
+ minOccurs: "0"
3763
+ },
3764
+ {
3765
+ name: "CINFO",
3766
+ type: "xs:string",
3767
+ minOccurs: "0"
3768
+ }
3769
+ ] }
3770
+ },
3771
+ {
3772
+ name: "TstccType",
3773
+ all: { element: [
3774
+ {
3775
+ name: "TCODE",
3776
+ type: "xs:string",
3777
+ minOccurs: "0"
3778
+ },
3779
+ {
3780
+ name: "S_WEBGUI",
3781
+ type: "xs:string",
3782
+ minOccurs: "0"
3783
+ },
3784
+ {
3785
+ name: "S_PLATIN",
3786
+ type: "xs:string",
3787
+ minOccurs: "0"
3788
+ },
3789
+ {
3790
+ name: "S_WIN32",
3791
+ type: "xs:string",
3792
+ minOccurs: "0"
3793
+ },
3794
+ {
3795
+ name: "S_MAC",
3796
+ type: "xs:string",
3797
+ minOccurs: "0"
3798
+ }
3799
+ ] }
3800
+ },
3801
+ {
3802
+ name: "TstctType",
3803
+ sequence: { element: [
3804
+ {
3805
+ name: "SPRSL",
3806
+ type: "xs:string",
3807
+ minOccurs: "0"
3808
+ },
3809
+ {
3810
+ name: "TCODE",
3811
+ type: "xs:string",
3812
+ minOccurs: "0"
3813
+ },
3814
+ {
3815
+ name: "TTEXT",
3816
+ type: "xs:string",
3817
+ minOccurs: "0"
3818
+ }
3819
+ ] }
3820
+ },
3821
+ {
3822
+ name: "TstctTabType",
3823
+ sequence: { element: [{
3824
+ name: "item",
3825
+ type: "TstctType",
3826
+ minOccurs: "0",
3827
+ maxOccurs: "unbounded"
3828
+ }] }
3829
+ },
3830
+ {
3831
+ name: "AbapType",
3832
+ sequence: { element: [{ ref: "asx:values" }] },
3833
+ attribute: [{
3834
+ name: "version",
3835
+ type: "xs:string",
3836
+ "default": "1.0"
3837
+ }]
3838
+ }
3839
+ ]
3840
+ };
3841
+
3842
+ //#endregion
3843
+ //#region src/schemas/generated/schemas/type.ts
3844
+ /**
3845
+ * Auto-generated schema from XSD
3846
+ *
3847
+ * DO NOT EDIT - Generated by ts-xsd codegen
3848
+ * Source: abapgit/type.xsd
3849
+ */
3850
+ var type_default = {
3851
+ $xmlns: {
3852
+ xs: "http://www.w3.org/2001/XMLSchema",
3853
+ asx: "http://www.sap.com/abapxml"
3854
+ },
3855
+ targetNamespace: "http://www.sap.com/abapxml",
3856
+ elementFormDefault: "unqualified",
3857
+ element: [
3858
+ {
3859
+ name: "abapGit",
3860
+ complexType: {
3861
+ sequence: { element: [{ ref: "asx:abap" }] },
3862
+ attribute: [
3863
+ {
3864
+ name: "version",
3865
+ type: "xs:string",
3866
+ use: "required"
3867
+ },
3868
+ {
3869
+ name: "serializer",
3870
+ type: "xs:string",
3871
+ use: "required"
3872
+ },
3873
+ {
3874
+ name: "serializer_version",
3875
+ type: "xs:string",
3876
+ use: "required"
3877
+ }
3878
+ ]
3879
+ }
3880
+ },
3881
+ {
3882
+ name: "Schema",
3883
+ abstract: true
3884
+ },
3885
+ {
3886
+ name: "values",
3887
+ type: "asx:AbapValuesType"
3888
+ },
3889
+ {
3890
+ name: "abap",
3891
+ type: "asx:AbapType"
3892
+ }
3893
+ ],
3894
+ complexType: [
3895
+ {
3896
+ name: "AbapValuesType",
3897
+ all: { element: [{
3898
+ name: "TYPE",
3899
+ type: "asx:TypeGroupType",
3900
+ minOccurs: "0"
3901
+ }] }
3902
+ },
3903
+ {
3904
+ name: "TypeGroupType",
3905
+ all: { element: [{
3906
+ name: "TYPEGROUP",
3907
+ type: "xs:string"
3908
+ }, {
3909
+ name: "DESCRIPT",
3910
+ type: "xs:string",
3911
+ minOccurs: "0"
3912
+ }] }
3913
+ },
3914
+ {
3915
+ name: "AbapType",
3916
+ sequence: { element: [{ ref: "asx:values" }] },
3917
+ attribute: [{
3918
+ name: "version",
3919
+ type: "xs:string",
3920
+ "default": "1.0"
3921
+ }]
3922
+ }
3923
+ ]
3924
+ };
3925
+
3926
+ //#endregion
3927
+ //#region src/schemas/generated/schemas/xslt.ts
3928
+ /**
3929
+ * Auto-generated schema from XSD
3930
+ *
3931
+ * DO NOT EDIT - Generated by ts-xsd codegen
3932
+ * Source: abapgit/xslt.xsd
3933
+ */
3934
+ var xslt_default = {
3935
+ $xmlns: {
3936
+ xs: "http://www.w3.org/2001/XMLSchema",
3937
+ asx: "http://www.sap.com/abapxml"
3938
+ },
3939
+ targetNamespace: "http://www.sap.com/abapxml",
3940
+ elementFormDefault: "unqualified",
3941
+ element: [
3942
+ {
3943
+ name: "abapGit",
3944
+ complexType: {
3945
+ sequence: { element: [{ ref: "asx:abap" }] },
3946
+ attribute: [
3947
+ {
3948
+ name: "version",
3949
+ type: "xs:string",
3950
+ use: "required"
3951
+ },
3952
+ {
3953
+ name: "serializer",
3954
+ type: "xs:string",
3955
+ use: "required"
3956
+ },
3957
+ {
3958
+ name: "serializer_version",
3959
+ type: "xs:string",
3960
+ use: "required"
3961
+ }
3962
+ ]
3963
+ }
3964
+ },
3965
+ {
3966
+ name: "Schema",
3967
+ abstract: true
3968
+ },
3969
+ {
3970
+ name: "values",
3971
+ type: "asx:AbapValuesType"
3972
+ },
3973
+ {
3974
+ name: "abap",
3975
+ type: "asx:AbapType"
3976
+ }
3977
+ ],
3978
+ complexType: [
3979
+ {
3980
+ name: "AbapValuesType",
3981
+ all: { element: [{
3982
+ name: "XSLT",
3983
+ type: "asx:XsltType",
3984
+ minOccurs: "0"
3985
+ }] }
3986
+ },
3987
+ {
3988
+ name: "XsltType",
3989
+ all: { element: [{
3990
+ name: "PROGNAME",
3991
+ type: "xs:string"
3992
+ }, {
3993
+ name: "DESCRIPT",
3994
+ type: "xs:string",
3995
+ minOccurs: "0"
3996
+ }] }
3997
+ },
3998
+ {
3999
+ name: "AbapType",
4000
+ sequence: { element: [{ ref: "asx:values" }] },
4001
+ attribute: [{
4002
+ name: "version",
4003
+ type: "xs:string",
4004
+ "default": "1.0"
4005
+ }]
4006
+ }
4007
+ ]
4008
+ };
4009
+
4010
+ //#endregion
4011
+ //#region src/schemas/generated/index.ts
4012
+ /**
4013
+ * AbapGit Typed Schemas
4014
+ *
4015
+ * Auto-generated by ts-xsd codegen - DO NOT EDIT
4016
+ *
4017
+ * Each schema provides:
4018
+ * - _type: Full AbapGitType (for XML build/parse)
4019
+ * - _values: Inner values type (for handler mapping)
4020
+ * - parse(xml) → fully typed object
4021
+ * - build(data) → XML string
4022
+ * - schema → raw schema literal
4023
+ */
4024
+ const clas = abapGitSchema(clas_default);
4025
+ const devc = abapGitSchema(devc_default);
4026
+ const doma = abapGitSchema(doma_default);
4027
+ const dtel = abapGitSchema(dtel_default);
4028
+ const intf = abapGitSchema(intf_default);
4029
+ const prog = abapGitSchema(prog_default);
4030
+ const fugr = abapGitSchema(fugr_default);
4031
+ const tabl = abapGitSchema(tabl_default);
4032
+ const ttyp = abapGitSchema(ttyp_default);
4033
+ const bdef = abapGitSchema(bdef_default);
4034
+ const srvd = abapGitSchema(srvd_default);
4035
+ const srvb = abapGitSchema(srvb_default);
4036
+ const dcls = abapGitSchema(dcls_default);
4037
+ const ddls = abapGitSchema(ddls_default);
4038
+ const ddlx = abapGitSchema(ddlx_default);
4039
+ const msag = abapGitSchema(msag_default);
4040
+ const view = abapGitSchema(view_default);
4041
+ const enqu = abapGitSchema(enqu_default);
4042
+ const shlp = abapGitSchema(shlp_default);
4043
+ const tran = abapGitSchema(tran_default);
4044
+ const type = abapGitSchema(type_default);
4045
+ const xslt = abapGitSchema(xslt_default);
4046
+
4047
+ //#endregion
4048
+ //#region src/lib/handlers/lang.ts
4049
+ /**
4050
+ * SAP 1-character language code ↔ ISO 639-1 mapping
4051
+ *
4052
+ * abapGit stores SAP internal 1-char codes (e.g. "E" for English).
4053
+ * SAP ADT REST API expects ISO 639-1 codes (e.g. "EN").
4054
+ */
4055
+ const SAP_TO_ISO = {
4056
+ E: "EN",
4057
+ D: "DE",
4058
+ F: "FR",
4059
+ I: "IT",
4060
+ S: "ES",
4061
+ P: "PT",
4062
+ J: "JA",
4063
+ K: "KO",
4064
+ "1": "ZH",
4065
+ W: "RU",
4066
+ T: "TR",
4067
+ N: "NL",
4068
+ H: "HU",
4069
+ L: "PL",
4070
+ Q: "CS",
4071
+ R: "SK",
4072
+ U: "SV",
4073
+ V: "DA",
4074
+ Y: "FI",
4075
+ X: "NO",
4076
+ G: "EL",
4077
+ B: "HE",
4078
+ A: "AR",
4079
+ "3": "TH",
4080
+ M: "UK",
4081
+ "6": "HR",
4082
+ O: "SL",
4083
+ "7": "BG",
4084
+ "8": "SR",
4085
+ C: "RO"
4086
+ };
4087
+ const ISO_TO_SAP = Object.fromEntries(Object.entries(SAP_TO_ISO).map(([k, v]) => [v, k]));
4088
+ /**
4089
+ * Convert SAP 1-char language code to ISO 639-1.
4090
+ * Returns the input uppercased if no mapping exists (may already be ISO).
4091
+ */
4092
+ function sapLangToIso(sapLang) {
4093
+ if (!sapLang) return "EN";
4094
+ const upper = sapLang.toUpperCase();
4095
+ if (upper.length >= 2) return upper;
4096
+ return SAP_TO_ISO[upper] ?? upper;
4097
+ }
4098
+ /**
4099
+ * Convert ISO 639-1 language code to SAP 1-char code.
4100
+ * Returns "E" (English) if no mapping exists.
4101
+ */
4102
+ function isoToSapLang(isoLang) {
4103
+ if (!isoLang) return "E";
2444
4104
  const upper = isoLang.toUpperCase();
2445
4105
  if (upper.length === 1) return upper;
2446
4106
  return ISO_TO_SAP[upper] ?? "E";
@@ -2680,6 +4340,19 @@ const programHandler = createHandler(AdkProgram, {
2680
4340
  }
2681
4341
  });
2682
4342
 
4343
+ //#endregion
4344
+ //#region src/lib/handlers/source-inclusion.ts
4345
+ /**
4346
+ * Shared source-inclusion rule for abapGit object handlers.
4347
+ *
4348
+ * A source is included when it is defined and either supplied by the caller
4349
+ * or non-empty on the object.
4350
+ */
4351
+ function shouldIncludeSource(source, suppliedSource) {
4352
+ if (source === void 0) return false;
4353
+ return suppliedSource !== void 0 || source !== "";
4354
+ }
4355
+
2683
4356
  //#endregion
2684
4357
  //#region src/lib/handlers/objects/fugr.ts
2685
4358
  /**
@@ -2696,6 +4369,75 @@ const programHandler = createHandler(AdkProgram, {
2696
4369
  * Note: FM metadata is stored INSIDE the main XML under <FUNCTIONS>,
2697
4370
  * NOT in separate per-FM XML files (that's how abapGit works).
2698
4371
  */
4372
+ function buildMainXmlFile(obj, objectName, nameUpper, functions, ctx) {
4373
+ const values = {
4374
+ AREAT: obj.description ?? "",
4375
+ INCLUDES: { SOBJ_NAME: [`L${nameUpper}TOP`, `SAPL${nameUpper}`] }
4376
+ };
4377
+ if (functions.length > 0) values.FUNCTIONS = { item: functions };
4378
+ const fullPayload = { abapGit: {
4379
+ abap: {
4380
+ version: "1.0",
4381
+ values
4382
+ },
4383
+ version: "v1.0.0",
4384
+ serializer: "LCL_OBJECT_FUGR",
4385
+ serializer_version: "v1.0.0"
4386
+ } };
4387
+ const xmlContent = formatAbapGitXml(fugr.build(fullPayload, { pretty: true }));
4388
+ return ctx.createFile(`${objectName}.fugr.xml`, xmlContent);
4389
+ }
4390
+ function deriveFmList(suppliedSources, objectName) {
4391
+ if (!suppliedSources) return {
4392
+ fmItems: [],
4393
+ functions: []
4394
+ };
4395
+ const lowerTopKey = `l${objectName}top`;
4396
+ const lowerSap = `sapl${objectName}`;
4397
+ const fmItems = Object.keys(suppliedSources).filter((key) => {
4398
+ const lower = key.toLowerCase();
4399
+ return lower !== "main" && lower !== lowerTopKey && lower !== lowerSap;
4400
+ }).map((key) => ({
4401
+ key,
4402
+ name: key.toLowerCase()
4403
+ }));
4404
+ return {
4405
+ fmItems,
4406
+ functions: fmItems.map((fm) => ({ FUNCNAME: fm.key.toUpperCase() }))
4407
+ };
4408
+ }
4409
+ async function buildTopSourceFile(obj, objectName, suppliedSources, ctx) {
4410
+ const hasExplicitSources = suppliedSources !== void 0;
4411
+ const topKey = `l${objectName}top`;
4412
+ const suppliedTopSource = suppliedSources ? suppliedSources.main ?? suppliedSources[topKey] : void 0;
4413
+ try {
4414
+ const topSource = suppliedTopSource !== void 0 || !hasExplicitSources ? suppliedTopSource ?? await obj.getSource() : void 0;
4415
+ if (shouldIncludeSource(topSource, suppliedTopSource)) return ctx.createFile(`${objectName}.fugr.l${objectName}top.abap`, topSource);
4416
+ } catch {}
4417
+ }
4418
+ function buildProgramFiles(objectName, nameUpper, fixpt, ctx) {
4419
+ return [
4420
+ ctx.createFile(`${objectName}.fugr.l${objectName}top.xml`, buildProgdirXml(`L${nameUpper}TOP`, "I", fixpt)),
4421
+ ctx.createFile(`${objectName}.fugr.sapl${objectName}.abap`, buildMainProgramSource(nameUpper)),
4422
+ ctx.createFile(`${objectName}.fugr.sapl${objectName}.xml`, buildProgdirXml(`SAPL${nameUpper}`, "F", fixpt))
4423
+ ];
4424
+ }
4425
+ async function buildFunctionModuleFiles(obj, fmItems, suppliedSources, ctx) {
4426
+ const objectName = ctx.getObjectName(obj);
4427
+ const files = [];
4428
+ const hasExplicitSources = suppliedSources !== void 0;
4429
+ for (const fm of fmItems) {
4430
+ const sourceKey = fm.key ?? fm.name;
4431
+ const funcName = fm.name.toLowerCase();
4432
+ const suppliedFmSource = suppliedSources?.[sourceKey];
4433
+ if (hasExplicitSources && suppliedFmSource === void 0) continue;
4434
+ try {
4435
+ const source = suppliedFmSource !== void 0 ? suppliedFmSource : await obj.client.adt.functions.groups.fmodules.source.main.get(obj.name, fm.name);
4436
+ if (shouldIncludeSource(source, suppliedFmSource)) files.push(ctx.createFile(`${objectName}.fugr.${funcName}.abap`, source));
4437
+ } catch {}
4438
+ }
4439
+ return files;
4440
+ }
2699
4441
  /**
2700
4442
  * Map ADT processingType to abapGit REMOTE_CALL flag
2701
4443
  */
@@ -2726,44 +4468,26 @@ const functionGroupHandler = createHandler(AdkFunctionGroup, {
2726
4468
  INCLUDES: { SOBJ_NAME: [`L${name}TOP`, `SAPL${name}`] }
2727
4469
  };
2728
4470
  },
2729
- serialize: async (obj, ctx) => {
4471
+ serialize: async (obj, ctx, options) => {
2730
4472
  const objectName = ctx.getObjectName(obj);
2731
4473
  const nameUpper = obj.name.toUpperCase();
2732
- const files = [];
2733
- const fmItems = await discoverFunctionModules(obj);
2734
- const functions = await serializeFunctions(obj, fmItems);
2735
- const values = {
2736
- AREAT: obj.description ?? "",
2737
- INCLUDES: { SOBJ_NAME: [`L${nameUpper}TOP`, `SAPL${nameUpper}`] }
2738
- };
2739
- if (functions.length > 0) values.FUNCTIONS = { item: functions };
2740
- const fullPayload = { abapGit: {
2741
- abap: {
2742
- version: "1.0",
2743
- values
2744
- },
2745
- version: "v1.0.0",
2746
- serializer: "LCL_OBJECT_FUGR",
2747
- serializer_version: "v1.0.0"
2748
- } };
2749
- let xmlContent = fugr.build(fullPayload, { pretty: true });
2750
- xmlContent = formatAbapGitXml(xmlContent);
2751
- files.push(ctx.createFile(`${objectName}.fugr.xml`, xmlContent));
2752
- try {
2753
- const topSource = await obj.getSource();
2754
- if (topSource) files.push(ctx.createFile(`${objectName}.fugr.l${objectName}top.abap`, topSource));
2755
- } catch {}
2756
- const fixpt = ctx.getData(obj).fixPointArithmetic ? "X" : "";
2757
- files.push(ctx.createFile(`${objectName}.fugr.l${objectName}top.xml`, buildProgdirXml(`L${nameUpper}TOP`, "I", fixpt)));
2758
- files.push(ctx.createFile(`${objectName}.fugr.sapl${objectName}.abap`, buildMainProgramSource(nameUpper)), ctx.createFile(`${objectName}.fugr.sapl${objectName}.xml`, buildProgdirXml(`SAPL${nameUpper}`, "F", fixpt)));
2759
- for (const fm of fmItems) {
2760
- const funcName = fm.name.toLowerCase();
2761
- try {
2762
- const source = await obj.client.adt.functions.groups.fmodules.source.main.get(obj.name, fm.name);
2763
- if (source) files.push(ctx.createFile(`${objectName}.fugr.${funcName}.abap`, source));
2764
- } catch {}
4474
+ const suppliedSources = options?.sources;
4475
+ const hasExplicitSources = suppliedSources !== void 0;
4476
+ let fmItems;
4477
+ let functions;
4478
+ if (hasExplicitSources) ({fmItems, functions} = deriveFmList(suppliedSources, objectName));
4479
+ else {
4480
+ fmItems = await discoverFunctionModules(obj);
4481
+ functions = await serializeFunctions(obj, fmItems);
2765
4482
  }
2766
- return files;
4483
+ const fixpt = ctx.getData(obj).fixPointArithmetic ? "X" : "";
4484
+ const topFile = await buildTopSourceFile(obj, objectName, suppliedSources, ctx);
4485
+ return [
4486
+ buildMainXmlFile(obj, objectName, nameUpper, functions, ctx),
4487
+ ...topFile ? [topFile] : [],
4488
+ ...buildProgramFiles(objectName, nameUpper, fixpt, ctx),
4489
+ ...await buildFunctionModuleFiles(obj, fmItems, suppliedSources, ctx)
4490
+ ];
2767
4491
  },
2768
4492
  fromAbapGit: ({ AREAT, FUNCTIONS }) => ({
2769
4493
  name: "",
@@ -2779,8 +4503,9 @@ const functionGroupHandler = createHandler(AdkFunctionGroup, {
2779
4503
  const mainSource = sources.main ?? sources[topKey];
2780
4504
  if (mainSource) obj._pendingSource = mainSource;
2781
4505
  const fmSources = {};
4506
+ const mainProgramKey = `sapl${name}`;
2782
4507
  for (const [suffix, content] of Object.entries(sources)) {
2783
- if (suffix === "main" || suffix === topKey || suffix.startsWith(`l${name}`) || suffix.startsWith(`sapl${name}`)) continue;
4508
+ if (suffix === "main" || suffix === topKey || suffix === mainProgramKey) continue;
2784
4509
  fmSources[suffix] = content;
2785
4510
  }
2786
4511
  if (Object.keys(fmSources).length > 0) obj._pendingFmSources = fmSources;
@@ -2799,7 +4524,7 @@ async function discoverFunctionModules(obj) {
2799
4524
  let match;
2800
4525
  while ((match = regex.exec(responseStr)) !== null) fmNames.push({ name: match[1] });
2801
4526
  const regex2 = /adtcore:name="([^"]+)"[^>]*type="FUGR\/FF"/g;
2802
- while ((match = regex2.exec(responseStr)) !== null) if (!fmNames.some((f) => f.name === match[1])) fmNames.push({ name: match[1] });
4527
+ while ((match = regex2.exec(responseStr)) !== null) if (match?.[1] && !fmNames.some((f) => f.name === match[1])) fmNames.push({ name: match[1] });
2803
4528
  return fmNames;
2804
4529
  } catch {
2805
4530
  return [];
@@ -3441,451 +5166,1377 @@ async function buildDD03P(members, tableName = "", resolver) {
3441
5166
  const field = member;
3442
5167
  entries.push(await buildFieldDD03P(field, tableName, resolver));
3443
5168
  }
3444
- return entries;
3445
- }
5169
+ return entries;
5170
+ }
5171
+
5172
+ //#endregion
5173
+ //#region src/lib/handlers/objects/tabl-serialize.ts
5174
+ /**
5175
+ * Table/Structure serialization helpers for abapGit format.
5176
+ *
5177
+ * This file contains the source-to-XML conversion used by the TABL handler.
5178
+ * It is intentionally separate from the handler definition file, which keeps
5179
+ * the handler surface focused on schema mapping (`toAbapGit`/`fromAbapGit`).
5180
+ */
5181
+ const XML_NAMED_ENTITIES = {
5182
+ amp: "&",
5183
+ lt: "<",
5184
+ gt: ">",
5185
+ quot: "\"",
5186
+ apos: "'"
5187
+ };
5188
+ function decodeNumericCode(decimal, hex) {
5189
+ const base = decimal !== void 0 ? 10 : 16;
5190
+ const value = decimal !== void 0 ? decimal : hex ?? "";
5191
+ return String.fromCharCode(parseInt(value, base));
5192
+ }
5193
+ function decodeXmlEntities(text) {
5194
+ return text.replace(/&(?:#(\d+)|#x([0-9a-fA-F]+)|([a-zA-Z][a-zA-Z0-9]*));/g, (match, decimal, hex, name) => decimal !== void 0 || hex !== void 0 ? decodeNumericCode(decimal, hex) : XML_NAMED_ENTITIES[name] ?? match);
5195
+ }
5196
+ /**
5197
+ * Strip undefined/empty-string values from an object
5198
+ * to avoid emitting empty XML elements
5199
+ */
5200
+ function stripEmpty(obj) {
5201
+ const result = {};
5202
+ for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== "") result[key] = value;
5203
+ return result;
5204
+ }
5205
+ /**
5206
+ * Parse ADT table settings XML into DD09L data for abapGit serialization.
5207
+ */
5208
+ function parseSettingsToDD09L(settingsXml, def) {
5209
+ let settings;
5210
+ try {
5211
+ const parsed = tablesettings.parse(settingsXml);
5212
+ if (!("tableSettings" in parsed)) return void 0;
5213
+ settings = parsed.tableSettings;
5214
+ } catch {
5215
+ return;
5216
+ }
5217
+ return stripEmpty({
5218
+ TABNAME: def.name.toUpperCase(),
5219
+ AS4LOCAL: "A",
5220
+ TABKAT: settings.sizeCategory,
5221
+ TABART: settings.dataClassCategory,
5222
+ BUFALLOW: settings.buffering?.allowed,
5223
+ PUFFERUNG: settings.buffering?.type,
5224
+ SCHFELDANZ: settings.buffering?.areaKeyFields && settings.buffering.areaKeyFields !== "0" ? settings.buffering.areaKeyFields : void 0,
5225
+ PROTOKOLL: settings.loggingEnabled ? "X" : void 0
5226
+ });
5227
+ }
5228
+ /**
5229
+ * Create a TypeResolver that resolves named types via ADT endpoints.
5230
+ */
5231
+ function resolveReferencedType(xml, comptype) {
5232
+ const result = { comptype };
5233
+ if (comptype === "E" && /<dtel:searchHelp>[^<]+<\/dtel:searchHelp>/.test(xml)) result.shlporigin = "D";
5234
+ const descMatch = xml.match(/adtcore:description="([^"]+)"/);
5235
+ if (descMatch) result.description = decodeXmlEntities(descMatch[1]);
5236
+ return result;
5237
+ }
5238
+ function createAdtTypeResolver(obj) {
5239
+ const cache = /* @__PURE__ */ new Map();
5240
+ return { async resolve(name) {
5241
+ const key = name.toLowerCase();
5242
+ const cached = cache.get(key);
5243
+ if (cached) return cached;
5244
+ const dtelXml = await obj.fetchText(`/sap/bc/adt/ddic/dataelements/${encodeURIComponent(key)}`);
5245
+ if (dtelXml) {
5246
+ const result = resolveReferencedType(dtelXml, "E");
5247
+ cache.set(key, result);
5248
+ return result;
5249
+ }
5250
+ const structXml = await obj.fetchText(`/sap/bc/adt/ddic/structures/${encodeURIComponent(key)}`);
5251
+ const result = structXml ? resolveReferencedType(structXml, "S") : { comptype: "E" };
5252
+ cache.set(key, result);
5253
+ return result;
5254
+ } };
5255
+ }
5256
+ async function resolveCdsSource(obj, sources) {
5257
+ if (sources !== void 0) {
5258
+ if (sources.main === void 0) return { fallback: true };
5259
+ return { source: sources.main };
5260
+ }
5261
+ try {
5262
+ return { source: await obj.getSource() };
5263
+ } catch {
5264
+ return { fallback: true };
5265
+ }
5266
+ }
5267
+ function createFallbackFile(obj, ctx) {
5268
+ const objectName = ctx.getObjectName(obj);
5269
+ return [ctx.createFile(`${objectName}.tabl.xml`, ctx.toAbapGitXml(obj))];
5270
+ }
5271
+ async function buildTablValues(obj, def) {
5272
+ const lang = isoToSapLang(obj.language || void 0);
5273
+ const dd02v = buildDD02V(def, lang, obj.description ?? "");
5274
+ const resolver = createAdtTypeResolver(obj);
5275
+ const dd03pEntries = await buildDD03P(def.members, def.name.toUpperCase(), resolver);
5276
+ const values = { DD02V: stripEmpty(dd02v) };
5277
+ if ("getSettings" in obj && typeof obj.getSettings === "function") try {
5278
+ const settingsXml = await obj.getSettings();
5279
+ if (settingsXml) {
5280
+ const dd09l = parseSettingsToDD09L(settingsXml, def);
5281
+ if (dd09l) values.DD09L = stripEmpty(dd09l);
5282
+ }
5283
+ } catch {}
5284
+ if (dd03pEntries.length > 0) values.DD03P_TABLE = { DD03P: dd03pEntries.map((entry) => stripEmpty(entry)) };
5285
+ return values;
5286
+ }
5287
+ function buildTablXml(obj, ctx, values) {
5288
+ const fullPayload = {
5289
+ abap: {
5290
+ version: "1.0",
5291
+ values
5292
+ },
5293
+ version: "v1.0.0",
5294
+ serializer: "LCL_OBJECT_TABL",
5295
+ serializer_version: "v1.0.0"
5296
+ };
5297
+ return formatAbapGitXml(tabl.build(fullPayload, { pretty: true }));
5298
+ }
5299
+ async function serializeTabl(obj, ctx, options) {
5300
+ const objectName = ctx.getObjectName(obj);
5301
+ const resolved = await resolveCdsSource(obj, options?.sources);
5302
+ if ("fallback" in resolved) return createFallbackFile(obj, ctx);
5303
+ const { ast, errors } = parse(resolved.source);
5304
+ if (errors.length > 0 || ast.definitions.length === 0) return createFallbackFile(obj, ctx);
5305
+ const def = ast.definitions[0];
5306
+ const xml = buildTablXml(obj, ctx, await buildTablValues(obj, def));
5307
+ return [ctx.createFile(`${objectName}.tabl.xml`, xml)];
5308
+ }
5309
+ function fromAbapGitTabl({ DD02V } = {}) {
5310
+ return {
5311
+ name: (DD02V?.TABNAME ?? "").toUpperCase(),
5312
+ type: DD02V?.TABCLASS === "INTTAB" ? "TABL/DS" : "TABL/DT",
5313
+ description: DD02V?.DDTEXT,
5314
+ language: sapLangToIso(DD02V?.DDLANGUAGE),
5315
+ masterLanguage: sapLangToIso(DD02V?.DDLANGUAGE)
5316
+ };
5317
+ }
5318
+
5319
+ //#endregion
5320
+ //#region src/lib/handlers/objects/tabl.ts
5321
+ /**
5322
+ * Table/Structure (TABL) object handlers for abapGit format
5323
+ *
5324
+ * Serializes SAP tables (TABL/DT) and structures (TABL/DS) to
5325
+ * abapGit-compatible XML format by fetching the CDS-style source code
5326
+ * from SAP, parsing it with @abapify/acds, and mapping the AST into
5327
+ * DD02V/DD03P structures.
5328
+ *
5329
+ * Tables and structures share the same serialization logic but use
5330
+ * different ADT endpoints (ddic/tables vs ddic/structures).
5331
+ *
5332
+ * Data sources:
5333
+ * - blueSource GET: name, type, description, language
5334
+ * - source/main GET: CDS source with annotations and field definitions
5335
+ * → parsed via @abapify/acds into AST
5336
+ * → mapped into DD02V annotations and DD03P field entries
5337
+ * - Per named type: /sap/bc/adt/ddic/dataelements/{name} or /structures/{name}
5338
+ * → resolves COMPTYPE (E vs S), SHLPORIGIN, description (DDTEXT)
5339
+ */
5340
+ const tableHandler = createHandler(AdkTable, {
5341
+ schema: tabl,
5342
+ version: "v1.0.0",
5343
+ serializer: "LCL_OBJECT_TABL",
5344
+ serializer_version: "v1.0.0",
5345
+ toAbapGit: (obj) => ({ DD02V: {
5346
+ TABNAME: obj.name ?? "",
5347
+ DDLANGUAGE: isoToSapLang(obj.language || void 0),
5348
+ TABCLASS: "TRANSP",
5349
+ DDTEXT: obj.description ?? ""
5350
+ } }),
5351
+ serialize: (obj, ctx, options) => serializeTabl(obj, ctx, options),
5352
+ fromAbapGit: fromAbapGitTabl
5353
+ });
5354
+ const structureHandler = createHandler(AdkStructure, {
5355
+ schema: tabl,
5356
+ version: "v1.0.0",
5357
+ serializer: "LCL_OBJECT_TABL",
5358
+ serializer_version: "v1.0.0",
5359
+ toAbapGit: (obj) => ({ DD02V: {
5360
+ TABNAME: obj.name ?? "",
5361
+ DDLANGUAGE: isoToSapLang(obj.language || void 0),
5362
+ TABCLASS: "INTTAB",
5363
+ DDTEXT: obj.description ?? ""
5364
+ } }),
5365
+ serialize: (obj, ctx, options) => serializeTabl(obj, ctx, options),
5366
+ fromAbapGit: fromAbapGitTabl
5367
+ });
5368
+
5369
+ //#endregion
5370
+ //#region src/lib/handlers/objects/ttyp.ts
5371
+ /**
5372
+ * Table Type (TTYP) object handler for abapGit format
5373
+ *
5374
+ * Maps between abapGit DD40V codes and ADT API enum values.
5375
+ */
5376
+ /** DD40V ACCESSMODE → ADT accessType */
5377
+ const ACCESS_MODE_TO_ADT = {
5378
+ T: "standard",
5379
+ S: "sorted",
5380
+ H: "hashed"
5381
+ };
5382
+ const ACCESS_MODE_FROM_ADT = Object.fromEntries(Object.entries(ACCESS_MODE_TO_ADT).map(([k, v]) => [v, k]));
5383
+ /** DD40V KEYDEF → ADT primaryKey.definition */
5384
+ const KEY_DEF_TO_ADT = {
5385
+ D: "standard",
5386
+ K: "keyComponents",
5387
+ T: "rowType"
5388
+ };
5389
+ const KEY_DEF_FROM_ADT = Object.fromEntries(Object.entries(KEY_DEF_TO_ADT).map(([k, v]) => [v, k]));
5390
+ /** DD40V KEYKIND → ADT primaryKey.kind */
5391
+ const KEY_KIND_TO_ADT = {
5392
+ N: "nonUnique",
5393
+ U: "unique",
5394
+ G: "notSpecified"
5395
+ };
5396
+ const KEY_KIND_FROM_ADT = Object.fromEntries(Object.entries(KEY_KIND_TO_ADT).map(([k, v]) => [v, k]));
5397
+ /**
5398
+ * DD40V ROWKIND → ADT rowType.typeKind
5399
+ *
5400
+ * ROWKIND='' (empty) + DATATYPE set → predefinedAbapType (built-in like STRING, INT4)
5401
+ * ROWKIND='S' → dictionaryType (references a structure/table/data element)
5402
+ * ROWKIND='R' → ref type
5403
+ */
5404
+ const ROW_KIND_TO_ADT = {
5405
+ "": "predefinedAbapType",
5406
+ S: "dictionaryType",
5407
+ R: "refToDictionaryType"
5408
+ };
5409
+ const ROW_KIND_FROM_ADT = Object.fromEntries(Object.entries(ROW_KIND_TO_ADT).map(([k, v]) => [v, k]));
5410
+ /**
5411
+ * DD40V DATATYPE (DDIC code) → ADT builtInType.dataType (ABAP type name)
5412
+ *
5413
+ * abapGit stores 4-char DDIC internal codes (e.g. STRG, RSTR).
5414
+ * SAP ADT REST API uses ABAP type names (e.g. STRING, XSTRING).
5415
+ * Most types use the same name in both (CHAR, NUMC, INT4, etc.);
5416
+ * only a few differ.
5417
+ */
5418
+ const DATATYPE_TO_ADT = {
5419
+ STRG: "STRING",
5420
+ SSTR: "SSTRING",
5421
+ RSTR: "XSTRING",
5422
+ UTCL: "UTCLONG"
5423
+ };
5424
+ const DATATYPE_FROM_ADT = Object.fromEntries(Object.entries(DATATYPE_TO_ADT).map(([k, v]) => [v, k]));
5425
+ /**
5426
+ * DD40V TYPELEN — fixed internal byte length for predefined types.
5427
+ *
5428
+ * Variable-length types (STRING, XSTRING) have a fixed pointer size (8 bytes)
5429
+ * that abapGit stores as TYPELEN. This is not the same as LENG (display length).
5430
+ * We only need to emit TYPELEN for types that have noLeng=true (no external LENG).
5431
+ */
5432
+ const FIXED_TYPELEN = {
5433
+ STRG: 8,
5434
+ RSTR: 8
5435
+ };
5436
+ const tableTypeHandler = createHandler(AdkTableType, {
5437
+ schema: ttyp,
5438
+ version: "v1.0.0",
5439
+ serializer: "LCL_OBJECT_TTYP",
5440
+ serializer_version: "v1.0.0",
5441
+ toAbapGit: (obj) => {
5442
+ const data = obj.dataSync;
5443
+ const accessMode = ACCESS_MODE_FROM_ADT[data?.accessType ?? ""] ?? data?.accessType ?? "";
5444
+ const keyDef = KEY_DEF_FROM_ADT[data?.primaryKey?.definition ?? ""] ?? data?.primaryKey?.definition ?? "";
5445
+ const keyKind = KEY_KIND_FROM_ADT[data?.primaryKey?.kind ?? ""] ?? data?.primaryKey?.kind ?? "";
5446
+ const rowKind = ROW_KIND_FROM_ADT[data?.rowType?.typeKind ?? ""] ?? data?.rowType?.typeKind ?? "";
5447
+ const adtDataType = data?.rowType?.builtInType?.dataType ?? "";
5448
+ const ddicDataType = DATATYPE_FROM_ADT[adtDataType] ?? adtDataType;
5449
+ const typeLen = FIXED_TYPELEN[ddicDataType];
5450
+ return { DD40V: {
5451
+ TYPENAME: obj.name ?? "",
5452
+ DDLANGUAGE: isoToSapLang(data?.language || void 0),
5453
+ ROWTYPE: data?.rowType?.typeName || void 0,
5454
+ ROWKIND: rowKind || void 0,
5455
+ DATATYPE: ddicDataType,
5456
+ LENG: data?.rowType?.builtInType?.length ? String(data.rowType.builtInType.length).padStart(6, "0") : void 0,
5457
+ DECIMALS: data?.rowType?.builtInType?.decimals ? String(data.rowType.builtInType.decimals).padStart(6, "0") : void 0,
5458
+ ACCESSMODE: accessMode,
5459
+ KEYDEF: keyDef,
5460
+ KEYKIND: keyKind,
5461
+ DDTEXT: obj.description ?? "",
5462
+ TYPELEN: typeLen ? String(typeLen).padStart(6, "0") : void 0,
5463
+ ABAP_LANGUAGE_VERSION: abapLangVerFromAdt(data?.abapLanguageVersion)
5464
+ } };
5465
+ },
5466
+ fromAbapGit: ({ DD40V }) => {
5467
+ const accessType = ACCESS_MODE_TO_ADT[DD40V?.ACCESSMODE ?? ""] ?? DD40V?.ACCESSMODE;
5468
+ const keyDef = KEY_DEF_TO_ADT[DD40V?.KEYDEF ?? ""] ?? DD40V?.KEYDEF;
5469
+ const keyKind = KEY_KIND_TO_ADT[DD40V?.KEYKIND ?? ""] ?? DD40V?.KEYKIND;
5470
+ const rowKind = ROW_KIND_TO_ADT[DD40V?.ROWKIND ?? ""] ?? DD40V?.ROWKIND;
5471
+ const ddicCode = DD40V?.DATATYPE ?? "";
5472
+ const adtDataType = DATATYPE_TO_ADT[ddicCode] ?? ddicCode;
5473
+ const isPredefined = rowKind === "predefinedAbapType";
5474
+ return {
5475
+ name: (DD40V?.TYPENAME ?? "").toUpperCase(),
5476
+ type: "TTYP/DA",
5477
+ description: DD40V?.DDTEXT,
5478
+ language: sapLangToIso(DD40V?.DDLANGUAGE),
5479
+ masterLanguage: sapLangToIso(DD40V?.DDLANGUAGE),
5480
+ abapLanguageVersion: abapLangVerToAdt(DD40V?.ABAP_LANGUAGE_VERSION),
5481
+ rowType: {
5482
+ typeKind: rowKind,
5483
+ typeName: isPredefined ? "" : DD40V?.ROWTYPE || "",
5484
+ builtInType: {
5485
+ dataType: ddicCode ? adtDataType : "",
5486
+ length: DD40V?.LENG ? Number(DD40V.LENG) : 0,
5487
+ decimals: DD40V?.DECIMALS ? Number(DD40V.DECIMALS) : 0
5488
+ },
5489
+ rangeType: ""
5490
+ },
5491
+ accessType,
5492
+ primaryKey: {
5493
+ definition: keyDef,
5494
+ kind: keyKind,
5495
+ components: { component: [] },
5496
+ alias: ""
5497
+ },
5498
+ secondaryKeys: { allowed: "notSpecified" }
5499
+ };
5500
+ }
5501
+ });
3446
5502
 
3447
5503
  //#endregion
3448
- //#region src/lib/handlers/objects/tabl.ts
5504
+ //#region src/lib/handlers/source-resolver.ts
3449
5505
  /**
3450
- * Table/Structure (TABL) object handlers for abapGit format
3451
- *
3452
- * Serializes SAP tables (TABL/DT) and structures (TABL/DS) to
3453
- * abapGit-compatible XML format by fetching the CDS-style source code
3454
- * from SAP, parsing it with @abapify/acds, and mapping the AST into
3455
- * DD02V/DD03P structures.
3456
- *
3457
- * Tables and structures share the same serialization logic but use
3458
- * different ADT endpoints (ddic/tables vs ddic/structures).
5506
+ * Resolve the `main` source component for an AFF source handler.
3459
5507
  *
3460
- * Data sources:
3461
- * - blueSource GET: name, type, description, language
3462
- * - source/main GET: CDS source with annotations and field definitions
3463
- * → parsed via @abapify/acds into AST
3464
- * → mapped into DD02V annotations and DD03P field entries
3465
- * - Per named type: /sap/bc/adt/ddic/dataelements/{name} or /structures/{name}
3466
- * → resolves COMPTYPE (E vs S), SHLPORIGIN, description (DDTEXT)
5508
+ * If `sources` is supplied, only the `main` key is accepted — any other
5509
+ * key throws `FORMAT_SOURCE_COMPONENT_UNSUPPORTED`. When `sources` is
5510
+ * absent, falls back to `object.getSource()`.
3467
5511
  */
5512
+ async function resolveMainSource(object, sources, typeLabel) {
5513
+ if (sources !== void 0) {
5514
+ const keys = Object.keys(sources);
5515
+ if (keys.some((key) => key !== "main")) throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", `${typeLabel} only supports the 'main' source component; received ${keys.join(", ")}.`);
5516
+ return sources.main;
5517
+ }
5518
+ return typeof object.getSource === "function" ? await object.getSource() : "";
5519
+ }
3468
5520
  /**
3469
- * Strip undefined/empty-string values from an object
3470
- * to avoid emitting empty XML elements
5521
+ * Build an AFF JSON metadata file. `extra` fields are merged at the
5522
+ * top level alongside `formatVersion` and `header`.
3471
5523
  */
3472
- function stripEmpty(obj) {
3473
- const result = {};
3474
- for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== "") result[key] = value;
3475
- return result;
5524
+ function buildAffJson(description, originalLanguage, abapLanguageVersion, extra = {}) {
5525
+ return `${JSON.stringify({
5526
+ formatVersion: "1",
5527
+ header: {
5528
+ description,
5529
+ originalLanguage: originalLanguage.toLowerCase(),
5530
+ ...abapLanguageVersion ? { abapLanguageVersion } : {}
5531
+ },
5532
+ ...extra
5533
+ }, null, 2)}\n`;
3476
5534
  }
3477
5535
  /**
3478
- * Create a TypeResolver that resolves named types via ADT endpoints.
3479
- * Tries data element first, then structure. Caches results.
5536
+ * Create the source file for an AFF handler if the source should be
5537
+ * included. Returns an empty array if the source is absent or empty.
3480
5538
  */
3481
- function createAdtTypeResolver(obj) {
3482
- const cache = /* @__PURE__ */ new Map();
3483
- return { async resolve(name) {
3484
- const key = name.toLowerCase();
3485
- if (cache.has(key)) return cache.get(key);
3486
- const dtelXml = await obj.fetchText(`/sap/bc/adt/ddic/dataelements/${encodeURIComponent(key)}`);
3487
- if (dtelXml) {
3488
- const result = { comptype: "E" };
3489
- if (dtelXml.match(/<dtel:searchHelp>[^<]+<\/dtel:searchHelp>/)) result.shlporigin = "D";
3490
- const descMatch = dtelXml.match(/adtcore:description="([^"]+)"/);
3491
- if (descMatch) result.description = descMatch[1];
3492
- cache.set(key, result);
3493
- return result;
3494
- }
3495
- const structXml = await obj.fetchText(`/sap/bc/adt/ddic/structures/${encodeURIComponent(key)}`);
3496
- if (structXml) {
3497
- const result = { comptype: "S" };
3498
- const descMatch = structXml.match(/adtcore:description="([^"]+)"/);
3499
- if (descMatch) result.description = descMatch[1];
3500
- cache.set(key, result);
3501
- return result;
3502
- }
3503
- const fallback = { comptype: "E" };
3504
- cache.set(key, fallback);
3505
- return fallback;
3506
- } };
5539
+ function createAffSourceFile(ctx, params) {
5540
+ if (params.source === void 0) return [];
5541
+ if (params.suppliedSource === void 0 && params.source === "") return [];
5542
+ const name = ctx.getObjectName(params.object);
5543
+ return [ctx.createFile(`${name}.${ctx.fileExtension}.${params.sourceExt}`, params.source)];
3507
5544
  }
3508
5545
  /**
3509
- * Parse ADT table settings XML into DD09L data for abapGit serialization.
5546
+ * Full serialize implementation for AFF source handlers.
3510
5547
  *
3511
- * Uses the typed `tablesettings` schema from @abapify/adt-schemas to parse
3512
- * the ADT endpoint response (/sap/bc/adt/ddic/db/settings/{name}).
5548
+ * Handles the complete source+metadata file layout in one call:
5549
+ * `<name>.<ext>.<sourceExt>` — source text (when included)
5550
+ * `<name>.<jsonExt>` — metadata JSON
3513
5551
  *
3514
- * Mapping:
3515
- * tableSettings.sizeCategory → DD09L.TABKAT
3516
- * tableSettings.dataClassCategory → DD09L.TABART
3517
- * tableSettings.buffering.allowed → DD09L.BUFALLOW
3518
- * tableSettings.buffering.type → DD09L.PUFFERUNG
3519
- * tableSettings.buffering.areaKeyFields → DD09L.SCHFELDANZ
3520
- * tableSettings.loggingEnabled → DD09L.PROTOKOLL
5552
+ * `extra` is merged into the JSON body for handler-specific fields
5553
+ * (e.g. SRVD's `generalInformation`, DDLS's `sourceOrigin`).
3521
5554
  */
3522
- function parseSettingsToDD09L(settingsXml, tableName) {
3523
- let settings;
3524
- try {
3525
- const parsed = tablesettings.parse(settingsXml);
3526
- if (!("tableSettings" in parsed)) return void 0;
3527
- settings = parsed.tableSettings;
3528
- } catch {
3529
- return;
3530
- }
3531
- const dd09l = {};
3532
- dd09l.TABNAME = tableName;
3533
- dd09l.AS4LOCAL = "A";
3534
- if (settings.sizeCategory) dd09l.TABKAT = settings.sizeCategory;
3535
- if (settings.dataClassCategory) dd09l.TABART = settings.dataClassCategory;
3536
- if (settings.buffering?.allowed) dd09l.BUFALLOW = settings.buffering.allowed;
3537
- const bufType = settings.buffering?.type;
3538
- if (bufType) dd09l.PUFFERUNG = bufType;
3539
- const areaKeyFields = settings.buffering?.areaKeyFields;
3540
- if (areaKeyFields && areaKeyFields !== "0") dd09l.SCHFELDANZ = areaKeyFields;
3541
- if (settings.loggingEnabled) dd09l.PROTOKOLL = "X";
3542
- return dd09l;
5555
+ async function serializeAffSource(object, ctx, options, config) {
5556
+ const source = await resolveMainSource(object, options, config.typeLabel);
5557
+ const name = ctx.getObjectName(object);
5558
+ return [...createAffSourceFile(ctx, {
5559
+ object,
5560
+ source,
5561
+ suppliedSource: options?.main,
5562
+ sourceExt: config.sourceExt
5563
+ }), ctx.createFile(`${name}.${config.jsonExt}`, buildAffJson(object.description || String(object.name ?? ""), object.originalLanguage ?? "en", object.abapLanguageVersion, config.extra))];
3543
5564
  }
3544
5565
  /**
3545
- * Shared serialize logic for tables and structures.
3546
- * Both use CDS source → DD02V/DD03P mapping.
5566
+ * Shared `getSource` handler function for source-driven AFF objects
5567
+ * (BDEF, SRVD). Delegates to `obj.getSource()` when available.
3547
5568
  */
3548
- async function serializeTabl(obj, ctx) {
3549
- const objectName = ctx.getObjectName(obj);
3550
- const lang = isoToSapLang(obj.language || void 0);
3551
- let cdsSource;
3552
- try {
3553
- cdsSource = await obj.getSource();
3554
- } catch {
3555
- const xmlContent = ctx.toAbapGitXml(obj);
3556
- return [ctx.createFile(`${objectName}.tabl.xml`, xmlContent)];
3557
- }
3558
- const { ast, errors } = parse(cdsSource);
3559
- if (errors.length > 0 || ast.definitions.length === 0) {
3560
- const xmlContent = ctx.toAbapGitXml(obj);
3561
- return [ctx.createFile(`${objectName}.tabl.xml`, xmlContent)];
3562
- }
3563
- const def = ast.definitions[0];
3564
- const dd02v = buildDD02V(def, lang, obj.description ?? "");
3565
- const resolver = createAdtTypeResolver(obj);
3566
- const dd03pEntries = await buildDD03P(def.members, def.name.toUpperCase(), resolver);
3567
- const values = { DD02V: stripEmpty(dd02v) };
3568
- if ("getSettings" in obj && typeof obj.getSettings === "function") try {
3569
- const settingsXml = await obj.getSettings();
3570
- if (settingsXml) {
3571
- const dd09l = parseSettingsToDD09L(settingsXml, def.name.toUpperCase());
3572
- if (dd09l) values.DD09L = stripEmpty(dd09l);
3573
- }
3574
- } catch {}
3575
- if (dd03pEntries.length > 0) values.DD03P_TABLE = { DD03P: dd03pEntries.map((entry) => stripEmpty(entry)) };
3576
- const fullPayload = {
3577
- abap: {
3578
- version: "1.0",
3579
- values
3580
- },
3581
- version: "v1.0.0",
3582
- serializer: "LCL_OBJECT_TABL",
3583
- serializer_version: "v1.0.0"
3584
- };
3585
- const xml = formatAbapGitXml(tabl.build(fullPayload, { pretty: true }));
3586
- return [ctx.createFile(`${objectName}.tabl.xml`, xml)];
5569
+ function affGetSource(obj) {
5570
+ return typeof obj?.getSource === "function" ? Promise.resolve(obj.getSource()) : Promise.resolve("");
3587
5571
  }
3588
- /** Shared fromAbapGit logic for both tables and structures */
3589
- function fromAbapGitTabl({ DD02V } = {}) {
5572
+ /**
5573
+ * Shared `fromAbapGit` handler function for source-driven AFF objects.
5574
+ * Extracts the uppercased name from the SKEY envelope.
5575
+ */
5576
+ function affFromAbapGit(SKEY) {
5577
+ return { name: String(SKEY?.NAME ?? "").toUpperCase() };
5578
+ }
5579
+ /**
5580
+ * Shared `setSources` handler function for source-driven AFF objects.
5581
+ * Stores the `main` source on a `_pendingSource` property.
5582
+ */
5583
+ function affSetSources(obj, sources) {
5584
+ if (sources.main !== void 0) obj._pendingSource = sources.main;
5585
+ }
5586
+ /**
5587
+ * Shared `fromAffJson` handler function for source-driven AFF objects.
5588
+ * Extracts the object name from the JSON header or falls back to the
5589
+ * filename-derived name passed in. The AFF JSON format stores the
5590
+ * description in `header.description` and the original language in
5591
+ * `header.originalLanguage`. Both are preserved for round-trip fidelity.
5592
+ */
5593
+ function affFromAffJson(json, fallbackName) {
5594
+ const header = json.header;
3590
5595
  return {
3591
- name: (DD02V?.TABNAME ?? "").toUpperCase(),
3592
- type: DD02V?.TABCLASS === "INTTAB" ? "TABL/DS" : "TABL/DT",
3593
- description: DD02V?.DDTEXT,
3594
- language: sapLangToIso(DD02V?.DDLANGUAGE),
3595
- masterLanguage: sapLangToIso(DD02V?.DDLANGUAGE)
5596
+ name: fallbackName,
5597
+ description: header?.description,
5598
+ originalLanguage: header?.originalLanguage,
5599
+ abapLanguageVersion: header?.abapLanguageVersion
3596
5600
  };
3597
5601
  }
3598
- const tableHandler = createHandler(AdkTable, {
3599
- schema: tabl,
5602
+ /**
5603
+ * Dual-format serialize for objects that support both AFF JSON and legacy XML.
5604
+ *
5605
+ * When `options.format === 'legacy'`, produces a legacy abapGit XML metadata
5606
+ * file (via `ctx.toAbapGitXml`) plus the source file. Otherwise (default or
5607
+ * `format === 'aff'`), delegates to `serializeAffSource` to produce the AFF
5608
+ * JSON sidecar plus source.
5609
+ */
5610
+ async function serializeDualFormat(object, ctx, options, config) {
5611
+ if (options?.format === "legacy") {
5612
+ const source = await resolveMainSource(object, options?.sources, config.typeLabel);
5613
+ const name = ctx.getObjectName(object);
5614
+ const files = [];
5615
+ if (source !== void 0) files.push(ctx.createFile(`${name}.${ctx.fileExtension}.${config.sourceExt}`, source));
5616
+ files.push(ctx.createFile(`${name}.${ctx.fileExtension}.xml`, ctx.toAbapGitXml(object)));
5617
+ return files;
5618
+ }
5619
+ return serializeAffSource(object, ctx, options?.sources, config);
5620
+ }
5621
+
5622
+ //#endregion
5623
+ //#region src/lib/handlers/objects/bdef.ts
5624
+ /**
5625
+ * BDEF (RAP Behavior Definition) object handler for abapGit format
5626
+ *
5627
+ * BDEF is source-driven: the semantic content lives in an `.abdl` file.
5628
+ * Supports BOTH formats:
5629
+ * - AFF (default): `.bdef.abdl` source + `.bdef.json` metadata sidecar
5630
+ * - Legacy XML: `.bdef.abdl` source + `.bdef.xml` metadata
5631
+ *
5632
+ * File layout (AFF):
5633
+ * src/zbp_foo.bdef.abdl — behavior source text
5634
+ * src/zbp_foo.bdef.json — ABAP File Formats metadata
5635
+ *
5636
+ * File layout (legacy):
5637
+ * src/zbp_foo.bdef.abdl — behavior source text
5638
+ * src/zbp_foo.bdef.xml — legacy abapGit XML metadata
5639
+ */
5640
+ const behaviorDefinitionHandler = createHandler("BDEF", {
5641
+ schema: bdef,
3600
5642
  version: "v1.0.0",
3601
- serializer: "LCL_OBJECT_TABL",
5643
+ serializer: "LCL_OBJECT_BDEF",
3602
5644
  serializer_version: "v1.0.0",
3603
- toAbapGit: (obj) => ({ DD02V: {
3604
- TABNAME: obj.name ?? "",
3605
- DDLANGUAGE: isoToSapLang(obj.language || void 0),
3606
- TABCLASS: "TRANSP",
3607
- DDTEXT: obj.description ?? ""
5645
+ toAbapGit: (obj) => ({ SKEY: {
5646
+ TYPE: "BDEF",
5647
+ NAME: String(obj?.name ?? "").toUpperCase()
3608
5648
  } }),
3609
- serialize: (obj, ctx) => serializeTabl(obj, ctx),
3610
- fromAbapGit: fromAbapGitTabl
5649
+ getSource: affGetSource,
5650
+ fromAbapGit: ({ SKEY }) => affFromAbapGit(SKEY),
5651
+ fromAffJson: (json) => affFromAffJson(json, ""),
5652
+ serialize: (object, ctx, options) => serializeDualFormat(object, ctx, options, {
5653
+ typeLabel: "BDEF",
5654
+ sourceExt: "abdl",
5655
+ jsonExt: `${ctx.fileExtension}.json`
5656
+ }),
5657
+ setSources: affSetSources
3611
5658
  });
3612
- const structureHandler = createHandler(AdkStructure, {
3613
- schema: tabl,
5659
+
5660
+ //#endregion
5661
+ //#region src/lib/handlers/objects/srvd.ts
5662
+ /**
5663
+ * SRVD (RAP Service Definition) object handler for abapGit format
5664
+ *
5665
+ * SRVD is source-driven: the semantic content lives in an `.acds` file.
5666
+ * Supports BOTH formats:
5667
+ * - AFF (default): `.srvd.acds` source + `.srvd.json` metadata sidecar
5668
+ * - Legacy XML: `.srvd.acds` source + `.srvd.xml` metadata
5669
+ *
5670
+ * File layout (AFF):
5671
+ * src/zui_foo.srvd.acds — service source text
5672
+ * src/zui_foo.srvd.json — ABAP File Formats metadata
5673
+ *
5674
+ * File layout (legacy):
5675
+ * src/zui_foo.srvd.acds — service source text
5676
+ * src/zui_foo.srvd.xml — legacy abapGit XML metadata
5677
+ */
5678
+ const serviceDefinitionHandler = createHandler("SRVD", {
5679
+ schema: srvd,
3614
5680
  version: "v1.0.0",
3615
- serializer: "LCL_OBJECT_TABL",
5681
+ serializer: "LCL_OBJECT_SRVD",
3616
5682
  serializer_version: "v1.0.0",
3617
- toAbapGit: (obj) => ({ DD02V: {
3618
- TABNAME: obj.name ?? "",
3619
- DDLANGUAGE: isoToSapLang(obj.language || void 0),
3620
- TABCLASS: "INTTAB",
3621
- DDTEXT: obj.description ?? ""
5683
+ toAbapGit: (obj) => ({ SKEY: {
5684
+ TYPE: "SRVD",
5685
+ NAME: String(obj?.name ?? "").toUpperCase()
3622
5686
  } }),
3623
- serialize: (obj, ctx) => serializeTabl(obj, ctx),
3624
- fromAbapGit: fromAbapGitTabl
5687
+ getSource: affGetSource,
5688
+ fromAbapGit: ({ SKEY }) => affFromAbapGit(SKEY),
5689
+ fromAffJson: (json) => ({
5690
+ ...affFromAffJson(json, ""),
5691
+ sourceOrigin: json?.generalInformation?.sourceOrigin,
5692
+ sourceType: json?.generalInformation?.sourceType
5693
+ }),
5694
+ serialize: (object, ctx, options) => serializeDualFormat(object, ctx, options, {
5695
+ typeLabel: "SRVD",
5696
+ sourceExt: "acds",
5697
+ jsonExt: `${ctx.fileExtension}.json`,
5698
+ extra: { generalInformation: {
5699
+ sourceOrigin: object.sourceOrigin ?? "abapDevelopmentTools",
5700
+ sourceType: object.sourceType ?? "definition"
5701
+ } }
5702
+ }),
5703
+ setSources: affSetSources
5704
+ });
5705
+
5706
+ //#endregion
5707
+ //#region src/lib/handlers/objects/srvb.ts
5708
+ /**
5709
+ * SRVB (RAP Service Binding) object handler for abapGit format
5710
+ *
5711
+ * Unlike BDEF/SRVD, SRVB is **metadata-only** — there is no source text.
5712
+ * Supports BOTH formats:
5713
+ * - Legacy XML (default): `<name>.srvb.xml` with SKEY + BINDING block
5714
+ * - AFF JSON: `<name>.srvb.json` with formatVersion + header
5715
+ *
5716
+ * File layout (legacy):
5717
+ * src/zui_foo.srvb.xml — metadata only
5718
+ *
5719
+ * File layout (AFF):
5720
+ * src/zui_foo.srvb.json — metadata only
5721
+ *
5722
+ * Mirrors the minimal-block approach used by
5723
+ * zcl_abapgit_object_srvb (abapGit upstream serialiser).
5724
+ */
5725
+ const serviceBindingHandler = createHandler("SRVB", {
5726
+ schema: srvb,
5727
+ version: "v1.0.0",
5728
+ serializer: "LCL_OBJECT_SRVB",
5729
+ serializer_version: "v1.0.0",
5730
+ toAbapGit: (obj) => ({
5731
+ SKEY: {
5732
+ TYPE: "SRVB",
5733
+ NAME: String(obj?.name ?? "").toUpperCase()
5734
+ },
5735
+ BINDING: {
5736
+ TYPE: "odataV4",
5737
+ VERSION: "4",
5738
+ CATEGORY: "odata_v4_ui"
5739
+ }
5740
+ }),
5741
+ fromAbapGit: ({ SKEY }) => ({ name: String(SKEY?.NAME ?? "").toUpperCase() }),
5742
+ fromAffJson: (json) => {
5743
+ const header = json?.header;
5744
+ const binding = json?.binding;
5745
+ return {
5746
+ name: "",
5747
+ description: header?.description,
5748
+ originalLanguage: header?.originalLanguage,
5749
+ abapLanguageVersion: header?.abapLanguageVersion,
5750
+ bindingType: binding?.bindingType,
5751
+ bindingTypeCategory: binding?.bindingTypeCategory,
5752
+ services: binding?.services
5753
+ };
5754
+ },
5755
+ async serialize(object, ctx, options) {
5756
+ const objectName = ctx.getObjectName(object);
5757
+ if (options?.format === "aff") {
5758
+ const bindingType = String(object?.bindingType ?? object?.data?.bindingType ?? "odataV4");
5759
+ const bindingTypeCategory = String(object?.bindingTypeCategory ?? object?.data?.bindingTypeCategory ?? "odata_v4_ui");
5760
+ const services = object?.services ?? object?.data?.services ?? [];
5761
+ const jsonContent = buildAffJson(String(object?.description ?? object?.name ?? ""), String(object?.originalLanguage ?? "en"), object?.abapLanguageVersion, { binding: {
5762
+ bindingType,
5763
+ bindingTypeCategory,
5764
+ ...Array.isArray(services) && services.length > 0 ? { services } : {}
5765
+ } });
5766
+ return [ctx.createFile(`${objectName}.${ctx.fileExtension}.json`, jsonContent)];
5767
+ }
5768
+ const xmlContent = ctx.toAbapGitXml(object);
5769
+ return [ctx.createFile(`${objectName}.${ctx.fileExtension}.xml`, xmlContent)];
5770
+ }
3625
5771
  });
3626
5772
 
3627
5773
  //#endregion
3628
- //#region src/lib/handlers/objects/ttyp.ts
5774
+ //#region src/lib/handlers/objects/cds-aff-source.ts
5775
+ /** Create the shared dual-format source-and-metadata layout used by CDS source types.
5776
+ * Supports both AFF JSON (default) and legacy abapGit XML metadata. */
5777
+ function createCdsAffSourceHandler(type, options) {
5778
+ const schema = options?.schema ?? bdef;
5779
+ return createHandler(type, {
5780
+ schema,
5781
+ version: "v1.0.0",
5782
+ serializer: `LCL_OBJECT_${type}`,
5783
+ serializer_version: "v1.0.0",
5784
+ toAbapGit: (obj) => ({ SKEY: {
5785
+ TYPE: type,
5786
+ NAME: String(obj?.name ?? "").toUpperCase()
5787
+ } }),
5788
+ getSource: affGetSource,
5789
+ fromAbapGit: ({ SKEY }) => affFromAbapGit(SKEY),
5790
+ fromAffJson: (json) => options?.fromAffJsonExtra ? {
5791
+ ...affFromAffJson(json, ""),
5792
+ ...options.fromAffJsonExtra(json)
5793
+ } : affFromAffJson(json, ""),
5794
+ setSources: affSetSources,
5795
+ serialize: (object, ctx, opts) => serializeDualFormat(object, ctx, opts, {
5796
+ typeLabel: type,
5797
+ sourceExt: "acds",
5798
+ jsonExt: `${ctx.fileExtension}.json`,
5799
+ extra: options?.serializeExtra?.(object)
5800
+ })
5801
+ });
5802
+ }
5803
+
5804
+ //#endregion
5805
+ //#region src/lib/handlers/objects/dcls.ts
3629
5806
  /**
3630
- * Table Type (TTYP) object handler for abapGit format
5807
+ * DCLS (ABAP Data Control Language Source) object handler for abapGit format
3631
5808
  *
3632
- * Maps between abapGit DD40V codes and ADT API enum values.
5809
+ * DCLS is source-driven: the semantic content lives in an `.acds` file.
5810
+ * Supports BOTH formats:
5811
+ * - AFF (default): `.dcls.acds` source + `.dcls.json` metadata sidecar
5812
+ * - Legacy XML: `.dcls.acds` source + `.dcls.xml` metadata
3633
5813
  */
3634
- /** DD40V ACCESSMODE → ADT accessType */
3635
- const ACCESS_MODE_TO_ADT = {
3636
- T: "standard",
3637
- S: "sorted",
3638
- H: "hashed"
3639
- };
3640
- const ACCESS_MODE_FROM_ADT = Object.fromEntries(Object.entries(ACCESS_MODE_TO_ADT).map(([k, v]) => [v, k]));
3641
- /** DD40V KEYDEF → ADT primaryKey.definition */
3642
- const KEY_DEF_TO_ADT = {
3643
- D: "standard",
3644
- K: "keyComponents",
3645
- T: "rowType"
3646
- };
3647
- const KEY_DEF_FROM_ADT = Object.fromEntries(Object.entries(KEY_DEF_TO_ADT).map(([k, v]) => [v, k]));
3648
- /** DD40V KEYKIND → ADT primaryKey.kind */
3649
- const KEY_KIND_TO_ADT = {
3650
- N: "nonUnique",
3651
- U: "unique",
3652
- G: "notSpecified"
3653
- };
3654
- const KEY_KIND_FROM_ADT = Object.fromEntries(Object.entries(KEY_KIND_TO_ADT).map(([k, v]) => [v, k]));
5814
+ const dclSourceHandler = createCdsAffSourceHandler("DCLS", { schema: dcls });
5815
+
5816
+ //#endregion
5817
+ //#region src/lib/handlers/objects/ddls.ts
3655
5818
  /**
3656
- * DD40V ROWKIND → ADT rowType.typeKind
5819
+ * DDLS (Data Definition Language Source) object handler for abapGit format
3657
5820
  *
3658
- * ROWKIND='' (empty) + DATATYPE set → predefinedAbapType (built-in like STRING, INT4)
3659
- * ROWKIND='S' → dictionaryType (references a structure/table/data element)
3660
- * ROWKIND='R' → ref type
5821
+ * DDLS is source-driven: the semantic content lives in an `.acds` file.
5822
+ * Supports BOTH formats:
5823
+ * - AFF (default): `.ddls.acds` source + `.ddls.json` metadata sidecar
5824
+ * - Legacy XML: `.ddls.acds` source + `.ddls.xml` metadata
3661
5825
  */
3662
- const ROW_KIND_TO_ADT = {
3663
- "": "predefinedAbapType",
3664
- S: "dictionaryType",
3665
- R: "refToDictionaryType"
3666
- };
3667
- const ROW_KIND_FROM_ADT = Object.fromEntries(Object.entries(ROW_KIND_TO_ADT).map(([k, v]) => [v, k]));
5826
+ const ddlSourceHandler = createCdsAffSourceHandler("DDLS", {
5827
+ schema: ddls,
5828
+ fromAffJsonExtra: (json) => ({
5829
+ sourceOrigin: json?.sourceOrigin,
5830
+ sourceType: json?.sourceType
5831
+ }),
5832
+ serializeExtra: (object) => ({
5833
+ sourceOrigin: object.sourceOrigin ?? "abapDevelopmentTools",
5834
+ sourceType: object.sourceType ?? "unknown"
5835
+ })
5836
+ });
5837
+
5838
+ //#endregion
5839
+ //#region src/lib/handlers/objects/ddlx.ts
3668
5840
  /**
3669
- * DD40V DATATYPE (DDIC code) → ADT builtInType.dataType (ABAP type name)
5841
+ * DDLX (CDS Metadata Extension) object handler for abapGit format
3670
5842
  *
3671
- * abapGit stores 4-char DDIC internal codes (e.g. STRG, RSTR).
3672
- * SAP ADT REST API uses ABAP type names (e.g. STRING, XSTRING).
3673
- * Most types use the same name in both (CHAR, NUMC, INT4, etc.);
3674
- * only a few differ.
5843
+ * DDLX is source-driven: the semantic content lives in an `.acds` file.
5844
+ * Supports BOTH formats:
5845
+ * - AFF (default): `.ddlx.acds` source + `.ddlx.json` metadata sidecar
5846
+ * - Legacy XML: `.ddlx.acds` source + `.ddlx.xml` metadata
3675
5847
  */
3676
- const DATATYPE_TO_ADT = {
3677
- STRG: "STRING",
3678
- SSTR: "SSTRING",
3679
- RSTR: "XSTRING",
3680
- UTCL: "UTCLONG"
3681
- };
3682
- const DATATYPE_FROM_ADT = Object.fromEntries(Object.entries(DATATYPE_TO_ADT).map(([k, v]) => [v, k]));
5848
+ const ddlExtensionHandler = createCdsAffSourceHandler("DDLX", { schema: ddlx });
5849
+
5850
+ //#endregion
5851
+ //#region src/lib/handlers/objects/msag.ts
3683
5852
  /**
3684
- * DD40V TYPELEN — fixed internal byte length for predefined types.
5853
+ * MSAG (Message Class) object handler for abapGit format
3685
5854
  *
3686
- * Variable-length types (STRING, XSTRING) have a fixed pointer size (8 bytes)
3687
- * that abapGit stores as TYPELEN. This is not the same as LENG (display length).
3688
- * We only need to emit TYPELEN for types that have noLeng=true (no external LENG).
5855
+ * Message classes are XML-only (no source code). The abapGit format stores
5856
+ * the class header in T100A and the individual message lines in the T100
5857
+ * table (wrapped in <item> elements).
3689
5858
  */
3690
- const FIXED_TYPELEN = {
3691
- STRG: 8,
3692
- RSTR: 8
3693
- };
3694
- const tableTypeHandler = createHandler(AdkTableType, {
3695
- schema: ttyp,
5859
+ const messageClassHandler = createHandler("MSAG", {
5860
+ schema: msag,
3696
5861
  version: "v1.0.0",
3697
- serializer: "LCL_OBJECT_TTYP",
5862
+ serializer: "LCL_OBJECT_MSAG",
3698
5863
  serializer_version: "v1.0.0",
3699
5864
  toAbapGit: (obj) => {
3700
- const data = obj.dataSync;
3701
- const accessMode = ACCESS_MODE_FROM_ADT[data?.accessType ?? ""] ?? data?.accessType ?? "";
3702
- const keyDef = KEY_DEF_FROM_ADT[data?.primaryKey?.definition ?? ""] ?? data?.primaryKey?.definition ?? "";
3703
- const keyKind = KEY_KIND_FROM_ADT[data?.primaryKey?.kind ?? ""] ?? data?.primaryKey?.kind ?? "";
3704
- const rowKind = ROW_KIND_FROM_ADT[data?.rowType?.typeKind ?? ""] ?? data?.rowType?.typeKind ?? "";
3705
- const adtDataType = data?.rowType?.builtInType?.dataType ?? "";
3706
- const ddicDataType = DATATYPE_FROM_ADT[adtDataType] ?? adtDataType;
3707
- const typeLen = FIXED_TYPELEN[ddicDataType];
3708
- return { DD40V: {
3709
- TYPENAME: obj.name ?? "",
3710
- DDLANGUAGE: isoToSapLang(data?.language || void 0),
3711
- ROWTYPE: data?.rowType?.typeName || void 0,
3712
- ROWKIND: rowKind || void 0,
3713
- DATATYPE: ddicDataType,
3714
- LENG: data?.rowType?.builtInType?.length ? String(data.rowType.builtInType.length).padStart(6, "0") : void 0,
3715
- DECIMALS: data?.rowType?.builtInType?.decimals ? String(data.rowType.builtInType.decimals).padStart(6, "0") : void 0,
3716
- ACCESSMODE: accessMode,
3717
- KEYDEF: keyDef,
3718
- KEYKIND: keyKind,
3719
- DDTEXT: obj.description ?? "",
3720
- TYPELEN: typeLen ? String(typeLen).padStart(6, "0") : void 0,
3721
- ABAP_LANGUAGE_VERSION: abapLangVerFromAdt(data?.abapLanguageVersion)
3722
- } };
5865
+ const messages = obj.messages ?? [];
5866
+ return {
5867
+ T100A: {
5868
+ ARBGB: String(obj.name ?? "").toUpperCase(),
5869
+ MASTERLANG: isoToSapLang(obj.masterLanguage || obj.language),
5870
+ STEXT: obj.description ?? ""
5871
+ },
5872
+ T100: messages.length > 0 ? { item: messages.map((m) => ({
5873
+ SPRSL: isoToSapLang(m.language || obj.masterLanguage || obj.language),
5874
+ ARBGB: String(obj.name ?? "").toUpperCase(),
5875
+ MSGNR: String(m.number ?? "").padStart(3, "0"),
5876
+ TEXT: m.text ?? ""
5877
+ })) } : void 0
5878
+ };
3723
5879
  },
3724
- fromAbapGit: ({ DD40V }) => {
3725
- const accessType = ACCESS_MODE_TO_ADT[DD40V?.ACCESSMODE ?? ""] ?? DD40V?.ACCESSMODE;
3726
- const keyDef = KEY_DEF_TO_ADT[DD40V?.KEYDEF ?? ""] ?? DD40V?.KEYDEF;
3727
- const keyKind = KEY_KIND_TO_ADT[DD40V?.KEYKIND ?? ""] ?? DD40V?.KEYKIND;
3728
- const rowKind = ROW_KIND_TO_ADT[DD40V?.ROWKIND ?? ""] ?? DD40V?.ROWKIND;
3729
- const ddicCode = DD40V?.DATATYPE ?? "";
3730
- const adtDataType = DATATYPE_TO_ADT[ddicCode] ?? ddicCode;
3731
- const isPredefined = rowKind === "predefinedAbapType";
5880
+ fromAbapGit: ({ T100A, T100 }) => {
5881
+ const rawItems = T100?.item ?? [];
5882
+ const items = Array.isArray(rawItems) ? rawItems : [rawItems];
3732
5883
  return {
3733
- name: (DD40V?.TYPENAME ?? "").toUpperCase(),
3734
- type: "TTYP/DA",
3735
- description: DD40V?.DDTEXT,
3736
- language: sapLangToIso(DD40V?.DDLANGUAGE),
3737
- masterLanguage: sapLangToIso(DD40V?.DDLANGUAGE),
3738
- abapLanguageVersion: abapLangVerToAdt(DD40V?.ABAP_LANGUAGE_VERSION),
3739
- rowType: {
3740
- typeKind: rowKind,
3741
- typeName: isPredefined ? "" : DD40V?.ROWTYPE || "",
3742
- builtInType: {
3743
- dataType: ddicCode ? adtDataType : "",
3744
- length: DD40V?.LENG ? Number(DD40V.LENG) : 0,
3745
- decimals: DD40V?.DECIMALS ? Number(DD40V.DECIMALS) : 0
3746
- },
3747
- rangeType: ""
5884
+ name: (T100A?.ARBGB ?? "").toUpperCase(),
5885
+ type: "MSAG/MS",
5886
+ description: T100A?.STEXT,
5887
+ language: sapLangToIso(T100A?.MASTERLANG),
5888
+ masterLanguage: sapLangToIso(T100A?.MASTERLANG),
5889
+ messages: items.map((m) => ({
5890
+ number: m.MSGNR ?? "",
5891
+ text: m.TEXT ?? "",
5892
+ language: sapLangToIso(m.SPRSL)
5893
+ }))
5894
+ };
5895
+ }
5896
+ });
5897
+
5898
+ //#endregion
5899
+ //#region src/lib/handlers/objects/view.ts
5900
+ /**
5901
+ * VIEW (Database View) object handler for abapGit format
5902
+ *
5903
+ * Database views are XML-only (no source code). The abapGit format stores
5904
+ * the view header in DD25V, table assignments in DD26V_TABLE, view fields
5905
+ * in DD27P_TABLE and selection conditions in DD28V_TABLE.
5906
+ */
5907
+ const viewHandler = createHandler("VIEW", {
5908
+ schema: view,
5909
+ version: "v1.0.0",
5910
+ serializer: "LCL_OBJECT_VIEW",
5911
+ serializer_version: "v1.0.0",
5912
+ toAbapGit: (obj) => {
5913
+ const tables = obj.tables ?? [];
5914
+ const fields = obj.fields ?? [];
5915
+ const conditions = obj.selectionConditions ?? [];
5916
+ return {
5917
+ DD25V: {
5918
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
5919
+ DDLANGUAGE: isoToSapLang(obj.language),
5920
+ DDTEXT: obj.description ?? "",
5921
+ VIEWCLASS: obj.viewClass ?? void 0,
5922
+ ROOTTAB: obj.baseTable ?? void 0,
5923
+ ROOTFIELD: obj.baseTableField ?? void 0
3748
5924
  },
3749
- accessType,
3750
- primaryKey: {
3751
- definition: keyDef,
3752
- kind: keyKind,
3753
- components: { component: [] },
3754
- alias: ""
5925
+ DD26V_TABLE: tables.length > 0 ? { item: tables.map((t) => ({
5926
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
5927
+ TABNAME: t.tableName ?? void 0,
5928
+ TABPOS: t.position ?? void 0,
5929
+ FVIEWNAME: t.foreignView ?? void 0,
5930
+ FFIELDNAME: t.foreignField ?? void 0,
5931
+ READONLY: t.readOnly ? "X" : void 0
5932
+ })) } : void 0,
5933
+ DD27P_TABLE: fields.length > 0 ? { item: fields.map((f) => ({
5934
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
5935
+ VIEWFIELD: f.viewField ?? void 0,
5936
+ TABNAME: f.tableName ?? void 0,
5937
+ FIELDNAME: f.fieldName ?? void 0,
5938
+ KEYFLAG: f.keyFlag ? "X" : void 0,
5939
+ READONLY: f.readOnly ? "X" : void 0,
5940
+ ROLLNAME: f.rollName ?? void 0,
5941
+ CHECKTABLE: f.checkTable ?? void 0,
5942
+ DDTEXT: f.description ?? void 0
5943
+ })) } : void 0,
5944
+ DD28V_TABLE: conditions.length > 0 ? { item: conditions.map((c) => ({
5945
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
5946
+ ANDOR: c.andOr ?? void 0,
5947
+ LEFTBRACKET: c.leftBracket ?? void 0,
5948
+ FIELDNAME: c.fieldName ?? void 0,
5949
+ OPERATOR: c.operator ?? void 0,
5950
+ CONST_VALUE: c.constantValue ?? void 0,
5951
+ RIGHTBRACKET: c.rightBracket ?? void 0
5952
+ })) } : void 0
5953
+ };
5954
+ },
5955
+ fromAbapGit: parseViewFromAbapGit
5956
+ });
5957
+ function normalizeItems$3(raw) {
5958
+ if (!raw) return [];
5959
+ return Array.isArray(raw) ? raw : [raw];
5960
+ }
5961
+ function parseViewTable(t) {
5962
+ return {
5963
+ tableName: t.TABNAME,
5964
+ position: t.TABPOS,
5965
+ foreignView: t.FVIEWNAME,
5966
+ foreignField: t.FFIELDNAME,
5967
+ readOnly: t.READONLY === "X"
5968
+ };
5969
+ }
5970
+ function parseViewField(f) {
5971
+ return {
5972
+ viewField: f.VIEWFIELD,
5973
+ tableName: f.TABNAME,
5974
+ fieldName: f.FIELDNAME,
5975
+ keyFlag: f.KEYFLAG === "X",
5976
+ readOnly: f.READONLY === "X",
5977
+ rollName: f.ROLLNAME,
5978
+ checkTable: f.CHECKTABLE,
5979
+ description: f.DDTEXT
5980
+ };
5981
+ }
5982
+ function parseViewCondition(c) {
5983
+ return {
5984
+ andOr: c.ANDOR,
5985
+ leftBracket: c.LEFTBRACKET,
5986
+ fieldName: c.FIELDNAME,
5987
+ operator: c.OPERATOR,
5988
+ constantValue: c.CONST_VALUE,
5989
+ rightBracket: c.RIGHTBRACKET
5990
+ };
5991
+ }
5992
+ function parseViewFromAbapGit({ DD25V, DD26V_TABLE, DD27P_TABLE, DD28V_TABLE }) {
5993
+ const tableItems = normalizeItems$3(DD26V_TABLE?.item);
5994
+ const fieldItems = normalizeItems$3(DD27P_TABLE?.item);
5995
+ const condItems = normalizeItems$3(DD28V_TABLE?.item);
5996
+ return {
5997
+ name: (DD25V?.VIEWNAME ?? "").toUpperCase(),
5998
+ type: "VIEW/VD",
5999
+ description: DD25V?.DDTEXT,
6000
+ language: sapLangToIso(DD25V?.DDLANGUAGE),
6001
+ masterLanguage: sapLangToIso(DD25V?.DDLANGUAGE),
6002
+ viewClass: DD25V?.VIEWCLASS,
6003
+ baseTable: DD25V?.ROOTTAB,
6004
+ baseTableField: DD25V?.ROOTFIELD,
6005
+ tables: tableItems.map(parseViewTable),
6006
+ fields: fieldItems.map(parseViewField),
6007
+ selectionConditions: condItems.map(parseViewCondition)
6008
+ };
6009
+ }
6010
+
6011
+ //#endregion
6012
+ //#region src/lib/handlers/objects/enqu.ts
6013
+ /**
6014
+ * ENQU (Lock Object) object handler for abapGit format
6015
+ *
6016
+ * Lock objects are XML-only (no source code). The abapGit format stores
6017
+ * the lock object header in DD25V, table assignments in DD26V_TABLE and
6018
+ * lock parameters in DD27P_TABLE.
6019
+ */
6020
+ const lockObjectHandler = createHandler("ENQU", {
6021
+ schema: enqu,
6022
+ version: "v1.0.0",
6023
+ serializer: "LCL_OBJECT_ENQU",
6024
+ serializer_version: "v1.0.0",
6025
+ toAbapGit: (obj) => {
6026
+ const tables = obj.tables ?? [];
6027
+ const parameters = obj.parameters ?? [];
6028
+ return {
6029
+ DD25V: {
6030
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
6031
+ DDLANGUAGE: isoToSapLang(obj.masterLanguage || obj.language),
6032
+ DDTEXT: obj.description ?? "",
6033
+ ROOTTAB: obj.baseTable ?? void 0,
6034
+ ROOTFIELD: obj.baseTableField ?? void 0
3755
6035
  },
3756
- secondaryKeys: { allowed: "notSpecified" }
6036
+ DD26V_TABLE: tables.length > 0 ? { item: tables.map((t) => ({
6037
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
6038
+ TABNAME: t.tableName ?? void 0,
6039
+ TABPOS: t.position ?? void 0,
6040
+ FVIEWNAME: t.foreignView ?? void 0,
6041
+ FFIELDNAME: t.foreignField ?? void 0,
6042
+ READONLY: t.readOnly ? "X" : void 0
6043
+ })) } : void 0,
6044
+ DD27P_TABLE: parameters.length > 0 ? { item: parameters.map((p) => ({
6045
+ VIEWNAME: String(obj.name ?? "").toUpperCase(),
6046
+ VIEWFIELD: p.viewField ?? void 0,
6047
+ TABNAME: p.tableName ?? void 0,
6048
+ FIELDNAME: p.fieldName ?? void 0,
6049
+ KEYFLAG: p.keyFlag ? "X" : void 0,
6050
+ ROLLNAME: p.rollName ?? void 0,
6051
+ CHECKTABLE: p.checkTable ?? void 0,
6052
+ DDTEXT: p.description ?? void 0
6053
+ })) } : void 0
3757
6054
  };
3758
- }
6055
+ },
6056
+ fromAbapGit: parseLockObjectFromAbapGit
3759
6057
  });
6058
+ function normalizeItems$2(raw) {
6059
+ if (!raw) return [];
6060
+ return Array.isArray(raw) ? raw : [raw];
6061
+ }
6062
+ function parseEnquTable(t) {
6063
+ return {
6064
+ tableName: t.TABNAME,
6065
+ position: t.TABPOS,
6066
+ foreignView: t.FVIEWNAME,
6067
+ foreignField: t.FFIELDNAME,
6068
+ readOnly: t.READONLY === "X"
6069
+ };
6070
+ }
6071
+ function parseEnquParam(p) {
6072
+ return {
6073
+ viewField: p.VIEWFIELD,
6074
+ tableName: p.TABNAME,
6075
+ fieldName: p.FIELDNAME,
6076
+ keyFlag: p.KEYFLAG === "X",
6077
+ rollName: p.ROLLNAME,
6078
+ checkTable: p.CHECKTABLE,
6079
+ description: p.DDTEXT
6080
+ };
6081
+ }
6082
+ function parseLockObjectFromAbapGit({ DD25V, DD26V_TABLE, DD27P_TABLE }) {
6083
+ const tableItems = normalizeItems$2(DD26V_TABLE?.item);
6084
+ const paramItems = normalizeItems$2(DD27P_TABLE?.item);
6085
+ return {
6086
+ name: (DD25V?.VIEWNAME ?? "").toUpperCase(),
6087
+ type: "ENQU/LO",
6088
+ description: DD25V?.DDTEXT,
6089
+ language: sapLangToIso(DD25V?.DDLANGUAGE),
6090
+ masterLanguage: sapLangToIso(DD25V?.DDLANGUAGE),
6091
+ baseTable: DD25V?.ROOTTAB,
6092
+ baseTableField: DD25V?.ROOTFIELD,
6093
+ tables: tableItems.map(parseEnquTable),
6094
+ parameters: paramItems.map(parseEnquParam)
6095
+ };
6096
+ }
3760
6097
 
3761
6098
  //#endregion
3762
- //#region src/lib/handlers/objects/bdef.ts
6099
+ //#region src/lib/handlers/objects/shlp.ts
3763
6100
  /**
3764
- * BDEF (RAP Behavior Definition) object handler for abapGit format
6101
+ * SHLP (Search Help) object handler for abapGit format
3765
6102
  *
3766
- * BDEF is source-driven: the semantic content lives in a `.abdl` file
3767
- * (the RAP behavior definition language) and abapGit stores it alongside
3768
- * a minimal SKEY metadata block.
6103
+ * Search helps are XML-only (no source code). The abapGit format stores
6104
+ * the search help header in DD30V, the text table info in DD31V,
6105
+ * parameters in DD32P_TABLE and field assignments in DD33V_TABLE.
6106
+ */
6107
+ const searchHelpHandler = createHandler("SHLP", {
6108
+ schema: shlp,
6109
+ version: "v1.0.0",
6110
+ serializer: "LCL_OBJECT_SHLP",
6111
+ serializer_version: "v1.0.0",
6112
+ toAbapGit: (obj) => {
6113
+ const parameters = obj.parameters ?? [];
6114
+ const assignments = obj.fieldAssignments ?? [];
6115
+ return {
6116
+ DD30V: {
6117
+ SHLPNAME: String(obj.name ?? "").toUpperCase(),
6118
+ DDLANGUAGE: isoToSapLang(obj.language),
6119
+ DDTEXT: obj.description ?? "",
6120
+ SHLPTYPE: obj.helpType ?? void 0,
6121
+ DIALOGTYPE: obj.dialogType ?? void 0
6122
+ },
6123
+ DD31V: obj.textTable ? {
6124
+ SHLPNAME: String(obj.name ?? "").toUpperCase(),
6125
+ TABNAME: obj.textTable.tableName ?? void 0,
6126
+ FIELDNAME: obj.textTable.fieldName ?? void 0,
6127
+ FLDLOCATION: obj.textTable.fieldLocation ?? void 0,
6128
+ ROLLNAME: obj.textTable.rollName ?? void 0
6129
+ } : void 0,
6130
+ DD32P_TABLE: parameters.length > 0 ? { item: parameters.map((p) => ({
6131
+ SHLPNAME: String(obj.name ?? "").toUpperCase(),
6132
+ FIELDNAME: p.fieldName ?? void 0,
6133
+ SHLPINPUT: p.input ? "X" : void 0,
6134
+ SHLPOUTPUT: p.output ? "X" : void 0,
6135
+ SHLPSELPOS: p.selectionPosition ?? void 0,
6136
+ SHLPLISPOS: p.listPosition ?? void 0,
6137
+ DEFAULTVAL: p.defaultValue ?? void 0,
6138
+ ROLLNAME: p.rollName ?? void 0,
6139
+ DDTEXT: p.description ?? void 0
6140
+ })) } : void 0,
6141
+ DD33V_TABLE: assignments.length > 0 ? { item: assignments.map((a) => ({
6142
+ SHLPNAME: String(obj.name ?? "").toUpperCase(),
6143
+ SHLPFIELD: a.helpField ?? void 0,
6144
+ TABNAME: a.tableName ?? void 0,
6145
+ FIELDNAME: a.fieldName ?? void 0,
6146
+ SHLPINPUT: a.input ? "X" : void 0,
6147
+ SHLPOUTPUT: a.output ? "X" : void 0
6148
+ })) } : void 0
6149
+ };
6150
+ },
6151
+ fromAbapGit: parseSearchHelpFromAbapGit
6152
+ });
6153
+ function normalizeItems$1(raw) {
6154
+ if (!raw) return [];
6155
+ return Array.isArray(raw) ? raw : [raw];
6156
+ }
6157
+ function parseShlpParam(p) {
6158
+ return {
6159
+ fieldName: p.FIELDNAME,
6160
+ input: p.SHLPINPUT === "X",
6161
+ output: p.SHLPOUTPUT === "X",
6162
+ selectionPosition: p.SHLPSELPOS,
6163
+ listPosition: p.SHLPLISPOS,
6164
+ defaultValue: p.DEFAULTVAL,
6165
+ rollName: p.ROLLNAME,
6166
+ description: p.DDTEXT
6167
+ };
6168
+ }
6169
+ function parseShlpAssign(a) {
6170
+ return {
6171
+ helpField: a.SHLPFIELD,
6172
+ tableName: a.TABNAME,
6173
+ fieldName: a.FIELDNAME,
6174
+ input: a.SHLPINPUT === "X",
6175
+ output: a.SHLPOUTPUT === "X"
6176
+ };
6177
+ }
6178
+ function parseSearchHelpFromAbapGit({ DD30V, DD31V, DD32P_TABLE, DD33V_TABLE }) {
6179
+ const paramItems = normalizeItems$1(DD32P_TABLE?.item);
6180
+ const assignItems = normalizeItems$1(DD33V_TABLE?.item);
6181
+ return {
6182
+ name: (DD30V?.SHLPNAME ?? "").toUpperCase(),
6183
+ type: "SHLP/SH",
6184
+ description: DD30V?.DDTEXT,
6185
+ language: sapLangToIso(DD30V?.DDLANGUAGE),
6186
+ masterLanguage: sapLangToIso(DD30V?.DDLANGUAGE),
6187
+ helpType: DD30V?.SHLPTYPE,
6188
+ dialogType: DD30V?.DIALOGTYPE,
6189
+ textTable: DD31V ? {
6190
+ tableName: DD31V.TABNAME,
6191
+ fieldName: DD31V.FIELDNAME,
6192
+ fieldLocation: DD31V.FLDLOCATION,
6193
+ rollName: DD31V.ROLLNAME
6194
+ } : void 0,
6195
+ parameters: paramItems.map(parseShlpParam),
6196
+ fieldAssignments: assignItems.map(parseShlpAssign)
6197
+ };
6198
+ }
6199
+
6200
+ //#endregion
6201
+ //#region src/lib/handlers/objects/tran.ts
6202
+ /**
6203
+ * TRAN (Transaction) object handler for abapGit format
3769
6204
  *
3770
- * File layout:
3771
- * src/zbp_foo.bdef.abdl — behavior source (.abdl text)
3772
- * src/zbp_foo.bdef.xml — minimal metadata wrapper
6205
+ * Transactions are XML-only (no source code). The abapGit format stores
6206
+ * the transaction code in TSTC, GUI attributes in TSTCC and texts in
6207
+ * the TSTCT table (wrapped in <item> elements).
6208
+ */
6209
+ const transactionHandler = createHandler("TRAN", {
6210
+ schema: tran,
6211
+ version: "v1.0.0",
6212
+ serializer: "LCL_OBJECT_TRAN",
6213
+ serializer_version: "v1.0.0",
6214
+ toAbapGit: buildTransactionToAbapGit,
6215
+ fromAbapGit: parseTransactionFromAbapGit
6216
+ });
6217
+ function normalizeItems(raw) {
6218
+ if (!raw) return [];
6219
+ return Array.isArray(raw) ? raw : [raw];
6220
+ }
6221
+ function parseGuiAttributes(TSTCC) {
6222
+ if (!TSTCC) return void 0;
6223
+ return {
6224
+ webGui: TSTCC.S_WEBGUI === "X",
6225
+ platinumGui: TSTCC.S_PLATIN === "X",
6226
+ win32Gui: TSTCC.S_WIN32 === "X",
6227
+ macGui: TSTCC.S_MAC === "X"
6228
+ };
6229
+ }
6230
+ function parseTransactionFromAbapGit({ TSTC, TSTCC, TSTCT }) {
6231
+ const textItems = normalizeItems(TSTCT?.item);
6232
+ const firstText = textItems[0];
6233
+ return {
6234
+ name: (TSTC?.TCODE ?? "").toUpperCase(),
6235
+ type: "TRAN/ST",
6236
+ description: firstText?.TTEXT,
6237
+ language: sapLangToIso(firstText?.SPRSL),
6238
+ masterLanguage: sapLangToIso(firstText?.SPRSL),
6239
+ programName: TSTC?.PGMNA,
6240
+ dynproNumber: TSTC?.DYPNO,
6241
+ transactionType: TSTC?.TYPE,
6242
+ guiAttributes: parseGuiAttributes(TSTCC),
6243
+ texts: textItems.map((t) => ({
6244
+ language: sapLangToIso(t.SPRSL),
6245
+ text: t.TTEXT
6246
+ }))
6247
+ };
6248
+ }
6249
+ function buildTstc(obj) {
6250
+ return {
6251
+ TCODE: String(obj.name ?? "").toUpperCase(),
6252
+ PGMNA: obj.programName ?? void 0,
6253
+ DYPNO: obj.dynproNumber ?? void 0,
6254
+ TYPE: obj.transactionType ?? void 0
6255
+ };
6256
+ }
6257
+ function buildTstcc(gui, tcode) {
6258
+ return {
6259
+ TCODE: tcode,
6260
+ S_WEBGUI: gui.webGui ? "X" : void 0,
6261
+ S_PLATIN: gui.platinumGui ? "X" : void 0,
6262
+ S_WIN32: gui.win32Gui ? "X" : void 0,
6263
+ S_MAC: gui.macGui ? "X" : void 0
6264
+ };
6265
+ }
6266
+ function buildTstct(texts, obj) {
6267
+ if (texts.length === 0) return void 0;
6268
+ return { item: texts.map((t) => ({
6269
+ SPRSL: isoToSapLang(t.language || obj.masterLanguage || obj.language),
6270
+ TCODE: String(obj.name ?? "").toUpperCase(),
6271
+ TTEXT: t.text ?? ""
6272
+ })) };
6273
+ }
6274
+ function buildTransactionToAbapGit(obj) {
6275
+ const texts = obj.texts ?? [];
6276
+ const tcode = String(obj.name ?? "").toUpperCase();
6277
+ return {
6278
+ TSTC: buildTstc(obj),
6279
+ TSTCC: obj.guiAttributes ? buildTstcc(obj.guiAttributes, tcode) : void 0,
6280
+ TSTCT: buildTstct(texts, obj)
6281
+ };
6282
+ }
6283
+
6284
+ //#endregion
6285
+ //#region src/lib/handlers/objects/type.ts
6286
+ /**
6287
+ * TYPE (Type Group / Type Pool) object handler for abapGit format
3773
6288
  *
3774
- * The handler uses the string form of `createHandler` ('BDEF') because
3775
- * the ADK object (`AdkBehaviorDefinition`) is a lightweight class without
3776
- * the AdkObject save/lock machinery — all lifecycle is source-based.
6289
+ * Type groups have both XML metadata (TYPE structure) and ABAP source
6290
+ * (<name>.type.abap). The abapGit format stores the header in TYPE.
6291
+ */
6292
+ const typeGroupHandler = createHandler("TYPE", {
6293
+ schema: type,
6294
+ version: "v1.0.0",
6295
+ serializer: "LCL_OBJECT_TYPE",
6296
+ serializer_version: "v1.0.0",
6297
+ toAbapGit: (obj) => ({ TYPE: {
6298
+ TYPEGROUP: String(obj.name ?? "").toUpperCase(),
6299
+ DESCRIPT: obj.description ?? ""
6300
+ } }),
6301
+ getSource: (obj) => typeof obj?.getSource === "function" ? Promise.resolve(obj.getSource()) : Promise.resolve(""),
6302
+ fromAbapGit: ({ TYPE }) => ({
6303
+ name: (TYPE?.TYPEGROUP ?? "").toUpperCase(),
6304
+ type: "TYPE/TY",
6305
+ description: TYPE?.DESCRIPT
6306
+ }),
6307
+ setSources: (obj, sources) => {
6308
+ if (sources.main) obj._pendingSource = sources.main;
6309
+ }
6310
+ });
6311
+
6312
+ //#endregion
6313
+ //#region src/lib/handlers/objects/xslt.ts
6314
+ /**
6315
+ * XSLT (Transformation) object handler for abapGit format
3777
6316
  *
3778
- * We override `serialize` so the source file gets the `.abdl` extension
3779
- * instead of the default `.abap`.
6317
+ * Transformations have both XML metadata (XSLT structure) and ABAP source
6318
+ * (<name>.xslt.abap). The abapGit format stores the header in XSLT.
3780
6319
  */
3781
- const behaviorDefinitionHandler = createHandler("BDEF", {
6320
+ const transformationHandler = createHandler("XSLT", {
6321
+ schema: xslt,
6322
+ version: "v1.0.0",
6323
+ serializer: "LCL_OBJECT_XSLT",
6324
+ serializer_version: "v1.0.0",
6325
+ toAbapGit: (obj) => ({ XSLT: {
6326
+ PROGNAME: String(obj.name ?? "").toUpperCase(),
6327
+ DESCRIPT: obj.description ?? ""
6328
+ } }),
6329
+ getSource: (obj) => typeof obj?.getSource === "function" ? Promise.resolve(obj.getSource()) : Promise.resolve(""),
6330
+ fromAbapGit: ({ XSLT }) => ({
6331
+ name: (XSLT?.PROGNAME ?? "").toUpperCase(),
6332
+ type: "XSLT/TX",
6333
+ description: XSLT?.DESCRIPT
6334
+ }),
6335
+ setSources: (obj, sources) => {
6336
+ if (sources.main) obj._pendingSource = sources.main;
6337
+ }
6338
+ });
6339
+
6340
+ //#endregion
6341
+ //#region src/lib/handlers/objects/dras.ts
6342
+ const cdsAspectHandler = createCdsAffSourceHandler("DRAS");
6343
+
6344
+ //#endregion
6345
+ //#region src/lib/handlers/objects/drty.ts
6346
+ const cdsTypeHandler = createCdsAffSourceHandler("DRTY");
6347
+
6348
+ //#endregion
6349
+ //#region src/lib/handlers/objects/dsfd.ts
6350
+ const scalarFunctionDefinitionHandler = createCdsAffSourceHandler("DSFD");
6351
+
6352
+ //#endregion
6353
+ //#region src/lib/handlers/objects/dsfi.ts
6354
+ const VALID_ENGINES = /* @__PURE__ */ new Set(["analyticalEngine", "sqlEngine"]);
6355
+ function isDsfiDefinition(value) {
6356
+ const candidate = value;
6357
+ return !!candidate && candidate.formatVersion === "1" && typeof candidate.scalarFunctionName === "string" && VALID_ENGINES.has(candidate.engine ?? "") && typeof candidate.header?.description === "string" && typeof candidate.header?.originalLanguage === "string";
6358
+ }
6359
+ function parseDsfiDefinition(value) {
6360
+ let parsed;
6361
+ try {
6362
+ parsed = typeof value === "string" ? JSON.parse(value) : value;
6363
+ } catch {
6364
+ throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", "DSFI source/main did not return valid JSON.");
6365
+ }
6366
+ if (!isDsfiDefinition(parsed)) throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", "DSFI source/main did not return the required ABAP File Format JSON document.");
6367
+ return parsed;
6368
+ }
6369
+ const scalarFunctionImplementationHandler = createHandler("DSFI", {
3782
6370
  schema: bdef,
3783
6371
  version: "v1.0.0",
3784
- serializer: "LCL_OBJECT_BDEF",
6372
+ serializer: "LCL_OBJECT_DSFI",
3785
6373
  serializer_version: "v1.0.0",
3786
6374
  toAbapGit: (obj) => ({ SKEY: {
3787
- TYPE: "BDEF",
6375
+ TYPE: "DSFI",
3788
6376
  NAME: String(obj?.name ?? "").toUpperCase()
3789
6377
  } }),
3790
- getSource: (obj) => typeof obj?.getSource === "function" ? obj.getSource() : Promise.resolve(""),
3791
- fromAbapGit: ({ SKEY }) => ({ name: String(SKEY?.NAME ?? "").toUpperCase() }),
3792
- async serialize(object, ctx) {
3793
- const files = [];
3794
- const objectName = ctx.getObjectName(object);
3795
- const source = await (typeof object?.getSource === "function" ? object.getSource() : Promise.resolve(""));
3796
- if (source) files.push(ctx.createFile(`${objectName}.${ctx.fileExtension}.abdl`, source));
3797
- const xmlContent = ctx.toAbapGitXml(object);
3798
- files.push(ctx.createFile(`${objectName}.${ctx.fileExtension}.xml`, xmlContent));
3799
- return files;
6378
+ fromAffJson: (json) => {
6379
+ const def = json;
6380
+ return {
6381
+ name: "",
6382
+ description: def?.header?.description,
6383
+ originalLanguage: def?.header?.originalLanguage,
6384
+ abapLanguageVersion: def?.header?.abapLanguageVersion
6385
+ };
3800
6386
  },
3801
- setSources: (obj, sources) => {
3802
- if (sources.main) obj._pendingSource = sources.main;
6387
+ async serialize(object, ctx) {
6388
+ const definition = parseDsfiDefinition(typeof object.getSource === "function" ? await object.getSource() : void 0);
6389
+ return [ctx.createFile(`${ctx.getObjectName(object)}.dsfi.json`, `${JSON.stringify(definition, null, 2)}\n`)];
3803
6390
  }
3804
6391
  });
3805
6392
 
3806
6393
  //#endregion
3807
- //#region src/lib/handlers/objects/srvd.ts
3808
- /**
3809
- * SRVD (RAP Service Definition) object handler for abapGit format
3810
- *
3811
- * SRVD is source-driven: the semantic content lives in a `.asrvd` file
3812
- * (CDS-like service definition) and abapGit stores it alongside
3813
- * a minimal SKEY metadata block.
3814
- *
3815
- * File layout:
3816
- * src/zui_foo.srvd.asrvd — service source (.asrvd text)
3817
- * src/zui_foo.srvd.xml — minimal metadata wrapper
3818
- *
3819
- * The handler uses the string form of `createHandler` ('SRVD') because
3820
- * the ADK object (`AdkServiceDefinition`) is a lightweight class without
3821
- * the AdkObject save/lock machinery — all lifecycle is source-based.
3822
- *
3823
- * We override `serialize` so the source file gets the `.asrvd` extension
3824
- * instead of the default `.abap`.
3825
- */
3826
- const serviceDefinitionHandler = createHandler("SRVD", {
3827
- schema: srvd,
6394
+ //#region src/lib/handlers/objects/dteb.ts
6395
+ const entityBufferHandler = createCdsAffSourceHandler("DTEB");
6396
+
6397
+ //#endregion
6398
+ //#region src/lib/handlers/objects/dtdc.ts
6399
+ const dynamicCacheHandler = createCdsAffSourceHandler("DTDC");
6400
+
6401
+ //#endregion
6402
+ //#region src/lib/handlers/objects/dtix.ts
6403
+ const tuningIndexHandler = createCdsAffSourceHandler("DTIX");
6404
+
6405
+ //#endregion
6406
+ //#region src/lib/handlers/objects/dtsc.ts
6407
+ const staticCacheHandler = createCdsAffSourceHandler("DTSC");
6408
+
6409
+ //#endregion
6410
+ //#region src/lib/handlers/objects/desd.ts
6411
+ const externalSchemaHandler = createHandler("DESD", {
6412
+ schema: bdef,
3828
6413
  version: "v1.0.0",
3829
- serializer: "LCL_OBJECT_SRVD",
6414
+ serializer: "LCL_OBJECT_DESD",
3830
6415
  serializer_version: "v1.0.0",
3831
6416
  toAbapGit: (obj) => ({ SKEY: {
3832
- TYPE: "SRVD",
6417
+ TYPE: "DESD",
3833
6418
  NAME: String(obj?.name ?? "").toUpperCase()
3834
6419
  } }),
3835
- getSource: (obj) => typeof obj?.getSource === "function" ? obj.getSource() : Promise.resolve(""),
3836
- fromAbapGit: ({ SKEY }) => ({ name: String(SKEY?.NAME ?? "").toUpperCase() }),
6420
+ fromAffJson: (json) => affFromAffJson(json, ""),
3837
6421
  async serialize(object, ctx) {
3838
- const files = [];
3839
- const objectName = ctx.getObjectName(object);
3840
- const source = await (typeof object?.getSource === "function" ? object.getSource() : Promise.resolve(""));
3841
- if (source) files.push(ctx.createFile(`${objectName}.${ctx.fileExtension}.asrvd`, source));
3842
- const xmlContent = ctx.toAbapGitXml(object);
3843
- files.push(ctx.createFile(`${objectName}.${ctx.fileExtension}.xml`, xmlContent));
3844
- return files;
3845
- },
3846
- setSources: (obj, sources) => {
3847
- if (sources.main) obj._pendingSource = sources.main;
6422
+ const header = {
6423
+ description: object.description || String(object.name ?? ""),
6424
+ originalLanguage: (object.originalLanguage ?? "en").toLowerCase()
6425
+ };
6426
+ return [ctx.createFile(`${ctx.getObjectName(object)}.desd.json`, `${JSON.stringify({
6427
+ formatVersion: "1",
6428
+ header
6429
+ }, null, 2)}\n`)];
3848
6430
  }
3849
6431
  });
3850
6432
 
3851
6433
  //#endregion
3852
- //#region src/lib/handlers/objects/srvb.ts
6434
+ //#region src/lib/handlers/aff-handler.ts
3853
6435
  /**
3854
- * SRVB (RAP Service Binding) object handler for abapGit format
3855
- *
3856
- * Unlike BDEF/SRVD, SRVB is **metadata-only** — there is no `.abdl`/
3857
- * `.asrvd`-style source text. The handler writes a single
3858
- * `<name>.srvb.xml` file carrying a minimal SKEY + BINDING block.
6436
+ * Shared factory for AFF-first object handlers.
3859
6437
  *
3860
- * File layout:
3861
- * src/zui_foo.srvb.xml — metadata only
6438
+ * The 17 AFF-first types (APLO, BGQC, CDBO, CHKC, CHKO, CHKV, COTA, EVTB,
6439
+ * GSMP, NONT, RONT, SAJC, SAJT, SMBC, UIAD, UIPG, UIST) all share the same
6440
+ * serialization logic — only the type code and file extension differ.
6441
+ * This factory eliminates the per-handler duplication.
6442
+ */
6443
+ /**
6444
+ * Create an AFF-first handler that serializes to JSON metadata only.
3862
6445
  *
3863
- * Mirrors the minimal-block approach used by
3864
- * zcl_abapgit_object_srvb (abapGit upstream serialiser).
6446
+ * @param type ABAP object type code (e.g. 'APLO')
6447
+ * @param ext Lowercase file extension for the JSON sidecar (e.g. 'aplo')
3865
6448
  */
3866
- const serviceBindingHandler = createHandler("SRVB", {
3867
- schema: srvb,
3868
- version: "v1.0.0",
3869
- serializer: "LCL_OBJECT_SRVB",
3870
- serializer_version: "v1.0.0",
3871
- toAbapGit: (obj) => ({
3872
- SKEY: {
3873
- TYPE: "SRVB",
6449
+ function createAffFirstHandler(type, ext) {
6450
+ return createHandler(type, {
6451
+ schema: bdef,
6452
+ version: "v1.0.0",
6453
+ serializer: `LCL_OBJECT_${type}`,
6454
+ serializer_version: "v1.0.0",
6455
+ toAbapGit: (obj) => ({ SKEY: {
6456
+ TYPE: type,
3874
6457
  NAME: String(obj?.name ?? "").toUpperCase()
3875
- },
3876
- BINDING: {
3877
- TYPE: "odataV4",
3878
- VERSION: "4",
3879
- CATEGORY: "odata_v4_ui"
6458
+ } }),
6459
+ fromAffJson: (json) => affFromAffJson(json, ""),
6460
+ async serialize(object, ctx) {
6461
+ const header = {
6462
+ description: object.description || String(object.name ?? ""),
6463
+ originalLanguage: (object.originalLanguage ?? "en").toLowerCase()
6464
+ };
6465
+ return [ctx.createFile(`${ctx.getObjectName(object)}.${ext}.json`, `${JSON.stringify({
6466
+ formatVersion: "1",
6467
+ header
6468
+ }, null, 2)}\n`)];
3880
6469
  }
3881
- }),
3882
- fromAbapGit: ({ SKEY }) => ({ name: String(SKEY?.NAME ?? "").toUpperCase() }),
3883
- async serialize(object, ctx) {
3884
- const objectName = ctx.getObjectName(object);
3885
- const xmlContent = ctx.toAbapGitXml(object);
3886
- return [ctx.createFile(`${objectName}.${ctx.fileExtension}.xml`, xmlContent)];
3887
- }
3888
- });
6470
+ });
6471
+ }
6472
+
6473
+ //#endregion
6474
+ //#region src/lib/handlers/objects/aplo.ts
6475
+ const applicationLogObjectHandler = createAffFirstHandler("APLO", "aplo");
6476
+
6477
+ //#endregion
6478
+ //#region src/lib/handlers/objects/bgqc.ts
6479
+ const bgqcHandler = createAffFirstHandler("BGQC", "bgqc");
6480
+
6481
+ //#endregion
6482
+ //#region src/lib/handlers/objects/cdbo.ts
6483
+ const cdboHandler = createAffFirstHandler("CDBO", "cdbo");
6484
+
6485
+ //#endregion
6486
+ //#region src/lib/handlers/objects/chkc.ts
6487
+ const checkCategoryHandler = createAffFirstHandler("CHKC", "chkc");
6488
+
6489
+ //#endregion
6490
+ //#region src/lib/handlers/objects/chko.ts
6491
+ const checkObjectHandler = createAffFirstHandler("CHKO", "chko");
6492
+
6493
+ //#endregion
6494
+ //#region src/lib/handlers/objects/chkv.ts
6495
+ const checkVariantHandler = createAffFirstHandler("CHKV", "chkv");
6496
+
6497
+ //#endregion
6498
+ //#region src/lib/handlers/objects/cota.ts
6499
+ const communicationTargetHandler = createAffFirstHandler("COTA", "cota");
6500
+
6501
+ //#endregion
6502
+ //#region src/lib/handlers/objects/evtb.ts
6503
+ const eventBindingHandler = createAffFirstHandler("EVTB", "evtb");
6504
+
6505
+ //#endregion
6506
+ //#region src/lib/handlers/objects/gsmp.ts
6507
+ const gsmpHandler = createAffFirstHandler("GSMP", "gsmp");
6508
+
6509
+ //#endregion
6510
+ //#region src/lib/handlers/objects/nont.ts
6511
+ const objectTypeNodeHandler = createAffFirstHandler("NONT", "nont");
6512
+
6513
+ //#endregion
6514
+ //#region src/lib/handlers/objects/ront.ts
6515
+ const objectTypeHandler = createAffFirstHandler("RONT", "ront");
6516
+
6517
+ //#endregion
6518
+ //#region src/lib/handlers/objects/sajc.ts
6519
+ const jobCatalogHandler = createAffFirstHandler("SAJC", "sajc");
6520
+
6521
+ //#endregion
6522
+ //#region src/lib/handlers/objects/sajt.ts
6523
+ const jobTemplateHandler = createAffFirstHandler("SAJT", "sajt");
6524
+
6525
+ //#endregion
6526
+ //#region src/lib/handlers/objects/smbc.ts
6527
+ const bcmHandler = createAffFirstHandler("SMBC", "smbc");
6528
+
6529
+ //#endregion
6530
+ //#region src/lib/handlers/objects/uiad.ts
6531
+ const launchpadAppDescriptorHandler = createAffFirstHandler("UIAD", "uiad");
6532
+
6533
+ //#endregion
6534
+ //#region src/lib/handlers/objects/uipg.ts
6535
+ const launchpadPageTemplateHandler = createAffFirstHandler("UIPG", "uipg");
6536
+
6537
+ //#endregion
6538
+ //#region src/lib/handlers/objects/uist.ts
6539
+ const launchpadSpaceTemplateHandler = createAffFirstHandler("UIST", "uist");
3889
6540
 
3890
6541
  //#endregion
3891
6542
  //#region src/lib/folder-logic.ts
@@ -4034,29 +6685,180 @@ function generateAbapGitXml(folderLogic) {
4034
6685
  }
4035
6686
 
4036
6687
  //#endregion
4037
- //#region src/lib/deserializer.ts
6688
+ //#region src/lib/filename-parser.ts
6689
+ /**
6690
+ * abapGit filename parsing utilities.
6691
+ *
6692
+ * Extracted into a standalone module so it can be unit-tested without
6693
+ * pulling in the full handler/schema dependency chain.
6694
+ */
4038
6695
  /**
4039
6696
  * abapGit file naming convention:
4040
6697
  * - XML metadata: {name}.{type}.xml (e.g., zcl_myclass.clas.xml)
6698
+ * - AFF JSON metadata: {name}.{type}.json (e.g., zfoo.bdef.json)
4041
6699
  * - Source code: {name}.{type}.abap (e.g., zcl_myclass.clas.abap)
4042
6700
  * - Source includes: {name}.{type}.{suffix}.abap (e.g., zcl_myclass.clas.testclasses.abap)
6701
+ * - AFF source files: {name}.{type}.abdl, {name}.{type}.acds, {name}.{type}.asrvd
4043
6702
  */
4044
6703
  /**
4045
6704
  * Parse abapGit filename to extract object info
6705
+ *
6706
+ * Supports both legacy XML metadata (.xml) and AFF JSON metadata (.json),
6707
+ * plus source file extensions (.abap, .abdl, .acds, .asrvd).
4046
6708
  */
4047
6709
  function parseAbapGitFilename(filename) {
4048
- const match = filename.match(/^([^.]+)\.([^.]+)(?:\.([^.]+))?\.(\w+)$/);
6710
+ const match = filename.match(/^([^.]+)\.([^.]+)(?:\.([^.]+))?\.(xml|json|abap|abdl|acds|asrvd)$/);
4049
6711
  if (!match) return null;
4050
6712
  const [, name, type, suffixOrExt, extension] = match;
4051
- if (extension === "xml" || extension === "abap") return {
6713
+ return {
4052
6714
  name: name.toUpperCase(),
4053
6715
  type: type.toUpperCase(),
4054
6716
  suffix: suffixOrExt && suffixOrExt !== extension ? suffixOrExt : void 0,
4055
6717
  extension
4056
6718
  };
6719
+ }
6720
+
6721
+ //#endregion
6722
+ //#region src/lib/deserializer.ts
6723
+ /**
6724
+ * abapGit file naming convention:
6725
+ * - XML metadata: {name}.{type}.xml (e.g., zcl_myclass.clas.xml)
6726
+ * - AFF JSON metadata: {name}.{type}.json (e.g., zfoo.bdef.json)
6727
+ * - Source code: {name}.{type}.abap (e.g., zcl_myclass.clas.abap)
6728
+ * - Source includes: {name}.{type}.{suffix}.abap (e.g., zcl_myclass.clas.testclasses.abap)
6729
+ * - AFF source files: {name}.{type}.abdl, {name}.{type}.acds, {name}.{type}.asrvd
6730
+ */
6731
+ /**
6732
+ * Resolve folder logic and start dir from .abapgit.xml
6733
+ */
6734
+ async function resolveFolderConfig(fileTree) {
6735
+ let folderLogic = "prefix";
6736
+ let startDir = "";
6737
+ const hasAbapGitXml = await fileTree.exists(".abapgit.xml");
6738
+ if (hasAbapGitXml) try {
6739
+ const xml = await fileTree.read(".abapgit.xml");
6740
+ const meta = parseAbapGitMetadata(xml);
6741
+ folderLogic = meta.folderLogic;
6742
+ startDir = stripSlashes(meta.startingFolder);
6743
+ } catch {}
6744
+ return {
6745
+ folderLogic,
6746
+ startDir,
6747
+ hasAbapGitXml
6748
+ };
6749
+ }
6750
+ /**
6751
+ * Parse a metadata file path into an object key and register it in the map.
6752
+ */
6753
+ function registerMetadataFile(path, supportedTypes, objectMap, field) {
6754
+ const filename = path.slice(path.lastIndexOf("/") + 1);
6755
+ const parsed = parseAbapGitFilename(filename);
6756
+ if (!parsed) return;
6757
+ if (!supportedTypes.has(parsed.type.toLowerCase())) return;
6758
+ if (parsed.suffix) return;
6759
+ const key = `${parsed.name}:${parsed.type}`;
6760
+ if (!objectMap.has(key)) objectMap.set(key, {
6761
+ name: parsed.name,
6762
+ type: parsed.type,
6763
+ sourceFiles: []
6764
+ });
6765
+ objectMap.get(key)[field] = path;
6766
+ }
6767
+ /**
6768
+ * Collect all source files and attach them to their objects in the map.
6769
+ */
6770
+ async function collectSourceFiles(fileTree, objectMap) {
6771
+ const [abapFiles, abdlFiles, acdsFiles, asrvdFiles] = await Promise.all([
6772
+ fileTree.glob("**/*.abap"),
6773
+ fileTree.glob("**/*.abdl"),
6774
+ fileTree.glob("**/*.acds"),
6775
+ fileTree.glob("**/*.asrvd")
6776
+ ]);
6777
+ for (const sourcePath of [
6778
+ ...abapFiles,
6779
+ ...abdlFiles,
6780
+ ...acdsFiles,
6781
+ ...asrvdFiles
6782
+ ]) {
6783
+ const filename = sourcePath.slice(sourcePath.lastIndexOf("/") + 1);
6784
+ const parsed = parseAbapGitFilename(filename);
6785
+ if (!parsed) continue;
6786
+ const obj = objectMap.get(`${parsed.name}:${parsed.type}`);
6787
+ if (obj) obj.sourceFiles.push({
6788
+ path: sourcePath,
6789
+ suffix: parsed.suffix
6790
+ });
6791
+ }
6792
+ }
6793
+ /**
6794
+ * Parse metadata (XML or JSON) and return values + isAffJson flag.
6795
+ */
6796
+ async function parseMetadata(fileTree, objFiles, handler) {
6797
+ if (objFiles.jsonFile) {
6798
+ const jsonContent = await fileTree.read(objFiles.jsonFile);
6799
+ let jsonData;
6800
+ try {
6801
+ jsonData = JSON.parse(jsonContent);
6802
+ } catch (parseError) {
6803
+ console.error(`Failed to parse AFF JSON ${objFiles.jsonFile}:`, parseError);
6804
+ return null;
6805
+ }
6806
+ return {
6807
+ values: handler?.fromAffJson ? handler.fromAffJson(jsonData) : jsonData,
6808
+ isAffJson: true
6809
+ };
6810
+ }
6811
+ if (objFiles.xmlFile) {
6812
+ const xmlContent = await fileTree.read(objFiles.xmlFile);
6813
+ return {
6814
+ values: handler.schema.parse(xmlContent)?.abapGit?.abap?.values ?? {},
6815
+ isAffJson: false
6816
+ };
6817
+ }
4057
6818
  return null;
4058
6819
  }
4059
6820
  /**
6821
+ * Build the payload from handler or filename fallback.
6822
+ */
6823
+ function buildPayload(handler, values, isAffJson, objFiles) {
6824
+ if (isAffJson && handler?.fromAffJson) return values;
6825
+ if (isAffJson) return { name: objFiles.name };
6826
+ if (handler?.fromAbapGit) return handler.fromAbapGit(values);
6827
+ return { name: objFiles.name };
6828
+ }
6829
+ /**
6830
+ * Set sources on an ADK object using handler or fallback.
6831
+ */
6832
+ function setObjectSources(handler, adkObject, sources) {
6833
+ if (Object.keys(sources).length === 0) return;
6834
+ if (handler?.setSources) {
6835
+ handler.setSources(adkObject, sources);
6836
+ return;
6837
+ }
6838
+ if (sources["main"]) adkObject._pendingSource = sources["main"];
6839
+ if (Object.keys(sources).length > 1) adkObject._pendingSources = sources;
6840
+ }
6841
+ /**
6842
+ * Resolve packageRef on an ADK object from folder logic or rootPackage.
6843
+ */
6844
+ function resolvePackageRef(adkObject, options, hasAbapGitXml, folderLogic) {
6845
+ if (!options?.rootPackage) return;
6846
+ const data = adkObject._data;
6847
+ if (!data || data.packageRef) return;
6848
+ if (hasAbapGitXml) {
6849
+ const relDir = adkObject._relDir ?? "";
6850
+ data.packageRef = { name: resolvePackageFromDir(relDir, folderLogic, options.rootPackage) };
6851
+ } else data.packageRef = { name: options.rootPackage };
6852
+ }
6853
+ /**
6854
+ * Set abapLanguageVersion on an ADK object if provided in options.
6855
+ */
6856
+ function setAbapLanguageVersion(adkObject, options) {
6857
+ if (!options?.abapLanguageVersion) return;
6858
+ const data = adkObject._data;
6859
+ if (data && !data.abapLanguageVersion) data.abapLanguageVersion = abapLangVerToAdt(options.abapLanguageVersion) ?? options.abapLanguageVersion;
6860
+ }
6861
+ /**
4060
6862
  * Deserialize abapGit files to ADK objects
4061
6863
  *
4062
6864
  * True generator - yields objects one at a time as they're discovered.
@@ -4068,119 +6870,131 @@ function parseAbapGitFilename(filename) {
4068
6870
  */
4069
6871
  async function* deserialize(fileTree, client, options) {
4070
6872
  const adk = createAdk(client);
4071
- let folderLogic = "prefix";
4072
- let startDir = "";
4073
- const hasAbapGitXml = await fileTree.exists(".abapgit.xml");
4074
- if (hasAbapGitXml) try {
4075
- const meta = parseAbapGitMetadata(await fileTree.read(".abapgit.xml"));
4076
- folderLogic = meta.folderLogic;
4077
- startDir = stripSlashes(meta.startingFolder);
4078
- } catch {}
4079
- const xmlFiles = await fileTree.glob("**/*.xml");
6873
+ const { folderLogic, startDir, hasAbapGitXml } = await resolveFolderConfig(fileTree);
6874
+ const [xmlFiles, jsonFiles] = await Promise.all([fileTree.glob("**/*.xml"), fileTree.glob("**/*.json")]);
4080
6875
  const objectMap = /* @__PURE__ */ new Map();
4081
6876
  const supportedTypes = new Set(getSupportedTypes().map((t) => t.toLowerCase()));
4082
6877
  for (const xmlPath of xmlFiles) {
4083
6878
  if (xmlPath.endsWith(".abapgit.xml")) continue;
4084
6879
  if (xmlPath.endsWith("package.devc.xml")) continue;
4085
- const parsed = parseAbapGitFilename(xmlPath.split("/").pop());
4086
- if (!parsed) continue;
4087
- if (!supportedTypes.has(parsed.type.toLowerCase())) continue;
4088
- if (parsed.suffix) continue;
4089
- const key = `${parsed.name}:${parsed.type}`;
4090
- if (!objectMap.has(key)) objectMap.set(key, {
4091
- name: parsed.name,
4092
- type: parsed.type,
4093
- sourceFiles: []
4094
- });
4095
- const obj = objectMap.get(key);
4096
- obj.xmlFile = xmlPath;
4097
- }
4098
- const abapFiles = await fileTree.glob("**/*.abap");
4099
- for (const abapPath of abapFiles) {
4100
- const parsed = parseAbapGitFilename(abapPath.split("/").pop());
4101
- if (!parsed) continue;
4102
- const key = `${parsed.name}:${parsed.type}`;
4103
- const obj = objectMap.get(key);
4104
- if (obj) obj.sourceFiles.push({
4105
- path: abapPath,
4106
- suffix: parsed.suffix
4107
- });
6880
+ registerMetadataFile(xmlPath, supportedTypes, objectMap, "xmlFile");
4108
6881
  }
6882
+ for (const jsonPath of jsonFiles) registerMetadataFile(jsonPath, supportedTypes, objectMap, "jsonFile");
6883
+ await collectSourceFiles(fileTree, objectMap);
4109
6884
  for (const [, objFiles] of objectMap) {
4110
- if (!objFiles.xmlFile) continue;
6885
+ if (!objFiles.xmlFile && !objFiles.jsonFile) continue;
4111
6886
  const handler = getHandler(objFiles.type);
4112
6887
  if (!handler) continue;
4113
6888
  try {
4114
- const xmlContent = await fileTree.read(objFiles.xmlFile);
4115
- const values = handler.schema.parse(xmlContent)?.abapGit?.abap?.values ?? {};
6889
+ const meta = await parseMetadata(fileTree, objFiles, handler);
6890
+ if (!meta) continue;
6891
+ const { values, isAffJson } = meta;
4116
6892
  const sources = {};
4117
6893
  for (const { path, suffix } of objFiles.sourceFiles) {
4118
6894
  const content = await fileTree.read(path);
4119
6895
  const sourceKey = suffix ? handler.suffixToSourceKey?.[suffix] ?? suffix : "main";
4120
6896
  sources[sourceKey] = content;
4121
6897
  }
4122
- const payload = handler.fromAbapGit ? handler.fromAbapGit(values) : { name: objFiles.name };
6898
+ const payload = buildPayload(handler, values, isAffJson, objFiles);
4123
6899
  const objectName = payload.name || objFiles.name;
4124
- const fullData = {
6900
+ const adkType = typeof payload.type === "string" ? payload.type : objFiles.type;
6901
+ const adkObject = adk.getWithData({
4125
6902
  ...payload,
4126
6903
  name: objectName
4127
- };
4128
- const adkType = typeof payload.type === "string" ? payload.type : objFiles.type;
4129
- const adkObject = adk.getWithData(fullData, adkType);
4130
- if (Object.keys(sources).length > 0) if (handler.setSources) handler.setSources(adkObject, sources);
4131
- else {
4132
- if (sources["main"]) adkObject._pendingSource = sources["main"];
4133
- if (Object.keys(sources).length > 1) adkObject._pendingSources = sources;
4134
- }
6904
+ }, adkType);
6905
+ setObjectSources(handler, adkObject, sources);
4135
6906
  if (payload.description) adkObject._pendingDescription = payload.description;
4136
6907
  if (hasAbapGitXml) {
4137
- const sourceDir = objFiles.xmlFile.split("/").slice(0, -1).join("/");
6908
+ const sourceDir = (objFiles.xmlFile ?? objFiles.jsonFile ?? "").split("/").slice(0, -1).join("/");
4138
6909
  adkObject._relDir = sourceDir.startsWith(startDir) ? sourceDir.slice(startDir.length).replace(/^\/+/, "") : sourceDir;
4139
6910
  adkObject._folderLogic = folderLogic;
4140
6911
  }
4141
- if (options?.rootPackage) {
4142
- const data = adkObject._data;
4143
- if (data && !data.packageRef) if (hasAbapGitXml) data.packageRef = { name: resolvePackageFromDir(adkObject._relDir ?? "", folderLogic, options.rootPackage) };
4144
- else data.packageRef = { name: options.rootPackage };
4145
- }
4146
- if (options?.abapLanguageVersion) {
4147
- const data = adkObject._data;
4148
- if (data && !data.abapLanguageVersion) data.abapLanguageVersion = abapLangVerToAdt(options.abapLanguageVersion) ?? options.abapLanguageVersion;
4149
- }
6912
+ resolvePackageRef(adkObject, options, hasAbapGitXml, folderLogic);
6913
+ setAbapLanguageVersion(adkObject, options);
4150
6914
  yield adkObject;
4151
- if (objFiles.type === "FUGR" && payload._functions) {
4152
- const fmDescriptors = extractFunctionDescriptors(payload._functions);
4153
- const fmSources = adkObject._pendingFmSources;
4154
- for (const fm of fmDescriptors) try {
4155
- const fmData = {
4156
- name: fm.funcName,
4157
- type: "FUGR/FF",
4158
- _groupName: objectName,
4159
- description: fm.shortText ?? "",
4160
- processingType: fm.processingType,
4161
- basXMLEnabled: fm.basXMLEnabled
4162
- };
4163
- const fmObject = adk.getWithData(fmData, "FUGR/FF");
4164
- const fmSourceKey = fm.funcName.toLowerCase();
4165
- const fmSource = fmSources?.[fmSourceKey];
4166
- if (fmSource) fmObject._pendingSource = fmSource;
4167
- if (options?.abapLanguageVersion) {
4168
- const fmObjData = fmObject._data;
4169
- if (fmObjData && !fmObjData.abapLanguageVersion) fmObjData.abapLanguageVersion = abapLangVerToAdt(options.abapLanguageVersion) ?? options.abapLanguageVersion;
4170
- }
4171
- yield fmObject;
4172
- } catch (fmError) {
4173
- console.error(`Failed to deserialize FM ${fm.funcName} in FUGR ${objectName}:`, fmError);
4174
- }
4175
- }
6915
+ if (objFiles.type === "FUGR" && payload._functions) yield* yieldFugrChildren(adk, payload, objectName, options, adkObject);
4176
6916
  } catch (error) {
4177
6917
  console.error(`Failed to deserialize ${objFiles.type} ${objFiles.name}:`, error);
4178
6918
  }
4179
6919
  }
4180
6920
  }
6921
+ /**
6922
+ * Yield FUGR child function modules as separate ADK objects.
6923
+ */
6924
+ async function* yieldFugrChildren(adk, payload, parentName, options, parentObject) {
6925
+ const fmDescriptors = extractFunctionDescriptors(payload._functions);
6926
+ const fmSources = parentObject._pendingFmSources;
6927
+ for (const fm of fmDescriptors) try {
6928
+ const fmObject = adk.getWithData({
6929
+ name: fm.funcName,
6930
+ type: "FUGR/FF",
6931
+ _groupName: parentName,
6932
+ description: fm.shortText ?? "",
6933
+ processingType: fm.processingType,
6934
+ basXMLEnabled: fm.basXMLEnabled
6935
+ }, "FUGR/FF");
6936
+ const fmSource = fmSources?.[fm.funcName.toLowerCase()];
6937
+ if (fmSource) fmObject._pendingSource = fmSource;
6938
+ setAbapLanguageVersion(fmObject, options);
6939
+ yield fmObject;
6940
+ } catch (fmError) {
6941
+ console.error(`Failed to deserialize FM ${fm.funcName} in FUGR ${parentName}:`, fmError);
6942
+ }
6943
+ }
4181
6944
 
4182
6945
  //#endregion
4183
6946
  //#region src/lib/format-plugin.ts
6947
+ /**
6948
+ * abapGit as a FormatPlugin.
6949
+ *
6950
+ * This is the thin adapter that plugs the abapGit handler registry into the
6951
+ * generic `FormatPlugin` contract defined by `@abapify/adt-plugin`. External
6952
+ * consumers (adt-export, adt-diff, adt-cli) interact with this plugin
6953
+ * **through the registry only** — they look it up with
6954
+ * `getFormatPlugin('abapgit')` and never import this package directly.
6955
+ *
6956
+ * Registration happens as a side-effect of importing `@abapify/adt-plugin-abapgit`;
6957
+ * the CLI bootstraps the registration once in `adt-cli/src/lib/cli.ts`.
6958
+ */
6959
+ function classifyFile(parsed, handler) {
6960
+ if (parsed.extension === "xml" || parsed.extension === "json") return { role: "metadata" };
6961
+ if (!parsed.suffix) return {
6962
+ role: "source",
6963
+ sourceComponent: "main"
6964
+ };
6965
+ if (parsed.type.toLowerCase() === "fugr" && parsed.suffix.toLowerCase() === `l${parsed.name.toLowerCase()}top`) return {
6966
+ role: "source",
6967
+ sourceComponent: "main"
6968
+ };
6969
+ return {
6970
+ role: "source",
6971
+ sourceComponent: handler.suffixToSourceKey?.[parsed.suffix] ?? parsed.suffix
6972
+ };
6973
+ }
6974
+ function assertSupportsExplicitSources(handler, objectType, sources) {
6975
+ if (!sources) return;
6976
+ if (Object.keys(sources).length === 0) return;
6977
+ if (handler.supportsExplicitSources) return;
6978
+ throw new FormatMaterializationError("FORMAT_SOURCE_COMPONENT_UNSUPPORTED", `abapGit handler for "${objectType}" does not support explicit source selection.`);
6979
+ }
6980
+ async function buildMaterializedFiles(handler, object, packageDir, sources, format) {
6981
+ return (await handler.serialize(object, {
6982
+ ...sources !== void 0 ? { sources } : {},
6983
+ ...format !== void 0 ? { format } : {}
6984
+ })).map((file) => {
6985
+ const parsed = parseAbapGitFilename(file.path);
6986
+ const classification = parsed ? classifyFile(parsed, handler) : {
6987
+ role: "source",
6988
+ sourceComponent: "main"
6989
+ };
6990
+ const path = posix.join("src", packageDir, file.path);
6991
+ return {
6992
+ ...file,
6993
+ path,
6994
+ ...classification
6995
+ };
6996
+ }).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
6997
+ }
4184
6998
  const abapgitFormatPlugin = {
4185
6999
  id: "abapgit",
4186
7000
  description: "abapGit serialization format",
@@ -4188,7 +7002,19 @@ const abapgitFormatPlugin = {
4188
7002
  return getSupportedTypes();
4189
7003
  },
4190
7004
  getHandler(type) {
4191
- return getHandler(type);
7005
+ const handler = getHandler(type);
7006
+ return handler ? {
7007
+ ...handler,
7008
+ supportsExplicitSources: handler.supportsExplicitSources ?? false
7009
+ } : void 0;
7010
+ },
7011
+ async materialize(input) {
7012
+ const handler = this.getHandler(input.objectType);
7013
+ if (!handler) throw new FormatMaterializationError("FORMAT_OBJECT_TYPE_UNSUPPORTED", `abapGit does not support object type "${input.objectType}".`);
7014
+ assertSupportsExplicitSources(handler, input.objectType, input.sources);
7015
+ const folderLogic = parseFolderLogic(input.formatOptions?.folderLogic) ?? "prefix";
7016
+ const packageDir = calculatePackageDir([...input.packagePath], folderLogic);
7017
+ return { files: await buildMaterializedFiles(handler, input.object, packageDir, input.sources, input.formatOptions?.format) };
4192
7018
  },
4193
7019
  parseFilename(filename) {
4194
7020
  return parseAbapGitFilename(filename) ?? void 0;
@@ -4217,6 +7043,9 @@ var AbapGitSerializer = class {
4217
7043
  const writtenFiles = [];
4218
7044
  for (const file of serializedFiles) {
4219
7045
  const filePath = join(fullPackageDir, file.path);
7046
+ const relativePath = relative(fullPackageDir, filePath);
7047
+ if (!relativePath || isAbsolute(relativePath) || relativePath === ".." || relativePath.startsWith(`..${sep}`)) throw new Error(`Invalid serialized file path: ${file.path}`);
7048
+ mkdirSync(dirname(filePath), { recursive: true });
4220
7049
  writeFileSync(filePath, file.content, file.encoding ?? "utf8");
4221
7050
  writtenFiles.push(filePath);
4222
7051
  }
@@ -4291,7 +7120,8 @@ function readFolderLogicFromExistingRepo(targetPath) {
4291
7120
  const abapgitXmlPath = join(targetPath, ".abapgit.xml");
4292
7121
  if (!existsSync(abapgitXmlPath)) return;
4293
7122
  try {
4294
- return parseFolderLogicFromAbapGitXml(readFileSync(abapgitXmlPath, "utf-8"));
7123
+ const xmlContent = readFileSync(abapgitXmlPath, "utf-8");
7124
+ return parseFolderLogicFromAbapGitXml(xmlContent);
4295
7125
  } catch {
4296
7126
  return;
4297
7127
  }
@@ -4338,7 +7168,27 @@ const abapGitPlugin = createPlugin({
4338
7168
  };
4339
7169
  }
4340
7170
  },
7171
+ /**
7172
+ * Export abapGit files to SAP
7173
+ *
7174
+ * Reads abapGit format files from FileTree and yields AdkObject instances.
7175
+ * True generator - streams objects one at a time for memory efficiency.
7176
+ *
7177
+ * @param fileTree - Virtual file system abstraction
7178
+ * @param client - ADT client for creating ADK objects
7179
+ */
4341
7180
  export: (fileTree, client, options) => deserialize(fileTree, client, options),
7181
+ /**
7182
+ * Delete local abapGit files for an object marked for deletion in SAP.
7183
+ *
7184
+ * Searches the `<targetPath>/src/` directory tree for files matching
7185
+ * the abapGit naming pattern `{name}.{type}[.suffix].{ext}` and removes
7186
+ * each one.
7187
+ *
7188
+ * Known limitation: if the object never made it into the repository (e.g.
7189
+ * PROG deletions without obj_func=D), no files are present and nothing
7190
+ * happens. This is expected behavior.
7191
+ */
4342
7192
  async delete(objectRef, targetPath, _context) {
4343
7193
  try {
4344
7194
  const matchedFiles = findObjectFiles(join(targetPath, "src"), objectRef.name, objectRef.type);
@@ -4370,145 +7220,12 @@ const abapGitPlugin = createPlugin({
4370
7220
  },
4371
7221
  hooks: { async afterImport(targetPath) {
4372
7222
  mkdirSync(targetPath, { recursive: true });
4373
- writeFileSync(join(targetPath, ".abapgit.xml"), generateAbapGitXml(currentFolderLogic), "utf-8");
7223
+ const abapgitXmlPath = join(targetPath, ".abapgit.xml");
7224
+ const abapgitXmlContent = generateAbapGitXml(currentFolderLogic);
7225
+ writeFileSync(abapgitXmlPath, abapgitXmlContent, "utf-8");
4374
7226
  } }
4375
7227
  });
4376
7228
 
4377
- //#endregion
4378
- //#region src/lib/filename/adt-uri-to-path.ts
4379
- /**
4380
- * Convert an ADT URI to an on-disk abapGit filename.
4381
- *
4382
- * abapGit stores objects as `<name>.<type>.<ext>` inside `src/`.
4383
- * Classes have additional includes serialised as
4384
- * `<name>.clas.<suffix>.abap`. Function modules become
4385
- * `<group>.fugr.<fm_name>.abap`. Namespaces are rendered as
4386
- * `(namespace)name`.
4387
- *
4388
- * Returns `null` for URIs we don't recognise; callers may warn or fall
4389
- * back to a synthetic path.
4390
- */
4391
- /**
4392
- * Apply abapGit namespace transform: `/MYNS/zcl_foo` → `(myns)zcl_foo`.
4393
- * Case is lower-cased afterwards; name without namespace returned as-is
4394
- * (lower-cased).
4395
- */
4396
- function applyNamespace(raw) {
4397
- const nsMatch = /^\/([^/]+)\/(.*)$/.exec(raw);
4398
- if (nsMatch) {
4399
- const [, ns, rest] = nsMatch;
4400
- return `(${ns.toLowerCase()})${rest.toLowerCase()}`;
4401
- }
4402
- return raw.toLowerCase();
4403
- }
4404
- function stripFragment(uri) {
4405
- const hashIndex = uri.indexOf("#");
4406
- return hashIndex >= 0 ? uri.slice(0, hashIndex) : uri;
4407
- }
4408
- const CLASS_INCLUDE_TO_SUFFIX = {
4409
- testclasses: ABAPGIT_SUFFIX.testclasses,
4410
- definitions: ABAPGIT_SUFFIX.definitions,
4411
- implementations: ABAPGIT_SUFFIX.implementations,
4412
- macros: ABAPGIT_SUFFIX.macros,
4413
- localtypes: ABAPGIT_SUFFIX.localtypes
4414
- };
4415
- const RULES = [
4416
- {
4417
- test: /^\/sap\/bc\/adt\/oo\/classes\/([^/]+)\/includes\/([^/]+)$/,
4418
- build: (m) => {
4419
- const name = applyNamespace(decodeURIComponent(m[1]));
4420
- const include = m[2].toLowerCase();
4421
- const suffix = CLASS_INCLUDE_TO_SUFFIX[include];
4422
- return suffix ? `src/${name}.clas.${suffix}.abap` : `src/${name}.clas.${include}.abap`;
4423
- }
4424
- },
4425
- {
4426
- test: /^\/sap\/bc\/adt\/oo\/classes\/([^/]+)\/source\/main$/,
4427
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.clas.abap`
4428
- },
4429
- {
4430
- test: /^\/sap\/bc\/adt\/oo\/classes\/([^/]+)$/,
4431
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.clas.abap`
4432
- },
4433
- {
4434
- test: /^\/sap\/bc\/adt\/oo\/interfaces\/([^/]+?)(?:\/source\/main)?$/,
4435
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.intf.abap`
4436
- },
4437
- {
4438
- test: /^\/sap\/bc\/adt\/programs\/programs\/([^/]+?)(?:\/source\/main)?$/,
4439
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.prog.abap`
4440
- },
4441
- {
4442
- test: /^\/sap\/bc\/adt\/functions\/groups\/([^/]+)$/,
4443
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.fugr.xml`
4444
- },
4445
- {
4446
- test: /^\/sap\/bc\/adt\/functions\/groups\/([^/]+)\/fmodules\/([^/]+?)(?:\/source\/main)?$/,
4447
- build: (m) => {
4448
- return `src/${applyNamespace(decodeURIComponent(m[1]))}.fugr.${applyNamespace(decodeURIComponent(m[2]))}.abap`;
4449
- }
4450
- },
4451
- {
4452
- test: /^\/sap\/bc\/adt\/functions\/groups\/([^/]+)\/includes\/([^/]+?)(?:\/source\/main)?$/,
4453
- build: (m) => {
4454
- return `src/${applyNamespace(decodeURIComponent(m[1]))}.fugr.${applyNamespace(decodeURIComponent(m[2]))}.abap`;
4455
- }
4456
- },
4457
- {
4458
- test: /^\/sap\/bc\/adt\/ddic\/ddl\/sources\/([^/]+?)(?:\/source\/main)?$/,
4459
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.ddls.asddls`
4460
- },
4461
- {
4462
- test: /^\/sap\/bc\/adt\/acm\/dcl\/sources\/([^/]+?)(?:\/source\/main)?$/,
4463
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.dcls.asdcls`
4464
- },
4465
- {
4466
- test: /^\/sap\/bc\/adt\/bo\/behaviordefinitions\/([^/]+?)(?:\/source\/main)?$/,
4467
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.bdef.abdl`
4468
- },
4469
- {
4470
- test: /^\/sap\/bc\/adt\/ddic\/srvd\/sources\/([^/]+?)(?:\/source\/main)?$/,
4471
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.srvd.asrvd`
4472
- },
4473
- {
4474
- test: /^\/sap\/bc\/adt\/businessservices\/bindings\/([^/]+?)(?:\/publishedstates)?$/,
4475
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.srvb.xml`
4476
- },
4477
- {
4478
- test: /^\/sap\/bc\/adt\/ddic\/domains\/([^/]+)$/,
4479
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.doma.xml`
4480
- },
4481
- {
4482
- test: /^\/sap\/bc\/adt\/ddic\/dataelements\/([^/]+)$/,
4483
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.dtel.xml`
4484
- },
4485
- {
4486
- test: /^\/sap\/bc\/adt\/ddic\/structures\/([^/]+)$/,
4487
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.tabl.xml`
4488
- },
4489
- {
4490
- test: /^\/sap\/bc\/adt\/ddic\/tables\/([^/]+)$/,
4491
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.tabl.xml`
4492
- },
4493
- {
4494
- test: /^\/sap\/bc\/adt\/packages\/([^/]+)$/,
4495
- build: (m) => `src/${applyNamespace(decodeURIComponent(m[1]))}.devc.xml`
4496
- }
4497
- ];
4498
- /**
4499
- * Convert an ADT URI to the abapGit on-disk filename (lower-cased,
4500
- * namespace-aware). Returns `null` when the URI shape is not recognised.
4501
- */
4502
- function adtUriToAbapGitPath(uri) {
4503
- if (!uri) return null;
4504
- const clean = stripFragment(uri).trim();
4505
- for (const rule of RULES) {
4506
- const m = rule.test.exec(clean);
4507
- if (m) return rule.build(m);
4508
- }
4509
- return null;
4510
- }
4511
-
4512
7229
  //#endregion
4513
7230
  //#region src/index.ts
4514
7231
  registerFormatPlugin(abapgitFormatPlugin);