diff --git a/README.md b/README.md index e257e7e..4762b71 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ - [Using Abilities](docs/4.using-abilities.md) - [REST API Reference](docs/5.rest-api.md) - [Hooks](docs/6.hooks.md) +- [JavaScript/TypeScript Client](docs/7.javascript-client.md) - [Contributing Guidelines](CONTRIBUTING.md) ## Inspiration diff --git a/docs/7.javascript-client.md b/docs/7.javascript-client.md new file mode 100644 index 0000000..2b4154c --- /dev/null +++ b/docs/7.javascript-client.md @@ -0,0 +1,186 @@ +# 7. JavaScript/TypeScript Client + +The JavaScript client provides an interface for discovering and executing WordPress Abilities from the browser. + +## Overview + +The JavaScript client enables frontend code to interact with the Abilities API system. It can: + +- Discover all registered abilities on your WordPress site +- Execute server-side PHP abilities +- Register and execute client-side JavaScript abilities + +You can read more about installation and setup in the [package readme](../packages/client/README.md). + +## Core API Functions + +### getAbilities() + +Returns an array of all registered abilities (both server-side and client-side). + +**Parameters:** None + +**Returns:** `Promise` - Array of ability objects + +**Example:** + +```javascript +const abilities = await getAbilities(); +console.log(`Found ${abilities.length} abilities`); + +// List all abilities +abilities.forEach(ability => { + console.log(`${ability.name}: ${ability.description}`); +}); +``` + +### getAbility(name) + +Retrieves a specific ability by name. + +**Parameters:** +- `name` (string) - The ability name (e.g., 'my-plugin/get-posts') + +**Returns:** `Promise` - The ability object or null if not found + +**Example:** + +```javascript +const ability = await getAbility('my-plugin/get-site-info'); +if (ability) { + console.log('Label:', ability.label); + console.log('Description:', ability.description); + console.log('Input Schema:', ability.input_schema); +} +``` + +### executeAbility(name, input) + +Executes an ability with the provided input data. + +**Parameters:** +- `name` (string) - The ability name +- `input` (object, optional) - Input data for the ability + +**Returns:** `Promise` - The ability's output + +**Example:** + +```javascript +// Execute without input +const siteTitle = await executeAbility('my-plugin/get-site-title'); +console.log('Site:', siteTitle); + +// Execute with input parameters +const posts = await executeAbility('my-plugin/get-posts', { + category: 'news', + limit: 5 +}); +posts.forEach(post => console.log(post.title)); +``` + +### registerAbility(ability) + +Registers a client-side ability that runs in the browser. + +**Parameters:** +- `ability` (object) - The ability configuration object + +**Returns:** `void` + +**Example:** + +```javascript +// showNotification function +const showNotification = (message) => { + new Notification(message); + return { success: true, displayed: message }; +} + +// Register a notification ability which calls the showNotification function +registerAbility({ + name: 'my-plugin/show-notification', + label: 'Show Notification', + description: 'Display a notification message to the user', + input_schema: { + type: 'object', + properties: { + message: { type: 'string' }, + type: { type: 'string', enum: ['success', 'error', 'warning', 'info'] } + }, + required: ['message'] + }, + callback: async ({ message, type = 'info' }) => { + // Show browser notification + if (!("Notification" in window)) { + alert("This browser does not support desktop notification"); + return { success: false, error: 'Browser does not support notifications' }; + } + if (Notification.permission !== 'granted') { + Notification.requestPermission().then((permission) => { + if (permission === "granted") { + return showNotification(message); + } + }); + } + return showNotification(message); + }, + permissionCallback: () => { + return !!wp.data.select('core').getCurrentUser(); + } +}); + +// Use the registered ability +const result = await executeAbility('my-plugin/show-notification', { + message: 'Hello World!', + type: 'success' +}); +``` + +### unregisterAbility(name) + +Removes a previously registered client-side ability. + +**Parameters:** +- `name` (string) - The ability name to unregister + +**Returns:** `boolean` - True if the ability was found and removed + +**Example:** + +```javascript +// Unregister an ability +const wasRemoved = unregisterAbility('my-plugin/old-ability'); +if (wasRemoved) { + console.log('Ability successfully removed'); +} + +// Check if ability is gone +const ability = await getAbility('my-plugin/old-ability'); +console.log('Ability exists:', ability !== null); +``` + +## Error Handling + +All functions return promises that may reject with specific error codes: + +```javascript +try { + const result = await executeAbility('my-plugin/restricted-action', input); + console.log('Success:', result); +} catch (error) { + switch (error.code) { + case 'ability_permission_denied': + console.error('Permission denied:', error.message); + break; + case 'ability_invalid_input': + console.error('Invalid input:', error.message); + break; + case 'rest_ability_not_found': + console.error('Ability not found:', error.message); + break; + default: + console.error('Execution failed:', error.message); + } +} +```