@duckity/js 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@duckity/js",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "module": "src/index.ts",
6
6
  "collaborators": [
7
7
  "Rafael Bradley <rafabradleyrb@gmail.com>"
8
8
  ],
9
9
  "files": [
10
+ "src",
10
11
  "dist"
11
12
  ],
12
13
  "exports": {
package/src/index.ts ADDED
@@ -0,0 +1,58 @@
1
+ import wrapper from "./processing/wrapper";
2
+ import { post } from "./requests";
3
+
4
+ /**
5
+ * Options for getting a Duckity challenge from the API.
6
+ */
7
+ export interface GetDuckityChallengeOptions {
8
+ /**
9
+ * The custom-context threat correlation keys to be sent with the request.
10
+ */
11
+ keys?: { [key: string]: string };
12
+
13
+ /**
14
+ * The base URL to the API endpoint. Defaults to `https://quack.duckity.com` if not provided.
15
+ *
16
+ * Update this when self-hosting a Duckling. No trailing slash must be included in the URL.
17
+ */
18
+ api?: string;
19
+ }
20
+
21
+ /**
22
+ * The response from the Duckling API when requesting a challenge.
23
+ */
24
+ interface ChallengeResponse {
25
+ /**
26
+ * The encoded challenge string returned by the Duckling API.
27
+ */
28
+ challenge: string;
29
+ }
30
+
31
+ /**
32
+ * Fetches, solves, and returns the solution to a Duckity challenge for the given protection
33
+ * profile ID.
34
+ *
35
+ * @param protectionProfileId The ID of the protection profile to get the challenge for.
36
+ * @param options Optional parameters for the challenge issuance.
37
+ * @returns The solution to the challenge issued by Duckity.
38
+ */
39
+ export async function solve(
40
+ protectionProfileId: string,
41
+ options?: GetDuckityChallengeOptions,
42
+ ): Promise<string> {
43
+ let response: ChallengeResponse = await post(
44
+ `${options?.api || "https://quack.duckity.com"}/v1/challenge`,
45
+ {
46
+ body: {
47
+ id: protectionProfileId,
48
+ keys: options?.keys || {},
49
+ },
50
+ },
51
+ );
52
+
53
+ let solution = await wrapper.solve(response.challenge);
54
+
55
+ return solution;
56
+ }
57
+
58
+ export default { solve }
@@ -0,0 +1,16 @@
1
+ import * as Comlink from "comlink";
2
+ import { process } from "@duckity/wasm";
3
+
4
+ /**
5
+ * Expose the `process` function from the WASM module to the main thread via Comlink.
6
+ *
7
+ * This allows the main thread to call the `process` function in the worker thread, which will
8
+ * handle the processing of the challenges without blocking the main thread.
9
+ */
10
+ const api = {
11
+ async process(challenge: string) {
12
+ return process(challenge);
13
+ },
14
+ };
15
+
16
+ Comlink.expose(api);
@@ -0,0 +1,18 @@
1
+ import wasm from "@duckity/wasm/worker.wasm";
2
+ import * as Comlink from "comlink";
3
+
4
+ /**
5
+ * Web Worker that handles the processing of the challenges.
6
+ */
7
+ const worker = new Worker(wasm, {
8
+ type: "module",
9
+ });
10
+
11
+ /**
12
+ * Wrap the worker with Comlink to allow for easy communication between the main thread and the
13
+ * worker thread. The worker exposes a `solve` function that takes a challenge string and returns a
14
+ * promise that resolves to the solution string.
15
+ */
16
+ export default Comlink.wrap<{
17
+ solve(challenge: string): Promise<string>;
18
+ }>(worker);
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Error schema for API responses. This is used to extract error messages from failed requests.
3
+ */
4
+ interface ErrorMessage {
5
+ title: string;
6
+ message: string;
7
+ }
8
+
9
+ /**
10
+ * Wrapper function for making HTTP requests using the Fetch API. It handles JSON parsing and error
11
+ * handling, throwing an error with a message extracted from the response if the request fails.
12
+ *
13
+ * @param url The URL to make the request to.
14
+ * @param options Extra options to pass to the Fetch API, such as method, headers, and body.
15
+ * @returns The parsed JSON response from the server if the request is successful.
16
+ */
17
+ async function request<T>(url: string, options?: RequestInit): Promise<T> {
18
+ const response = await fetch(url, options);
19
+
20
+ let data;
21
+
22
+ try {
23
+ data = await response.json();
24
+ } catch {
25
+ data = await response.text();
26
+ }
27
+
28
+ if (!response.ok) {
29
+ throw new Error(
30
+ (data as ErrorMessage | undefined)?.message || "Request failed",
31
+ );
32
+ }
33
+
34
+ return data as T;
35
+ }
36
+
37
+ /**
38
+ * Makes a GET request to the specified URL and returns the parsed JSON response.
39
+ *
40
+ * If the request fails, it throws an error with a message extracted from the response if
41
+ * available, or a generic message if not.
42
+ *
43
+ * @param url The URL to make the request to.
44
+ * @param options Extra options to pass to the Fetch API, such as headers and body.
45
+ * @returns The parsed JSON response from the server if the request is successful.
46
+ */
47
+ export function get<T>(url: string, options?: RequestInit): Promise<T> {
48
+ return request(url, {
49
+ method: "GET",
50
+ ...options,
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Makes a POST request to the specified URL and returns the parsed JSON response.
56
+ *
57
+ * If the request fails, it throws an error with a message extracted from the response if
58
+ * available, or a generic message if not.
59
+ *
60
+ * @param url The URL to make the request to.
61
+ * @param options Extra options to pass to the Fetch API, such as headers and body.
62
+ * @returns The parsed JSON response from the server if the request is successful.
63
+ */
64
+ export function post<T>(url: string, options?: Omit<RequestInit, "body"> & { body: any }): Promise<T> {
65
+ return request(url, {
66
+ method: "POST",
67
+ headers: {
68
+ "Content-Type": "application/json",
69
+ ...options?.headers,
70
+ },
71
+ body: options?.body ? JSON.stringify(options.body) : undefined,
72
+ ...options,
73
+ });
74
+ }