diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..5d47c21c4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eleventy.js b/.eleventy.js new file mode 100644 index 000000000..2ce0822f7 --- /dev/null +++ b/.eleventy.js @@ -0,0 +1,94 @@ +// development host for playground proxy +const PLAYGROUND_PROXY_HOST = 'http://localhost:8788'; + +const drafts = [ + 'CG-FINAL', + 'CR', + 'ED', + 'FCGS', + 'PR', + 'REC', + 'WD', + 'latest' +]; + +export default async function(eleventyConfig) { + eleventyConfig.addPassthroughCopy('404.html'); + eleventyConfig.addPassthroughCopy('.htaccess'); + eleventyConfig.addPassthroughCopy('LICENSE.md'); + eleventyConfig.addPassthroughCopy('_headers'); + eleventyConfig.addPassthroughCopy('_redirects'); + eleventyConfig.addPassthroughCopy('benchmarks/**/*.{jsonld,nq,md}'); + eleventyConfig.addPassthroughCopy('contexts/**/*.{htaccess,html,jsonld}'); + eleventyConfig.addPassthroughCopy('contexts/{event,person,place,recipe,remote-context}'); + eleventyConfig.addPassthroughCopy('examples/**/*.{html,ttl,txt,json}'); + eleventyConfig.addPassthroughCopy('favicon.ico'); + eleventyConfig.addPassthroughCopy('fonts'); + eleventyConfig.addPassthroughCopy('functions/**/*.js'); + eleventyConfig.addPassthroughCopy('images/**/*.{htaccess,png,svg,xcf}'); + eleventyConfig.addPassthroughCopy('ns/**/*.{html,jsonld}'); + eleventyConfig.addPassthroughCopy('playground/**/*.{css,php,js}'); + eleventyConfig.addPassthroughCopy('presentations'); + eleventyConfig.addPassthroughCopy('schemas/**/*.json'); + eleventyConfig.addPassthroughCopy('site.css'); + eleventyConfig.addPassthroughCopy('spec/LICENSE.md'); + for(const draft of drafts) { + eleventyConfig.addPassthroughCopy(`spec/${draft}`); + } + eleventyConfig.addPassthroughCopy('static'); + eleventyConfig.addPassthroughCopy('test-suite'); + eleventyConfig.ignores.add('CONTRIBUTING.md'); + eleventyConfig.ignores.add('LICENSE.md'); + eleventyConfig.ignores.add('README.md'); + eleventyConfig.ignores.add('benchmarks/README.md'); + eleventyConfig.ignores.add('contexts/person.html'); + eleventyConfig.ignores.add('examples'); + eleventyConfig.ignores.add('images/Makefile'); + eleventyConfig.ignores.add('images/README.md'); + eleventyConfig.ignores.add('minutes/**/*'); + eleventyConfig.ignores.add('ns/json-ld.html'); + eleventyConfig.ignores.add('playground/dev/README.md'); + eleventyConfig.ignores.add('presentations'); + eleventyConfig.ignores.add('scripts'); + eleventyConfig.ignores.add('spec/tools'); + eleventyConfig.ignores.add('spec/LICENSE.md'); + for(const draft of drafts) { + eleventyConfig.ignores.add(`spec/${draft}`); + } + eleventyConfig.ignores.add('test-suite'); + + // setup development proxy to cloudflare pages function server + if(process.env.ELEVENTY_RUN_MODE === 'serve') { + eleventyConfig.setServerOptions({ + onRequest: { + '/playground/proxy': playgroundProxy + } + }); + } +}; + +// proxy to worker proxy +async function playgroundProxy({url}) { + const targetUrl = url.searchParams.get('url'); + // eleventy only provides the URL + // approximate what the live playground does + const search = new URLSearchParams(); + search.set('url', targetUrl); + const proxyUrl = + new URL(`${PLAYGROUND_PROXY_HOST}/playground/proxy?${search}`); + const res = await fetch(proxyUrl, { + headers: { + 'Accept': 'application/ld+json, application/json' + } + }); + // create headers object and filter properties + // suffient for the site development purposes + const headers = Object.fromEntries( + Array.from(res.headers.entries()).filter( + v => !['content-length', 'content-encoding'].includes(v[0]))); + return { + status: res.status, + headers, + body: await res.text() + } +} diff --git a/.gitignore b/.gitignore index edfb95873..0d5760310 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ *~ +*.backup *.sw[op] .DS_Store -node_modules +.wrangler +node_modules/ playground/jsonld.js +_site/ +package-lock.json diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 84c18f9ff..000000000 --- a/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ - - ForceType application/ld+json - SetHandler default_handler - Header set Access-Control-Allow-Origin "*" - - -Redirect 302 /playground-dev /playground diff --git a/404.html b/404.html new file mode 100644 index 000000000..49526412b --- /dev/null +++ b/404.html @@ -0,0 +1,100 @@ + + + + JSON-LD - JSON for Linking Data + + + + + + + + + + + + + + + + + + + + + + +
+

