@wordpress/core-abilities 0.11.0 → 0.12.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.
package/README.md CHANGED
@@ -16,26 +16,18 @@ _This package assumes that your code will run in an ES2015+ environment. If you'
16
16
 
17
17
  ## Usage
18
18
 
19
- This package is designed to be loaded as a script module on WordPress admin pages. When loaded, it automatically:
19
+ This package is designed to be loaded as a script module on WordPress admin pages. It exposes an `initialize()` function that, on first call:
20
20
 
21
- 1. Fetches all ability categories from `/wp-abilities/v1/categories`
22
- 2. Registers them with `@wordpress/abilities`
23
- 3. Fetches all abilities from `/wp-abilities/v1/abilities`
24
- 4. Registers them with a `callback` that handles execution via REST API
21
+ 1. Fetches all ability categories from `/wp-abilities/v1/categories` and registers them with `@wordpress/abilities`
22
+ 2. Fetches all abilities from `/wp-abilities/v1/abilities` and registers them with a `callback` that handles execution via REST API
25
23
 
26
- Simply import the package to initialize the WordPress abilities:
24
+ Initialization is lazy: importing the package does not trigger network requests. Call `initialize()` from the feature that needs abilities — for example, when the workflow palette opens. Repeated calls return the same in-flight or resolved promise, so the requests fire at most once.
27
25
 
