@nebulacomponents/citable 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/universal/.agents/skills/citable/SKILL.md +1 -1
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/manifest.json +14 -5
- package/dist/universal/.agents/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.agents/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.agents/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.agents/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.agents/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.agents/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.agents/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.claude/agents/citable/manifest.json +2 -2
- package/dist/universal/.claude/skills/citable/SKILL.md +1 -1
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/manifest.json +14 -5
- package/dist/universal/.claude/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.claude/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.claude/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.claude/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.claude/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.claude/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.claude/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.cursor/skills/citable/SKILL.md +1 -1
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/manifest.json +14 -5
- package/dist/universal/.cursor/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.cursor/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.cursor/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.cursor/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.cursor/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.cursor/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.gemini/skills/citable/SKILL.md +1 -1
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/manifest.json +14 -5
- package/dist/universal/.gemini/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.gemini/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.gemini/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.gemini/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.gemini/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.gemini/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.github/skills/citable/SKILL.md +1 -1
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/manifest.json +14 -5
- package/dist/universal/.github/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.github/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.github/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.github/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.github/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.github/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.github/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.kiro/skills/citable/SKILL.md +1 -1
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/manifest.json +14 -5
- package/dist/universal/.kiro/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.kiro/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.kiro/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.kiro/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.kiro/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.kiro/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.opencode/skills/citable/SKILL.md +1 -1
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/manifest.json +14 -5
- package/dist/universal/.opencode/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.opencode/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.opencode/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.opencode/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.opencode/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.opencode/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +1 -1
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/manifest.json +14 -5
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.qoder/skills/citable/SKILL.md +1 -1
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/manifest.json +14 -5
- package/dist/universal/.qoder/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.qoder/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.qoder/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.qoder/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.qoder/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.qoder/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.rovodev/skills/citable/SKILL.md +1 -1
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/manifest.json +14 -5
- package/dist/universal/.rovodev/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.rovodev/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.rovodev/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.rovodev/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.rovodev/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.rovodev/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae/skills/citable/SKILL.md +1 -1
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/manifest.json +14 -5
- package/dist/universal/.trae/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +1 -1
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/manifest.json +14 -5
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-evidence.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/claim-verification.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/derivation.schema.json +18 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-comparison.schema.json +16 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/external-observation.schema.json +32 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/prompt-set.schema.json +47 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/representation-data.schema.json +21 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/research-dataset.schema.json +27 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/search-discovery-data.schema.json +20 -0
- package/dist/universal/manifest.json +172 -64
- package/package.json +1 -1
- package/schemas/claim-evidence.schema.json +21 -0
- package/schemas/claim-verification.schema.json +18 -0
- package/schemas/derivation.schema.json +18 -0
- package/schemas/external-comparison.schema.json +16 -0
- package/schemas/external-observation.schema.json +32 -0
- package/schemas/prompt-set.schema.json +47 -0
- package/schemas/representation-data.schema.json +21 -0
- package/schemas/research-dataset.schema.json +27 -0
- package/schemas/search-discovery-data.schema.json +20 -0
- package/skill/SKILL.md +1 -1
- package/src/connectors/gsc.js +4 -0
- package/src/discovery/searchConsole.js +186 -0
- package/src/evidence/external.js +251 -0
- package/src/representation/prompts.js +117 -0
- package/src/representation/representation.js +186 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebulacomponents/citable",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Production-grade SEO, AEO, and GEO agent skill with auditable detection, claim governance, evidence validation, and multi-agent installation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"seo",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/claim-evidence.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Claim to evidence lineage",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["claim_id", "claim_version", "proposition", "declared_scope", "evidence_references", "observation_references", "dataset_references", "derivation_references", "assertion", "limitations", "claim_hash"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"claim_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"claim_version": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"proposition": { "type": "string", "minLength": 3 },
|
|
12
|
+
"declared_scope": { "type": "object", "additionalProperties": false, "required": ["type", "generalization"], "properties": { "type": { "type": "string", "enum": ["observation", "dataset", "declared_population"] }, "observation_reference": { "type": "string" }, "dataset_reference": { "type": "string" }, "generalization": { "type": "string", "enum": ["bounded", "universal"] } } },
|
|
13
|
+
"evidence_references": { "type": "array", "items": { "type": "string" } },
|
|
14
|
+
"observation_references": { "type": "array", "items": { "type": "string" } },
|
|
15
|
+
"dataset_references": { "type": "array", "items": { "type": "string" } },
|
|
16
|
+
"derivation_references": { "type": "array", "items": { "type": "string" } },
|
|
17
|
+
"assertion": { "type": "object" },
|
|
18
|
+
"limitations": { "type": "array", "items": { "type": "string" } },
|
|
19
|
+
"claim_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/claim-verification.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Bounded claim verification result",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["claim_id", "claim_version", "overall", "proposition_support", "scope_support", "reasons", "lineage"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"claim_id": { "type": "string" },
|
|
10
|
+
"claim_version": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"overall": { "type": "string", "enum": ["SUPPORTED", "PARTIALLY_SUPPORTED", "UNSUPPORTED", "CONTRADICTED", "INDETERMINATE", "NOT_OBSERVED"] },
|
|
12
|
+
"proposition_support": { "type": "string", "enum": ["SUPPORTED", "PARTIALLY_SUPPORTED", "UNSUPPORTED", "CONTRADICTED", "INDETERMINATE", "NOT_OBSERVED"] },
|
|
13
|
+
"scope_support": { "type": "string", "enum": ["SUPPORTED", "PARTIALLY_SUPPORTED", "UNSUPPORTED", "INDETERMINATE", "NOT_OBSERVED"] },
|
|
14
|
+
"reasons": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["code", "message"], "properties": { "code": { "type": "string" }, "message": { "type": "string" } } } },
|
|
15
|
+
"lineage": { "type": "object", "additionalProperties": false, "required": ["claim_reference", "observation_references", "dataset_references", "derivation_references"], "properties": { "claim_reference": { "type": "string" }, "observation_references": { "type": "array", "items": { "type": "string" } }, "dataset_references": { "type": "array", "items": { "type": "string" } }, "derivation_references": { "type": "array", "items": { "type": "string" } } } },
|
|
16
|
+
"limitations": { "type": "array", "items": { "type": "string" } }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/derivation.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Evidence derivation",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["derivation_id", "derivation_version", "operation", "input_observation_references", "result", "implementation", "derivation_hash"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"derivation_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"derivation_version": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"operation": { "type": "string", "minLength": 1 },
|
|
12
|
+
"input_observation_references": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
13
|
+
"predicate": { "type": "object", "additionalProperties": false, "required": ["path", "operator", "value"], "properties": { "path": { "type": "string" }, "operator": { "type": "string", "enum": ["equals"] }, "value": {} } },
|
|
14
|
+
"result": { "type": "object" },
|
|
15
|
+
"implementation": { "type": "object", "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string" }, "version": { "type": "string" } } },
|
|
16
|
+
"derivation_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/external-comparison.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "External evidence comparison",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["comparison_id", "left_reference", "right_reference", "status", "dimensions", "limitations"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"comparison_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"left_reference": { "type": "string", "minLength": 1 },
|
|
11
|
+
"right_reference": { "type": "string", "minLength": 1 },
|
|
12
|
+
"status": { "type": "string", "enum": ["COMPARABLE", "PARTIALLY_COMPARABLE", "NOT_COMPARABLE", "INDETERMINATE"] },
|
|
13
|
+
"dimensions": { "type": "object", "additionalProperties": { "type": "string", "enum": ["match", "mismatch", "missing", "unknown"] } },
|
|
14
|
+
"limitations": { "type": "array", "items": { "type": "string" } }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/external-observation.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Provider-neutral external observation",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["observation_id", "observation_type", "subject", "source", "collector", "observed_at", "population", "observation_status", "retrieval_status", "data", "evidence_hash"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"observation_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"observation_type": { "type": "string", "enum": ["PAGE_OBSERVATION", "EXTERNAL_OBSERVATION", "DERIVED_OBSERVATION"] },
|
|
11
|
+
"subject": { "type": "object", "additionalProperties": false, "required": ["type", "id"], "properties": { "type": { "type": "string", "minLength": 1 }, "id": { "type": "string", "minLength": 1 } } },
|
|
12
|
+
"source": { "type": "object", "additionalProperties": false, "required": ["provider", "method", "authority"], "properties": { "provider": { "type": "string", "minLength": 1 }, "method": { "type": "string", "minLength": 1 }, "authority": { "type": "string", "enum": ["first_party", "external_provider", "third_party", "synthetic", "inferred", "unknown"] }, "account_or_property": { "type": ["string", "null"] } } },
|
|
13
|
+
"collector": { "type": "object", "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } },
|
|
14
|
+
"parser": { "type": ["object", "null"], "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } },
|
|
15
|
+
"configuration": { "type": ["object", "null"], "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } },
|
|
16
|
+
"observed_at": { "type": "string", "format": "date-time" },
|
|
17
|
+
"retrieved_at": { "type": ["string", "null"], "format": "date-time" },
|
|
18
|
+
"executed_at": { "type": ["string", "null"], "format": "date-time" },
|
|
19
|
+
"population": { "$ref": "#/definitions/population" },
|
|
20
|
+
"observation_status": { "type": "string", "enum": ["observed", "not_observed", "incomplete", "indeterminate", "failed"] },
|
|
21
|
+
"retrieval_status": { "type": "string", "enum": ["retrieved", "not_retrieved", "partial", "indeterminate", "failed"] },
|
|
22
|
+
"determination_status": { "type": ["string", "null"], "enum": ["pass", "fail", "warning", "indeterminate", "not_tested", "error", "not_applicable", null] },
|
|
23
|
+
"data": { "type": "object" },
|
|
24
|
+
"context": { "type": "object", "additionalProperties": false, "properties": { "observed": { "type": "object" }, "supplied": { "type": "object" } } },
|
|
25
|
+
"lineage": { "type": "object", "additionalProperties": false, "properties": { "source_evidence": { "type": "array", "items": { "type": "string" } }, "predecessor_references": { "type": "array", "items": { "type": "string" } }, "successor_references": { "type": "array", "items": { "type": "string" } }, "derivation_inputs": { "type": "array", "items": { "type": "string" } }, "package_reference": { "type": ["string", "null"] } } },
|
|
26
|
+
"limitations": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
27
|
+
"evidence_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
28
|
+
},
|
|
29
|
+
"definitions": {
|
|
30
|
+
"population": { "type": "object", "additionalProperties": false, "required": ["declared", "observed", "retrieved", "evaluated", "unavailable", "failed"], "properties": { "declared": { "type": ["object", "null"], "additionalProperties": false, "properties": { "id": { "type": "string" }, "size": { "type": "integer", "minimum": 0 } } }, "observed": { "type": "integer", "minimum": 0 }, "retrieved": { "type": "integer", "minimum": 0 }, "evaluated": { "type": "integer", "minimum": 0 }, "unavailable": { "type": "integer", "minimum": 0 }, "failed": { "type": "integer", "minimum": 0 } } }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/prompt-set.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Immutable versioned prompt population",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["prompt_set_id", "prompt_set_version", "purpose", "generation_method", "generated_at", "population_size", "sampling_method", "included_prompts", "excluded_prompts", "intended_population", "limitations", "prompt_set_hash"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"prompt_set_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"prompt_set_version": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"purpose": { "type": "string", "minLength": 1 },
|
|
12
|
+
"generation_method": { "type": "string", "minLength": 1 },
|
|
13
|
+
"generation_method_version": { "type": ["string", "null"] },
|
|
14
|
+
"generation_input": {},
|
|
15
|
+
"generated_at": { "type": "string", "format": "date-time" },
|
|
16
|
+
"population_size": { "type": "integer", "minimum": 0 },
|
|
17
|
+
"sampling_method": { "type": "string", "minLength": 1 },
|
|
18
|
+
"included_prompts": { "type": "array", "items": { "$ref": "#/definitions/prompt" } },
|
|
19
|
+
"excluded_prompts": { "type": "array", "items": { "$ref": "#/definitions/excludedPrompt" } },
|
|
20
|
+
"intended_population": { "type": "string", "minLength": 1 },
|
|
21
|
+
"limitations": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
22
|
+
"prompt_set_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
23
|
+
},
|
|
24
|
+
"definitions": {
|
|
25
|
+
"prompt": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"required": ["prompt_id", "prompt_text"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"prompt_id": { "type": "string", "minLength": 1 },
|
|
31
|
+
"prompt_text": { "type": "string", "minLength": 1 },
|
|
32
|
+
"context": { "type": ["object", "null"] },
|
|
33
|
+
"locale": { "type": ["string", "null"] }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"excludedPrompt": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"required": ["prompt_id", "exclusion_reason"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"prompt_id": { "type": "string", "minLength": 1 },
|
|
42
|
+
"prompt_text": { "type": ["string", "null"] },
|
|
43
|
+
"exclusion_reason": { "type": "string", "minLength": 1 }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/representation-data.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Provider-neutral AI representation observation data",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["provider", "product", "surface", "model", "prompt", "prompt_set", "execution", "response", "representation", "evidence_origin"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"provider": { "type": "string", "minLength": 1 },
|
|
10
|
+
"product": { "type": "string", "minLength": 1 },
|
|
11
|
+
"surface": { "type": "string", "minLength": 1 },
|
|
12
|
+
"model": { "type": "object", "additionalProperties": false, "required": ["value", "status"], "properties": { "value": { "type": ["string", "null"] }, "status": { "type": "string", "enum": ["known", "UNKNOWN", "UNAVAILABLE", "NOT_DISCLOSED"] } } },
|
|
13
|
+
"prompt": { "type": "object", "additionalProperties": false, "required": ["prompt_id", "prompt_version", "prompt_text"], "properties": { "prompt_id": { "type": "string", "minLength": 1 }, "prompt_version": { "type": "integer", "minimum": 1 }, "prompt_text": { "type": "string", "minLength": 1 } } },
|
|
14
|
+
"prompt_set": { "type": "object", "additionalProperties": false, "required": ["prompt_set_id", "prompt_set_version"], "properties": { "prompt_set_id": { "type": "string", "minLength": 1 }, "prompt_set_version": { "type": "integer", "minimum": 1 } } },
|
|
15
|
+
"execution": { "type": "object", "additionalProperties": false, "required": ["executed_at", "configuration"], "properties": { "executed_at": { "type": "string", "format": "date-time" }, "configuration": { "type": "object", "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } } } },
|
|
16
|
+
"response": { "type": "object", "additionalProperties": false, "required": ["status", "response_hash", "parser", "raw_response_retained"], "properties": { "status": { "type": "string", "enum": ["succeeded", "refused", "error", "timeout", "rate_limited", "malformed", "unavailable", "parser_failed"] }, "response_hash": { "type": ["string", "null"], "pattern": "^[a-f0-9]{64}$" }, "parser": { "type": "object", "additionalProperties": false, "required": ["id", "version"], "properties": { "id": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } }, "raw_response_retained": { "type": "boolean" }, "provider_metadata": { "type": "object" } } },
|
|
17
|
+
"representation": { "type": "object", "additionalProperties": false, "required": ["brand_mentioned", "domain_mentioned", "domain_cited", "cited_urls", "mentioned_entities", "cited_domains"], "properties": { "brand_mentioned": { "type": ["boolean", "null"] }, "domain_mentioned": { "type": ["boolean", "null"] }, "domain_cited": { "type": ["boolean", "null"] }, "cited_urls": { "type": "array", "items": { "type": "string", "format": "uri" } }, "mentioned_entities": { "type": "array", "items": { "type": "string" } }, "cited_domains": { "type": "array", "items": { "type": "string" } } } },
|
|
18
|
+
"evidence_origin": { "type": "string", "enum": ["LIVE_PROVIDER", "RECORDED_FIXTURE", "SYNTHETIC_TEST"] },
|
|
19
|
+
"limitations": { "type": "array", "items": { "type": "string" } }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/research-dataset.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Immutable research dataset",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["dataset_id", "dataset_version", "declared_purpose", "declared_population", "population_size", "inclusion_criteria", "exclusion_criteria", "collection", "source_references", "observation_references", "normalization", "derivation_references", "coverage", "coverage_status", "limitations", "dataset_hash"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"dataset_id": { "type": "string", "minLength": 1 },
|
|
10
|
+
"dataset_version": { "type": "integer", "minimum": 1 },
|
|
11
|
+
"declared_purpose": { "type": "string", "minLength": 1 },
|
|
12
|
+
"declared_population": { "type": "object", "additionalProperties": false, "required": ["type", "id"], "properties": { "type": { "type": "string", "minLength": 1 }, "id": { "type": "string", "minLength": 1 }, "size": { "type": ["integer", "null"], "minimum": 0 } } },
|
|
13
|
+
"population_size": { "type": ["integer", "null"], "minimum": 0 },
|
|
14
|
+
"inclusion_criteria": { "type": "array", "items": { "type": "string" } },
|
|
15
|
+
"exclusion_criteria": { "type": "array", "items": { "type": "string" } },
|
|
16
|
+
"collection": { "type": "object", "additionalProperties": false, "required": ["started_at", "ended_at"], "properties": { "started_at": { "type": "string", "format": "date-time" }, "ended_at": { "type": "string", "format": "date-time" } } },
|
|
17
|
+
"source_references": { "type": "array", "items": { "type": "string" } },
|
|
18
|
+
"source_retrieval_timestamps": { "type": "array", "items": { "type": "string", "format": "date-time" } },
|
|
19
|
+
"observation_references": { "type": "array", "items": { "type": "string" } },
|
|
20
|
+
"normalization": { "type": "object", "additionalProperties": false, "required": ["method", "version"], "properties": { "method": { "type": "string", "minLength": 1 }, "version": { "type": "string", "minLength": 1 } } },
|
|
21
|
+
"derivation_references": { "type": "array", "items": { "type": "string" } },
|
|
22
|
+
"coverage": { "type": "object", "additionalProperties": false, "required": ["declared", "retrieved", "evaluated", "unavailable", "failed"], "properties": { "declared": { "type": "integer", "minimum": 0 }, "retrieved": { "type": "integer", "minimum": 0 }, "evaluated": { "type": "integer", "minimum": 0 }, "unavailable": { "type": "integer", "minimum": 0 }, "failed": { "type": "integer", "minimum": 0 } } },
|
|
23
|
+
"coverage_status": { "type": "string", "enum": ["complete_for_declared_population", "partial", "truncated", "indeterminate", "error"] },
|
|
24
|
+
"limitations": { "type": "array", "items": { "type": "string" } },
|
|
25
|
+
"dataset_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "citable://schemas/search-discovery-data.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"title": "Search discovery observation data",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["query", "page", "interval", "metrics", "request", "coverage_status", "evidence_origin"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"query": { "type": ["string", "null"] },
|
|
10
|
+
"page": { "type": ["string", "null"], "format": "uri" },
|
|
11
|
+
"interval": { "type": "object", "additionalProperties": false, "required": ["start_date", "end_date"], "properties": { "start_date": { "type": "string", "format": "date" }, "end_date": { "type": "string", "format": "date" } } },
|
|
12
|
+
"metrics": { "type": "object", "additionalProperties": false, "required": ["impressions", "clicks", "ctr", "average_position"], "properties": { "impressions": { "type": ["number", "null"], "minimum": 0 }, "clicks": { "type": ["number", "null"], "minimum": 0 }, "ctr": { "type": ["number", "null"], "minimum": 0 }, "average_position": { "type": ["number", "null"], "minimum": 0 } } },
|
|
13
|
+
"dimensions": { "type": "object", "additionalProperties": false, "properties": { "country": { "type": ["string", "null"] }, "device": { "type": ["string", "null"] }, "search_appearance": { "type": ["string", "null"] }, "date": { "type": ["string", "null"], "format": "date" } } },
|
|
14
|
+
"request": { "type": "object", "additionalProperties": false, "required": ["property_id", "dimensions", "filters", "row_limit"], "properties": { "property_id": { "type": "string", "minLength": 1 }, "dimensions": { "type": "array", "items": { "type": "string" } }, "filters": { "type": "object" }, "row_limit": { "type": "integer", "minimum": 1, "maximum": 25000 } } },
|
|
15
|
+
"provider_reported": { "type": "object", "additionalProperties": false, "properties": { "row_count": { "type": ["integer", "null"], "minimum": 0 }, "page_start": { "type": "integer", "minimum": 0 } } },
|
|
16
|
+
"response_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
17
|
+
"coverage_status": { "type": "string", "enum": ["complete", "truncated", "indeterminate", "provider_bounded"] },
|
|
18
|
+
"evidence_origin": { "type": "string", "enum": ["LIVE_PROVIDER", "RECORDED_FIXTURE", "SYNTHETIC_TEST"] }
|
|
19
|
+
}
|
|
20
|
+
}
|
package/skill/SKILL.md
CHANGED
|
@@ -7,7 +7,7 @@ description: >
|
|
|
7
7
|
generative engine optimization, structured data governance, claim substantiation,
|
|
8
8
|
crawler policy, entity consistency, content discoverability, or wants a site audited
|
|
9
9
|
for how search and AI systems will retrieve, understand, cite, or recommend it.
|
|
10
|
-
version: 1.
|
|
10
|
+
version: 1.21.0
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Citable — evidence and change control for search and AI citation readiness
|
package/src/connectors/gsc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { providerRequest } from './http.js';
|
|
2
2
|
import { collectionResult, errorMessage, paginationBoundary } from './collectionResult.js';
|
|
3
|
+
import { collectSearchConsoleObservations } from '../discovery/searchConsole.js';
|
|
3
4
|
|
|
4
5
|
const BASE = 'https://www.googleapis.com/webmasters/v3';
|
|
5
6
|
const METRICS = {
|
|
@@ -73,4 +74,7 @@ export const gscConnector = {
|
|
|
73
74
|
});
|
|
74
75
|
return { rows, cursor: endDate, limitations: collection.limitations, collection, ...(connectorError ? { connectorError } : {}) };
|
|
75
76
|
},
|
|
77
|
+
async collectObservations(connection, request, context = {}) {
|
|
78
|
+
return collectSearchConsoleObservations({ connection, request, ...context });
|
|
79
|
+
},
|
|
76
80
|
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createExternalObservation, verifyExternalObservation } from '../evidence/external.js';
|
|
2
|
+
import { canonicalEvidenceJson } from '../evidence/hashes.js';
|
|
3
|
+
import { sha256, nowIso } from '../shared/io.js';
|
|
4
|
+
import { validateAgainst } from '../shared/schemaValidator.js';
|
|
5
|
+
import { providerRequest } from '../connectors/http.js';
|
|
6
|
+
import { collectionResult, errorMessage } from '../connectors/collectionResult.js';
|
|
7
|
+
|
|
8
|
+
const SEARCH_DATA_SCHEMA = 'search-discovery-data.schema.json';
|
|
9
|
+
const BASE = 'https://www.googleapis.com/webmasters/v3';
|
|
10
|
+
const DIMENSIONS = new Set(['date', 'query', 'page', 'country', 'device', 'searchAppearance']);
|
|
11
|
+
const COLLECTOR = { id: 'citable-search-console-observations', version: '1.0.0' };
|
|
12
|
+
const PARSER = { id: 'google-search-analytics-response', version: '1.0.0' };
|
|
13
|
+
|
|
14
|
+
function assertDate(value, name) {
|
|
15
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value) || Number.isNaN(Date.parse(`${value}T00:00:00Z`))) {
|
|
16
|
+
throw new TypeError(`${name} must be an ISO date (YYYY-MM-DD)`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function validateRequest(request) {
|
|
21
|
+
if (!request || typeof request !== 'object' || Array.isArray(request)) throw new TypeError('Search Console request is required');
|
|
22
|
+
if (typeof request.property_id !== 'string' || !request.property_id) throw new TypeError('property_id is required');
|
|
23
|
+
assertDate(request.start_date, 'start_date');
|
|
24
|
+
assertDate(request.end_date, 'end_date');
|
|
25
|
+
if (request.start_date > request.end_date) throw new RangeError('start_date must not be after end_date');
|
|
26
|
+
const dimensions = request.dimensions ?? ['query', 'page'];
|
|
27
|
+
if (!Array.isArray(dimensions) || dimensions.length === 0 || dimensions.some((item) => !DIMENSIONS.has(item))) throw new TypeError('dimensions contain an unsupported Search Console dimension');
|
|
28
|
+
const filters = request.filters ?? {};
|
|
29
|
+
if (!filters || typeof filters !== 'object' || Array.isArray(filters)) throw new TypeError('filters must be an object');
|
|
30
|
+
const rowLimit = request.row_limit ?? 25000;
|
|
31
|
+
if (!Number.isInteger(rowLimit) || rowLimit < 1 || rowLimit > 25000) throw new RangeError('row_limit must be an integer from 1 to 25000');
|
|
32
|
+
return { property_id: request.property_id, start_date: request.start_date, end_date: request.end_date, dimensions: [...new Set(dimensions)], filters, row_limit: rowLimit };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function configurationFor(request) {
|
|
36
|
+
const configuration = { property_id: request.property_id, dimensions: request.dimensions, filters: request.filters, row_limit: request.row_limit };
|
|
37
|
+
return { id: `gsc-${sha256(canonicalEvidenceJson(configuration)).slice(0, 16)}`, version: '1.0.0' };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function numberOrNull(value) {
|
|
41
|
+
return value == null || value === '' || !Number.isFinite(Number(value)) ? null : Number(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requestBody(request, startRow) {
|
|
45
|
+
const body = { startDate: request.start_date, endDate: request.end_date, dimensions: request.dimensions, rowLimit: request.row_limit, startRow, dataState: 'final' };
|
|
46
|
+
if (Object.keys(request.filters).length) body.dimensionFilterGroups = [{ groupType: 'and', filters: Object.entries(request.filters).map(([dimension, expression]) => ({ dimension, operator: 'equals', expression: String(expression) })) }];
|
|
47
|
+
return body;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeRow(row, request, dimensions, pageStart, coverageStatus, providerTotal, responseHash, origin) {
|
|
51
|
+
if (!row || !Array.isArray(row.keys)) throw new TypeError('Search Console row is malformed');
|
|
52
|
+
const values = Object.fromEntries(dimensions.map((name, index) => [name, row.keys[index] ?? null]));
|
|
53
|
+
const data = {
|
|
54
|
+
query: values.query ?? null,
|
|
55
|
+
page: values.page ?? null,
|
|
56
|
+
interval: { start_date: request.start_date, end_date: request.end_date },
|
|
57
|
+
metrics: { impressions: numberOrNull(row.impressions), clicks: numberOrNull(row.clicks), ctr: numberOrNull(row.ctr), average_position: numberOrNull(row.position) },
|
|
58
|
+
dimensions: { country: values.country ?? null, device: values.device ?? null, search_appearance: values.searchAppearance ?? null, date: values.date ?? null },
|
|
59
|
+
request: { property_id: request.property_id, dimensions: request.dimensions, filters: request.filters, row_limit: request.row_limit },
|
|
60
|
+
provider_reported: { row_count: providerTotal, page_start: pageStart },
|
|
61
|
+
coverage_status: coverageStatus,
|
|
62
|
+
response_hash: responseHash,
|
|
63
|
+
evidence_origin: origin,
|
|
64
|
+
};
|
|
65
|
+
const check = validateAgainst(SEARCH_DATA_SCHEMA, data);
|
|
66
|
+
if (!check.valid) throw new TypeError(`search discovery data violates contract: ${check.errors.join('; ')}`);
|
|
67
|
+
const subjectId = `query:${data.query ?? ''}|page:${data.page ?? ''}`;
|
|
68
|
+
return createExternalObservation({
|
|
69
|
+
observation_id: `GSC-${sha256(canonicalEvidenceJson({ request, values, row })).slice(0, 24)}`,
|
|
70
|
+
observation_type: 'EXTERNAL_OBSERVATION',
|
|
71
|
+
subject: { type: 'search_query_page', id: subjectId || `row:${pageStart}` },
|
|
72
|
+
source: { provider: 'google_search_console', method: 'search_analytics_api', authority: 'external_provider', account_or_property: request.property_id },
|
|
73
|
+
collector: COLLECTOR,
|
|
74
|
+
parser: PARSER,
|
|
75
|
+
configuration: configurationFor(request),
|
|
76
|
+
observed_at: nowIso(),
|
|
77
|
+
retrieved_at: nowIso(),
|
|
78
|
+
population: { declared: { id: `GSC-${sha256(canonicalEvidenceJson(request)).slice(0, 16)}`, size: providerTotal }, observed: 1, retrieved: 1, evaluated: 1, unavailable: 0, failed: 0 },
|
|
79
|
+
observation_status: coverageStatus === 'complete' ? 'observed' : 'incomplete',
|
|
80
|
+
retrieval_status: coverageStatus === 'complete' ? 'retrieved' : coverageStatus === 'indeterminate' ? 'indeterminate' : 'partial',
|
|
81
|
+
data,
|
|
82
|
+
lineage: { source_evidence: [responseHash] },
|
|
83
|
+
limitations: ['Search Console reports provider-returned search analytics rows, not universal search demand.', 'Provider privacy filtering, aggregation, and reporting revisions may limit representativeness.'],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Collect normalized Search Console search-analytics observations. This is a
|
|
89
|
+
* provider adapter: credentials are supplied only to the transport and are
|
|
90
|
+
* never written into observations or continuation state.
|
|
91
|
+
*/
|
|
92
|
+
export async function collectSearchConsoleObservations({ connection, request, token, fetchImpl = globalThis.fetch, maxPages = 1000, evidenceOrigin = 'LIVE_PROVIDER' } = {}) {
|
|
93
|
+
const normalized = validateRequest(request);
|
|
94
|
+
if (!['LIVE_PROVIDER', 'RECORDED_FIXTURE', 'SYNTHETIC_TEST'].includes(evidenceOrigin)) throw new TypeError('evidenceOrigin is invalid');
|
|
95
|
+
if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 10000) throw new RangeError('maxPages must be an integer from 1 to 10000');
|
|
96
|
+
if (connection?.property_id && connection.property_id !== normalized.property_id) throw new Error('connection property_id does not match request property_id');
|
|
97
|
+
const dimensions = normalized.dimensions;
|
|
98
|
+
const rows = [];
|
|
99
|
+
const observations = [];
|
|
100
|
+
const errors = [];
|
|
101
|
+
let startRow = 0;
|
|
102
|
+
let providerTotal = null;
|
|
103
|
+
let continuationState = null;
|
|
104
|
+
let pagesRequested = 0;
|
|
105
|
+
let pagesRetrieved = 0;
|
|
106
|
+
let lastResponseHash = null;
|
|
107
|
+
let response;
|
|
108
|
+
do {
|
|
109
|
+
pagesRequested += 1;
|
|
110
|
+
try {
|
|
111
|
+
response = await providerRequest(`${BASE}/sites/${encodeURIComponent(normalized.property_id)}/searchAnalytics/query`, {
|
|
112
|
+
token, fetchImpl, method: 'POST', body: requestBody(normalized, startRow),
|
|
113
|
+
});
|
|
114
|
+
if (!response || !Array.isArray(response.rows)) throw new Error('Search Console response rows must be an array');
|
|
115
|
+
} catch (error) {
|
|
116
|
+
errors.push(`start row ${startRow}: ${errorMessage(error)}`);
|
|
117
|
+
continuationState = { start_row: startRow };
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
pagesRetrieved += 1;
|
|
121
|
+
lastResponseHash = sha256(canonicalEvidenceJson(response));
|
|
122
|
+
if (Number.isInteger(response.rowCount) && response.rowCount >= 0) providerTotal = response.rowCount;
|
|
123
|
+
rows.push(...response.rows.map((row) => ({ row, pageStart: startRow, responseHash: lastResponseHash })));
|
|
124
|
+
const moreRows = providerTotal !== null && rows.length < providerTotal;
|
|
125
|
+
if (providerTotal !== null && !moreRows) break;
|
|
126
|
+
if (providerTotal === null && response.rows.length < normalized.row_limit) break;
|
|
127
|
+
startRow += normalized.row_limit;
|
|
128
|
+
if (pagesRequested >= maxPages) {
|
|
129
|
+
continuationState = { start_row: startRow };
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
} while (true);
|
|
133
|
+
|
|
134
|
+
const collection = collectionResult({
|
|
135
|
+
items: rows,
|
|
136
|
+
paginationState: { pages_requested: pagesRequested, pages_retrieved: pagesRetrieved, boundary: { max_pages: maxPages, row_limit: normalized.row_limit } },
|
|
137
|
+
providerReportedTotal: providerTotal,
|
|
138
|
+
continuationState,
|
|
139
|
+
providerCompleteness: providerTotal !== null ? 'declared' : 'unknown',
|
|
140
|
+
limitations: ['Search Console privacy filtering and aggregation apply.', 'A complete result is complete only for the declared API request; it is not a census of all search behavior.'],
|
|
141
|
+
errors,
|
|
142
|
+
});
|
|
143
|
+
const normalizedRows = rows.map(({ row, pageStart, responseHash }) => normalizeRow(row, normalized, dimensions, pageStart, collection.coverage_status, providerTotal, responseHash || sha256(canonicalEvidenceJson({ request: normalized, rows: [] })), evidenceOrigin));
|
|
144
|
+
observations.push(...normalizedRows);
|
|
145
|
+
return { observations, collection, request: normalized, provider: 'google_search_console', response_hash: lastResponseHash, errors };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function compareSearchDiscoveryObservations(left, right) {
|
|
149
|
+
if (!verifyExternalObservation(left).valid || !verifyExternalObservation(right).valid) return { status: 'INDETERMINATE', dimensions: {}, limitations: ['Observation integrity could not be verified.'] };
|
|
150
|
+
const dimensions = {
|
|
151
|
+
provider: left.source.provider === right.source.provider ? 'match' : 'mismatch',
|
|
152
|
+
property: left.source.account_or_property === right.source.account_or_property ? 'match' : 'mismatch',
|
|
153
|
+
query: left.data.query === right.data.query ? 'match' : 'mismatch',
|
|
154
|
+
page: left.data.page === right.data.page ? 'match' : 'mismatch',
|
|
155
|
+
configuration: left.configuration?.id === right.configuration?.id ? 'match' : 'mismatch',
|
|
156
|
+
interval: JSON.stringify(left.data.interval) === JSON.stringify(right.data.interval) ? 'match' : 'mismatch',
|
|
157
|
+
coverage: left.data.coverage_status === right.data.coverage_status ? 'match' : 'mismatch',
|
|
158
|
+
};
|
|
159
|
+
const hardMismatch = ['provider', 'property', 'query', 'page', 'configuration'].some((key) => dimensions[key] === 'mismatch');
|
|
160
|
+
const incomplete = [left.data.coverage_status, right.data.coverage_status].some((status) => !['complete'].includes(status));
|
|
161
|
+
const status = hardMismatch ? 'NOT_COMPARABLE' : incomplete ? 'INDETERMINATE' : dimensions.interval === 'mismatch' ? 'PARTIALLY_COMPARABLE' : 'COMPARABLE';
|
|
162
|
+
return {
|
|
163
|
+
comparison_id: `GSC-CMP-${sha256(canonicalEvidenceJson({ left: left.observation_id, right: right.observation_id, dimensions })).slice(0, 16)}`,
|
|
164
|
+
left_reference: left.observation_id,
|
|
165
|
+
right_reference: right.observation_id,
|
|
166
|
+
status,
|
|
167
|
+
dimensions,
|
|
168
|
+
limitations: status === 'COMPARABLE' ? [] : ['Search Console interval, configuration, and coverage limitations remain attached; no business opportunity or ranking conclusion is inferred.'],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function validSearchObservations(observations) {
|
|
173
|
+
return (Array.isArray(observations) ? observations : []).filter((item) => verifyExternalObservation(item).valid && item.source?.provider === 'google_search_console');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function pagesForQuery(observations, query) {
|
|
177
|
+
return validSearchObservations(observations).filter((item) => item.data.query === query && item.data.page).map((item) => item);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function queriesForPage(observations, page) {
|
|
181
|
+
return validSearchObservations(observations).filter((item) => item.data.page === page && item.data.query).map((item) => item);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function relationshipObservations(observations, { query = null, page = null } = {}) {
|
|
185
|
+
return validSearchObservations(observations).filter((item) => (query == null || item.data.query === query) && (page == null || item.data.page === page));
|
|
186
|
+
}
|