Page not found.

+
+ + + + + diff --git a/README.md b/README.md new file mode 100644 index 000000000..cd9a3bcc7 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +json-ld.org +=========== + +Introduction +------------ + +[![Join the chat at https://gitter.im/json-ld/json-ld.org](https://badges.gitter.im/json-ld/json-ld.org.svg)](https://gitter.im/json-ld/json-ld.org) + +This is the source for the https://json-ld.org/ website. + +JSON-LD (JavaScript Object Notation for Linking Data) is a lightweight Linked +Data format. It is easy for humans to read and write. It is easy for machines +to parse and generate. It is based on the already successful JSON format and +provides a way to help JSON data interoperate at Web-scale. If you are already +familiar with JSON, writing JSON-LD is very easy. There is a smooth migration +path from the JSON you use today, to the JSON-LD you will use in the future. +These properties make JSON-LD an ideal Linked Data interchange language for +JavaScript environments, Web services, and unstructured databases such as +CouchDB and MongoDB. + +If you are already using JSON-LD, add yourself to the [list of users][] in our wiki. + + +A Simple Example +---------------- + +A simple example of a JSON object with added semantics:: + +```json +{ + "@context": "https://json-ld.org/contexts/person.jsonld", + "@id": "http://dbpedia.org/resource/John_Lennon", + "name": "John Lennon", + "born": "1940-10-09", + "spouse": "http://dbpedia.org/resource/Cynthia_Lennon" +} +``` + +The example above describes a person whose name is John Lennon. The difference +between regular JSON and JSON-LD is that the JSON-LD object above uniquely +identifies itself on the Web and can be used, without introducing ambiguity, +across every Web site, Web services and databases in operation today. + +The Playground +-------------- + +If you would like to play around with JSON-LD markup, you may do so here: + +https://json-ld.org/playground/ + +The Specifications +------------------ + +If you are a developer, you may be interested in the official JSON-LD W3C +specifications: + +* [JSON-LD 1.1 - A JSON-based Serialization for Linked Data][] +* [JSON-LD 1.1 Processing Algorithms and API][] +* [JSON-LD 1.1 Framing][] + +A list of all previous specification drafts is also available. + +https://json-ld.org/spec/ + +Website Development +------------------- + +- This site is published using [Eleventy][]. +- The site is deployed using [Cloudflare Pages][]. +- The [playground][] has a special proxy to handle `http:` URLs. + +To develop this website locally: + +```sh +# install dependencies +npm i +# to just build the static files to `_site/` +npm run build +# to rebuild the files on changes +npm run watch +# to serve `_site/` with Cloudflare Pages feature support +npm run pages # visit http://localhost:8788/ +``` + +Additionally, if you want to use or test the playground `http:` proxy, also run +the [Wrangler][] server to emulate the [Cloudflare Pages Functions][] code: + +```sh +npm run dev +``` + +[Cloudflare Pages Functions]: https://developers.cloudflare.com/pages/functions/ +[Cloudflare Pages]: https://pages.cloudflare.com/ +[Eleventy]: https://www.11ty.dev/ +[JSON-LD 1.1 - A JSON-based Serialization for Linked Data]: http://www.w3.org/TR/json-ld/ +[JSON-LD 1.1 Framing]: https://www.w3.org/TR/json-ld-framing/ +[JSON-LD 1.1 Processing Algorithms and API]: https://www.w3.org/TR/json-ld-api/ +[Wrangler]: https://developers.cloudflare.com/workers/wrangler/ +[list of users]: https://github.com/json-ld/json-ld.org/wiki/Users-of-JSON-LD +[playground]: https://json-ld.org/playground/ diff --git a/README.rst b/README.rst deleted file mode 100644 index c606ad181..000000000 --- a/README.rst +++ /dev/null @@ -1,63 +0,0 @@ -Introduction ------------- - -.. image:: https://badges.gitter.im/json-ld/json-ld.org.svg - :alt: Join the chat at https://gitter.im/json-ld/json-ld.org - :target: https://gitter.im/json-ld/json-ld.org?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge - -JSON-LD (JavaScript Object Notation for Linking Data) is a lightweight Linked -Data format. It is easy for humans to read and write. It is easy for machines -to parse and generate. It is based on the already successful JSON format and -provides a way to help JSON data interoperate at Web-scale. If you are already -familiar with JSON, writing JSON-LD is very easy. There is a smooth migration -path from the JSON you use today, to the JSON-LD you will use in the future. -These properties make JSON-LD an ideal Linked Data interchange language for -JavaScript environments, Web services, and unstructured databases such as -CouchDB and MongoDB. - -If you are already using JSON-LD, add yourself to the `list of users`_ in our wiki. - - -A Simple Example ----------------- - -A simple example of a JSON object with added semantics:: - - { - "@context": "https://json-ld.org/contexts/person.jsonld", - "@id": "http://dbpedia.org/resource/John_Lennon", - "name": "John Lennon", - "born": "1940-10-09", - "spouse": "http://dbpedia.org/resource/Cynthia_Lennon" - } - -The example above describes a person whose name is John Lennon. The difference -between regular JSON and JSON-LD is that the JSON-LD object above uniquely -identifies itself on the Web and can be used, without introducing ambiguity, -across every Web site, Web services and databases in operation today. - -The Playground --------------- - -If you would like to play around with JSON-LD markup, you may do so here: - -https://json-ld.org/playground/ - -The Specifications ------------------- - -If you are a developer, you may be interested in the official JSON-LD W3C -specifications: - -* `JSON-LD 1.1 - A JSON-based Serialization for Linked Data`_ -* `JSON-LD 1.1 Processing Algorithms and API`_ -* `JSON-LD 1.1 Framing`_ - -A list of all previous specification drafts is also available. - -https://json-ld.org/spec/ - -.. _list of users: https://github.com/json-ld/json-ld.org/wiki/Users-of-JSON-LD -.. _JSON-LD 1.1 - A JSON-based Serialization for Linked Data: http://www.w3.org/TR/json-ld/ -.. _JSON-LD 1.1 Processing Algorithms and API: http://www.w3.org/TR/json-ld-api/ -.. _JSON-LD 1.1 Framing: http://www.w3.org/TR/json-ld-framing/ diff --git a/_headers b/_headers new file mode 100644 index 000000000..8cede675b --- /dev/null +++ b/_headers @@ -0,0 +1,30 @@ +/contexts/event + Content-Type: application/ld+json + Access-Control-Allow-Origin: "*" +/contexts/person + Content-Type: application/ld+json + Access-Control-Allow-Origin: "*" +/contexts/place + Content-Type: application/ld+json + Access-Control-Allow-Origin: "*" +/contexts/recipe + Content-Type: application/ld+json + Access-Control-Allow-Origin: "*" + +# Tests 0009-0011 Add link header +/test-suite/tests/remote-doc-0009-in.jsonld + Link: ; rel="http://www.w3.org/ns/json-ld#context" +/test-suite/tests/remote-doc-0010-in.json + Link: ; rel="http://www.w3.org/ns/json-ld#context" +/test-suite/tests/remote-doc-0011-in.jldt + Link: ; rel="http://www.w3.org/ns/json-ld#context" + +# Test 00012 adds multiple link headers +/test-suite/tests/remote-doc-0012-in.json + Link: ; rel="http://www.w3.org/ns/json-ld#context" + Link: ; rel="http://www.w3.org/ns/json-ld#context" + +/test-suite/tests/*.jldt + Content-Type: application/jldTest+json +/test-suite/tests/*.jldte + Content-Type: application/jldTest diff --git a/_redirects b/_redirects new file mode 100644 index 000000000..719487ebe --- /dev/null +++ b/_redirects @@ -0,0 +1,20 @@ +/playground-dev /playground 302 + +/contexts/schema.org.jsonld https://schema.org/ 301 + +/spec/latest/json-ld-syntax https://www.w3.org/TR/json-ld/ 301 +/spec/latest/json-ld-syntax/ https://www.w3.org/TR/json-ld/ 301 + +# Tests 0005-0007, status redirect to 0001 +/test-suite/remote-doc-0005-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 301 +/test-suite/remote-doc-0006-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 303 +/test-suite/remote-doc-0007-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 307 + +/spec/latest/rdf-graph-normalization https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/ 307 +/spec/latest/rdf-graph-normalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307 +/spec/latest/rdf-dataset-normalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307 +/spec/latest/rdf-dataset-canonicalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307 + +/spec/latest/json-ld/* https://www.w3.org/TR/json-ld/:splat 301 +/spec/latest/json-ld-api/* https://www.w3.org/TR/json-ld-api/:splat 301 +/spec/latest/json-ld-framing/* https://www.w3.org/TR/json-ld-framing/:splat 301 diff --git a/contexts/.htaccess b/contexts/.htaccess deleted file mode 100644 index a8f948ec7..000000000 --- a/contexts/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ - - ForceType application/ld+json - SetHandler default_handler - Header set Access-Control-Allow-Origin "*" - - -Redirect 301 /contexts/schema.org.jsonld http://schema.org/ diff --git a/earl.jsonld b/earl.jsonld deleted file mode 100644 index e69de29bb..000000000 diff --git a/functions/playground/proxy.js b/functions/playground/proxy.js new file mode 100644 index 000000000..9fe3dc2af --- /dev/null +++ b/functions/playground/proxy.js @@ -0,0 +1,68 @@ +// json-ld.org playground proxy +// +// details: +// - Basic proxy for use with the json-ld.org playground. +// - Built for deployment using Cloudflare Pages Functions API +// - https://developers.cloudflare.com/pages/functions/ +// - Only handle GET requests. +// - Only designed to be used by the playground, no CORS support. +// - Only handle requests for 'http:' URLs. +// - Short timeout enough for expected development use cases. +// - Only support JSON-LD and JSON content types for target response. +// - Not intended to be very robust (but improvements welcome). +// +// usage: +// - GET /playground/proxy?url={encoded-url} +// - Proxy errors have JSON content and a +// 'X-JSON-LD-Playground-Proxy-Status' header. + +const RESPONSE_HEADER = 'X-JSON-LD-Playground-Proxy-Status'; + +function makeError({error}) { + return Response.json({error}, { + status: 400, + headers: { + [RESPONSE_HEADER]: '400' + } + }); +} + +export async function onRequestGet(context) { + const request = context.request; + try { + const requestUrl = new URL(request.url); + const targetUrl = new URL(requestUrl.searchParams.get('url')); + // check self request + if(targetUrl.host === requestUrl.host) { + return makeError({error: 'self request'}); + } + // check url protocol + if(targetUrl.protocol !== 'http:') { + return makeError({error: 'unsupported URL protocol'}); + } + // make similar request with new target url + const req = new Request(targetUrl, request); + const res = await fetch(req, { + redirect: 'follow', + // fail for long requests + signal: AbortSignal.timeout(3000) + }); + // check return type is JSON-LD or JSON + const ct = res.headers.get('content-type'); + if(!(ct === 'application/ld+json' || ct === 'application/json')) { + return makeError({error: 'unsupported response content type'}); + } + // check if remote seems to be the proxy itself + if(res.headers.has(RESPONSE_HEADER)) { + return makeError({error: 'playground proxy response found'}); + } + return res; + } catch(e) { + // special case timeout error + if(e.name === 'TimeoutError') { + return makeError({error: 'timeout'}); + } + // fallback error + return makeError({error: 'bad request'}); + } +} diff --git a/functions/test-suite/_middleware.js b/functions/test-suite/_middleware.js new file mode 100644 index 000000000..bc2db3b83 --- /dev/null +++ b/functions/test-suite/_middleware.js @@ -0,0 +1,57 @@ +function parseAcceptHeader(acceptHeader) { + // generated by Google AI "parse accept header javascript" + if (!acceptHeader) { + return []; + } + + return acceptHeader.split(',') + .map(item => item.trim()) + .map(item => { + const parts = item.split(';'); + const value = parts[0].trim(); + let quality = 1; + + if (parts.length > 1) { + const q = parts[1].trim(); + if (q.startsWith('q=')) { + quality = parseFloat(q.substring(2)); + } + } + + return { value, quality }; + }) + .sort((a, b) => b.quality - a.quality); + // From: + // text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8 + // Expected output: + // [ + // { value: 'text/html', quality: 1 }, + // { value: 'application/xhtml+xml', quality: 1 }, + // { value: 'application/xml', quality: 0.9 }, + // { value: '*/*', quality: 0.8 } + // ] +} + +const typeToExt = { + "text/turtle": "ttl", + "application/ld+json": "jsonld" +}; + +export async function onRequest(context) { + try { + const parsedAccept = parseAcceptHeader(context.request.headers.get('Accept')); + const accept = parsedAccept[0].value; + + // if we have a mapping for this extension, send it. Otherwise fall through. + if(Object.keys(typeToExt).indexOf(accept) > -1) { + const ext = typeToExt[accept] || `html`; + const rewrittenUrl = context.request.url + '.' + ext; + const asset = await context.env.ASSETS.fetch(rewrittenUrl); + const response = new Response(asset.body, asset); + return response; + } + return context.next(); + } catch (err) { + return new Response(`${err.message}\n${err.stack}`, { status: 500 }); + } +} diff --git a/images/.htaccess b/images/.htaccess deleted file mode 100644 index ee8e4d25e..000000000 --- a/images/.htaccess +++ /dev/null @@ -1,10 +0,0 @@ - - order allow,deny - deny from all - - - - order allow,deny - deny from all - - diff --git a/images/index.html b/images/index.html index 4ad5c9d9b..00b88149e 100644 --- a/images/index.html +++ b/images/index.html @@ -19,7 +19,7 @@ - + @@ -52,7 +52,7 @@ Playground
  • - Documentation + Documentation
  • Developers
  • - Documentation + Documentation
  • Developers
  • -
  • Branding
  • +
  • Branding
  • @@ -145,6 +145,6 @@

    Blog Posts

    - + diff --git a/package.json b/package.json new file mode 100644 index 000000000..a1e31927a --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "json-ld.org", + "private": true, + "description": "json-ld.org homepage", + "type": "module", + "scripts": { + "build": "eleventy", + "pages": "wrangler pages dev _site/ --compatibility-date=2025-04-02", + "watch": "eleventy --watch", + "dev": "wrangler pages dev _site --compatibility-date=2024-04-27 --live-reload --port 8788", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "@11ty/eleventy": "^3.0.0", + "wrangler": "^4.6.0" + } +} diff --git a/playground/1.0/index.html b/playground/1.0/index.html index 9e9f219de..be75e45d3 100644 --- a/playground/1.0/index.html +++ b/playground/1.0/index.html @@ -44,7 +44,7 @@ Playground
  • - Documentation + Documentation
  • Developers
  • - Documentation + Documentation
  • Developers
  • - Documentation + Documentation
  • Developers
  • - Documentation + Documentation
  • Developers
  • - Documentation + Documentation
  • Developers
  • - + Documentation diff --git a/test-suite/vocab_template.haml b/test-suite/vocab_template.haml index bf5c534d3..9aeb27b43 100644 --- a/test-suite/vocab_template.haml +++ b/test-suite/vocab_template.haml @@ -33,7 +33,7 @@ %span.icon-beer Playground %li - %a{:href => "../learn.html"} + %a{:href => "../learn/"} %span.icon-book Documentation %li diff --git a/utils/.htaccess b/utils/.htaccess deleted file mode 100644 index d5d0d53bd..000000000 --- a/utils/.htaccess +++ /dev/null @@ -1,5 +0,0 @@ - - order allow,deny - deny from all - - diff --git a/utils/README b/utils/README deleted file mode 100644 index 8bb70691c..000000000 --- a/utils/README +++ /dev/null @@ -1,13 +0,0 @@ -Usage ------ - -In order to use the git.php file, you must create a file called -remote-update-token.txt and place a value in there. You must then call -the git.php file with a URL parameter named 'token' set to the value in -the file. For example: - -https://json-ld.org/utils/git.php?token=7384724849 - -While this is not a fool-proof security solution, it'll be good enough -for now. Updates are throttled, even in the event of a DDoS, the update -rate is once every 5 seconds. diff --git a/utils/git.php b/utils/git.php deleted file mode 100644 index 97296748b..000000000 --- a/utils/git.php +++ /dev/null @@ -1,74 +0,0 @@ -