Skip to content

Conversation

emdashcodes
Copy link
Contributor

@emdashcodes emdashcodes commented Sep 5, 2025

Closes #41

This PR introduces a client library that provides access to abilities registered on the server, making it easy to discover and execute WordPress abilities from JavaScript code.

  • getAbilities() - Fetches all available abilities
  • getAbility(name) - Retrieves a specific ability by name
  • executeAbility(name, input) - Executes abilities

In addition to the client API, this also includes automatic script registration when used as a plugin and a helper function for Composer-based installations. The API is exposed in the wp.abilities namespace when enqueued.

It consolidates other aspects of client-side registry work for Abilities API:

Usage Example

const {getAbilities, executeAbility } = wp.abilities;

// Getall abilities
const abilities = await getAbilities();

// Execute an ability
const result = await executeAbility('my-plugin/create-post', {
    title: 'New Post',
    content: 'Post content'
});

Testing

cd packages/client

# Install dependencies
npm install

# Build the package
npm run build
# or npm run build:client from root

I recommend registering at least 1 tool and 1 resource, here are 2 dummy ones that I will use in the examples below: https://gist.github.com/emdashcodes/a2f2d2fe440d52d9736fac4ab535cef8

Open the developer console and paste the following:

await wp.abilities.getAbilities()

You should see both abilities (and any others you registered) listed.

Paste in the following:

await wp.abilities.executeAbility('demo/get-pages', {
    limit: 5,
    orderby: 'title',
   order: 'ASC'
});

You should see a list of pages returned.

Paste in:

await wp.abilities.getAbility('demo/get-pages');

See that info for the demo/get-pages ability is called.

Finally, paste in the following:

await wp.abilities.executeAbility('demo/create-post', {
    title: 'Test Post from Abilities API',
    content: '<p>This post was created using the WordPress Abilities API!</p>',
    excerpt: 'A test post created via the browser console',
    status: 'draft'
});

See that a demo draft post has been created.

@emdashcodes emdashcodes self-assigned this Sep 5, 2025
Copy link

github-actions bot commented Sep 5, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: emdashcodes <[email protected]>
Co-authored-by: gziolo <[email protected]>
Co-authored-by: justlevine <[email protected]>
Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: jsnajdr <[email protected]>
Co-authored-by: t-hamano <[email protected]>
Co-authored-by: youknowriad <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@emdashcodes emdashcodes changed the title [WIP] Implement client-side package for Abilities API Add client-side package for Abilities API Sep 5, 2025
@gziolo gziolo added the [Type] Enhancement New feature or request label Sep 5, 2025
@gziolo gziolo added this to the WP 6.9 beta milestone Sep 5, 2025
Copy link

codecov bot commented Sep 5, 2025

Codecov Report

❌ Patch coverage is 79.52756% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.84%. Comparing base (2f3cda7) to head (dea7b40).
⚠️ Report is 1 commits behind head on trunk.

Files with missing lines Patch % Lines
includes/assets/class-wp-abilities-assets-init.php 0.00% 31 Missing ⚠️
packages/client/src/api.ts 87.27% 7 Missing ⚠️
includes/bootstrap.php 0.00% 5 Missing ⚠️
packages/client/src/validation.ts 94.31% 5 Missing ⚠️
packages/client/src/store/reducer.ts 89.65% 3 Missing ⚠️
packages/client/src/store/resolvers.ts 92.85% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk      #60      +/-   ##
============================================
- Coverage     87.45%   84.84%   -2.61%     
- Complexity       95      102       +7     
============================================
  Files             8       16       +8     
  Lines           518      772     +254     
  Branches          0       85      +85     
