@wordpress/abilities 0.1.1-next.dc3f6d3c1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE.md +788 -0
  3. package/README.md +302 -0
  4. package/build/api.js +189 -0
  5. package/build/api.js.map +7 -0
  6. package/build/index.js +53 -0
  7. package/build/index.js.map +7 -0
  8. package/build/store/actions.js +183 -0
  9. package/build/store/actions.js.map +7 -0
  10. package/build/store/constants.js +58 -0
  11. package/build/store/constants.js.map +7 -0
  12. package/build/store/index.js +77 -0
  13. package/build/store/index.js.map +7 -0
  14. package/build/store/reducer.js +126 -0
  15. package/build/store/reducer.js.map +7 -0
  16. package/build/store/resolvers.js +107 -0
  17. package/build/store/resolvers.js.map +7 -0
  18. package/build/store/selectors.js +64 -0
  19. package/build/store/selectors.js.map +7 -0
  20. package/build/store/tests/actions.test.js +792 -0
  21. package/build/store/tests/actions.test.js.map +7 -0
  22. package/build/store/tests/reducer.test.js +743 -0
  23. package/build/store/tests/reducer.test.js.map +7 -0
  24. package/build/store/tests/resolvers.test.js +520 -0
  25. package/build/store/tests/resolvers.test.js.map +7 -0
  26. package/build/store/tests/selectors.test.js +349 -0
  27. package/build/store/tests/selectors.test.js.map +7 -0
  28. package/build/tests/api.test.js +546 -0
  29. package/build/tests/api.test.js.map +7 -0
  30. package/build/tests/validation.test.js +453 -0
  31. package/build/tests/validation.test.js.map +7 -0
  32. package/build/types.js +19 -0
  33. package/build/types.js.map +7 -0
  34. package/build/validation.js +144 -0
  35. package/build/validation.js.map +7 -0
  36. package/build-module/api.js +146 -0
  37. package/build-module/api.js.map +7 -0
  38. package/build-module/index.js +28 -0
  39. package/build-module/index.js.map +7 -0
  40. package/build-module/store/actions.js +161 -0
  41. package/build-module/store/actions.js.map +7 -0
  42. package/build-module/store/constants.js +24 -0
  43. package/build-module/store/constants.js.map +7 -0
  44. package/build-module/store/index.js +47 -0
  45. package/build-module/store/index.js.map +7 -0
  46. package/build-module/store/reducer.js +112 -0
  47. package/build-module/store/reducer.js.map +7 -0
  48. package/build-module/store/resolvers.js +84 -0
  49. package/build-module/store/resolvers.js.map +7 -0
  50. package/build-module/store/selectors.js +36 -0
  51. package/build-module/store/selectors.js.map +7 -0
  52. package/build-module/store/tests/actions.test.js +804 -0
  53. package/build-module/store/tests/actions.test.js.map +7 -0
  54. package/build-module/store/tests/reducer.test.js +726 -0
  55. package/build-module/store/tests/reducer.test.js.map +7 -0
  56. package/build-module/store/tests/resolvers.test.js +523 -0
  57. package/build-module/store/tests/resolvers.test.js.map +7 -0
  58. package/build-module/store/tests/selectors.test.js +352 -0
  59. package/build-module/store/tests/selectors.test.js.map +7 -0
  60. package/build-module/tests/api.test.js +530 -0
  61. package/build-module/tests/api.test.js.map +7 -0
  62. package/build-module/tests/validation.test.js +451 -0
  63. package/build-module/tests/validation.test.js.map +7 -0
  64. package/build-module/types.js +1 -0
  65. package/build-module/types.js.map +7 -0
  66. package/build-module/validation.js +109 -0
  67. package/build-module/validation.js.map +7 -0
  68. package/build-types/api.d.ts +138 -0
  69. package/build-types/api.d.ts.map +1 -0
  70. package/build-types/index.d.ts +41 -0
  71. package/build-types/index.d.ts.map +1 -0
  72. package/build-types/store/actions.d.ts +76 -0
  73. package/build-types/store/actions.d.ts.map +1 -0
  74. package/build-types/store/constants.d.ts +14 -0
  75. package/build-types/store/constants.d.ts.map +1 -0
  76. package/build-types/store/index.d.ts +7 -0
  77. package/build-types/store/index.d.ts.map +1 -0
  78. package/build-types/store/reducer.d.ts +22 -0
  79. package/build-types/store/reducer.d.ts.map +1 -0
  80. package/build-types/store/resolvers.d.ts +47 -0
  81. package/build-types/store/resolvers.d.ts.map +1 -0
  82. package/build-types/store/selectors.d.ts +37 -0
  83. package/build-types/store/selectors.d.ts.map +1 -0
  84. package/build-types/types.d.ts +163 -0
  85. package/build-types/types.d.ts.map +1 -0
  86. package/build-types/validation.d.ts +23 -0
  87. package/build-types/validation.d.ts.map +1 -0
  88. package/package.json +52 -0
  89. package/src/api.ts +339 -0
  90. package/src/index.ts +65 -0
  91. package/src/store/actions.ts +247 -0
  92. package/src/store/constants.ts +15 -0
  93. package/src/store/index.ts +55 -0
  94. package/src/store/reducer.ts +184 -0
  95. package/src/store/resolvers.ts +151 -0
  96. package/src/store/selectors.ts +82 -0
  97. package/src/store/tests/actions.test.ts +950 -0
  98. package/src/store/tests/reducer.test.ts +861 -0
  99. package/src/store/tests/resolvers.test.ts +642 -0
  100. package/src/store/tests/selectors.test.ts +412 -0
  101. package/src/tests/api.test.ts +655 -0
  102. package/src/tests/validation.test.ts +532 -0
  103. package/src/types.ts +190 -0
  104. package/src/validation.ts +207 -0
