wp-native-client 0.0.1
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/LICENSE +23 -0
- package/README.md +41 -0
- package/dist/abilities/catalog.d.ts +49 -0
- package/dist/abilities/catalog.d.ts.map +1 -0
- package/dist/abilities/catalog.js +65 -0
- package/dist/abilities/catalog.js.map +1 -0
- package/dist/abilities/discovery.d.ts +43 -0
- package/dist/abilities/discovery.d.ts.map +1 -0
- package/dist/abilities/discovery.js +82 -0
- package/dist/abilities/discovery.js.map +1 -0
- package/dist/abilities/index.d.ts +7 -0
- package/dist/abilities/index.d.ts.map +1 -0
- package/dist/abilities/index.js +6 -0
- package/dist/abilities/index.js.map +1 -0
- package/dist/abilities/types.d.ts +88 -0
- package/dist/abilities/types.d.ts.map +1 -0
- package/dist/abilities/types.js +15 -0
- package/dist/abilities/types.js.map +1 -0
- package/dist/client.d.ts +110 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +142 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/transports/auth-fetch.d.ts +132 -0
- package/dist/transports/auth-fetch.d.ts.map +1 -0
- package/dist/transports/auth-fetch.js +217 -0
- package/dist/transports/auth-fetch.js.map +1 -0
- package/dist/transports/fetch.d.ts +42 -0
- package/dist/transports/fetch.d.ts.map +1 -0
- package/dist/transports/fetch.js +68 -0
- package/dist/transports/fetch.js.map +1 -0
- package/dist/transports/types.d.ts +22 -0
- package/dist/transports/types.d.ts.map +1 -0
- package/dist/transports/types.js +9 -0
- package/dist/transports/types.js.map +1 -0
- package/dist/transports/wp-api-fetch.d.ts +25 -0
- package/dist/transports/wp-api-fetch.d.ts.map +1 -0
- package/dist/transports/wp-api-fetch.js +40 -0
- package/dist/transports/wp-api-fetch.js.map +1 -0
- package/dist/wordpress.d.ts +8 -0
- package/dist/wordpress.d.ts.map +1 -0
- package/dist/wordpress.js +8 -0
- package/dist/wordpress.js.map +1 -0
- package/package.json +45 -0
- package/src/__smoke__/discovery.smoke.ts +66 -0
- package/src/abilities/catalog.ts +75 -0
- package/src/abilities/discovery.ts +111 -0
- package/src/abilities/index.ts +18 -0
- package/src/abilities/types.ts +98 -0
- package/src/client.ts +191 -0
- package/src/index.ts +32 -0
- package/src/transports/auth-fetch.ts +310 -0
- package/src/transports/fetch.ts +107 -0
- package/src/transports/types.ts +24 -0
- package/src/transports/wp-api-fetch.ts +58 -0
- package/src/wordpress.ts +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
GNU GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 2, June 1991
|
|
3
|
+
|
|
4
|
+
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
|
5
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
6
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
7
|
+
of this license document, but changing it is not allowed.
|
|
8
|
+
|
|
9
|
+
This program is free software; you can redistribute it and/or modify
|
|
10
|
+
it under the terms of the GNU General Public License as published by
|
|
11
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
12
|
+
(at your option) any later version.
|
|
13
|
+
|
|
14
|
+
This program is distributed in the hope that it will be useful,
|
|
15
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
GNU General Public License for more details.
|
|
18
|
+
|
|
19
|
+
You should have received a copy of the GNU General Public License along
|
|
20
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
21
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
22
|
+
|
|
23
|
+
Full license text: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# wp-native-client
|
|
2
|
+
|
|
3
|
+
Universal WordPress client built on the Abilities API.
|
|
4
|
+
|
|
5
|
+
**Status:** scaffold only. Implementation lands in M3 (see [ROADMAP](../../docs/ROADMAP.md)).
|
|
6
|
+
|
|
7
|
+
## What this package will provide
|
|
8
|
+
|
|
9
|
+
- `WPNativeClient` — single universal client, no subclasses
|
|
10
|
+
- `discoverAbilities()` — fetches the site's ability catalog
|
|
11
|
+
- `execute(abilityName, args)` — invokes any ability by name
|
|
12
|
+
- `AuthTransport` — token lifecycle (load / save / refresh / clear)
|
|
13
|
+
- `WpApiFetchTransport` — adapter for `@wordpress/api-fetch` (Gutenberg blocks)
|
|
14
|
+
- *(post-v0.1)* `generateTypes()` — CLI tool to codegen TypeScript types from ability JSON schemas
|
|
15
|
+
|
|
16
|
+
## The mental model
|
|
17
|
+
|
|
18
|
+
The client doesn't know what abilities exist on a given WordPress site. **It asks.** The site is the source of truth, always current, self-describing.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { WPNativeClient, AuthTransport } from 'wp-native-client';
|
|
22
|
+
|
|
23
|
+
const client = new WPNativeClient({
|
|
24
|
+
baseUrl: 'https://extrachill.com/wp-json',
|
|
25
|
+
transport: new AuthTransport({ /* ... */ }),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Discovery happens once at startup
|
|
29
|
+
await client.discoverAbilities();
|
|
30
|
+
|
|
31
|
+
// From there, anything any plugin registered is callable
|
|
32
|
+
const posts = await client.execute('wp/post.list', { per_page: 20 });
|
|
33
|
+
const artist = await client.execute('extrachill/artist.get', { id: 42 });
|
|
34
|
+
const calendar = await client.execute('extrachill/event.calendar', { venue: 'continental-club' });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
No hand-typed wrappers. No per-site subclasses. No drift between client and server.
|
|
38
|
+
|
|
39
|
+
## Why not a typed client per site?
|
|
40
|
+
|
|
41
|
+
See the [ROADMAP](../../docs/ROADMAP.md#the-principle). Per-site typed clients defeat the purpose of a generic framework — they'd require every consumer of every WordPress site to ship their own `@whatever/api-client`. Abilities discovery is the only pattern that delivers on "any WordPress site."
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbilityCatalog — in-memory index of a WordPress site's ability registry.
|
|
3
|
+
*
|
|
4
|
+
* Built once at app startup via WPNativeClient.discover(). Provides O(1)
|
|
5
|
+
* lookup by ability name plus convenience accessors for filtering by
|
|
6
|
+
* category / namespace.
|
|
7
|
+
*
|
|
8
|
+
* The catalog is the source of truth for "what can this site do?" —
|
|
9
|
+
* shell screens consult it to decide which ability to call for which
|
|
10
|
+
* UI slot, consumers query it to feature-detect, and the client uses
|
|
11
|
+
* it to fail fast when execute() is called with an unknown ability.
|
|
12
|
+
*/
|
|
13
|
+
import type { AbilityDescriptor } from './types';
|
|
14
|
+
export declare class AbilityCatalog {
|
|
15
|
+
private readonly byName;
|
|
16
|
+
constructor(abilities: readonly AbilityDescriptor[]);
|
|
17
|
+
/**
|
|
18
|
+
* Get a single ability descriptor by name.
|
|
19
|
+
* Returns undefined if the ability is not registered on this site.
|
|
20
|
+
*/
|
|
21
|
+
get(name: string): AbilityDescriptor | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the given ability is registered on this site.
|
|
24
|
+
*/
|
|
25
|
+
has(name: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* All registered ability names, in catalog order.
|
|
28
|
+
*/
|
|
29
|
+
names(): string[];
|
|
30
|
+
/**
|
|
31
|
+
* All registered abilities.
|
|
32
|
+
*/
|
|
33
|
+
all(): AbilityDescriptor[];
|
|
34
|
+
/**
|
|
35
|
+
* Abilities filtered by category slug.
|
|
36
|
+
*/
|
|
37
|
+
byCategory(category: string): AbilityDescriptor[];
|
|
38
|
+
/**
|
|
39
|
+
* Abilities whose name starts with the given namespace prefix.
|
|
40
|
+
*
|
|
41
|
+
* Example: catalog.byNamespace('wp-native') returns all wp-native/* abilities.
|
|
42
|
+
*/
|
|
43
|
+
byNamespace(namespace: string): AbilityDescriptor[];
|
|
44
|
+
/**
|
|
45
|
+
* Total number of abilities in the catalog.
|
|
46
|
+
*/
|
|
47
|
+
size(): number;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/abilities/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAE5C,SAAS,EAAE,SAAS,iBAAiB,EAAE;IAInD;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIhD;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,KAAK,IAAI,MAAM,EAAE;IAIjB;;OAEG;IACH,GAAG,IAAI,iBAAiB,EAAE;IAI1B;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAIjD;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAKnD;;OAEG;IACH,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbilityCatalog — in-memory index of a WordPress site's ability registry.
|
|
3
|
+
*
|
|
4
|
+
* Built once at app startup via WPNativeClient.discover(). Provides O(1)
|
|
5
|
+
* lookup by ability name plus convenience accessors for filtering by
|
|
6
|
+
* category / namespace.
|
|
7
|
+
*
|
|
8
|
+
* The catalog is the source of truth for "what can this site do?" —
|
|
9
|
+
* shell screens consult it to decide which ability to call for which
|
|
10
|
+
* UI slot, consumers query it to feature-detect, and the client uses
|
|
11
|
+
* it to fail fast when execute() is called with an unknown ability.
|
|
12
|
+
*/
|
|
13
|
+
export class AbilityCatalog {
|
|
14
|
+
byName;
|
|
15
|
+
constructor(abilities) {
|
|
16
|
+
this.byName = new Map(abilities.map((a) => [a.name, a]));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get a single ability descriptor by name.
|
|
20
|
+
* Returns undefined if the ability is not registered on this site.
|
|
21
|
+
*/
|
|
22
|
+
get(name) {
|
|
23
|
+
return this.byName.get(name);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Whether the given ability is registered on this site.
|
|
27
|
+
*/
|
|
28
|
+
has(name) {
|
|
29
|
+
return this.byName.has(name);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* All registered ability names, in catalog order.
|
|
33
|
+
*/
|
|
34
|
+
names() {
|
|
35
|
+
return Array.from(this.byName.keys());
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* All registered abilities.
|
|
39
|
+
*/
|
|
40
|
+
all() {
|
|
41
|
+
return Array.from(this.byName.values());
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Abilities filtered by category slug.
|
|
45
|
+
*/
|
|
46
|
+
byCategory(category) {
|
|
47
|
+
return this.all().filter((a) => a.category === category);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Abilities whose name starts with the given namespace prefix.
|
|
51
|
+
*
|
|
52
|
+
* Example: catalog.byNamespace('wp-native') returns all wp-native/* abilities.
|
|
53
|
+
*/
|
|
54
|
+
byNamespace(namespace) {
|
|
55
|
+
const prefix = `${namespace}/`;
|
|
56
|
+
return this.all().filter((a) => a.name.startsWith(prefix));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Total number of abilities in the catalog.
|
|
60
|
+
*/
|
|
61
|
+
size() {
|
|
62
|
+
return this.byName.size;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/abilities/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,OAAO,cAAc;IACR,MAAM,CAAiC;IAExD,YAAY,SAAuC;QACjD,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED;;;OAGG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,GAAG;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,QAAgB;QACzB,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,SAAiB;QAC3B,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,CAAC;QAC/B,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abilities discovery — walks /wp-abilities/v1/abilities and builds a catalog.
|
|
3
|
+
*
|
|
4
|
+
* The Abilities REST API is paginated. discoverAbilities() loops through
|
|
5
|
+
* all pages and returns a fully-populated AbilityCatalog. For sites with
|
|
6
|
+
* hundreds of abilities this is a few sequential requests at startup;
|
|
7
|
+
* the catalog is then cached in memory for the app session.
|
|
8
|
+
*/
|
|
9
|
+
import type { Transport } from '../transports/types';
|
|
10
|
+
import { AbilityCatalog } from './catalog';
|
|
11
|
+
import type { AbilityDescriptor, AbilityListParams, AbilityCategory } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* Fetch a single page of abilities.
|
|
14
|
+
*
|
|
15
|
+
* Returns the items only — pagination is driven by the discovery loop.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fetchAbilitiesPage(transport: Transport, params?: AbilityListParams): Promise<AbilityDescriptor[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Fetch one ability descriptor by name.
|
|
20
|
+
*
|
|
21
|
+
* Useful for refreshing a single ability's schema without re-discovering
|
|
22
|
+
* the whole catalog.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fetchAbility(transport: Transport, name: string): Promise<AbilityDescriptor>;
|
|
25
|
+
/**
|
|
26
|
+
* Fetch all available ability categories.
|
|
27
|
+
*/
|
|
28
|
+
export declare function fetchAbilityCategories(transport: Transport): Promise<AbilityCategory[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Discover all abilities on a WordPress site and return a populated catalog.
|
|
31
|
+
*
|
|
32
|
+
* Walks pages until an empty page is returned. The Abilities API caps
|
|
33
|
+
* `per_page` at 100, so a site with N abilities resolves in ⌈N / 100⌉ requests.
|
|
34
|
+
*
|
|
35
|
+
* Optionally filter by category — useful when an app only cares about a
|
|
36
|
+
* subset of the surface (e.g. only `wp-native/*` abilities for the auth
|
|
37
|
+
* bootstrap flow).
|
|
38
|
+
*/
|
|
39
|
+
export declare function discoverAbilities(transport: Transport, options?: {
|
|
40
|
+
category?: string;
|
|
41
|
+
perPage?: number;
|
|
42
|
+
}): Promise<AbilityCatalog>;
|
|
43
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/abilities/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EAChB,MAAM,SAAS,CAAC;AAMjB;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,SAAS,EACpB,MAAM,GAAE,iBAAsB,GAC7B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAY9B;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAK5B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,eAAe,EAAE,CAAC,CAK5B;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,cAAc,CAAC,CA0BzB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abilities discovery — walks /wp-abilities/v1/abilities and builds a catalog.
|
|
3
|
+
*
|
|
4
|
+
* The Abilities REST API is paginated. discoverAbilities() loops through
|
|
5
|
+
* all pages and returns a fully-populated AbilityCatalog. For sites with
|
|
6
|
+
* hundreds of abilities this is a few sequential requests at startup;
|
|
7
|
+
* the catalog is then cached in memory for the app session.
|
|
8
|
+
*/
|
|
9
|
+
import { AbilityCatalog } from './catalog';
|
|
10
|
+
const DEFAULT_PER_PAGE = 100;
|
|
11
|
+
const ABILITIES_PATH = 'wp-abilities/v1/abilities';
|
|
12
|
+
const CATEGORIES_PATH = 'wp-abilities/v1/categories';
|
|
13
|
+
/**
|
|
14
|
+
* Fetch a single page of abilities.
|
|
15
|
+
*
|
|
16
|
+
* Returns the items only — pagination is driven by the discovery loop.
|
|
17
|
+
*/
|
|
18
|
+
export async function fetchAbilitiesPage(transport, params = {}) {
|
|
19
|
+
const query = new URLSearchParams();
|
|
20
|
+
query.set('per_page', String(params.perPage ?? DEFAULT_PER_PAGE));
|
|
21
|
+
query.set('page', String(params.page ?? 1));
|
|
22
|
+
if (params.category) {
|
|
23
|
+
query.set('category', params.category);
|
|
24
|
+
}
|
|
25
|
+
return transport.request({
|
|
26
|
+
path: `${ABILITIES_PATH}?${query.toString()}`,
|
|
27
|
+
method: 'GET',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fetch one ability descriptor by name.
|
|
32
|
+
*
|
|
33
|
+
* Useful for refreshing a single ability's schema without re-discovering
|
|
34
|
+
* the whole catalog.
|
|
35
|
+
*/
|
|
36
|
+
export async function fetchAbility(transport, name) {
|
|
37
|
+
return transport.request({
|
|
38
|
+
path: `${ABILITIES_PATH}/${encodeURIComponent(name)}`,
|
|
39
|
+
method: 'GET',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fetch all available ability categories.
|
|
44
|
+
*/
|
|
45
|
+
export async function fetchAbilityCategories(transport) {
|
|
46
|
+
return transport.request({
|
|
47
|
+
path: CATEGORIES_PATH,
|
|
48
|
+
method: 'GET',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Discover all abilities on a WordPress site and return a populated catalog.
|
|
53
|
+
*
|
|
54
|
+
* Walks pages until an empty page is returned. The Abilities API caps
|
|
55
|
+
* `per_page` at 100, so a site with N abilities resolves in ⌈N / 100⌉ requests.
|
|
56
|
+
*
|
|
57
|
+
* Optionally filter by category — useful when an app only cares about a
|
|
58
|
+
* subset of the surface (e.g. only `wp-native/*` abilities for the auth
|
|
59
|
+
* bootstrap flow).
|
|
60
|
+
*/
|
|
61
|
+
export async function discoverAbilities(transport, options = {}) {
|
|
62
|
+
const perPage = options.perPage ?? DEFAULT_PER_PAGE;
|
|
63
|
+
const all = [];
|
|
64
|
+
let page = 1;
|
|
65
|
+
while (true) {
|
|
66
|
+
const params = { page, perPage };
|
|
67
|
+
if (options.category !== undefined) {
|
|
68
|
+
params.category = options.category;
|
|
69
|
+
}
|
|
70
|
+
const items = await fetchAbilitiesPage(transport, params);
|
|
71
|
+
if (items.length === 0) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
all.push(...items);
|
|
75
|
+
if (items.length < perPage) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
page += 1;
|
|
79
|
+
}
|
|
80
|
+
return new AbilityCatalog(all);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/abilities/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAO3C,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,cAAc,GAAG,2BAA2B,CAAC;AACnD,MAAM,eAAe,GAAG,4BAA4B,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAoB,EACpB,SAA4B,EAAE;IAE9B,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAClE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,CAAC,OAAO,CAAsB;QAC5C,IAAI,EAAE,GAAG,cAAc,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE;QAC7C,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAoB,EACpB,IAAY;IAEZ,OAAO,SAAS,CAAC,OAAO,CAAoB;QAC1C,IAAI,EAAE,GAAG,cAAc,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE;QACrD,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAoB;IAEpB,OAAO,SAAS,CAAC,OAAO,CAAoB;QAC1C,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAoB,EACpB,UAAmD,EAAE;IAErD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACpD,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,MAAM,GAAsB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACrC,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAE1D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAEnB,IAAI,KAAK,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;YAC3B,MAAM;QACR,CAAC;QAED,IAAI,IAAI,CAAC,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public surface for the abilities subsystem.
|
|
3
|
+
*/
|
|
4
|
+
export { AbilityCatalog } from './catalog';
|
|
5
|
+
export { discoverAbilities, fetchAbilitiesPage, fetchAbility, fetchAbilityCategories, } from './discovery';
|
|
6
|
+
export type { AbilityDescriptor, AbilityCategory, AbilityExecutionResponse, AbilityListPage, AbilityListParams, } from './types';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abilities/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,wBAAwB,EACxB,eAAe,EACf,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/abilities/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,GACvB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the WordPress Abilities API REST surface.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the response shapes documented at:
|
|
5
|
+
* GET /wp-abilities/v1/abilities
|
|
6
|
+
* GET /wp-abilities/v1/abilities/{name}
|
|
7
|
+
* POST /wp-abilities/v1/abilities/{name}/run
|
|
8
|
+
*
|
|
9
|
+
* These types describe the wire format. They are intentionally permissive
|
|
10
|
+
* about ability-specific input/output (modeled as `unknown`) because the
|
|
11
|
+
* universal client can't know each ability's schema at compile time —
|
|
12
|
+
* codegen from `input_schema` / `output_schema` is a post-v0.1 concern.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A single ability as returned by the catalog endpoints.
|
|
16
|
+
*/
|
|
17
|
+
export interface AbilityDescriptor {
|
|
18
|
+
/**
|
|
19
|
+
* Unique identifier, namespaced. Examples:
|
|
20
|
+
* wp/post.list
|
|
21
|
+
* wp-native/auth-login
|
|
22
|
+
* extrachill/artist.get
|
|
23
|
+
*/
|
|
24
|
+
name: string;
|
|
25
|
+
/** Human-readable display label. */
|
|
26
|
+
label: string;
|
|
27
|
+
/** Longer description of what the ability does. */
|
|
28
|
+
description: string;
|
|
29
|
+
/**
|
|
30
|
+
* Category slug. Categories group related abilities for UI / discovery.
|
|
31
|
+
* Validated server-side against /^[a-z0-9]+(?:-[a-z0-9]+)*$/ — no slashes.
|
|
32
|
+
*/
|
|
33
|
+
category: string;
|
|
34
|
+
/**
|
|
35
|
+
* JSON Schema describing the shape of the `input` argument expected by
|
|
36
|
+
* POST /abilities/{name}/run. May be an empty object for nullary abilities.
|
|
37
|
+
*/
|
|
38
|
+
input_schema: Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* JSON Schema describing the shape of `result` returned by /run.
|
|
41
|
+
*/
|
|
42
|
+
output_schema: Record<string, unknown>;
|
|
43
|
+
/**
|
|
44
|
+
* Meta information attached to the ability registration.
|
|
45
|
+
*/
|
|
46
|
+
meta?: {
|
|
47
|
+
annotations?: Record<string, unknown> | boolean | null;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* An ability category as returned by GET /wp-abilities/v1/categories.
|
|
53
|
+
*/
|
|
54
|
+
export interface AbilityCategory {
|
|
55
|
+
slug: string;
|
|
56
|
+
label: string;
|
|
57
|
+
description: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Successful response from POST /wp-abilities/v1/abilities/{name}/run.
|
|
61
|
+
*
|
|
62
|
+
* The actual `result` shape is ability-specific; consumers narrow with
|
|
63
|
+
* a type parameter at the `client.execute<TResult>()` call site.
|
|
64
|
+
*/
|
|
65
|
+
export interface AbilityExecutionResponse<TResult = unknown> {
|
|
66
|
+
result: TResult;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Pagination wrapper. The Abilities REST API uses standard WP REST
|
|
70
|
+
* paginated lists — total count comes in the `X-WP-Total` header.
|
|
71
|
+
*/
|
|
72
|
+
export interface AbilityListPage {
|
|
73
|
+
items: AbilityDescriptor[];
|
|
74
|
+
totalItems: number;
|
|
75
|
+
totalPages: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Optional filters for catalog listing.
|
|
79
|
+
*/
|
|
80
|
+
export interface AbilityListParams {
|
|
81
|
+
/** Filter by category slug. */
|
|
82
|
+
category?: string;
|
|
83
|
+
/** Page number (1-indexed). Default: 1. */
|
|
84
|
+
page?: number;
|
|
85
|
+
/** Items per page. Server max is typically 100. Default: 50. */
|
|
86
|
+
perPage?: number;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/abilities/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IAEd,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEtC;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC;;OAEG;IACH,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;QACvD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB,CAAC,OAAO,GAAG,OAAO;IACzD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the WordPress Abilities API REST surface.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the response shapes documented at:
|
|
5
|
+
* GET /wp-abilities/v1/abilities
|
|
6
|
+
* GET /wp-abilities/v1/abilities/{name}
|
|
7
|
+
* POST /wp-abilities/v1/abilities/{name}/run
|
|
8
|
+
*
|
|
9
|
+
* These types describe the wire format. They are intentionally permissive
|
|
10
|
+
* about ability-specific input/output (modeled as `unknown`) because the
|
|
11
|
+
* universal client can't know each ability's schema at compile time —
|
|
12
|
+
* codegen from `input_schema` / `output_schema` is a post-v0.1 concern.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/abilities/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WPNativeClient — universal WordPress client built on the Abilities API.
|
|
3
|
+
*
|
|
4
|
+
* One client. No subclasses. No per-site wrappers.
|
|
5
|
+
*
|
|
6
|
+
* The client wraps a Transport (FetchTransport, AuthFetchTransport, or
|
|
7
|
+
* WpApiFetchTransport) and exposes two surfaces:
|
|
8
|
+
*
|
|
9
|
+
* 1. Discovery — fetch the site's ability catalog at startup
|
|
10
|
+
* 2. Execution — call abilities by name with typed input/output
|
|
11
|
+
*
|
|
12
|
+
* Site-specific concerns (extrachill/* abilities, woocommerce/* abilities,
|
|
13
|
+
* etc.) are addressed via ability namespacing on the server side. The
|
|
14
|
+
* client itself never knows the difference between a core WP ability and
|
|
15
|
+
* a plugin-registered one — they're all just strings + JSON Schemas.
|
|
16
|
+
*
|
|
17
|
+
* Example:
|
|
18
|
+
*
|
|
19
|
+
* import { WPNativeClient, AuthFetchTransport } from 'wp-native-client';
|
|
20
|
+
*
|
|
21
|
+
* const client = new WPNativeClient(
|
|
22
|
+
* new AuthFetchTransport({ baseUrl: 'https://example.com/wp-json', ... })
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* await client.discover();
|
|
26
|
+
*
|
|
27
|
+
* const posts = await client.execute<Post[]>('wp/post.list', { per_page: 20 });
|
|
28
|
+
* const me = await client.execute<User>('wp-native/user.me');
|
|
29
|
+
*/
|
|
30
|
+
import type { Transport } from './transports/types';
|
|
31
|
+
import { AbilityCatalog } from './abilities/catalog';
|
|
32
|
+
import type { AbilityDescriptor } from './abilities/types';
|
|
33
|
+
export interface WPNativeClientConfig {
|
|
34
|
+
/**
|
|
35
|
+
* Auto-fail execute() calls for abilities not present in the catalog.
|
|
36
|
+
*
|
|
37
|
+
* When true (default), execute() throws synchronously if the ability
|
|
38
|
+
* name is not registered on the site, before making the HTTP request.
|
|
39
|
+
* This catches typos and missing-plugin scenarios at the call site.
|
|
40
|
+
*
|
|
41
|
+
* Set to false if you want to call abilities before discover() has
|
|
42
|
+
* run (auth bootstrap, for example) — but prefer executeUnchecked()
|
|
43
|
+
* for that case so the intent is explicit.
|
|
44
|
+
*/
|
|
45
|
+
validateAbilityNames?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare class WPNativeClient {
|
|
48
|
+
private readonly transport;
|
|
49
|
+
private readonly config;
|
|
50
|
+
private _catalog;
|
|
51
|
+
constructor(transport: Transport, config?: WPNativeClientConfig);
|
|
52
|
+
/**
|
|
53
|
+
* Walk the Abilities API and populate the in-memory catalog.
|
|
54
|
+
*
|
|
55
|
+
* Call once at app startup, after auth is established. Subsequent
|
|
56
|
+
* calls replace the catalog (cheap to re-run if the server registers
|
|
57
|
+
* new abilities at runtime).
|
|
58
|
+
*
|
|
59
|
+
* Optionally filter to a subset by category — useful when bootstrapping
|
|
60
|
+
* a minimal client that only needs auth abilities.
|
|
61
|
+
*/
|
|
62
|
+
discover(options?: {
|
|
63
|
+
category?: string;
|
|
64
|
+
}): Promise<AbilityCatalog>;
|
|
65
|
+
/**
|
|
66
|
+
* The current catalog. Throws if discover() has not been called.
|
|
67
|
+
*
|
|
68
|
+
* Use catalogOrNull() if you want to feature-detect without throwing.
|
|
69
|
+
*/
|
|
70
|
+
get catalog(): AbilityCatalog;
|
|
71
|
+
/**
|
|
72
|
+
* Non-throwing accessor for the catalog. Returns null if discover()
|
|
73
|
+
* has not been called.
|
|
74
|
+
*/
|
|
75
|
+
catalogOrNull(): AbilityCatalog | null;
|
|
76
|
+
/**
|
|
77
|
+
* Whether discover() has populated the catalog.
|
|
78
|
+
*/
|
|
79
|
+
hasCatalog(): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Execute an ability by name.
|
|
82
|
+
*
|
|
83
|
+
* The ability is looked up in the catalog (when validateAbilityNames is
|
|
84
|
+
* enabled), then POSTed to /wp-abilities/v1/abilities/{name}/run with
|
|
85
|
+
* the given input as the request body's `input` field.
|
|
86
|
+
*
|
|
87
|
+
* The response shape `{ result: TResult }` is unwrapped — callers receive
|
|
88
|
+
* the result value directly.
|
|
89
|
+
*
|
|
90
|
+
* Throws:
|
|
91
|
+
* - Error if the ability is not in the catalog (and validation enabled)
|
|
92
|
+
* - ApiError if the server returns a non-2xx response
|
|
93
|
+
*/
|
|
94
|
+
execute<TResult = unknown, TInput = unknown>(name: string, input?: TInput): Promise<TResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Execute an ability without checking the catalog.
|
|
97
|
+
*
|
|
98
|
+
* Use this for the auth bootstrap path (login → discover) where the
|
|
99
|
+
* client must call abilities before the catalog exists. For all other
|
|
100
|
+
* call sites, prefer execute() so missing abilities fail loudly at
|
|
101
|
+
* the call site.
|
|
102
|
+
*/
|
|
103
|
+
executeUnchecked<TResult = unknown, TInput = unknown>(name: string, input?: TInput): Promise<TResult>;
|
|
104
|
+
/**
|
|
105
|
+
* Get a single ability descriptor from the catalog.
|
|
106
|
+
* Returns undefined if not registered or catalog not loaded.
|
|
107
|
+
*/
|
|
108
|
+
describe(name: string): AbilityDescriptor | undefined;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAI3B,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAA+B;gBAEnC,SAAS,EAAE,SAAS,EAAE,MAAM,GAAE,oBAAyB;IAOnE;;;;;;;;;OASG;IACG,QAAQ,CAAC,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAU5E;;;;OAIG;IACH,IAAI,OAAO,IAAI,cAAc,CAO5B;IAED;;;OAGG;IACH,aAAa,IAAI,cAAc,GAAG,IAAI;IAItC;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;;;;;;;;;;OAaG;IACG,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,EAC/C,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;OAOG;IACG,gBAAgB,CAAC,OAAO,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC;IAwBnB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;CAGtD"}
|