Skip to content

Commit

Permalink
feat: prefetch frequently used contexts in OA v4
Browse files Browse the repository at this point in the history
  • Loading branch information
HJunyuan committed Jul 26, 2024
1 parent 48905c2 commit 07c4461
Show file tree
Hide file tree
Showing 5 changed files with 416 additions and 10 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"test:vc": "scripts/runVcTest.sh",
"lint": "eslint . --ext .ts,.json --max-warnings 0",
"lint:fix": "npm run lint -- --fix",
"fetch-v4-contexts": "npx ts-node scripts/fetchV4Contexts.ts",
"generate-v4-fixtures": "npx ts-node scripts/generateV4JsonFixtures.ts",
"generate-v4-json-schemas": "npx ts-node scripts/generateV4JsonSchemas.ts",
"publish:schema": "./scripts/publishSchema.sh",
Expand Down
25 changes: 25 additions & 0 deletions scripts/fetchV4Contexts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from "fs";
import path from "path";
import { ContextUrl, urlToSafeFilename } from "../src/4.0/context";

const OUTPUT_DIR = path.resolve("./src/4.0/contexts/__generated__");

// make sure the output directory exists
if (fs.existsSync(OUTPUT_DIR)) {
fs.rmSync(OUTPUT_DIR, { recursive: true });
}
fs.mkdirSync(OUTPUT_DIR, { recursive: true });

const CONTEXTS_TO_FETCH = Object.values(ContextUrl);

for (const url of CONTEXTS_TO_FETCH) {
fetch(url)
.then((res) => res.json())
.then((context) => {
const filename = urlToSafeFilename(url);
fs.writeFileSync(path.join(OUTPUT_DIR, filename), JSON.stringify(context, null, 2));
})
.catch((err) => {
console.error(`Unable to fetch OA v4.0 context`, err);
});
}
36 changes: 26 additions & 10 deletions src/4.0/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expand, Options, JsonLdDocument } from "jsonld";
import { fetch } from "cross-fetch";
import { readFile } from "fs/promises";
import { expand, Options, JsonLdDocument } from "jsonld";

export const ContextUrl = {
w3c_vc_v2: "https://www.w3.org/ns/credentials/v2",
Expand All @@ -11,25 +12,31 @@ export const ContextType = {
OAV4Context: "OpenAttestationCredential",
} as const;

const preloadedContextList = [ContextUrl.w3c_vc_v2, ContextUrl.oa_vc_v4];
const contexts: Map<string, any> = new Map();
const PREFETECHED_CONTEXT_LIST = Object.values(ContextUrl);
const contextCache: Map<string, any> = new Map();

// Preload frequently used contexts
// https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader
let isFirstLoad = true;
// https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader
// FIXME: @types/json-ld seems to be outdated as callback is supposed to be options
const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => {
// On first load: Preload frequently used contexts from "src/4.0/contexts/__generated__/*"
if (isFirstLoad) {
isFirstLoad = false;
for (const url of preloadedContextList) {
const document = await fetch(url).then((res) => res.json());
contexts.set(url, document);
for (const url of PREFETECHED_CONTEXT_LIST) {
try {
const filename = urlToSafeFilename(url);
const document = await readFile(`../4.0/contexts/__generated__/${filename}`, "utf-8");
const parsed = JSON.parse(document);
contextCache.set(url, parsed);
} catch (e) {
console.warn(`Unable to prefetch context from ${url}`, e);
}
}
}
if (contexts.get(url)) {
if (contextCache.get(url)) {
return {
contextUrl: undefined, // this is for a context via a link header
document: contexts.get(url), // this is the actual document that was loaded
document: contextCache.get(url), // this is the actual document that was loaded
documentUrl: url, // this is the actual context URL after redirects
};
} else {
Expand Down Expand Up @@ -57,3 +64,12 @@ export class UnableToInterpretContextError extends Error {
Object.setPrototypeOf(this, UnableToInterpretContextError.prototype);
}
}

/**
* Convert URL to a filename-safe string
* @param url string
* @returns string
*/
export function urlToSafeFilename(url: string) {
return url.replace(/[/\\?%*:|"<>]/g, "-");
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 07c4461

Please sign in to comment.