package/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # WordPress Abilities API Client
2
+
3
+ Client library for the WordPress Abilities API, providing a standardized way to discover and execute WordPress capabilities.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [API Reference](#api-reference)
10
+ - [Development and Testing](#development-and-testing)
11
+
12
+ ## Installation
13
+
14
+ The client is currently available as a part of the Composer package.
15
+
16
+ ### As a WordPress Script
17
+
18
+ When the Abilities API is installed, the client is automatically registered and enqueue in the admin.
19
+
20
+ ## Usage
21
+
22
+ ```javascript
23
+ // In your WordPress plugin or theme JavaScript
24
+ const { getAbilities, getAbility, executeAbility } = wp.abilities;
25
+ // or import { getAbilities, getAbility, executeAbility } from '@wordpress/abilities'; depending on your setup
26
+
27
+ // Get all abilities
28
+ const abilities = await getAbilities();
29
+
30
+ // Get a specific ability
31
+ const ability = await getAbility( 'my-plugin/my-ability' );
32
+
33
+ // Execute an ability
34
+ const result = await executeAbility( 'my-plugin/my-ability', {
35
+ param1: 'value1',
36
+ param2: 'value2',
37
+ } );
38
+ ```
39
+
40
+ ### Using with React and WordPress Data
41
+
42
+ The client includes a data store that integrates with `@wordpress/data` for use in React components:
43
+
44
+ ```javascript
45
+ import { useSelect } from '@wordpress/data';
46
+ import { store as abilitiesStore } from '@wordpress/abilities';
47
+
48
+ function MyComponent() {
49
+ const abilities = useSelect(
50
+ ( select ) => select( abilitiesStore ).getAbilities(),
51
+ []
52
+ );
53
+
54
+ const specificAbility = useSelect(
55
+ ( select ) =>
56
+ select( abilitiesStore ).getAbility( 'my-plugin/my-ability' ),
57
+ []
58
+ );
59
+
60
+ return (
61
+ <div>
62
+ <h2>All Abilities</h2>
63
+ <ul>
64
+ { abilities.map( ( ability ) => (
65
+ <li key={ ability.name }>
66
+ <strong>{ ability.label }</strong>:{ ' ' }
67
+ { ability.description }
68
+ </li>
69
+ ) ) }
70
+ </ul>
71
+ </div>
72
+ );
73
+ }
74
+ ```
75
+
76
+ ## API Reference
77
+
78
+ ### Functions
79
+
80
+ #### `getAbilities( args: AbilitiesQueryArgs = {} ): Promise<Ability[]>`
81
+
82
+ Returns all registered abilities. Optionally filter by category slug. Automatically handles pagination to fetch all abilities across multiple pages if needed.
83
+
84
+ ```javascript
85
+ // Get all abilities
86
+ const abilities = await getAbilities();
87
+ console.log( `Found ${ abilities.length } abilities` );
88
+
89
+ // Get abilities in a specific category
90
+ const dataAbilities = await getAbilities( { category: 'data-retrieval' } );
91
+ console.log( `Found ${ dataAbilities.length } data retrieval abilities` );
92
+ ```
93
+
94
+ #### `getAbility( name: string ): Promise<Ability | null>`
95
+
96
+ Returns a specific ability by name, or null if not found.
97
+
98
+ ```javascript
99
+ const ability = await getAbility( 'my-plugin/create-post' );
100
+ if ( ability ) {
101
+ console.log( `Found ability: ${ ability.label }` );
102
+ }
103
+ ```
104
+
105
+ #### `getAbilityCategories(): Promise<AbilityCategory[]>`
106
+
107
+ Returns all registered ability categories. Categories are used to organize abilities into logical groups.
108
+
109
+ ```javascript
110
+ const categories = await getAbilityCategories();
111
+ console.log( `Found ${ categories.length } categories` );
112
+
113
+ categories.forEach( ( category ) => {
114
+ console.log( `${ category.label }: ${ category.description }` );
115
+ } );
116
+ ```
117
+
118
+ #### `getAbilityCategory( slug: string ): Promise<AbilityCategory | null>`
119
+
120
+ Returns a specific ability category by slug, or null if not found.
121
+
122
+ ```javascript
123
+ const category = await getAbilityCategory( 'data-retrieval' );
124
+ if ( category ) {
125
+ console.log( `Found category: ${ category.label }` );
126
+ console.log( `Description: ${ category.description }` );
127
+ }
128
+ ```
129
+
130
+ #### `registerAbility( ability: Ability ): Promise<void>`
131
+
132
+ Registers a client-side ability. Client abilities are executed locally in the browser and must include a callback function and a valid category.
133
+
134
+ ```javascript
135
+ import { registerAbility } from '@wordpress/abilities';
136
+
137
+ await registerAbility( {
138
+ name: 'my-plugin/navigate',
139
+ label: 'Navigate to URL',
140
+ description: 'Navigates to a URL within WordPress admin',
141
+ category: 'navigation',
142
+ input_schema: {
143
+ type: 'object',
144
+ properties: {
145
+ url: { type: 'string' },
146
+ },
147
+ required: [ 'url' ],
148
+ },
149
+ callback: async ( { url } ) => {
150
+ window.location.href = url;
151
+ return { success: true };
152
+ },
153
+ } );
154
+ ```
155
+
156
+ #### `unregisterAbility( name: string ): void`
157
+
158
+ Unregisters a client-side ability from the store.
159
+
160
+ ```javascript
161
+ import { unregisterAbility } from '@wordpress/abilities';
162
+
163
+ unregisterAbility( 'my-plugin/navigate' );
164
+ ```
165
+
166
+ #### `registerAbilityCategory( slug: string, args: AbilityCategoryArgs ): Promise<void>`
167
+
168
+ Registers a client-side ability category. This is useful when registering client-side abilities that introduce new categories not defined by the server.
169
+
170
+ ```javascript
171
+ import { registerAbilityCategory } from '@wordpress/abilities';
172
+
173
+ // Register a new category
174
+ await registerAbilityCategory( 'block-editor', {
175
+ label: 'Block Editor',
176
+ description: 'Abilities for interacting with the WordPress block editor',
177
+ } );
178
+
179
+ // Register a category with optional metadata
180
+ await registerAbilityCategory( 'custom-category', {
181
+ label: 'Custom Category',
182
+ description: 'A category for custom abilities',
183
+ meta: {
184
+ color: '#ff0000',
185
+ },
186
+ } );
187
+
188
+ // Then register abilities using the new category
189
+ await registerAbility( {
190
+ name: 'my-plugin/insert-block',
191
+ label: 'Insert Block',
192
+ description: 'Inserts a block into the editor',
193
+ category: 'block-editor', // Uses the client-registered category
194
+ callback: async ( { blockType } ) => {
195
+ // Implementation
196
+ return { success: true };
197
+ },
198
+ } );
199
+ ```
200
+
201
+ #### `unregisterAbilityCategory( slug: string ): void`
202
+
203
+ Unregisters an ability category from the store.
204
+
205
+ ```javascript
206
+ import { unregisterAbilityCategory } from '@wordpress/abilities';
207
+
208
+ unregisterAbilityCategory( 'block-editor' );
209
+ ```
210
+
211
+ #### `executeAbility( name: string, input?: Record<string, any> ): Promise<any>`
212
+
213
+ Executes an ability with optional input parameters. The HTTP method is automatically determined based on the ability's annotations:
214
+
215
+ - `readonly` abilities use GET (read-only operations)
216
+ - regular abilities use POST (write operations)
217
+
218
+ ```javascript
219
+ // Execute a read-only ability (GET)
220
+ const data = await executeAbility( 'my-plugin/get-data', {
221
+ id: 123,
222
+ } );
223
+
224
+ // Execute a regular ability (POST)
225
+ const result = await executeAbility( 'my-plugin/create-item', {
226
+ title: 'New Item',
227
+ content: 'Item content',
228
+ } );
229
+ ```
230
+
231
+ ### Store Selectors
232
+
233
+ When using with `@wordpress/data`:
234
+
235
+ - `getAbilities( args: AbilitiesQueryArgs = {} )` - Returns all abilities from the store, optionally filtered by query arguments
236
+ - `getAbility( name: string )` - Returns a specific ability from the store
237
+ - `getAbilityCategories()` - Returns all categories from the store
238
+ - `getAbilityCategory( slug: string )` - Returns a specific category from the store
239
+
240
+ ```javascript
241
+ import { useSelect } from '@wordpress/data';
242
+ import { store as abilitiesStore } from '@wordpress/abilities';
243
+
244
+ function MyComponent() {
245
+ // Get all abilities
246
+ const allAbilities = useSelect(
247
+ ( select ) => select( abilitiesStore ).getAbilities(),
248
+ []
249
+ );
250
+
251
+ // Get all categories
252
+ const categories = useSelect(
253
+ ( select ) => select( abilitiesStore ).getAbilityCategories(),
254
+ []
255
+ );
256
+
257
+ // Get abilities in a specific category
258
+ const dataAbilities = useSelect(
259
+ ( select ) =>
260
+ select( abilitiesStore ).getAbilities( {
261
+ category: 'data-retrieval',
262
+ } ),
263
+ []
264
+ );
265
+
266
+ // Get a specific category
267
+ const dataCategory = useSelect(
268
+ ( select ) =>
269
+ select( abilitiesStore ).getAbilityCategory( 'data-retrieval' ),
270
+ []
271
+ );
272
+
273
+ return (
274
+ <div>
275
+ <h2>All Abilities ({ allAbilities.length })</h2>
276
+ <h2>Categories ({ categories.length })</h2>
277
+ <ul>
278
+ { categories.map( ( category ) => (
279
+ <li key={ category.slug }>
280
+ <strong>{ category.label }</strong>:{ ' ' }
281
+ { category.description }
282
+ </li>
283
+ ) ) }
284
+ </ul>
285
+ <h2>{ dataCategory?.label } Abilities</h2>
286
+ <ul>
287
+ { dataAbilities.map( ( ability ) => (
288
+ <li key={ ability.name }>{ ability.label }</li>
289
+ ) ) }
290
+ </ul>
291
+ </div>
292
+ );
293
+ }
294
+ ```
295
+
296
+ ## Contributing to this package
297
+
298
+ This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects.
299
+
300
+ To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md).
301
+
302
+ <br /><br /><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p>
package/build/api.js ADDED
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // packages/abilities/src/api.ts
31
+ var api_exports = {};
32
+ __export(api_exports, {
33
+ executeAbility: () => executeAbility,
34
+ getAbilities: () => getAbilities,
35
+ getAbility: () => getAbility,
36
+ getAbilityCategories: () => getAbilityCategories,
37
+ getAbilityCategory: () => getAbilityCategory,
38
+ registerAbility: () => registerAbility,
39
+ registerAbilityCategory: () => registerAbilityCategory,
40
+ unregisterAbility: () => unregisterAbility,
41
+ unregisterAbilityCategory: () => unregisterAbilityCategory
42
+ });
43
+ module.exports = __toCommonJS(api_exports);
44
+ var import_data = require("@wordpress/data");
45
+ var import_api_fetch = __toESM(require("@wordpress/api-fetch"));
46
+ var import_url = require("@wordpress/url");
47
+ var import_i18n = require("@wordpress/i18n");
48
+ var import_store = require("./store");
49
+ var import_validation = require("./validation");
50
+ async function getAbilities(args = {}) {
51
+ return await (0, import_data.resolveSelect)(import_store.store).getAbilities(args);
52
+ }
53
+ async function getAbility(name) {
54
+ return await (0, import_data.resolveSelect)(import_store.store).getAbility(name);
55
+ }
56
+ async function getAbilityCategories() {
57
+ return await (0, import_data.resolveSelect)(import_store.store).getAbilityCategories();
58
+ }
59
+ async function getAbilityCategory(slug) {
60
+ return await (0, import_data.resolveSelect)(import_store.store).getAbilityCategory(slug);
61
+ }
62
+ async function registerAbility(ability) {
63
+ await (0, import_data.dispatch)(import_store.store).registerAbility(ability);
64
+ }
65
+ function unregisterAbility(name) {
66
+ (0, import_data.dispatch)(import_store.store).unregisterAbility(name);
67
+ }
68
+ async function registerAbilityCategory(slug, args) {
69
+ await (0, import_data.dispatch)(import_store.store).registerAbilityCategory(slug, args);
70
+ }
71
+ function unregisterAbilityCategory(slug) {
72
+ (0, import_data.dispatch)(import_store.store).unregisterAbilityCategory(slug);
73
+ }
74
+ async function executeClientAbility(ability, input) {
75
+ if (!ability.callback) {
76
+ throw new Error(
77
+ (0, import_i18n.sprintf)(
78
+ "Client ability %s is missing callback function",
79
+ ability.name
80
+ )
81
+ );
82
+ }
83
+ if (ability.permissionCallback) {
84
+ const hasPermission = await ability.permissionCallback(input);
85
+ if (!hasPermission) {
86
+ const error = new Error(
87
+ (0, import_i18n.sprintf)("Permission denied for ability: %s", ability.name)
88
+ );
89
+ error.code = "ability_permission_denied";
90
+ throw error;
91
+ }
92
+ }
93
+ if (ability.input_schema) {
94
+ const inputValidation = (0, import_validation.validateValueFromSchema)(
95
+ input,
96
+ ability.input_schema,
97
+ "input"
98
+ );
99
+ if (inputValidation !== true) {
100
+ const error = new Error(
101
+ (0, import_i18n.sprintf)(
102
+ 'Ability "%1$s" has invalid input. Reason: %2$s',
103
+ ability.name,
104
+ inputValidation
105
+ )
106
+ );
107
+ error.code = "ability_invalid_input";
108
+ throw error;
109
+ }
110
+ }
111
+ let result;
112
+ try {
113
+ result = await ability.callback(input);
114
+ } catch (error) {
115
+ console.error(
116
+ `Error executing client ability ${ability.name}:`,
117
+ error
118
+ );
119
+ throw error;
120
+ }
121
+ if (ability.output_schema) {
122
+ const outputValidation = (0, import_validation.validateValueFromSchema)(
123
+ result,
124
+ ability.output_schema,
125
+ "output"
126
+ );
127
+ if (outputValidation !== true) {
128
+ const error = new Error(
129
+ (0, import_i18n.sprintf)(
130
+ 'Ability "%1$s" has invalid output. Reason: %2$s',
131
+ ability.name,
132
+ outputValidation
133
+ )
134
+ );
135
+ error.code = "ability_invalid_output";
136
+ throw error;
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ async function executeServerAbility(ability, input) {
142
+ let method = "POST";
143
+ if (!!ability.meta?.annotations?.readonly) {
144
+ method = "GET";
145
+ } else if (!!ability.meta?.annotations?.destructive && !!ability.meta?.annotations?.idempotent) {
146
+ method = "DELETE";
147
+ }
148
+ let path = `/wp-abilities/v1/abilities/${ability.name}/run`;
149
+ const options = {
150
+ method
151
+ };
152
+ if (["GET", "DELETE"].includes(method) && input !== null) {
153
+ path = (0, import_url.addQueryArgs)(path, { input });
154
+ } else if (method === "POST" && input !== null) {
155
+ options.data = { input };
156
+ }
157
+ try {
158
+ return await (0, import_api_fetch.default)({
159
+ path,
160
+ ...options
161
+ });
162
+ } catch (error) {
163
+ console.error(`Error executing ability ${ability.name}:`, error);
164
+ throw error;
165
+ }
166
+ }
167
+ async function executeAbility(name, input) {
168
+ const ability = await getAbility(name);
169
+ if (!ability) {
170
+ throw new Error((0, import_i18n.sprintf)("Ability not found: %s", name));
171
+ }
172
+ if (ability.callback) {
173
+ return executeClientAbility(ability, input);
174
+ }
175
+ return executeServerAbility(ability, input);
176
+ }
177
+ // Annotate the CommonJS export names for ESM import in node:
178
+ 0 && (module.exports = {
179
+ executeAbility,
180
+ getAbilities,
181
+ getAbility,
182
+ getAbilityCategories,
183
+ getAbilityCategory,
184
+ registerAbility,
185
+ registerAbilityCategory,
186
+ unregisterAbility,
187
+ unregisterAbilityCategory
188
+ });
189
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/api.ts"],
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { dispatch, resolveSelect } from '@wordpress/data';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\nimport { sprintf } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport { store } from './store';\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityCategoryArgs,\n\tAbilitiesQueryArgs,\n\tAbilityInput,\n\tAbilityOutput,\n} from './types';\nimport { validateValueFromSchema } from './validation';\n\n/**\n * Get all available abilities with optional filtering.\n *\n * @param args Optional query arguments to filter. Defaults to empty object.\n * @return Promise resolving to array of abilities.\n */\nexport async function getAbilities(\n\targs: AbilitiesQueryArgs = {}\n): Promise< Ability[] > {\n\treturn await resolveSelect( store ).getAbilities( args );\n}\n\n/**\n * Get a specific ability by name.\n *\n * @param name The ability name.\n * @return Promise resolving to the ability or null if not found.\n */\nexport async function getAbility( name: string ): Promise< Ability | null > {\n\treturn await resolveSelect( store ).getAbility( name );\n}\n\n/**\n * Get all available ability categories.\n *\n * @return Promise resolving to array of categories.\n */\nexport async function getAbilityCategories(): Promise< AbilityCategory[] > {\n\treturn await resolveSelect( store ).getAbilityCategories();\n}\n\n/**\n * Get a specific ability category by slug.\n *\n * @param slug The category slug.\n * @return Promise resolving to the category or null if not found.\n */\nexport async function getAbilityCategory(\n\tslug: string\n): Promise< AbilityCategory | null > {\n\treturn await resolveSelect( store ).getAbilityCategory( slug );\n}\n\n/**\n * Register a client-side ability.\n *\n * Client abilities are executed locally in the browser and must include\n * a callback function. The ability will be validated by the store action,\n * and an error will be thrown if validation fails.\n *\n * Categories will be automatically fetched from the REST API if they\n * haven't been loaded yet, so you don't need to call getAbilityCategories()\n * before registering abilities.\n *\n * @param ability The ability definition including callback.\n * @return Promise that resolves when registration is complete.\n * @throws {Error} If the ability fails validation.\n *\n * @example\n * ```js\n * await registerAbility({\n * name: 'my-plugin/navigate',\n * label: 'Navigate to URL',\n * description: 'Navigates to a URL within WordPress admin',\n * category: 'navigation',\n * input_schema: {\n * type: 'object',\n * properties: {\n * url: { type: 'string' }\n * },\n * required: ['url']\n * },\n * callback: async ({ url }) => {\n * window.location.href = url;\n * return { success: true };\n * }\n * });\n * ```\n */\nexport async function registerAbility( ability: Ability ): Promise< void > {\n\tawait dispatch( store ).registerAbility( ability );\n}\n\n/**\n * Unregister an ability from the store.\n *\n * Remove a client-side ability from the store.\n * Note: This will return an error for server-side abilities.\n *\n * @param name The ability name to unregister.\n */\nexport function unregisterAbility( name: string ): void {\n\tdispatch( store ).unregisterAbility( name );\n}\n\n/**\n * Register a client-side ability category.\n *\n * Categories registered on the client are stored alongside server-side categories\n * in the same store and can be used when registering client side abilities.\n * This is useful when registering client-side abilities that introduce new\n * categories not defined by the server.\n *\n * Categories will be automatically fetched from the REST API if they haven't been\n * loaded yet to check for duplicates against server-side categories.\n *\n * @param slug Category slug (lowercase alphanumeric with dashes only).\n * @param args Category arguments (label, description, optional meta).\n * @return Promise that resolves when registration is complete.\n * @throws {Error} If the category fails validation.\n *\n * @example\n * ```js\n * // Register a new category for block editor abilities\n * await registerAbilityCategory('block-editor', {\n * label: 'Block Editor',\n * description: 'Abilities for interacting with the WordPress block editor'\n * });\n *\n * // Then register abilities using this category\n * await registerAbility({\n * name: 'my-plugin/insert-block',\n * label: 'Insert Block',\n * description: 'Inserts a block into the editor',\n * category: 'block-editor',\n * callback: async ({ blockType }) => {\n * // Implementation\n * return { success: true };\n * }\n * });\n * ```\n */\nexport async function registerAbilityCategory(\n\tslug: string,\n\targs: AbilityCategoryArgs\n): Promise< void > {\n\tawait dispatch( store ).registerAbilityCategory( slug, args );\n}\n\n/**\n * Unregister an ability category.\n *\n * Removes a category from the store.\n *\n * @param slug The category slug to unregister.\n *\n * @example\n * ```js\n * unregisterAbilityCategory('block-editor');\n * ```\n */\nexport function unregisterAbilityCategory( slug: string ): void {\n\tdispatch( store ).unregisterAbilityCategory( slug );\n}\n\n/**\n * Execute a client-side ability.\n *\n * @param ability The ability to execute.\n * @param input Input parameters for the ability.\n * @return Promise resolving to the ability execution result.\n * @throws Error if validation fails or execution errors.\n */\nasync function executeClientAbility(\n\tability: Ability,\n\tinput: AbilityInput\n): Promise< AbilityOutput > {\n\tif ( ! ability.callback ) {\n\t\tthrow new Error(\n\t\t\tsprintf(\n\t\t\t\t'Client ability %s is missing callback function',\n\t\t\t\tability.name\n\t\t\t)\n\t\t);\n\t}\n\n\t// Check permission callback if defined\n\tif ( ability.permissionCallback ) {\n\t\tconst hasPermission = await ability.permissionCallback( input );\n\t\tif ( ! hasPermission ) {\n\t\t\tconst error = new Error(\n\t\t\t\tsprintf( 'Permission denied for ability: %s', ability.name )\n\t\t\t);\n\t\t\t( error as any ).code = 'ability_permission_denied';\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tif ( ability.input_schema ) {\n\t\tconst inputValidation = validateValueFromSchema(\n\t\t\tinput,\n\t\t\tability.input_schema,\n\t\t\t'input'\n\t\t);\n\t\tif ( inputValidation !== true ) {\n\t\t\tconst error = new Error(\n\t\t\t\tsprintf(\n\t\t\t\t\t'Ability \"%1$s\" has invalid input. Reason: %2$s',\n\t\t\t\t\tability.name,\n\t\t\t\t\tinputValidation\n\t\t\t\t)\n\t\t\t);\n\t\t\t( error as any ).code = 'ability_invalid_input';\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tlet result: AbilityOutput;\n\ttry {\n\t\tresult = await ability.callback( input );\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error(\n\t\t\t`Error executing client ability ${ ability.name }:`,\n\t\t\terror\n\t\t);\n\t\tthrow error;\n\t}\n\n\tif ( ability.output_schema ) {\n\t\tconst outputValidation = validateValueFromSchema(\n\t\t\tresult,\n\t\t\tability.output_schema,\n\t\t\t'output'\n\t\t);\n\t\tif ( outputValidation !== true ) {\n\t\t\tconst error = new Error(\n\t\t\t\tsprintf(\n\t\t\t\t\t'Ability \"%1$s\" has invalid output. Reason: %2$s',\n\t\t\t\t\tability.name,\n\t\t\t\t\toutputValidation\n\t\t\t\t)\n\t\t\t);\n\t\t\t( error as any ).code = 'ability_invalid_output';\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Execute a server-side ability.\n *\n * @param ability The ability to execute.\n * @param input Input parameters for the ability.\n * @return Promise resolving to the ability execution result.\n * @throws Error if the API call fails.\n */\nasync function executeServerAbility(\n\tability: Ability,\n\tinput: AbilityInput\n): Promise< AbilityOutput > {\n\tlet method = 'POST';\n\tif ( !! ability.meta?.annotations?.readonly ) {\n\t\tmethod = 'GET';\n\t} else if (\n\t\t!! ability.meta?.annotations?.destructive &&\n\t\t!! ability.meta?.annotations?.idempotent\n\t) {\n\t\tmethod = 'DELETE';\n\t}\n\n\tlet path = `/wp-abilities/v1/abilities/${ ability.name }/run`;\n\tconst options: {\n\t\tmethod: string;\n\t\tdata?: { input: AbilityInput };\n\t} = {\n\t\tmethod,\n\t};\n\n\tif ( [ 'GET', 'DELETE' ].includes( method ) && input !== null ) {\n\t\t// For GET and DELETE requests, pass the input directly.\n\t\tpath = addQueryArgs( path, { input } );\n\t} else if ( method === 'POST' && input !== null ) {\n\t\toptions.data = { input };\n\t}\n\n\t// Note: Input and output validation happens on the server side for these abilities.\n\ttry {\n\t\treturn await apiFetch< AbilityOutput >( {\n\t\t\tpath,\n\t\t\t...options,\n\t\t} );\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( `Error executing ability ${ ability.name }:`, error );\n\t\tthrow error;\n\t}\n}\n\n/**\n * Execute an ability.\n *\n * Determines whether to execute locally (client abilities) or remotely (server abilities)\n * based on whether the ability has a callback function.\n *\n * @param name The ability name.\n * @param input Optional input parameters for the ability.\n * @return Promise resolving to the ability execution result.\n * @throws Error if the ability is not found or execution fails.\n */\nexport async function executeAbility(\n\tname: string,\n\tinput?: AbilityInput\n): Promise< AbilityOutput > {\n\tconst ability = await getAbility( name );\n\tif ( ! ability ) {\n\t\tthrow new Error( sprintf( 'Ability not found: %s', name ) );\n\t}\n\n\tif ( ability.callback ) {\n\t\treturn executeClientAbility( ability, input );\n\t}\n\n\treturn executeServerAbility( ability, input );\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["apiFetch"]
7
+ }
package/build/index.js ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // packages/abilities/src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ executeAbility: () => import_api.executeAbility,
24
+ getAbilities: () => import_api.getAbilities,
25
+ getAbility: () => import_api.getAbility,
26
+ getAbilityCategories: () => import_api.getAbilityCategories,
27
+ getAbilityCategory: () => import_api.getAbilityCategory,
28
+ registerAbility: () => import_api.registerAbility,
29
+ registerAbilityCategory: () => import_api.registerAbilityCategory,
30
+ store: () => import_store.store,
31
+ unregisterAbility: () => import_api.unregisterAbility,
32
+ unregisterAbilityCategory: () => import_api.unregisterAbilityCategory,
33
+ validateValueFromSchema: () => import_validation.validateValueFromSchema
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_api = require("./api");
37
+ var import_store = require("./store");
38
+ var import_validation = require("./validation");
39
+ // Annotate the CommonJS export names for ESM import in node:
40
+ 0 && (module.exports = {
41
+ executeAbility,
42
+ getAbilities,
43
+ getAbility,
44
+ getAbilityCategories,
45
+ getAbilityCategory,
46
+ registerAbility,
47
+ registerAbilityCategory,
48
+ store,
49
+ unregisterAbility,
50
+ unregisterAbilityCategory,
51
+ validateValueFromSchema
52
+ });
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["/**\n * WordPress Abilities API Client\n *\n * This package provides a client for interacting with the\n * WordPress Abilities API, allowing you to list, retrieve, and execute\n * abilities from client-side code.\n *\n * @package\n */\n\n/**\n * Public API functions\n */\nexport {\n\tgetAbilities,\n\tgetAbility,\n\tgetAbilityCategories,\n\tgetAbilityCategory,\n\texecuteAbility,\n\tregisterAbility,\n\tunregisterAbility,\n\tregisterAbilityCategory,\n\tunregisterAbilityCategory,\n} from './api';\n\n/**\n * The store can be used directly with @wordpress/data via selectors\n * in React components with useSelect.\n *\n * @example\n * ```js\n * import { useSelect } from '@wordpress/data';\n * import { store as abilitiesStore } from '@wordpress/abilities';\n *\n * function MyComponent() {\n * const abilities = useSelect(\n * (select) => select(abilitiesStore).getAbilities(),\n * []\n * );\n * // Use abilities...\n * }\n * ```\n */\nexport { store } from './store';\n\n/**\n * Type definitions\n */\nexport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityCategoryArgs,\n\tAbilitiesState,\n\tAbilitiesQueryArgs,\n\tAbilityCallback,\n\tPermissionCallback,\n\tAbilityInput,\n\tAbilityOutput,\n\tValidationError,\n} from './types';\n\n/**\n * Validation utilities\n */\nexport { validateValueFromSchema } from './validation';\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,iBAUO;AAoBP,mBAAsB;AAqBtB,wBAAwC;",
6
+ "names": []
7
+ }