core(jsonld): add structured data validation (#6750)

This commit is contained in:
Patrick Hulce 2019-04-08 16:53:01 -05:00 коммит произвёл GitHub
Родитель fcd8115382
Коммит 3f15b67d23
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 17947 добавлений и 0 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,30 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
/**
* Recursively (DFS) traverses an object and calls provided function for each field.
*
* @param {*} obj
* @param {function(string, any, Array<string>, any): void} callback
* @param {Array<string>} path
*/
module.exports = function walkObject(obj, callback, path = []) {
if (obj === null) {
return;
}
Object.entries(obj).forEach(([fieldName, fieldValue]) => {
const newPath = Array.from(path);
newPath.push(fieldName);
callback(fieldName, fieldValue, newPath, obj);
if (typeof fieldValue === 'object') {
walkObject(fieldValue, callback, newPath);
}
});
};

Просмотреть файл

@ -0,0 +1,56 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
const {URL} = require('../url-shim.js');
const jsonld = require('jsonld');
const schemaOrgContext = require('./assets/jsonldcontext.json');
const SCHEMA_ORG_HOST = 'schema.org';
/**
* Custom loader that prevents network calls and allows us to return local version of the
* schema.org document
* @param {string} schemaUrl
* @param {(err: null|Error, value?: any) => void} callback
*/
function documentLoader(schemaUrl, callback) {
let urlObj = null;
try {
// Give a dummy base URL so relative URLs will be considered valid.
urlObj = new URL(schemaUrl, 'http://example.com');
} catch (e) {
return callback(new Error('Error parsing URL: ' + schemaUrl), undefined);
}
if (urlObj.host === SCHEMA_ORG_HOST && urlObj.pathname === '/') {
callback(null, {
document: schemaOrgContext,
});
} else {
// We only process schema.org, for other schemas we return an empty object
callback(null, {
document: {},
});
}
}
/**
* Takes JSON-LD object and normalizes it by following the expansion algorithm
* (https://json-ld.org/spec/latest/json-ld-api/#expansion).
*
* @param {any} inputObject
* @returns {Promise<LH.StructuredData.ExpandedSchemaRepresentation|null>}
*/
module.exports = async function expand(inputObject) {
try {
return await jsonld.expand(inputObject, {documentLoader});
} catch (err) {
// jsonld wraps real errors in a bunch of junk, so see we have an underlying error first
if (err.details && err.details.cause) throw err.details.cause;
throw err;
}
};

Просмотреть файл

@ -0,0 +1,49 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
const jsonlint = require('jsonlint-mod');
/**
* @param {string} input
* @returns {{message: string, lineNumber: string|null}|null}
*/
module.exports = function parseJSON(input) {
try {
jsonlint.parse(input);
} catch (error) {
/** @type {string|null} */
let line = error.at;
// extract line number from message
if (!line) {
const regexLineResult = error.message.match(/Parse error on line (\d+)/);
if (regexLineResult) {
line = regexLineResult[1];
}
}
// jsonlint error message points to a specific character, but we just want the message.
// Example:
// ---------^
// Unexpected character {
let message = /** @type {string} */ (error.message);
const regexMessageResult = error.message.match(/-+\^\n(.+)$/);
if (regexMessageResult) {
message = regexMessageResult[1];
}
return {
message,
lineNumber: line,
};
}
return null;
};

Просмотреть файл

@ -0,0 +1,50 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
const walkObject = require('./helpers/walk-object.js');
// This list comes from the JSON-LD 1.1 editors draft:
// https://w3c.github.io/json-ld-syntax/#syntax-tokens-and-keywords
const VALID_KEYWORDS = new Set([
'@base',
'@container',
'@context',
'@graph',
'@id',
'@index',
'@language',
'@list',
'@nest',
'@none',
'@prefix',
'@reverse',
'@set',
'@type',
'@value',
'@version',
'@vocab',
]);
/**
* @param {*} json
* @return {Array<{path: string, message: string}>}
*/
module.exports = function validateJsonLD(json) {
/** @type {Array<{path: string, message: string}>} */
const errors = [];
walkObject(json, (name, value, path) => {
if (name.startsWith('@') && !VALID_KEYWORDS.has(name)) {
errors.push({
path: path.join('/'),
message: 'Unknown keyword',
});
}
});
return errors;
};

Просмотреть файл

@ -0,0 +1,135 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
const walkObject = require('./helpers/walk-object.js');
const schemaStructure = require('./assets/schema-tree.json');
const TYPE_KEYWORD = '@type';
const SCHEMA_ORG_URL_REGEX = /https?:\/\/schema\.org\//;
/**
* @param {string} uri
* @returns {string}
*/
function cleanName(uri) {
return uri.replace(SCHEMA_ORG_URL_REGEX, '');
}
/**
* @param {string} type
* @returns {Array<string>}
*/
function getPropsForType(type) {
const cleanType = cleanName(type);
const props = schemaStructure.properties
.filter(prop => prop.parent.includes(cleanType))
.map(prop => prop.name);
const foundType = findType(type);
if (!foundType) throw new Error(`Unable to get props for missing type "${type}"`);
const parentTypes = foundType.parent;
return parentTypes.reduce((allProps, type) => allProps.concat(getPropsForType(type)), props);
}
/**
* @param {string} type
* @returns {{name: string, parent: Array<string>}|undefined}
*/
function findType(type) {
const cleanType = cleanName(type);
return schemaStructure.types.find(typeObj => typeObj.name === cleanType);
}
/**
* Validates keys of given object based on its type(s). Returns an array of error messages.
*
* @param {string|Array<string>} typeOrTypes
* @param {Array<string>} keys
* @returns {Array<string>}
*/
function validateObjectKeys(typeOrTypes, keys) {
/** @type {Array<string>} */
let types = [];
if (typeof typeOrTypes === 'string') {
types = [typeOrTypes];
} else if (Array.isArray(typeOrTypes)) {
types = typeOrTypes;
const invalidIndex = typeOrTypes.findIndex(s => typeof s !== 'string');
if (invalidIndex >= 0) return [`Unknown value type at index ${invalidIndex}`];
} else {
return ['Unknown value type'];
}
const unknownTypes = types.filter(t => !findType(t));
if (unknownTypes.length) {
return unknownTypes
.filter(type => SCHEMA_ORG_URL_REGEX.test(type))
.map(type => `Unrecognized schema.org type ${type}`);
}
/** @type {Set<string>} */
const allKnownProps = new Set();
types.forEach(type => {
const knownProps = getPropsForType(type);
knownProps.forEach(key => allKnownProps.add(key));
});
const cleanKeys = keys
// Skip JSON-LD keywords (including invalid ones as they were already flagged in the json-ld validator)
.filter(key => key.indexOf('@') !== 0)
.map(key => cleanName(key));
return cleanKeys
// remove Schema.org input/output constraints http://schema.org/docs/actions.html#part-4
.map(key => key.replace(/-(input|output)$/, ''))
.filter(key => !allKnownProps.has(key))
.map(key => `Unexpected property "${key}"`);
}
/**
* @param {LH.StructuredData.ExpandedSchemaRepresentation|null} expandedObj Valid JSON-LD object in expanded form
* @return {Array<{path: string, message: string}>}
*/
module.exports = function validateSchemaOrg(expandedObj) {
/** @type {Array<{path: string, message: string}>} */
const errors = [];
if (expandedObj === null) {
return errors;
}
// If the array only has a single item, treat it as if it was at the root to simplify the error path.
// Arrays longer than a single item are handled in `walkObject` below.
if (Array.isArray(expandedObj) && expandedObj.length === 1) {
expandedObj = expandedObj[0];
}
walkObject(expandedObj, (name, value, path, obj) => {
if (name === TYPE_KEYWORD) {
const keyErrorMessages = validateObjectKeys(value, Object.keys(obj));
keyErrorMessages.forEach(message =>
errors.push({
// get rid of the first chunk (/@type) as it's the same for all errors
path:
'/' +
path
.slice(0, -1)
.map(cleanName)
.join('/'),
message,
})
);
}
});
return errors;
};

Просмотреть файл

@ -0,0 +1,30 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
/**
* Call this script to update assets/jsonldcontext.json with the latest schema.org spec
*/
const fetch = require('isomorphic-fetch');
const path = require('path');
const fs = require('fs');
const SCHEMA_ORG_URL = 'https://schema.org';
const CONTEXT_FILE = path.join(__dirname, '../assets/jsonldcontext.json');
async function run() {
try {
const response = await fetch(SCHEMA_ORG_URL, {headers: {Accept: 'application/ld+json'}});
const data = await response.json();
fs.writeFileSync(CONTEXT_FILE, JSON.stringify(data, null, 2));
console.log('Success.'); // eslint-disable-line no-console
} catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
run();

Просмотреть файл

@ -0,0 +1,97 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
/**
* Call this script to update assets/schema-tree.json with the latest schema.org spec
*/
const fetch = require('isomorphic-fetch');
const path = require('path');
const fs = require('fs');
const SCHEMA_ORG_URL = 'https://schema.org/version/latest/schema.jsonld';
const SCHEMA_TREE_FILE = path.join(__dirname, '../assets/schema-tree.json');
/** @typedef {import('jsonlint-mod').SchemaTreeItem} SchemaDefinition */
/** @typedef {import('jsonlint-mod').JSONSchemaSource} SchemaSource */
/** @typedef {{'@id': string}} IDRef */
/**
* @param {SchemaSource} data
*/
function processData(data) {
/** @type {SchemaDefinition[]} */
const types = [];
/** @type {SchemaDefinition[]} */
const properties = [];
/** @param {string} str */
function removePrefix(str) {
return str.replace('http://schema.org/', '');
}
/**
* Accepts some set of id references and returns the array of cleaned references without the `http://schema.org` prefix.
* @param {Array<IDRef>|IDRef|undefined} parents
*/
function cleanIdPrefixes(parents) {
if (Array.isArray(parents)) {
return parents.map(item => removePrefix(item['@id']));
} else if (parents && parents['@id']) {
return [removePrefix(parents['@id'])];
}
return [];
}
// Go through all the graph entries to find the valid types and properties.
// i.e. this converts
// [
// {@id: 'http://schema.org/CafeOrCoffeeShop', @type: 'rdfs:Class', rdfs:subClassOf: {@id: 'http://schema.org/FoodEstablishment'}},
// {@id: 'http://schema.org/bestRating', @type: 'rdfs:Property', http://schema.org/domainIncludes: {@id: 'http://schema.org/Rating'}},
// ]
// into
// {
// types: [{name: 'CafeOrCoffeeShop', parent: ['FoodEstablishment']}],
// properties: [{name: 'bestRating', parent: ['Rating']}],
// }
data['@graph'].forEach(item => {
if (item['rdfs:label'] === undefined) {
return;
}
if (item['@type'] === 'rdf:Property') {
properties.push({
name: item['rdfs:label'],
parent: cleanIdPrefixes(item['http://schema.org/domainIncludes']),
});
} else {
types.push({
name: item['rdfs:label'],
parent: cleanIdPrefixes(item['rdfs:subClassOf']),
});
}
});
return {types, properties};
}
async function run() {
try {
const response = await fetch(SCHEMA_ORG_URL);
const data = await response.json();
const processed = processData(data);
fs.writeFileSync(SCHEMA_TREE_FILE, JSON.stringify(processed, null, 2));
console.log('Success.'); // eslint-disable-line no-console
} catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
run();

Просмотреть файл

@ -0,0 +1,75 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
const parseJSON = require('./json-linter.js');
const validateJsonLD = require('./jsonld-keyword-validator.js');
const expandAsync = require('./json-expander.js');
const validateSchemaOrg = require('./schema-validator.js');
/** @typedef {'json'|'json-ld'|'json-ld-expand'|'schema-org'} ValidatorType */
/**
* Validates JSON-LD input. Returns array of error objects.
*
* @param {string} textInput
* @returns {Promise<Array<{path: ?string, validator: ValidatorType, message: string}>>}
*/
module.exports = async function validate(textInput) {
// STEP 1: VALIDATE JSON
const parseError = parseJSON(textInput);
if (parseError) {
return [{
validator: 'json',
path: parseError.lineNumber,
message: parseError.message,
}];
}
const inputObject = JSON.parse(textInput);
// STEP 2: VALIDATE JSONLD
const jsonLdErrors = validateJsonLD(inputObject);
if (jsonLdErrors.length) {
return jsonLdErrors.map(error => {
return {
validator: /** @type {ValidatorType} */ ('json-ld'),
path: error.path,
message: error.message,
};
});
}
// STEP 3: EXPAND
/** @type {LH.StructuredData.ExpandedSchemaRepresentation|null} */
let expandedObj = null;
try {
expandedObj = await expandAsync(inputObject);
} catch (error) {
return [{
validator: 'json-ld-expand',
path: null,
message: error.message,
}];
}
// STEP 4: VALIDATE SCHEMA
const schemaOrgErrors = validateSchemaOrg(expandedObj);
if (schemaOrgErrors.length) {
return schemaOrgErrors.map(error => {
return {
validator: /** @type {ValidatorType} */ ('schema-org'),
path: error.path,
message: error.message,
};
});
}
return [];
};

Просмотреть файл

@ -0,0 +1,209 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
/* eslint-env jest */
const assert = require('assert');
const validateJSONLD = require('../../lib/sd-validation/sd-validation.js');
describe('JSON validation', () => {
it('reports missing closing bracket', async () => {
const errors = await validateJSONLD(`{
"test": "test"
`);
assert.equal(errors.length, 1);
assert.equal(errors[0].path, 2);
assert.ok(errors[0].message.indexOf(`Expecting '}'`) === 0);
});
it('reports missing comma', async () => {
const errors = await validateJSONLD(`{
"test": "test"
"test2": "test2"
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].path, 2);
assert.ok(errors[0].message.indexOf(`Expecting 'EOF', '}', ':', ',', ']'`) === 0);
});
it('reports duplicated property', async () => {
const errors = await validateJSONLD(`{
"test": "test",
"test2": {
"test2-1": "test",
"test2-1": "test2"
}
}`);
assert.equal(errors.length, 1);
assert.ok(errors[0].message, `Duplicate key 'test2-1'`);
});
it('parses valid json', async () => {
const errors = await validateJSONLD(`{
"test": "test",
"test2": {
"test2-1": "test",
"test2-2": "test2"
},
"test3": null,
"test4": 123,
"test5": [1,2,3]
}`);
assert.equal(errors.length, 0);
});
});
describe('JSON-LD validation', () => {
it('reports unknown keywords', async () => {
const errors = await validateJSONLD(`{
"@type": {},
"@context": {},
"@test": {}
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].message, 'Unknown keyword');
assert.equal(errors[0].path, '@test');
});
it('reports invalid context', async () => {
const errors = await validateJSONLD(`{
"@context": {"x":"x"}
}`);
assert.equal(errors.length, 1);
assert.ok(errors[0].message.indexOf('@context terms must define an @id') !== -1);
});
it('reports invalid keyword value', async () => {
const errors = await validateJSONLD(`{
"@context": "http://schema.org/",
"@type": 23
}`);
assert.equal(errors.length, 1);
assert.ok(errors[0].message.indexOf('"@type" value must a string') !== -1);
});
it('reports invalid id value', async () => {
const errors = await validateJSONLD(`{
"@context": {
"image": {
"@id": "@error"
}
}
}`);
assert.equal(errors.length, 1);
assert.ok(errors[0].message.indexOf('@id value must be an absolute IRI') !== -1);
});
it('reports invalid context URL', async () => {
const errors = await validateJSONLD(`{
"@context": "http://"
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].message, 'Error parsing URL: http://');
});
});
describe('schema.org validation', () => {
it('reports unknown types', async () => {
const errors = await validateJSONLD(`{
"@context": "http://schema.org",
"@type": "Cat"
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].message, 'Unrecognized schema.org type http://schema.org/Cat');
});
it('handles arrays of json schemas', async () => {
const errors = await validateJSONLD(`[
{
"@context": "http://schema.org",
"@type": "Cat"
},
{
"@context": "http://schema.org",
"@type": "Dog"
}
]`);
assert.equal(errors.length, 2);
assert.equal(errors[0].message, 'Unrecognized schema.org type http://schema.org/Cat');
assert.equal(errors[1].message, 'Unrecognized schema.org type http://schema.org/Dog');
});
it('reports unknown types for objects with multiple types', async () => {
const errors = await validateJSONLD(`{
"@context": "http://schema.org",
"@type": ["Article", "Dog"]
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].message, 'Unrecognized schema.org type http://schema.org/Dog');
});
it('reports unexpected fields', async () => {
const errors = await validateJSONLD(`{
"@context": "https://schema.org",
"@type": "Article",
"author": "Cat",
"datePublished": "Oct 29th 2017",
"dateModified": "Oct 29th 2017",
"headline": "Human's New Best Friend - Cat",
"image": "https://cats.rock/cat.bmp",
"publisher": "Cat Magazine",
"mainEntityOfPage": "https://cats.rock/magazine.html",
"controversial": true
}`);
assert.equal(errors.length, 1);
assert.equal(errors[0].message, 'Unexpected property "controversial"');
});
it('passes if non-schema.org context', async () => {
const errors = await validateJSONLD(`{
"@context": "http://www.w3.org/ns/activitystreams",
"@type": "Create",
"actor": {
"@type": "Person",
"@id": "acct:sally@example.org",
"displayName": "Sally"
},
"object": {
"@type": "Note",
"content": "This is a simple note"
},
"published": "2015-01-25T12:34:56Z"
}`);
assert.equal(errors.length, 0);
});
it('passes if everything is OK', async () => {
const errors = await validateJSONLD(`{
"@context": "http://schema.org",
"@type": "Article",
"author": "Cat",
"datePublished": "Oct 29th 2017",
"dateModified": "Oct 29th 2017",
"headline": "Human's New Best Friend - Cat",
"image": "https://cats.rock/cat.bmp",
"publisher": "Cat Magazine",
"mainEntityOfPage": "https://cats.rock/magazine.html"
}`);
assert.equal(errors.length, 0);
});
});

Просмотреть файл

@ -113,6 +113,7 @@
"glob": "^7.1.3",
"idb-keyval": "2.2.0",
"intl": "^1.2.5",
"isomorphic-fetch": "^2.2.1",
"jest": "^24.3.0",
"jsdom": "^12.2.0",
"make-dir": "^1.3.0",
@ -140,6 +141,8 @@
"intl-messageformat-parser": "^1.4.0",
"jpeg-js": "0.1.2",
"js-library-detector": "^5.1.0",
"jsonld": "^1.5.0",
"jsonlint-mod": "^1.7.4",
"lighthouse-logger": "^1.2.0",
"lodash.isequal": "^4.5.0",
"lookup-closest-locale": "6.0.4",

4
types/isomorphic-fetch.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
declare module 'isomorphic-fetch' {
// Just reuse the types from the built-in window.fetch
export = window.fetch
}

15
types/jsonld/index.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
declare module 'jsonld' {
type CallbackFn = (err: null|Error, result?: any) => void
interface JsonldOptions {
documentLoader: (url: string, callback: CallbackFn) => void
}
export function expand(object: any, options: JsonldOptions): Promise<any>;
}

29
types/jsonlint-mod.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
declare module 'jsonlint-mod' {
export function parse(input: string): unknown;
interface SchemaIDReference {
'@id': string;
}
interface SchemaSourceItem {
'@id': string;
'@type': string;
'rdfs:label'?: string;
'rdfs:subClassOf'?: SchemaIDReference | SchemaIDReference[];
'http://schema.org/domainIncludes'?: SchemaIDReference | SchemaIDReference[];
}
interface SchemaTreeItem {
name: string;
parent: string[];
}
export interface JSONSchemaSource {
'@graph': SchemaSourceItem[];
}
export interface JSONSchemaTree {
types: SchemaTreeItem[];
properties: SchemaTreeItem[];
}
}

29
types/structured-data.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
declare global {
module LH {
module StructuredData {
export interface ExpandedSchemaRepresentationItem {
[schemaRef: string]: Array<
string |
{
'@id'?: string;
'@type'?: string;
'@value'?: string;
}
>;
}
export type ExpandedSchemaRepresentation =
| Array<ExpandedSchemaRepresentationItem>
| ExpandedSchemaRepresentationItem;
}
}
}
// empty export to keep file a module
export {};

Просмотреть файл

@ -751,6 +751,11 @@ JSONStream@^1.0.4:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
"JSV@>= 4.0.x":
version "4.0.2"
resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57"
integrity sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=
abab@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
@ -930,6 +935,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ansi-styles@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@ -1849,6 +1859,15 @@ chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=
dependencies:
ansi-styles "~1.0.0"
has-color "~0.1.0"
strip-ansi "~0.1.0"
chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
@ -3938,6 +3957,11 @@ has-ansi@^2.0.0:
dependencies:
ansi-regex "^2.0.0"
has-color@~0.1.0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=
has-flag@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
@ -4684,6 +4708,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-fetch@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isstream@0.1.x, isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@ -5298,6 +5330,24 @@ jsonify@~0.0.0:
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
jsonld@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-1.5.0.tgz#c9f4d1680ab18530f760e5685eead25251451943"
integrity sha512-7jF9WXK4nuHvhz/qT6A4DEZ58tUYgrV98xBJEgHFhQ6GQaNT+oU1zqkFXKtDZsKsiEs/1K/VShNnat6SISb3jg==
dependencies:
rdf-canonize "^1.0.1"
request "^2.88.0"
semver "^5.6.0"
xmldom "0.1.19"
jsonlint-mod@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/jsonlint-mod/-/jsonlint-mod-1.7.4.tgz#310390e1a6a85cef99f45f200e662ef23b48f7a6"
integrity sha512-FYOkwHqiuBbdVCHgXYlmtL+iUOz9AxCgjotzXl+edI0Hc1km1qK6TrBEAyPpO+5R0/IX3XENRp66mfob4jwxow==
dependencies:
JSV ">= 4.0.x"
nomnom ">= 1.5.x"
jsonparse@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
@ -5969,11 +6019,24 @@ node-fetch@1.6.3:
encoding "^0.1.11"
is-stream "^1.0.1"
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-fetch@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==
node-forge@^0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@ -5994,6 +6057,14 @@ node-notifier@^5.2.1:
shellwords "^0.1.1"
which "^1.3.0"
"nomnom@>= 1.5.x":
version "1.8.1"
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
integrity sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=
dependencies:
chalk "~0.4.0"
underscore "~1.6.0"
normalize-package-data@^2.3.0, normalize-package-data@^2.3.5:
version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
@ -6810,6 +6881,14 @@ rc@^1.0.1, rc@^1.1.6:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
rdf-canonize@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-1.0.1.tgz#0a200a12c169e0008dd10e5b2b3855a55db0626c"
integrity sha512-vQq6q7BIUwrVQijKRYdunxlodkn0Btjv2MnJ4S3rOUELsghq7fGuDaWuqBNbXca3KRbcRS6HwTIT2hJbJej2UA==
dependencies:
node-forge "^0.7.6"
semver "^5.6.0"
read-only-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"
@ -7788,6 +7867,11 @@ strip-ansi@^5.0.0:
dependencies:
ansi-regex "^4.0.0"
strip-ansi@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=
strip-bom@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794"
@ -8211,6 +8295,11 @@ undeclared-identifiers@^1.1.2:
simple-concat "^1.0.0"
xtend "^4.0.1"
underscore@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
integrity sha1-izixDKze9jM3uLJOT/htRa6lKag=
union-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@ -8416,6 +8505,11 @@ whatwg-fetch@2.0.1:
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.1.tgz#078b9461bbe91cea73cbce8bb122a05f9e92b772"
integrity sha1-B4uUYbvpHOpzy86LsSKgX56St3I=
whatwg-fetch@>=0.10.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4"
@ -8617,6 +8711,11 @@ xmlchars@^1.3.1:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-1.3.1.tgz#1dda035f833dbb4f86a0c28eaa6ca769214793cf"
integrity sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==
xmldom@0.1.19:
version "0.1.19"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
integrity sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"