@websolutespa/payload-plugin-seo 0.1.3 → 2.0.1-next.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 (52) hide show
  1. package/CHANGELOG.md +73 -55
  2. package/README.md +1 -2
  3. package/dist/api/tokens.service.js +41 -0
  4. package/dist/api/tokens.service.js.map +1 -0
  5. package/dist/api/tokens.service.test.js +83 -0
  6. package/dist/api/tokens.service.test.js.map +1 -0
  7. package/dist/collections/MetatagsRule.js +67 -0
  8. package/dist/collections/MetatagsRule.js.map +1 -0
  9. package/dist/components/TextWithTokens.js +45 -0
  10. package/dist/components/TextWithTokens.js.map +1 -0
  11. package/dist/components/Tokens.js +49 -0
  12. package/dist/components/Tokens.js.map +1 -0
  13. package/dist/exports/client.js +4 -0
  14. package/dist/exports/client.js.map +1 -0
  15. package/dist/index.d.ts +45 -39
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +5 -487
  18. package/dist/index.js.map +1 -0
  19. package/dist/options.js +28 -0
  20. package/dist/options.js.map +1 -0
  21. package/dist/seo.js +271 -0
  22. package/dist/seo.js.map +1 -0
  23. package/dist/seo.test.js +144 -0
  24. package/dist/seo.test.js.map +1 -0
  25. package/dist/test/collections/AnotherPageCollection.js +45 -0
  26. package/dist/test/collections/AnotherPageCollection.js.map +1 -0
  27. package/dist/test/collections/CustomPageCollection.js +110 -0
  28. package/dist/test/collections/CustomPageCollection.js.map +1 -0
  29. package/dist/test/index.js +7 -0
  30. package/dist/test/index.js.map +1 -0
  31. package/dist/test/payload.config.js +45 -0
  32. package/dist/test/payload.config.js.map +1 -0
  33. package/dist/test/utils.js +180 -0
  34. package/dist/test/utils.js.map +1 -0
  35. package/dist/test_/helpers/testHelpers.js +212 -0
  36. package/dist/test_/helpers/testHelpers.js.map +1 -0
  37. package/dist/test_/test.config.js +209 -0
  38. package/dist/test_/test.config.js.map +1 -0
  39. package/dist/translations.js +24 -0
  40. package/dist/translations.js.map +1 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -0
  42. package/dist/types.js +3 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/utils/cache.js +45 -0
  45. package/dist/utils/cache.js.map +1 -0
  46. package/dist/utils/fields.js +39 -0
  47. package/dist/utils/fields.js.map +1 -0
  48. package/dist/utils/findVal.js +17 -0
  49. package/dist/utils/findVal.js.map +1 -0
  50. package/dist/utils/index.js +4 -0
  51. package/dist/utils/index.js.map +1 -0
  52. package/package.json +84 -28
package/CHANGELOG.md CHANGED
@@ -1,55 +1,73 @@
1
- # @websolutespa/payload-plugin-seo
2
-
3
- ## 0.1.3
4
-
5
- ### Patch Changes
6
-
7
- - - Add rules field localization
8
-
9
- ## 0.1.2
10
-
11
- ### Patch Changes
12
-
13
- - 0b4e928: Modified: translations
14
- - Updated dependencies [0b4e928]
15
- - Updated dependencies [e921c43]
16
- - Updated dependencies [e921c43]
17
- - @websolutespa/payload-utils@0.0.3
18
-
19
- ## 0.1.1
20
-
21
- ### Patch Changes
22
-
23
- - 15ad595: Modified: payload local api consolidated overrideAccess
24
-
25
- ## 0.1.0
26
-
27
- ### Minor Changes
28
-
29
- - 8475b68: Added: external dependency payload-utils
30
- - 946a81f: Updating: dependency payload 2.28.0
31
-
32
- ### Patch Changes
33
-
34
- - Updated dependencies [8475b68]
35
- - @websolutespa/payload-utils@0.0.1
36
-
37
- ## 0.0.3
38
-
39
- ### Patch Changes
40
-
41
- - Fixing: utility functions exports
42
-
43
- ## 0.0.2
44
-
45
- ### Patch Changes
46
-
47
- - 882a9be: add token autocomplete
48
- add readme
49
- - 8696866: add new tests
50
-
51
- ## 0.0.1
52
-
53
- ### Patch Changes
54
-
55
- - 06a3127: Initial release
1
+ # @websolutespa/payload-plugin-seo
2
+
3
+ ## 2.0.1-next.0
4
+
5
+ ### Major Changes
6
+
7
+ - e74b305: Release: Payload 3.7.0
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [e74b305]
12
+ - @websolutespa/payload-utils@2.0.1-next.0
13
+ - @websolutespa/bom-core@2.0.1-next.0
14
+
15
+ ## 0.1.4
16
+
17
+ ### Patch Changes
18
+
19
+ - - Improved rules evaluation performance
20
+
21
+ ## 0.1.3
22
+
23
+ ### Patch Changes
24
+
25
+ - - Add rules field localization
26
+
27
+ ## 0.1.2
28
+
29
+ ### Patch Changes
30
+
31
+ - 0b4e928: Modified: translations
32
+ - Updated dependencies [0b4e928]
33
+ - Updated dependencies [e921c43]
34
+ - Updated dependencies [e921c43]
35
+ - @websolutespa/payload-utils@0.0.3
36
+
37
+ ## 0.1.1
38
+
39
+ ### Patch Changes
40
+
41
+ - 15ad595: Modified: payload local api consolidated overrideAccess
42
+
43
+ ## 0.1.0
44
+
45
+ ### Minor Changes
46
+
47
+ - 8475b68: Added: external dependency payload-utils
48
+ - 946a81f: Updating: dependency payload 2.28.0
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [8475b68]
53
+ - @websolutespa/payload-utils@0.0.1
54
+
55
+ ## 0.0.3
56
+
57
+ ### Patch Changes
58
+
59
+ - Fixing: utility functions exports
60
+
61
+ ## 0.0.2
62
+
63
+ ### Patch Changes
64
+
65
+ - 882a9be: add token autocomplete
66
+ add readme
67
+ - 8696866: add new tests
68
+
69
+ ## 0.0.1
70
+
71
+ ### Patch Changes
72
+
73
+ - 06a3127: Initial release
package/README.md CHANGED
@@ -14,7 +14,7 @@ Create rules for managing the meta tags of all the documents in specific collect
14
14
 
