@wordpress/abilities 0.2.1-next.8b30e05b0.0 → 0.3.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 (72) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +32 -40
  3. package/build/api.js +19 -66
  4. package/build/api.js.map +3 -3
  5. package/build/index.js.map +2 -2
  6. package/build/store/actions.js +41 -31
  7. package/build/store/actions.js.map +2 -2
  8. package/build/store/constants.js +6 -15
  9. package/build/store/constants.js.map +2 -2
  10. package/build/store/index.js +1 -27
  11. package/build/store/index.js.map +3 -3
  12. package/build/store/reducer.js +2 -22
  13. package/build/store/reducer.js.map +2 -2
  14. package/build/store/selectors.js +1 -1
  15. package/build/store/selectors.js.map +2 -2
  16. package/build/store/types.js +19 -0
  17. package/build/store/types.js.map +7 -0
  18. package/build/types.js.map +1 -1
  19. package/build-module/api.js +20 -57
  20. package/build-module/api.js.map +2 -2
  21. package/build-module/index.js.map +2 -2
  22. package/build-module/store/actions.js +43 -32
  23. package/build-module/store/actions.js.map +2 -2
  24. package/build-module/store/constants.js +4 -10
  25. package/build-module/store/constants.js.map +2 -2
  26. package/build-module/store/index.js +3 -34
  27. package/build-module/store/index.js.map +2 -2
  28. package/build-module/store/reducer.js +2 -24
  29. package/build-module/store/reducer.js.map +2 -2
  30. package/build-module/store/selectors.js +1 -1
  31. package/build-module/store/selectors.js.map +2 -2
  32. package/build-module/store/types.js +1 -0
  33. package/build-module/store/types.js.map +7 -0
  34. package/build-types/api.d.ts +16 -23
  35. package/build-types/api.d.ts.map +1 -1
  36. package/build-types/index.d.ts +1 -1
  37. package/build-types/index.d.ts.map +1 -1
  38. package/build-types/store/actions.d.ts +5 -27
  39. package/build-types/store/actions.d.ts.map +1 -1
  40. package/build-types/store/constants.d.ts +2 -5
  41. package/build-types/store/constants.d.ts.map +1 -1
  42. package/build-types/store/index.d.ts.map +1 -1
  43. package/build-types/store/reducer.d.ts +21 -11
  44. package/build-types/store/reducer.d.ts.map +1 -1
  45. package/build-types/store/selectors.d.ts +2 -1
  46. package/build-types/store/selectors.d.ts.map +1 -1
  47. package/build-types/store/types.d.ts +18 -0
  48. package/build-types/store/types.d.ts.map +1 -0
  49. package/build-types/types.d.ts +14 -24
  50. package/build-types/types.d.ts.map +1 -1
  51. package/package.json +4 -7
  52. package/src/api.ts +43 -123
  53. package/src/index.ts +0 -1
  54. package/src/store/actions.ts +61 -46
  55. package/src/store/constants.ts +4 -5
  56. package/src/store/index.ts +2 -34
  57. package/src/store/reducer.ts +27 -33
  58. package/src/store/selectors.ts +3 -7
  59. package/src/store/tests/actions.test.ts +100 -244
  60. package/src/store/tests/reducer.test.ts +0 -451
  61. package/src/store/tests/selectors.test.ts +1 -1
  62. package/src/store/types.ts +19 -0
  63. package/src/tests/api.test.ts +112 -158
  64. package/src/types.ts +14 -26
  65. package/build/store/resolvers.js +0 -107
  66. package/build/store/resolvers.js.map +0 -7
  67. package/build-module/store/resolvers.js +0 -84
  68. package/build-module/store/resolvers.js.map +0 -7
  69. package/build-types/store/resolvers.d.ts +0 -47
  70. package/build-types/store/resolvers.d.ts.map +0 -1
  71. package/src/store/resolvers.ts +0 -151
  72. package/src/store/tests/resolvers.test.ts +0 -642
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/store/constants.ts"],
4
- "sourcesContent": ["/**\n * Store constants\n */\nexport const STORE_NAME = 'core/abilities';\nexport const ENTITY_KIND = 'root';\nexport const ENTITY_NAME = 'abilities';\nexport const ENTITY_NAME_CATEGORIES = 'ability-categories';\n\n// Action types\nexport const RECEIVE_ABILITIES = 'RECEIVE_ABILITIES';\nexport const REGISTER_ABILITY = 'REGISTER_ABILITY';\nexport const UNREGISTER_ABILITY = 'UNREGISTER_ABILITY';\nexport const RECEIVE_CATEGORIES = 'RECEIVE_CATEGORIES';\nexport const REGISTER_ABILITY_CATEGORY = 'REGISTER_ABILITY_CATEGORY';\nexport const UNREGISTER_ABILITY_CATEGORY = 'UNREGISTER_ABILITY_CATEGORY';\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,cAAc;AACpB,IAAM,yBAAyB;AAG/B,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,8BAA8B;",
4
+ "sourcesContent": ["/**\n * Store constants\n */\nexport const STORE_NAME = 'core/abilities';\n\n// Validation patterns\nexport const ABILITY_NAME_PATTERN = /^[a-z0-9-]+\\/[a-z0-9-]+$/;\nexport const CATEGORY_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n// Action types\nexport const REGISTER_ABILITY = 'REGISTER_ABILITY';\nexport const UNREGISTER_ABILITY = 'UNREGISTER_ABILITY';\nexport const REGISTER_ABILITY_CATEGORY = 'REGISTER_ABILITY_CATEGORY';\nexport const UNREGISTER_ABILITY_CATEGORY = 'UNREGISTER_ABILITY_CATEGORY';\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,aAAa;AAGnB,IAAM,uBAAuB;AAC7B,IAAM,wBAAwB;AAG9B,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,8BAA8B;",
6
6
  "names": []
7
7
  }
@@ -34,42 +34,16 @@ __export(store_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(store_exports);
36
36
  var import_data = require("@wordpress/data");
37
- var import_core_data = require("@wordpress/core-data");
38
- var import_i18n = require("@wordpress/i18n");
39
37
  var import_reducer = __toESM(require("./reducer"));
40
38
  var actions = __toESM(require("./actions"));
41
39
  var selectors = __toESM(require("./selectors"));
42
- var resolvers = __toESM(require("./resolvers"));
43
40
  var import_constants = require("./constants");
44
41
  var store = (0, import_data.createReduxStore)(import_constants.STORE_NAME, {
45
42
  reducer: import_reducer.default,
46
43
  actions,
47
- selectors,
48
- resolvers
44
+ selectors
49
45
  });
50
46
  (0, import_data.register)(store);
51
- (0, import_data.dispatch)(import_core_data.store).addEntities([
52
- {
53
- name: import_constants.ENTITY_NAME,
54
- kind: import_constants.ENTITY_KIND,
55
- key: "name",
56
- baseURL: "/wp-abilities/v1/abilities",
57
- baseURLParams: { context: "edit" },
58
- plural: "abilities",
59
- label: (0, import_i18n.__)("Abilities"),
60
- supportsPagination: true
61
- },
62
- {
63
- name: import_constants.ENTITY_NAME_CATEGORIES,
64
- kind: import_constants.ENTITY_KIND,
65
- key: "slug",
66
- baseURL: "/wp-abilities/v1/categories",
67
- baseURLParams: { context: "edit" },
68
- plural: "ability-categories",
69
- label: (0, import_i18n.__)("Ability Categories"),
70
- supportsPagination: true
71
- }
72
- ]);
73
47
  // Annotate the CommonJS export names for ESM import in node:
74
48
  0 && (module.exports = {
75
49
  store
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/store/index.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { createReduxStore, register, dispatch } from '@wordpress/data';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { __ } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport reducer from './reducer';\nimport * as actions from './actions';\nimport * as selectors from './selectors';\nimport * as resolvers from './resolvers';\nimport {\n\tSTORE_NAME,\n\tENTITY_KIND,\n\tENTITY_NAME,\n\tENTITY_NAME_CATEGORIES,\n} from './constants';\n\n/**\n * The abilities store definition.\n */\nexport const store = createReduxStore( STORE_NAME, {\n\treducer,\n\tactions,\n\tselectors,\n\tresolvers,\n} );\n\nregister( store );\n\ndispatch( coreStore ).addEntities( [\n\t{\n\t\tname: ENTITY_NAME,\n\t\tkind: ENTITY_KIND,\n\t\tkey: 'name',\n\t\tbaseURL: '/wp-abilities/v1/abilities',\n\t\tbaseURLParams: { context: 'edit' },\n\t\tplural: 'abilities',\n\t\tlabel: __( 'Abilities' ),\n\t\tsupportsPagination: true,\n\t},\n\t{\n\t\tname: ENTITY_NAME_CATEGORIES,\n\t\tkind: ENTITY_KIND,\n\t\tkey: 'slug',\n\t\tbaseURL: '/wp-abilities/v1/categories',\n\t\tbaseURLParams: { context: 'edit' },\n\t\tplural: 'ability-categories',\n\t\tlabel: __( 'Ability Categories' ),\n\t\tsupportsPagination: true,\n\t},\n] );\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAqD;AACrD,uBAAmC;AACnC,kBAAmB;AAKnB,qBAAoB;AACpB,cAAyB;AACzB,gBAA2B;AAC3B,gBAA2B;AAC3B,uBAKO;AAKA,IAAM,YAAQ,8BAAkB,6BAAY;AAAA,EAClD,wBAAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAE;AAAA,IAEF,sBAAU,KAAM;AAAA,IAEhB,sBAAU,iBAAAC,KAAU,EAAE,YAAa;AAAA,EAClC;AAAA,IACC,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,IACL,SAAS;AAAA,IACT,eAAe,EAAE,SAAS,OAAO;AAAA,IACjC,QAAQ;AAAA,IACR,WAAO,gBAAI,WAAY;AAAA,IACvB,oBAAoB;AAAA,EACrB;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,IACL,SAAS;AAAA,IACT,eAAe,EAAE,SAAS,OAAO;AAAA,IACjC,QAAQ;AAAA,IACR,WAAO,gBAAI,oBAAqB;AAAA,IAChC,oBAAoB;AAAA,EACrB;AACD,CAAE;",
6
- "names": ["reducer", "coreStore"]
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { createReduxStore, register } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport reducer from './reducer';\nimport * as actions from './actions';\nimport * as selectors from './selectors';\nimport { STORE_NAME } from './constants';\n\n/**\n * The abilities store definition.\n */\nexport const store = createReduxStore( STORE_NAME, {\n\treducer,\n\tactions,\n\tselectors,\n} );\n\nregister( store );\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAA2C;AAK3C,qBAAoB;AACpB,cAAyB;AACzB,gBAA2B;AAC3B,uBAA2B;AAKpB,IAAM,YAAQ,8BAAkB,6BAAY;AAAA,EAClD,wBAAAA;AAAA,EACA;AAAA,EACA;AACD,CAAE;AAAA,IAEF,sBAAU,KAAM;",
6
+ "names": ["reducer"]
7
7
  }
@@ -56,16 +56,6 @@ function sanitizeCategory(category) {
56
56
  var DEFAULT_STATE = {};
57
57
  function abilitiesByName(state = DEFAULT_STATE, action) {
58
58
  switch (action.type) {
59
- case import_constants.RECEIVE_ABILITIES: {
60
- if (!action.abilities) {
61
- return state;
62
- }
63
- const newState = {};
64
- action.abilities.forEach((ability) => {
65
- newState[ability.name] = sanitizeAbility(ability);
66
- });
67
- return newState;
68
- }
69
59
  case import_constants.REGISTER_ABILITY: {
70
60
  if (!action.ability) {
71
61
  return state;
@@ -76,7 +66,7 @@ function abilitiesByName(state = DEFAULT_STATE, action) {
76
66
  };
77
67
  }
78
68
  case import_constants.UNREGISTER_ABILITY: {
79
- if (!action.name || !state[action.name]) {
69
+ if (!state[action.name]) {
80
70
  return state;
81
71
  }
82
72
  const { [action.name]: _, ...newState } = state;
@@ -89,16 +79,6 @@ function abilitiesByName(state = DEFAULT_STATE, action) {
89
79
  var DEFAULT_CATEGORIES_STATE = {};
90
80
  function categoriesBySlug(state = DEFAULT_CATEGORIES_STATE, action) {
91
81
  switch (action.type) {
92
- case import_constants.RECEIVE_CATEGORIES: {
93
- if (!action.categories) {
94
- return state;
95
- }
96
- const newState = {};
97
- action.categories.forEach((category) => {
98
- newState[category.slug] = sanitizeCategory(category);
99
- });
100
- return newState;
101
- }
102
82
  case import_constants.REGISTER_ABILITY_CATEGORY: {
103
83
  if (!action.category) {
104
84
  return state;
@@ -109,7 +89,7 @@ function categoriesBySlug(state = DEFAULT_CATEGORIES_STATE, action) {
109
89
  };
110
90
  }
111
91
  case import_constants.UNREGISTER_ABILITY_CATEGORY: {
112
- if (!action.slug || !state[action.slug]) {
92
+ if (!state[action.slug]) {
113
93
  return state;
114
94
  }
115
95
  const { [action.slug]: _, ...newState } = state;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/store/reducer.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { combineReducers } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport type { Ability, AbilityCategory } from '../types';\nimport {\n\tRECEIVE_ABILITIES,\n\tREGISTER_ABILITY,\n\tUNREGISTER_ABILITY,\n\tRECEIVE_CATEGORIES,\n\tREGISTER_ABILITY_CATEGORY,\n\tUNREGISTER_ABILITY_CATEGORY,\n} from './constants';\n\n/**\n * Valid keys for an Ability object.\n * Used to filter out non-standard properties from server responses.\n */\nconst ABILITY_KEYS = [\n\t'name',\n\t'label',\n\t'description',\n\t'category',\n\t'input_schema',\n\t'output_schema',\n\t'meta',\n\t'callback',\n\t'permissionCallback',\n] as const;\n\n/**\n * Valid keys for an AbilityCategory object.\n * Used to filter out non-standard properties from server responses.\n */\nconst CATEGORY_KEYS = [ 'slug', 'label', 'description', 'meta' ] as const;\n\n/**\n * Sanitizes an ability object to only include valid properties.\n * This ensures consistent shape regardless of source (server/client).\n *\n * @param ability Raw ability object that may contain extra properties.\n * @return Sanitized ability with only valid properties.\n */\nfunction sanitizeAbility( ability: any ): Ability {\n\treturn Object.keys( ability )\n\t\t.filter(\n\t\t\t( key ) =>\n\t\t\t\tABILITY_KEYS.includes( key as any ) &&\n\t\t\t\tability[ key ] !== undefined\n\t\t)\n\t\t.reduce(\n\t\t\t( obj, key ) => ( { ...obj, [ key ]: ability[ key ] } ),\n\t\t\t{} as Ability\n\t\t);\n}\n\n/**\n * Sanitizes a category object to only include valid properties.\n * This ensures consistent shape regardless of source.\n *\n * @param category Raw category object that may contain extra properties.\n * @return Sanitized category with only valid properties.\n */\nfunction sanitizeCategory( category: any ): AbilityCategory {\n\treturn Object.keys( category )\n\t\t.filter(\n\t\t\t( key ) =>\n\t\t\t\tCATEGORY_KEYS.includes( key as any ) &&\n\t\t\t\tcategory[ key ] !== undefined\n\t\t)\n\t\t.reduce(\n\t\t\t( obj, key ) => ( { ...obj, [ key ]: category[ key ] } ),\n\t\t\t{} as AbilityCategory\n\t\t);\n}\n\ninterface AbilitiesAction {\n\ttype: string;\n\tabilities?: Ability[];\n\tability?: Ability;\n\tcategories?: AbilityCategory[];\n\tcategory?: AbilityCategory;\n\tname?: string;\n\tslug?: string;\n}\n\nconst DEFAULT_STATE: Record< string, Ability > = {};\n\n/**\n * Reducer managing the abilities by name.\n *\n * @param state Current state.\n * @param action Dispatched action.\n * @return New state.\n */\nfunction abilitiesByName(\n\tstate: Record< string, Ability > = DEFAULT_STATE,\n\taction: AbilitiesAction\n): Record< string, Ability > {\n\tswitch ( action.type ) {\n\t\tcase RECEIVE_ABILITIES: {\n\t\t\tif ( ! action.abilities ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst newState: Record< string, Ability > = {};\n\t\t\taction.abilities.forEach( ( ability ) => {\n\t\t\t\tnewState[ ability.name ] = sanitizeAbility( ability );\n\t\t\t} );\n\t\t\treturn newState;\n\t\t}\n\t\tcase REGISTER_ABILITY: {\n\t\t\tif ( ! action.ability ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\t[ action.ability.name ]: sanitizeAbility( action.ability ),\n\t\t\t};\n\t\t}\n\t\tcase UNREGISTER_ABILITY: {\n\t\t\tif ( ! action.name || ! state[ action.name ] ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst { [ action.name ]: _, ...newState } = state;\n\t\t\treturn newState;\n\t\t}\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n\nconst DEFAULT_CATEGORIES_STATE: Record< string, AbilityCategory > = {};\n\n/**\n * Reducer managing the categories by slug.\n *\n * @param state Current state.\n * @param action Dispatched action.\n * @return New state.\n */\nfunction categoriesBySlug(\n\tstate: Record< string, AbilityCategory > = DEFAULT_CATEGORIES_STATE,\n\taction: AbilitiesAction\n): Record< string, AbilityCategory > {\n\tswitch ( action.type ) {\n\t\tcase RECEIVE_CATEGORIES: {\n\t\t\tif ( ! action.categories ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst newState: Record< string, AbilityCategory > = {};\n\t\t\taction.categories.forEach( ( category ) => {\n\t\t\t\tnewState[ category.slug ] = sanitizeCategory( category );\n\t\t\t} );\n\t\t\treturn newState;\n\t\t}\n\t\tcase REGISTER_ABILITY_CATEGORY: {\n\t\t\tif ( ! action.category ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\t[ action.category.slug ]: sanitizeCategory( action.category ),\n\t\t\t};\n\t\t}\n\t\tcase UNREGISTER_ABILITY_CATEGORY: {\n\t\t\tif ( ! action.slug || ! state[ action.slug ] ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst { [ action.slug ]: _, ...newState } = state;\n\t\t\treturn newState;\n\t\t}\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n\nexport default combineReducers( {\n\tabilitiesByName,\n\tcategoriesBySlug,\n} );\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { combineReducers } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport type { Ability, AbilityCategory } from '../types';\nimport {\n\tREGISTER_ABILITY,\n\tUNREGISTER_ABILITY,\n\tREGISTER_ABILITY_CATEGORY,\n\tUNREGISTER_ABILITY_CATEGORY,\n} from './constants';\n\n/**\n * Valid keys for an Ability object.\n * Used to filter out non-standard properties from server responses.\n */\nconst ABILITY_KEYS = [\n\t'name',\n\t'label',\n\t'description',\n\t'category',\n\t'input_schema',\n\t'output_schema',\n\t'meta',\n\t'callback',\n\t'permissionCallback',\n] as const;\n\n/**\n * Valid keys for an AbilityCategory object.\n * Used to filter out non-standard properties from server responses.\n */\nconst CATEGORY_KEYS = [ 'slug', 'label', 'description', 'meta' ] as const;\n\n/**\n * Sanitizes an ability object to only include valid properties.\n * This ensures consistent shape regardless of source (server/client).\n *\n * @param ability Raw ability object that may contain extra properties.\n * @return Sanitized ability with only valid properties.\n */\nfunction sanitizeAbility( ability: any ): Ability {\n\treturn Object.keys( ability )\n\t\t.filter(\n\t\t\t( key ) =>\n\t\t\t\tABILITY_KEYS.includes( key as any ) &&\n\t\t\t\tability[ key ] !== undefined\n\t\t)\n\t\t.reduce(\n\t\t\t( obj, key ) => ( { ...obj, [ key ]: ability[ key ] } ),\n\t\t\t{} as Ability\n\t\t);\n}\n\n/**\n * Sanitizes a category object to only include valid properties.\n * This ensures consistent shape regardless of source.\n *\n * @param category Raw category object that may contain extra properties.\n * @return Sanitized category with only valid properties.\n */\nfunction sanitizeCategory( category: any ): AbilityCategory {\n\treturn Object.keys( category )\n\t\t.filter(\n\t\t\t( key ) =>\n\t\t\t\tCATEGORY_KEYS.includes( key as any ) &&\n\t\t\t\tcategory[ key ] !== undefined\n\t\t)\n\t\t.reduce(\n\t\t\t( obj, key ) => ( { ...obj, [ key ]: category[ key ] } ),\n\t\t\t{} as AbilityCategory\n\t\t);\n}\n\ninterface RegisterAbilityAction {\n\ttype: typeof REGISTER_ABILITY;\n\tability: Ability;\n}\n\ninterface UnregisterAbilityAction {\n\ttype: typeof UNREGISTER_ABILITY;\n\tname: string;\n}\n\ninterface RegisterAbilityCategoryAction {\n\ttype: typeof REGISTER_ABILITY_CATEGORY;\n\tcategory: AbilityCategory;\n}\n\ninterface UnregisterAbilityCategoryAction {\n\ttype: typeof UNREGISTER_ABILITY_CATEGORY;\n\tslug: string;\n}\n\ntype AbilitiesAction = RegisterAbilityAction | UnregisterAbilityAction;\n\ntype AbilitiesCategoryAction =\n\t| RegisterAbilityCategoryAction\n\t| UnregisterAbilityCategoryAction;\n\nconst DEFAULT_STATE: Record< string, Ability > = {};\n\n/**\n * Reducer managing the abilities by name.\n *\n * @param state Current state.\n * @param action Dispatched action.\n * @return New state.\n */\nfunction abilitiesByName(\n\tstate: Record< string, Ability > = DEFAULT_STATE,\n\taction: AbilitiesAction\n): Record< string, Ability > {\n\tswitch ( action.type ) {\n\t\tcase REGISTER_ABILITY: {\n\t\t\tif ( ! action.ability ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\t[ action.ability.name ]: sanitizeAbility( action.ability ),\n\t\t\t};\n\t\t}\n\t\tcase UNREGISTER_ABILITY: {\n\t\t\tif ( ! state[ action.name ] ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst { [ action.name ]: _, ...newState } = state;\n\t\t\treturn newState;\n\t\t}\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n\nconst DEFAULT_CATEGORIES_STATE: Record< string, AbilityCategory > = {};\n\n/**\n * Reducer managing the categories by slug.\n *\n * @param state Current state.\n * @param action Dispatched action.\n * @return New state.\n */\nfunction categoriesBySlug(\n\tstate: Record< string, AbilityCategory > = DEFAULT_CATEGORIES_STATE,\n\taction: AbilitiesCategoryAction\n): Record< string, AbilityCategory > {\n\tswitch ( action.type ) {\n\t\tcase REGISTER_ABILITY_CATEGORY: {\n\t\t\tif ( ! action.category ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\t[ action.category.slug ]: sanitizeCategory( action.category ),\n\t\t\t};\n\t\t}\n\t\tcase UNREGISTER_ABILITY_CATEGORY: {\n\t\t\tif ( ! state[ action.slug ] ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst { [ action.slug ]: _, ...newState } = state;\n\t\t\treturn newState;\n\t\t}\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n\nexport default combineReducers( {\n\tabilitiesByName,\n\tcategoriesBySlug,\n} );\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAAgC;AAMhC,uBAKO;AAMP,IAAM,eAAe;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAMA,IAAM,gBAAgB,CAAE,QAAQ,SAAS,eAAe,MAAO;AAS/D,SAAS,gBAAiB,SAAwB;AACjD,SAAO,OAAO,KAAM,OAAQ,EAC1B;AAAA,IACA,CAAE,QACD,aAAa,SAAU,GAAW,KAClC,QAAS,GAAI,MAAM;AAAA,EACrB,EACC;AAAA,IACA,CAAE,KAAK,SAAW,EAAE,GAAG,KAAK,CAAE,GAAI,GAAG,QAAS,GAAI,EAAE;AAAA,IACpD,CAAC;AAAA,EACF;AACF;AASA,SAAS,iBAAkB,UAAiC;AAC3D,SAAO,OAAO,KAAM,QAAS,EAC3B;AAAA,IACA,CAAE,QACD,cAAc,SAAU,GAAW,KACnC,SAAU,GAAI,MAAM;AAAA,EACtB,EACC;AAAA,IACA,CAAE,KAAK,SAAW,EAAE,GAAG,KAAK,CAAE,GAAI,GAAG,SAAU,GAAI,EAAE;AAAA,IACrD,CAAC;AAAA,EACF;AACF;AA4BA,IAAM,gBAA2C,CAAC;AASlD,SAAS,gBACR,QAAmC,eACnC,QAC4B;AAC5B,UAAS,OAAO,MAAO;AAAA,IACtB,KAAK,mCAAkB;AACtB,UAAK,CAAE,OAAO,SAAU;AACvB,eAAO;AAAA,MACR;AACA,aAAO;AAAA,QACN,GAAG;AAAA,QACH,CAAE,OAAO,QAAQ,IAAK,GAAG,gBAAiB,OAAO,OAAQ;AAAA,MAC1D;AAAA,IACD;AAAA,IACA,KAAK,qCAAoB;AACxB,UAAK,CAAE,MAAO,OAAO,IAAK,GAAI;AAC7B,eAAO;AAAA,MACR;AACA,YAAM,EAAE,CAAE,OAAO,IAAK,GAAG,GAAG,GAAG,SAAS,IAAI;AAC5C,aAAO;AAAA,IACR;AAAA,IACA;AACC,aAAO;AAAA,EACT;AACD;AAEA,IAAM,2BAA8D,CAAC;AASrE,SAAS,iBACR,QAA2C,0BAC3C,QACoC;AACpC,UAAS,OAAO,MAAO;AAAA,IACtB,KAAK,4CAA2B;AAC/B,UAAK,CAAE,OAAO,UAAW;AACxB,eAAO;AAAA,MACR;AACA,aAAO;AAAA,QACN,GAAG;AAAA,QACH,CAAE,OAAO,SAAS,IAAK,GAAG,iBAAkB,OAAO,QAAS;AAAA,MAC7D;AAAA,IACD;AAAA,IACA,KAAK,8CAA6B;AACjC,UAAK,CAAE,MAAO,OAAO,IAAK,GAAI;AAC7B,eAAO;AAAA,MACR;AACA,YAAM,EAAE,CAAE,OAAO,IAAK,GAAG,GAAG,GAAG,SAAS,IAAI;AAC5C,aAAO;AAAA,IACR;AAAA,IACA;AACC,aAAO;AAAA,EACT;AACD;AAEA,IAAO,sBAAQ,6BAAiB;AAAA,EAC/B;AAAA,EACA;AACD,CAAE;",
6
6
  "names": []
7
7
  }
@@ -37,7 +37,7 @@ var getAbilities = (0, import_data.createSelector)(
37
37
  }
38
38
  return abilities;
39
39
  },
40
- (state, category) => [
40
+ (state, { category } = {}) => [
41
41
  state.abilitiesByName,
42
42
  category
43
43
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/store/selectors.ts"],
4
- "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { createSelector } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport type {\n\tAbility,\n\tAbilityCategory,\n\tAbilitiesQueryArgs,\n\tAbilitiesState,\n} from '../types';\n\n/**\n * Returns all registered abilities.\n * Optionally filters by query arguments.\n *\n * @param state Store state.\n * @param args Optional query arguments to filter. Defaults to empty object.\n * @return Array of abilities.\n */\nexport const getAbilities = createSelector(\n\t(\n\t\tstate: AbilitiesState,\n\t\t{ category }: AbilitiesQueryArgs = {}\n\t): Ability[] => {\n\t\tconst abilities = Object.values( state.abilitiesByName );\n\t\tif ( category ) {\n\t\t\treturn abilities.filter(\n\t\t\t\t( ability ) => ability.category === category\n\t\t\t);\n\t\t}\n\t\treturn abilities;\n\t},\n\t( state: AbilitiesState, category?: string ) => [\n\t\tstate.abilitiesByName,\n\t\tcategory,\n\t]\n);\n\n/**\n * Returns a specific ability by name.\n *\n * @param state Store state.\n * @param name Ability name.\n * @return Ability object or undefined if not found.\n */\nexport function getAbility(\n\tstate: AbilitiesState,\n\tname: string\n): Ability | undefined {\n\treturn state.abilitiesByName[ name ];\n}\n\n/**\n * Returns all registered ability categories.\n *\n * @param state Store state.\n * @return Array of categories.\n */\nexport const getAbilityCategories = createSelector(\n\t( state: AbilitiesState ): AbilityCategory[] => {\n\t\treturn Object.values( state.categoriesBySlug );\n\t},\n\t( state: AbilitiesState ) => [ state.categoriesBySlug ]\n);\n\n/**\n * Returns a specific ability category by slug.\n *\n * @param state Store state.\n * @param slug Category slug.\n * @return Category object or undefined if not found.\n */\nexport function getAbilityCategory(\n\tstate: AbilitiesState,\n\tslug: string\n): AbilityCategory | undefined {\n\treturn state.categoriesBySlug[ slug ];\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAA+B;AAoBxB,IAAM,mBAAe;AAAA,EAC3B,CACC,OACA,EAAE,SAAS,IAAwB,CAAC,MACrB;AACf,UAAM,YAAY,OAAO,OAAQ,MAAM,eAAgB;AACvD,QAAK,UAAW;AACf,aAAO,UAAU;AAAA,QAChB,CAAE,YAAa,QAAQ,aAAa;AAAA,MACrC;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EACA,CAAE,OAAuB,aAAuB;AAAA,IAC/C,MAAM;AAAA,IACN;AAAA,EACD;AACD;AASO,SAAS,WACf,OACA,MACsB;AACtB,SAAO,MAAM,gBAAiB,IAAK;AACpC;AAQO,IAAM,2BAAuB;AAAA,EACnC,CAAE,UAA8C;AAC/C,WAAO,OAAO,OAAQ,MAAM,gBAAiB;AAAA,EAC9C;AAAA,EACA,CAAE,UAA2B,CAAE,MAAM,gBAAiB;AACvD;AASO,SAAS,mBACf,OACA,MAC8B;AAC9B,SAAO,MAAM,iBAAkB,IAAK;AACrC;",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { createSelector } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport type { Ability, AbilityCategory, AbilitiesQueryArgs } from '../types';\nimport type { AbilitiesState } from './types';\n\n/**\n * Returns all registered abilities.\n * Optionally filters by query arguments.\n *\n * @param state Store state.\n * @param args Optional query arguments to filter. Defaults to empty object.\n * @return Array of abilities.\n */\nexport const getAbilities = createSelector(\n\t(\n\t\tstate: AbilitiesState,\n\t\t{ category }: AbilitiesQueryArgs = {}\n\t): Ability[] => {\n\t\tconst abilities = Object.values( state.abilitiesByName );\n\t\tif ( category ) {\n\t\t\treturn abilities.filter(\n\t\t\t\t( ability ) => ability.category === category\n\t\t\t);\n\t\t}\n\t\treturn abilities;\n\t},\n\t( state: AbilitiesState, { category }: AbilitiesQueryArgs = {} ) => [\n\t\tstate.abilitiesByName,\n\t\tcategory,\n\t]\n);\n\n/**\n * Returns a specific ability by name.\n *\n * @param state Store state.\n * @param name Ability name.\n * @return Ability object or undefined if not found.\n */\nexport function getAbility(\n\tstate: AbilitiesState,\n\tname: string\n): Ability | undefined {\n\treturn state.abilitiesByName[ name ];\n}\n\n/**\n * Returns all registered ability categories.\n *\n * @param state Store state.\n * @return Array of categories.\n */\nexport const getAbilityCategories = createSelector(\n\t( state: AbilitiesState ): AbilityCategory[] => {\n\t\treturn Object.values( state.categoriesBySlug );\n\t},\n\t( state: AbilitiesState ) => [ state.categoriesBySlug ]\n);\n\n/**\n * Returns a specific ability category by slug.\n *\n * @param state Store state.\n * @param slug Category slug.\n * @return Category object or undefined if not found.\n */\nexport function getAbilityCategory(\n\tstate: AbilitiesState,\n\tslug: string\n): AbilityCategory | undefined {\n\treturn state.categoriesBySlug[ slug ];\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAA+B;AAgBxB,IAAM,mBAAe;AAAA,EAC3B,CACC,OACA,EAAE,SAAS,IAAwB,CAAC,MACrB;AACf,UAAM,YAAY,OAAO,OAAQ,MAAM,eAAgB;AACvD,QAAK,UAAW;AACf,aAAO,UAAU;AAAA,QAChB,CAAE,YAAa,QAAQ,aAAa;AAAA,MACrC;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EACA,CAAE,OAAuB,EAAE,SAAS,IAAwB,CAAC,MAAO;AAAA,IACnE,MAAM;AAAA,IACN;AAAA,EACD;AACD;AASO,SAAS,WACf,OACA,MACsB;AACtB,SAAO,MAAM,gBAAiB,IAAK;AACpC;AAQO,IAAM,2BAAuB;AAAA,EACnC,CAAE,UAA8C;AAC/C,WAAO,OAAO,OAAQ,MAAM,gBAAiB;AAAA,EAC9C;AAAA,EACA,CAAE,UAA2B,CAAE,MAAM,gBAAiB;AACvD;AASO,SAAS,mBACf,OACA,MAC8B;AAC9B,SAAO,MAAM,iBAAkB,IAAK;AACrC;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,19 @@
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 __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // packages/abilities/src/store/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/store/types.ts"],
4
+ "sourcesContent": ["/**\n * Internal dependencies\n */\nimport type { Ability, AbilityCategory } from '../types';\n\n/**\n * The state shape for the abilities store.\n */\nexport interface AbilitiesState {\n\t/**\n\t * Map of ability names to ability objects.\n\t */\n\tabilitiesByName: Record< string, Ability >;\n\n\t/**\n\t * Map of category slugs to category objects.\n\t */\n\tcategoriesBySlug: Record< string, AbilityCategory >;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/types.ts"],
4
- "sourcesContent": ["/**\n * WordPress Abilities API Types\n */\n\n/**\n * Callback function for client-side abilities.\n */\nexport type AbilityCallback = (\n\tinput: AbilityInput\n) => AbilityOutput | Promise< AbilityOutput >;\n\n/**\n * Permission callback function for client-side abilities.\n * Returns true if the ability can be executed, false otherwise.\n */\nexport type PermissionCallback = (\n\tinput?: AbilityInput\n) => boolean | Promise< boolean >;\n\n/**\n * Represents an ability in the WordPress Abilities API.\n *\n * @see WP_Ability\n */\nexport interface Ability {\n\t/**\n\t * The unique name/identifier of the ability, with its namespace.\n\t * Example: 'my-plugin/my-ability'\n\t * @see WP_Ability::get_name()\n\t */\n\tname: string;\n\n\t/**\n\t * The human-readable label for the ability.\n\t * @see WP_Ability::get_label()\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the ability.\n\t * @see WP_Ability::get_description()\n\t */\n\tdescription: string;\n\n\t/**\n\t * The category this ability belongs to.\n\t * Must be a valid category slug (lowercase alphanumeric with dashes).\n\t * Example: 'data-retrieval', 'user-management'\n\t * @see WP_Ability::get_category()\n\t */\n\tcategory: string;\n\n\t/**\n\t * JSON Schema for the ability's input parameters.\n\t * @see WP_Ability::get_input_schema()\n\t */\n\tinput_schema?: Record< string, any >;\n\n\t/**\n\t * JSON Schema for the ability's output format.\n\t * @see WP_Ability::get_output_schema()\n\t */\n\toutput_schema?: Record< string, any >;\n\n\t/**\n\t * Callback function for client-side abilities.\n\t * If present, the ability will be executed locally in the browser.\n\t * If not present, the ability will be executed via REST API on the server.\n\t */\n\tcallback?: AbilityCallback;\n\n\t/**\n\t * Client Permission callback for abilities.\n\t * Called before executing the ability to check if it's allowed.\n\t * If it returns false, the ability execution will be denied.\n\t */\n\tpermissionCallback?: PermissionCallback;\n\n\t/**\n\t * Metadata about the ability.\n\t * @see WP_Ability::get_meta()\n\t */\n\tmeta?: {\n\t\tannotations?: {\n\t\t\treadonly?: boolean | null;\n\t\t\tdestructive?: boolean | null;\n\t\t\tidempotent?: boolean | null;\n\t\t};\n\t\t[ key: string ]: any;\n\t};\n}\n\n/**\n * The shape of the arguments for querying abilities.\n */\nexport interface AbilitiesQueryArgs {\n\t/**\n\t * Optional category slug to filter abilities.\n\t */\n\tcategory?: string;\n}\n\n/**\n * Represents an ability category in the WordPress Abilities API.\n *\n * @see WP_Ability_Category\n */\nexport interface AbilityCategory {\n\t/**\n\t * The unique slug identifier for the category.\n\t * Must be lowercase alphanumeric with dashes only.\n\t * Example: 'data-retrieval', 'user-management'\n\t * @see WP_Ability_Category::get_slug()\n\t */\n\tslug: string;\n\n\t/**\n\t * The human-readable label for the category.\n\t * @see WP_Ability_Category::get_label()\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the category.\n\t * @see WP_Ability_Category::get_description()\n\t */\n\tdescription: string;\n\n\t/**\n\t * Metadata about the category.\n\t * @see WP_Ability_Category::get_meta()\n\t */\n\tmeta?: Record< string, any >;\n}\n\n/**\n * Arguments for registering an ability category.\n * Matches the server-side wp_register_ability_category() $args parameter.\n *\n * @see wp_register_ability_category()\n */\nexport interface AbilityCategoryArgs {\n\t/**\n\t * The human-readable label for the category.\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the category.\n\t */\n\tdescription: string;\n\n\t/**\n\t * Optional metadata about the category.\n\t */\n\tmeta?: Record< string, any >;\n}\n\n/**\n * The state shape for the abilities store.\n */\nexport interface AbilitiesState {\n\t/**\n\t * Map of ability names to ability objects.\n\t */\n\tabilitiesByName: Record< string, Ability >;\n\n\t/**\n\t * Map of category slugs to category objects.\n\t */\n\tcategoriesBySlug: Record< string, AbilityCategory >;\n}\n\n/**\n * Input parameters for ability execution.\n * Can be any JSON-serializable value: primitive, array, object, or null.\n */\nexport type AbilityInput = any;\n\n/**\n * Result from ability execution.\n * The actual shape depends on the ability's output schema.\n */\nexport type AbilityOutput = any;\n\n/**\n * Validation error - just a message string.\n * The Abilities API wraps this with the appropriate error code.\n */\nexport type ValidationError = string;\n"],
4
+ "sourcesContent": ["/**\n * WordPress Abilities API Types\n */\n\n/**\n * Callback function for client-side abilities.\n */\nexport type AbilityCallback = (\n\tinput: AbilityInput\n) => AbilityOutput | Promise< AbilityOutput >;\n\n/**\n * Permission callback function for client-side abilities.\n * Returns true if the ability can be executed, false otherwise.\n */\nexport type PermissionCallback = (\n\tinput?: AbilityInput\n) => boolean | Promise< boolean >;\n\n/**\n * Represents an ability in the WordPress Abilities API.\n *\n * @see WP_Ability\n */\nexport interface Ability {\n\t/**\n\t * The unique name/identifier of the ability, with its namespace.\n\t * Example: 'my-plugin/my-ability'\n\t * @see WP_Ability::get_name()\n\t */\n\tname: string;\n\n\t/**\n\t * The human-readable label for the ability.\n\t * @see WP_Ability::get_label()\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the ability.\n\t * @see WP_Ability::get_description()\n\t */\n\tdescription: string;\n\n\t/**\n\t * The category this ability belongs to.\n\t * Must be a valid category slug (lowercase alphanumeric with dashes).\n\t * Example: 'data-retrieval', 'user-management'\n\t * @see WP_Ability::get_category()\n\t */\n\tcategory: string;\n\n\t/**\n\t * JSON Schema for the ability's input parameters.\n\t * @see WP_Ability::get_input_schema()\n\t */\n\tinput_schema?: Record< string, any >;\n\n\t/**\n\t * JSON Schema for the ability's output format.\n\t * @see WP_Ability::get_output_schema()\n\t */\n\toutput_schema?: Record< string, any >;\n\n\t/**\n\t * Callback function for ability execution.\n\t * This property is required for all abilities.\n\t */\n\tcallback?: AbilityCallback;\n\n\t/**\n\t * Permission callback for abilities.\n\t * Called before executing the ability to check if it's allowed.\n\t * If it returns false, the ability execution will be denied.\n\t */\n\tpermissionCallback?: PermissionCallback;\n\n\t/**\n\t * Metadata about the ability.\n\t */\n\tmeta?: {\n\t\tannotations?: {\n\t\t\tclientRegistered?: boolean;\n\t\t\tserverRegistered?: boolean;\n\t\t\treadonly?: boolean;\n\t\t\tdestructive?: boolean;\n\t\t\tidempotent?: boolean;\n\t\t};\n\t};\n}\n\n/**\n * The shape of the arguments for querying abilities.\n */\nexport interface AbilitiesQueryArgs {\n\t/**\n\t * Optional category slug to filter abilities.\n\t */\n\tcategory?: string;\n}\n\n/**\n * Represents an ability category in the WordPress Abilities API.\n *\n * @see WP_Ability_Category\n */\nexport interface AbilityCategory {\n\t/**\n\t * The unique slug identifier for the category.\n\t * Must be lowercase alphanumeric with dashes only.\n\t * Example: 'data-retrieval', 'user-management'\n\t * @see WP_Ability_Category::get_slug()\n\t */\n\tslug: string;\n\n\t/**\n\t * The human-readable label for the category.\n\t * @see WP_Ability_Category::get_label()\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the category.\n\t * @see WP_Ability_Category::get_description()\n\t */\n\tdescription: string;\n\n\t/**\n\t * Metadata about the category.\n\t */\n\tmeta?: {\n\t\tannotations?: {\n\t\t\tclientRegistered?: boolean;\n\t\t\tserverRegistered?: boolean;\n\t\t};\n\t};\n}\n\n/**\n * Arguments for registering an ability category.\n * Matches the server-side wp_register_ability_category() $args parameter.\n *\n * @see wp_register_ability_category()\n */\nexport interface AbilityCategoryArgs {\n\t/**\n\t * The human-readable label for the category.\n\t */\n\tlabel: string;\n\n\t/**\n\t * The detailed description of the category.\n\t */\n\tdescription: string;\n\n\t/**\n\t * Optional metadata about the category.\n\t */\n\tmeta?: Record< string, any >;\n}\n\n/**\n * Input parameters for ability execution.\n * Can be any JSON-serializable value: primitive, array, object, or null.\n */\nexport type AbilityInput = any;\n\n/**\n * Result from ability execution.\n * The actual shape depends on the ability's output schema.\n */\nexport type AbilityOutput = any;\n\n/**\n * Validation error - just a message string.\n * The Abilities API wraps this with the appropriate error code.\n */\nexport type ValidationError = string;\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -1,39 +1,41 @@
1
1
  // packages/abilities/src/api.ts
2
- import { dispatch, resolveSelect } from "@wordpress/data";
3
- import apiFetch from "@wordpress/api-fetch";
4
- import { addQueryArgs } from "@wordpress/url";
2
+ import { dispatch, select } from "@wordpress/data";
5
3
  import { sprintf } from "@wordpress/i18n";
6
4
  import { store } from "./store";
7
5
  import { validateValueFromSchema } from "./validation";
8
- async function getAbilities(args = {}) {
9
- return await resolveSelect(store).getAbilities(args);
6
+ function getAbilities(args = {}) {
7
+ return select(store).getAbilities(args);
10
8
  }
11
- async function getAbility(name) {
12
- return await resolveSelect(store).getAbility(name);
9
+ function getAbility(name) {
10
+ return select(store).getAbility(name);
13
11
  }
14
- async function getAbilityCategories() {
15
- return await resolveSelect(store).getAbilityCategories();
12
+ function getAbilityCategories() {
13
+ return select(store).getAbilityCategories();
16
14
  }
17
- async function getAbilityCategory(slug) {
18
- return await resolveSelect(store).getAbilityCategory(slug);
15
+ function getAbilityCategory(slug) {
16
+ return select(store).getAbilityCategory(slug);
19
17
  }
20
- async function registerAbility(ability) {
21
- await dispatch(store).registerAbility(ability);
18
+ function registerAbility(ability) {
19
+ dispatch(store).registerAbility(ability);
22
20
  }
23
21
  function unregisterAbility(name) {
24
22
  dispatch(store).unregisterAbility(name);
25
23
  }
26
- async function registerAbilityCategory(slug, args) {
27
- await dispatch(store).registerAbilityCategory(slug, args);
24
+ function registerAbilityCategory(slug, args) {
25
+ dispatch(store).registerAbilityCategory(slug, args);
28
26
  }
29
27
  function unregisterAbilityCategory(slug) {
30
28
  dispatch(store).unregisterAbilityCategory(slug);
31
29
  }
32
- async function executeClientAbility(ability, input) {
30
+ async function executeAbility(name, input) {
31
+ const ability = getAbility(name);
32
+ if (!ability) {
33
+ throw new Error(sprintf("Ability not found: %s", name));
34
+ }
33
35
  if (!ability.callback) {
34
36
  throw new Error(
35
37
  sprintf(
36
- "Client ability %s is missing callback function",
38
+ 'Ability "%s" is missing callback. Please ensure the ability is properly registered.',
37
39
  ability.name
38
40
  )
39
41
  );
@@ -70,10 +72,7 @@ async function executeClientAbility(ability, input) {
70
72
  try {
71
73
  result = await ability.callback(input);
72
74
  } catch (error) {
73
- console.error(
74
- `Error executing client ability ${ability.name}:`,
75
- error
76
- );
75
+ console.error(`Error executing ability ${ability.name}:`, error);
77
76
  throw error;
78
77
  }
79
78
  if (ability.output_schema) {
@@ -96,42 +95,6 @@ async function executeClientAbility(ability, input) {
96
95
  }
97
96
  return result;
98
97
  }
99
- async function executeServerAbility(ability, input) {
100
- let method = "POST";
101
- if (!!ability.meta?.annotations?.readonly) {
102
- method = "GET";
103
- } else if (!!ability.meta?.annotations?.destructive && !!ability.meta?.annotations?.idempotent) {
104
- method = "DELETE";
105
- }
106
- let path = `/wp-abilities/v1/abilities/${ability.name}/run`;
107
- const options = {
108
- method
109
- };
110
- if (["GET", "DELETE"].includes(method) && input !== null) {
111
- path = addQueryArgs(path, { input });
112
- } else if (method === "POST" && input !== null) {
113
- options.data = { input };
114
- }
115
- try {
116
- return await apiFetch({
117
- path,
118
- ...options
119
- });
120
- } catch (error) {
121
- console.error(`Error executing ability ${ability.name}:`, error);
122
- throw error;
123
- }
124
- }
125
- async function executeAbility(name, input) {
126
- const ability = await getAbility(name);
127
- if (!ability) {
128
- throw new Error(sprintf("Ability not found: %s", name));
129
- }
130
- if (ability.callback) {
131
- return executeClientAbility(ability, input);
132
- }
133
- return executeServerAbility(ability, input);
134
- }
135
98
  export {
136
99
  executeAbility,
137
100
  getAbilities,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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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",
4
+ "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { dispatch, select } from '@wordpress/data';\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 Array of abilities.\n */\nexport function getAbilities( args: AbilitiesQueryArgs = {} ): Ability[] {\n\treturn select( store ).getAbilities( args );\n}\n\n/**\n * Get a specific ability by name.\n *\n * @param name The ability name.\n * @return The ability or undefined if not found.\n */\nexport function getAbility( name: string ): Ability | undefined {\n\treturn select( store ).getAbility( name );\n}\n\n/**\n * Get all available ability categories.\n *\n * @return Array of categories.\n */\nexport function getAbilityCategories(): AbilityCategory[] {\n\treturn select( store ).getAbilityCategories();\n}\n\n/**\n * Get a specific ability category by slug.\n *\n * @param slug The category slug.\n * @return The category or undefined if not found.\n */\nexport function getAbilityCategory(\n\tslug: string\n): AbilityCategory | undefined {\n\treturn select( 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 * The category must already be registered before registering abilities.\n *\n * @param ability The ability definition including callback.\n * @throws {Error} If the ability fails validation.\n *\n * @example\n * ```js\n * 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 function registerAbility( ability: Ability ): void {\n\tdispatch( 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 * @param slug Category slug (lowercase alphanumeric with dashes only).\n * @param args Category arguments (label, description, optional meta).\n * @throws {Error} If the category fails validation.\n *\n * @example\n * ```js\n * // Register a new category for block editor abilities\n * 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 * 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 function registerAbilityCategory(\n\tslug: string,\n\targs: AbilityCategoryArgs\n): void {\n\tdispatch( 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 an ability.\n *\n * Executes abilities with validation for client-side abilities only.\n * Server abilities bypass validation as it's handled on the server.\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 = 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\tthrow new Error(\n\t\t\tsprintf(\n\t\t\t\t'Ability \"%s\" is missing callback. Please ensure the ability is properly registered.',\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\t// Validate input\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\t// Execute the ability\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( `Error executing ability ${ ability.name }:`, error );\n\t\tthrow error;\n\t}\n\n\t// Validate output\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"],
5
+ "mappings": ";AAGA,SAAS,UAAU,cAAc;AACjC,SAAS,eAAe;AAKxB,SAAS,aAAa;AAStB,SAAS,+BAA+B;AAQjC,SAAS,aAAc,OAA2B,CAAC,GAAe;AACxE,SAAO,OAAQ,KAAM,EAAE,aAAc,IAAK;AAC3C;AAQO,SAAS,WAAY,MAAoC;AAC/D,SAAO,OAAQ,KAAM,EAAE,WAAY,IAAK;AACzC;AAOO,SAAS,uBAA0C;AACzD,SAAO,OAAQ,KAAM,EAAE,qBAAqB;AAC7C;AAQO,SAAS,mBACf,MAC8B;AAC9B,SAAO,OAAQ,KAAM,EAAE,mBAAoB,IAAK;AACjD;AAmCO,SAAS,gBAAiB,SAAyB;AACzD,WAAU,KAAM,EAAE,gBAAiB,OAAQ;AAC5C;AAUO,SAAS,kBAAmB,MAAqB;AACvD,WAAU,KAAM,EAAE,kBAAmB,IAAK;AAC3C;AAmCO,SAAS,wBACf,MACA,MACO;AACP,WAAU,KAAM,EAAE,wBAAyB,MAAM,IAAK;AACvD;AAcO,SAAS,0BAA2B,MAAqB;AAC/D,WAAU,KAAM,EAAE,0BAA2B,IAAK;AACnD;AAaA,eAAsB,eACrB,MACA,OAC2B;AAC3B,QAAM,UAAU,WAAY,IAAK;AACjC,MAAK,CAAE,SAAU;AAChB,UAAM,IAAI,MAAO,QAAS,yBAAyB,IAAK,CAAE;AAAA,EAC3D;AAEA,MAAK,CAAE,QAAQ,UAAW;AACzB,UAAM,IAAI;AAAA,MACT;AAAA,QACC;AAAA,QACA,QAAQ;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAGA,MAAK,QAAQ,oBAAqB;AACjC,UAAM,gBAAgB,MAAM,QAAQ,mBAAoB,KAAM;AAC9D,QAAK,CAAE,eAAgB;AACtB,YAAM,QAAQ,IAAI;AAAA,QACjB,QAAS,qCAAqC,QAAQ,IAAK;AAAA,MAC5D;AACA,MAAE,MAAe,OAAO;AACxB,YAAM;AAAA,IACP;AAAA,EACD;AAGA,MAAK,QAAQ,cAAe;AAC3B,UAAM,kBAAkB;AAAA,MACvB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACD;AACA,QAAK,oBAAoB,MAAO;AAC/B,YAAM,QAAQ,IAAI;AAAA,QACjB;AAAA,UACC;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AACA,MAAE,MAAe,OAAO;AACxB,YAAM;AAAA,IACP;AAAA,EACD;AAGA,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,QAAQ,SAAU,KAAM;AAAA,EACxC,SAAU,OAAQ;AAEjB,YAAQ,MAAO,2BAA4B,QAAQ,IAAK,KAAK,KAAM;AACnE,UAAM;AAAA,EACP;AAGA,MAAK,QAAQ,eAAgB;AAC5B,UAAM,mBAAmB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACD;AACA,QAAK,qBAAqB,MAAO;AAChC,YAAM,QAAQ,IAAI;AAAA,QACjB;AAAA,UACC;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AACA,MAAE,MAAe,OAAO;AACxB,YAAM;AAAA,IACP;AAAA,EACD;AAEA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAoBP,SAAS,aAAa;AAqBtB,SAAS,+BAA+B;",
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\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,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAoBP,SAAS,aAAa;AAoBtB,SAAS,+BAA+B;",
6
6
  "names": []
7
7
  }
@@ -1,33 +1,30 @@
1
1
  // packages/abilities/src/store/actions.ts
2
2
  import { sprintf } from "@wordpress/i18n";
3
- import { resolveSelect } from "@wordpress/data";
4
3
  import {
5
- RECEIVE_ABILITIES,
6
4
  REGISTER_ABILITY,
7
5
  UNREGISTER_ABILITY,
8
- RECEIVE_CATEGORIES,
9
6
  REGISTER_ABILITY_CATEGORY,
10
7
  UNREGISTER_ABILITY_CATEGORY,
11
- STORE_NAME
8
+ ABILITY_NAME_PATTERN,
9
+ CATEGORY_SLUG_PATTERN
12
10
  } from "./constants";
13
- function receiveAbilities(abilities) {
14
- return {
15
- type: RECEIVE_ABILITIES,
16
- abilities
17
- };
18
- }
19
- function receiveCategories(categories) {
20
- return {
21
- type: RECEIVE_CATEGORIES,
22
- categories
23
- };
11
+ function filterAnnotations(sourceAnnotations, allowedKeys) {
12
+ const annotations = {};
13
+ if (sourceAnnotations) {
14
+ for (const key of allowedKeys) {
15
+ if (sourceAnnotations[key] !== void 0) {
16
+ annotations[key] = sourceAnnotations[key];
17
+ }
18
+ }
19
+ }
20
+ return annotations;
24
21
  }
25
22
  function registerAbility(ability) {
26
- return async ({ select, dispatch }) => {
23
+ return ({ select, dispatch }) => {
27
24
  if (!ability.name) {
28
25
  throw new Error("Ability name is required");
29
26
  }
30
- if (!/^[a-z0-9-]+\/[a-z0-9-]+$/.test(ability.name)) {
27
+ if (!ABILITY_NAME_PATTERN.test(ability.name)) {
31
28
  throw new Error(
32
29
  'Ability name must be a string containing a namespace prefix, i.e. "my-plugin/my-ability". It can only contain lowercase alphanumeric characters, dashes and the forward slash.'
33
30
  );
@@ -47,16 +44,16 @@ function registerAbility(ability) {
47
44
  sprintf('Ability "%s" must have a category', ability.name)
48
45
  );
49
46
  }
50
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(ability.category)) {
47
+ if (!CATEGORY_SLUG_PATTERN.test(ability.category)) {
51
48
  throw new Error(
52
49
  sprintf(
53
- 'Ability "%1$s" has an invalid category. Category must be lowercase alphanumeric with dashes only Got: "%2$s"',
50
+ 'Ability "%1$s" has an invalid category. Category must be lowercase alphanumeric with dashes only. Got: "%2$s"',
54
51
  ability.name,
55
52
  ability.category
56
53
  )
57
54
  );
58
55
  }
59
- const categories = await resolveSelect(STORE_NAME).getAbilityCategories();
56
+ const categories = select.getAbilityCategories();
60
57
  const existingCategory = categories.find(
61
58
  (cat) => cat.slug === ability.category
62
59
  );
@@ -83,9 +80,23 @@ function registerAbility(ability) {
83
80
  sprintf('Ability "%s" is already registered', ability.name)
84
81
  );
85
82
  }
83
+ const annotations = filterAnnotations(ability.meta?.annotations, [
84
+ "readonly",
85
+ "destructive",
86
+ "idempotent",
87
+ "serverRegistered",
88
+ "clientRegistered"
89
+ ]);
90
+ if (!annotations.serverRegistered) {
91
+ annotations.clientRegistered = true;
92
+ }
93
+ const meta = { annotations };
86
94
  dispatch({
87
95
  type: REGISTER_ABILITY,
88
- ability
96
+ ability: {
97
+ ...ability,
98
+ meta
99
+ }
89
100
  });
90
101
  };
91
102
  }
@@ -96,16 +107,15 @@ function unregisterAbility(name) {
96
107
  };
97
108
  }
98
109
  function registerAbilityCategory(slug, args) {
99
- return async ({ select, dispatch }) => {
110
+ return ({ select, dispatch }) => {
100
111
  if (!slug) {
101
112
  throw new Error("Category slug is required");
102
113
  }
103
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
114
+ if (!CATEGORY_SLUG_PATTERN.test(slug)) {
104
115
  throw new Error(
105
116
  "Category slug must contain only lowercase alphanumeric characters and dashes."
106
117
  );
107
118
  }
108
- await resolveSelect(STORE_NAME).getAbilityCategories();
109
119
  const existingCategory = select.getAbilityCategory(slug);
110
120
  if (existingCategory) {
111
121
  throw new Error(
@@ -127,16 +137,19 @@ function registerAbilityCategory(slug, args) {
127
137
  "The category properties should provide a valid `meta` object."
128
138
  );
129
139
  }
140
+ const annotations = filterAnnotations(args.meta?.annotations, [
141
+ "serverRegistered",
142
+ "clientRegistered"
143
+ ]);
144
+ if (!annotations.serverRegistered) {
145
+ annotations.clientRegistered = true;
146
+ }
147
+ const meta = { annotations };
130
148
  const category = {
131
149
  slug,
132
150
  label: args.label,
133
151
  description: args.description,
134
- meta: {
135
- ...args.meta || {},
136
- // Internal implementation note: Client-registered categories will have `meta._clientRegistered` set to `true` to differentiate them from server-fetched categories.
137
- // This is used internally by the resolver to determine whether to fetch categories from the server.
138
- _clientRegistered: true
139
- }
152
+ meta
140
153
  };
141
154
  dispatch({
142
155
  type: REGISTER_ABILITY_CATEGORY,
@@ -151,8 +164,6 @@ function unregisterAbilityCategory(slug) {
151
164
  };
152
165
  }
153
166
  export {
154
- receiveAbilities,
155
- receiveCategories,
156
167
  registerAbility,
157
168
  registerAbilityCategory,
158
169
  unregisterAbility,