@nuxtseo/protocol 0.5.4 → 0.5.5

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.
@@ -193,6 +193,10 @@ const keywordResearchEvidenceClientSchema = schema.discriminatedUnion('_tag', [
193
193
  ]);
194
194
  const keywordResearchDataSchema = defineResponseObject({
195
195
  topic: schema.string().min(1),
196
+ topicMetrics: schema.array(schema.discriminatedUnion('_tag', [
197
+ schema.strictObject({ _tag: schema.literal('measured'), topic: schema.string().min(1), metrics: liveKeywordSchema.producer }),
198
+ schema.strictObject({ _tag: schema.literal('no-data'), topic: schema.string().min(1) }),
199
+ ])),
196
200
  keywords: schema.array(liveKeywordSchema.producer),
197
201
  totalFound: schema.number().int().nonnegative(),
198
202
  filters: schema.strictObject({
@@ -224,6 +228,10 @@ const keywordResearchDataSchema = defineResponseObject({
224
228
  evidence: keywordResearchEvidenceProducerSchema,
225
229
  }, {
226
230
  topic: schema.string().min(1),
231
+ topicMetrics: schema.array(schema.discriminatedUnion('_tag', [
232
+ schema.looseObject({ _tag: schema.literal('measured'), topic: schema.string().min(1), metrics: liveKeywordSchema.client }),
233
+ schema.looseObject({ _tag: schema.literal('no-data'), topic: schema.string().min(1) }),
234
+ ])),
227
235
  keywords: schema.array(liveKeywordSchema.client),
228
236
  totalFound: schema.number().int().nonnegative(),
229
237
  filters: schema.looseObject({
@@ -478,11 +486,11 @@ export const keywordResearchOperation = defineOperation({
478
486
  lifecycle: { introduced: '1.0.0' },
479
487
  docs: {
480
488
  summary: 'Research keyword ideas',
481
- description: 'Returns keyword ideas with volume, difficulty, intent, cost data, and explicit provider execution and spend evidence. Local guidance and cached reads consume no units.',
489
+ description: 'Returns exact metrics for each requested topic alongside filtered keyword ideas. Missing provider data stays explicit. Local guidance and cached reads consume no units.',
482
490
  tags: ['Research'],
483
491
  examples: [{
484
492
  request: { params: { siteId: 'site_01JXYZ' }, query: { topic: 'nuxt seo', limit: 20 } },
485
- response: { status: 200, body: { data: { topic: 'nuxt seo', keywords: [], totalFound: 0, filters: { minVolume: 10, maxVolume: 10000, minDifficulty: 0, maxDifficulty: 60 }, evidence: { _tag: 'live', provider: 'dataforseo', spend: { key: 'dataforseo', units: 1 } } }, meta: { requestId: 'req_01JXYZ', version: '1.0' } } },
493
+ response: { status: 200, body: { data: { topic: 'nuxt seo', topicMetrics: [{ _tag: 'no-data', topic: 'nuxt seo' }], keywords: [], totalFound: 0, filters: { minVolume: 10, maxVolume: 10000, minDifficulty: 0, maxDifficulty: 60 }, evidence: { _tag: 'live', provider: 'dataforseo', spend: { key: 'dataforseo', units: 1 } } }, meta: { requestId: 'req_01JXYZ', version: '1.0' } } },
486
494
  }],
487
495
  },
488
496
  });
@@ -705,6 +713,112 @@ export const domainAvailabilityOperation = defineOperation({
705
713
  }],
706
714
  },
707
715
  });
716
+ const trackedCompetitorSchema = schema.strictObject({
717
+ id: schema.string().min(1),
718
+ domain: schema.string().min(1),
719
+ });
720
+ export const competitorsListResponseSchema = defineSuccessEnvelope(defineResponseObject({
721
+ competitors: schema.array(trackedCompetitorSchema),
722
+ }, {
723
+ competitors: schema.array(trackedCompetitorSchema),
724
+ }));
725
+ export const competitorAddResponseSchema = defineSuccessEnvelope(defineResponseObject({
726
+ competitor: trackedCompetitorSchema,
727
+ status: schema.literal('queued'),
728
+ }, {
729
+ competitor: trackedCompetitorSchema,
730
+ status: schema.literal('queued'),
731
+ }));
732
+ export const competitorRemoveResponseSchema = defineSuccessEnvelope(defineResponseObject({
733
+ competitorId: schema.string().min(1),
734
+ deleted: schema.literal(true),
735
+ }, {
736
+ competitorId: schema.string().min(1),
737
+ deleted: schema.literal(true),
738
+ }));
739
+ const competitorWriteAuth = {
740
+ credentials: ['session', 'api_token'],
741
+ scopes: ['research:write'],
742
+ minimumRole: 'member',
743
+ ownership: 'site',
744
+ };
745
+ const competitorResources = {
746
+ reads: [{ type: 'site.research', idFrom: 'params.siteId' }],
747
+ changes: [{ type: 'site.research', idFrom: 'params.siteId' }],
748
+ };
749
+ const competitorWriteErrors = liveResearchErrors;
750
+ export const competitorsListOperation = defineOperation({
751
+ id: 'research.competitors.list',
752
+ method: 'GET',
753
+ path: '/api/v1/sites/{siteId}/research/competitors',
754
+ visibility: 'public',
755
+ semantics: liveResearchSemantics,
756
+ auth: liveResearchAuth,
757
+ request: { params: researchSiteParamsSchema, query: null, headers: null, body: null },
758
+ responses: { 200: competitorsListResponseSchema },
759
+ errors: liveResearchErrors,
760
+ errorResponse: publicV1ErrorEnvelope,
761
+ resources: { reads: competitorResources.reads, changes: [] },
762
+ policy: { rate: publicV1StandardRatePolicy, idempotency: { _tag: 'none' }, entitlement: null, meter: null },
763
+ lifecycle: { introduced: '1.2.0' },
764
+ docs: {
765
+ summary: 'List tracked competitors',
766
+ description: 'Returns competitors you added to this Site. Suggested competitors stay excluded. Reads start no provider work.',
767
+ tags: ['Research'],
768
+ examples: [{ request: { params: { siteId: 's_example' } }, response: { status: 200, body: {
769
+ data: { competitors: [{ id: 'c_example', domain: 'rival.com' }] },
770
+ meta: { requestId: 'req_example', version: '1.0' },
771
+ } } }],
772
+ },
773
+ });
774
+ export const competitorAddOperation = defineOperation({
775
+ id: 'research.competitors.add',
776
+ method: 'POST',
777
+ path: '/api/v1/sites/{siteId}/research/competitors',
778
+ visibility: 'public',
779
+ semantics: { kind: 'mutation', sideEffects: 'state', retry: 'never', readConsistency: null },
780
+ auth: competitorWriteAuth,
781
+ request: { params: researchSiteParamsSchema, query: null, headers: null, body: schema.strictObject({ domain: schema.string().trim().min(1).max(2048) }) },
782
+ responses: { 200: competitorAddResponseSchema },
783
+ errors: competitorWriteErrors,
784
+ errorResponse: publicV1ErrorEnvelope,
785
+ resources: competitorResources,
786
+ policy: { rate: publicV1StandardRatePolicy, idempotency: { _tag: 'none' }, entitlement: 'growth', meter: { key: 'dataforseo', units: 1, charge: 'always' } },
787
+ lifecycle: { introduced: '1.2.0' },
788
+ docs: {
789
+ summary: 'Add one tracked competitor',
790
+ description: 'Queues analysis for one public domain you name. The existing Site and Team limits apply. Duplicate domains start no work.',
791
+ tags: ['Research'],
792
+ examples: [{ request: { params: { siteId: 's_example' }, body: { domain: 'rival.com' } }, response: { status: 200, body: {
793
+ data: { competitor: { id: 'c_example', domain: 'rival.com' }, status: 'queued' },
794
+ meta: { requestId: 'req_example', version: '1.0' },
795
+ } } }],
796
+ },
797
+ });
798
+ export const competitorRemoveOperation = defineOperation({
799
+ id: 'research.competitors.remove',
800
+ method: 'DELETE',
801
+ path: '/api/v1/sites/{siteId}/research/competitors/{competitorId}',
802
+ visibility: 'public',
803
+ semantics: { kind: 'mutation', sideEffects: 'state', retry: 'never', readConsistency: null },
804
+ auth: competitorWriteAuth,
805
+ request: { params: researchSiteParamsSchema.extend({ competitorId: schema.string().min(1) }), query: null, headers: null, body: null },
806
+ responses: { 200: competitorRemoveResponseSchema },
807
+ errors: competitorWriteErrors,
808
+ errorResponse: publicV1ErrorEnvelope,
809
+ resources: competitorResources,
810
+ policy: { rate: publicV1StandardRatePolicy, idempotency: { _tag: 'none' }, entitlement: null, meter: null },
811
+ lifecycle: { introduced: '1.2.0' },
812
+ docs: {
813
+ summary: 'Remove one tracked competitor',
814
+ description: 'Removes a competitor from this Site using its listed id. Removal starts no provider work.',
815
+ tags: ['Research'],
816
+ examples: [{ request: { params: { siteId: 's_example', competitorId: 'c_example' } }, response: { status: 200, body: {
817
+ data: { competitorId: 'c_example', deleted: true },
818
+ meta: { requestId: 'req_example', version: '1.0' },
819
+ } } }],
820
+ },
821
+ });
708
822
  export const researchV1Registry = defineRegistry({
709
823
  feature: 'research',
710
824
  operations: {
@@ -714,5 +828,8 @@ export const researchV1Registry = defineRegistry({
714
828
  rankings: rankingsResearchOperation,
715
829
  domainTraffic: domainTrafficOperation,
716
830
  domainAvailability: domainAvailabilityOperation,
831
+ listCompetitors: competitorsListOperation,
832
+ addCompetitor: competitorAddOperation,
833
+ removeCompetitor: competitorRemoveOperation,
717
834
  },
718
835
  });