15
15
  ### Requirements:
16
16
 
17
- - Payload version 2.28.0 or higher is required.
17
+ - Payload version 3.48.0 or higher is required.
18
18
 
19
19
  ## Installation
20
20
 
@@ -48,7 +48,6 @@ export default buildConfig({
48
48
  if (!doc.title) {
49
49
  return '';
50
50
  }
51
-
52
51
  if (locale) {
53
52
  const fieldConfig = getDataField(collection.fields, 'title');
54
53
  return fieldConfig.localized ? doc.title[locale.code]?.toUpperCase() : doc.title.toUpperCase();
@@ -0,0 +1,41 @@
1
+ import { ResponseError, ResponseSuccess } from '@websolutespa/payload-utils/server';
2
+ import payload from 'payload';
3
+ import { options } from '../options';
4
+ import { eachTokenizableField } from '../utils/fields';
5
+ const getTokens = (collections)=>{
6
+ const collectionConfigs = collections.length ? payload.config.collections.filter((config)=>collections.includes(config.slug)) : payload.config.collections.filter((config)=>options.collections.includes(config.slug));
7
+ // retrieve common tokens between collections
8
+ let tokens = collectionConfigs.reduce((acc, config)=>{
9
+ const fields = [];
10
+ eachTokenizableField(config.fields, (field)=>{
11
+ fields.push(`[${field.name}]`);
12
+ });
13
+ return acc.length ? acc.filter((field)=>fields.includes(field)) : fields;
14
+ }, []);
15
+ // remove duplicates
16
+ tokens = [
17
+ ...new Set(tokens)
18
+ ];
19
+ // merge collections tokens with custom tokens
20
+ tokens = [
21
+ ...tokens,
22
+ ...options.customTokens.map((token)=>`[${token.name}]`)
23
+ ];
24
+ return tokens;
25
+ };
26
+ export const tokensGet = ()=>({
27
+ path: '/seo/tokens',
28
+ method: 'get',
29
+ handler: async (req)=>{
30
+ try {
31
+ const qsCollections = req.query.collections || '';
32
+ const collections = qsCollections !== '' ? qsCollections.split(',') : [];
33
+ const tokens = getTokens(collections);
34
+ return ResponseSuccess(tokens);
35
+ } catch (error) {
36
+ return ResponseError(error);
37
+ }
38
+ }
39
+ });
40
+
41
+ //# sourceMappingURL=tokens.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/api/tokens.service.ts"],"sourcesContent":["import { ResponseError, ResponseSuccess } from '@websolutespa/payload-utils/server';\r\nimport payload, { Endpoint, PayloadRequest } from 'payload';\r\nimport { options } from '../options';\r\nimport { eachTokenizableField } from '../utils/fields';\r\n\r\nconst getTokens = (collections: string[]) => {\r\n const collectionConfigs = collections.length ?\r\n payload.config.collections.filter((config) => collections.includes(config.slug))\r\n : payload.config.collections.filter((config) => options.collections.includes(config.slug));\r\n\r\n // retrieve common tokens between collections\r\n let tokens: string[] = collectionConfigs.reduce((acc, config) => {\r\n const fields: string[] = [];\r\n eachTokenizableField(config.fields, (field) => {\r\n fields.push(`[${field.name}]`);\r\n });\r\n return acc.length ? acc.filter((field) => fields.includes(field)) : fields;\r\n }, [] as string[]);\r\n // remove duplicates\r\n tokens = [...new Set(tokens)];\r\n\r\n // merge collections tokens with custom tokens\r\n tokens = [...tokens, ...options.customTokens.map(token => `[${token.name}]`)];\r\n\r\n return tokens;\r\n};\r\n\r\nexport const tokensGet: (() => Endpoint) = () => ({\r\n path: '/seo/tokens',\r\n method: 'get',\r\n handler: async (req: PayloadRequest) => {\r\n try {\r\n const qsCollections = (req.query.collections || '') as string;\r\n const collections = qsCollections !== '' ? qsCollections.split(',') : [];\r\n const tokens = getTokens(collections);\r\n return ResponseSuccess(tokens);\r\n } catch (error) {\r\n return ResponseError(error);\r\n }\r\n },\r\n});\r\n"],"names":["ResponseError","ResponseSuccess","payload","options","eachTokenizableField","getTokens","collections","collectionConfigs","length","config","filter","includes","slug","tokens","reduce","acc","fields","field","push","name","Set","customTokens","map","token","tokensGet","path","method","handler","req","qsCollections","query","split","error"],"mappings":"AAAA,SAASA,aAAa,EAAEC,eAAe,QAAQ,qCAAqC;AACpF,OAAOC,aAA2C,UAAU;AAC5D,SAASC,OAAO,QAAQ,aAAa;AACrC,SAASC,oBAAoB,QAAQ,kBAAkB;AAEvD,MAAMC,YAAY,CAACC;IACjB,MAAMC,oBAAoBD,YAAYE,MAAM,GAC1CN,QAAQO,MAAM,CAACH,WAAW,CAACI,MAAM,CAAC,CAACD,SAAWH,YAAYK,QAAQ,CAACF,OAAOG,IAAI,KAC5EV,QAAQO,MAAM,CAACH,WAAW,CAACI,MAAM,CAAC,CAACD,SAAWN,QAAQG,WAAW,CAACK,QAAQ,CAACF,OAAOG,IAAI;IAE1F,6CAA6C;IAC7C,IAAIC,SAAmBN,kBAAkBO,MAAM,CAAC,CAACC,KAAKN;QACpD,MAAMO,SAAmB,EAAE;QAC3BZ,qBAAqBK,OAAOO,MAAM,EAAE,CAACC;YACnCD,OAAOE,IAAI,CAAC,CAAC,CAAC,EAAED,MAAME,IAAI,CAAC,CAAC,CAAC;QAC/B;QACA,OAAOJ,IAAIP,MAAM,GAAGO,IAAIL,MAAM,CAAC,CAACO,QAAUD,OAAOL,QAAQ,CAACM,UAAUD;IACtE,GAAG,EAAE;IACL,oBAAoB;IACpBH,SAAS;WAAI,IAAIO,IAAIP;KAAQ;IAE7B,8CAA8C;IAC9CA,SAAS;WAAIA;WAAWV,QAAQkB,YAAY,CAACC,GAAG,CAACC,CAAAA,QAAS,CAAC,CAAC,EAAEA,MAAMJ,IAAI,CAAC,CAAC,CAAC;KAAE;IAE7E,OAAON;AACT;AAEA,OAAO,MAAMW,YAA8B,IAAO,CAAA;QAChDC,MAAM;QACNC,QAAQ;QACRC,SAAS,OAAOC;YACd,IAAI;gBACF,MAAMC,gBAAiBD,IAAIE,KAAK,CAACxB,WAAW,IAAI;gBAChD,MAAMA,cAAcuB,kBAAkB,KAAKA,cAAcE,KAAK,CAAC,OAAO,EAAE;gBACxE,MAAMlB,SAASR,UAAUC;gBACzB,OAAOL,gBAAgBY;YACzB,EAAE,OAAOmB,OAAO;gBACd,OAAOhC,cAAcgC;YACvB;QACF;IACF,CAAA,EAAG"}
@@ -0,0 +1,83 @@
1
+ import { AnotherPageCollection, config, CustomPageCollection } from '@/test';
2
+ import { clearContext, getContext, users } from '@websolutespa/test/payload';
3
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
4
+ describe('tokens', ()=>{
5
+ let payload;
6
+ let client;
7
+ let adminToken;
8
+ let fetchTokens;
9
+ beforeAll(async ()=>{
10
+ const context = await getContext(config);
11
+ payload = context.payload;
12
+ client = context.client;
13
+ adminToken = await client.getToken('users', users.admin);
14
+ fetchTokens = async (collections)=>{
15
+ const tokens = await client.get(`/seo/tokens?collections=${collections?.join(',') || ''}`, {
16
+ headers: {
17
+ Authorization: `JWT ${adminToken}`
18
+ }
19
+ });
20
+ return tokens;
21
+ };
22
+ });
23
+ afterAll(async ()=>{
24
+ await clearContext();
25
+ });
26
+ describe('token.service', ()=>{
27
+ describe('tokensGet', ()=>{
28
+ describe('when one collection is specified as input', ()=>{
29
+ let tokens;
30
+ beforeAll(async ()=>{
31
+ tokens = await fetchTokens([
32
+ CustomPageCollection.slug
33
+ ]);
34
+ });
35
+ it('should return a list of tokens, one for each tokenizable field (TextField | TextareaField | RichTextField | RelationshipField) of the collection', async ()=>{
36
+ expect(tokens).toContain('[textField]');
37
+ expect(tokens).toContain('[richtextField]');
38
+ expect(tokens).toContain('[textareaField]');
39
+ expect(tokens).toContain('[relationshipField]');
40
+ });
41
+ it('should also return token for nested fields', async ()=>{
42
+ expect(tokens).toContain('[nestedArrayField]');
43
+ expect(tokens).toContain('[nestedArrayField]');
44
+ expect(tokens).toContain('[nestedCollapsibleField]');
45
+ expect(tokens).toContain('[nestedGroupField]');
46
+ expect(tokens).toContain('[nestedRowField]');
47
+ expect(tokens).toContain('[nestedTabField]');
48
+ });
49
+ it('should return the custom token', async ()=>{
50
+ expect(tokens).toContain('[test-custom-token]');
51
+ });
52
+ });
53
+ describe('when multiple collection are specified as input', ()=>{
54
+ let tokens;
55
+ beforeAll(async ()=>{
56
+ tokens = await fetchTokens([
57
+ CustomPageCollection.slug,
58
+ AnotherPageCollection.slug
59
+ ]);
60
+ });
61
+ it('should return a list of common tokens', async ()=>{
62
+ expect(tokens).toContain('[textField]');
63
+ expect(tokens).toContain('[nestedGroupField]');
64
+ });
65
+ it('shouldn\'t return non common tokens', async ()=>{
66
+ expect(tokens).not.toContain('[richtextField]');
67
+ expect(tokens).not.toContain('[textareaField]');
68
+ expect(tokens).not.toContain('[relationshipField]');
69
+ expect(tokens).not.toContain('[nestedArrayField]');
70
+ expect(tokens).not.toContain('[nestedArrayField]');
71
+ expect(tokens).not.toContain('[nestedCollapsibleField]');
72
+ expect(tokens).not.toContain('[nestedRowField]');
73
+ expect(tokens).not.toContain('[nestedTabField]');
74
+ });
75
+ it('should return the custom token', async ()=>{
76
+ expect(tokens).toContain('[test-custom-token]');
77
+ });
78
+ });
79
+ });
80
+ });
81
+ });
82
+
83
+ //# sourceMappingURL=tokens.service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/api/tokens.service.test.ts"],"sourcesContent":["import { AnotherPageCollection, config, CustomPageCollection } from '@/test';\r\nimport { clearContext, getContext, RestClient, users } from '@websolutespa/test/payload';\r\nimport { Payload } from 'payload';\r\nimport { afterAll, beforeAll, describe, expect, it } from 'vitest';\r\n\r\ndescribe('tokens', () => {\r\n\r\n let payload: Payload;\r\n let client: RestClient;\r\n let adminToken: string;\r\n let fetchTokens: (collections: string[]) => Promise<string[]>;\r\n\r\n beforeAll(async () => {\r\n const context = await getContext(config);\r\n payload = context.payload;\r\n client = context.client;\r\n adminToken = await client.getToken('users', users.admin);\r\n fetchTokens = async (collections: string[]): Promise<string[]> => {\r\n const tokens = await client.get(`/seo/tokens?collections=${(collections?.join(',') || '')}`, {\r\n headers: {\r\n Authorization: `JWT ${adminToken}`,\r\n },\r\n }) as string[];\r\n return tokens;\r\n };\r\n });\r\n\r\n afterAll(async () => {\r\n await clearContext();\r\n });\r\n\r\n describe('token.service', () => {\r\n describe('tokensGet', () => {\r\n describe('when one collection is specified as input', () => {\r\n let tokens: string[];\r\n\r\n beforeAll(async () => {\r\n tokens = await fetchTokens([CustomPageCollection.slug]);\r\n });\r\n\r\n it('should return a list of tokens, one for each tokenizable field (TextField | TextareaField | RichTextField | RelationshipField) of the collection', async () => {\r\n expect(tokens).toContain('[textField]');\r\n expect(tokens).toContain('[richtextField]');\r\n expect(tokens).toContain('[textareaField]');\r\n expect(tokens).toContain('[relationshipField]');\r\n });\r\n\r\n it('should also return token for nested fields', async () => {\r\n expect(tokens).toContain('[nestedArrayField]');\r\n expect(tokens).toContain('[nestedArrayField]');\r\n expect(tokens).toContain('[nestedCollapsibleField]');\r\n expect(tokens).toContain('[nestedGroupField]');\r\n expect(tokens).toContain('[nestedRowField]');\r\n expect(tokens).toContain('[nestedTabField]');\r\n });\r\n\r\n it('should return the custom token', async () => {\r\n expect(tokens).toContain('[test-custom-token]');\r\n });\r\n });\r\n\r\n describe('when multiple collection are specified as input', () => {\r\n let tokens: string[];\r\n\r\n beforeAll(async () => {\r\n tokens = await fetchTokens([CustomPageCollection.slug, AnotherPageCollection.slug]);\r\n });\r\n\r\n it('should return a list of common tokens', async () => {\r\n expect(tokens).toContain('[textField]');\r\n expect(tokens).toContain('[nestedGroupField]');\r\n });\r\n\r\n it('shouldn\\'t return non common tokens', async () => {\r\n expect(tokens).not.toContain('[richtextField]');\r\n expect(tokens).not.toContain('[textareaField]');\r\n expect(tokens).not.toContain('[relationshipField]');\r\n expect(tokens).not.toContain('[nestedArrayField]');\r\n expect(tokens).not.toContain('[nestedArrayField]');\r\n expect(tokens).not.toContain('[nestedCollapsibleField]');\r\n expect(tokens).not.toContain('[nestedRowField]');\r\n expect(tokens).not.toContain('[nestedTabField]');\r\n });\r\n\r\n it('should return the custom token', async () => {\r\n expect(tokens).toContain('[test-custom-token]');\r\n });\r\n });\r\n });\r\n });\r\n});\r\n"],"names":["AnotherPageCollection","config","CustomPageCollection","clearContext","getContext","users","afterAll","beforeAll","describe","expect","it","payload","client","adminToken","fetchTokens","context","getToken","admin","collections","tokens","get","join","headers","Authorization","slug","toContain","not"],"mappings":"AAAA,SAASA,qBAAqB,EAAEC,MAAM,EAAEC,oBAAoB,QAAQ,SAAS;AAC7E,SAASC,YAAY,EAAEC,UAAU,EAAcC,KAAK,QAAQ,6BAA6B;AAEzF,SAASC,QAAQ,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAEnEF,SAAS,UAAU;IAEjB,IAAIG;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJP,UAAU;QACR,MAAMQ,UAAU,MAAMX,WAAWH;QACjCU,UAAUI,QAAQJ,OAAO;QACzBC,SAASG,QAAQH,MAAM;QACvBC,aAAa,MAAMD,OAAOI,QAAQ,CAAC,SAASX,MAAMY,KAAK;QACvDH,cAAc,OAAOI;YACnB,MAAMC,SAAS,MAAMP,OAAOQ,GAAG,CAAC,CAAC,wBAAwB,EAAGF,aAAaG,KAAK,QAAQ,GAAI,CAAC,EAAE;gBAC3FC,SAAS;oBACPC,eAAe,CAAC,IAAI,EAAEV,WAAW,CAAC;gBACpC;YACF;YACA,OAAOM;QACT;IACF;IAEAb,SAAS;QACP,MAAMH;IACR;IAEAK,SAAS,iBAAiB;QACxBA,SAAS,aAAa;YACpBA,SAAS,6CAA6C;gBACpD,IAAIW;gBAEJZ,UAAU;oBACRY,SAAS,MAAML,YAAY;wBAACZ,qBAAqBsB,IAAI;qBAAC;gBACxD;gBAEAd,GAAG,oJAAoJ;oBACrJD,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;gBAC3B;gBAEAf,GAAG,8CAA8C;oBAC/CD,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;gBAC3B;gBAEAf,GAAG,kCAAkC;oBACnCD,OAAOU,QAAQM,SAAS,CAAC;gBAC3B;YACF;YAEAjB,SAAS,mDAAmD;gBAC1D,IAAIW;gBAEJZ,UAAU;oBACRY,SAAS,MAAML,YAAY;wBAACZ,qBAAqBsB,IAAI;wBAAExB,sBAAsBwB,IAAI;qBAAC;gBACpF;gBAEAd,GAAG,yCAAyC;oBAC1CD,OAAOU,QAAQM,SAAS,CAAC;oBACzBhB,OAAOU,QAAQM,SAAS,CAAC;gBAC3B;gBAEAf,GAAG,uCAAuC;oBACxCD,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;oBAC7BhB,OAAOU,QAAQO,GAAG,CAACD,SAAS,CAAC;gBAC/B;gBAEAf,GAAG,kCAAkC;oBACnCD,OAAOU,QAAQM,SAAS,CAAC;gBAC3B;YACF;QACF;IACF;AACF"}
@@ -0,0 +1,67 @@
1
+ import { options } from '../options';
2
+ export const MetatagsRule = {
3
+ slug: options.slug.metatagsRule,
4
+ admin: {
5
+ group: options.group.seo
6
+ },
7
+ fields: [
8
+ {
9
+ name: 'collections',
10
+ type: 'select',
11
+ options: options.collections,
12
+ hasMany: true
13
+ },
14
+ {
15
+ name: 'title',
16
+ type: 'textarea',
17
+ admin: {
18
+ components: {
19
+ Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens'
20
+ }
21
+ },
22
+ localized: true
23
+ },
24
+ {
25
+ name: 'description',
26
+ type: 'textarea',
27
+ admin: {
28
+ components: {
29
+ Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens'
30
+ }
31
+ },
32
+ localized: true
33
+ },
34
+ {
35
+ name: 'keywords',
36
+ type: 'textarea',
37
+ admin: {
38
+ components: {
39
+ Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens'
40
+ }
41
+ },
42
+ localized: true
43
+ },
44
+ {
45
+ label: 'Available Tokens',
46
+ type: 'collapsible',
47
+ admin: {
48
+ initCollapsed: true,
49
+ position: 'sidebar'
50
+ },
51
+ fields: [
52
+ {
53
+ name: 'tokens',
54
+ type: 'text',
55
+ hasMany: true,
56
+ admin: {
57
+ components: {
58
+ Field: '@websolutespa/payload-plugin-seo/client#Tokens'
59
+ }
60
+ }
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ };
66
+
67
+ //# sourceMappingURL=MetatagsRule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/collections/MetatagsRule.ts"],"sourcesContent":["import { CollectionConfig } from 'payload';\r\nimport { options } from '../options';\r\n\r\nexport const MetatagsRule: CollectionConfig = {\r\n slug: options.slug.metatagsRule,\r\n admin: {\r\n group: options.group.seo,\r\n },\r\n fields: [\r\n {\r\n name: 'collections',\r\n type: 'select',\r\n options: options.collections,\r\n hasMany: true,\r\n },\r\n {\r\n name: 'title',\r\n type: 'textarea',\r\n admin: {\r\n components: {\r\n Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens',\r\n },\r\n },\r\n localized: true,\r\n },\r\n {\r\n name: 'description',\r\n type: 'textarea',\r\n admin: {\r\n components: {\r\n Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens',\r\n },\r\n },\r\n localized: true,\r\n },\r\n {\r\n name: 'keywords',\r\n type: 'textarea',\r\n admin: {\r\n components: {\r\n Field: '@websolutespa/payload-plugin-seo/client#TextWithTokens',\r\n },\r\n },\r\n localized: true,\r\n },\r\n {\r\n label: 'Available Tokens',\r\n type: 'collapsible',\r\n admin: {\r\n initCollapsed: true,\r\n position: 'sidebar',\r\n },\r\n fields: [\r\n {\r\n name: 'tokens',\r\n type: 'text',\r\n hasMany: true,\r\n admin: {\r\n components: {\r\n Field: '@websolutespa/payload-plugin-seo/client#Tokens',\r\n },\r\n },\r\n },\r\n ],\r\n },\r\n ],\r\n};\r\n"],"names":["options","MetatagsRule","slug","metatagsRule","admin","group","seo","fields","name","type","collections","hasMany","components","Field","localized","label","initCollapsed","position"],"mappings":"AACA,SAASA,OAAO,QAAQ,aAAa;AAErC,OAAO,MAAMC,eAAiC;IAC5CC,MAAMF,QAAQE,IAAI,CAACC,YAAY;IAC/BC,OAAO;QACLC,OAAOL,QAAQK,KAAK,CAACC,GAAG;IAC1B;IACAC,QAAQ;QACN;YACEC,MAAM;YACNC,MAAM;YACNT,SAASA,QAAQU,WAAW;YAC5BC,SAAS;QACX;QACA;YACEH,MAAM;YACNC,MAAM;YACNL,OAAO;gBACLQ,YAAY;oBACVC,OAAO;gBACT;YACF;YACAC,WAAW;QACb;QACA;YACEN,MAAM;YACNC,MAAM;YACNL,OAAO;gBACLQ,YAAY;oBACVC,OAAO;gBACT;YACF;YACAC,WAAW;QACb;QACA;YACEN,MAAM;YACNC,MAAM;YACNL,OAAO;gBACLQ,YAAY;oBACVC,OAAO;gBACT;YACF;YACAC,WAAW;QACb;QACA;YACEC,OAAO;YACPN,MAAM;YACNL,OAAO;gBACLY,eAAe;gBACfC,UAAU;YACZ;YACAV,QAAQ;gBACN;oBACEC,MAAM;oBACNC,MAAM;oBACNE,SAAS;oBACTP,OAAO;wBACLQ,YAAY;4BACVC,OAAO;wBACT;oBACF;gBACF;aACD;QACH;KACD;AACH,EAAE"}
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { FieldLabel, useField, useFormFields } from '@payloadcms/ui';
4
+ import * as React from 'react';
5
+ import TextInput from 'react-autocomplete-input';
6
+ const baseClass = 'text-with-tokens';
7
+ export const TextWithTokens = (props)=>{
8
+ const { path, field } = props;
9
+ const { label, required } = field;
10
+ const { value, setValue } = useField({
11
+ path
12
+ });
13
+ const tokensField = useFormFields(([fields, dispatch])=>fields.tokens);
14
+ let tokens = tokensField?.value || [];
15
+ tokens = tokens.map((token)=>token.replace('[', '').replace(']', ''));
16
+ const classes = [
17
+ 'field-type',
18
+ 'textarea',
19
+ baseClass
20
+ ].join(' ');
21
+ return /*#__PURE__*/ _jsxs("div", {
22
+ className: classes,
23
+ children: [
24
+ /*#__PURE__*/ _jsx(FieldLabel, {
25
+ label: label,
26
+ path: path,
27
+ required: required
28
+ }),
29
+ /*#__PURE__*/ _jsx(TextInput, {
30
+ trigger: [
31
+ '['
32
+ ],
33
+ options: {
34
+ '[': tokens
35
+ },
36
+ maxOptions: 50,
37
+ spacer: '] ',
38
+ defaultValue: value,
39
+ onChange: (e)=>setValue(e)
40
+ })
41
+ ]
42
+ });
43
+ };
44
+
45
+ //# sourceMappingURL=TextWithTokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/TextWithTokens.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { FieldLabel, useField, useFormFields } from '@payloadcms/ui';\r\nimport { TextFieldClientProps } from 'payload';\r\nimport * as React from 'react';\r\nimport TextInput from 'react-autocomplete-input';\r\n\r\nconst baseClass = 'text-with-tokens';\r\n\r\nexport const TextWithTokens: React.FC<TextFieldClientProps> = (props) => {\r\n const { path, field } = props;\r\n const { label, required } = field;\r\n const { value, setValue } = useField<string>({ path });\r\n\r\n const tokensField = useFormFields(([fields, dispatch]) => fields.tokens);\r\n let tokens = (tokensField?.value as string[]) || [];\r\n tokens = tokens.map((token) => token.replace('[', '').replace(']', ''));\r\n\r\n const classes = ['field-type', 'textarea', baseClass].join(' ');\r\n\r\n return (\r\n <div className={classes}>\r\n <FieldLabel label={label} path={path} required={required} />\r\n <TextInput\r\n trigger={['[']}\r\n options={{ '[': tokens }}\r\n maxOptions={50}\r\n spacer={'] '}\r\n defaultValue={value}\r\n onChange={(e) => setValue(e)}\r\n />\r\n </div>\r\n );\r\n};\r\n"],"names":["FieldLabel","useField","useFormFields","React","TextInput","baseClass","TextWithTokens","props","path","field","label","required","value","setValue","tokensField","fields","dispatch","tokens","map","token","replace","classes","join","div","className","trigger","options","maxOptions","spacer","defaultValue","onChange","e"],"mappings":"AAAA;;AAEA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,aAAa,QAAQ,iBAAiB;AAErE,YAAYC,WAAW,QAAQ;AAC/B,OAAOC,eAAe,2BAA2B;AAEjD,MAAMC,YAAY;AAElB,OAAO,MAAMC,iBAAiD,CAACC;IAC7D,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGF;IACxB,MAAM,EAAEG,KAAK,EAAEC,QAAQ,EAAE,GAAGF;IAC5B,MAAM,EAAEG,KAAK,EAAEC,QAAQ,EAAE,GAAGZ,SAAiB;QAAEO;IAAK;IAEpD,MAAMM,cAAcZ,cAAc,CAAC,CAACa,QAAQC,SAAS,GAAKD,OAAOE,MAAM;IACvE,IAAIA,SAAS,AAACH,aAAaF,SAAsB,EAAE;IACnDK,SAASA,OAAOC,GAAG,CAAC,CAACC,QAAUA,MAAMC,OAAO,CAAC,KAAK,IAAIA,OAAO,CAAC,KAAK;IAEnE,MAAMC,UAAU;QAAC;QAAc;QAAYhB;KAAU,CAACiB,IAAI,CAAC;IAE3D,qBACE,MAACC;QAAIC,WAAWH;;0BACd,KAACrB;gBAAWU,OAAOA;gBAAOF,MAAMA;gBAAMG,UAAUA;;0BAChD,KAACP;gBACCqB,SAAS;oBAAC;iBAAI;gBACdC,SAAS;oBAAE,KAAKT;gBAAO;gBACvBU,YAAY;gBACZC,QAAQ;gBACRC,cAAcjB;gBACdkB,UAAU,CAACC,IAAMlB,SAASkB;;;;AAIlC,EAAE"}
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useField, useFormFields } from '@payloadcms/ui';
4
+ import * as React from 'react';
5
+ export const Tokens = (props)=>{
6
+ const { path, field } = props;
7
+ const { label, required } = field;
8
+ const collectionsField = useFormFields(([fields])=>fields['collections']);
9
+ const { value = [], setValue } = useField({
10
+ path
11
+ });
12
+ React.useEffect(()=>{
13
+ const fetchOptions = async ()=>{
14
+ try {
15
+ const collections = collectionsField?.value;
16
+ const url = `${process.env.PAYLOAD_PUBLIC_BASE_PATH || ''}/api/seo/tokens?collections=${collections?.join(',') || ''}`;
17
+ const response = await fetch(url, {
18
+ method: 'GET',
19
+ credentials: 'include',
20
+ headers: {
21
+ 'Content-Type': 'application/json'
22
+ }
23
+ });
24
+ if (response.ok) {
25
+ const tokens = await response.json();
26
+ console.log(tokens);
27
+ setValue(tokens);
28
+ } else {
29
+ setValue([]);
30
+ }
31
+ } catch (error) {
32
+ console.error('Error fetching data:', error);
33
+ }
34
+ };
35
+ fetchOptions();
36
+ }, [
37
+ collectionsField
38
+ ]);
39
+ return /*#__PURE__*/ _jsx("div", {
40
+ className: "tokens-list",
41
+ children: /*#__PURE__*/ _jsx("ul", {
42
+ children: value.map((token)=>/*#__PURE__*/ _jsx("li", {
43
+ children: token
44
+ }, token))
45
+ })
46
+ });
47
+ };
48
+
49
+ //# sourceMappingURL=Tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/Tokens.tsx"],"sourcesContent":["'use client';\r\n\r\nimport { useField, useFormFields } from '@payloadcms/ui';\r\nimport { TextFieldClientProps } from 'payload';\r\nimport * as React from 'react';\r\n\r\nexport const Tokens: React.FC<TextFieldClientProps> = (props) => {\r\n const { path, field } = props;\r\n const { label, required } = field;\r\n const collectionsField = useFormFields(([fields]) => fields['collections']);\r\n const { value = [], setValue } = useField<string[]>({ path });\r\n\r\n React.useEffect(() => {\r\n const fetchOptions = async () => {\r\n try {\r\n const collections = collectionsField?.value as string[] | undefined;\r\n const url = `${(process.env.PAYLOAD_PUBLIC_BASE_PATH || '')}/api/seo/tokens?collections=${(collections?.join(',') || '')}`;\r\n const response = await fetch(url, {\r\n method: 'GET',\r\n credentials: 'include',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n });\r\n if (response.ok) {\r\n const tokens = (await response.json()) as string[];\r\n console.log(tokens);\r\n setValue(tokens);\r\n } else {\r\n setValue([]);\r\n }\r\n } catch (error) {\r\n console.error('Error fetching data:', error);\r\n }\r\n };\r\n\r\n fetchOptions();\r\n }, [collectionsField]);\r\n\r\n return (\r\n <div className=\"tokens-list\">\r\n <ul>\r\n {value.map((token) => (\r\n <li key={token}>{token}</li>\r\n ))}\r\n </ul>\r\n </div>\r\n );\r\n};\r\n"],"names":["useField","useFormFields","React","Tokens","props","path","field","label","required","collectionsField","fields","value","setValue","useEffect","fetchOptions","collections","url","process","env","PAYLOAD_PUBLIC_BASE_PATH","join","response","fetch","method","credentials","headers","ok","tokens","json","console","log","error","div","className","ul","map","token","li"],"mappings":"AAAA;;AAEA,SAASA,QAAQ,EAAEC,aAAa,QAAQ,iBAAiB;AAEzD,YAAYC,WAAW,QAAQ;AAE/B,OAAO,MAAMC,SAAyC,CAACC;IACrD,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGF;IACxB,MAAM,EAAEG,KAAK,EAAEC,QAAQ,EAAE,GAAGF;IAC5B,MAAMG,mBAAmBR,cAAc,CAAC,CAACS,OAAO,GAAKA,MAAM,CAAC,cAAc;IAC1E,MAAM,EAAEC,QAAQ,EAAE,EAAEC,QAAQ,EAAE,GAAGZ,SAAmB;QAAEK;IAAK;IAE3DH,MAAMW,SAAS,CAAC;QACd,MAAMC,eAAe;YACnB,IAAI;gBACF,MAAMC,cAAcN,kBAAkBE;gBACtC,MAAMK,MAAM,CAAC,EAAGC,QAAQC,GAAG,CAACC,wBAAwB,IAAI,GAAI,4BAA4B,EAAGJ,aAAaK,KAAK,QAAQ,GAAI,CAAC;gBAC1H,MAAMC,WAAW,MAAMC,MAAMN,KAAK;oBAChCO,QAAQ;oBACRC,aAAa;oBACbC,SAAS;wBACP,gBAAgB;oBAClB;gBACF;gBACA,IAAIJ,SAASK,EAAE,EAAE;oBACf,MAAMC,SAAU,MAAMN,SAASO,IAAI;oBACnCC,QAAQC,GAAG,CAACH;oBACZf,SAASe;gBACX,OAAO;oBACLf,SAAS,EAAE;gBACb;YACF,EAAE,OAAOmB,OAAO;gBACdF,QAAQE,KAAK,CAAC,wBAAwBA;YACxC;QACF;QAEAjB;IACF,GAAG;QAACL;KAAiB;IAErB,qBACE,KAACuB;QAAIC,WAAU;kBACb,cAAA,KAACC;sBACEvB,MAAMwB,GAAG,CAAC,CAACC,sBACV,KAACC;8BAAgBD;mBAARA;;;AAKnB,EAAE"}
@@ -0,0 +1,4 @@
1
+ export { TextWithTokens } from '../components/TextWithTokens';
2
+ export { Tokens } from '../components/Tokens';
3
+
4
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { TextWithTokens } from '../components/TextWithTokens';\r\nexport { Tokens } from '../components/Tokens';\r\n"],"names":["TextWithTokens","Tokens"],"mappings":"AAAA,SAASA,cAAc,QAAQ,+BAA+B;AAC9D,SAASC,MAAM,QAAQ,uBAAuB"}
package/dist/index.d.ts CHANGED
@@ -1,52 +1,52 @@
1
- import { DataField } from '@websolutespa/payload-utils';
2
- import { Resource } from 'i18next';
3
- import { Locale, Access, Plugin } from 'payload/config';
4
- import { PayloadRequest, CollectionConfig, TextField, TextareaField, RichTextField, RelationshipField, Field } from 'payload/types';
5
- import { Block, Tab } from 'payload/dist/fields/config/types';
1
+ import { DataField, Resource } from "@websolutespa/payload-utils";
2
+ import { Access, Block, CollectionConfig, CollectionSlug, Field, Locale, PayloadRequest, Plugin, RelationshipField, RichTextField, Tab, TextField, TextareaField } from "payload";
6
3
 
4
+ //#region src/types.d.ts
7
5
  type SeoInitOptions = {
8
- collections?: string[];
9
- additionalFields?: DataField[];
10
- customTokens?: SeoToken[];
11
- qsMetatagsRules?: string;
12
- metatagsRulesAccess?: MetatagsRuleAccess;
6
+ collections?: string[];
7
+ additionalFields?: DataField[];
8
+ customTokens?: SeoToken[];
9
+ qsMetatagsRules?: string;
10
+ metatagsRulesAccess?: MetatagsRuleAccess;
11
+ enabled?: boolean;
13
12
  };
14
- type SeoSlug = {
15
- metatagsRule: string;
16
- [key: string]: string;
13
+ type SeoSlug = Record<string, CollectionSlug> & {
14
+ metatagsRule: CollectionSlug;
17
15
  };
18
16
  type SeoGroup = {
19
- seo: string;
20
- [key: string]: string;
17
+ seo: string;
18
+ [key: string]: string;
21
19
  };
22
20
  type SeoToken = {
23
- name: string;
24
- replacementFunction: (req: PayloadRequest, collection: CollectionConfig, doc: {
25
- [key: string]: any;
26
- }, locale: Locale | null) => Promise<string> | string;
21
+ name: string;
22
+ replacementFunction: (req: PayloadRequest, collection: CollectionConfig, doc: {
23
+ [key: string]: any;
24
+ }, locale: Locale | null) => Promise<string> | string;
27
25
  };
28
26
  type MetatagsRuleAccess = {
29
- create?: Access;
30
- read?: Access;
31
- readVersions?: Access;
32
- update?: Access;
33
- delete?: Access;
34
- admin?: (args?: any) => boolean | Promise<boolean>;
35
- unlock?: Access;
27
+ create?: Access;
28
+ read?: Access;
29
+ readVersions?: Access;
30
+ update?: Access;
31
+ delete?: Access;
32
+ admin?: (args?: any) => boolean | Promise<boolean>;
33
+ unlock?: Access;
36
34
  };
37
35
  type SeoOptions = {
38
- collections: string[];
39
- customTokens: SeoToken[];
40
- additionalFields: DataField[];
41
- qsMetatagsRules: string;
42
- defaultLocale: string;
43
- defaultMarket: string;
44
- group: SeoGroup;
45
- locales: string[] | Locale[];
46
- slug: SeoSlug;
47
- translations: Resource;
36
+ collections: string[];
37
+ customTokens: SeoToken[];
38
+ additionalFields: DataField[];
39
+ qsMetatagsRules: string;
40
+ defaultLocale: string;
41
+ defaultMarket: string;
42
+ group: SeoGroup;
43
+ locales: string[] | Locale[];
44
+ slug: SeoSlug;
45
+ translations: Resource;
48
46
  };
49
-
47
+ //# sourceMappingURL=types.d.ts.map
48
+ //#endregion
49
+ //#region src/utils/fields.d.ts
50
50
  type SeoField = TextField | TextareaField | RichTextField | RelationshipField;
51
51
  declare function isSeoField(field: Field): field is SeoField;
52
52
  declare const TOKENIZABLE_FIELDS: string[];
@@ -55,9 +55,15 @@ declare function isTokenizableField(field: Field): field is TokenizableField;
55
55
  declare function eachTokenizableField(fields: Field[], callback: (field: TokenizableField, paths: string[], parent?: Field | Block | Tab) => boolean | void, maxDepth?: number): boolean | void;
56
56
  declare function getDataField(fields: Field[], name: string): DataField | undefined;
57
57
  declare function getTokenizableFields(collection: CollectionConfig): TokenizableField[];
58
-
58
+ //# sourceMappingURL=fields.d.ts.map
59
+ //#endregion
60
+ //#region src/utils/findVal.d.ts
59
61
  declare const findVal: any;
62
+ //# sourceMappingURL=findVal.d.ts.map
60
63
 
64
+ //#endregion
65
+ //#region src/seo.d.ts
61
66
  declare const seo: (sourceOptions: SeoInitOptions) => Plugin;
62
-
67
+ //#endregion
63
68
  export { MetatagsRuleAccess, SeoField, SeoGroup, SeoInitOptions, SeoOptions, SeoSlug, SeoToken, TOKENIZABLE_FIELDS, TokenizableField, seo as default, eachTokenizableField, findVal, getDataField, getTokenizableFields, isSeoField, isTokenizableField };
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/utils/fields.ts","../src/utils/findVal.ts","../src/seo.ts"],"sourcesContent":[],"mappings":";;;;KAGY,cAAA;;EAAA,gBAAA,CAAc,EAEL,SAFK,EAAA;EAAA,YAAA,CAAA,EAGT,QAHS,EAAA;EAAA,eAEL,CAAA,EAAA,MAAA;EAAS,mBACb,CAAA,EAEO,kBAFP;EAAQ,OAED,CAAA,EAAA,OAAA;AAAkB,CAAA;AAI9B,KAAA,OAAA,GAAU,MAAH,CAAA,MAAA,EAAkB,cAAlB,CAAA,GAAA;EAAA,YAAA,EACH,cADG;CAAA;AAAG,KAIV,QAAA,GAJU;EAAM,GACZ,EAAA,MAAA;EAAc,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAG9B,CAAA;AAKY,KAAA,QAAA,GAAQ;EAAA,IAAA,EAAA,MAAA;EAAA,mBAGX,EAAA,CAAA,GAAA,EAAA,cAAA,EAAA,UAAA,EACO,gBADP,EAAA,GAAA,EAAA;IACO,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,GAAA;EAAgB,CAAA,EAEpB,MAAA,EAAA,MAAA,GAAA,IAAA,EAAA,GACL,OADK,CAAA,MAAA,CAAA,GAAA,MAAA;CAAM;AACJ,KAGF,kBAAA,GAHE;EAGF,MAAA,CAAA,EACD,MADC;EAAkB,IAAA,CAAA,EAErB,MAFqB;EAAA,YACnB,CAAA,EAEM,MAFN;EAAM,MACR,CAAA,EAEE,MAFF;EAAM,MACE,CAAA,EAEN,MAFM;EAAM,KACZ,CAAA,EAAA,CAAA,IAAA,CAAA,EAAA,GAAA,EAAA,GAAA,OAAA,GAEyB,OAFzB,CAAA,OAAA,CAAA;EAAM,MACN,CAAA,EAEA,MAFA;CAAM;AAEN,KAGC,UAAA,GAHD;EAAM,WAAA,EAAA,MAAA,EAAA;EAGL,YAAA,EAEI,QAFM,EAAA;EAAA,gBAAA,EAGF,SAHE,EAAA;EAAA,eAEN,EAAA,MAAA;EAAQ,aACJ,EAAA,MAAA;EAAS,aAIpB,EAAA,MAAA;EAAQ,KACK,EADb,QACa;EAAM,OACpB,EAAA,MAAA,EAAA,GADc,MACd,EAAA;EAAO,IACC,EADR,OACQ;EAAQ,YAAA,EAAR,QAAQ;;;;;KChDZ,QAAA,GAAW,YAAY,gBAAgB,gBAAgB;iBAEnD,UAAA,QAAkB,iBAAiB;ADFvC,cCMC,kBDNa,EAAA,MAAA,EAAA;AAAA,KCQd,gBAAA,GAAmB,SDRL,GCQiB,aDRjB,GCQiC,aDRjC,GCQiD,iBDRjD;AAEL,iBCQL,kBAAA,CDRK,KAAA,ECQqB,KDRrB,CAAA,EAAA,KAAA,ICQsC,gBDRtC;AACJ,iBCWD,oBAAA,CDXC,MAAA,ECW4B,KDX5B,EAAA,EAAA,QAAA,EAAA,CAAA,KAAA,ECWuD,gBDXvD,EAAA,KAAA,EAAA,MAAA,EAAA,EAAA,MAAA,CAAA,ECWmG,KDXnG,GCW2G,KDX3G,GCWmH,GDXnH,EAAA,GAAA,OAAA,GAAA,IAAA,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EAAA,OAAA,GAAA,IAAA;AAEO,iBCiBR,YAAA,CDjBQ,MAAA,ECiBa,KDjBb,EAAA,EAAA,IAAA,EAAA,MAAA,CAAA,ECiBqC,SDjBrC,GAAA,SAAA;AAAkB,iBC4B1B,oBAAA,CD5B0B,UAAA,EC4BO,gBD5BP,CAAA,EC4BuB,gBD5BvB,EAAA;AAI1C;;;cEZa;;;;;AFqBO,cG8OP,GH9OO,EAAA,CAAA,aAAA,EG8Oe,cH9Of,EAAA,GG8OgC,MH9OhC"}