@sphereon/ssi-sdk.resource-resolver
Version:
348 lines (342 loc) • 11.7 kB
JavaScript
"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