============================================
+ Hits            453      655     +202     
- Misses           65      117      +52     
Flag Coverage Δ
javascript 92.66% <92.66%> (?)
unit 81.76% <0.00%> (-5.69%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@emdashcodes emdashcodes added the props-bot Manually triggers Props Bot to ensure the list of props is up to date. label Sep 5, 2025
@github-actions github-actions bot removed the props-bot Manually triggers Props Bot to ensure the list of props is up to date. label Sep 5, 2025
@gziolo gziolo removed this from the WP 6.9 beta milestone Sep 9, 2025
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't like that we're muddying up the global namespace unnecessarily, or that this file is nested inside includes/abilities-api. Also it's breaks the current repo CS.

IMO, either this should be moved up into includes/* or the plugin-specific needs should be sanitized out of the WordPress-core needs into a class-assets-init.php or similar (following the pattern established in class-wp-rest-abilities-init.php)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might leave a similar feedback in another PR. This will stay local, similar to WP_REST_Abilities_Init. So maybe in both cases, the name shouldn't be prefixed with wp, but in this PR it's fine to change the newly proposed name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In dce8f8c, I have moved these from the global namespace and created a WP_Abilities_Assets_Init class.

emdashcodes and others added 13 commits September 24, 2025 13:30
* Client only abilities

* fix: Add proper server ability validation in registerAbility

* feat: Add ClientAbility and ServerAbility types for better type safety

* refactor: Remove location field, use callback presence for execution type

* feat: add permissionCallback on the client

* update: check if ability is already registered in registerAbility

* fix: wrap client errors in i18n

* Add client library tests and code quality infrastructure  (#70)

* Add testing & linting infa

* Remove uncessary combined checks

* Add client tests

* test: Use proper TypeScript types instead of 'as any' in tests

* Fix typescript check for extra server check

* test: update tests for client-only abilities API changes

* chore: add build:client and dev:client scripts to root package.json

* fix: formatting

* fix: prettier issues

* fix: apply Prettier formatting to README and validation.ts

* deps: remove 		@types/wordpress__api-fetch since types are already shipped

* fix: move validation to the store

* fix: validate ability name uses same format as server

* tests: clean up CI checks

* deps: fix dev dependencies and remove extra package-lock.json

* fix: rename main dev and build commands

* fix: better match server validation rules for schema

* Fix remaining validation issues

Co-authored-by: emdashcodes <[email protected]>
Co-authored-by: gziolo <[email protected]>
Co-authored-by: justlevine <[email protected]>
Co-authored-by: jonathanbossenger <[email protected]>

---------

Co-authored-by: emdashcodes <[email protected]>
Co-authored-by: gziolo <[email protected]>
Co-authored-by: justlevine <[email protected]>
Co-authored-by: jonathanbossenger <[email protected]>
Co-authored-by: swissspidy <[email protected]>
@gziolo
Copy link
Member

gziolo commented Sep 24, 2025

I took this branch for a spin and identified a few issues that I tried to fix. First, I rebased the branch with the latest changes from trunk.

Next, I covered in 17dfa62:

  • Updated most of the depenndecies to the latest version and removed these that are referenced in the code. I still would like to understand why ajv-draft-04 is not referenced in the code, but instead, there is ajv in use, which has a larger size.
  • Fixed security issues reported by npm audit fix. There are still 47 remaining warnings, but they can be only fixed upstream in Gutenberg.
  • Executed npm dedupe to ensure npm packages are only installed in the top-level node_modules folder.

Next, I noticed some issues with the Prettier config when running npm run lint:js, which I resolved with a42c858. This also required npm run format, which updated several files to adhere to WordPress coding style conventions.

@gziolo
Copy link
Member

gziolo commented Sep 24, 2025

I also tested the following commands and they worked correctly with my latest changes applied:

  • npm run plugin-zip
  • npm run build
  • npm run dev
  • npm run clean
  • npm run format
  • npm run lint:js
  • npm run lint:js:fix
  • npm run lint:all
  • npm run typecheck
  • npm run test:client
  • npm run test:client:watch
  • npm run test:client:coverage
  • npm run test:all
  • npm run wp-env

The only task left for me that I have to perform later is ensuring that everything works correctly in the browser. In the long run, we might want to introduce some form of e2e testing with wp-script test-playwright. However, that can be added progressively as we work on adding abilities as discussed in #52.

@emdashcodes
Copy link
Contributor Author

Updated most of the depenndecies to the latest version and removed these that are referenced in the code. I still would like to understand why ajv-draft-04 is not referenced in the code, but instead, there is ajv in use, which has a larger size.

Thanks, I believe I messed up the import somewhere along the way, but ajv-draft-04 is what we actually want to be importing from the validation code.

via https://github.com/ajv-validator/ajv-draft-04

You need to install both ajv and this package (to allow upgrading ajv without upgrading this package):

npm i ajv ajv-draft-04
import Ajv from "ajv-draft-04"
const ajv = new Ajv()

I also cleaned up the two jest dependencies you noted.

@gziolo
Copy link
Member

gziolo commented Sep 25, 2025

I followed the testing instructions included in the description of this PR. This is the result:

Screenshot 2025-09-25 at 12 40 20 Screenshot 2025-09-25 at 12 40 47 Screenshot 2025-09-25 at 12 43 44 Screenshot 2025-09-25 at 12 42 11

I also performed an additional test to validate what happens when an invalid ability name is requested:

Screenshot 2025-09-25 at 12 41 20 Screenshot 2025-09-25 at 12 47 54

Everything works as expected. My only feedback is that the REST API structure gets propagated to the list of abilities registered on the server. It's reflected in the presence of _links for individual abilities. It's rather something we should filter out from the ability objects.

@gziolo
Copy link
Member

gziolo commented Sep 25, 2025

I followed the instructions from #69 to see how things change when there are also abilities registered on the client. Everything works correctly.

Screenshot 2025-09-25 at 12 57 20 Screenshot 2025-09-25 at 13 00 43

The only nitpick I have is that I still see location: 'client' in the ability object.

I can confirm that it isn't possible to register an ability using the same name twice. It is possible to unregister an ability (even when registered on the server), and register it again on the client.


The only remaining low-priority task would be aligning the shapes of abilities between the server-side representation and client-side representation. Namely, obsolete _links in the abilities coming from the server, and obsolete location for the abilities coming from the client.

A separate story is to coninue discussing whether some follow-up work would be required after landing this PR to optimize the abilities data store's design as discussed in #60 (comment).

@emdashcodes
Copy link
Contributor Author

The only nitpick I have is that I still see location: 'client' in the ability object.

This was coming from my example abilities and not the code itself, but I agree invalid fields should be stripped alongside things like _links from the server response.

I added some code for this and tests in
dea7b40

@gziolo gziolo merged commit 4beab25 into trunk Sep 26, 2025
19 of 21 checks passed
@gziolo gziolo deleted the add/client-library branch September 26, 2025 04:28
@github-project-automation github-project-automation bot moved this from Needs review to Done in WordPress Abilities API: planning Sep 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Enhancement New feature or request
Projects
Development

Successfully merging this pull request may close these issues.

Implement client-side integration for abilities registered on the server
6 participants