@ahtmljs/schema 0.1.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/dist/diff.d.ts +16 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +90 -0
- package/dist/diff.js.map +1 -0
- package/dist/format-compact.d.ts +29 -0
- package/dist/format-compact.d.ts.map +1 -0
- package/dist/format-compact.js +567 -0
- package/dist/format-compact.js.map +1 -0
- package/dist/format-json.d.ts +17 -0
- package/dist/format-json.d.ts.map +1 -0
- package/dist/format-json.js +39 -0
- package/dist/format-json.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/snapshot.d.ts +49 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +109 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/types.d.ts +256 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +17 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +179 -0
- package/dist/validate.js.map +1 -0
- package/package.json +23 -0
- package/src/schema.json +222 -0
package/dist/validate.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency structural validator for AHTML snapshots.
|
|
3
|
+
*
|
|
4
|
+
* Returns a list of human-readable issues. Empty array = valid.
|
|
5
|
+
* For full JSON Schema validation, run schema.json through any standard
|
|
6
|
+
* JSON Schema validator (ajv, etc.). This validator is intentionally lean
|
|
7
|
+
* so the @ahtmljs/schema package has zero runtime dependencies.
|
|
8
|
+
*/
|
|
9
|
+
const ENTITY_TYPES = new Set([
|
|
10
|
+
'product',
|
|
11
|
+
'document',
|
|
12
|
+
'task',
|
|
13
|
+
'profile',
|
|
14
|
+
'dataset',
|
|
15
|
+
'conversation',
|
|
16
|
+
]);
|
|
17
|
+
const PAGE_TYPES = new Set([
|
|
18
|
+
'home',
|
|
19
|
+
'product_detail',
|
|
20
|
+
'product_list',
|
|
21
|
+
'article',
|
|
22
|
+
'document',
|
|
23
|
+
'profile',
|
|
24
|
+
'task_list',
|
|
25
|
+
'task_detail',
|
|
26
|
+
'dataset',
|
|
27
|
+
'conversation',
|
|
28
|
+
'checkout',
|
|
29
|
+
'search_results',
|
|
30
|
+
'category',
|
|
31
|
+
'other',
|
|
32
|
+
]);
|
|
33
|
+
export function validate(snap) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
if (typeof snap !== 'object' || snap === null) {
|
|
36
|
+
issues.push({ path: '', message: 'snapshot must be an object', severity: 'error' });
|
|
37
|
+
return issues;
|
|
38
|
+
}
|
|
39
|
+
const s = snap;
|
|
40
|
+
if (s.ahtml !== '0.1') {
|
|
41
|
+
issues.push({
|
|
42
|
+
path: 'ahtml',
|
|
43
|
+
message: `unsupported version "${String(s.ahtml)}" (expected "0.1")`,
|
|
44
|
+
severity: 'error',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (typeof s.url !== 'string' || !s.url) {
|
|
48
|
+
issues.push({ path: 'url', message: 'url is required', severity: 'error' });
|
|
49
|
+
}
|
|
50
|
+
if (typeof s.fetched_at !== 'string' || !isIso8601(s.fetched_at)) {
|
|
51
|
+
issues.push({
|
|
52
|
+
path: 'fetched_at',
|
|
53
|
+
message: 'fetched_at must be an ISO 8601 timestamp',
|
|
54
|
+
severity: 'error',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (typeof s.page_type !== 'string' || !PAGE_TYPES.has(s.page_type)) {
|
|
58
|
+
issues.push({
|
|
59
|
+
path: 'page_type',
|
|
60
|
+
message: `unknown page_type "${String(s.page_type)}"`,
|
|
61
|
+
severity: 'error',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(s.entities)) {
|
|
65
|
+
issues.push({ path: 'entities', message: 'entities must be an array', severity: 'error' });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
s.entities.forEach((e, i) => {
|
|
70
|
+
const p = `entities[${i}]`;
|
|
71
|
+
issues.push(...validateEntity(e, p));
|
|
72
|
+
if (e?.id) {
|
|
73
|
+
if (seen.has(e.id)) {
|
|
74
|
+
issues.push({ path: p + '.id', message: `duplicate entity id "${e.id}"`, severity: 'error' });
|
|
75
|
+
}
|
|
76
|
+
seen.add(e.id);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (!Array.isArray(s.actions)) {
|
|
81
|
+
issues.push({ path: 'actions', message: 'actions must be an array', severity: 'error' });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const seen = new Set();
|
|
85
|
+
s.actions.forEach((a, i) => {
|
|
86
|
+
const p = `actions[${i}]`;
|
|
87
|
+
issues.push(...validateAction(a, p));
|
|
88
|
+
if (a?.id) {
|
|
89
|
+
if (seen.has(a.id)) {
|
|
90
|
+
issues.push({ path: p + '.id', message: `duplicate action id "${a.id}"`, severity: 'error' });
|
|
91
|
+
}
|
|
92
|
+
seen.add(a.id);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (s.ttl !== undefined && (typeof s.ttl !== 'number' || s.ttl < 0)) {
|
|
97
|
+
issues.push({ path: 'ttl', message: 'ttl must be a non-negative number', severity: 'error' });
|
|
98
|
+
}
|
|
99
|
+
return issues;
|
|
100
|
+
}
|
|
101
|
+
function validateEntity(e, path) {
|
|
102
|
+
const issues = [];
|
|
103
|
+
if (typeof e !== 'object' || e === null) {
|
|
104
|
+
issues.push({ path, message: 'entity must be an object', severity: 'error' });
|
|
105
|
+
return issues;
|
|
106
|
+
}
|
|
107
|
+
const ent = e;
|
|
108
|
+
if (!ent.id || typeof ent.id !== 'string') {
|
|
109
|
+
issues.push({ path: path + '.id', message: 'entity.id is required', severity: 'error' });
|
|
110
|
+
}
|
|
111
|
+
else if (!/^[a-z_]+:[A-Za-z0-9_\-.]+$/.test(ent.id)) {
|
|
112
|
+
issues.push({
|
|
113
|
+
path: path + '.id',
|
|
114
|
+
message: `entity id "${ent.id}" should match "type:slug" (e.g. "product:mbp-14")`,
|
|
115
|
+
severity: 'warning',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (!ent.type || !ENTITY_TYPES.has(ent.type)) {
|
|
119
|
+
issues.push({
|
|
120
|
+
path: path + '.type',
|
|
121
|
+
message: `unknown entity type "${String(ent.type)}"`,
|
|
122
|
+
severity: 'error',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
else if (ent.id && !ent.id.startsWith(ent.type + ':')) {
|
|
126
|
+
issues.push({
|
|
127
|
+
path: path + '.id',
|
|
128
|
+
message: `id prefix should match type ("${ent.type}:..."), got "${ent.id}"`,
|
|
129
|
+
severity: 'warning',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (ent.type === 'product') {
|
|
133
|
+
if (!ent.name) {
|
|
134
|
+
issues.push({ path: path + '.name', message: 'product.name is required', severity: 'error' });
|
|
135
|
+
}
|
|
136
|
+
if (ent.price) {
|
|
137
|
+
if (typeof ent.price.amount !== 'number') {
|
|
138
|
+
issues.push({ path: path + '.price.amount', message: 'price.amount must be a number', severity: 'error' });
|
|
139
|
+
}
|
|
140
|
+
if (typeof ent.price.currency !== 'string') {
|
|
141
|
+
issues.push({ path: path + '.price.currency', message: 'price.currency must be a string (ISO 4217)', severity: 'error' });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return issues;
|
|
146
|
+
}
|
|
147
|
+
function validateAction(a, path) {
|
|
148
|
+
const issues = [];
|
|
149
|
+
if (typeof a !== 'object' || a === null) {
|
|
150
|
+
issues.push({ path, message: 'action must be an object', severity: 'error' });
|
|
151
|
+
return issues;
|
|
152
|
+
}
|
|
153
|
+
const act = a;
|
|
154
|
+
if (!act.id || typeof act.id !== 'string') {
|
|
155
|
+
issues.push({ path: path + '.id', message: 'action.id is required', severity: 'error' });
|
|
156
|
+
}
|
|
157
|
+
if (act.cost && !['free', 'purchase', 'subscription', 'rate_limited', 'compute'].includes(act.cost.category)) {
|
|
158
|
+
issues.push({
|
|
159
|
+
path: path + '.cost.category',
|
|
160
|
+
message: `unknown cost category "${act.cost.category}"`,
|
|
161
|
+
severity: 'error',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (act.confirmation && !['none', 'recommended', 'required'].includes(act.confirmation)) {
|
|
165
|
+
issues.push({
|
|
166
|
+
path: path + '.confirmation',
|
|
167
|
+
message: `confirmation must be none|recommended|required`,
|
|
168
|
+
severity: 'error',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return issues;
|
|
172
|
+
}
|
|
173
|
+
function isIso8601(s) {
|
|
174
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/.test(s);
|
|
175
|
+
}
|
|
176
|
+
export function isValid(snap) {
|
|
177
|
+
return validate(snap).every((i) => i.severity !== 'error');
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ahtmljs/schema",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AHTML semantic snapshot schema — types, validator, builder, and dual-format serializers (JSON + token-optimal compact text).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./schema.json": "./src/schema.json"
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "src/schema.json"],
|
|
16
|
+
"publishConfig": { "access": "public" },
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"dev": "tsc -p tsconfig.json --watch"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["ahtml", "agent", "semantic-web", "ai", "llm", "crawler", "mcp"],
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
}
|
package/src/schema.json
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://ahtml.dev/schema/v0.1/snapshot.json",
|
|
4
|
+
"title": "AHTML Snapshot",
|
|
5
|
+
"description": "Canonical agent-facing representation of a web page.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["ahtml", "url", "fetched_at", "page_type", "entities", "actions"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"ahtml": { "const": "0.1" },
|
|
10
|
+
"url": { "type": "string", "format": "uri" },
|
|
11
|
+
"fetched_at": { "type": "string", "format": "date-time" },
|
|
12
|
+
"ttl": { "type": "integer", "minimum": 0 },
|
|
13
|
+
"etag": { "type": "string" },
|
|
14
|
+
"page_type": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"enum": ["home", "product_detail", "product_list", "article", "document", "profile", "task_list", "task_detail", "dataset", "conversation", "checkout", "search_results", "category", "other"]
|
|
17
|
+
},
|
|
18
|
+
"policy": { "$ref": "#/$defs/Policy" },
|
|
19
|
+
"provenance": { "$ref": "#/$defs/Provenance" },
|
|
20
|
+
"entities": { "type": "array", "items": { "$ref": "#/$defs/Entity" } },
|
|
21
|
+
"actions": { "type": "array", "items": { "$ref": "#/$defs/Action" } },
|
|
22
|
+
"links": { "$ref": "#/$defs/Links" },
|
|
23
|
+
"schemas": { "type": "object", "additionalProperties": true },
|
|
24
|
+
"meta": { "type": "object", "additionalProperties": true }
|
|
25
|
+
},
|
|
26
|
+
"$defs": {
|
|
27
|
+
"Money": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": ["amount", "currency"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"amount": { "type": "number" },
|
|
32
|
+
"currency": { "type": "string", "minLength": 3, "maxLength": 3 }
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"Stock": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["status"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"status": { "enum": ["in_stock", "low_stock", "out_of_stock", "preorder", "discontinued"] },
|
|
40
|
+
"quantity": { "type": "integer", "minimum": 0 }
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"Entity": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": ["id", "type"],
|
|
46
|
+
"properties": {
|
|
47
|
+
"id": { "type": "string", "pattern": "^[a-z_]+:[A-Za-z0-9_\\-.]+$" },
|
|
48
|
+
"type": { "enum": ["product", "document", "task", "profile", "dataset", "conversation"] },
|
|
49
|
+
"freshness": { "enum": ["live", "near_realtime", "daily", "static"] },
|
|
50
|
+
"updated_at": { "type": "string", "format": "date-time" }
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": true,
|
|
53
|
+
"oneOf": [
|
|
54
|
+
{ "$ref": "#/$defs/Product" },
|
|
55
|
+
{ "$ref": "#/$defs/Document" },
|
|
56
|
+
{ "$ref": "#/$defs/Task" },
|
|
57
|
+
{ "$ref": "#/$defs/Profile" },
|
|
58
|
+
{ "$ref": "#/$defs/Dataset" },
|
|
59
|
+
{ "$ref": "#/$defs/Conversation" }
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"Product": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"required": ["id", "type", "name"],
|
|
65
|
+
"properties": {
|
|
66
|
+
"type": { "const": "product" },
|
|
67
|
+
"name": { "type": "string" },
|
|
68
|
+
"brand": { "type": "string" },
|
|
69
|
+
"description": { "type": "string" },
|
|
70
|
+
"price": { "$ref": "#/$defs/Money" },
|
|
71
|
+
"list_price": { "$ref": "#/$defs/Money" },
|
|
72
|
+
"stock": { "$ref": "#/$defs/Stock" },
|
|
73
|
+
"sku": { "type": "string" },
|
|
74
|
+
"rating": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"properties": {
|
|
77
|
+
"average": { "type": "number", "minimum": 0, "maximum": 5 },
|
|
78
|
+
"count": { "type": "integer", "minimum": 0 }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"Document": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": ["id", "type", "title"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"type": { "const": "document" },
|
|
88
|
+
"title": { "type": "string" },
|
|
89
|
+
"author": { "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] },
|
|
90
|
+
"published_at": { "type": "string", "format": "date-time" },
|
|
91
|
+
"summary": { "type": "string" },
|
|
92
|
+
"content": { "type": "string" },
|
|
93
|
+
"word_count": { "type": "integer", "minimum": 0 },
|
|
94
|
+
"language": { "type": "string" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"Task": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"required": ["id", "type", "title", "state"],
|
|
100
|
+
"properties": {
|
|
101
|
+
"type": { "const": "task" },
|
|
102
|
+
"title": { "type": "string" },
|
|
103
|
+
"state": { "enum": ["open", "in_progress", "blocked", "done", "cancelled"] },
|
|
104
|
+
"priority": { "enum": ["low", "medium", "high", "urgent"] },
|
|
105
|
+
"assignee": { "type": "string" },
|
|
106
|
+
"due_at": { "type": "string", "format": "date-time" }
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"Profile": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"required": ["id", "type", "name", "kind"],
|
|
112
|
+
"properties": {
|
|
113
|
+
"type": { "const": "profile" },
|
|
114
|
+
"name": { "type": "string" },
|
|
115
|
+
"kind": { "enum": ["person", "organization", "bot"] }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"Dataset": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": ["id", "type", "name", "columns", "rows"],
|
|
121
|
+
"properties": {
|
|
122
|
+
"type": { "const": "dataset" },
|
|
123
|
+
"name": { "type": "string" },
|
|
124
|
+
"columns": { "type": "array" },
|
|
125
|
+
"rows": { "type": "array" }
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"Conversation": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"required": ["id", "type", "participants", "messages"],
|
|
131
|
+
"properties": {
|
|
132
|
+
"type": { "const": "conversation" },
|
|
133
|
+
"participants": { "type": "array", "items": { "type": "string" } },
|
|
134
|
+
"messages": { "type": "array" }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"Action": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"required": ["id"],
|
|
140
|
+
"properties": {
|
|
141
|
+
"id": { "type": "string" },
|
|
142
|
+
"label": { "type": "string" },
|
|
143
|
+
"target": { "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] },
|
|
144
|
+
"auth": {
|
|
145
|
+
"oneOf": [
|
|
146
|
+
{ "enum": ["none", "optional", "required"] },
|
|
147
|
+
{
|
|
148
|
+
"type": "object",
|
|
149
|
+
"required": ["scheme"],
|
|
150
|
+
"properties": {
|
|
151
|
+
"scheme": { "type": "string" },
|
|
152
|
+
"scopes": { "type": "array", "items": { "type": "string" } }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
"cost": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"required": ["category"],
|
|
160
|
+
"properties": {
|
|
161
|
+
"amount": { "type": "number" },
|
|
162
|
+
"currency": { "type": "string" },
|
|
163
|
+
"category": { "enum": ["free", "purchase", "subscription", "rate_limited", "compute"] }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"reversible": {
|
|
167
|
+
"type": "object",
|
|
168
|
+
"required": ["reversible"],
|
|
169
|
+
"properties": {
|
|
170
|
+
"reversible": { "type": "boolean" },
|
|
171
|
+
"window": { "type": "string" },
|
|
172
|
+
"policy": { "type": "string" }
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
"side_effects": { "type": "array", "items": { "type": "string" } },
|
|
176
|
+
"confirmation": { "enum": ["none", "recommended", "required"] },
|
|
177
|
+
"category": { "enum": ["read", "search", "navigate", "create", "update", "delete", "transact", "send", "auth"] }
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"Policy": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"required": ["agents_welcome"],
|
|
183
|
+
"properties": {
|
|
184
|
+
"agents_welcome": { "type": "boolean" },
|
|
185
|
+
"license": { "type": "string" },
|
|
186
|
+
"rate_limit": { "type": "string" },
|
|
187
|
+
"actions_require": { "type": "string" },
|
|
188
|
+
"contact": { "type": "string" },
|
|
189
|
+
"republish": { "enum": ["allowed", "denied", "attribution_only"] }
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"Provenance": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"properties": {
|
|
195
|
+
"issuer": { "type": "string" },
|
|
196
|
+
"signed": { "type": "boolean" },
|
|
197
|
+
"signature": { "type": "string" },
|
|
198
|
+
"signature_alg": { "type": "string" }
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"Links": {
|
|
202
|
+
"type": "object",
|
|
203
|
+
"properties": {
|
|
204
|
+
"self": { "type": "string" },
|
|
205
|
+
"canonical": { "type": "string" },
|
|
206
|
+
"parent": { "type": "string" },
|
|
207
|
+
"related": { "type": "array", "items": { "type": "string" } },
|
|
208
|
+
"next": { "$ref": "#/$defs/PaginationLink" },
|
|
209
|
+
"prev": { "$ref": "#/$defs/PaginationLink" }
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"PaginationLink": {
|
|
213
|
+
"type": "object",
|
|
214
|
+
"properties": {
|
|
215
|
+
"cursor": { "type": "string" },
|
|
216
|
+
"url": { "type": "string" },
|
|
217
|
+
"expected": { "type": "integer" },
|
|
218
|
+
"total": { "type": "integer" }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|