{"ref":"artifact:sleibach-cap-collaborative-draft","page":"/artifacts/sleibach-cap-collaborative-draft","json":"/artifacts/sleibach-cap-collaborative-draft.json","generated":"2026-10-26","drafted_with_ai":true,"entity":{"id":"sleibach-cap-collaborative-draft","name":"cap-collaborative-draft","artifact_type":"cds-plugin","ecosystem":"cap","flavors":[{"key":"node","source":{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"A `cds-plugin` that enables **SAP Fiori Elements Collaborative Draft** on CAP Node.js backends — a feature normally exclusive to ABAP RAP."}}],"domain":"ui-and-frontend-bridge","origin_ring":"independent","description":{"one_line":"A cds-plugin that gives a CAP Node.js service the OData contract, tables and handlers Fiori elements needs for collaborative draft, so a second user joins an open draft instead of meeting HTTP 423.","short":"Collaborative draft support for CAP Node.js services","what_it_solves":"CAP's lean draft belongs to one editor: a second user opening it is refused with HTTP 423, locked by another user. This plugin makes the draft shared — it adds the participant, field-lock and collaborative-state tables to the model, injects the `CollaborativeDraftEnabled`, `DraftAccessType`, `DraftAdministrativeUser` and `ColDraftShare` parts of the OData contract into `$metadata`, and prepends handlers so a second user joins the draft while a PATCH on a field another participant holds answers 409. Two annotations on the service entity switch it on, `@odata.draft.enabled` and `@CollaborativeDraft.enabled: true`, and entities without the second are unaffected. It stops at the backend contract: the participant avatar group and the discard dialog are UI5 snippets the consuming application adds to its own Object Page, and cross-participant conflicts found on activation are logged rather than merged. Live presence needs [@cap-js-community/websocket](artifact:cap-js-community-websocket), and that relay is per process, so without the adapter the plugin runs pull-only and with it one CAP instance still does not broadcast to another instance's participants.","when_not_to_use":"The maintainer marks this under active development; public APIs, annotation names and OData contracts may still change."},"links":{"repo":"https://github.com/sleibach/cap-collaborative-draft","npm":"https://www.npmjs.com/package/cap-collaborative-draft"},"identity":{"author":"sleibach","license":"Apache-2.0"},"compat":{"runtimes":["node"],"cds_range":">=8.0"},"type_properties":{"distribution":{"value":["npm"],"sources":[{"url":"https://github.com/sleibach/cap-collaborative-draft","title":"cap-collaborative-draft — README","checked":"2026-10-18","quote":"npm add cap-collaborative-draft"}]}},"provides":[{"capability":"capability:draft-handling","what_it_does":"Turns a CAP Node.js draft from one editor's into several editors'. A user opening a draft that already exists joins it rather than being refused, the plugin deploys DRAFT.DraftParticipants, DRAFT.DraftFieldLocks and DRAFT.CollaborativeDraftState alongside the standard draft tables, and any participant can activate rather than only the originator. Fiori elements is told through injected $metadata: CollaborativeDraftEnabled, DraftAccessType, a DraftAdministrativeUser navigation property for the avatar group, and a ColDraftShare bound action behind the invite dialog. Entities that do not carry @CollaborativeDraft.enabled are untouched.","sources":[{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"If a draft already exists for the entity, the current user joins it (added to the presence store) rather than receiving HTTP 423 \"locked by another user\"."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"Creates `DRAFT.DraftParticipants`, `DRAFT.DraftFieldLocks` and `DRAFT.CollaborativeDraftState` entity definitions in the raw CSN if they do not already exist, causing CDS to deploy the corresponding database tables alongside the standard draft tables."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"Sets `InProcessByUser` to the current user so any participant can activate (not just the originator)."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"This plugin replicates the full OData contract on CAP Node.js, allowing Fiori Elements clients to activate their collaborative draft UI against a CAP backend. The plugin is additive — existing entities without `@CollaborativeDraft.enabled` are completely unaffected."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"A contained navigation property `DraftAdministrativeUser` is added to `DraftAdministrativeData`, pointing to a collection of:"}]},{"capability":"capability:concurrency-control","what_it_does":"Locks a shared draft field by field. A PATCH acquires a lock per OData property name in DRAFT.DraftFieldLocks and answers HTTP 409 when another participant already holds a non-expired lock on one of the patched fields. Locks older than the configured TTL, 120 seconds by default, stop blocking, and the check reads the table rather than memory, so it holds across CAP instances. Nested child entities and associations are not covered.","sources":[{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"Acquires field-level locks in `DRAFT.DraftFieldLocks`. Responds HTTP 409 if another participant holds a non-expired lock on any of the patched fields."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"Each lock record carries a `LockedAt` timestamp; locks older than the configured TTL (default: 120 seconds) are treated as expired and do not block new acquisitions."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"The locking granularity corresponds to individual OData property names (field names as they appear in the PATCH request body)."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"**Field lock checks** read from `DRAFT.DraftFieldLocks` in the database, so they are also cross-instance consistent."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"| PATCH field lock granularity | Property-level | Locks are per property name. Composite nested entity locking (child entities, associations) is not yet implemented. |"}]}],"requires":[{"ref":"artifact:cap-js-community-websocket","requirement":"conditional","role":"presence-relay","condition":"Real-time presence, participant avatars and field-lock indicators in the Fiori elements UI.","for":"Relaying join, leave, lock and change messages between the participants of a draft; without it presence and lock state are still read over OData, in pull-only mode.","sources":[{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"**Required peer dependency:** `@cap-js-community/websocket` — the plugin registers a WebSocket relay service for real-time presence updates, participant avatars, and field-lock indicators."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"Without WebSocket, the plugin operates in **pull-only mode**: presence and lock state are correct and available via standard OData reads, but updates from other participants only appear after a client-triggered re-read (governed by `@Common.SideEffects`)."},{"url":"https://raw.githubusercontent.com/sleibach/cap-collaborative-draft/main/README.md","title":"sleibach/cap-collaborative-draft — README","checked":"2026-09-28","quote":"| `@cap-js-community/websocket` | `>= 1.0` (optional, for real-time relay) |"}]}],"provenance":{"discovered_via":[{"source":"source:github","channel":"sleibach"}],"first_seen":"2026-08-31","claimed_by":"actor:sleibach"},"maintainers":["actor:sleibach"],"history":[{"date":"2026-09-28","field":"provides","from":"capability:draft-handling","to":"capability:draft-handling, capability:concurrency-control","reason":"Refresh 2026-09-28. The plugin acquires a lock per OData property name in DRAFT.DraftFieldLocks and answers HTTP 409 when another participant holds a non-expired one, which is the job concurrency-control names; the edge was missing and both edges now carry what_it_does with the README's sentences."},{"date":"2026-09-28","field":"description.one_line","from":"Enables SAP Fiori Elements collaborative draft on CAP Node.js backends.","to":"A cds-plugin that gives a CAP Node.js service the OData contract, tables and handlers Fiori elements needs for collaborative draft, so a second user joins an open draft instead of meeting HTTP 423.","reason":"Placement copied the package's own npm description. Replaced with a line written for this graph — what it does and where it sits — because an unattributed sentence is an unattributed claim."},{"date":"2026-09-28","field":"description.what_it_solves","from":null,"to":"CAP's lean draft belongs to one editor: a second user opening it is refused with HTTP 423, locked by another user. …","reason":"The page had no answer to what it does; the paragraph states what it adds, what turns it on, and where it stops — the UI snippets the application owns, conflicts logged rather than merged, and the per-process WebSocket relay."},{"date":"2026-09-28","field":"requires","from":null,"to":"artifact:cap-js-community-websocket","reason":"The README names it a required peer dependency for the real-time relay while its own compatibility table calls it optional; recorded as conditional on real-time presence, with both sentences quoted."},{"date":"2026-09-28","field":"compat.cds_range","from":null,"to":">=8.0","reason":"The README and the package's peerDependencies both state @sap/cds >= 8.0; the entry carried the runtime and not the range."},{"date":"2026-09-28","field":"flavors","from":null,"to":"node","reason":"Stated rather than left to the cds-plugin fallback: the README's first line says CAP Node.js backends."}],"drafted_with_ai":true},"effective_normative_relations":[{"instrument":"instrument:sap-developer-license-3-2-cap","relation":"use-constrained-by","sections":["1","2","3"],"applies_when":"A cds-plugin is used with SAP CAP materials, or an artifact is explicitly identified as a CAP plugin or application.","note":"This is inherited applicability, not a licence verdict about the artifact itself. Review the agreement for the actual materials and use; an explicit artifact relationship to another revision takes precedence.","scope":{"kind":"artifact"},"sources":[{"url":"https://cap.cloud.sap/resources/license/developer-license-3_2_CAP.txt","title":"SAP Developer License Agreement 3.2 CAP","checked":"2026-09-16","quote":"documentation, plug-ins, templates, scripts and sample code, libraries, software development kits"}],"inherited_from":"cap-plugins-and-applications","relevant_capabilities":["agent-systems-access","async-processing","data-import-export","database-connectors","deployment","destinations","llm-integration","mcp-servers","plugin-authoring","replication","retrieval-augmented-generation","rfc-connectivity"]}],"readings":{"liveness":[{"label":"Maintenance","tier":"fair","evidence":"5 months since release"},{"label":"Continuity","tier":"fair","evidence":"one contributor, personally owned"},{"label":"Repository","tier":"good","evidence":"pushed 2026-06, not archived"}],"compatibility":[{"label":"Declared for","tier":"good","evidence":">=8.0 — includes cds 10 and 9"},{"label":"Survived majors","tier":"poor","evidence":"no release since @sap/cds 10 shipped (July 2026)"},{"label":"Maintainer's CI","tier":"good","evidence":"CI green, 2026-06-05"}],"practice":[{"label":"Tests","tier":"good","evidence":"35 test files, npm test"},{"label":"Docs","tier":"good","evidence":"README 32 KB"},{"label":"Changelog","tier":"fair","evidence":"none in the repo"}],"reach":[{"value":0,"unit":"installs/week"},{"value":10,"unit":"watching"}]},"signals":{"crawled_at":"2026-10-26","latest_version":"1.0.2","majors":{"1":{"version":"1.0.0","date":"2026-05-11"}},"license":"Apache-2.0","first_release":"2026-05-11","last_release":"2026-06-05","releases":3,"cadence_days":25,"maintainers":1,"repository":"git+https://github.com/sleibach/cap-collaborative-draft.git","peer_cds":">=8.0","test_script":true,"types":false,"downloads_weekly":0,"stars":10,"forks":0,"open_issues":0,"archived":false,"pushed_at":"2026-06-05","license_spdx":null,"org_owned":false,"default_branch":"main","contributors":0,"tree_truncated":false,"test_files":35,"workflows":1,"other_ci":false,"readme_bytes":33121,"docs_files":0,"changelog":false,"license_file":false,"dts_files":8,"ci_status":"success","ci_run_at":"2026-06-05","ci_workflow":"CI","ci_event":"push","readme_path":"README.md","readme_sha":"4f37132e22257fb15e83778a5031d4d92b50bee6","months_since_release":5,"dormant":false,"repository_directory":null,"dependencies":0,"node_engine":">=18","downloads_monthly":8,"created_at":"2026-03-30T18:29:42Z","open_prs":0,"first_commit":"2026-03-29","last_commit":"2026-06-05","commits":21,"repo_read":"2026-10-26"},"release_notes":{"license":"Apache-2.0","quotable":true,"versions":[{"version":"1.0.2","date":"2026-06-05"},{"version":"1.0.1","date":"2026-05-11"},{"version":"1.0.0","date":"2026-05-11"}],"crawled_at":"2026-10-25"},"edges":{"maintainers":[{"ref":"actor:sleibach","name":"Sören Leibach","json":"/actors/sleibach.json"}],"contributors":[],"explained_by":[],"alternative_to_this":[],"needed_by":[],"available_via":[],"parts":[],"add_ons":[],"answered_through_parts":[]}}