diff --git a/.github/skills/add-endpoint/SKILL.md b/.agents/skills/add-endpoint/SKILL.md similarity index 90% rename from .github/skills/add-endpoint/SKILL.md rename to .agents/skills/add-endpoint/SKILL.md index 1d7fe227..2a594a23 100644 --- a/.github/skills/add-endpoint/SKILL.md +++ b/.agents/skills/add-endpoint/SKILL.md @@ -97,7 +97,7 @@ export default { $xmlns: { adtcore: 'http://www.sap.com/adt/core', xsd: 'http://www.w3.org/2001/XMLSchema', - myns: 'http://www.sap.com/adt/my-namespace', // ← your namespace + myns: 'http://www.sap.com/adt/my-namespace', // ← your namespace }, $imports: [adtcore], targetNamespace: 'http://www.sap.com/adt/my-namespace', @@ -105,7 +105,7 @@ export default { elementFormDefault: 'qualified', element: [ { - name: 'myRootElement', // ← root element name (camelCase) + name: 'myRootElement', // ← root element name (camelCase) type: 'myns:MyRootType', }, ], @@ -114,7 +114,7 @@ export default { name: 'MyRootType', complexContent: { extension: { - base: 'adtcore:AdtMainObject', // ← extend appropriate base type + base: 'adtcore:AdtMainObject', // ← extend appropriate base type sequence: { element: [ { @@ -138,6 +138,7 @@ export default { ``` **Key rules for schema literals:** + - Must end with `} as const` - One root element per schema document - Use `$imports` for schemas you depend on (adtcore, abapoo, abapsource, etc.) @@ -160,6 +161,7 @@ export const mySchemaName: TypedSchema = ``` > **Note on types**: The `types/` files are auto-generated from XSD schemas by the codegen tool. For manually created schemas, you can either: +> > - Run `npx nx build adt-schemas` to trigger codegen (if XSD source exists) > - Or manually create a minimal types file at `packages/adt-schemas/src/schemas/generated/types/sap/mySchemaName.types.ts` following the pattern of existing types files. @@ -213,10 +215,13 @@ export const myObjectContract = { * in URLs, and SAP conventionally normalizes to lowercase in the URL path). */ get: (name: string) => - http.get(`/sap/bc/adt/{your-path}/${encodeURIComponent(name.toLowerCase())}`, { - responses: { 200: mySchemaName }, - headers: { Accept: 'application/vnd.sap.adt.{mimetype}.v1+xml' }, - }), + http.get( + `/sap/bc/adt/{your-path}/${encodeURIComponent(name.toLowerCase())}`, + { + responses: { 200: mySchemaName }, + headers: { Accept: 'application/vnd.sap.adt.{mimetype}.v1+xml' }, + }, + ), }; export type MyObjectContract = typeof myObjectContract; @@ -273,14 +278,14 @@ import { myObjectContract } from './myObject'; export interface OoContract { classes: typeof classesContract; interfaces: typeof interfacesContract; - myObject: typeof myObjectContract; // ← add here + myObject: typeof myObjectContract; // ← add here } // Update the module aggregate: export const ooContract: OoContract = { classes: classesContract, interfaces: interfacesContract, - myObject: myObjectContract, // ← add here + myObject: myObjectContract, // ← add here }; ``` @@ -334,6 +339,7 @@ Create `packages/adt-fixtures/src/fixtures/{module}/{name}.xml`: ``` **Tips for realistic fixtures:** + - Use `ZTEST_` or `$TMP` names (standard test object convention in SAP) - Include all namespace declarations (`xmlns:`) - Match the exact XML structure the schema expects @@ -353,7 +359,7 @@ export const registry = { oo: { class: 'oo/class.xml', interface: 'oo/interface.xml', - myObject: 'oo/myObject.xml', // ← add here + myObject: 'oo/myObject.xml', // ← add here }, } as const; ``` @@ -406,22 +412,22 @@ describe('mySchemaName schema', () => { ### Base types to extend (from adtcore.ts) -| Base type | Purpose | -|-----------|---------| -| `adtcore:AdtObject` | Any ADT object (name, type, description, version, links) | -| `adtcore:AdtMainObject` | Repository object (+ package, responsible, masterLanguage) | -| `abapoo:AbapOoObject` | OO base (+ modeled, syntaxConfiguration) | -| `abapsource:AbapSourceObject` | Source-enabled object (+ sourceUri, fixPointArithmetic) | +| Base type | Purpose | +| ----------------------------- | ---------------------------------------------------------- | +| `adtcore:AdtObject` | Any ADT object (name, type, description, version, links) | +| `adtcore:AdtMainObject` | Repository object (+ package, responsible, masterLanguage) | +| `abapoo:AbapOoObject` | OO base (+ modeled, syntaxConfiguration) | +| `abapsource:AbapSourceObject` | Source-enabled object (+ sourceUri, fixPointArithmetic) | ### Namespace URIs (for `$xmlns`) -| Prefix | URI | -|--------|-----| -| `adtcore` | `http://www.sap.com/adt/core` | +| Prefix | URI | +| ------------ | ----------------------------------- | +| `adtcore` | `http://www.sap.com/adt/core` | | `abapsource` | `http://www.sap.com/adt/abapsource` | -| `abapoo` | `http://www.sap.com/adt/oo` | -| `atom` | `http://www.w3.org/2005/Atom` | -| `xsd` | `http://www.w3.org/2001/XMLSchema` | +| `abapoo` | `http://www.sap.com/adt/oo` | +| `atom` | `http://www.w3.org/2005/Atom` | +| `xsd` | `http://www.w3.org/2001/XMLSchema` | ### Content-Type patterns for SAP ADT diff --git a/.github/skills/add-object-type/SKILL.md b/.agents/skills/add-object-type/SKILL.md similarity index 86% rename from .github/skills/add-object-type/SKILL.md rename to .agents/skills/add-object-type/SKILL.md index fa0fccbc..d2fd1b3d 100644 --- a/.github/skills/add-object-type/SKILL.md +++ b/.agents/skills/add-object-type/SKILL.md @@ -29,17 +29,17 @@ Before writing code, gather details about the object type from live system or re ### Information to collect -| Item | Example (for PROG) | Where to find | -|------|--------------------|---------------| -| ADT object type | `PROG/P` | SAP ADT documentation | -| ADT endpoint path | `/sap/bc/adt/programs/programs` | Live system or community | -| ADT content-type | `application/vnd.sap.adt.programs.programs.v2+xml` | Live system response headers | -| ADT XML namespace | `http://www.sap.com/adt/programs` | Live system XML | -| ADT root element | `abapProgram` | Live system XML | -| abapGit serializer | `LCL_OBJECT_PROG` | abapGit source | -| abapGit main table | `TRDIR` | abapGit source | -| abapGit structure fields | `NAME, SECU, EDTX, ...` | SAP DDIC / abapGit XML sample | -| Has source code | Yes (`.abap` file) | abapGit repo samples | +| Item | Example (for PROG) | Where to find | +| ------------------------ | -------------------------------------------------- | ----------------------------- | +| ADT object type | `PROG/P` | SAP ADT documentation | +| ADT endpoint path | `/sap/bc/adt/programs/programs` | Live system or community | +| ADT content-type | `application/vnd.sap.adt.programs.programs.v2+xml` | Live system response headers | +| ADT XML namespace | `http://www.sap.com/adt/programs` | Live system XML | +| ADT root element | `abapProgram` | Live system XML | +| abapGit serializer | `LCL_OBJECT_PROG` | abapGit source | +| abapGit main table | `TRDIR` | abapGit source | +| abapGit structure fields | `NAME, SECU, EDTX, ...` | SAP DDIC / abapGit XML sample | +| Has source code | Yes (`.abap` file) | abapGit repo samples | ### Option A: Live System Available @@ -96,7 +96,7 @@ export default { adtcore: 'http://www.sap.com/adt/core', abapsource: 'http://www.sap.com/adt/abapsource', xsd: 'http://www.w3.org/2001/XMLSchema', - prog: 'http://www.sap.com/adt/programs', // ← your namespace + prog: 'http://www.sap.com/adt/programs', // ← your namespace }, $imports: [adtcore, abapsource], targetNamespace: 'http://www.sap.com/adt/programs', @@ -104,7 +104,7 @@ export default { elementFormDefault: 'qualified', element: [ { - name: 'abapProgram', // ← root element name + name: 'abapProgram', // ← root element name type: 'prog:AbapProgram', }, ], @@ -128,6 +128,7 @@ export default { ``` Then add to `packages/adt-schemas/src/schemas/generated/typed.ts`: + ```typescript import type { AbapProgramSchema } from './types/sap/abapProgram.types'; import _abapProgram from './schemas/sap/abapProgram'; @@ -136,6 +137,7 @@ export const abapProgram: TypedSchema = ``` And add to `packages/adt-contracts/src/generated/schemas.ts`: + ```typescript export const abapProgram = toSpeciSchema(adtSchemas.abapProgram); ``` @@ -183,7 +185,7 @@ Edit `packages/adk/src/base/kinds.ts` to add the new kind constant: export const Package = 'Package' as const; export const Class = 'Class' as const; export const Interface = 'Interface' as const; -export const Program = 'Program' as const; // ← add your kind here, alphabetically +export const Program = 'Program' as const; // ← add your kind here, alphabetically // Update the AdkKind union type: export type AdkKind = @@ -192,8 +194,8 @@ export type AdkKind = | typeof Package | typeof Class | typeof Interface - | typeof Program // ← add here - // ... + | typeof Program; // ← add here +// ... ``` --- @@ -275,7 +277,7 @@ export class AdkProgram extends AdkMainObject { // in the ADK codebase (see clas.model.ts, intf.model.ts). The base class defines // crudContract as `any` to support different contract structures per object type. protected override get crudContract(): any { - return this.ctx.client.adt.programs.programs; // ← your module path + return this.ctx.client.adt.programs.programs; // ← your module path } // ============================================ @@ -294,6 +296,7 @@ registerObjectType('PROG', ProgramKind, AdkProgram); ``` **Notes:** + - The `wrapperKey` matches the root element name in the schema (e.g., `abapProgram`) - The `crudContract` path must match where you registered the contract in `adtContract` (e.g., `ctx.client.adt.programs.programs`) - `registerObjectType('PROG', ...)` uses the 4-letter ABAP type code (not the full `PROG/P`) @@ -329,12 +332,16 @@ import type { ProgramResponse as _ProgramResponse } from '@abapify/adt-client'; // response type is a union or a single object type. // Example - use whichever form is correct for your contract: -export type ProgramResponse = Extract<_ProgramResponse, { abapProgram: unknown }>; +export type ProgramResponse = Extract< + _ProgramResponse, + { abapProgram: unknown } +>; // OR: // export type { ProgramResponse } from '@abapify/adt-client'; ``` Then in the model file, import as: + ```typescript import type { ProgramResponse } from '../../../base/adt'; ``` @@ -351,12 +358,15 @@ export type { ProgramXml } from './objects/repository/prog'; ### Update `packages/adk/src/base/kinds.ts` KindToObject mapping ```typescript -export type AdkObjectForKind = - K extends typeof Class ? AdkClass - : K extends typeof Interface ? AdkInterface - : K extends typeof Package ? AdkPackage - : K extends typeof Program ? AdkProgram // ← add - : AdkObject; +export type AdkObjectForKind = K extends typeof Class + ? AdkClass + : K extends typeof Interface + ? AdkInterface + : K extends typeof Package + ? AdkPackage + : K extends typeof Program + ? AdkProgram // ← add + : AdkObject; ``` --- @@ -368,12 +378,14 @@ The abapGit schema captures the structure of the XML file that abapGit writes fo ### 5a: Research the abapGit XML format Find out what fields the abapGit serializer writes. Resources: + - **abapGit repository**: https://github.com/abapGit/abapGit — look in `src/objects/` for `ZCL_ABAPGIT_OBJECT_{TYPE}.clas.abap` - Look for `CREATE_VSEO*`, `ZIF_ABAPGIT_OBJECT~DESERIALIZE`, or `SERIALIZE` methods - Look at `.xml` files in abapGit test repositories - Search for `{TYPE}` in abapGit's `docs/` directory A typical abapGit XML looks like: + ```xml @@ -453,7 +465,7 @@ export default { all: { element: [ { - name: 'TRDIR', // ← main SAP table + name: 'TRDIR', // ← main SAP table type: 'asx:TrdirType', minOccurs: '0', }, @@ -461,7 +473,7 @@ export default { }, }, { - name: 'TrdirType', // ← table structure type + name: 'TrdirType', // ← table structure type all: { element: [ { @@ -510,6 +522,7 @@ export default { ``` **Pattern rules for abapGit schemas (follow existing schemas like `intf.ts`, `dtel.ts`):** + - All fields optional (`minOccurs: '0'`) except primary key field - Use `asx:` prefix for types in the `AbapValuesType` and type definitions - Structure name matches SAP DDIC table/structure name (e.g., `TRDIR`, `VSEOCLASS`, `DD04V`) @@ -563,6 +576,7 @@ export type ProgSchema = ``` **Pattern:** The type is always a union of two variants: + 1. `{ abapGit: { abap: { values: ... }, version, serializer, serializer_version } }` — full document 2. `{ abap: { values: ... } }` — inner abap fragment @@ -605,7 +619,7 @@ import { createHandler } from '../base'; export const programHandler = createHandler(AdkProgram, { schema: prog, version: 'v1.0.0', - serializer: 'LCL_OBJECT_PROG', // ← from abapGit source + serializer: 'LCL_OBJECT_PROG', // ← from abapGit source serializer_version: 'v1.0.0', // SAP → Git: Map ADK object to abapGit values @@ -624,8 +638,8 @@ export const programHandler = createHandler(AdkProgram, { // Git → SAP: Map abapGit values to ADK data fromAbapGit: ({ TRDIR }) => ({ name: (TRDIR?.NAME ?? '').toUpperCase(), - type: 'PROG/P', // ← full ADT type code - description: undefined, // TRDIR has no description field + type: 'PROG/P', // ← full ADT type code + description: undefined, // TRDIR has no description field }), // Git → SAP: Set source files on ADK object @@ -635,7 +649,8 @@ export const programHandler = createHandler(AdkProgram, { // the established pattern in this codebase (see clas.ts, intf.ts handlers). setSources: (prog, sources) => { if (sources.main) { - (prog as unknown as { _pendingSource: string })._pendingSource = sources.main; + (prog as unknown as { _pendingSource: string })._pendingSource = + sources.main; } }, }); @@ -668,6 +683,7 @@ setSources: (obj, sources) => { ``` **For objects without source (like DEVC):** + - Do not provide `getSource` or `getSources` - `setSources` is not needed - Only XML file will be serialized @@ -683,7 +699,7 @@ export * from './devc'; export * from './doma'; export * from './dtel'; export * from './intf'; -export * from './prog'; // ← add here +export * from './prog'; // ← add here ``` --- @@ -725,6 +741,7 @@ npx nx lint ### Quick smoke tests **Schema parsing test:** + ```typescript // packages/adt-fixtures tests import { fixtures } from '@abapify/adt-fixtures'; @@ -736,6 +753,7 @@ expect(data.abapProgram?.['adtcore:name']).toBe('ZTEST_PROGRAM'); ``` **abapGit handler test:** + ```typescript import { prog } from '@abapify/adt-plugin-abapgit/schemas/generated'; import { getHandler } from '@abapify/adt-plugin-abapgit/lib/handlers/base'; @@ -748,23 +766,24 @@ expect(handler).toBeDefined(); ## Common Object Type Reference -| ABAP Type | ADT Path | abapGit Serializer | Main Table | -|-----------|----------|--------------------|------------| -| `CLAS/OC` | `/sap/bc/adt/oo/classes` | `LCL_OBJECT_CLAS` | `VSEOCLASS` | -| `INTF/OI` | `/sap/bc/adt/oo/interfaces` | `LCL_OBJECT_INTF` | `VSEOINTERF` | -| `DEVC/K` | `/sap/bc/adt/packages` | `LCL_OBJECT_DEVC` | `DEVC` | -| `DTEL/DE` | `/sap/bc/adt/ddic/dataelements` | `LCL_OBJECT_DTEL` | `DD04V` | -| `DOMA/DO` | `/sap/bc/adt/ddic/domains` | `LCL_OBJECT_DOMA` | `DD01V` | -| `PROG/P` | `/sap/bc/adt/programs/programs` | `LCL_OBJECT_PROG` | `TRDIR` | -| `FUGR/F` | `/sap/bc/adt/functions/groups` | `LCL_OBJECT_FUGR` | `ENLFDIR` | -| `TABL/DT` | `/sap/bc/adt/ddic/tables` | `LCL_OBJECT_TABL` | `DD02V` | -| `MSAG/E` | `/sap/bc/adt/messageclass` | `LCL_OBJECT_MSAG` | `T100A` | +| ABAP Type | ADT Path | abapGit Serializer | Main Table | +| --------- | ------------------------------- | ------------------ | ------------ | +| `CLAS/OC` | `/sap/bc/adt/oo/classes` | `LCL_OBJECT_CLAS` | `VSEOCLASS` | +| `INTF/OI` | `/sap/bc/adt/oo/interfaces` | `LCL_OBJECT_INTF` | `VSEOINTERF` | +| `DEVC/K` | `/sap/bc/adt/packages` | `LCL_OBJECT_DEVC` | `DEVC` | +| `DTEL/DE` | `/sap/bc/adt/ddic/dataelements` | `LCL_OBJECT_DTEL` | `DD04V` | +| `DOMA/DO` | `/sap/bc/adt/ddic/domains` | `LCL_OBJECT_DOMA` | `DD01V` | +| `PROG/P` | `/sap/bc/adt/programs/programs` | `LCL_OBJECT_PROG` | `TRDIR` | +| `FUGR/F` | `/sap/bc/adt/functions/groups` | `LCL_OBJECT_FUGR` | `ENLFDIR` | +| `TABL/DT` | `/sap/bc/adt/ddic/tables` | `LCL_OBJECT_TABL` | `DD02V` | +| `MSAG/E` | `/sap/bc/adt/messageclass` | `LCL_OBJECT_MSAG` | `T100A` | --- ## Checklist ### ADT Endpoint Layer + - [ ] Endpoint details gathered (live system or web research) - [ ] Schema literal created in `adt-schemas/src/schemas/generated/schemas/sap/` - [ ] Typed wrapper added to `adt-schemas/src/schemas/generated/typed.ts` @@ -775,6 +794,7 @@ expect(handler).toBeDefined(); - [ ] Fixture registered in `adt-fixtures/src/fixtures/registry.ts` ### ADK Layer + - [ ] Kind constant added to `adk/src/base/kinds.ts` - [ ] `AdkKind` union type updated in `kinds.ts` - [ ] `AdkObjectForKind` mapping updated in `kinds.ts` @@ -783,6 +803,7 @@ expect(handler).toBeDefined(); - [ ] `adk/src/base/adt.ts` exports the response type ### abapGit Layer + - [ ] abapGit schema literal created in `adt-plugin-abapgit/src/schemas/generated/schemas/{type}.ts` - [ ] TypeScript types created in `adt-plugin-abapgit/src/schemas/generated/types/{type}.ts` - [ ] Schema registered in `adt-plugin-abapgit/src/schemas/generated/index.ts` @@ -791,6 +812,7 @@ expect(handler).toBeDefined(); - [ ] ADK re-export added to `adt-plugin-abapgit/src/lib/handlers/adk.ts` ### Verification + - [ ] `npx nx build adt-schemas adt-contracts adk adt-plugin-abapgit adt-fixtures` passes - [ ] `npx nx typecheck` passes - [ ] `npx nx test adt-schemas adt-contracts adk adt-plugin-abapgit` passes diff --git a/.github/skills/link-workspace-packages/SKILL.md b/.agents/skills/link-workspace-packages/SKILL.md similarity index 100% rename from .github/skills/link-workspace-packages/SKILL.md rename to .agents/skills/link-workspace-packages/SKILL.md diff --git a/.github/skills/monitor-ci/SKILL.md b/.agents/skills/monitor-ci/SKILL.md similarity index 100% rename from .github/skills/monitor-ci/SKILL.md rename to .agents/skills/monitor-ci/SKILL.md diff --git a/.github/skills/nx-generate/SKILL.md b/.agents/skills/nx-generate/SKILL.md similarity index 100% rename from .github/skills/nx-generate/SKILL.md rename to .agents/skills/nx-generate/SKILL.md diff --git a/.github/skills/nx-plugins/SKILL.md b/.agents/skills/nx-plugins/SKILL.md similarity index 100% rename from .github/skills/nx-plugins/SKILL.md rename to .agents/skills/nx-plugins/SKILL.md diff --git a/.github/skills/nx-run-tasks/SKILL.md b/.agents/skills/nx-run-tasks/SKILL.md similarity index 100% rename from .github/skills/nx-run-tasks/SKILL.md rename to .agents/skills/nx-run-tasks/SKILL.md diff --git a/.github/skills/nx-workspace/SKILL.md b/.agents/skills/nx-workspace/SKILL.md similarity index 100% rename from .github/skills/nx-workspace/SKILL.md rename to .agents/skills/nx-workspace/SKILL.md diff --git a/.github/skills/nx-workspace/references/AFFECTED.md b/.agents/skills/nx-workspace/references/AFFECTED.md similarity index 100% rename from .github/skills/nx-workspace/references/AFFECTED.md rename to .agents/skills/nx-workspace/references/AFFECTED.md diff --git a/.claude/skills/git-commit b/.claude/git-commit similarity index 100% rename from .claude/skills/git-commit rename to .claude/git-commit diff --git a/.claude/skills/nx-ci b/.claude/nx-ci similarity index 100% rename from .claude/skills/nx-ci rename to .claude/nx-ci diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 00000000..6474bd71 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +/home/pplenkov/git_projects/adt-cli/.agents/skills \ No newline at end of file diff --git a/.github/skills b/.github/skills new file mode 120000 index 00000000..6474bd71 --- /dev/null +++ b/.github/skills @@ -0,0 +1 @@ +/home/pplenkov/git_projects/adt-cli/.agents/skills \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56062e18..9d94d5b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,8 @@ name: CI on: push: branches: - # Change this if your primary branch is not main + # Runs on merge to main only - main - - 'copilot/**' pull_request: # Needed for nx-set-shas when run on the main branch diff --git a/.gitignore b/.gitignore index 04f4044d..e6a3de47 100644 --- a/.gitignore +++ b/.gitignore @@ -141,4 +141,8 @@ vite.config.*.timestamp* .github/instructions/nx.instructions.md vitest.config.*.timestamp* -secrets \ No newline at end of file +secrets + +.adt + +bun.lock \ No newline at end of file diff --git a/.windsurf/skills b/.windsurf/skills new file mode 120000 index 00000000..6474bd71 --- /dev/null +++ b/.windsurf/skills @@ -0,0 +1 @@ +/home/pplenkov/git_projects/adt-cli/.agents/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index f540170f..f96a7f88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -167,6 +167,24 @@ Key specs: - `docs/specs/oat/` — OAT file format - `docs/specs/adt-cli/` — CLI design decisions +## File Lifecycle — Know Before You Edit + +**Before editing ANY file**, check whether it's generated/downloaded: + +```bash +# Check if a file path appears in any Nx target outputs +npx nx show project --json | grep -i "xsd\|generated\|download" +``` + +| Pattern | Lifecycle | Rule | +| ------------------------------------- | ------------------- | ------------------------------------------------------ | +| `packages/*/src/schemas/generated/**` | Codegen output | Never edit — fix the generator or XSD source | +| `packages/adt-schemas/.xsd/sap/**` | Downloaded from SAP | Never edit — create custom extension in `.xsd/custom/` | +| `packages/adt-schemas/.xsd/custom/**` | Hand-maintained | Safe to edit | +| `packages/*/dist/**` | Build output | Never edit | + +If an edit keeps "reverting": **stop**. Something is regenerating the file. Check Nx targets before using `sed`/force-writes. + ## After Making Changes ```bash diff --git a/adt.config.ts b/adt.config.ts index e9bf3683..b371cde8 100644 --- a/adt.config.ts +++ b/adt.config.ts @@ -14,6 +14,8 @@ export default { '@abapify/adt-codegen/commands/codegen', // ATC (ABAP Test Cockpit) plugin - code quality checks '@abapify/adt-atc/commands/atc', + // AUnit (ABAP Unit Tests) plugin - with JUnit XML for GitLab CI + '@abapify/adt-aunit/commands/aunit', // Export plugin - deploy local files to SAP (aliased as 'deploy') '@abapify/adt-export/commands/export', ], diff --git a/nx.json b/nx.json index 8a473520..1737bc90 100644 --- a/nx.json +++ b/nx.json @@ -1,7 +1,10 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", "defaultBase": "main", - "nxCloudId": "69ab0980542defed8d2aa8f4", + "nxCloudId": "69ab0980542defed8d2aa8f4", + "cli": { + "packageManager": "npm" + }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ diff --git a/package-lock.json b/package-lock.json index 5e5f7601..87877783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,12 @@ "zod": "^4.3.6" }, "devDependencies": { + "@abapify/adt-atc": "workspace:*", + "@abapify/adt-cli": "workspace:*", + "@abapify/adt-codegen": "workspace:*", + "@abapify/adt-config": "workspace:*", + "@abapify/adt-export": "workspace:*", + "@abapify/adt-playwright": "workspace:*", "@eslint/js": "^10.0.1", "@nx/eslint": "22.5.3", "@nx/eslint-plugin": "22.5.3", @@ -92,6 +98,10 @@ "resolved": "packages/adt-atc", "link": true }, + "node_modules/@abapify/adt-aunit": { + "resolved": "packages/adt-aunit", + "link": true + }, "node_modules/@abapify/adt-auth": { "resolved": "packages/adt-auth", "link": true @@ -413,9 +423,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", - "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.7.tgz", + "integrity": "sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==", "dev": true, "license": "MIT", "dependencies": { @@ -2028,13 +2038,13 @@ } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz", - "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.1.tgz", + "integrity": "sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6", + "@babel/helper-define-polyfill-provider": "^0.6.7", "core-js-compat": "^3.48.0" }, "peerDependencies": { @@ -2873,37 +2883,53 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz", - "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==", + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.2", + "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", - "minimatch": "^10.2.1" + "minimatch": "^10.2.4" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@eslint/config-helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", - "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0" + "@eslint/core": "^1.1.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", - "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2935,9 +2961,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", - "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2945,13 +2971,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", - "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.0", + "@eslint/core": "^1.1.1", "levn": "^0.4.1" }, "engines": { @@ -2959,9 +2985,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.10", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.10.tgz", - "integrity": "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==", + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", + "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -5535,9 +5561,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.6.tgz", - "integrity": "sha512-kvjTSWGcrv+BaR2vge57rsKiYdVR8V8CoS0vgKrc570qRBfty4bT+1X0z3j2TaVV+kAYzA0PjeB9+mdZyqUZlg==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.8.tgz", + "integrity": "sha512-5bcmMQDWEfWUq3m79Mcf/kbO6e5Jr6YjKSsA1RnpXR6k73hQ9z1B17+4h93jXpzHvS18p7bQHM1HN/fSd+9zog==", "cpu": [ "arm64" ], @@ -5552,9 +5578,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.6.tgz", - "integrity": "sha512-+tJhD21KvGNtUrpLXrZQlT+j5HZKiEwR2qtcZb3vNOUpvoT9QjEykr75ZW/Kr0W89gose/HVXU6351uVZD8Qvw==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.8.tgz", + "integrity": "sha512-dcHPd5N4g9w2iiPRJmAvO0fsIWzF2JPr9oSuTjxLL56qu+oML5aMbBMNwWbk58Mt3pc7vYs9CCScwLxdXPdRsg==", "cpu": [ "arm64" ], @@ -5569,9 +5595,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.6.tgz", - "integrity": "sha512-DKNhjMk38FAWaHwUt1dFR3rA/qRAvn2NUvSG2UGvxvlMxSmN/qqww/j4ABAbXhNRXtGQNmrAINMXRuwHl16ZHg==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.8.tgz", + "integrity": "sha512-mw0VzDvoj8AuR761QwpdCFN0sc/jspuc7eRYJetpLWd+XyansUrH3C7IgNw6swBOgQT9zBHNKsVCjzpfGJlhUA==", "cpu": [ "x64" ], @@ -5586,9 +5612,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.6.tgz", - "integrity": "sha512-8TThsRkCPAnfyMBShxrGdtoOE6h36QepqRQI97iFaQSCRbHFWHcDHppcojZnzXoruuhPnjMEygzaykvPVJsMRg==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.8.tgz", + "integrity": "sha512-xNrRa6mQ9NmMIJBdJtPMPG8Mso0OhM526pDzc/EKnRrIrrkHD1E0Z6tONZRmUeJElfsQ6h44lQQCcDilSNIvSQ==", "cpu": [ "x64" ], @@ -5603,9 +5629,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.6.tgz", - "integrity": "sha512-ZfmFoOwPUZCWtGOVC9/qbQzfc0249FrRUOzV2XabSMUV60Crp211OWLQN1zmQAsRIVWRcEwhJ46Z1mXGo/L/nQ==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.8.tgz", + "integrity": "sha512-WgCKoO6O/rRUwimWfEJDeztwJJmuuX0N2bYLLRxmXDTtCwjToTOqk7Pashl/QpQn3H/jHjx0b5yCMbcTVYVpNg==", "cpu": [ "arm" ], @@ -5620,9 +5646,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.6.tgz", - "integrity": "sha512-ZsGzbNETxPodGlLTYHaCSGVhNN/rvkMDCJYHdT7PZr5jFJRmBfmDi2awhF64Dt2vxrJqY6VeeYSgOzEbHRsb7Q==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.8.tgz", + "integrity": "sha512-tOHgTOQa8G4Z3ULj4G3NYOGGJEsqPHR91dT72u63OtVsZ7B6wFJKOx+ZKv+pvwzxWz92/I2ycaqi2/Ll4l+rlg==", "cpu": [ "arm64" ], @@ -5637,9 +5663,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.6.tgz", - "integrity": "sha512-elPpdevtCdUOqziemR86C4CSCr/5sUxalzDrf/CJdMT+kZt2C556as++qHikNOz0vuFf52h+GJNXZM08eWgGPQ==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.8.tgz", + "integrity": "sha512-oRbxcgDujCi2Yp1GTxoUFsIFlZsuPHU4OV4AzNc3/6aUmR4lfm9FK0uwQu82PJsuUwnF2jFdop3Ep5c1uK7Uxg==", "cpu": [ "arm64" ], @@ -5653,10 +5679,44 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.8.tgz", + "integrity": "sha512-oaLRyUHw8kQE5M89RqrDJZ10GdmGJcMeCo8tvaE4ukOofqgjV84AbqBSH6tTPjeT2BHv+xlKj678GBuIb47lKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.8.tgz", + "integrity": "sha512-1hjSKFrod5MwBBdLOOA0zpUuSfSDkYIY+QqcMcIU1WOtswZtZdUkcFcZza9b2HcAb0bnpmmyo0LZcaxLb2ov1g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.6.tgz", - "integrity": "sha512-IBwXsf56o3xhzAyaZxdM1CX8UFiBEUFCjiVUgny67Q8vPIqkjzJj0YKhd3TbBHanuxThgBa59f6Pgutg2OGk5A==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.8.tgz", + "integrity": "sha512-a1+F0aV4Wy9tT3o+cHl3XhOy6aFV+B8Ll+/JFj98oGkb6lGk3BNgrxd+80RwYRVd23oLGvj3LwluKYzlv1PEuw==", "cpu": [ "x64" ], @@ -5671,9 +5731,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.6.tgz", - "integrity": "sha512-vOk7G8V9Zm+8a6PL6JTpCea61q491oYlGtO6CvnsbhNLlKdf0bbCPytFzGQhYmCKZDKkEbmnkcIprTEGCURnwg==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.8.tgz", + "integrity": "sha512-bGyXCFU11seFrf7z8PcHSwGEiFVkZ9vs+auLacVOQrVsI8PFHJzzJROF3P6b0ODDmXr0m6Tj5FlDhcXVk0Jp8w==", "cpu": [ "x64" ], @@ -5688,9 +5748,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.6.tgz", - "integrity": "sha512-ASjEDI4MRv7XCQb2JVaBzfEYO98JKCGrAgoW6M03fJzH/ilCnC43Mb3ptB9q/lzsaahoJyIBoAGKAYEjUvpyvQ==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.8.tgz", + "integrity": "sha512-n8d+L2bKgf9G3+AM0bhHFWdlz9vYKNim39ujRTieukdRek0RAo2TfG2uEnV9spa4r4oHUfL9IjcY3M9SlqN1gw==", "cpu": [ "arm64" ], @@ -5705,9 +5765,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.6.tgz", - "integrity": "sha512-mYa1+h2l6Zc0LvmwUh0oXKKYihnw/1WC73vTqw+IgtfEtv47A+rWzzcWwVDkW73+UDr0d/Ie/HRXoaOY22pQDw==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.8.tgz", + "integrity": "sha512-4R4iJDIk7BrJdteAbEAICXPoA7vZoY/M0OBfcRlQxzQvUYMcEp2GbC/C8UOgQJhu2TjGTpX1H8vVO1xHWcRqQA==", "cpu": [ "wasm32" ], @@ -5750,9 +5810,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.6.tgz", - "integrity": "sha512-e2ABskbNH3MRUBMjgxaMjYIw11DSwjLJxBII3UgpF6WClGLIh8A20kamc+FKH5vIaFVnYQInmcLYSUVpqMPLow==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.8.tgz", + "integrity": "sha512-3lwnklba9qQOpFnQ7EW+A1m4bZTWXZE4jtehsZ0YOl2ivW1FQqp5gY7X2DLuKITggesyuLwcmqS11fA7NtrmrA==", "cpu": [ "arm64" ], @@ -5767,9 +5827,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.6.tgz", - "integrity": "sha512-dJVc3ifhaRXxIEh1xowLohzFrlQXkJ66LepHm+CmSprTWgVrPa8Fx3OL57xwIqDEH9hufcKkDX2v65rS3NZyRA==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.8.tgz", + "integrity": "sha512-VGjCx9Ha1P/r3tXGDZyG0Fcq7Q0Afnk64aaKzr1m40vbn1FL8R3W0V1ELDvPgzLXaaqK/9PnsqSaLWXfn6JtGQ==", "cpu": [ "x64" ], @@ -5784,9 +5844,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.6.tgz", - "integrity": "sha512-Y0+JT8Mi1mmW08K6HieG315XNRu4L0rkfCpA364HtytjgiqYnMYRdFPcxRl+BQQqNXzecL2S9nii+RUpO93XIA==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.8.tgz", + "integrity": "sha512-wzJwL82/arVfeSP3BLr1oTy40XddjtEdrdgtJ4lLRBu06mP3q/8HGM6K0JRlQuTA3XB0pNJx2so/nmpY4xyOew==", "dev": true, "license": "MIT" }, @@ -7089,9 +7149,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", - "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", + "version": "25.3.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.5.tgz", + "integrity": "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7210,6 +7270,185 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -7220,17 +7459,33 @@ "node": ">= 4" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", - "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { @@ -7246,14 +7501,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", - "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.1", - "@typescript-eslint/types": "^8.56.1", + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { @@ -7268,14 +7523,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", - "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1" + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7286,9 +7541,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", - "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { @@ -7303,15 +7558,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", - "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -7328,9 +7583,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", - "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { @@ -7342,16 +7597,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", - "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.56.1", - "@typescript-eslint/tsconfig-utils": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -7386,16 +7641,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", - "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1" + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7410,13 +7665,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", - "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -7441,28 +7696,28 @@ } }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-Xj0ZeHEy+yJ/bIg6psPwl0POvBf1j5u7IZAXsUqgvgWbMIvdM9JOGmhpifcj6j28LcXM6GTvXUoXwlatxJ73Qg==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-ZK+ExK7scBzUCAXCTtAwUm6QENJ+l3tCDQXNCly4WcGUvbIAWdaiNns4brganGN9nrxxRkC9Rx0CrxvIsn9zHA==", "dev": true, "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260304.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260304.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20260304.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260304.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20260304.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260304.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20260304.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260309.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260309.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20260309.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260309.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20260309.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260309.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20260309.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-TnTUxYt+dShRSoeOldx7VlKoEG+bvPHnyPEBImlNc7c3WP0AHYyNHrNg6EbLbzkOorARtd06J3Vk+XYzkrRzZg==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-Vszk6vbONyyT47mUTEFNAXk+bJisM8F0pI+MyNPM8i2oorex7Gbp7ivFUGzdZHRFPDXMrlw6AXmgx1U2tZxiHw==", "cpu": [ "arm64" ], @@ -7474,9 +7729,9 @@ ] }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-1nwXX1zbyYI3sDKdaR8NsBdM7LmE0J6OzVtlWgEJ/8YR7oC2/HY6/SfShF3DHHcEOHOFxRLbkJ9zVTJJspWLCw==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-UmmW/L1fW6URMILx5HqxcL2kElOyTYbY6M8yRMQK7gmBzsbkGj37JYN+WZgPkz/PQCVsxwIFcot6WmKRRXeBxQ==", "cpu": [ "x64" ], @@ -7488,9 +7743,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-TXZClCJVteK2f9gcI+I7o1Sxgq3qdMtraXOP9GZF8o0sKCLdDWENN8uORfZSeQv2qOJohcKvrrEz6LLSSngvEg==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-G5zgoOZP2NjZ1kga9mend2in1e3C+Mm3XufelVZ9RwWRka744s6KxAsen853LizCrxBh58foj9pPVnH6gKUJvg==", "cpu": [ "arm" ], @@ -7502,9 +7757,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-cw+xqroXtsk/yVTKbelcPWMd6oZdET9kNWmigyc189KWwzOu2eq2EPXPQsrhEigq8O3j0xW0z3q2oqG+smOiXg==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-sN5rQRvqre8JHUISJhybUQ1e4a+mb/Ifa+uWHJawJ2tojTXWkU1rJTZBnAN3/XeoIJgeSdaZQAZRDlW9B7zbvw==", "cpu": [ "arm64" ], @@ -7516,9 +7771,9 @@ ] }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-EXufnN4PG0HYBHYbHXQXXRXtaQKuKBT3e6nxPhKnwpBBgy2MgWDIxzroTLvI9+SllhbJQzHNZOWiB+SU+KdCNw==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-ZuHu9Sg4/akGSrO49hKLNKwrFXx7AZ2CS3PcTd85cC4nKudqB1aGD9rHxZZZyClj++e0qcNQ+4eTMn1sxDA9VQ==", "cpu": [ "x64" ], @@ -7530,9 +7785,9 @@ ] }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-Be9yyDDbT/PEdNlhG+NXT47fwuiIeN0+/9BkeRKkiLgzY8DqQIC9w5FRWmwAJ+9PVa2sKr5cjD1SpJDHGrPIrA==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-RNIidoGPsRaALc1znXiWfNARkGptm9e55qYnaz11YPvMrqbRKP9Y6Ipx4Oh/diIeF7y9UYiikeyk7EsyKe//sw==", "cpu": [ "arm64" ], @@ -7544,9 +7799,9 @@ ] }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20260304.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260304.1.tgz", - "integrity": "sha512-lg/w+rZ9NIUoqSsk2TbtDsqyD9nW0/rhTMYd14RFP7vuNijLrTbl7GPiMhFtMxaqCSOFapwbql7/3lU4BKHB6g==", + "version": "7.0.0-dev.20260309.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260309.1.tgz", + "integrity": "sha512-/rEvAKowcoEdL2VeNju8apkGHEmbat10jIn1Sncny1zIaWvaMFw6bhmny+kKwX+9deitMfo9ihLlo5GCPJuMPQ==", "cpu": [ "x64" ], @@ -10154,14 +10409,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", - "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.16.tgz", + "integrity": "sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==", "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.6", + "@babel/helper-define-polyfill-provider": "^0.6.7", "semver": "^6.3.1" }, "peerDependencies": { @@ -10193,13 +10448,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", - "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.7.tgz", + "integrity": "sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6" + "@babel/helper-define-polyfill-provider": "^0.6.7" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -10786,9 +11041,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001776", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", - "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==", + "version": "1.0.30001777", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", + "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", "dev": true, "funding": [ { @@ -12441,18 +12696,18 @@ } }, "node_modules/eslint": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz", - "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz", + "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.2", + "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", - "@eslint/core": "^1.1.0", - "@eslint/plugin-kit": "^0.6.0", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -12461,7 +12716,7 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.1", + "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", @@ -12474,7 +12729,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.1", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -12760,9 +13015,9 @@ } }, "node_modules/eslint-scope": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", - "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -12860,6 +13115,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -12877,9 +13148,9 @@ } }, "node_modules/espree": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz", - "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -13148,12 +13419,12 @@ } }, "node_modules/express-rate-limit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", - "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz", + "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", "license": "MIT", "dependencies": { - "ip-address": "10.0.1" + "ip-address": "10.1.0" }, "engines": { "node": ">= 16" @@ -13701,9 +13972,9 @@ } }, "node_modules/flatted": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", - "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "dev": true, "license": "ISC" }, @@ -15198,9 +15469,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "license": "MIT", "engines": { "node": ">= 12" @@ -17102,9 +17373,9 @@ } }, "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -17472,9 +17743,9 @@ } }, "node_modules/knip": { - "version": "5.85.0", - "resolved": "https://registry.npmjs.org/knip/-/knip-5.85.0.tgz", - "integrity": "sha512-V2kyON+DZiYdNNdY6GALseiNCwX7dYdpz9Pv85AUn69Gk0UKCts+glOKWfe5KmaMByRjM9q17Mzj/KinTVOyxg==", + "version": "5.86.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.86.0.tgz", + "integrity": "sha512-tGpRCbP+L+VysXnAp1bHTLQ0k/SdC3M3oX18+Cpiqax1qdS25iuCPzpK8LVmAKARZv0Ijri81Wq09Rzk0JTl+Q==", "dev": true, "funding": [ { @@ -17492,13 +17763,14 @@ "fast-glob": "^3.3.3", "formatly": "^0.3.0", "jiti": "^2.6.0", - "js-yaml": "^4.1.1", "minimist": "^1.2.8", - "oxc-resolver": "^11.15.0", + "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.5.2", "strip-json-comments": "5.0.3", + "unbash": "^2.2.0", + "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { @@ -20282,14 +20554,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.6", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.6.tgz", - "integrity": "sha512-B8vFPV1ADyegoYfhg+E7RAucYKv0xdVlwYYsIJgfPNeiSxZGWNxts9RqhyGzC11ULK/VaeXyKezGCwpMiH8Ktw==", + "version": "1.0.0-rc.8", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.8.tgz", + "integrity": "sha512-RGOL7mz/aoQpy/y+/XS9iePBfeNRDUdozrhCEJxdpJyimW8v6yp4c30q6OviUU5AnUJVLRL9GP//HUs6N3ALrQ==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.115.0", - "@rolldown/pluginutils": "1.0.0-rc.6" + "@rolldown/pluginutils": "1.0.0-rc.8" }, "bin": { "rolldown": "bin/cli.mjs" @@ -20298,25 +20570,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.6", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.6", - "@rolldown/binding-darwin-x64": "1.0.0-rc.6", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.6", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.6", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.6", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.6", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.6", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.6", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.6", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.6", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.6", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.6" + "@rolldown/binding-android-arm64": "1.0.0-rc.8", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.8", + "@rolldown/binding-darwin-x64": "1.0.0-rc.8", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.8", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.8", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.8", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.8", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.8", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.8", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.8", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.8", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.8", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.8", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.8", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.8" } }, "node_modules/rolldown-plugin-dts": { - "version": "0.22.3", - "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.22.3.tgz", - "integrity": "sha512-APIGZGChvLVu05f+7bMmgf+lpvhjIvELhkOsg7c/95IVdOgULVFOX9iciaHJLaBfZeTthIgp+gryGBjgyKNA1A==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.22.4.tgz", + "integrity": "sha512-pueqTPyN1N6lWYivyDGad+j+GO3DT67pzpct8s8e6KGVIezvnrDjejuw1AXFeyDRas3xTq4Ja6Lj5R5/04C5GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -21641,9 +21915,9 @@ } }, "node_modules/tar-fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", "license": "MIT", "dependencies": { "pump": "^3.0.0", @@ -22192,9 +22466,9 @@ } }, "node_modules/tsdown": { - "version": "0.21.0-beta.4", - "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.0-beta.4.tgz", - "integrity": "sha512-ziDGs4VUzCSXAJ/4CucV+We9bt4ApShTdwoV7jg7W1OQkyBQgdSYMbEK/tDOql4x3qX/Oc2H3rxcKHsvmywf5g==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.1.tgz", + "integrity": "sha512-2Qgm5Pztm1ZOBr6AfJ4pAlspuufa5SlnBgnUx7a0QSm0a73FrBETiRB422gHtMKbgWf1oUtjBL/eK+po7OXwKw==", "dev": true, "license": "MIT", "dependencies": { @@ -22206,14 +22480,14 @@ "import-without-cache": "^0.2.5", "obug": "^2.1.1", "picomatch": "^4.0.3", - "rolldown": "1.0.0-rc.6", - "rolldown-plugin-dts": "^0.22.3", + "rolldown": "1.0.0-rc.8", + "rolldown-plugin-dts": "^0.22.4", "semver": "^7.7.4", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tree-kill": "^1.2.2", "unconfig-core": "^7.5.0", - "unrun": "^0.2.29" + "unrun": "^0.2.31" }, "bin": { "tsdown": "dist/run.mjs" @@ -22226,8 +22500,8 @@ }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", - "@tsdown/css": "0.21.0-beta.4", - "@tsdown/exe": "0.21.0-beta.4", + "@tsdown/css": "0.21.1", + "@tsdown/exe": "0.21.1", "@vitejs/devtools": "*", "publint": "^0.3.0", "typescript": "^5.0.0", @@ -22497,6 +22771,201 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -22524,6 +22993,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unbash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-2.2.0.tgz", + "integrity": "sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -22693,13 +23172,13 @@ } }, "node_modules/unrun": { - "version": "0.2.29", - "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.29.tgz", - "integrity": "sha512-smM+bKuQiyfmKr0/3H+/MRxSoAPDjDQRMZYYWlJGDRUero4jRu0UKuYHkQXLnKDE2XUihM8Y6bWi2fI0/JwBSw==", + "version": "0.2.31", + "resolved": "https://registry.npmjs.org/unrun/-/unrun-0.2.31.tgz", + "integrity": "sha512-qltXRUeKQSrIgVS4NbH6PXEFqq+dru2ivH9QINfB+TinSlslgQvursJEV56QzaX8VaDCV5KfbROwKTQf/APJFA==", "dev": true, "license": "MIT", "dependencies": { - "rolldown": "1.0.0-rc.6" + "rolldown": "1.0.0-rc.8" }, "bin": { "unrun": "dist/cli.mjs" @@ -24585,6 +25064,13 @@ "chalk": "^5.3.0" } }, + "packages/adt-aunit": { + "name": "@abapify/adt-aunit", + "version": "0.1.0", + "dependencies": { + "@abapify/adt-plugin": "^0.1.7" + } + }, "packages/adt-auth": { "name": "@abapify/adt-auth", "version": "0.1.10", @@ -24755,9 +25241,9 @@ } }, "packages/adt-playwright/node_modules/@types/node": { - "version": "22.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", - "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "dev": true, "license": "MIT", "dependencies": { @@ -24884,9 +25370,9 @@ } }, "packages/browser-auth/node_modules/@types/node": { - "version": "22.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", - "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index cfdb64b1..b1c1202d 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,12 @@ "zod": "^4.3.6" }, "devDependencies": { + "@abapify/adt-atc": "workspace:*", + "@abapify/adt-cli": "workspace:*", + "@abapify/adt-codegen": "workspace:*", + "@abapify/adt-config": "workspace:*", + "@abapify/adt-export": "workspace:*", + "@abapify/adt-playwright": "workspace:*", "@eslint/js": "^10.0.1", "@nx/eslint": "22.5.3", "@nx/eslint-plugin": "22.5.3", diff --git a/packages/adk/src/objects/repository/devc/devc.model.ts b/packages/adk/src/objects/repository/devc/devc.model.ts index b982968d..cc3650a0 100644 --- a/packages/adk/src/objects/repository/devc/devc.model.ts +++ b/packages/adk/src/objects/repository/devc/devc.model.ts @@ -112,15 +112,62 @@ export class AdkPackage async getSubpackages(): Promise { return this.lazy('subpackages', async () => { - // NOTE: Could use client.repository.packages.children(this.name) when available - return []; + // Search for subpackages using repository search + const response = + await this.ctx.client.adt.repository.informationsystem.search.quickSearch( + { + query: '*', + packageName: this.name, + objectType: 'DEVC', + maxResults: 1000, + }, + ); + + // Parse object references - filter for DEVC type and exclude self + const refs = response.objectReferences?.objectReference ?? []; + const subpkgRefs = (Array.isArray(refs) ? refs : [refs]).filter( + (ref) => ref.type === 'DEVC/K' && ref.name !== this.name, + ); + + // Create AdkPackage instances for each subpackage + return Promise.all( + subpkgRefs.map(async (ref) => { + const pkg = new AdkPackage(this.ctx, ref.name); + await pkg.load(); + return pkg; + }), + ); }); } async getObjects(): Promise { return this.lazy('objects', async () => { - // NOTE: Could use client.repository.packages.objects(this.name) when available - return []; + // Search for objects in this package (exact match, not subpackages) + const response = + await this.ctx.client.adt.repository.informationsystem.search.quickSearch( + { + query: '*', + packageName: this.name, + maxResults: 1000, + }, + ); + + // Parse object references - filter out packages (DEVC) and objects from other packages + const refs = response.objectReferences?.objectReference ?? []; + const objRefs = (Array.isArray(refs) ? refs : [refs]).filter( + (ref) => + ref.type !== 'DEVC/K' && + ref.packageName?.toUpperCase() === this.name.toUpperCase(), + ); + + // Return as AbapObject array + return objRefs.map((ref) => ({ + type: ref.type?.split('/')[0] ?? '', // Extract main type from "CLAS/OC" -> "CLAS" + name: ref.name, + description: ref.description ?? '', + uri: ref.uri ?? '', + packageName: ref.packageName ?? '', + })); }); } diff --git a/packages/adt-aunit/package.json b/packages/adt-aunit/package.json new file mode 100644 index 00000000..57d178b2 --- /dev/null +++ b/packages/adt-aunit/package.json @@ -0,0 +1,33 @@ +{ + "name": "@abapify/adt-aunit", + "publishConfig": { + "access": "public" + }, + "version": "0.1.0", + "description": "ABAP Unit Test CLI plugin for adt-cli with JUnit XML output for GitLab CI", + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": "./dist/index.mjs", + "./commands/aunit": "./dist/commands/aunit.mjs", + "./package.json": "./package.json" + }, + "files": [ + "dist", + "README.md" + ], + "keywords": [ + "sap", + "adt", + "abap", + "aunit", + "unit-test", + "junit", + "gitlab-ci" + ], + "dependencies": { + "@abapify/adt-plugin": "^0.1.7" + }, + "module": "./dist/index.mjs" +} diff --git a/packages/adt-aunit/project.json b/packages/adt-aunit/project.json new file mode 100644 index 00000000..6d4d4770 --- /dev/null +++ b/packages/adt-aunit/project.json @@ -0,0 +1,8 @@ +{ + "name": "adt-aunit", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/adt-aunit/src", + "projectType": "library", + "tags": ["scope:adt", "type:plugin"], + "targets": {} +} diff --git a/packages/adt-aunit/src/commands/aunit.ts b/packages/adt-aunit/src/commands/aunit.ts new file mode 100644 index 00000000..adfe8672 --- /dev/null +++ b/packages/adt-aunit/src/commands/aunit.ts @@ -0,0 +1,514 @@ +/** + * AUnit Command Plugin + * + * CLI-agnostic command for running ABAP Unit tests. + * Uses the CliContext.getAdtClient() factory for API access. + * + * Supports JUnit XML output for GitLab CI integration: + * @see https://docs.gitlab.com/ci/testing/unit_test_reports/ + */ + +import type { CliCommandPlugin, CliContext } from '@abapify/adt-plugin'; +// Simple ANSI color helpers (no external dependency) +const ansi = { + red: (s: string) => `\x1b[31m${s}\x1b[0m`, + green: (s: string) => `\x1b[32m${s}\x1b[0m`, + yellow: (s: string) => `\x1b[33m${s}\x1b[0m`, + cyan: (s: string) => `\x1b[36m${s}\x1b[0m`, + dim: (s: string) => `\x1b[2m${s}\x1b[0m`, +}; +import { outputJunitReport } from '../formatters'; +import type { + AunitResult, + AunitProgram, + AunitTestClass, + AunitTestMethod, + AunitAlert, + OutputFormat, +} from '../types'; + +// Client type - the plugin receives a typed client from context +// The client.adt.aunit namespace provides the testruns operation +interface AdtClient { + adt: { + aunit: { + testruns: { + post: (body: RunConfigurationBody) => Promise; + }; + }; + }; +} + +// Request body shape (matches aunitRun schema) +interface RunConfigurationBody { + runConfiguration: { + external?: { + coverage?: { active?: string }; + }; + options?: { + uriType?: { value?: string }; + testDeterminationStrategy?: { + sameProgram?: string; + assignedTests?: string; + appendAssignedTestsPreview?: string; + }; + testRiskLevels?: { + harmless?: string; + dangerous?: string; + critical?: string; + }; + testDurations?: { + short?: string; + medium?: string; + long?: string; + }; + withNavigationUri?: { enabled?: string }; + }; + objectSets: { + objectSet: Array<{ + kind: string; + objectReferences?: { + objectReference: Array<{ uri: string }>; + }; + }>; + }; + }; +} + +// Response shape (matches aunitResult schema) +interface RunResultResponse { + runResult: { + program?: Array<{ + uri?: string; + type?: string; + name?: string; + uriType?: string; + testClasses?: { + testClass?: Array<{ + uri?: string; + name?: string; + uriType?: string; + durationCategory?: string; + riskLevel?: string; + testMethods?: { + testMethod?: Array<{ + uri?: string; + name?: string; + executionTime?: string; + uriType?: string; + unit?: string; + alerts?: { + alert?: Array<{ + kind?: string; + severity?: string; + title?: string; + details?: { + detail?: Array<{ text?: string }>; + }; + stack?: { + stackEntry?: Array<{ + uri?: string; + type?: string; + name?: string; + description?: string; + }>; + }; + }>; + }; + }>; + }; + alerts?: { + alert?: Array<{ + kind?: string; + severity?: string; + title?: string; + details?: { + detail?: Array<{ text?: string }>; + }; + stack?: { + stackEntry?: Array<{ + uri?: string; + type?: string; + name?: string; + description?: string; + }>; + }; + }>; + }; + }>; + }; + alerts?: { + alert?: Array<{ + kind?: string; + severity?: string; + title?: string; + }>; + }; + }>; + }; +} + +/** + * Build the default runConfiguration request body + */ +function buildRunConfiguration(targetUris: string[]): RunConfigurationBody { + return { + runConfiguration: { + external: { + coverage: { active: 'false' }, + }, + options: { + uriType: { value: 'semantic' }, + testDeterminationStrategy: { + sameProgram: 'true', + assignedTests: 'false', + appendAssignedTestsPreview: 'true', + }, + testRiskLevels: { + harmless: 'true', + dangerous: 'true', + critical: 'true', + }, + testDurations: { + short: 'true', + medium: 'true', + long: 'true', + }, + withNavigationUri: { enabled: 'false' }, + }, + objectSets: { + objectSet: [ + { + kind: 'inclusive', + objectReferences: { + objectReference: targetUris.map((uri) => ({ uri })), + }, + }, + ], + }, + }, + }; +} + +/** + * Convert SAP AUnit response to our normalized AunitResult + */ +function convertResponse(response: RunResultResponse): AunitResult { + const programs: AunitProgram[] = []; + let totalTests = 0; + let passCount = 0; + let failCount = 0; + let errorCount = 0; + let skipCount = 0; + let totalTime = 0; + + for (const prog of response.runResult.program || []) { + const testClasses: AunitTestClass[] = []; + + for (const tc of prog.testClasses?.testClass || []) { + const methods: AunitTestMethod[] = []; + + for (const tm of tc.testMethods?.testMethod || []) { + const execTime = parseFloat(tm.executionTime || '0'); + totalTime += execTime; + + const alerts: AunitAlert[] = []; + let status: AunitTestMethod['status'] = 'pass'; + + for (const alert of tm.alerts?.alert || []) { + const details = (alert.details?.detail || []).map( + (d) => d.text || '', + ); + const stack = (alert.stack?.stackEntry || []).map((s) => ({ + uri: s.uri, + type: s.type, + name: s.name, + description: s.description, + })); + + alerts.push({ + kind: alert.kind || 'unknown', + severity: alert.severity || 'unknown', + title: alert.title || '', + details, + stack, + }); + + // Determine status from alert severity/kind + if ( + alert.severity === 'critical' || + alert.kind === 'failedAssertion' + ) { + status = 'fail'; + } else if (alert.severity === 'fatal' || alert.kind === 'error') { + status = 'error'; + } + } + + totalTests++; + if (status === 'pass') passCount++; + else if (status === 'fail') failCount++; + else if (status === 'error') errorCount++; + else if (status === 'skip') skipCount++; + + methods.push({ + name: tm.name || 'UNKNOWN', + uri: tm.uri, + executionTime: execTime, + status, + alerts, + }); + } + + testClasses.push({ + name: tc.name || 'UNKNOWN', + uri: tc.uri, + riskLevel: tc.riskLevel, + durationCategory: tc.durationCategory, + methods, + }); + } + + programs.push({ + name: prog.name || 'UNKNOWN', + type: prog.type, + uri: prog.uri, + testClasses, + }); + } + + return { + programs, + totalTests, + passCount, + failCount, + errorCount, + skipCount, + totalTime, + }; +} + +/** + * Create OSC 8 hyperlink for terminal + */ +function hyperlink(text: string, url: string): string { + const OSC = '\x1b]'; + const BEL = '\x07'; + const SEP = ';'; + return `${OSC}8${SEP}${SEP}${url}${BEL}${text}${OSC}8${SEP}${SEP}${BEL}`; +} + +/** + * Create ADT Eclipse link + */ +function adtLink(name: string, uri: string, systemName?: string): string { + if (!systemName || !name || !uri) { + return name ? ansi.cyan(name) : ''; + } + const path = uri.startsWith('/sap/bc/adt') ? uri : `/sap/bc/adt${uri}`; + const url = `adt://${systemName}${path}`; + return hyperlink(ansi.cyan(name), url); +} + +/** + * Display AUnit results in console + */ +function displayResults(result: AunitResult, systemName?: string): void { + if (result.totalTests === 0) { + console.log(`\n⚠️ No tests found`); + return; + } + + const allPassed = result.failCount === 0 && result.errorCount === 0; + + console.log(`\n${allPassed ? '✅' : '❌'} ABAP Unit Test Results:`); + console.log( + ` 📋 Total: ${result.totalTests} tests in ${result.totalTime.toFixed(3)}s`, + ); + if (result.passCount > 0) + console.log(` ${ansi.green(`✓ ${result.passCount} passed`)}`); + if (result.failCount > 0) + console.log(` ${ansi.red(`✗ ${result.failCount} failed`)}`); + if (result.errorCount > 0) + console.log(` ${ansi.red(`⚠ ${result.errorCount} errors`)}`); + if (result.skipCount > 0) + console.log(` ${ansi.yellow(`○ ${result.skipCount} skipped`)}`); + + // Show failed tests + for (const prog of result.programs) { + for (const tc of prog.testClasses) { + const failedMethods = tc.methods.filter( + (m) => m.status === 'fail' || m.status === 'error', + ); + if (failedMethods.length === 0) continue; + + const classLink = adtLink( + `${prog.name} → ${tc.name}`, + tc.uri || prog.uri || '', + systemName, + ); + console.log(`\n ${classLink}`); + + for (const method of failedMethods) { + const icon = method.status === 'fail' ? ansi.red('✗') : ansi.red('⚠'); + console.log(` ${icon} ${method.name} (${method.executionTime}s)`); + for (const alert of method.alerts) { + console.log(` ${ansi.dim(alert.title)}`); + for (const detail of alert.details) { + console.log(` ${ansi.dim(` ${detail}`)}`); + } + } + } + } + } +} + +/** + * AUnit Command Plugin + */ +export const aunitCommand: CliCommandPlugin = { + name: 'aunit', + description: 'Run ABAP Unit tests', + + options: [ + { + flags: '-p, --package ', + description: 'Run tests on package', + }, + { + flags: '-o, --object ', + description: + 'Run tests on specific object (e.g., /sap/bc/adt/oo/classes/zcl_my_class)', + }, + { + flags: '-c, --class ', + description: 'Run tests on ABAP class (e.g., ZCL_MY_CLASS)', + }, + { + flags: '-t, --transport ', + description: 'Run tests on transport request (e.g., NPLK900042)', + }, + { + flags: '-f, --from-file ', + description: 'Run tests on objects listed in file (one URI per line)', + }, + { + flags: '--format ', + description: 'Output format: console, json, junit', + default: 'console', + }, + { + flags: '--output ', + description: + 'Output file (required for junit format, e.g., aunit-report.xml)', + }, + ], + + async execute(args, ctx: CliContext) { + const options = args as { + package?: string; + object?: string; + class?: string; + transport?: string; + fromFile?: string; + format?: OutputFormat; + output?: string; + }; + + // Validate: at least one target + const targetCount = [ + options.package, + options.object, + options.class, + options.transport, + options.fromFile, + ].filter(Boolean).length; + + if (targetCount === 0) { + ctx.logger.error( + '❌ One of --package, --object, --class, --transport, or --from-file is required', + ); + process.exit(1); + } + + if (targetCount > 1) { + ctx.logger.error( + '❌ Only one of --package, --object, --class, --transport, or --from-file can be specified', + ); + process.exit(1); + } + + // Validate output file for junit format + if (options.format === 'junit' && !options.output) { + ctx.logger.error('❌ --output is required for junit format'); + process.exit(1); + } + + // Get ADT client + if (!ctx.getAdtClient) { + ctx.logger.error('❌ ADT client not available. Run: adt auth login'); + process.exit(1); + } + + const client = (await ctx.getAdtClient()) as AdtClient; + + // Determine targets + let targetUris: string[]; + let targetName: string; + + if (options.fromFile) { + const { readFileSync } = await import('fs'); + const content = readFileSync(options.fromFile, 'utf-8'); + targetUris = content + .split('\n') + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith('#')); + if (targetUris.length === 0) { + ctx.logger.error(`❌ No objects found in ${options.fromFile}`); + process.exit(1); + } + targetName = `${targetUris.length} objects from ${options.fromFile}`; + } else if (options.transport) { + targetUris = [ + `/sap/bc/adt/cts/transportrequests/${options.transport.toUpperCase()}`, + ]; + targetName = `Transport ${options.transport.toUpperCase()}`; + } else if (options.class) { + targetUris = [`/sap/bc/adt/oo/classes/${options.class.toLowerCase()}`]; + targetName = `Class ${options.class.toUpperCase()}`; + } else if (options.package) { + targetUris = [`/sap/bc/adt/packages/${options.package.toUpperCase()}`]; + targetName = `Package ${options.package.toUpperCase()}`; + } else { + targetUris = [options.object!]; + targetName = options.object!; + } + + ctx.logger.info(`🧪 Running ABAP Unit tests on ${targetName}...`); + + // Execute test run + const body = buildRunConfiguration(targetUris); + const response = await client.adt.aunit.testruns.post(body); + + // Convert to normalized result + const result = convertResponse(response); + + // Output + if (options.format === 'json') { + console.log(JSON.stringify(result, null, 2)); + } else if (options.format === 'junit' && options.output) { + await outputJunitReport(result, options.output); + // Also print summary to console + displayResults(result, ctx.adtSystemName); + } else { + displayResults(result, ctx.adtSystemName); + } + + // Exit with error code if tests failed + if (result.failCount > 0 || result.errorCount > 0) { + process.exit(1); + } + }, +}; + +export default aunitCommand; diff --git a/packages/adt-aunit/src/formatters/index.ts b/packages/adt-aunit/src/formatters/index.ts new file mode 100644 index 00000000..0ea1ec4e --- /dev/null +++ b/packages/adt-aunit/src/formatters/index.ts @@ -0,0 +1 @@ +export { toJunitXml, outputJunitReport } from './junit'; diff --git a/packages/adt-aunit/src/formatters/junit.ts b/packages/adt-aunit/src/formatters/junit.ts new file mode 100644 index 00000000..1825ce02 --- /dev/null +++ b/packages/adt-aunit/src/formatters/junit.ts @@ -0,0 +1,145 @@ +/** + * JUnit XML Formatter + * + * Outputs AUnit results in JUnit XML format compatible with + * GitLab CI unit_test_reports. + * + * @see https://docs.gitlab.com/ci/testing/unit_test_reports/ + * + * GitLab parses: + * - testsuites > testsuite (name, time) + * - testsuite > testcase (classname, name, file, time) + * - testcase > failure, error, skipped + * - testcase > system-out, system-err + */ + +import { writeFile } from 'fs/promises'; +import type { AunitResult, AunitTestMethod } from '../types'; + +/** + * Escape XML special characters + */ +function escapeXml(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** + * Build failure/error message from alerts + */ +function buildAlertMessage(method: AunitTestMethod): string { + return method.alerts + .map((alert) => { + const lines: string[] = []; + lines.push(alert.title); + for (const detail of alert.details) { + lines.push(` ${detail}`); + } + if (alert.stack.length > 0) { + lines.push('Stack:'); + for (const entry of alert.stack) { + const desc = entry.description || entry.name || ''; + lines.push(` at ${desc}${entry.uri ? ` (${entry.uri})` : ''}`); + } + } + return lines.join('\n'); + }) + .join('\n---\n'); +} + +/** + * Convert AUnit result to JUnit XML string + */ +export function toJunitXml(result: AunitResult): string { + const lines: string[] = []; + lines.push(''); + + // Collect all test suites (one per test class) + const suites: string[] = []; + + for (const program of result.programs) { + for (const testClass of program.testClasses) { + const tests = testClass.methods.length; + const failures = testClass.methods.filter( + (m) => m.status === 'fail', + ).length; + const errors = testClass.methods.filter( + (m) => m.status === 'error', + ).length; + const skipped = testClass.methods.filter( + (m) => m.status === 'skip', + ).length; + const time = testClass.methods.reduce( + (sum, m) => sum + m.executionTime, + 0, + ); + + const suiteLines: string[] = []; + suiteLines.push( + ` `, + ); + + for (const method of testClass.methods) { + const classname = `${program.name}.${testClass.name}`; + suiteLines.push( + ` `, + ); + + if (method.status === 'fail') { + const message = method.alerts[0]?.title || 'Assertion failed'; + const body = buildAlertMessage(method); + suiteLines.push( + ` ${escapeXml(body)}`, + ); + } else if (method.status === 'error') { + const message = method.alerts[0]?.title || 'Error'; + const body = buildAlertMessage(method); + suiteLines.push( + ` ${escapeXml(body)}`, + ); + } else if (method.status === 'skip') { + suiteLines.push(' '); + } + + // Add system-out with ADT URI for navigation + if (method.uri) { + suiteLines.push( + ` ${escapeXml(method.uri)}`, + ); + } + + suiteLines.push(' '); + } + + suiteLines.push(' '); + suites.push(suiteLines.join('\n')); + } + } + + lines.push( + ``, + ); + lines.push(...suites); + lines.push(''); + + return lines.join('\n'); +} + +/** + * Write JUnit XML report to file + */ +export async function outputJunitReport( + result: AunitResult, + outputFile: string, +): Promise { + const xml = toJunitXml(result); + await writeFile(outputFile, xml, 'utf-8'); + console.log(`\n📄 JUnit XML report written to: ${outputFile}`); + console.log( + `📊 ${result.totalTests} tests (${result.passCount} passed, ${result.failCount} failed, ${result.errorCount} errors)`, + ); +} diff --git a/packages/adt-aunit/src/index.ts b/packages/adt-aunit/src/index.ts new file mode 100644 index 00000000..de807283 --- /dev/null +++ b/packages/adt-aunit/src/index.ts @@ -0,0 +1,39 @@ +/** + * @abapify/adt-aunit + * + * ABAP Unit Test CLI plugin for adt-cli. + * Supports JUnit XML output for GitLab CI integration. + * + * @example + * ```typescript + * // In adt.config.ts + * export default { + * commands: [ + * '@abapify/adt-aunit/commands/aunit', + * ], + * }; + * ``` + * + * @example GitLab CI + * ```yaml + * abap-unit: + * script: + * - npx adt aunit -p $PACKAGE --format junit --output aunit-report.xml + * artifacts: + * when: always + * reports: + * junit: aunit-report.xml + * ``` + */ + +export { aunitCommand } from './commands/aunit'; +export { toJunitXml, outputJunitReport } from './formatters'; +export type { + AunitResult, + AunitProgram, + AunitTestClass, + AunitTestMethod, + AunitAlert, + AunitStackEntry, + OutputFormat, +} from './types'; diff --git a/packages/adt-aunit/src/types.ts b/packages/adt-aunit/src/types.ts new file mode 100644 index 00000000..52c672ef --- /dev/null +++ b/packages/adt-aunit/src/types.ts @@ -0,0 +1,98 @@ +/** + * AUnit Types + * + * Types for ABAP Unit test results and formatters. + */ + +/** + * A single test method result + */ +export interface AunitTestMethod { + /** Test method name (e.g., TEST_ALWAYS_FAILS) */ + name: string; + /** ADT URI for navigation */ + uri?: string; + /** Execution time in seconds */ + executionTime: number; + /** 'pass' | 'fail' | 'error' | 'skip' */ + status: 'pass' | 'fail' | 'error' | 'skip'; + /** Alert details if test failed */ + alerts: AunitAlert[]; +} + +/** + * A test class containing test methods + */ +export interface AunitTestClass { + /** Test class name (e.g., LTCL_TEST) */ + name: string; + /** ADT URI for navigation */ + uri?: string; + /** Risk level: harmless, dangerous, critical */ + riskLevel?: string; + /** Duration category: short, medium, long */ + durationCategory?: string; + /** Test methods in this class */ + methods: AunitTestMethod[]; +} + +/** + * A program/object containing test classes + */ +export interface AunitProgram { + /** Object name (e.g., ZCL_MY_CLASS) */ + name: string; + /** Object type (e.g., CLAS/OC) */ + type?: string; + /** ADT URI for navigation */ + uri?: string; + /** Test classes in this program */ + testClasses: AunitTestClass[]; +} + +/** + * An alert (assertion failure, error, etc.) + */ +export interface AunitAlert { + /** Alert kind: failedAssertion, warning, etc. */ + kind: string; + /** Severity: critical, tolerable, etc. */ + severity: string; + /** Alert title/message */ + title: string; + /** Detail messages */ + details: string[]; + /** Stack trace entries */ + stack: AunitStackEntry[]; +} + +/** + * Stack trace entry + */ +export interface AunitStackEntry { + uri?: string; + type?: string; + name?: string; + description?: string; +} + +/** + * Full AUnit run result + */ +export interface AunitResult { + /** All programs/objects tested */ + programs: AunitProgram[]; + /** Summary counts */ + totalTests: number; + passCount: number; + failCount: number; + errorCount: number; + skipCount: number; + /** Total execution time in seconds */ + totalTime: number; +} + +/** + * Output format options + */ +export type OutputFormat = 'console' | 'json' | 'junit'; diff --git a/packages/adt-aunit/tsconfig.json b/packages/adt-aunit/tsconfig.json new file mode 100644 index 00000000..ba5b39c6 --- /dev/null +++ b/packages/adt-aunit/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "moduleResolution": "bundler", + "module": "ESNext", + "target": "ES2022", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"], + "references": [ + { + "path": "../adt-plugin" + } + ] +} diff --git a/packages/adt-aunit/tsdown.config.ts b/packages/adt-aunit/tsdown.config.ts new file mode 100644 index 00000000..04c42e3a --- /dev/null +++ b/packages/adt-aunit/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsdown'; + +export default defineConfig({ + entry: { + index: 'src/index.ts', + 'commands/aunit': 'src/commands/aunit.ts', + }, + format: ['esm'], + dts: true, + sourcemap: true, + clean: true, + external: ['@abapify/adt-plugin', '@abapify/adt-contracts'], +}); diff --git a/packages/adt-auth/package.json b/packages/adt-auth/package.json index 4d17d4fb..98431d31 100644 --- a/packages/adt-auth/package.json +++ b/packages/adt-auth/package.json @@ -11,6 +11,9 @@ ".": "./dist/index.mjs", "./basic": "./dist/basic.mjs", "./plugins/basic": "./dist/plugins/basic.mjs", + "./plugins/service-key": "./dist/plugins/service-key.mjs", + "./service-key": "./dist/service-key.mjs", + "./utils/env": "./dist/utils/env.mjs", "./package.json": "./package.json" }, "files": [ diff --git a/packages/adt-auth/src/index.ts b/packages/adt-auth/src/index.ts index 47ae8bec..6d8601ca 100644 --- a/packages/adt-auth/src/index.ts +++ b/packages/adt-auth/src/index.ts @@ -13,6 +13,10 @@ export { FileStorage } from './storage/file-storage'; // Built-in plugins export { default as basicAuthPlugin } from './plugins/basic'; +export { default as serviceKeyAuthPlugin } from './plugins/service-key'; + +// Utilities +export { resolveServiceKeyFromEnv, readServiceKey } from './utils/env'; // Types - New format (single source of truth) export type { @@ -29,3 +33,9 @@ export type { BasicAuthResult, ConnectionTestResult, } from './types'; + +export type { + BTPServiceKey, + UAACredentials, + ServiceKeyPluginOptions, +} from './types/service-key'; diff --git a/packages/adt-auth/src/plugins/service-key.ts b/packages/adt-auth/src/plugins/service-key.ts new file mode 100644 index 00000000..89bac6c8 --- /dev/null +++ b/packages/adt-auth/src/plugins/service-key.ts @@ -0,0 +1,243 @@ +import { createServer, type Server } from 'http'; +import { parse as parseUrl } from 'url'; +import type { AuthPlugin, AuthPluginOptions, CookieAuthResult } from '../types'; +import { + ServiceKeyParser, + type BTPServiceKey, + type ServiceKeyPluginOptions, +} from '../types/service-key'; +import { + generateCodeVerifier, + generateCodeChallenge, + generateState, +} from '../utils/pkce'; + +const DEFAULT_PORT = 3000; +const DEFAULT_REDIRECT_PATH = '/callback'; +const DEFAULT_TIMEOUT_MS = 300_000; // 5 minutes + +interface OAuthTokenResponse { + access_token: string; + token_type: string; + expires_in: number; + scope?: string; + refresh_token?: string; +} + +/** + * Exchange an authorization code for tokens using the PKCE flow. + */ +async function exchangeCodeForToken( + serviceKey: BTPServiceKey, + code: string, + codeVerifier: string, + redirectUri: string, +): Promise { + const { clientid, clientsecret, url: uaaUrl } = serviceKey.uaa; + const credentials = Buffer.from(`${clientid}:${clientsecret}`).toString( + 'base64', + ); + + const response = await fetch(`${uaaUrl}/oauth/token`, { + method: 'POST', + headers: { + Authorization: `Basic ${credentials}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + code, + redirect_uri: redirectUri, + code_verifier: codeVerifier, + }), + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Token exchange failed (${response.status}): ${text}`); + } + + const data = (await response.json()) as OAuthTokenResponse; + + if (!data.access_token) { + throw new Error('Token response missing access_token'); + } + + return data; +} + +/** + * Run the OAuth PKCE authorization code flow: + * 1. Start local callback server + * 2. Build XSUAA authorize URL with PKCE + * 3. Open browser for user login + * 4. Receive auth code via redirect + * 5. Exchange code for user token + */ +async function performPkceFlow( + serviceKey: BTPServiceKey, + openBrowser: (url: string) => Promise, + port: number, + timeoutMs: number, +): Promise { + const codeVerifier = generateCodeVerifier(); + const codeChallenge = generateCodeChallenge(codeVerifier); + const state = generateState(); + + const redirectUri = `http://localhost:${port}${DEFAULT_REDIRECT_PATH}`; + + // Build XSUAA authorize URL + const authUrl = new URL(`${serviceKey.uaa.url}/oauth/authorize`); + authUrl.searchParams.set('client_id', serviceKey.uaa.clientid); + authUrl.searchParams.set('response_type', 'code'); + authUrl.searchParams.set('redirect_uri', redirectUri); + authUrl.searchParams.set('code_challenge', codeChallenge); + authUrl.searchParams.set('code_challenge_method', 'S256'); + authUrl.searchParams.set('state', state); + authUrl.searchParams.set('scope', 'openid'); + + return new Promise((resolve, reject) => { + // eslint-disable-next-line prefer-const -- server must be declared before timeout but assigned after + let server: Server; + + const timeout = setTimeout(() => { + server?.close(); + reject(new Error('Authentication timed out')); + }, timeoutMs); + + server = createServer(async (req, res) => { + try { + const url = parseUrl(req.url || '', true); + + if (url.pathname !== DEFAULT_REDIRECT_PATH) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('Not Found'); + return; + } + + const { + code, + state: returnedState, + error, + error_description, + } = url.query; + + if (error) { + throw new Error( + `OAuth error: ${error}${error_description ? ` - ${error_description}` : ''}`, + ); + } + + if (returnedState !== state) { + throw new Error('State mismatch - possible CSRF attack'); + } + + if (!code) { + throw new Error('No authorization code received'); + } + + const tokenData = await exchangeCodeForToken( + serviceKey, + code as string, + codeVerifier, + redirectUri, + ); + + res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); + res.end( + '' + + '

Authentication successful!

' + + '

You can close this window and return to the terminal.

' + + '' + + '', + ); + + clearTimeout(timeout); + setTimeout(() => server.close(() => resolve(tokenData)), 500); + } catch (err) { + res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' }); + res.end( + '' + + `

Authentication failed

${err instanceof Error ? err.message : String(err)}

` + + '', + ); + + clearTimeout(timeout); + server.close(() => reject(err)); + } + }); + + server.on('error', (err) => { + clearTimeout(timeout); + reject(err); + }); + + server.listen(port, () => { + openBrowser(authUrl.toString()).catch((err) => { + clearTimeout(timeout); + server.close(); + reject( + new Error('Could not open browser for authentication', { + cause: err, + }), + ); + }); + }); + }); +} + +const authPlugin: AuthPlugin = { + async authenticate(options: AuthPluginOptions): Promise { + const { serviceKey, openBrowser, callbackPort, timeoutMs } = + options as ServiceKeyPluginOptions; + + if (!serviceKey) { + throw new Error('service-key plugin requires a serviceKey option'); + } + + const parsed = ServiceKeyParser.parse(serviceKey); + + // Resolve openBrowser: use provided callback, or dynamically import 'open' + let browserOpener: (url: string) => Promise; + if (openBrowser) { + browserOpener = openBrowser; + } else { + // Fallback: try dynamic import of 'open' package + try { + const openModule = await import('open'); + const openFn = openModule.default; + browserOpener = async (url: string) => { + await openFn(url); + }; + } catch { + throw new Error( + 'No openBrowser callback provided and "open" package not available. ' + + 'Install "open" or pass an openBrowser function in plugin options.', + ); + } + } + + const port = typeof callbackPort === 'number' ? callbackPort : DEFAULT_PORT; + const timeout = + typeof timeoutMs === 'number' ? timeoutMs : DEFAULT_TIMEOUT_MS; + + const tokenData = await performPkceFlow( + parsed, + browserOpener, + port, + timeout, + ); + + const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000); + + return { + method: 'cookie', + credentials: { + cookies: `Authorization: Bearer ${tokenData.access_token}`, + expiresAt, + }, + }; + }, +}; + +export default authPlugin; diff --git a/packages/adt-cli/src/lib/auth-utils.ts b/packages/adt-auth/src/types/service-key.ts similarity index 69% rename from packages/adt-cli/src/lib/auth-utils.ts rename to packages/adt-auth/src/types/service-key.ts index 85058420..a116d401 100644 --- a/packages/adt-cli/src/lib/auth-utils.ts +++ b/packages/adt-auth/src/types/service-key.ts @@ -1,5 +1,4 @@ -// Standalone auth utilities for ADT CLI -// Based on @abapify/btp-service-key-parser +// BTP Service Key types export interface UAACredentials { tenantmode: string; @@ -52,6 +51,19 @@ export interface OAuthToken { expires_at: Date; } +export interface ServiceKeyPluginOptions { + url: string; + client?: string; + serviceKey: string | object; + /** Callback to open a URL in the user's browser. Falls back to dynamic import of 'open'. */ + openBrowser?: (url: string) => Promise; + /** Port for the local OAuth callback server (default: 3000) */ + callbackPort?: number; + /** Timeout for the auth flow in ms (default: 300000 = 5 min) */ + timeoutMs?: number; + [key: string]: unknown; +} + export class ServiceKeyParser { static parse(serviceKeyJson: string | object): BTPServiceKey { let parsed: unknown; @@ -66,17 +78,16 @@ export class ServiceKeyParser { parsed = serviceKeyJson; } - // Basic validation if (!parsed || typeof parsed !== 'object') { throw new Error('Invalid service key format'); } - const serviceKey = parsed as any; + const serviceKey = parsed as Record; - if (!serviceKey.uaa || !serviceKey.url || !serviceKey.systemid) { + if (!serviceKey['uaa'] || !serviceKey['url'] || !serviceKey['systemid']) { throw new Error('Missing required fields in service key'); } - return serviceKey as BTPServiceKey; + return serviceKey as unknown as BTPServiceKey; } } diff --git a/packages/adt-auth/src/utils/env.ts b/packages/adt-auth/src/utils/env.ts new file mode 100644 index 00000000..d42e0314 --- /dev/null +++ b/packages/adt-auth/src/utils/env.ts @@ -0,0 +1,67 @@ +import { readFileSync, existsSync } from 'fs'; +import type { Destination } from '../auth-manager'; +import { ServiceKeyParser, type BTPServiceKey } from '../types/service-key'; + +/** + * Read a BTP service key from a file path. + * + * @param filePath - Path to a JSON file containing the BTP service key + * @returns Parsed and validated `BTPServiceKey` + * @throws Error if the input looks like raw JSON (a file path is required) + * @throws Error if the file does not exist + */ +export function readServiceKey(filePath: string): BTPServiceKey { + const trimmed = filePath.trim(); + + if (trimmed.startsWith('{')) { + throw new Error( + 'Expected a path to a JSON file, but received a raw JSON string.\n' + + 'Write the service key JSON to a file and pass the file path instead.\n' + + 'Example: /path/to/service-key.json', + ); + } + + if (!existsSync(trimmed)) { + throw new Error(`Service key file not found: ${trimmed}`); + } + + const raw = readFileSync(trimmed, 'utf-8'); + return ServiceKeyParser.parse(raw); +} + +/** + * Resolves a service key destination from the `ADT_SERVICE_KEY` environment + * variable. + * + * The value can be a path to a JSON file or a raw JSON string (for backward + * compatibility with CI pipelines that store the key as a secret JSON value). + * + * @returns A `Destination` object for the service-key plugin, or `null` if + * `ADT_SERVICE_KEY` is not set. + */ +export function resolveServiceKeyFromEnv(): Destination | null { + const raw = process.env['ADT_SERVICE_KEY']; + + if (!raw) { + return null; + } + + const trimmed = raw.trim(); + + // Accept both a raw JSON string and a file path so that CI environments that + // store the key as a secret JSON value continue to work without changes. + let serviceKey: BTPServiceKey; + if (trimmed.startsWith('{')) { + serviceKey = ServiceKeyParser.parse(trimmed); + } else { + serviceKey = readServiceKey(trimmed); + } + + return { + type: '@abapify/adt-auth/plugins/service-key', + options: { + url: serviceKey.url, + serviceKey, + }, + }; +} diff --git a/packages/adt-cli/src/lib/oauth-utils.ts b/packages/adt-auth/src/utils/pkce.ts similarity index 100% rename from packages/adt-cli/src/lib/oauth-utils.ts rename to packages/adt-auth/src/utils/pkce.ts diff --git a/packages/adt-auth/tsdown.config.ts b/packages/adt-auth/tsdown.config.ts index 53f5512d..d0c3416f 100644 --- a/packages/adt-auth/tsdown.config.ts +++ b/packages/adt-auth/tsdown.config.ts @@ -7,6 +7,9 @@ export default defineConfig({ index: 'src/index.ts', 'plugins/basic': 'src/plugins/basic.ts', basic: 'src/plugins/basic.ts', + 'plugins/service-key': 'src/plugins/service-key.ts', + 'service-key': 'src/plugins/service-key.ts', + 'utils/env': 'src/utils/env.ts', }, tsconfig: 'tsconfig.lib.json', }); diff --git a/packages/adt-cli/src/lib/cli.ts b/packages/adt-cli/src/lib/cli.ts index 306ce5eb..8c22cea1 100644 --- a/packages/adt-cli/src/lib/cli.ts +++ b/packages/adt-cli/src/lib/cli.ts @@ -24,7 +24,10 @@ import { refreshCommand } from './commands/auth/refresh'; // Deploy command moved to @abapify/adt-export plugin // Add '@abapify/adt-export/commands/export' to adt.config.ts commands array to enable import { createCliLogger, AVAILABLE_COMPONENTS } from './utils/logger-config'; -import { setCliContext } from './utils/adt-client-v2'; +import { setCliContext, getCliContext } from './utils/adt-client-v2'; +import { getAuthManager, setDefaultSid } from './utils/auth'; +import { readServiceKey } from '@abapify/adt-auth'; +import { CALLBACK_SERVER_PORT, OAUTH_TIMEOUT_MS } from './config'; import { loadCommandPlugins, loadStaticPlugins } from './plugin-loader'; import type { CliCommandPlugin } from '@abapify/adt-plugin'; import { existsSync, readFileSync } from 'fs'; @@ -116,6 +119,10 @@ export async function createCLI(options?: { false, ) .option('--config ', 'Path to config file (default: adt.config.ts)') + .option( + '--service-key ', + 'Path to a BTP service key JSON file. Auto-authenticates before running the command.', + ) .hook('preAction', (thisCommand) => { const opts = thisCommand.optsWithGlobals(); @@ -225,7 +232,7 @@ export async function main(options?: { const program = await createCLI(options); // Add a hook to set up logger before command execution - program.hook('preAction', (thisCommand, actionCommand) => { + program.hook('preAction', async (thisCommand, actionCommand) => { // Set CLI mode for ADT client logger to enable pretty formatting process.env.ADT_CLI_MODE = 'true'; @@ -254,6 +261,49 @@ export async function main(options?: { verbose: globalOptions.verbose, configPath: globalOptions.config, }); + + // Handle --service-key: auto-authenticate before running the command + if (globalOptions.serviceKey) { + try { + const serviceKey = readServiceKey(globalOptions.serviceKey as string); + + if (!serviceKey.systemid && !globalOptions.sid) { + throw new Error( + 'Service key does not contain a systemid. Use --sid to specify the system ID.', + ); + } + + const sid = + globalOptions.sid?.toUpperCase() || serviceKey.systemid.toUpperCase(); + + const authManager = getAuthManager(); + const openModule = await import('open'); + const openFn = openModule.default; + await authManager.login(sid, { + type: '@abapify/adt-auth/plugins/service-key', + options: { + url: serviceKey.url, + serviceKey, + openBrowser: async (url: string) => { + console.log('🌐 Opening browser for authentication...'); + await openFn(url); + }, + callbackPort: CALLBACK_SERVER_PORT, + timeoutMs: OAUTH_TIMEOUT_MS, + }, + }); + + // Explicitly set the SID so downstream commands find the session + setDefaultSid(sid); + setCliContext({ ...getCliContext(), sid }); + } catch (error) { + console.error( + '❌ Service key authentication failed:', + error instanceof Error ? error.message : String(error), + ); + process.exit(1); + } + } }); await program.parseAsync(process.argv); diff --git a/packages/adt-cli/src/lib/commands/auth/login.ts b/packages/adt-cli/src/lib/commands/auth/login.ts index bc1f8940..46d115d1 100644 --- a/packages/adt-cli/src/lib/commands/auth/login.ts +++ b/packages/adt-cli/src/lib/commands/auth/login.ts @@ -53,10 +53,23 @@ export const loginCommand = new Command('login') if (dest) { console.log(`📋 Authenticating to ${sid}...\n`); + // For basic auth without credentials, prompt for username/password + const destOptions = dest.options as DestinationOptions; + if ( + dest.type === '@abapify/adt-auth/plugins/basic' && + !destOptions.username + ) { + const credentials = await promptForBasicAuthCredentials( + destOptions.url, + options, + ); + Object.assign(destOptions, credentials); + } + const authManager = getAuthManager(); const session = await authManager.login(sid, { type: dest.type, - options: dest.options as DestinationOptions, + options: destOptions, }); // Set as default @@ -219,6 +232,39 @@ async function collectPluginOptions( }; } +/** + * Prompt for basic auth credentials only (used when destination in config lacks credentials) + */ +async function promptForBasicAuthCredentials( + url: string, + commandOptions: any, +): Promise<{ username: string; password: string; client?: string }> { + const client = await input({ + message: 'Client (optional, e.g., 100)', + default: '', + }); + + const username = await input({ + message: 'Username', + validate: (value) => (value ? true : 'Username is required'), + }); + + const userPassword = await password({ + message: 'Password', + validate: (value) => (value ? true : 'Password is required'), + }); + + if (commandOptions.insecure) { + console.log('⚠️ SSL certificate verification disabled\n'); + } + + return { + client: client || undefined, + username, + password: userPassword, + }; +} + /** * Prompt for manual configuration (no adt.config.ts) */ diff --git a/packages/adt-cli/src/lib/commands/import/package.ts b/packages/adt-cli/src/lib/commands/import/package.ts index 5abbb7a5..2b681680 100644 --- a/packages/adt-cli/src/lib/commands/import/package.ts +++ b/packages/adt-cli/src/lib/commands/import/package.ts @@ -19,8 +19,8 @@ export const importPackageCommand = new Command('package') .option('--sub-packages', 'Include subpackages', false) .option( '--format ', - 'Output format: oat | abapgit | @abapify/oat | @abapify/abapgit', - 'oat', + 'Output format: abapgit | oat | @abapify/abapgit | @abapify/oat', + 'abapgit', ) .option('--debug', 'Enable debug output', false) .action(async (packageName, targetFolder, options) => { @@ -31,10 +31,7 @@ export const importPackageCommand = new Command('package') const importService = new ImportService(); // Determine output path: --output option, targetFolder argument, or default - const outputPath = - options.output || - targetFolder || - `./oat-${packageName.toLowerCase().replace('$', '')}`; + const outputPath = options.output || targetFolder || `./src`; // Show start message console.log(`🚀 Starting import of package: ${packageName}`); diff --git a/packages/adt-cli/src/lib/services/import/service.ts b/packages/adt-cli/src/lib/services/import/service.ts index 149ac64b..135f9062 100644 --- a/packages/adt-cli/src/lib/services/import/service.ts +++ b/packages/adt-cli/src/lib/services/import/service.ts @@ -1,7 +1,12 @@ import { loadFormatPlugin, parseFormatSpec } from '../../utils/format-loader'; import { getConfig } from '../../utils/destinations'; import type { ImportContext, FormatOptionValue } from '@abapify/adt-plugin'; -import { AdkPackage, AdkTransport } from '@abapify/adk'; +import { + AdkPackage, + AdkTransport, + getGlobalContext, + createAdkFactory, +} from '@abapify/adk'; /** * Options for importing a transport request @@ -298,6 +303,9 @@ export class ImportService { // Load format plugin const plugin = await loadFormatPlugin(options.format); + const configFormatOptions = await this.getConfigFormatOptions( + options.format, + ); if (options.debug) { console.log(`✅ Loaded plugin: ${plugin.name}`); @@ -333,23 +341,87 @@ export class ImportService { const results = { success: 0, skipped: 0, failed: 0 }; const objectsByType: Record = {}; + /** + * Resolve full package path from root to the given package. + * Uses ADK to load package → super package → etc until root. + */ + async function resolvePackagePath(packageName: string): Promise { + const path: string[] = []; + let currentPackage = packageName; + + while (currentPackage) { + path.unshift(currentPackage); + try { + const pkgData = await AdkPackage.get(currentPackage); + const superPkg = pkgData.superPackage; + if (superPkg?.name) { + currentPackage = superPkg.name; + } else { + break; + } + } catch { + break; + } + } + return path; + } + if (options.debug) { console.log(`📦 Processing ${objectsToImport.length} objects...`); } - // Process each object - AbapObject has type and name + // Process each object - AbapObject has type, name, uri for (const obj of objectsToImport) { try { - // Delegate to plugin - import object from SAP to local files - // Note: AbapObject from getObjects() may need to be loaded first - await plugin.instance.importObject(obj, options.outputPath); + // Check if plugin supports this object type + if (!plugin.instance.registry.isSupported(obj.type)) { + results.skipped++; + if (options.debug) { + console.log(` ⏭️ ${obj.type} ${obj.name}: type not supported`); + } + continue; + } - // Track statistics - objectsByType[obj.type] = (objectsByType[obj.type] || 0) + 1; - results.success++; + // Load the ADK object using the factory + const ctx = getGlobalContext(); + const factory = createAdkFactory(ctx); + const adkObject = factory.get(obj.name, obj.type); + await (adkObject as any).load(); - if (options.debug) { - console.log(` ✅ ${obj.type} ${obj.name}`); + if (!adkObject) { + results.skipped++; + if (options.debug) { + console.log(` ⏭️ ${obj.type} ${obj.name}: failed to load`); + } + continue; + } + + // Build import context + const context: ImportContext = { + resolvePackagePath, + configFormatOptions, + }; + + // Delegate to plugin - import object from SAP to local files + const result = await plugin.instance.format.import( + adkObject as any, + options.outputPath, + context, + ); + + if (result.success) { + objectsByType[obj.type] = (objectsByType[obj.type] || 0) + 1; + results.success++; + if (options.debug) { + console.log(` ✅ ${obj.type} ${obj.name}`); + } + } else { + results.failed++; + if (options.debug) { + console.log( + ` ❌ ${obj.type} ${obj.name}: ${result.errors?.join(', ') || 'unknown error'}`, + ); + } } } catch (error) { results.failed++; @@ -361,6 +433,11 @@ export class ImportService { } } + // Call afterImport hook if available + if (plugin.instance.hooks?.afterImport) { + await plugin.instance.hooks.afterImport(options.outputPath); + } + return { packageName: options.packageName, description: pkg.description || `Package ${options.packageName}`, diff --git a/packages/adt-cli/src/lib/utils/adt-client-v2.ts b/packages/adt-cli/src/lib/utils/adt-client-v2.ts index fc00c191..6d7f2905 100644 --- a/packages/adt-cli/src/lib/utils/adt-client-v2.ts +++ b/packages/adt-cli/src/lib/utils/adt-client-v2.ts @@ -206,6 +206,7 @@ export async function getAdtClientV2( let username: string | undefined; let password: string | undefined; let cookieHeader: string | undefined; + let authorizationHeader: string | undefined; if (session.auth.method === 'basic') { const creds = session.auth.credentials as BasicCredentials; @@ -219,7 +220,16 @@ export async function getAdtClientV2( const creds = session.auth.credentials as CookieCredentials; // Decode URL-encoded cookie values (e.g., %3d -> =) - cookieHeader = decodeURIComponent(creds.cookies); + const rawCookies = decodeURIComponent(creds.cookies); + + const AUTH_PREFIX = 'Authorization: '; + if (rawCookies.startsWith(AUTH_PREFIX)) { + // Bearer token from OAuth (e.g., BTP service key auth) — pass as + // Authorization header, not as a Cookie + authorizationHeader = rawCookies.substring(AUTH_PREFIX.length); + } else { + cookieHeader = rawCookies; + } } else { console.error(`❌ Unsupported auth method: ${session.auth.method}`); process.exit(1); @@ -340,6 +350,7 @@ export async function getAdtClientV2( username, password, cookieHeader, + authorizationHeader, client, logger, plugins, diff --git a/packages/adt-cli/tsdown.config.ts b/packages/adt-cli/tsdown.config.ts index 210d4e53..6fcb8f66 100644 --- a/packages/adt-cli/tsdown.config.ts +++ b/packages/adt-cli/tsdown.config.ts @@ -6,6 +6,4 @@ export default defineConfig({ ...baseConfig, entry: ['./src/index.ts', './src/bin/adt.ts', './src/bin/adt-all.ts'], tsconfig: 'tsconfig.lib.json', - // Force bundle these packages instead of marking as external - deps: { alwaysBundle: ['@abapify/adt-plugin-abapgit'] }, }); diff --git a/packages/adt-client/src/adapter.ts b/packages/adt-client/src/adapter.ts index 8dc2a98e..4f1ccaa6 100644 --- a/packages/adt-client/src/adapter.ts +++ b/packages/adt-client/src/adapter.ts @@ -36,6 +36,7 @@ export function createAdtAdapter(config: AdtAdapterConfig): HttpAdapter { username, password, cookieHeader, + authorizationHeader, client, language, logger, @@ -46,10 +47,14 @@ export function createAdtAdapter(config: AdtAdapterConfig): HttpAdapter { // Determine auth method const isSamlAuth = !!cookieHeader; - // Create Basic Auth header (if not using SAML) - const authHeader = isSamlAuth - ? undefined - : `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; + // Build Authorization header: + // 1. Explicit authorizationHeader (e.g., "Bearer " for OAuth) + // 2. Basic Auth header (username/password) when not using SAML + const authHeader = + authorizationHeader ?? + (isSamlAuth + ? undefined + : `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`); // Create session manager for stateful sessions const sessionManager = new SessionManager(logger); diff --git a/packages/adt-client/src/types.ts b/packages/adt-client/src/types.ts index 3d01071f..20ca1d00 100644 --- a/packages/adt-client/src/types.ts +++ b/packages/adt-client/src/types.ts @@ -13,6 +13,7 @@ export interface AdtConnectionConfig { username?: string; password?: string; cookieHeader?: string; // For SAML authentication + authorizationHeader?: string; // For Bearer token auth (e.g., OAuth / BTP service key) client?: string; language?: string; logger?: Logger; diff --git a/packages/adt-config/src/config-loader.ts b/packages/adt-config/src/config-loader.ts index 09dfbf35..80773fda 100644 --- a/packages/adt-config/src/config-loader.ts +++ b/packages/adt-config/src/config-loader.ts @@ -108,8 +108,12 @@ function createLoadedConfig(config: AdtConfig): LoadedConfig { const dest = config.destinations?.[name]; if (!dest) return undefined; // Handle string shorthand (URL) by converting to Destination object + // Uses built-in basic auth plugin as the default if (typeof dest === 'string') { - return { type: 'url', options: { url: dest } }; + return { + type: '@abapify/adt-auth/plugins/basic', + options: { url: dest }, + }; } return dest; }, diff --git a/packages/adt-contracts/src/adt/aunit/index.ts b/packages/adt-contracts/src/adt/aunit/index.ts new file mode 100644 index 00000000..3c1ab479 --- /dev/null +++ b/packages/adt-contracts/src/adt/aunit/index.ts @@ -0,0 +1,46 @@ +/** + * ADT AUnit (ABAP Unit) Contracts + * + * Manually-defined endpoints for ABAP Unit test runs. + * + * Structure mirrors URL tree: + * - /sap/bc/adt/abapunit/testruns → aunit.testruns (POST with body) + */ + +import { http, contract } from '../../base'; +import { aunitRun, aunitResult } from '../../schemas'; + +/** + * /sap/bc/adt/abapunit/testruns + * + * Run ABAP Unit tests on objects specified in the request body. + * + * NOTE: Not in SAP discovery - manually defined + */ +const testruns = contract({ + /** + * POST /sap/bc/adt/abapunit/testruns + * Execute ABAP Unit test run with a runConfiguration body + * + * @returns runResult with programs, test classes, methods, and alerts + */ + post: () => + http.post('/sap/bc/adt/abapunit/testruns', { + body: aunitRun, + responses: { 200: aunitResult }, + headers: { + Accept: 'application/vnd.sap.adt.abapunit.testruns.result.v2+xml', + 'Content-Type': + 'application/vnd.sap.adt.abapunit.testruns.config.v4+xml', + }, + }), +}); + +/** + * Combined AUnit contract + */ +export const aunitContract = { + testruns, +}; + +export type AunitContract = typeof aunitContract; diff --git a/packages/adt-contracts/src/adt/core/http/systeminformation.ts b/packages/adt-contracts/src/adt/core/http/systeminformation.ts index 68163886..e4f5dbc4 100644 --- a/packages/adt-contracts/src/adt/core/http/systeminformation.ts +++ b/packages/adt-contracts/src/adt/core/http/systeminformation.ts @@ -22,7 +22,7 @@ export const systeminformationContract: { 200: systeminformationSchema, }, headers: { - Accept: 'application/json', + Accept: 'application/vnd.sap.adt.core.http.systeminformation.v1+json', }, }), }; diff --git a/packages/adt-contracts/src/adt/index.ts b/packages/adt-contracts/src/adt/index.ts index 8c297309..67f2c0a1 100644 --- a/packages/adt-contracts/src/adt/index.ts +++ b/packages/adt-contracts/src/adt/index.ts @@ -4,6 +4,7 @@ export * from './cts'; export * from './atc'; +export * from './aunit'; export * from './oo'; export * from './discovery'; export * from './packages'; @@ -15,6 +16,7 @@ export * from './repository'; */ import { ctsContract, type CtsContract } from './cts'; import { atcContract, type AtcContract } from './atc'; +import { aunitContract, type AunitContract } from './aunit'; import { ooContract, type OoContract } from './oo'; import { discoveryContract, type DiscoveryContract } from './discovery'; import { packagesContract, type PackagesContract } from './packages'; @@ -27,6 +29,7 @@ import { repositoryContract, type RepositoryContract } from './repository'; export interface AdtContract { cts: CtsContract; atc: AtcContract; + aunit: AunitContract; oo: OoContract; discovery: DiscoveryContract; packages: PackagesContract; @@ -37,6 +40,7 @@ export interface AdtContract { export const adtContract: AdtContract = { cts: ctsContract, atc: atcContract, + aunit: aunitContract, oo: ooContract, discovery: discoveryContract, packages: packagesContract, diff --git a/packages/adt-contracts/src/adt/repository/informationsystem/search.ts b/packages/adt-contracts/src/adt/repository/informationsystem/search.ts index 0060fdef..ef3cccf7 100644 --- a/packages/adt-contracts/src/adt/repository/informationsystem/search.ts +++ b/packages/adt-contracts/src/adt/repository/informationsystem/search.ts @@ -14,6 +14,8 @@ export const searchContract = { * * @param query - Search query (supports wildcards like *) * @param maxResults - Maximum number of results (default: 50) + * @param packageName - Filter by package name + * @param objectType - Filter by object type (e.g., 'CLAS', 'INTF') * @returns Object references matching the search query (union of all adtcore element types) * * @example @@ -22,17 +24,35 @@ export const searchContract = { * query: 'zcl*', * maxResults: 10 * }); + * + * @example + * // List all objects in a package + * const results = await client.repository.informationsystem.search.quickSearch({ + * query: '*', + * packageName: 'ZABAPGIT_EXAMPLES', + * maxResults: 200 + * }); */ - quickSearch: (options: { query: string; maxResults?: number }) => + quickSearch: (options: { + query: string; + maxResults?: number; + packageName?: string; + objectType?: string; + }) => http.get('/sap/bc/adt/repository/informationsystem/search', { query: { operation: 'quickSearch', query: options.query, maxResults: options.maxResults || 50, + ...(options.packageName && { packageName: options.packageName }), + ...(options.objectType && { objectType: options.objectType }), }, responses: { 200: adtcore, }, + headers: { + Accept: 'application/xml', + }, }), }; diff --git a/packages/adt-contracts/src/generated/schemas.ts b/packages/adt-contracts/src/generated/schemas.ts index 09f15c5f..fb4819d2 100644 --- a/packages/adt-contracts/src/generated/schemas.ts +++ b/packages/adt-contracts/src/generated/schemas.ts @@ -51,6 +51,8 @@ export const transportmanagmentSingle = toSpeciSchema( adtSchemas.transportmanagmentSingle, ); export const transportsearch = toSpeciSchema(adtSchemas.transportsearch); +export const aunitRun = toSpeciSchema(adtSchemas.aunitRun); +export const aunitResult = toSpeciSchema(adtSchemas.aunitResult); // ============================================================================ // JSON Schemas (re-exported directly - they use zod, not ts-xsd) diff --git a/packages/adt-contracts/tests/contracts/aunit.test.ts b/packages/adt-contracts/tests/contracts/aunit.test.ts new file mode 100644 index 00000000..4a400b88 --- /dev/null +++ b/packages/adt-contracts/tests/contracts/aunit.test.ts @@ -0,0 +1,53 @@ +/** + * AUnit (ABAP Unit) Contract Tests + * + * Tests manually-defined contracts from src/adt/aunit/ + * + * Two types of tests: + * 1. Contract Definition Tests - validate method, path, headers, body, responses + * 2. Fixture parsing tests - validate schemas parse real SAP XML + */ + +import { fixtures } from '@abapify/adt-fixtures'; +import { aunitRun, aunitResult } from '../../src/schemas'; +import { ContractScenario, runScenario, type ContractOperation } from './base'; + +// Import contracts +import { aunitContract } from '../../src/adt/aunit'; + +// ============================================================================= +// Contract Definition Tests +// ============================================================================= + +class AunitTestrunsScenario extends ContractScenario { + readonly name = 'AUnit Testruns'; + + readonly operations: ContractOperation[] = [ + { + name: 'run ABAP Unit tests', + contract: () => aunitContract.testruns.post(), + method: 'POST', + path: '/sap/bc/adt/abapunit/testruns', + headers: { + Accept: 'application/vnd.sap.adt.abapunit.testruns.result.v2+xml', + 'Content-Type': + 'application/vnd.sap.adt.abapunit.testruns.config.v4+xml', + }, + body: { + schema: aunitRun, + fixture: fixtures.aunit.runRequest, + }, + response: { + status: 200, + schema: aunitResult, + fixture: fixtures.aunit.runResult, + }, + }, + ]; +} + +// ============================================================================= +// Run contract definition scenarios +// ============================================================================= + +runScenario(new AunitTestrunsScenario()); diff --git a/packages/adt-contracts/tests/contracts/core.test.ts b/packages/adt-contracts/tests/contracts/core.test.ts index 820bf837..f7f53393 100644 --- a/packages/adt-contracts/tests/contracts/core.test.ts +++ b/packages/adt-contracts/tests/contracts/core.test.ts @@ -44,7 +44,7 @@ class SystemInformationScenario extends ContractScenario { method: 'GET', path: '/sap/bc/adt/core/http/systeminformation', headers: { - Accept: 'application/json', + Accept: 'application/vnd.sap.adt.core.http.systeminformation.v1+json', }, response: { status: 200, diff --git a/packages/adt-fixtures/src/fixtures/aunit/run-request.xml b/packages/adt-fixtures/src/fixtures/aunit/run-request.xml new file mode 100644 index 00000000..e83dde90 --- /dev/null +++ b/packages/adt-fixtures/src/fixtures/aunit/run-request.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/adt-fixtures/src/fixtures/aunit/run-result.xml b/packages/adt-fixtures/src/fixtures/aunit/run-result.xml new file mode 100644 index 00000000..d3beb772 --- /dev/null +++ b/packages/adt-fixtures/src/fixtures/aunit/run-result.xml @@ -0,0 +1 @@ +Use of ABAP List features
Critical Assertion Error: 'This test is designed to always fail'
\ No newline at end of file diff --git a/packages/adt-fixtures/src/fixtures/registry.ts b/packages/adt-fixtures/src/fixtures/registry.ts index 302aa443..cb1a9d2f 100644 --- a/packages/adt-fixtures/src/fixtures/registry.ts +++ b/packages/adt-fixtures/src/fixtures/registry.ts @@ -16,6 +16,10 @@ export const registry = { worklist: 'atc/worklist.xml', runsResponse: 'atc/runs-response.xml', }, + aunit: { + runRequest: 'aunit/run-request.xml', + runResult: 'aunit/run-result.xml', + }, packages: { tmp: 'packages/tmp.xml', }, diff --git a/packages/adt-playwright/src/index.ts b/packages/adt-playwright/src/index.ts index 0adbfb61..8d2365b7 100644 --- a/packages/adt-playwright/src/index.ts +++ b/packages/adt-playwright/src/index.ts @@ -66,9 +66,10 @@ export function withPlaywright( ...options, }); } else { + // Destination-specific options override global options destinations[name] = createPlaywrightDestination({ - ...(dest as BrowserAuthOptions), ...options, + ...(dest as BrowserAuthOptions), }); } } diff --git a/packages/adt-schemas/.xsd/custom/adtcoreObjectSets.xsd b/packages/adt-schemas/.xsd/custom/adtcoreObjectSets.xsd new file mode 100644 index 00000000..e8761773 --- /dev/null +++ b/packages/adt-schemas/.xsd/custom/adtcoreObjectSets.xsd @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/adt-schemas/.xsd/custom/aunitResult.xsd b/packages/adt-schemas/.xsd/custom/aunitResult.xsd new file mode 100644 index 00000000..ca70fb58 --- /dev/null +++ b/packages/adt-schemas/.xsd/custom/aunitResult.xsd @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/adt-schemas/.xsd/custom/aunitRun.xsd b/packages/adt-schemas/.xsd/custom/aunitRun.xsd new file mode 100644 index 00000000..927a7434 --- /dev/null +++ b/packages/adt-schemas/.xsd/custom/aunitRun.xsd @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/adt-schemas/AGENTS.md b/packages/adt-schemas/AGENTS.md index 59625344..298b6b7e 100644 --- a/packages/adt-schemas/AGENTS.md +++ b/packages/adt-schemas/AGENTS.md @@ -13,7 +13,36 @@ ## 🚨 Critical Rules -### 1. NEVER Edit Generated Files +### 1. NEVER Edit Downloaded XSD Files + +`.xsd/sap/` files are **downloaded from SAP** via `npx nx run adt-schemas:download`. They will be overwritten. Never edit them. + +| Directory | Ownership | Editable? | +| -------------- | ---------------- | --------- | +| `.xsd/sap/` | SAP (downloaded) | ❌ NEVER | +| `.xsd/custom/` | Hand-maintained | ✅ Yes | + +**If SAP's XSD is missing types you need:** + +1. Create a custom extension XSD in `.xsd/custom/` with the **same `targetNamespace`** as the SAP schema +2. Use `xs:include` to bring in the SAP schema +3. Add new types/elements in the extension +4. Have consumers import the extension instead of the SAP schema directly + +Example: `adtcoreObjectSets.xsd` extends `adtcore.xsd` with `objectSets` types. + +### 2. XSD Files Must Be Valid + +Every XSD in `.xsd/` must be a **valid W3C XML Schema**. Never create broken XSDs and patch tooling to handle them. + +Key rules: + +- `xs:import` = different namespace. Only ONE per namespace. +- `xs:include` = same namespace (composition/extension). +- Every `ref="prefix:name"` must resolve to an in-scope element declaration. +- Every `type="prefix:TypeName"` must resolve to an in-scope type definition. + +### 3. NEVER Edit Generated TypeScript Files Generated files are in `src/schemas/generated/`: diff --git a/packages/adt-schemas/project.json b/packages/adt-schemas/project.json index 90c01d22..42ae16ca 100644 --- a/packages/adt-schemas/project.json +++ b/packages/adt-schemas/project.json @@ -12,7 +12,7 @@ "cache": false }, "codegen": { - "command": "npx ts-xsd codegen --verbose", + "command": "npx ts-xsd codegen --verbose && cd ../.. && npx prettier --write \"packages/adt-schemas/src/schemas/generated/**/*.{ts,js,json}\"", "options": { "cwd": "{projectRoot}" }, diff --git a/packages/adt-schemas/src/schemas/generated/schemas/custom/adtcoreObjectSets.ts b/packages/adt-schemas/src/schemas/generated/schemas/custom/adtcoreObjectSets.ts new file mode 100644 index 00000000..5f79f805 --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/schemas/custom/adtcoreObjectSets.ts @@ -0,0 +1,58 @@ +/** + * Auto-generated schema from XSD + * + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/adtcoreObjectSets.xsd + */ + +import adtcore from '../sap/adtcore'; + +export default { + $xmlns: { + xsd: 'http://www.w3.org/2001/XMLSchema', + adtcore: 'http://www.sap.com/adt/core', + }, + $includes: [adtcore], + targetNamespace: 'http://www.sap.com/adt/core', + element: [ + { + name: 'objectSets', + type: 'adtcore:AdtObjectSets', + }, + ], + complexType: [ + { + name: 'AdtObjectSets', + sequence: { + element: [ + { + name: 'objectSet', + type: 'adtcore:AdtObjectSet', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AdtObjectSet', + sequence: { + element: [ + { + ref: 'adtcore:objectReferences', + minOccurs: '0', + maxOccurs: '1', + }, + ], + }, + attribute: [ + { + name: 'kind', + type: 'xsd:string', + form: 'unqualified', + }, + ], + }, + ], +} as const; diff --git a/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitResult.ts b/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitResult.ts new file mode 100644 index 00000000..8d31bb93 --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitResult.ts @@ -0,0 +1,294 @@ +/** + * Auto-generated schema from XSD + * + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/aunitResult.xsd + */ + +export default { + $xmlns: { + xsd: 'http://www.w3.org/2001/XMLSchema', + aunit: 'http://www.sap.com/adt/aunit', + }, + targetNamespace: 'http://www.sap.com/adt/aunit', + attributeFormDefault: 'unqualified', + elementFormDefault: 'qualified', + element: [ + { + name: 'runResult', + type: 'aunit:AunitRunResult', + }, + ], + complexType: [ + { + name: 'AunitRunResult', + sequence: { + element: [ + { + name: 'program', + type: 'aunit:AunitProgram', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitProgram', + sequence: { + element: [ + { + name: 'testClasses', + type: 'aunit:AunitTestClasses', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'alerts', + type: 'aunit:AunitAlerts', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + attribute: [ + { + name: 'uri', + type: 'xsd:anyURI', + }, + { + name: 'type', + type: 'xsd:string', + }, + { + name: 'name', + type: 'xsd:string', + }, + { + name: 'uriType', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitTestClasses', + sequence: { + element: [ + { + name: 'testClass', + type: 'aunit:AunitTestClass', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitTestClass', + sequence: { + element: [ + { + name: 'testMethods', + type: 'aunit:AunitTestMethods', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'alerts', + type: 'aunit:AunitAlerts', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + attribute: [ + { + name: 'uri', + type: 'xsd:anyURI', + }, + { + name: 'name', + type: 'xsd:string', + }, + { + name: 'uriType', + type: 'xsd:string', + }, + { + name: 'durationCategory', + type: 'xsd:string', + }, + { + name: 'riskLevel', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitTestMethods', + sequence: { + element: [ + { + name: 'testMethod', + type: 'aunit:AunitTestMethod', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitTestMethod', + sequence: { + element: [ + { + name: 'alerts', + type: 'aunit:AunitAlerts', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + attribute: [ + { + name: 'uri', + type: 'xsd:anyURI', + }, + { + name: 'name', + type: 'xsd:string', + }, + { + name: 'executionTime', + type: 'xsd:string', + }, + { + name: 'uriType', + type: 'xsd:string', + }, + { + name: 'unit', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitAlerts', + sequence: { + element: [ + { + name: 'alert', + type: 'aunit:AunitAlert', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitAlert', + sequence: { + element: [ + { + name: 'title', + type: 'xsd:string', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'details', + type: 'aunit:AunitAlertDetails', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'stack', + type: 'aunit:AunitAlertStack', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + attribute: [ + { + name: 'kind', + type: 'xsd:string', + }, + { + name: 'severity', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitAlertDetails', + sequence: { + element: [ + { + name: 'detail', + type: 'aunit:AunitAlertDetail', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitAlertDetail', + attribute: [ + { + name: 'text', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitAlertStack', + sequence: { + element: [ + { + name: 'stackEntry', + type: 'aunit:AunitStackEntry', + minOccurs: '0', + maxOccurs: 'unbounded', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitStackEntry', + attribute: [ + { + name: 'uri', + type: 'xsd:anyURI', + }, + { + name: 'type', + type: 'xsd:string', + }, + { + name: 'name', + type: 'xsd:string', + }, + { + name: 'description', + type: 'xsd:string', + }, + ], + }, + ], +} as const; diff --git a/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitRun.ts b/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitRun.ts new file mode 100644 index 00000000..487fd76e --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/schemas/custom/aunitRun.ts @@ -0,0 +1,188 @@ +/** + * Auto-generated schema from XSD + * + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/aunitRun.xsd + */ + +import adtcoreObjectSets from './adtcoreObjectSets'; + +export default { + $xmlns: { + xsd: 'http://www.w3.org/2001/XMLSchema', + aunit: 'http://www.sap.com/adt/aunit', + adtcore: 'http://www.sap.com/adt/core', + }, + $imports: [adtcoreObjectSets], + targetNamespace: 'http://www.sap.com/adt/aunit', + attributeFormDefault: 'unqualified', + elementFormDefault: 'qualified', + element: [ + { + name: 'runConfiguration', + type: 'aunit:AunitRunConfiguration', + }, + ], + complexType: [ + { + name: 'AunitRunConfiguration', + sequence: { + element: [ + { + name: 'external', + type: 'aunit:AunitExternal', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'options', + type: 'aunit:AunitOptions', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + ref: 'adtcore:objectSets', + minOccurs: '1', + maxOccurs: '1', + }, + ], + }, + }, + { + name: 'AunitExternal', + sequence: { + element: [ + { + name: 'coverage', + type: 'aunit:AunitCoverage', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitCoverage', + attribute: [ + { + name: 'active', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitOptions', + sequence: { + element: [ + { + name: 'uriType', + type: 'aunit:AunitUriType', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'testDeterminationStrategy', + type: 'aunit:AunitTestDeterminationStrategy', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'testRiskLevels', + type: 'aunit:AunitTestRiskLevels', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'testDurations', + type: 'aunit:AunitTestDurations', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + { + name: 'withNavigationUri', + type: 'aunit:AunitWithNavigationUri', + minOccurs: '0', + maxOccurs: '1', + form: 'unqualified', + }, + ], + }, + }, + { + name: 'AunitUriType', + attribute: [ + { + name: 'value', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitTestDeterminationStrategy', + attribute: [ + { + name: 'sameProgram', + type: 'xsd:string', + }, + { + name: 'assignedTests', + type: 'xsd:string', + }, + { + name: 'appendAssignedTestsPreview', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitTestRiskLevels', + attribute: [ + { + name: 'harmless', + type: 'xsd:string', + }, + { + name: 'dangerous', + type: 'xsd:string', + }, + { + name: 'critical', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitTestDurations', + attribute: [ + { + name: 'short', + type: 'xsd:string', + }, + { + name: 'medium', + type: 'xsd:string', + }, + { + name: 'long', + type: 'xsd:string', + }, + ], + }, + { + name: 'AunitWithNavigationUri', + attribute: [ + { + name: 'enabled', + type: 'xsd:string', + }, + ], + }, + ], +} as const; diff --git a/packages/adt-schemas/src/schemas/generated/schemas/custom/index.ts b/packages/adt-schemas/src/schemas/generated/schemas/custom/index.ts index 9e8f669f..a1069f19 100644 --- a/packages/adt-schemas/src/schemas/generated/schemas/custom/index.ts +++ b/packages/adt-schemas/src/schemas/generated/schemas/custom/index.ts @@ -11,3 +11,6 @@ export { default as transportfind } from './transportfind'; export { default as transportmanagmentCreate } from './transportmanagmentCreate'; export { default as transportmanagmentSingle } from './transportmanagmentSingle'; export { default as atcRun } from './atcRun'; +export { default as aunitRun } from './aunitRun'; +export { default as aunitResult } from './aunitResult'; +export { default as adtcoreObjectSets } from './adtcoreObjectSets'; diff --git a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcexemption.ts b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcexemption.ts index b74de583..9a27662e 100644 --- a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcexemption.ts +++ b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcexemption.ts @@ -6,6 +6,7 @@ */ import atcfinding from './atcfinding'; +import atc from './atc'; export default { $xmlns: { @@ -13,8 +14,9 @@ export default { ecore: 'http://www.eclipse.org/emf/2002/Ecore', atcexmpt: 'http://www.sap.com/adt/atc/exemption', atcfinding: 'http://www.sap.com/adt/atc/finding', + atc: 'http://www.sap.com/adt/atc', }, - $imports: [atcfinding], + $imports: [atcfinding, atc], targetNamespace: 'http://www.sap.com/adt/atc/exemption', attributeFormDefault: 'unqualified', elementFormDefault: 'qualified', @@ -244,6 +246,18 @@ export default { minOccurs: '0', maxOccurs: '1', }, + { + name: 'reasons', + type: 'atc:AtcReasons', + minOccurs: '0', + maxOccurs: '1', + }, + { + name: 'validities', + type: 'atc:AtcValidities', + minOccurs: '0', + maxOccurs: '1', + }, ], }, }, diff --git a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcresult.ts b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcresult.ts index 31ee32e7..e80a9d77 100644 --- a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcresult.ts +++ b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcresult.ts @@ -10,6 +10,7 @@ import atcfinding from './atcfinding'; import atcobject from './atcobject'; import atctagdescription from './atctagdescription'; import atcinfo from './atcinfo'; +import atc from './atc'; export default { $xmlns: { @@ -21,8 +22,16 @@ export default { atcinfo: 'http://www.sap.com/adt/atc/info', atcresultquery: 'http://www.sap.com/adt/atc/resultquery', atcresult: 'http://www.sap.com/adt/atc/result', + atc: 'http://www.sap.com/adt/atc', }, - $imports: [atcresultquery, atcfinding, atcobject, atctagdescription, atcinfo], + $imports: [ + atcresultquery, + atcfinding, + atcobject, + atctagdescription, + atcinfo, + atc, + ], targetNamespace: 'http://www.sap.com/adt/atc/result', attributeFormDefault: 'qualified', elementFormDefault: 'qualified', @@ -62,6 +71,12 @@ export default { { ref: 'atctd:descriptionTags', }, + { + name: 'scaAttributes', + type: 'atc:ScaAttributes', + minOccurs: '0', + maxOccurs: '1', + }, { name: 'infos', type: 'atcinfo:AtcInfoList', diff --git a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcworklist.ts b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcworklist.ts index 3c29efcf..17b948e9 100644 --- a/packages/adt-schemas/src/schemas/generated/schemas/sap/atcworklist.ts +++ b/packages/adt-schemas/src/schemas/generated/schemas/sap/atcworklist.ts @@ -8,6 +8,7 @@ import atcinfo from './atcinfo'; import atcobject from './atcobject'; import atctagdescription from './atctagdescription'; +import atc from './atc'; export default { $xmlns: { @@ -17,8 +18,9 @@ export default { atcobject: 'http://www.sap.com/adt/atc/object', atctd: 'http://www.sap.com/adt/atc/tagdescription', atcworklist: 'http://www.sap.com/adt/atc/worklist', + atc: 'http://www.sap.com/adt/atc', }, - $imports: [atcinfo, atcobject, atctagdescription], + $imports: [atcinfo, atcobject, atctagdescription, atc], targetNamespace: 'http://www.sap.com/adt/atc/worklist', attributeFormDefault: 'qualified', elementFormDefault: 'qualified', @@ -35,6 +37,12 @@ export default { name: 'objects', type: 'atcobject:AtcObjectList', }, + { + name: 'scaAttributes', + type: 'atc:ScaAttributes', + minOccurs: '0', + maxOccurs: '1', + }, { ref: 'atctd:descriptionTags', }, diff --git a/packages/adt-schemas/src/schemas/generated/typed.ts b/packages/adt-schemas/src/schemas/generated/typed.ts index af34a39e..4e81aeeb 100644 --- a/packages/adt-schemas/src/schemas/generated/typed.ts +++ b/packages/adt-schemas/src/schemas/generated/typed.ts @@ -49,6 +49,8 @@ import type { TransportfindSchema } from './types/custom/transportfind.types'; import type { TransportmanagmentCreateSchema } from './types/custom/transportmanagmentCreate.types'; import type { TransportmanagmentSingleSchema } from './types/custom/transportmanagmentSingle.types'; import type { AtcRunSchema } from './types/custom/atcRun.types'; +import type { AunitRunSchema } from './types/custom/aunitRun.types'; +import type { AunitResultSchema } from './types/custom/aunitResult.types'; // SAP schemas import _atom from './schemas/sap/atom'; @@ -154,3 +156,9 @@ export const transportmanagmentSingle: TypedSchema = typedSchema(_atcRun); +import _aunitRun from './schemas/custom/aunitRun'; +export const aunitRun: TypedSchema = + typedSchema(_aunitRun); +import _aunitResult from './schemas/custom/aunitResult'; +export const aunitResult: TypedSchema = + typedSchema(_aunitResult); diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/adtcoreObjectSets.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/adtcoreObjectSets.types.ts new file mode 100644 index 00000000..d91cbb7e --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/types/custom/adtcoreObjectSets.types.ts @@ -0,0 +1,109 @@ +/** + * Auto-generated TypeScript interfaces from XSD + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/adtcoreObjectSets.xsd + * Mode: Flattened + */ + +export type AdtcoreObjectSetsSchema = + | { + objectSets: { + objectSet?: { + objectReferences?: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }[]; + name?: string; + }; + kind?: string; + }[]; + }; + } + | { + mainObject: { + containerRef?: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + adtTemplate?: { + adtProperty?: { + $value?: string; + key?: string; + }[]; + name?: string; + }; + packageRef?: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + name: string; + type: string; + changedBy?: string; + changedAt?: string; + createdAt?: string; + createdBy?: string; + version?: + | '' + | 'active' + | 'inactive' + | 'workingArea' + | 'new' + | 'partlyActive' + | 'activeWithInactiveVersion'; + description?: string; + descriptionTextLimit?: number; + language?: string; + masterSystem?: string; + masterLanguage?: string; + responsible?: string; + abapLanguageVersion?: string; + }; + } + | { + objectReferences: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }[]; + name?: string; + }; + } + | { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + } + | { + content: { + $value?: string; + type?: string; + encoding?: string; + }; + }; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/atcRun.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/atcRun.types.ts index 1a919c9f..3bebfd9a 100644 --- a/packages/adt-schemas/src/schemas/generated/types/custom/atcRun.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/custom/atcRun.types.ts @@ -9,6 +9,18 @@ export type AtcRunSchema = { run: { objectSets: { objectSet: { + objectReferences?: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }[]; + name?: string; + }; kind?: string; }[]; }; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/aunitResult.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/aunitResult.types.ts new file mode 100644 index 00000000..b2c7a231 --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/types/custom/aunitResult.types.ts @@ -0,0 +1,95 @@ +/** + * Auto-generated TypeScript interfaces from XSD + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/aunitResult.xsd + * Mode: Flattened + */ + +export type AunitResultSchema = { + runResult: { + program?: { + testClasses?: { + testClass?: { + testMethods?: { + testMethod?: { + alerts?: { + alert?: { + title?: string; + details?: { + detail?: { + text?: string; + }[]; + }; + stack?: { + stackEntry?: { + uri?: string; + type?: string; + name?: string; + description?: string; + }[]; + }; + kind?: string; + severity?: string; + }[]; + }; + uri?: string; + name?: string; + executionTime?: string; + uriType?: string; + unit?: string; + }[]; + }; + alerts?: { + alert?: { + title?: string; + details?: { + detail?: { + text?: string; + }[]; + }; + stack?: { + stackEntry?: { + uri?: string; + type?: string; + name?: string; + description?: string; + }[]; + }; + kind?: string; + severity?: string; + }[]; + }; + uri?: string; + name?: string; + uriType?: string; + durationCategory?: string; + riskLevel?: string; + }[]; + }; + alerts?: { + alert?: { + title?: string; + details?: { + detail?: { + text?: string; + }[]; + }; + stack?: { + stackEntry?: { + uri?: string; + type?: string; + name?: string; + description?: string; + }[]; + }; + kind?: string; + severity?: string; + }[]; + }; + uri?: string; + type?: string; + name?: string; + uriType?: string; + }[]; + }; +}; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/aunitRun.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/aunitRun.types.ts new file mode 100644 index 00000000..ee466507 --- /dev/null +++ b/packages/adt-schemas/src/schemas/generated/types/custom/aunitRun.types.ts @@ -0,0 +1,44 @@ +/** + * Auto-generated TypeScript interfaces from XSD + * DO NOT EDIT - Generated by ts-xsd codegen + * Source: xsd/custom/aunitRun.xsd + * Mode: Flattened + */ + +export type AunitRunSchema = { + runConfiguration: { + external?: { + coverage?: { + active?: string; + }; + }; + options?: { + uriType?: { + value?: string; + }; + testDeterminationStrategy?: { + sameProgram?: string; + assignedTests?: string; + appendAssignedTestsPreview?: string; + }; + testRiskLevels?: { + harmless?: string; + dangerous?: string; + critical?: string; + }; + testDurations?: { + short?: string; + medium?: string; + long?: string; + }; + withNavigationUri?: { + enabled?: string; + }; + }; + objectSets: { + objectSet?: { + kind?: string; + }[]; + }; + }; +}; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/discovery.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/discovery.types.ts index e23250ae..5db80b10 100644 --- a/packages/adt-schemas/src/schemas/generated/types/custom/discovery.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/custom/discovery.types.ts @@ -8,8 +8,16 @@ export type DiscoverySchema = { service: { workspace?: { + title?: string; collection?: { + title?: string; accept?: string[]; + category?: { + term?: string; + scheme?: string; + label?: string; + }[]; + templateLinks?: unknown; href: string; }[]; }[]; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/http.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/http.types.ts index bb981325..e9897581 100644 --- a/packages/adt-schemas/src/schemas/generated/types/custom/http.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/custom/http.types.ts @@ -7,6 +7,16 @@ export type HttpSchema = { session: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; properties?: { property?: { $value?: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/custom/transportmanagmentSingle.types.ts b/packages/adt-schemas/src/schemas/generated/types/custom/transportmanagmentSingle.types.ts index eb934366..39effc77 100644 --- a/packages/adt-schemas/src/schemas/generated/types/custom/transportmanagmentSingle.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/custom/transportmanagmentSingle.types.ts @@ -16,6 +16,16 @@ export type TransportmanagmentSingleSchema = { packageName?: string; description?: string; }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; adtTemplate?: { adtProperty?: { $value?: string; @@ -25,6 +35,16 @@ export type TransportmanagmentSingleSchema = { }; request?: { long_desc?: string; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; attributes?: { attribute?: string; description?: string; @@ -32,6 +52,16 @@ export type TransportmanagmentSingleSchema = { position?: string; }[]; abap_object?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; pgmid?: string; type?: string; name?: string; @@ -47,6 +77,16 @@ export type TransportmanagmentSingleSchema = { }[]; all_objects?: { abap_object?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; pgmid?: string; type?: string; name?: string; @@ -63,7 +103,27 @@ export type TransportmanagmentSingleSchema = { }; task?: { long_desc?: string; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; pgmid?: string; type?: string; name?: string; @@ -131,7 +191,27 @@ export type TransportmanagmentSingleSchema = { }; task?: { long_desc?: string; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; pgmid?: string; type?: string; name?: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/abapsource.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/abapsource.types.ts index 2a41c1b9..84044e0c 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/abapsource.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/abapsource.types.ts @@ -12,8 +12,28 @@ export type AbapsourceSchema = language?: { version?: string; description?: string; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }; objectUsage?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; restricted?: boolean; }; }[]; @@ -24,8 +44,28 @@ export type AbapsourceSchema = language?: { version?: string; description?: string; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }; objectUsage?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; restricted?: boolean; }; }; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/adtcore.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/adtcore.types.ts index 739b4d55..f5e7224d 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/adtcore.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/adtcore.types.ts @@ -17,6 +17,16 @@ export type AdtcoreSchema = packageName?: string; description?: string; }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; adtTemplate?: { adtProperty?: { $value?: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/atcexemption.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/atcexemption.types.ts index cc9be695..77eb38fd 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/atcexemption.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/atcexemption.types.ts @@ -53,6 +53,19 @@ export type AtcexemptionSchema = checkClass: string; validUntil: string; supportPackage?: string; + reasons?: { + reason?: { + id?: string; + justificationMandatory?: boolean; + title?: string; + }[]; + }; + validities?: { + validity?: { + id?: string; + value?: string; + }[]; + }; }; } | { @@ -103,6 +116,19 @@ export type AtcexemptionSchema = checkClass: string; validUntil: string; supportPackage?: string; + reasons?: { + reason?: { + id?: string; + justificationMandatory?: boolean; + title?: string; + }[]; + }; + validities?: { + validity?: { + id?: string; + value?: string; + }[]; + }; }; status: { message: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/atcfinding.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/atcfinding.types.ts index a6a4072e..50ece58b 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/atcfinding.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/atcfinding.types.ts @@ -9,6 +9,16 @@ export type AtcfindingSchema = | { finding: { extension?: unknown; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; quickfixes: { manual?: boolean; automatic?: boolean; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/atcresult.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/atcresult.types.ts index e19cc86a..c5064742 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/atcresult.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/atcresult.types.ts @@ -75,6 +75,27 @@ export type AtcresultSchema = objectTypeId?: string; }[]; }; + descriptionTags: { + tagWithDescription?: { + name: string; + descriptions: { + description?: { + value?: string; + description?: string; + }[]; + }; + }[]; + }; + scaAttributes?: { + scaAttribute?: { + attributeName?: string; + refAttributeName?: string; + label?: boolean; + labelS?: string; + labelM?: string; + labelL?: string; + }[]; + }; infos: { info?: { type: string; @@ -85,5 +106,28 @@ export type AtcresultSchema = }; } | { - queryChoice: unknown; + queryChoice: { + activeResultQuery?: { + includeAggregates: boolean; + includeFindings: boolean; + contactPerson: string; + queryEnabled: boolean; + }; + specificResultQuery?: { + includeAggregates: boolean; + includeFindings: boolean; + contactPerson: string; + queryEnabled: boolean; + displayId: string; + }; + userResultQuery?: { + includeAggregates: boolean; + includeFindings: boolean; + contactPerson: string; + queryEnabled: boolean; + createdBy: string; + ageMin: number; + ageMax: number; + }; + }; }; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/atcworklist.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/atcworklist.types.ts index 1a48ae75..f2631179 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/atcworklist.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/atcworklist.types.ts @@ -69,6 +69,27 @@ export type AtcworklistSchema = objectTypeId?: string; }[]; }; + scaAttributes?: { + scaAttribute?: { + attributeName?: string; + refAttributeName?: string; + label?: boolean; + labelS?: string; + labelM?: string; + labelL?: string; + }[]; + }; + descriptionTags: { + tagWithDescription?: { + name: string; + descriptions: { + description?: { + value?: string; + description?: string; + }[]; + }; + }[]; + }; infos: { info?: { type: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/checklist.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/checklist.types.ts index 683ac591..478d96b8 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/checklist.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/checklist.types.ts @@ -29,6 +29,16 @@ export type ChecklistSchema = { column?: number; word?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; objDescr: string; type: unknown; line?: number; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/checkrun.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/checkrun.types.ts index 56457e50..a3ebc9ba 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/checkrun.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/checkrun.types.ts @@ -54,6 +54,16 @@ export type CheckrunSchema = column?: number; word?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; uri?: string; type?: unknown; shortText?: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/configuration.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/configuration.types.ts index 723f9375..bd454c87 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/configuration.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/configuration.types.ts @@ -14,6 +14,16 @@ export type ConfigurationSchema = { isMandatory?: boolean; }[]; }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }; client?: string; configName?: string; createdBy?: string; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/configurations.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/configurations.types.ts index 80a2aefe..81891cc3 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/configurations.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/configurations.types.ts @@ -6,5 +6,31 @@ */ export type ConfigurationsSchema = { - configurations: unknown; + configurations: { + configuration: { + properties: { + property: { + $value?: string; + key?: string; + isMandatory?: boolean; + }[]; + }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }; + client?: string; + configName?: string; + createdBy?: string; + createdAt?: string; + changedBy?: string; + changedAt?: string; + }[]; + }; }; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/log.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/log.types.ts index ef21c0d0..a19daa5b 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/log.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/log.types.ts @@ -8,8 +8,28 @@ export type LogSchema = | { logKeys: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; progVersion?: { key?: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; value?: string; calls?: number; lastCall?: string; @@ -24,6 +44,16 @@ export type LogSchema = fieldList: { field?: { value: { + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; e?: { t?: boolean; v?: string; @@ -41,6 +71,16 @@ export type LogSchema = name?: string; }[]; }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }; } | { diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/logpoint.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/logpoint.types.ts index daa8dbce..d75c9f08 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/logpoint.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/logpoint.types.ts @@ -101,6 +101,16 @@ export type LogpointSchema = inactivatedBy?: string; inactiveSince?: string; }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; location?: { includePosition?: { extension?: unknown; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/quickfixes.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/quickfixes.types.ts index b8895c8f..9479475f 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/quickfixes.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/quickfixes.types.ts @@ -11,6 +11,25 @@ export type QuickfixesSchema = affectedObjects?: { unit?: { content: string; + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }[]; }; }; @@ -18,8 +37,27 @@ export type QuickfixesSchema = | { evaluationResults: { evaluationResult?: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; userContent?: string; - affectedObjects?: unknown; + affectedObjects?: { + objectReference?: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }[]; + }; }[]; }; } @@ -27,10 +65,48 @@ export type QuickfixesSchema = proposalRequest: { input: { content: string; + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }; affectedObjects?: { unit?: { content: string; + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }[]; }; userContent?: string; @@ -41,6 +117,25 @@ export type QuickfixesSchema = deltas: { unit?: { content: string; + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; }[]; }; selection?: { @@ -53,6 +148,18 @@ export type QuickfixesSchema = description?: string; }; variableSourceStates?: { + objectReferences?: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }[]; + name?: string; + }[]; keepCursor?: boolean; }; statusMessages?: { diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/traces.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/traces.types.ts index 8f5381fc..f36c0d76 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/traces.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/traces.types.ts @@ -233,5 +233,15 @@ export type TracesSchema = }; } | { - uriMapping: unknown; + uriMapping: { + objectReference: { + extension?: unknown; + uri?: string; + parentUri?: string; + type?: string; + name?: string; + packageName?: string; + description?: string; + }; + }; }; diff --git a/packages/adt-schemas/src/schemas/generated/types/sap/transportmanagment.types.ts b/packages/adt-schemas/src/schemas/generated/types/sap/transportmanagment.types.ts index 486cd26c..ac1c14f4 100644 --- a/packages/adt-schemas/src/schemas/generated/types/sap/transportmanagment.types.ts +++ b/packages/adt-schemas/src/schemas/generated/types/sap/transportmanagment.types.ts @@ -22,12 +22,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -59,12 +79,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -96,12 +136,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -136,12 +196,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -173,12 +253,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -210,12 +310,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -251,12 +371,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -288,12 +428,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -325,12 +485,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -365,12 +545,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -402,12 +602,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -439,12 +659,32 @@ export type TransportmanagmentSchema = { obj_info?: string; obj_desc?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; number?: string; owner?: string; desc?: string; status?: string; uri?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; abap_object?: { pgmid?: string; type?: string; @@ -484,6 +724,16 @@ export type TransportmanagmentSchema = { column?: number; word?: string; }[]; + link?: { + href: string; + rel?: string; + type?: string; + hreflang?: string; + title?: string; + length?: number; + etag?: string; + _text?: string; + }[]; uri?: string; type?: unknown; shortText?: string; diff --git a/packages/adt-schemas/ts-xsd.config.ts b/packages/adt-schemas/ts-xsd.config.ts index 555c1bed..ae21183e 100644 --- a/packages/adt-schemas/ts-xsd.config.ts +++ b/packages/adt-schemas/ts-xsd.config.ts @@ -67,6 +67,8 @@ const targetSchemas = [ 'custom/transportmanagmentCreate', 'custom/transportmanagmentSingle', 'custom/atcRun', + 'custom/aunitRun', + 'custom/aunitResult', ]; export default defineConfig({ @@ -89,6 +91,7 @@ export default defineConfig({ defaultExport: true, $xmlns: true, $imports: true, + singleQuote: true, }), // Generate TypeScript interfaces to generated/types/ directory // Uses ts-morph type checker for accurate type expansion diff --git a/packages/browser-auth/src/auth-core.ts b/packages/browser-auth/src/auth-core.ts index 604769e4..db3178fc 100644 --- a/packages/browser-auth/src/auth-core.ts +++ b/packages/browser-auth/src/auth-core.ts @@ -36,6 +36,7 @@ export async function authenticate( ): Promise { const { url, + loginUrl, headless = false, timeout = DEFAULT_TIMEOUT, userAgent, @@ -45,7 +46,8 @@ export async function authenticate( log = console.log, } = options; - const targetUrl = new URL(SYSTEM_INFO_PATH, url).toString(); + // URL to navigate to for SSO login + const targetUrl = loginUrl || new URL(SYSTEM_INFO_PATH, url).toString(); const sapHost = new URL(url).hostname; const profileDir = resolveUserDataDir(userDataDir); @@ -70,7 +72,7 @@ export async function authenticate( log('🧹 Clearing old SAP cookies...'); await adapter.clearCookies(sapHost); - // Step 3: Wait for authentication (200 response from target URL AND required cookies) + // Step 3: Wait for authentication (required cookies appear after SSO) log('🌐 Complete SSO login if prompted...'); const cookiesToWait = @@ -92,11 +94,17 @@ export async function authenticate( reject(new Error('Authentication cancelled - browser was closed')); }); - // Check for both 200 response AND required cookies on every response + // Check for required cookies on every response from SAP domain adapter.onResponse(async (event) => { - // Only check on 200 responses from the target URL - if (event.url === targetUrl && event.status === 200) { - // Got 200 from target URL - now check if cookies are set + // Check responses from SAP domain (not just targetUrl, since SSO may redirect) + const eventHost = new URL(event.url).hostname; + const isSapDomain = + eventHost === sapHost || + eventHost.endsWith('.' + sapHost) || + sapHost.endsWith('.' + eventHost); + + if (isSapDomain && event.status === 200) { + // Got 200 from SAP domain - check if required cookies are set const allCookies = await adapter.getCookies(); const domainCookies = allCookies.filter( (c) => diff --git a/packages/browser-auth/src/types.ts b/packages/browser-auth/src/types.ts index 98cd2bf1..d25f8876 100644 --- a/packages/browser-auth/src/types.ts +++ b/packages/browser-auth/src/types.ts @@ -37,8 +37,14 @@ export interface BrowserCredentials { * Browser auth configuration options */ export interface BrowserAuthOptions { - /** SAP system URL */ + /** SAP system URL (used for cookie domain matching) */ url: string; + /** + * URL to open for SSO login (default: {url}/sap/bc/adt/core/http/systeminformation). + * Use this when the base SAP URL requires basic auth but SSO is available + * via a different path (e.g., /fiori, /sap/bc/ui5_ui5/ui2/ushell/shells/abap/FioriLaunchpad.html). + */ + loginUrl?: string; /** Show browser window during login (default: false) */ headless?: boolean; /** Timeout for login in ms (default: 300000 = 5 minutes) */ diff --git a/packages/ts-xsd/src/codegen/generate.ts b/packages/ts-xsd/src/codegen/generate.ts index 4f236b03..ed48c4f4 100644 --- a/packages/ts-xsd/src/codegen/generate.ts +++ b/packages/ts-xsd/src/codegen/generate.ts @@ -53,6 +53,8 @@ export interface GenerateOptions { * Uses `satisfies Schema as const` instead of just `as const`. */ isolatedDeclarations?: boolean; + /** Use single quotes for string literals and property names */ + singleQuote?: boolean; } /** @@ -292,10 +294,11 @@ function schemaToLiteral( pretty = true, indent = ' ', isolatedDeclarations = false, + singleQuote = false, } = options; const safeName = escapeReservedWord(name); - const literal = objectToLiteral(schema, pretty, indent, 0); + const literal = objectToLiteral(schema, pretty, indent, 0, singleQuote); // For isolatedDeclarations compatibility, use `satisfies Schema as const` // This makes the type explicit so TypeScript can emit declarations @@ -314,6 +317,7 @@ function objectToLiteral( pretty: boolean, indent: string, depth: number, + singleQuote = false, ): string { if (value === null || value === undefined) { return 'undefined'; @@ -325,7 +329,11 @@ function objectToLiteral( } if (typeof value === 'string') { - return JSON.stringify(value); + const json = JSON.stringify(value); + if (singleQuote) { + return `'${json.slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`; + } + return json; } if (typeof value === 'number' || typeof value === 'boolean') { @@ -338,7 +346,7 @@ function objectToLiteral( } const items = value.map((item) => - objectToLiteral(item, pretty, indent, depth + 1), + objectToLiteral(item, pretty, indent, depth + 1, singleQuote), ); if (pretty) { @@ -361,8 +369,20 @@ function objectToLiteral( } const props = entries.map(([key, val]) => { - const keyStr = isValidIdentifier(key) ? key : JSON.stringify(key); - const valStr = objectToLiteral(val, pretty, indent, depth + 1); + let keyStr = key; + if (!isValidIdentifier(key)) { + const json = JSON.stringify(key); + keyStr = singleQuote + ? `'${json.slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'` + : json; + } + const valStr = objectToLiteral( + val, + pretty, + indent, + depth + 1, + singleQuote, + ); return `${keyStr}: ${valStr}`; }); diff --git a/packages/ts-xsd/src/codegen/ts-morph.ts b/packages/ts-xsd/src/codegen/ts-morph.ts index 7bd90449..be78a92a 100644 --- a/packages/ts-xsd/src/codegen/ts-morph.ts +++ b/packages/ts-xsd/src/codegen/ts-morph.ts @@ -778,7 +778,14 @@ function addElementProperty( // Handle element reference if (element.ref) { const refName = stripNsPrefix(element.ref); - const refElement = ctx.schema.element?.find((e) => e.name === refName); + // Search in current schema first, then in $imports + let refElement = ctx.schema.element?.find((e) => e.name === refName); + if (!refElement && ctx.schema.$imports) { + for (const imported of ctx.schema.$imports) { + refElement = imported.element?.find((e) => e.name === refName); + if (refElement) break; + } + } if (refElement) { addElementProperty( { diff --git a/packages/ts-xsd/src/generators/interfaces.ts b/packages/ts-xsd/src/generators/interfaces.ts index e69820eb..5f0a4342 100644 --- a/packages/ts-xsd/src/generators/interfaces.ts +++ b/packages/ts-xsd/src/generators/interfaces.ts @@ -93,7 +93,10 @@ export function interfaces(options: InterfacesOptions = {}): GeneratorPlugin { // Always resolve the schema to merge all imports into one flat schema // This ensures all types are available in a single source file - const targetSchema = resolveSchema(schema.schema); + // keepImportsRef preserves $imports so element refs from imported schemas can be resolved + const targetSchema = resolveSchema(schema.schema, { + keepImportsRef: true, + }); // Check if resolved schema has root elements (check AFTER resolution for imported elements) const hasRootElements = diff --git a/packages/ts-xsd/src/generators/raw-schema.ts b/packages/ts-xsd/src/generators/raw-schema.ts index 8a122aaa..8be1ab60 100644 --- a/packages/ts-xsd/src/generators/raw-schema.ts +++ b/packages/ts-xsd/src/generators/raw-schema.ts @@ -61,6 +61,8 @@ export interface RawSchemaOptions { * Also adds `import type { Schema } from '@abapify/ts-xsd';` */ isolatedDeclarations?: boolean; + /** Use single quotes for string literals and property names */ + singleQuote?: boolean; } // ============================================================================ @@ -95,6 +97,7 @@ export function rawSchema(options: RawSchemaOptions = {}): GeneratorPlugin { resolveIncludes = false, resolveAll = false, isolatedDeclarations = false, + singleQuote = false, } = options; return { @@ -202,7 +205,7 @@ export function rawSchema(options: RawSchemaOptions = {}): GeneratorPlugin { } // Schema literal - const literal = objectToLiteral(outputSchema, true, ' ', 0); + const literal = objectToLiteral(outputSchema, true, ' ', 0, singleQuote); // For isolatedDeclarations, use named export with `as const satisfies Schema` // This is required because default exports can't be inferred with --isolatedDeclarations @@ -424,6 +427,7 @@ function objectToLiteral( pretty: boolean, indent: string, depth: number, + singleQuote = false, ): string { if (value === null || value === undefined) { return 'undefined'; @@ -434,7 +438,11 @@ function objectToLiteral( } if (typeof value === 'string') { - return JSON.stringify(value); + const json = JSON.stringify(value); + if (singleQuote) { + return `'${json.slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`; + } + return json; } if (typeof value === 'number' || typeof value === 'boolean') { @@ -445,7 +453,7 @@ function objectToLiteral( if (value.length === 0) return '[]'; const items = value.map((item) => - objectToLiteral(item, pretty, indent, depth + 1), + objectToLiteral(item, pretty, indent, depth + 1, singleQuote), ); if (pretty) { @@ -466,8 +474,20 @@ function objectToLiteral( if (entries.length === 0) return '{}'; const props = entries.map(([key, val]) => { - const keyStr = isValidIdentifier(key) ? key : JSON.stringify(key); - const valStr = objectToLiteral(val, pretty, indent, depth + 1); + let keyStr = key; + if (!isValidIdentifier(key)) { + const json = JSON.stringify(key); + keyStr = singleQuote + ? `'${json.slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'` + : json; + } + const valStr = objectToLiteral( + val, + pretty, + indent, + depth + 1, + singleQuote, + ); return `${keyStr}: ${valStr}`; }); diff --git a/packages/ts-xsd/src/xml/build.ts b/packages/ts-xsd/src/xml/build.ts index 46780718..972e839c 100644 --- a/packages/ts-xsd/src/xml/build.ts +++ b/packages/ts-xsd/src/xml/build.ts @@ -535,10 +535,17 @@ function buildFieldWithTagName( // Element explicitly marked as qualified - use prefix usePrefix = prefix; } else { - // No element-level form - use schema default - const elementFormDefault = (rootSchema as { elementFormDefault?: string }) - .elementFormDefault; - usePrefix = elementFormDefault === 'qualified' ? prefix : undefined; + // No element-level form - use the element's defining schema's default first, + // then fall back to root schema default. This ensures elements from imported + // schemas (e.g., objectSet in adtcore:AdtObjectSets) respect their own + // schema's elementFormDefault rather than the root schema's. + const definingFormDefault = ( + elementSchema as { elementFormDefault?: string } + ).elementFormDefault; + const effectiveFormDefault = + definingFormDefault ?? + (rootSchema as { elementFormDefault?: string }).elementFormDefault; + usePrefix = effectiveFormDefault === 'qualified' ? prefix : undefined; } actualTagName = usePrefix ? `${usePrefix}:${tagName}` : tagName; } diff --git a/packages/ts-xsd/src/xsd/parse.ts b/packages/ts-xsd/src/xsd/parse.ts index 8d863361..86debf5f 100644 --- a/packages/ts-xsd/src/xsd/parse.ts +++ b/packages/ts-xsd/src/xsd/parse.ts @@ -1109,9 +1109,11 @@ function copyAttr( target: Record, name: string, ): void { - const value = el.getAttribute(name); - if (value !== null) { - target[name] = value; + if (el.hasAttribute(name)) { + const value = el.getAttribute(name); + if (value !== null) { + target[name] = value; + } } } @@ -1120,9 +1122,11 @@ function copyBoolAttr( target: Record, name: string, ): void { - const value = el.getAttribute(name); - if (value !== null) { - target[name] = value === 'true'; + if (el.hasAttribute(name)) { + const value = el.getAttribute(name); + if (value !== null) { + target[name] = value === 'true'; + } } }