@getvouch/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Vouch SDK
2
+
3
+ This is the Vouch SDK, a TypeScript library for interacting with vouch. Currently it allows you to generate a link to start the web proof flow.
4
+
5
+ ## Installation
6
+
7
+ To install the Vouch SDK, you can use npm or other package managers.
8
+
9
+ ```bash
10
+ npm install @vouch/vouch-sdk
11
+ yarn add @vouch/vouch-sdk
12
+ pnpm add @vouch/vouch-sdk
13
+ bun add @vouch/vouch-sdk
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ Create vouch instance:
19
+
20
+ ```typescript
21
+ import { Vouch } from "@vouch/vouch-sdk";
22
+ const vouch = new Vouch();
23
+ ```
24
+
25
+ You may provide a configuration object with a set of optional parameters:
26
+
27
+ ```typescript
28
+ import { Vouch } from "@vouch/vouch-sdk";
29
+ const vouch = new Vouch({
30
+ vouchHost: "https://app.getvouch.io", // The base URL for the Vouch API. Can be a string or an URL object. Defaults to "https://app.getvouch.io".
31
+ });
32
+ ```
33
+
34
+ ### Get vouch start URL
35
+
36
+ [Generate a link to start the Vouch flow](https://docs.getvouch.io/getting-started/first-steps#redirect-user-to-vouch).
37
+
38
+ ```typescript
39
+ const url = vouch.getStartUrl({
40
+ requestId, // Optional. If not provided, a new request ID will be generated.
41
+ datasourceId: "93826be6-6c7d-495a-9859-de5a1d17f30b", // Datasource ID. Here we use example.com data source. Must be a UUIDv4.
42
+ customerId: "1be03be8-5014-413c-835a-feddf4020da2", // Your unique customer ID.
43
+ redirectBackUrl: `https://docs.getvouch.io/getting-started/first-steps?requestId=${requestId}`, // Return destination.
44
+ webhookUrl: `https://docs.getvouch.io/api/web-proof/${requestId}`, // (Optional) Proof delivery endpoint.
45
+ });
46
+ ```
@@ -0,0 +1 @@
1
+ export * from "./lib/vouch";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./lib/vouch";
@@ -0,0 +1,16 @@
1
+ export interface Config {
2
+ vouchHost?: string | URL;
3
+ }
4
+ export interface VouchUrlParams {
5
+ datasourceId: string;
6
+ redirectBackUrl: string;
7
+ customerId?: string;
8
+ requestId?: string;
9
+ inputs?: string;
10
+ webhookUrl?: string;
11
+ }
12
+ export declare class Vouch {
13
+ host: URL;
14
+ constructor({ vouchHost }?: Config);
15
+ getStartUrl({ customerId, datasourceId, requestId, redirectBackUrl, inputs, webhookUrl, }: VouchUrlParams): URL;
16
+ }
@@ -0,0 +1,28 @@
1
+ import { validatedHost } from "@/utils/validated-host";
2
+ import { validatedUuidV4 } from "@/utils/validated-uuid";
3
+ const DEFAULT_VOUCH_HOST = "https://app.getvouch.io";
4
+ const DEFAULT_CUSTOMER_ID = "1be03be8-5014-413c-835a-feddf4020da2";
5
+ export class Vouch {
6
+ constructor({ vouchHost = DEFAULT_VOUCH_HOST } = {}) {
7
+ this.host = validatedHost(vouchHost);
8
+ }
9
+ getStartUrl({ customerId, datasourceId, requestId, redirectBackUrl, inputs, webhookUrl, }) {
10
+ const params = new URLSearchParams({
11
+ requestId: requestId
12
+ ? validatedUuidV4(requestId, "requestId")
13
+ : crypto.randomUUID(),
14
+ customerId: customerId
15
+ ? validatedUuidV4(customerId, "customerId")
16
+ : DEFAULT_CUSTOMER_ID,
17
+ datasourceId: validatedUuidV4(datasourceId, "datasourceId"),
18
+ redirectBackUrl,
19
+ });
20
+ if (inputs) {
21
+ params.set("inputs", inputs);
22
+ }
23
+ if (webhookUrl) {
24
+ params.set("webhookUrl", webhookUrl);
25
+ }
26
+ return new URL(`/start?${params.toString()}`, this.host);
27
+ }
28
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { Vouch } from "./vouch";
2
+ import { beforeEach, expect } from "vitest";
3
+ import { validatedUuidV4 } from "@/utils/validated-uuid";
4
+ describe("Vouch SDK", () => {
5
+ it("correctly sets default vouch host", () => {
6
+ const vouch = new Vouch();
7
+ expect(vouch.host).toStrictEqual(new URL("https://app.getvouch.io"));
8
+ });
9
+ describe("getVouchUrl", () => {
10
+ let vouch;
11
+ beforeEach(() => {
12
+ vouch = new Vouch();
13
+ });
14
+ it("constructs correct URL", () => {
15
+ const params = {
16
+ requestId: crypto.randomUUID(),
17
+ datasourceId: crypto.randomUUID(),
18
+ customerId: crypto.randomUUID(),
19
+ redirectBackUrl: "https://example.com/redirect",
20
+ };
21
+ const url = vouch.getStartUrl(params);
22
+ expect(url.toString()).toBe(`https://app.getvouch.io/start?requestId=${params.requestId}&customerId=${params.customerId}&datasourceId=${params.datasourceId}&redirectBackUrl=${encodeURIComponent(params.redirectBackUrl)}`);
23
+ });
24
+ it("adds inputs", () => {
25
+ const params = {
26
+ requestId: crypto.randomUUID(),
27
+ datasourceId: crypto.randomUUID(),
28
+ customerId: crypto.randomUUID(),
29
+ redirectBackUrl: "https://example.com/redirect",
30
+ inputs: "eyJrZXkiOiAidmFsdWUifQ",
31
+ };
32
+ const url = vouch.getStartUrl(params);
33
+ expect(url.toString()).toBe(`https://app.getvouch.io/start?requestId=${params.requestId}&customerId=${params.customerId}&datasourceId=${params.datasourceId}&redirectBackUrl=${encodeURIComponent(params.redirectBackUrl)}&inputs=${params.inputs}`);
34
+ });
35
+ it("adds webhookUrl", () => {
36
+ const params = {
37
+ requestId: crypto.randomUUID(),
38
+ datasourceId: crypto.randomUUID(),
39
+ customerId: crypto.randomUUID(),
40
+ redirectBackUrl: "https://example.com/redirect",
41
+ webhookUrl: "https://example.com/webhook",
42
+ };
43
+ const url = vouch.getStartUrl(params);
44
+ expect(url.toString()).toBe(`https://app.getvouch.io/start?requestId=${params.requestId}&customerId=${params.customerId}&datasourceId=${params.datasourceId}&redirectBackUrl=${encodeURIComponent(params.redirectBackUrl)}&webhookUrl=${encodeURIComponent(params.webhookUrl)}`);
45
+ });
46
+ it("generates requestId", () => {
47
+ var _a;
48
+ const params = {
49
+ datasourceId: crypto.randomUUID(),
50
+ customerId: crypto.randomUUID(),
51
+ redirectBackUrl: "https://example.com/redirect",
52
+ };
53
+ const url = vouch.getStartUrl(params);
54
+ const generatedRequestId = (_a = url.searchParams.get("requestId")) !== null && _a !== void 0 ? _a : "";
55
+ expect(() => validatedUuidV4(generatedRequestId)).not.toThrow();
56
+ });
57
+ it("sets default customerId", () => {
58
+ const params = {
59
+ requestId: crypto.randomUUID(),
60
+ datasourceId: crypto.randomUUID(),
61
+ redirectBackUrl: "https://example.com/redirect",
62
+ };
63
+ const url = vouch.getStartUrl(params);
64
+ expect(url.searchParams.get("customerId")).toBe("1be03be8-5014-413c-835a-feddf4020da2");
65
+ });
66
+ it("validates UUIDs", () => {
67
+ const params = {
68
+ requestId: crypto.randomUUID(),
69
+ datasourceId: crypto.randomUUID(),
70
+ customerId: crypto.randomUUID(),
71
+ redirectBackUrl: "https://example.com/redirect",
72
+ };
73
+ const keys = ["requestId", "datasourceId", "customerId"];
74
+ keys.forEach((key) => {
75
+ expect(() => vouch.getStartUrl(Object.assign(Object.assign({}, params), { [key]: "invalid-uuid" }))).toThrow(`Invalid UUIDv4 for ${key}: invalid-uuid`);
76
+ });
77
+ });
78
+ });
79
+ });
@@ -0,0 +1 @@
1
+ export declare function validatedHost(url: string | URL): URL;
@@ -0,0 +1,18 @@
1
+ export function validatedHost(url) {
2
+ const parsedUrl = parseUrl(url);
3
+ if (!/^https?:$/.exec(parsedUrl.protocol)) {
4
+ throw new Error(`Invalid vouch host: ${url.toString()}. Only HTTP and HTTPS protocols are allowed.`);
5
+ }
6
+ if (parsedUrl.pathname !== "/" || parsedUrl.search || parsedUrl.hash) {
7
+ throw new Error(`Invalid vouch host: ${url.toString()}. Only root URLs are allowed as vouch host.`);
8
+ }
9
+ return parsedUrl;
10
+ }
11
+ function parseUrl(url) {
12
+ try {
13
+ return new URL(url);
14
+ }
15
+ catch (_a) {
16
+ throw new Error(`Invalid URL: ${url.toString()}`);
17
+ }
18
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { validatedHost } from "./validated-host";
2
+ describe("Validate vouch host", () => {
3
+ it("should validate a valid vouch host URL", () => {
4
+ const validUrls = [
5
+ "https://vouch.example.com",
6
+ "https://vouch.example.com/",
7
+ "http://localhost:3000",
8
+ "http://127.0.0.1",
9
+ "http://127.0.0.1:3000",
10
+ ];
11
+ validUrls.forEach((url) => {
12
+ expect(() => validatedHost(url)).not.toThrow();
13
+ });
14
+ });
15
+ it("should throw an error for invalid vouch host URLs", () => {
16
+ const invalidUrls = [
17
+ "https://vouch.example.com/some/path",
18
+ "https://vouch.example.com?query=param",
19
+ "https://vouch.example.com#fragment",
20
+ "https://vouch.example.com/some/path?query=param#fragment",
21
+ "invalid-url",
22
+ "localhost:3000",
23
+ ];
24
+ invalidUrls.forEach((url) => {
25
+ expect(() => validatedHost(url)).toThrow();
26
+ });
27
+ });
28
+ });
@@ -0,0 +1 @@
1
+ export declare function validatedUuidV4(uuid: string, tag?: string): string;
@@ -0,0 +1,7 @@
1
+ export function validatedUuidV4(uuid, tag) {
2
+ const uuidv4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3
+ if (!uuidv4Regex.test(uuid)) {
4
+ throw new Error(`Invalid UUIDv4${tag ? ` for ${tag}` : ""}: ${uuid}`);
5
+ }
6
+ return uuid.toLowerCase();
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { validatedUuidV4 } from "./validated-uuid";
2
+ describe("Validate UUIDv4", () => {
3
+ it("passes for correct UUIDs", () => {
4
+ const exampleUuids = [
5
+ "f1df320a-774e-4061-ac7c-a0941d34a4fc",
6
+ "618379a9-7331-4556-b289-8039d1066aee",
7
+ "304875b0-becc-4ad6-a958-13867b03928c",
8
+ "4248b872-79b6-4e0d-a315-36b9579cae8c",
9
+ ];
10
+ exampleUuids.forEach((uuid) => {
11
+ expect(() => validatedUuidV4(uuid)).not.toThrow();
12
+ });
13
+ });
14
+ it("converts to lowercase", () => {
15
+ const uuid = crypto.randomUUID().toUpperCase();
16
+ expect(validatedUuidV4(uuid)).toBe(uuid.toLowerCase());
17
+ });
18
+ it("throws for incorrect UUIDs", () => {
19
+ const invalidUuids = [
20
+ "definitely not a uuid",
21
+ "12345678-1234-1234-1234-123456789012", // not a v4 UUID
22
+ "123e4567-e89b-12d3-a456-426614174000", // not a v4 UUID
23
+ "g23e4567-e89b-12d3-a456-426614174000", // invalid character
24
+ "123e4567-e89b-12d3-a456-42661417400", // too short
25
+ "123e4567-e89b-12d3-a456-4266141740000", // too long
26
+ ];
27
+ invalidUuids.forEach((uuid) => {
28
+ expect(() => validatedUuidV4(uuid)).toThrow();
29
+ });
30
+ });
31
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@getvouch/sdk",
3
+ "author": "Vouch",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/vlayer-xyz/vouch.git#main"
9
+ },
10
+ "private": false,
11
+ "version": "0.1.0",
12
+ "description": "Vouch SDK",
13
+ "keywords": [
14
+ "webproof",
15
+ "sdk",
16
+ "vouch"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run",
21
+ "lint": "eslint src && tsc --noEmit",
22
+ "lint:fix": "eslint src --fix && tsc --noEmit",
23
+ "format": "prettier --check .",
24
+ "format:fix": "prettier --write ."
25
+ },
26
+ "devDependencies": {
27
+ "eslint": "^9.24.0",
28
+ "eslint-config-prettier": "^10.1.1",
29
+ "eslint-plugin-prettier": "^5.2.6",
30
+ "prettier": "^3.5.3",
31
+ "typescript": "^5",
32
+ "typescript-eslint": "^8.30.1",
33
+ "vite-tsconfig-paths": "^5.1.4",
34
+ "vitest": "^3.2.3"
35
+ }
36
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "target": "ES2017",
5
+ "types": ["vitest/globals"],
6
+ "moduleDetection": "force",
7
+ "moduleResolution": "node",
8
+ "declaration": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "allowJs": true,
12
+ "strict": true,
13
+ "skipLibCheck": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noPropertyAccessFromIndexSignature": true,
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ }
23
+ },
24
+ "include": ["src/**/*"],
25
+ "exclude": ["dist", "node_modules"]
26
+ }