UNPKG

@sphereon/ssi-sdk.resource-resolver

Version:

348 lines (342 loc) • 11.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // plugin.schema.json var require_plugin_schema = __commonJS({ "plugin.schema.json"(exports, module2) { module2.exports = { IResourceResolver: { components: { schemas: { ResourceResolverOptions: { type: "object", properties: {} }, ResolveArgs: { type: "object", properties: {} }, ResolveOptions: { type: "object", properties: {} }, ResourceType: { type: "object", properties: {} }, ClearAllResourcesArgs: { type: "object", properties: {} }, PersistResourceArgs: { type: "object", properties: {} }, GetResourceArgs: { type: "object", properties: {} }, StoreIdStrArgs: { type: "object", properties: {} }, NamespaceStrArgs: { type: "object", properties: {} }, PrefixArgs: { type: "object", properties: {} }, StoreArgs: { type: "object", properties: {} }, Resource: { type: "object", properties: {} }, SerializedResponse: { type: "object", properties: {} } }, methods: { resourceResolve: { description: "", arguments: { $ref: "#/components/schemas/ResolveArgs" }, returnType: {} }, resourceClearAllResources: { description: "", arguments: { $ref: "#/components/schemas/ClearAllResourcesArgs" }, returnType: {} }, resourceDefaultStoreId: { description: "", returnType: {} }, resourceDefaultNamespace: { description: "", returnType: {} }, resourceDefaultTtl: { description: "", returnType: {} } } } } }; } }); // src/index.ts var index_exports = {}; __export(index_exports, { ResourceResolver: () => ResourceResolver, schema: () => schema }); module.exports = __toCommonJS(index_exports); // src/agent/ResourceResolver.ts var import_ssi_sdk = require("@sphereon/ssi-sdk.kv-store-temp"); var import_cross_fetch2 = __toESM(require("cross-fetch"), 1); // src/utils/ResourceResolverUtils.ts var import_cross_fetch = require("cross-fetch"); var import_from_string = require("uint8arrays/from-string"); var import_to_string = require("uint8arrays/to-string"); var getResourceIdentifier = /* @__PURE__ */ __name((input) => { if (typeof input === "string") { return input; } else if (input instanceof import_cross_fetch.Request) { return input.url; } else if (input instanceof URL) { return input.toString(); } throw new Error("Invalid input type. Expected Request, string, or URL."); }, "getResourceIdentifier"); var serializeResponse = /* @__PURE__ */ __name(async (response) => { const arrayBuffer = await response.arrayBuffer(); const base64Url = (0, import_to_string.toString)(new Uint8Array(arrayBuffer), "base64url"); return { status: response.status, statusText: response.statusText, // @ts-ignore headers: Object.fromEntries(response.headers.entries()), body: base64Url }; }, "serializeResponse"); var deserializeResponse = /* @__PURE__ */ __name(async (data) => { const { status, statusText, headers, body } = data; const uint8Array = (0, import_from_string.fromString)(body, "base64url"); const arrayBuffer = uint8Array.buffer.slice(uint8Array.byteOffset, uint8Array.byteOffset + uint8Array.byteLength); return new import_cross_fetch.Response(arrayBuffer, { status, statusText, headers: new import_cross_fetch.Headers(headers) }); }, "deserializeResponse"); var isCacheWithinMaxAge = /* @__PURE__ */ __name((cachedResource, resolveOpts) => { return cachedResource && (resolveOpts?.maxAgeMs === void 0 || Date.now() - cachedResource.insertedAt < resolveOpts.maxAgeMs); }, "isCacheWithinMaxAge"); // src/agent/ResourceResolver.ts var ResourceResolver = class { static { __name(this, "ResourceResolver"); } schema = schema.IResourceResolver; methods = { resourceResolve: this.resourceResolve.bind(this), resourceClearAllResources: this.resourceClearAllResources.bind(this), resourceDefaultStoreId: this.resourceDefaultStoreId.bind(this), resourceDefaultNamespace: this.resourceDefaultNamespace.bind(this), resourceDefaultTtl: this.resourceDefaultTtl.bind(this) }; defaultStoreId; defaultNamespace; defaultTtl; detectLocation; _resourceStores; constructor(options) { const { defaultStore, defaultNamespace, resourceStores, ttl, detectLocation } = options ?? {}; this.defaultStoreId = defaultStore ?? "_default"; this.defaultNamespace = defaultNamespace ?? "resources"; this.defaultTtl = ttl ?? 3600; this.detectLocation = detectLocation ?? false; if (resourceStores && resourceStores instanceof Map) { this._resourceStores = resourceStores; } else if (resourceStores) { this._resourceStores = (/* @__PURE__ */ new Map()).set(this.defaultStoreId, resourceStores); } else { this._resourceStores = (/* @__PURE__ */ new Map()).set(this.defaultStoreId, new import_ssi_sdk.KeyValueStore({ namespace: this.defaultNamespace, store: /* @__PURE__ */ new Map(), ttl: this.defaultTtl })); } } /** {@inheritDoc IResourceResolver.resourceResolve} */ async resourceResolve(args, context) { const { input, init, resourceType, resolveOpts, partyCorrelationId, storeId, namespace } = args; const resourceIdentifier = getResourceIdentifier(input); const cachedResource = await this.getResource({ resourceIdentifier, storeId, namespace }); if (cachedResource.value && isCacheWithinMaxAge(cachedResource.value, resolveOpts)) { return deserializeResponse(cachedResource.value.response); } if (resolveOpts?.onlyCache) { return new import_cross_fetch2.Response(JSON.stringify({ error: "Resource not found" }), { status: 404, statusText: "Not Found", headers: new import_cross_fetch2.Headers({ "Content-Type": "application/json" }) }); } let location; if (this.detectLocation) { location = await this.retrieveLocation(input, context); } const response = await (0, import_cross_fetch2.default)(input, init); if (!resolveOpts?.skipPersistence && response.status >= 200 && response.status < 300) { const serializedResponse = await serializeResponse(response); const resource = { location, response: serializedResponse, resourceType, insertedAt: Date.now(), partyCorrelationId }; const cachedResource2 = await this.persistResource({ resource, resourceIdentifier, namespace, storeId }); if (!cachedResource2.value) { return Promise.reject(Error("Resource not present in persistence result")); } return deserializeResponse(cachedResource2.value.response); } return response; } async retrieveLocation(input, context) { let url; if (input instanceof Request && input.url !== void 0 && input.url !== null) { url = new URL(input.url); } else if (input instanceof URL) { url = input; } else { throw Error(`input type is required to be RequestInfo | URL`); } return await context.agent.anomalyDetectionLookupLocation({ ipOrHostname: url.hostname }); } /** {@inheritDoc IResourceResolver.resourceClearAllResources} */ async resourceClearAllResources(args, context) { const { storeId } = args; return await this.store({ stores: this._resourceStores, storeId }).clear().then(() => true); } /** {@inheritDoc IResourceResolver.resourceDefaultStoreId} */ async resourceDefaultStoreId(context) { return this.defaultStoreId; } /** {@inheritDoc IResourceResolver.resourceDefaultNamespace} */ async resourceDefaultNamespace(context) { return this.defaultNamespace; } /** {@inheritDoc IResourceResolver.resourceDefaultTtl} */ async resourceDefaultTtl(context) { return this.defaultTtl; } async getResource(args) { const { resourceIdentifier, storeId, namespace } = args; return this.store({ stores: this._resourceStores, storeId }).getAsValueData(this.prefix({ namespace, resourceIdentifier })); } async persistResource(args) { const { resource, resourceIdentifier, ttl } = args; const namespace = this.namespaceStr(args); const storeId = this.storeIdStr(args); return await this.store({ stores: this._resourceStores, storeId }).set(this.prefix({ namespace, resourceIdentifier }), resource, ttl ?? this.defaultTtl); } store(args) { const storeId = this.storeIdStr({ storeId: args.storeId }); const store = args.stores.get(storeId); if (!store) { throw Error(`Could not get resource store: ${storeId}`); } return store; } storeIdStr(args) { const { storeId } = args; return storeId ?? this.defaultStoreId; } namespaceStr(args) { const { namespace } = args; return namespace ?? this.defaultNamespace; } prefix(args) { const { namespace, resourceIdentifier } = args; return `${this.namespaceStr({ namespace })}:${resourceIdentifier}`; } }; // src/index.ts var schema = require_plugin_schema(); //# sourceMappingURL=index.cjs.map