28
26
  ```js
29
- import '@wordpress/core-abilities';
30
- ```
31
-
32
- Initialization is asynchronous because categories and abilities are fetched from the REST API. To wait for registration to finish before calling `getAbilities()` or `executeAbility()`, await the exported `ready` promise:
33
-
34
- ```js
35
- import { ready } from '@wordpress/core-abilities';
27
+ import { initialize } from '@wordpress/core-abilities';
36
28
  import { getAbilities, executeAbility } from '@wordpress/abilities';
37
29
 
38
- await ready;
30
+ await initialize();
39
31
 
40
32
  console.log( getAbilities() );
41
33
  console.log( await executeAbility( 'core/get-site-info' ) );
package/build/index.cjs CHANGED
@@ -30,7 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // packages/core-abilities/src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- ready: () => ready
33
+ initialize: () => initialize
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
  var import_abilities = require("@wordpress/abilities");
@@ -111,13 +111,18 @@ async function initializeAbilities() {
111
111
  console.error("Failed to fetch abilities:", error);
112
112
  }
113
113
  }
114
- async function initialize() {
115
- await initializeCategories();
116
- await initializeAbilities();
114
+ var initializePromise = null;
115
+ function initialize() {
116
+ if (!initializePromise) {
117
+ initializePromise = (async () => {
118
+ await initializeCategories();
119
+ await initializeAbilities();
120
+ })();
121
+ }
122
+ return initializePromise;
117
123
  }
118
- var ready = initialize();
119
124
  // Annotate the CommonJS export names for ESM import in node:
120
125
  0 && (module.exports = {
121
- ready
126
+ initialize
122
127
  });
123
128
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { registerAbility, registerAbilityCategory } from '@wordpress/abilities';\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityInput,\n\tAbilityOutput,\n} from '@wordpress/abilities';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\n/**\n * API endpoint constants.\n */\nconst API_BASE = '/wp-abilities/v1';\nconst ABILITIES_ENDPOINT = `${ API_BASE }/abilities`;\nconst CATEGORIES_ENDPOINT = `${ API_BASE }/categories`;\n\n/**\n * Creates a serverCallback function for a WordPress REST API ability.\n *\n * @param ability The ability to create a callback for.\n * @return The serverCallback function.\n */\nfunction createServerCallback(\n\tability: Ability\n): ( input: AbilityInput ) => Promise< AbilityOutput > {\n\treturn async ( input: AbilityInput ) => {\n\t\t// Determine HTTP method based on ability annotations\n\t\tlet method = 'POST';\n\t\tif ( !! ability.meta?.annotations?.readonly ) {\n\t\t\tmethod = 'GET';\n\t\t} else if (\n\t\t\t!! ability.meta?.annotations?.destructive &&\n\t\t\t!! ability.meta?.annotations?.idempotent\n\t\t) {\n\t\t\tmethod = 'DELETE';\n\t\t}\n\n\t\tlet path = `${ ABILITIES_ENDPOINT }/${ ability.name }/run`;\n\t\tconst options: {\n\t\t\tmethod: string;\n\t\t\tdata?: { input: AbilityInput };\n\t\t} = {\n\t\t\tmethod,\n\t\t};\n\n\t\tif (\n\t\t\t[ 'GET', 'DELETE' ].includes( method ) &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\t// For GET and DELETE requests, pass the input as query parameters.\n\t\t\tpath = addQueryArgs( path, { input } );\n\t\t} else if (\n\t\t\tmethod === 'POST' &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\toptions.data = { input };\n\t\t}\n\n\t\t// Input and output validation happens on the server side for these abilities.\n\t\treturn apiFetch< AbilityOutput >( {\n\t\t\tpath,\n\t\t\t...options,\n\t\t} );\n\t};\n}\n\n/**\n * Fetches and registers all ability categories from WordPress.\n */\nasync function initializeCategories(): Promise< void > {\n\ttry {\n\t\tconst categories = await apiFetch< AbilityCategory[] >( {\n\t\t\tpath: addQueryArgs( CATEGORIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( categories && Array.isArray( categories ) ) {\n\t\t\tfor ( const category of categories ) {\n\t\t\t\tregisterAbilityCategory( category.slug, {\n\t\t\t\t\tlabel: category.label,\n\t\t\t\t\tdescription: category.description,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: { serverRegistered: true },\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch ability categories:', error );\n\t}\n}\n\n/**\n * Fetches and registers all abilities from WordPress.\n */\nasync function initializeAbilities(): Promise< void > {\n\ttry {\n\t\tconst abilities = await apiFetch< Ability[] >( {\n\t\t\tpath: addQueryArgs( ABILITIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( abilities && Array.isArray( abilities ) ) {\n\t\t\tfor ( const ability of abilities ) {\n\t\t\t\t// Register the ability with a callback\n\t\t\t\t// The abilities package filters annotations to allowed keys\n\t\t\t\tregisterAbility( {\n\t\t\t\t\t...ability,\n\t\t\t\t\tcallback: createServerCallback( ability ),\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: {\n\t\t\t\t\t\t\t...ability.meta?.annotations,\n\t\t\t\t\t\t\tserverRegistered: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch abilities:', error );\n\t}\n}\n\n/**\n * Initialize WordPress abilities integration.\n */\nasync function initialize(): Promise< void > {\n\t// Fetch and register categories, then abilities\n\tawait initializeCategories();\n\tawait initializeAbilities();\n}\n\n// Auto-initialize on import.\nexport const ready: Promise< void > = initialize();\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,uBAAyD;AAOzD,uBAAqB;AACrB,iBAA6B;AAK7B,IAAM,WAAW;AACjB,IAAM,qBAAqB,GAAI,QAAS;AACxC,IAAM,sBAAsB,GAAI,QAAS;AAQzC,SAAS,qBACR,SACsD;AACtD,SAAO,OAAQ,UAAyB;AAEvC,QAAI,SAAS;AACb,QAAK,CAAC,CAAE,QAAQ,MAAM,aAAa,UAAW;AAC7C,eAAS;AAAA,IACV,WACC,CAAC,CAAE,QAAQ,MAAM,aAAa,eAC9B,CAAC,CAAE,QAAQ,MAAM,aAAa,YAC7B;AACD,eAAS;AAAA,IACV;AAEA,QAAI,OAAO,GAAI,kBAAmB,IAAK,QAAQ,IAAK;AACpD,UAAM,UAGF;AAAA,MACH;AAAA,IACD;AAEA,QACC,CAAE,OAAO,QAAS,EAAE,SAAU,MAAO,KACrC,UAAU,QACV,UAAU,QACT;AAED,iBAAO,yBAAc,MAAM,EAAE,MAAM,CAAE;AAAA,IACtC,WACC,WAAW,UACX,UAAU,QACV,UAAU,QACT;AACD,cAAQ,OAAO,EAAE,MAAM;AAAA,IACxB;AAGA,eAAO,iBAAAA,SAA2B;AAAA,MACjC;AAAA,MACA,GAAG;AAAA,IACJ,CAAE;AAAA,EACH;AACD;AAKA,eAAe,uBAAwC;AACtD,MAAI;AACH,UAAM,aAAa,UAAM,iBAAAA,SAA+B;AAAA,MACvD,UAAM,yBAAc,qBAAqB;AAAA,QACxC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,cAAc,MAAM,QAAS,UAAW,GAAI;AAChD,iBAAY,YAAY,YAAa;AACpC,sDAAyB,SAAS,MAAM;AAAA,UACvC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,UACtB,MAAM;AAAA,YACL,aAAa,EAAE,kBAAkB,KAAK;AAAA,UACvC;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,uCAAuC,KAAM;AAAA,EAC7D;AACD;AAKA,eAAe,sBAAuC;AACrD,MAAI;AACH,UAAM,YAAY,UAAM,iBAAAA,SAAuB;AAAA,MAC9C,UAAM,yBAAc,oBAAoB;AAAA,QACvC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,aAAa,MAAM,QAAS,SAAU,GAAI;AAC9C,iBAAY,WAAW,WAAY;AAGlC,8CAAiB;AAAA,UAChB,GAAG;AAAA,UACH,UAAU,qBAAsB,OAAQ;AAAA,UACxC,MAAM;AAAA,YACL,aAAa;AAAA,cACZ,GAAG,QAAQ,MAAM;AAAA,cACjB,kBAAkB;AAAA,YACnB;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,8BAA8B,KAAM;AAAA,EACpD;AACD;AAKA,eAAe,aAA8B;AAE5C,QAAM,qBAAqB;AAC3B,QAAM,oBAAoB;AAC3B;AAGO,IAAM,QAAyB,WAAW;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { registerAbility, registerAbilityCategory } from '@wordpress/abilities';\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityInput,\n\tAbilityOutput,\n} from '@wordpress/abilities';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\n/**\n * API endpoint constants.\n */\nconst API_BASE = '/wp-abilities/v1';\nconst ABILITIES_ENDPOINT = `${ API_BASE }/abilities`;\nconst CATEGORIES_ENDPOINT = `${ API_BASE }/categories`;\n\n/**\n * Creates a serverCallback function for a WordPress REST API ability.\n *\n * @param ability The ability to create a callback for.\n * @return The serverCallback function.\n */\nfunction createServerCallback(\n\tability: Ability\n): ( input: AbilityInput ) => Promise< AbilityOutput > {\n\treturn async ( input: AbilityInput ) => {\n\t\t// Determine HTTP method based on ability annotations\n\t\tlet method = 'POST';\n\t\tif ( !! ability.meta?.annotations?.readonly ) {\n\t\t\tmethod = 'GET';\n\t\t} else if (\n\t\t\t!! ability.meta?.annotations?.destructive &&\n\t\t\t!! ability.meta?.annotations?.idempotent\n\t\t) {\n\t\t\tmethod = 'DELETE';\n\t\t}\n\n\t\tlet path = `${ ABILITIES_ENDPOINT }/${ ability.name }/run`;\n\t\tconst options: {\n\t\t\tmethod: string;\n\t\t\tdata?: { input: AbilityInput };\n\t\t} = {\n\t\t\tmethod,\n\t\t};\n\n\t\tif (\n\t\t\t[ 'GET', 'DELETE' ].includes( method ) &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\t// For GET and DELETE requests, pass the input as query parameters.\n\t\t\tpath = addQueryArgs( path, { input } );\n\t\t} else if (\n\t\t\tmethod === 'POST' &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\toptions.data = { input };\n\t\t}\n\n\t\t// Input and output validation happens on the server side for these abilities.\n\t\treturn apiFetch< AbilityOutput >( {\n\t\t\tpath,\n\t\t\t...options,\n\t\t} );\n\t};\n}\n\n/**\n * Fetches and registers all ability categories from WordPress.\n */\nasync function initializeCategories(): Promise< void > {\n\ttry {\n\t\tconst categories = await apiFetch< AbilityCategory[] >( {\n\t\t\tpath: addQueryArgs( CATEGORIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( categories && Array.isArray( categories ) ) {\n\t\t\tfor ( const category of categories ) {\n\t\t\t\tregisterAbilityCategory( category.slug, {\n\t\t\t\t\tlabel: category.label,\n\t\t\t\t\tdescription: category.description,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: { serverRegistered: true },\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch ability categories:', error );\n\t}\n}\n\n/**\n * Fetches and registers all abilities from WordPress.\n */\nasync function initializeAbilities(): Promise< void > {\n\ttry {\n\t\tconst abilities = await apiFetch< Ability[] >( {\n\t\t\tpath: addQueryArgs( ABILITIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( abilities && Array.isArray( abilities ) ) {\n\t\t\tfor ( const ability of abilities ) {\n\t\t\t\t// Register the ability with a callback\n\t\t\t\t// The abilities package filters annotations to allowed keys\n\t\t\t\tregisterAbility( {\n\t\t\t\t\t...ability,\n\t\t\t\t\tcallback: createServerCallback( ability ),\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: {\n\t\t\t\t\t\t\t...ability.meta?.annotations,\n\t\t\t\t\t\t\tserverRegistered: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch abilities:', error );\n\t}\n}\n\nlet initializePromise: Promise< void > | null = null;\n\n/**\n * Fetches and registers core abilities and their categories.\n *\n * Memoized: repeated calls return the same in-flight or resolved promise, so\n * callers can invoke it from multiple entry points without triggering duplicate\n * REST requests.\n */\nexport function initialize(): Promise< void > {\n\tif ( ! initializePromise ) {\n\t\tinitializePromise = ( async () => {\n\t\t\tawait initializeCategories();\n\t\t\tawait initializeAbilities();\n\t\t} )();\n\t}\n\treturn initializePromise;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,uBAAyD;AAOzD,uBAAqB;AACrB,iBAA6B;AAK7B,IAAM,WAAW;AACjB,IAAM,qBAAqB,GAAI,QAAS;AACxC,IAAM,sBAAsB,GAAI,QAAS;AAQzC,SAAS,qBACR,SACsD;AACtD,SAAO,OAAQ,UAAyB;AAEvC,QAAI,SAAS;AACb,QAAK,CAAC,CAAE,QAAQ,MAAM,aAAa,UAAW;AAC7C,eAAS;AAAA,IACV,WACC,CAAC,CAAE,QAAQ,MAAM,aAAa,eAC9B,CAAC,CAAE,QAAQ,MAAM,aAAa,YAC7B;AACD,eAAS;AAAA,IACV;AAEA,QAAI,OAAO,GAAI,kBAAmB,IAAK,QAAQ,IAAK;AACpD,UAAM,UAGF;AAAA,MACH;AAAA,IACD;AAEA,QACC,CAAE,OAAO,QAAS,EAAE,SAAU,MAAO,KACrC,UAAU,QACV,UAAU,QACT;AAED,iBAAO,yBAAc,MAAM,EAAE,MAAM,CAAE;AAAA,IACtC,WACC,WAAW,UACX,UAAU,QACV,UAAU,QACT;AACD,cAAQ,OAAO,EAAE,MAAM;AAAA,IACxB;AAGA,eAAO,iBAAAA,SAA2B;AAAA,MACjC;AAAA,MACA,GAAG;AAAA,IACJ,CAAE;AAAA,EACH;AACD;AAKA,eAAe,uBAAwC;AACtD,MAAI;AACH,UAAM,aAAa,UAAM,iBAAAA,SAA+B;AAAA,MACvD,UAAM,yBAAc,qBAAqB;AAAA,QACxC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,cAAc,MAAM,QAAS,UAAW,GAAI;AAChD,iBAAY,YAAY,YAAa;AACpC,sDAAyB,SAAS,MAAM;AAAA,UACvC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,UACtB,MAAM;AAAA,YACL,aAAa,EAAE,kBAAkB,KAAK;AAAA,UACvC;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,uCAAuC,KAAM;AAAA,EAC7D;AACD;AAKA,eAAe,sBAAuC;AACrD,MAAI;AACH,UAAM,YAAY,UAAM,iBAAAA,SAAuB;AAAA,MAC9C,UAAM,yBAAc,oBAAoB;AAAA,QACvC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,aAAa,MAAM,QAAS,SAAU,GAAI;AAC9C,iBAAY,WAAW,WAAY;AAGlC,8CAAiB;AAAA,UAChB,GAAG;AAAA,UACH,UAAU,qBAAsB,OAAQ;AAAA,UACxC,MAAM;AAAA,YACL,aAAa;AAAA,cACZ,GAAG,QAAQ,MAAM;AAAA,cACjB,kBAAkB;AAAA,YACnB;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,8BAA8B,KAAM;AAAA,EACpD;AACD;AAEA,IAAI,oBAA4C;AASzC,SAAS,aAA8B;AAC7C,MAAK,CAAE,mBAAoB;AAC1B,yBAAsB,YAAY;AACjC,YAAM,qBAAqB;AAC3B,YAAM,oBAAoB;AAAA,IAC3B,GAAI;AAAA,EACL;AACA,SAAO;AACR;",
6
6
  "names": ["apiFetch"]
7
7
  }
@@ -77,12 +77,17 @@ async function initializeAbilities() {
77
77
  console.error("Failed to fetch abilities:", error);
78
78
  }
79
79
  }
80
- async function initialize() {
81
- await initializeCategories();
82
- await initializeAbilities();
80
+ var initializePromise = null;
81
+ function initialize() {
82
+ if (!initializePromise) {
83
+ initializePromise = (async () => {
84
+ await initializeCategories();
85
+ await initializeAbilities();
86
+ })();
87
+ }
88
+ return initializePromise;
83
89
  }
84
- var ready = initialize();
85
90
  export {
86
- ready
91
+ initialize
87
92
  };
88
93
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { registerAbility, registerAbilityCategory } from '@wordpress/abilities';\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityInput,\n\tAbilityOutput,\n} from '@wordpress/abilities';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\n/**\n * API endpoint constants.\n */\nconst API_BASE = '/wp-abilities/v1';\nconst ABILITIES_ENDPOINT = `${ API_BASE }/abilities`;\nconst CATEGORIES_ENDPOINT = `${ API_BASE }/categories`;\n\n/**\n * Creates a serverCallback function for a WordPress REST API ability.\n *\n * @param ability The ability to create a callback for.\n * @return The serverCallback function.\n */\nfunction createServerCallback(\n\tability: Ability\n): ( input: AbilityInput ) => Promise< AbilityOutput > {\n\treturn async ( input: AbilityInput ) => {\n\t\t// Determine HTTP method based on ability annotations\n\t\tlet method = 'POST';\n\t\tif ( !! ability.meta?.annotations?.readonly ) {\n\t\t\tmethod = 'GET';\n\t\t} else if (\n\t\t\t!! ability.meta?.annotations?.destructive &&\n\t\t\t!! ability.meta?.annotations?.idempotent\n\t\t) {\n\t\t\tmethod = 'DELETE';\n\t\t}\n\n\t\tlet path = `${ ABILITIES_ENDPOINT }/${ ability.name }/run`;\n\t\tconst options: {\n\t\t\tmethod: string;\n\t\t\tdata?: { input: AbilityInput };\n\t\t} = {\n\t\t\tmethod,\n\t\t};\n\n\t\tif (\n\t\t\t[ 'GET', 'DELETE' ].includes( method ) &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\t// For GET and DELETE requests, pass the input as query parameters.\n\t\t\tpath = addQueryArgs( path, { input } );\n\t\t} else if (\n\t\t\tmethod === 'POST' &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\toptions.data = { input };\n\t\t}\n\n\t\t// Input and output validation happens on the server side for these abilities.\n\t\treturn apiFetch< AbilityOutput >( {\n\t\t\tpath,\n\t\t\t...options,\n\t\t} );\n\t};\n}\n\n/**\n * Fetches and registers all ability categories from WordPress.\n */\nasync function initializeCategories(): Promise< void > {\n\ttry {\n\t\tconst categories = await apiFetch< AbilityCategory[] >( {\n\t\t\tpath: addQueryArgs( CATEGORIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( categories && Array.isArray( categories ) ) {\n\t\t\tfor ( const category of categories ) {\n\t\t\t\tregisterAbilityCategory( category.slug, {\n\t\t\t\t\tlabel: category.label,\n\t\t\t\t\tdescription: category.description,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: { serverRegistered: true },\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch ability categories:', error );\n\t}\n}\n\n/**\n * Fetches and registers all abilities from WordPress.\n */\nasync function initializeAbilities(): Promise< void > {\n\ttry {\n\t\tconst abilities = await apiFetch< Ability[] >( {\n\t\t\tpath: addQueryArgs( ABILITIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( abilities && Array.isArray( abilities ) ) {\n\t\t\tfor ( const ability of abilities ) {\n\t\t\t\t// Register the ability with a callback\n\t\t\t\t// The abilities package filters annotations to allowed keys\n\t\t\t\tregisterAbility( {\n\t\t\t\t\t...ability,\n\t\t\t\t\tcallback: createServerCallback( ability ),\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: {\n\t\t\t\t\t\t\t...ability.meta?.annotations,\n\t\t\t\t\t\t\tserverRegistered: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch abilities:', error );\n\t}\n}\n\n/**\n * Initialize WordPress abilities integration.\n */\nasync function initialize(): Promise< void > {\n\t// Fetch and register categories, then abilities\n\tawait initializeCategories();\n\tawait initializeAbilities();\n}\n\n// Auto-initialize on import.\nexport const ready: Promise< void > = initialize();\n"],
5
- "mappings": ";AAGA,SAAS,iBAAiB,+BAA+B;AAOzD,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAK7B,IAAM,WAAW;AACjB,IAAM,qBAAqB,GAAI,QAAS;AACxC,IAAM,sBAAsB,GAAI,QAAS;AAQzC,SAAS,qBACR,SACsD;AACtD,SAAO,OAAQ,UAAyB;AAEvC,QAAI,SAAS;AACb,QAAK,CAAC,CAAE,QAAQ,MAAM,aAAa,UAAW;AAC7C,eAAS;AAAA,IACV,WACC,CAAC,CAAE,QAAQ,MAAM,aAAa,eAC9B,CAAC,CAAE,QAAQ,MAAM,aAAa,YAC7B;AACD,eAAS;AAAA,IACV;AAEA,QAAI,OAAO,GAAI,kBAAmB,IAAK,QAAQ,IAAK;AACpD,UAAM,UAGF;AAAA,MACH;AAAA,IACD;AAEA,QACC,CAAE,OAAO,QAAS,EAAE,SAAU,MAAO,KACrC,UAAU,QACV,UAAU,QACT;AAED,aAAO,aAAc,MAAM,EAAE,MAAM,CAAE;AAAA,IACtC,WACC,WAAW,UACX,UAAU,QACV,UAAU,QACT;AACD,cAAQ,OAAO,EAAE,MAAM;AAAA,IACxB;AAGA,WAAO,SAA2B;AAAA,MACjC;AAAA,MACA,GAAG;AAAA,IACJ,CAAE;AAAA,EACH;AACD;AAKA,eAAe,uBAAwC;AACtD,MAAI;AACH,UAAM,aAAa,MAAM,SAA+B;AAAA,MACvD,MAAM,aAAc,qBAAqB;AAAA,QACxC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,cAAc,MAAM,QAAS,UAAW,GAAI;AAChD,iBAAY,YAAY,YAAa;AACpC,gCAAyB,SAAS,MAAM;AAAA,UACvC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,UACtB,MAAM;AAAA,YACL,aAAa,EAAE,kBAAkB,KAAK;AAAA,UACvC;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,uCAAuC,KAAM;AAAA,EAC7D;AACD;AAKA,eAAe,sBAAuC;AACrD,MAAI;AACH,UAAM,YAAY,MAAM,SAAuB;AAAA,MAC9C,MAAM,aAAc,oBAAoB;AAAA,QACvC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,aAAa,MAAM,QAAS,SAAU,GAAI;AAC9C,iBAAY,WAAW,WAAY;AAGlC,wBAAiB;AAAA,UAChB,GAAG;AAAA,UACH,UAAU,qBAAsB,OAAQ;AAAA,UACxC,MAAM;AAAA,YACL,aAAa;AAAA,cACZ,GAAG,QAAQ,MAAM;AAAA,cACjB,kBAAkB;AAAA,YACnB;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,8BAA8B,KAAM;AAAA,EACpD;AACD;AAKA,eAAe,aAA8B;AAE5C,QAAM,qBAAqB;AAC3B,QAAM,oBAAoB;AAC3B;AAGO,IAAM,QAAyB,WAAW;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { registerAbility, registerAbilityCategory } from '@wordpress/abilities';\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilityInput,\n\tAbilityOutput,\n} from '@wordpress/abilities';\nimport apiFetch from '@wordpress/api-fetch';\nimport { addQueryArgs } from '@wordpress/url';\n\n/**\n * API endpoint constants.\n */\nconst API_BASE = '/wp-abilities/v1';\nconst ABILITIES_ENDPOINT = `${ API_BASE }/abilities`;\nconst CATEGORIES_ENDPOINT = `${ API_BASE }/categories`;\n\n/**\n * Creates a serverCallback function for a WordPress REST API ability.\n *\n * @param ability The ability to create a callback for.\n * @return The serverCallback function.\n */\nfunction createServerCallback(\n\tability: Ability\n): ( input: AbilityInput ) => Promise< AbilityOutput > {\n\treturn async ( input: AbilityInput ) => {\n\t\t// Determine HTTP method based on ability annotations\n\t\tlet method = 'POST';\n\t\tif ( !! ability.meta?.annotations?.readonly ) {\n\t\t\tmethod = 'GET';\n\t\t} else if (\n\t\t\t!! ability.meta?.annotations?.destructive &&\n\t\t\t!! ability.meta?.annotations?.idempotent\n\t\t) {\n\t\t\tmethod = 'DELETE';\n\t\t}\n\n\t\tlet path = `${ ABILITIES_ENDPOINT }/${ ability.name }/run`;\n\t\tconst options: {\n\t\t\tmethod: string;\n\t\t\tdata?: { input: AbilityInput };\n\t\t} = {\n\t\t\tmethod,\n\t\t};\n\n\t\tif (\n\t\t\t[ 'GET', 'DELETE' ].includes( method ) &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\t// For GET and DELETE requests, pass the input as query parameters.\n\t\t\tpath = addQueryArgs( path, { input } );\n\t\t} else if (\n\t\t\tmethod === 'POST' &&\n\t\t\tinput !== null &&\n\t\t\tinput !== undefined\n\t\t) {\n\t\t\toptions.data = { input };\n\t\t}\n\n\t\t// Input and output validation happens on the server side for these abilities.\n\t\treturn apiFetch< AbilityOutput >( {\n\t\t\tpath,\n\t\t\t...options,\n\t\t} );\n\t};\n}\n\n/**\n * Fetches and registers all ability categories from WordPress.\n */\nasync function initializeCategories(): Promise< void > {\n\ttry {\n\t\tconst categories = await apiFetch< AbilityCategory[] >( {\n\t\t\tpath: addQueryArgs( CATEGORIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( categories && Array.isArray( categories ) ) {\n\t\t\tfor ( const category of categories ) {\n\t\t\t\tregisterAbilityCategory( category.slug, {\n\t\t\t\t\tlabel: category.label,\n\t\t\t\t\tdescription: category.description,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: { serverRegistered: true },\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch ability categories:', error );\n\t}\n}\n\n/**\n * Fetches and registers all abilities from WordPress.\n */\nasync function initializeAbilities(): Promise< void > {\n\ttry {\n\t\tconst abilities = await apiFetch< Ability[] >( {\n\t\t\tpath: addQueryArgs( ABILITIES_ENDPOINT, {\n\t\t\t\tper_page: -1,\n\t\t\t\tcontext: 'edit',\n\t\t\t} ),\n\t\t} );\n\n\t\tif ( abilities && Array.isArray( abilities ) ) {\n\t\t\tfor ( const ability of abilities ) {\n\t\t\t\t// Register the ability with a callback\n\t\t\t\t// The abilities package filters annotations to allowed keys\n\t\t\t\tregisterAbility( {\n\t\t\t\t\t...ability,\n\t\t\t\t\tcallback: createServerCallback( ability ),\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\tannotations: {\n\t\t\t\t\t\t\t...ability.meta?.annotations,\n\t\t\t\t\t\t\tserverRegistered: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\t} catch ( error ) {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Failed to fetch abilities:', error );\n\t}\n}\n\nlet initializePromise: Promise< void > | null = null;\n\n/**\n * Fetches and registers core abilities and their categories.\n *\n * Memoized: repeated calls return the same in-flight or resolved promise, so\n * callers can invoke it from multiple entry points without triggering duplicate\n * REST requests.\n */\nexport function initialize(): Promise< void > {\n\tif ( ! initializePromise ) {\n\t\tinitializePromise = ( async () => {\n\t\t\tawait initializeCategories();\n\t\t\tawait initializeAbilities();\n\t\t} )();\n\t}\n\treturn initializePromise;\n}\n"],
5
+ "mappings": ";AAGA,SAAS,iBAAiB,+BAA+B;AAOzD,OAAO,cAAc;AACrB,SAAS,oBAAoB;AAK7B,IAAM,WAAW;AACjB,IAAM,qBAAqB,GAAI,QAAS;AACxC,IAAM,sBAAsB,GAAI,QAAS;AAQzC,SAAS,qBACR,SACsD;AACtD,SAAO,OAAQ,UAAyB;AAEvC,QAAI,SAAS;AACb,QAAK,CAAC,CAAE,QAAQ,MAAM,aAAa,UAAW;AAC7C,eAAS;AAAA,IACV,WACC,CAAC,CAAE,QAAQ,MAAM,aAAa,eAC9B,CAAC,CAAE,QAAQ,MAAM,aAAa,YAC7B;AACD,eAAS;AAAA,IACV;AAEA,QAAI,OAAO,GAAI,kBAAmB,IAAK,QAAQ,IAAK;AACpD,UAAM,UAGF;AAAA,MACH;AAAA,IACD;AAEA,QACC,CAAE,OAAO,QAAS,EAAE,SAAU,MAAO,KACrC,UAAU,QACV,UAAU,QACT;AAED,aAAO,aAAc,MAAM,EAAE,MAAM,CAAE;AAAA,IACtC,WACC,WAAW,UACX,UAAU,QACV,UAAU,QACT;AACD,cAAQ,OAAO,EAAE,MAAM;AAAA,IACxB;AAGA,WAAO,SAA2B;AAAA,MACjC;AAAA,MACA,GAAG;AAAA,IACJ,CAAE;AAAA,EACH;AACD;AAKA,eAAe,uBAAwC;AACtD,MAAI;AACH,UAAM,aAAa,MAAM,SAA+B;AAAA,MACvD,MAAM,aAAc,qBAAqB;AAAA,QACxC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,cAAc,MAAM,QAAS,UAAW,GAAI;AAChD,iBAAY,YAAY,YAAa;AACpC,gCAAyB,SAAS,MAAM;AAAA,UACvC,OAAO,SAAS;AAAA,UAChB,aAAa,SAAS;AAAA,UACtB,MAAM;AAAA,YACL,aAAa,EAAE,kBAAkB,KAAK;AAAA,UACvC;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,uCAAuC,KAAM;AAAA,EAC7D;AACD;AAKA,eAAe,sBAAuC;AACrD,MAAI;AACH,UAAM,YAAY,MAAM,SAAuB;AAAA,MAC9C,MAAM,aAAc,oBAAoB;AAAA,QACvC,UAAU;AAAA,QACV,SAAS;AAAA,MACV,CAAE;AAAA,IACH,CAAE;AAEF,QAAK,aAAa,MAAM,QAAS,SAAU,GAAI;AAC9C,iBAAY,WAAW,WAAY;AAGlC,wBAAiB;AAAA,UAChB,GAAG;AAAA,UACH,UAAU,qBAAsB,OAAQ;AAAA,UACxC,MAAM;AAAA,YACL,aAAa;AAAA,cACZ,GAAG,QAAQ,MAAM;AAAA,cACjB,kBAAkB;AAAA,YACnB;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD,SAAU,OAAQ;AAEjB,YAAQ,MAAO,8BAA8B,KAAM;AAAA,EACpD;AACD;AAEA,IAAI,oBAA4C;AASzC,SAAS,aAA8B;AAC7C,MAAK,CAAE,mBAAoB;AAC1B,yBAAsB,YAAY;AACjC,YAAM,qBAAqB;AAC3B,YAAM,oBAAoB;AAAA,IAC3B,GAAI;AAAA,EACL;AACA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,9 @@
1
- export declare const ready: Promise<void>;
1
+ /**
2
+ * Fetches and registers core abilities and their categories.
3
+ *
4
+ * Memoized: repeated calls return the same in-flight or resolved promise, so
5
+ * callers can invoke it from multiple entry points without triggering duplicate
6
+ * REST requests.
7
+ */
8
+ export declare function initialize(): Promise<void>;
2
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiJA,eAAO,MAAM,KAAK,EAAE,OAAO,CAAE,IAAI,CAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyIA;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAE,IAAI,CAAE,CAQ5C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/core-abilities",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "WordPress core abilities integration for the Abilities API.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -46,12 +46,12 @@
46
46
  "types": "build-types",
47
47
  "sideEffects": true,
48
48
  "dependencies": {
49
- "@wordpress/abilities": "^0.12.0",
50
- "@wordpress/api-fetch": "^7.46.0",
51
- "@wordpress/url": "^4.46.0"
49
+ "@wordpress/abilities": "^0.13.0",
50
+ "@wordpress/api-fetch": "^7.47.0",
51
+ "@wordpress/url": "^4.47.0"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"
55
55
  },
56
- "gitHead": "51264e33b95fadff9a06b68141e674ce9cde9675"
56
+ "gitHead": "d653c5fd6161571a0c2ebde28553d6e25624eacc"
57
57
  }
package/src/index.ts CHANGED
@@ -133,14 +133,21 @@ async function initializeAbilities(): Promise< void > {
133
133
  }
134
134
  }
135
135
 
136
+ let initializePromise: Promise< void > | null = null;
137
+
136
138
  /**
137
- * Initialize WordPress abilities integration.
139
+ * Fetches and registers core abilities and their categories.
140
+ *
141
+ * Memoized: repeated calls return the same in-flight or resolved promise, so
142
+ * callers can invoke it from multiple entry points without triggering duplicate
143
+ * REST requests.
138
144
  */
139
- async function initialize(): Promise< void > {
140
- // Fetch and register categories, then abilities
141
- await initializeCategories();
142
- await initializeAbilities();
145
+ export function initialize(): Promise< void > {
146
+ if ( ! initializePromise ) {
147
+ initializePromise = ( async () => {
148
+ await initializeCategories();
149
+ await initializeAbilities();
150
+ } )();
151
+ }
152
+ return initializePromise;
143
153
  }
144
-
145
- // Auto-initialize on import.
146
- export const ready: Promise< void > = initialize();