diff --git a/AGENTS.md b/AGENTS.md index a1973f0..6b0e17d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,18 +55,22 @@ against the repos, don't guess): | Repo | Role | |---|---| -| [cap2UI5/cap2UI5](https://github.com/cap2UI5/cap2UI5) | the npm package `cap2ui5`: a CAP plugin that hosts upstream's transpiled runtime | +| [cap2UI5/cap2UI5](https://github.com/cap2UI5/cap2UI5) | the npm package `cap2ui5`: a CAP plugin that hosts upstream's transpiled runtime. Also the home of the decision records (`docs/adr/`) | +| [abap2UI5/abap2UI5](https://github.com/abap2UI5/abap2UI5) | the framework itself, in ABAP. Downported and transpiled, it is published as `@abap2ui5/node-runtime` | +| [cap2UI5/docs](https://github.com/cap2UI5/docs) | this site | Both packages are on npm since 2026-09-27: `cap2ui5@0.1.0` (Node ≥ 20, peer `@sap/cds` ≥ 9) and `@abap2ui5/node-runtime@1.145.0` (Node ≥ 22), which `cap2ui5` pins **exactly**. The runtime package was renamed from `@abap2ui5/runtime` before its first publish — the old name never existed on npm, and on the site it appears only in prose that says it is the old name. -| [abap2UI5/abap2UI5](https://github.com/abap2UI5/abap2UI5) | the framework itself, in ABAP. Downported and transpiled, it is published as `@abap2ui5/node-runtime` | -| [cap2UI5/builder-abap2UI5-js](https://github.com/cap2UI5/builder-abap2UI5-js) | the ABAP→JS transpiler pipelines | -The three builder repos that generated the old CAP application -(`builder-cap2UI5`, `builder-cap2UI5-web`, `web-cap2UI5-build`) are archived. +The port's four repositories — `builder-abap2UI5-js` (the ABAP→JS transpiler +and its conformance gate), `builder-cap2UI5`, `builder-cap2UI5-web` and +`web-cap2UI5-build` (which generated the old CAP application and the +playground) — are archived or being archived, and nothing consumes their +output. Do not link them: an archived repository may be deleted. Their decision +records live in cap2UI5's `docs/adr/`. There is no generated app, no vendored `core/`, no mirrored `app/z2ui5/webapp`. There is **no static frontend route** and no `webapp` option either. The page @@ -102,9 +106,12 @@ against the cap2UI5 repository. ## Rules - Recommend `srv/apps/` as the place for apps — it is the plugin's default. -- The framework's own classes are **not importable**. An app imports +- The framework's own classes are **not a supported import**. Importing + `@abap2ui5/node-runtime/output/…` technically works — the package exports + `./output/*` — but it couples an app to transpiler output. A JS app imports `cap2ui5` and nothing else; `c.raw` is the escape hatch to the transpiled - `z2ui5_if_client`. + `z2ui5_if_client`. An app that wants the framework's ABAP API (the view + builder, for one) is written in ABAP and transpiled — see the views guide. - Measure before documenting a framework behaviour. The runtime is upstream's ABAP running on open-abap, and not everything upstream does works here — the user exit is discovered by a class-repository lookup in ABAP and had to diff --git a/HANDOVER.md b/HANDOVER.md index e297149..27fe340 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -17,7 +17,7 @@ The reasoning behind all of it is in [ROADMAP.md](ROADMAP.md) §§8–25 and in |---|---| | upstream, the four seams + the runtime package job | [abap2UI5/abap2UI5#2772](https://github.com/abap2UI5/abap2UI5/pull/2772) merged | | the plugin repository | [cap2UI5/cap2UI5#72](https://github.com/cap2UI5/cap2UI5/pull/72) merged, plus #75 (CI ref), #76 (the user exit), #77 (publishable package, consumer test) and #79 (`@abap2ui5/node-runtime`, startup addresses, trusted publishing) | -| the conformance gate, the prototype, the ADRs | [cap2UI5/builder-abap2UI5-js#29](https://github.com/cap2UI5/builder-abap2UI5-js/pull/29) merged | +| the conformance gate, the prototype, the ADRs | cap2UI5/builder-abap2UI5-js#29 merged (that repository is being archived; its decision records are being copied to `cap2UI5/cap2UI5:docs/adr/`) | | this site, migrated to the plugin | [cap2UI5/docs#20](https://github.com/cap2UI5/docs/pull/20), [#21](https://github.com/cap2UI5/docs/pull/21), [#22](https://github.com/cap2UI5/docs/pull/22) merged | | the cutover (ADR-008 steps 3–5) | `update_cap` and `build web` disabled, `generated-app-final` tagged at `595c76f`, `builder-cap2UI5`, `builder-cap2UI5-web` and `web-cap2UI5-build` archived | | publishing | `cap2ui5@0.1.0` and `@abap2ui5/node-runtime@1.145.0` on npm; the site's quickstart runs verbatim against them (`cds init --nodejs`, `npm install cap2ui5`, `cds watch`) | @@ -30,10 +30,12 @@ route every roundtrip gets 403. The site documents the working setup — an extra route for the roundtrip path with `"csrfProtection": false`, safe because abap2UI5 refuses a cross-origin POST itself (`docs/reference/deployment.md`). -[abap2UI5/abap2UI5#2802](https://github.com/abap2UI5/abap2UI5/pull/2802) (open) -teaches the frontend the token handshake. Once it is in a release and -`cap2ui5` pins that release, drop the extra route from the deployment page and -the known limit from `guide/roadmap.md`. +[abap2UI5/abap2UI5#2802](https://github.com/abap2UI5/abap2UI5/pull/2802) +teaches the frontend the token handshake. It was merged on 2026-09-27 as +`5a1bd70`, after the 1.145.0 release, so no abap2UI5 release carries it yet. +Until `cap2ui5` pins a runtime release that does, the approuter still needs the +extra route. Then drop it from the deployment page and the known limit from +`guide/roadmap.md`. Not exercised at all so far: a real HANA or BTP deployment. The deployment page says so. @@ -65,17 +67,17 @@ page says so. - **Where the docs live** — `cap2UI5/docs` stays, or becomes a folder in the plugin repo. Less urgent than it was: the rewrite is done either way, and moving it now is a `git mv` plus the two workflows. -- **`builder-abap2UI5-js`**: still active and still running its nightly - pipeline (it mirrored and transpiled upstream `ad0a2dd` today, and its oracle - classification improved — 1,285 green methods, up from 1,272, because the - seams merged). ADR-008 archives it *after* the conformance suite is copied - where it should live. Its two red suites (`cs_event` constants, the - `upstream-units` ratchet) are `main`'s, both have a proposed patch on - [#29](https://github.com/cap2UI5/builder-abap2UI5-js/pull/29), and both are - work on a repository that is going away — worth deciding whether to fix them - at all, or to let the archive settle it. -- The four deploy keys (`ACTION_KEY_CAP`, `ACTION_KEY_APP`, `ACTION_KEY_WEB`, - `BUILT_DEPLOY_KEY`) can go from the archived repositories' secrets. +- **`builder-abap2UI5-js`** is being archived. Nothing consumes its output: + `cap2ui5` depends on `@abap2ui5/node-runtime` from npm, which abap2UI5 + builds itself. The decision records that existed only there (ADR-001 to + ADR-004, ADR-006 and `transpiler-roadmap.md`) are being copied to + `cap2UI5/cap2UI5:docs/adr/`. Its two red suites (`cs_event` constants, the + `upstream-units` ratchet) had a proposed patch on its PR #29; the archive + settles them. +- `builder-cap2UI5`, `builder-cap2UI5-web` and `web-cap2UI5-build` are archived + and may be deleted — nothing links to them any more. The four deploy keys + (`ACTION_KEY_CAP`, `ACTION_KEY_APP`, `ACTION_KEY_WEB`, `BUILT_DEPLOY_KEY`) go + with them. ## What I could not do, and why diff --git a/README.md b/README.md index 535eeb9..bfd5402 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ VitePress documentation for [**cap2UI5**](https://github.com/cap2UI5/cap2UI5) — the CAP / Node.js port of the [abap2UI5](https://github.com/abap2UI5/abap2UI5) concept. Published at **[cap2ui5.github.io/docs](https://cap2ui5.github.io/docs/)**. -A zero-install playground of the framework runs at [cap2ui5.github.io/web-cap2UI5-build](https://cap2ui5.github.io/web-cap2UI5-build/) (built by [builder-cap2UI5-web](https://github.com/cap2UI5/builder-cap2UI5-web) into [web-cap2UI5-build](https://github.com/cap2UI5/web-cap2UI5-build)). +To try it, follow the [Quickstart](https://cap2ui5.github.io/docs/guide/getting-started): a CAP project, `npm install cap2ui5`, `cds watch`. ## Develop locally diff --git a/ROADMAP.md b/ROADMAP.md index a9f6bc7..46b5ef0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -535,10 +535,11 @@ recommendation was wrong on the facts and was not executed. ### Where the work is tracked -[builder-abap2UI5-js `docs/adr-006-conformance.md`](https://github.com/cap2UI5/builder-abap2UI5-js/blob/main/docs/adr-006-conformance.md) +ADR-006, then in builder-abap2UI5-js and now +[`cap2UI5/cap2UI5:docs/adr/adr-006-conformance.md`](https://github.com/cap2UI5/cap2UI5/blob/main/docs/adr/adr-006-conformance.md) — decision, the five-item worklist, and how to grow the corpus from 2 apps to 11 using upstream's own `zcl_tst_*` framework exercises. -[`docs/adr-007-repo-consolidation.md`](https://github.com/cap2UI5/builder-abap2UI5-js/blob/main/docs/adr-007-repo-consolidation.md) +ADR-007, now [`docs/adr/adr-007-repo-consolidation.md`](https://github.com/cap2UI5/cap2UI5/blob/main/docs/adr/adr-007-repo-consolidation.md) — six repos to two, deliberately sequenced *after* the worklist: reorganising the delivery of a broken artefact reorganises the delivery of a broken artefact. @@ -871,7 +872,8 @@ by identity (its signature has no name parameter; it matches by value). It lived only in a scratchpad, which for the most substantial part of this work was the wrong place. It is now -[`builder-abap2UI5-js/docs/prototypes/open-abap-cap/`](https://github.com/cap2UI5/builder-abap2UI5-js/tree/main/docs/prototypes/open-abap-cap) +`builder-abap2UI5-js/docs/prototypes/open-abap-cap/` (that repository is +being archived; the prototype became cap2UI5's `plugin/` and `examples/`) — the 548 hand-written lines plus a README with reproduction steps. The 19 MB of transpiled framework and the webapp are gitignored, because any checkout can rebuild them. diff --git a/docs/.verify-refs-ignore b/docs/.verify-refs-ignore index 54d1b99..a8b74ad 100644 --- a/docs/.verify-refs-ignore +++ b/docs/.verify-refs-ignore @@ -15,7 +15,7 @@ app/customer-list # a hypothetical Fiori elements app, used for contrast z2ui5_cl_app_xyz # stands for "your app class" in the interface description my_first_app # the class the getting-started walkthrough has you write my_app_name # stands for "your class" in the ?app_start= URL on the navigation page -zcl_my_app # stands for "the name you gave defineApp" in the why-cap2UI5 pitch +zcl_my_app # stands for "your app class": the name you gave defineApp in the why-cap2UI5 pitch, and the ABAP class on the views page — an ABAP app is registered by its transpiled class, not by defineApp ClassName # literal placeholder in the URL-parameter description # Upstream's frozen src/99 needs no entries any more: the plugin hosts diff --git a/docs/guide/ecosystem.md b/docs/guide/ecosystem.md index da20149..0e54419 100644 --- a/docs/guide/ecosystem.md +++ b/docs/guide/ecosystem.md @@ -10,7 +10,17 @@ owns what saves time when something breaks. | [**abap2UI5/abap2UI5**](https://github.com/abap2UI5/abap2UI5) | the framework itself: the ABAP sources and the UI5 shell. Everything cap2UI5 runs comes from here | | [**cap2UI5/cap2UI5**](https://github.com/cap2UI5/cap2UI5) | the plugin. `plugin/` is the npm package `cap2ui5`; `examples/bookshop` is a CAP project using it; `runtime/` is a stand-in for `@abap2ui5/node-runtime` that the repository's own tests build from upstream | | [**cap2UI5/docs**](https://github.com/cap2UI5/docs) | this site | -| [**cap2UI5/builder-abap2UI5-js**](https://github.com/cap2UI5/builder-abap2UI5-js) | the conformance gate and the ADRs that led to the current design. Historical: the build pipelines it ran are retired | + +The decisions that led to the current design are in the plugin repository, +`docs/adr/` — [ADR-008](https://github.com/cap2UI5/cap2UI5/blob/main/docs/adr/adr-008-host-not-port.md) +is the one that made cap2UI5 a host rather than a port. + +The four repositories of the earlier port — `builder-abap2UI5-js`, which +transpiled abap2UI5 into JavaScript, and `builder-cap2UI5`, +`builder-cap2UI5-web` and `web-cap2UI5-build`, which generated an application +and a playground from it — are archived or being archived. Nothing consumes +their output: `cap2ui5` depends on `@abap2ui5/node-runtime` from npm, which +abap2UI5 builds itself. ## The packages diff --git a/docs/guide/roadmap.md b/docs/guide/roadmap.md index 38e6698..17e5334 100644 --- a/docs/guide/roadmap.md +++ b/docs/guide/roadmap.md @@ -25,7 +25,8 @@ The frontend in runtime `1.145.0` sends no `X-CSRF-Token`, and the route is refused with `403` until the roundtrip path gets a route of its own with `"csrfProtection": false`. The route, and why it is safe, are on [Deployment](../reference/deployment#the-approuter-needs-one-extra-route-today). -abap2UI5/abap2UI5#2802 (open) teaches the frontend the token handshake. +abap2UI5/abap2UI5#2802 teaches the frontend the token handshake; it is merged, +but no abap2UI5 release carries it yet. ### The facade does not cover everything @@ -77,8 +78,9 @@ than by completing a table. ## What is deliberately not planned **A cap2UI5 view builder.** Views are UI5 XML strings; a template literal is -shorter and clearer than a fluent chain in JavaScript. abap2UI5's builder is in -the runtime and reachable through `c.raw` if you want it. +shorter and clearer than a fluent chain in JavaScript. abap2UI5's builder runs +in the runtime, and an app written in ABAP can use it — see +[The ABAP view builder](./views#the-abap-view-builder). **A second implementation of anything upstream owns.** The whole point of the current design is that there is one implementation of the framework. A feature diff --git a/docs/guide/views.md b/docs/guide/views.md index 3c9eb59..853eb08 100644 --- a/docs/guide/views.md +++ b/docs/guide/views.md @@ -79,9 +79,133 @@ There is no `modelUpdate()`. It existed, called a framework method documented as ## The ABAP view builder abap2UI5 ships `z2ui5_cl_ui5_view_builder`, a fluent builder for the same XML. -It is in the runtime and ABAP apps use it — but the plugin's facade does not -expose it, because in JavaScript a template literal is shorter and clearer than -a builder chain. If you want it, it is reachable through `c.raw`. +It runs in the hosted runtime — the shipped `Z2UI5_CL_UI5_APP_HI_WORLD` is +built with it and works under cap2UI5. The facade does not expose it, and +`c.raw` does not reach it either: `z2ui5_if_client` has no reference to the +builder. In JavaScript a template literal is shorter and clearer than a builder +chain, so a JS app keeps `c.view` with `c.bind` and `c.event`. + +If you want the builder, write the app in ABAP. + +### An app in ABAP + +A normal `z2ui5_if_app` class that uses `z2ui5_cl_ui5_view_builder`, transpiled +against the runtime the plugin hosts. The steps are the ones in the +`@abap2ui5/node-runtime` README, with three additions for a CAP project. + +`abap/zcl_my_app.clas.abap` — an input and a button, built with the builder: + +```abap +CLASS zcl_my_app DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + INTERFACES z2ui5_if_app. + DATA name TYPE string. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS zcl_my_app IMPLEMENTATION. + + METHOD z2ui5_if_app~main. + DATA view TYPE REF TO z2ui5_cl_ui5_view_builder. + + IF client->check_on_init( ) IS NOT INITIAL. + + view = z2ui5_cl_ui5_view_builder=>factory( + )->ele( n = `View` ns = `mvc` + )->a( n = `xmlns` v = `sap.m` + )->a( n = `xmlns:mvc` v = `sap.ui.core.mvc` + )->a( n = `displayBlock` v = `true` + )->ele( `Page` + )->a( n = `title` v = `My ABAP app` + )->tag( `Input` + )->a( n = `value` v = client->_bind_edit( name ) + )->tag( `Button` + )->a( n = `text` v = `Post` + )->a( n = `press` v = client->_event( `POST` ) ). + + client->view_display( view->stringify( ) ). + + ELSEIF client->check_on_event( `POST` ) IS NOT INITIAL. + client->message_toast_display( |Hello { name }| ). + ENDIF. + + ENDMETHOD. + +ENDCLASS. +``` + +Install the transpiler at exactly the version the runtime was built with: + +```bash +npm install --save-dev --save-exact @abaplint/transpiler-cli@$(node -p "require('@abap2ui5/node-runtime/package.json').abap2ui5.transpiler") +``` + +Without `--save-exact`, npm saves a caret range, and a later install can drift +away from the runtime. + +`abap_transpile.json`, with your classes in `abap/`: + +```json +{ + "input_folder": "abap", + "output_folder": "output", + "libs": [ + { "folder": "/node_modules/@abap2ui5/node-runtime/downport", "files": "/**/*.*" }, + { "url": "https://github.com/open-abap/open-abap-core", "folder": "/deps/open-abap-core" } + ], + "write_unit_tests": false, + "options": { "ignoreSyntaxCheck": false, "addFilenames": true, "unknownTypes": "runtimeError" } +} +``` + +```bash +git clone --depth 1 https://github.com/open-abap/open-abap-core deps/open-abap-core +npx abap_transpile abap_transpile.json +mkdir -p srv/abap && cp output/zcl_my_app.clas.mjs srv/abap/ +``` + +- **Clone open-abap-core once.** The transpiler uses `deps/open-abap-core` + when the folder exists; otherwise it clones into a temporary folder on every + run. +- **Copy only your own class.** `output/` also receives a full second copy of + the framework and of open-abap-core. Your class file has no imports: it + resolves everything through the runtime that is already running. +- **Put it under `srv/`.** `cds build --production` copies `srv/`, not a + top-level `output/`. Never point `output_folder` at `srv/apps`. + +Then load it with one app module, `srv/apps/abap-apps.mjs`. The plugin imports +app modules after the runtime has booted, which is when a transpiled class can +register itself: + +```js +await import("../abap/zcl_my_app.clas.mjs"); +``` + +`?app_start=ZCL_MY_APP` starts it (lowercase works too), and its state survives +roundtrips like any app's. The transpile type-checks against the framework, so +a misspelled builder method fails there rather than at runtime. The startup +lines list only the apps registered with `defineApp`, not ABAP apps. + +### From a JavaScript app + +Reaching the transpiled class through the runtime's global, +`abap.Classes["Z2UI5_CL_UI5_VIEW_BUILDER"]`, or through the package's +`./output/*` export works, but it is awkward and unsupported: every call is +async, every result is dereferenced with `.get()`, and the app is coupled to +transpiler output. + +::: warning `c.event()` does not survive the builder +In cap2ui5 0.1.0, the placeholder `c.event()` returns does not survive the +builder's XML escaping: the response then carries a raw NUL and is not valid +JSON. With the builder, the event string has to come from `c.raw` +(`z2ui5_if_client$_event`). A fix in cap2UI5 is under way. +::: + +Keep template literals with `c.bind` and `c.event` in a JavaScript app. ## Next diff --git a/docs/guide/where-it-comes-from.md b/docs/guide/where-it-comes-from.md index e3d50d9..a671429 100644 --- a/docs/guide/where-it-comes-from.md +++ b/docs/guide/where-it-comes-from.md @@ -86,11 +86,12 @@ transpiled Node runtime, and fixed at the source for everybody. ## What is left of the port -The conformance gate that found the drift, and the measurements that made the -decision, live in -[builder-abap2UI5-js](https://github.com/cap2UI5/builder-abap2UI5-js) together -with the ADRs. The app-building pipelines are archived: nothing is generated any -more. +The decisions, and the measurements behind them, are recorded in the plugin +repository's [`docs/adr/`](https://github.com/cap2UI5/cap2UI5/tree/main/docs/adr). +The port's repositories — the transpiler with its conformance gate, and the +three that generated the application and the playground — are archived or being +archived. Nothing consumes their output any more: `cap2ui5` depends on +`@abap2ui5/node-runtime` from npm, which abap2UI5 builds itself. ## Why the ABAP names remain diff --git a/docs/reference/deployment.md b/docs/reference/deployment.md index 9d471a5..e991f70 100644 --- a/docs/reference/deployment.md +++ b/docs/reference/deployment.md @@ -131,9 +131,10 @@ gets `403`. That protection is the framework's CSRF gate, so leave use this route. ::: info Pending upstream: abap2UI5/abap2UI5#2802 -That pull request (open, not merged) teaches the frontend the standard -`X-CSRF-Token` fetch-and-send handshake. Once `cap2ui5` pins a runtime release -that carries it, the extra route can go and the generated catch-all works as +That pull request teaches the frontend the standard `X-CSRF-Token` +fetch-and-send handshake. It was merged on 2026-09-27, after the release +`cap2ui5` pins, and no abap2UI5 release carries it yet. Once `cap2ui5` pins a +runtime release that does, the extra route can go and the generated catch-all works as it is. Until then, keep the route above. :::