@majikah/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.
Files changed (71) hide show
  1. package/LICENSE +67 -0
  2. package/README.md +1112 -0
  3. package/dist/client/MajikahSDKClient.d.ts +308 -0
  4. package/dist/client/MajikahSDKClient.js +318 -0
  5. package/dist/errors/APIError.d.ts +7 -0
  6. package/dist/errors/APIError.js +14 -0
  7. package/dist/errors/AuthenticationError.d.ts +3 -0
  8. package/dist/errors/AuthenticationError.js +4 -0
  9. package/dist/errors/MajikahError.d.ts +4 -0
  10. package/dist/errors/MajikahError.js +9 -0
  11. package/dist/errors/QuotaExhaustedError.d.ts +3 -0
  12. package/dist/errors/QuotaExhaustedError.js +4 -0
  13. package/dist/errors/RateLimitError.d.ts +5 -0
  14. package/dist/errors/RateLimitError.js +10 -0
  15. package/dist/errors/ServiceUnavailableError.d.ts +3 -0
  16. package/dist/errors/ServiceUnavailableError.js +4 -0
  17. package/dist/errors/ValidationError.d.ts +5 -0
  18. package/dist/errors/ValidationError.js +10 -0
  19. package/dist/errors/index.d.ts +8 -0
  20. package/dist/errors/index.js +8 -0
  21. package/dist/errors/mapError.d.ts +3 -0
  22. package/dist/errors/mapError.js +22 -0
  23. package/dist/index.d.ts +10 -0
  24. package/dist/index.js +10 -0
  25. package/dist/services/index.d.ts +4 -0
  26. package/dist/services/index.js +4 -0
  27. package/dist/services/muid/MUIDClient.d.ts +95 -0
  28. package/dist/services/muid/MUIDClient.js +138 -0
  29. package/dist/services/muid/key-resolver.d.ts +24 -0
  30. package/dist/services/muid/key-resolver.js +32 -0
  31. package/dist/services/notary/NotaryClient.d.ts +188 -0
  32. package/dist/services/notary/NotaryClient.js +291 -0
  33. package/dist/services/notary/validation.d.ts +7 -0
  34. package/dist/services/notary/validation.js +19 -0
  35. package/dist/services/shared/encoding.d.ts +21 -0
  36. package/dist/services/shared/encoding.js +43 -0
  37. package/dist/services/shared/resolve-signature.d.ts +23 -0
  38. package/dist/services/shared/resolve-signature.js +31 -0
  39. package/dist/services/shared/sleep.d.ts +1 -0
  40. package/dist/services/shared/sleep.js +3 -0
  41. package/dist/services/shared/validation.d.ts +9 -0
  42. package/dist/services/shared/validation.js +16 -0
  43. package/dist/services/slink/SLinkClient.d.ts +174 -0
  44. package/dist/services/slink/SLinkClient.js +231 -0
  45. package/dist/services/slink/validation.d.ts +24 -0
  46. package/dist/services/slink/validation.js +31 -0
  47. package/dist/services/tsa/TSAClient.d.ts +101 -0
  48. package/dist/services/tsa/TSAClient.js +178 -0
  49. package/dist/services/tsa/validation.d.ts +2 -0
  50. package/dist/services/tsa/validation.js +12 -0
  51. package/dist/transport/HttpClient.d.ts +85 -0
  52. package/dist/transport/HttpClient.js +135 -0
  53. package/dist/transport/RouteResolver.d.ts +54 -0
  54. package/dist/transport/RouteResolver.js +67 -0
  55. package/dist/transport/retry-after.d.ts +17 -0
  56. package/dist/transport/retry-after.js +39 -0
  57. package/dist/transport/retry.d.ts +8 -0
  58. package/dist/transport/retry.js +61 -0
  59. package/dist/types/common.d.ts +133 -0
  60. package/dist/types/common.js +42 -0
  61. package/dist/types/index.d.ts +4 -0
  62. package/dist/types/index.js +1 -0
  63. package/dist/types/muid.d.ts +80 -0
  64. package/dist/types/muid.js +1 -0
  65. package/dist/types/notary.d.ts +243 -0
  66. package/dist/types/notary.js +1 -0
  67. package/dist/types/slink.d.ts +60 -0
  68. package/dist/types/slink.js +1 -0
  69. package/dist/types/tsa.d.ts +144 -0
  70. package/dist/types/tsa.js +1 -0
  71. package/package.json +67 -0
@@ -0,0 +1,135 @@
1
+ import { RouteResolver } from "./RouteResolver";
2
+ import { mapError } from "../errors/mapError";
3
+ import { MajikahError } from "../errors/MajikahError";
4
+ import { MAJIKAH_CLIENT_DEFAULTS, resolveVersions, } from "../types/common";
5
+ import { retryWithBackoff } from "./retry";
6
+ import { parseRetryAfterMs } from "./retry-after";
7
+ /**
8
+ * Internal HTTP transport used by the Majikah SDK service clients.
9
+ *
10
+ * Handles API URL resolution, authentication headers, request timeouts,
11
+ * response parsing, error mapping, and retry behavior for retry-safe requests.
12
+ */
13
+ export class HttpClient {
14
+ routes;
15
+ apiKey;
16
+ timeoutMs;
17
+ retry;
18
+ fetchImpl;
19
+ extraHeaders;
20
+ /**
21
+ * Creates an HTTP client from the SDK configuration.
22
+ *
23
+ * @param options API client configuration including authentication,
24
+ * timeout, retry, and transport settings.
25
+ * @throws MajikahError When no API key is provided.
26
+ */
27
+ constructor(options) {
28
+ if (!options.apiKey) {
29
+ throw new MajikahError("MajikahSDKClient requires an apiKey");
30
+ }
31
+ this.apiKey = options.apiKey;
32
+ this.timeoutMs = options.timeoutMs ?? MAJIKAH_CLIENT_DEFAULTS.timeoutMs;
33
+ this.retry = {
34
+ ...MAJIKAH_CLIENT_DEFAULTS.retry,
35
+ ...(options.retry ?? {}),
36
+ };
37
+ this.fetchImpl = options.fetch ?? fetch;
38
+ this.extraHeaders = options.headers ?? {};
39
+ this.routes = new RouteResolver(options.baseUrl ?? MAJIKAH_CLIENT_DEFAULTS.baseUrl, resolveVersions(options.version));
40
+ }
41
+ /**
42
+ * Sends a request to a Majikah API service.
43
+ *
44
+ * GET requests are automatically retried according to the configured retry
45
+ * policy. POST requests are only retried when explicitly marked as
46
+ * idempotent.
47
+ *
48
+ * Successful API responses are unwrapped and return their `data` value
49
+ * directly. API errors are converted into SDK-specific error types.
50
+ *
51
+ * @typeParam T Expected type of the unwrapped response data.
52
+ * @param group Service group receiving the request.
53
+ * @param path API path relative to the service route.
54
+ * @param opts HTTP method, query parameters, body, and retry settings.
55
+ * @returns The response data returned by the API.
56
+ * @throws MajikahError When the request fails or times out.
57
+ * @throws RateLimitError When the API rejects the request due to rate limits.
58
+ */
59
+ async request(group, path, opts) {
60
+ const url = this.buildUrl(group, path, opts.query);
61
+ const canRetry = opts.method === "GET" || opts.idempotent === true;
62
+ if (!canRetry) {
63
+ return this.attempt(url, opts);
64
+ }
65
+ return retryWithBackoff(() => this.attempt(url, opts), this.retry);
66
+ }
67
+ /**
68
+ * Builds a fully resolved API URL from a service route and query parameters.
69
+ *
70
+ * Undefined query values are omitted from the final URL.
71
+ *
72
+ * @param group Service group used to resolve the API route.
73
+ * @param path API path relative to the resolved service route.
74
+ * @param query Optional query parameters.
75
+ * @returns Fully resolved request URL.
76
+ */
77
+ buildUrl(group, path, query) {
78
+ const url = new URL(this.routes.build(group, path));
79
+ if (query) {
80
+ for (const [key, value] of Object.entries(query)) {
81
+ if (value !== undefined) {
82
+ url.searchParams.set(key, value);
83
+ }
84
+ }
85
+ }
86
+ return url;
87
+ }
88
+ /**
89
+ * Executes a single HTTP request without retrying.
90
+ *
91
+ * The response envelope is validated and unwrapped before returning the
92
+ * contained data. HTTP and API errors are converted through `mapError()`.
93
+ *
94
+ * @typeParam T Expected type of the unwrapped response data.
95
+ * @param url Fully resolved request URL.
96
+ * @param opts Request configuration.
97
+ * @returns The response data returned by the API.
98
+ * @throws MajikahError When the request times out or the API returns an error.
99
+ */
100
+ async attempt(url, opts) {
101
+ const controller = new AbortController();
102
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
103
+ try {
104
+ const res = await this.fetchImpl(url.toString(), {
105
+ method: opts.method,
106
+ headers: {
107
+ "X-API-KEY": this.apiKey,
108
+ ...(opts.body ? { "Content-Type": "application/json" } : {}),
109
+ ...this.extraHeaders,
110
+ },
111
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
112
+ signal: controller.signal,
113
+ });
114
+ const requestId = res.headers.get("x-request-id") ?? undefined;
115
+ const json = (await res.json().catch(() => undefined));
116
+ if (!res.ok || !json || json.success === false) {
117
+ throw mapError(res.status, json && json.success === false ? json : undefined, requestId, res.status === 429 ? parseRetryAfterMs(res) : undefined);
118
+ }
119
+ return json.data;
120
+ }
121
+ catch (err) {
122
+ if (err instanceof DOMException && err.name === "AbortError") {
123
+ throw new MajikahError(`Request timed out after ${this.timeoutMs}ms`);
124
+ }
125
+ throw err;
126
+ }
127
+ finally {
128
+ clearTimeout(timer);
129
+ }
130
+ }
131
+ }
132
+ // Freeze static methods
133
+ Object.freeze(HttpClient);
134
+ // Freeze instance methods
135
+ Object.freeze(HttpClient.prototype);
@@ -0,0 +1,54 @@
1
+ import type { ServiceGroup } from "../types/common";
2
+ /**
3
+ * Resolves logical service routes into fully qualified, versioned API URLs.
4
+ *
5
+ * `RouteResolver` centralizes URL construction so individual service clients
6
+ * do not need to know the gateway's route prefixes or API versioning scheme.
7
+ *
8
+ * The resolver:
9
+ * - maps a {@link ServiceGroup} to its configured route segment;
10
+ * - applies the configured API version for that service group;
11
+ * - normalizes the base URL to prevent duplicate path separators; and
12
+ * - normalizes the route path to ensure it begins with `/`.
13
+ *
14
+ * Instances are intentionally treated as immutable after construction.
15
+ */
16
+ export declare class RouteResolver {
17
+ private readonly baseUrl;
18
+ private readonly versions;
19
+ /**
20
+ * Creates a route resolver for the configured API gateway.
21
+ *
22
+ * @param baseUrl - Base URL of the API gateway, with or without a trailing `/`.
23
+ * @param versions - API version number for each supported service group.
24
+ */
25
+ constructor(baseUrl: string, versions: Record<ServiceGroup, number>);
26
+ /**
27
+ * Builds a fully qualified, versioned route for a service.
28
+ *
29
+ * The resulting URL has the following structure:
30
+ *
31
+ * `BASE_URL/SERVICE_SEGMENT/vVERSION/PATH`
32
+ *
33
+ * Both `baseUrl` and `path` are normalized, so callers may provide
34
+ * either leading or trailing slashes without producing duplicate `/`
35
+ * separators at the join points.
36
+ *
37
+ * @param group - Logical service group whose route segment and API version
38
+ * should be used.
39
+ * @param path - Service-relative route path, with or without a leading `/`.
40
+ * @returns The fully qualified, versioned API route.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const resolver = new RouteResolver("https://api.example.com/", {
45
+ * tsa: 1,
46
+ * muid: 1,
47
+ * });
48
+ *
49
+ * resolver.build("tsa", "/timestamp");
50
+ * // "https://api.example.com/tsa/v1/timestamp"
51
+ * ```
52
+ */
53
+ build(group: ServiceGroup, path: string): string;
54
+ }
@@ -0,0 +1,67 @@
1
+ import { SERVICE_ROUTE_SEGMENT } from "../types/common";
2
+ /**
3
+ * Resolves logical service routes into fully qualified, versioned API URLs.
4
+ *
5
+ * `RouteResolver` centralizes URL construction so individual service clients
6
+ * do not need to know the gateway's route prefixes or API versioning scheme.
7
+ *
8
+ * The resolver:
9
+ * - maps a {@link ServiceGroup} to its configured route segment;
10
+ * - applies the configured API version for that service group;
11
+ * - normalizes the base URL to prevent duplicate path separators; and
12
+ * - normalizes the route path to ensure it begins with `/`.
13
+ *
14
+ * Instances are intentionally treated as immutable after construction.
15
+ */
16
+ export class RouteResolver {
17
+ baseUrl;
18
+ versions;
19
+ /**
20
+ * Creates a route resolver for the configured API gateway.
21
+ *
22
+ * @param baseUrl - Base URL of the API gateway, with or without a trailing `/`.
23
+ * @param versions - API version number for each supported service group.
24
+ */
25
+ constructor(baseUrl, versions) {
26
+ this.baseUrl = baseUrl;
27
+ this.versions = versions;
28
+ }
29
+ /**
30
+ * Builds a fully qualified, versioned route for a service.
31
+ *
32
+ * The resulting URL has the following structure:
33
+ *
34
+ * `BASE_URL/SERVICE_SEGMENT/vVERSION/PATH`
35
+ *
36
+ * Both `baseUrl` and `path` are normalized, so callers may provide
37
+ * either leading or trailing slashes without producing duplicate `/`
38
+ * separators at the join points.
39
+ *
40
+ * @param group - Logical service group whose route segment and API version
41
+ * should be used.
42
+ * @param path - Service-relative route path, with or without a leading `/`.
43
+ * @returns The fully qualified, versioned API route.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const resolver = new RouteResolver("https://api.example.com/", {
48
+ * tsa: 1,
49
+ * muid: 1,
50
+ * });
51
+ *
52
+ * resolver.build("tsa", "/timestamp");
53
+ * // "https://api.example.com/tsa/v1/timestamp"
54
+ * ```
55
+ */
56
+ build(group, path) {
57
+ const segment = SERVICE_ROUTE_SEGMENT[group];
58
+ const version = this.versions[group];
59
+ const cleanBase = this.baseUrl.replace(/\/+$/, "");
60
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
61
+ return `${cleanBase}/${segment}/v${version}${cleanPath}`;
62
+ }
63
+ }
64
+ // Freeze static methods
65
+ Object.freeze(RouteResolver);
66
+ // Freeze instance methods
67
+ Object.freeze(RouteResolver.prototype);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Parses the standard HTTP `Retry-After` header into milliseconds.
3
+ *
4
+ * Per RFC 9110 §10.2.3, the header may be either:
5
+ * 1. delay-seconds — an integer number of seconds, e.g. "120"
6
+ * 2. HTTP-date — an absolute timestamp, e.g. "Wed, 21 Oct 2026 07:28:00 GMT"
7
+ *
8
+ * Our gateway (buildGatewayResponseHeaders) currently only emits form 1,
9
+ * but a CDN or edge layer in front of it could plausibly inject form 2,
10
+ * so both are handled here rather than assuming the numeric case always
11
+ * holds.
12
+ *
13
+ * Returns undefined when the header is missing or doesn't parse as
14
+ * either form — callers treat that as "no server-specified wait,
15
+ * fall back to our own backoff schedule."
16
+ */
17
+ export declare function parseRetryAfterMs(res: Response): number | undefined;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Parses the standard HTTP `Retry-After` header into milliseconds.
3
+ *
4
+ * Per RFC 9110 §10.2.3, the header may be either:
5
+ * 1. delay-seconds — an integer number of seconds, e.g. "120"
6
+ * 2. HTTP-date — an absolute timestamp, e.g. "Wed, 21 Oct 2026 07:28:00 GMT"
7
+ *
8
+ * Our gateway (buildGatewayResponseHeaders) currently only emits form 1,
9
+ * but a CDN or edge layer in front of it could plausibly inject form 2,
10
+ * so both are handled here rather than assuming the numeric case always
11
+ * holds.
12
+ *
13
+ * Returns undefined when the header is missing or doesn't parse as
14
+ * either form — callers treat that as "no server-specified wait,
15
+ * fall back to our own backoff schedule."
16
+ */
17
+ export function parseRetryAfterMs(res) {
18
+ const header = res.headers.get("retry-after");
19
+ if (!header)
20
+ return undefined;
21
+ const trimmed = header.trim();
22
+ // Form 1: delay-seconds. Must be a non-negative integer per spec —
23
+ // reject negative numbers and non-integer garbage like "12.5abc"
24
+ // that Number() would otherwise coerce leniently.
25
+ if (/^\d+$/.test(trimmed)) {
26
+ const seconds = Number(trimmed);
27
+ return seconds * 1000;
28
+ }
29
+ // Form 2: HTTP-date. Date.parse handles RFC 7231 IMF-fixdate format
30
+ // (and is lenient enough to also accept a few common variants).
31
+ const dateMs = Date.parse(trimmed);
32
+ if (Number.isNaN(dateMs))
33
+ return undefined;
34
+ const deltaMs = dateMs - Date.now();
35
+ // A date in the past (clock skew, or server told us to retry
36
+ // "immediately" via an already-elapsed timestamp) should mean
37
+ // "no additional wait," not a negative sleep duration.
38
+ return Math.max(deltaMs, 0);
39
+ }
@@ -0,0 +1,8 @@
1
+ import type { RetryOptions } from "../types/common";
2
+ /**
3
+ * Retries an async function with exponential backoff + jitter.
4
+ * Only retries errors that pass `isRetryable` — caller still decides
5
+ * (via HttpClient) whether this method should be attempted more than
6
+ * once at all, e.g. non-idempotent POSTs like tsa.issue never call this.
7
+ */
8
+ export declare function retryWithBackoff<T>(fn: () => Promise<T>, options: Required<RetryOptions>, onRetry?: (attempt: number, delayMs: number, err: unknown) => void): Promise<T>;
@@ -0,0 +1,61 @@
1
+ import { APIError } from "../errors/APIError";
2
+ import { RateLimitError } from "../errors/RateLimitError";
3
+ import { MajikahError } from "../errors/MajikahError";
4
+ function sleep(ms) {
5
+ return new Promise((resolve) => setTimeout(resolve, ms));
6
+ }
7
+ /**
8
+ * Whether an error is worth retrying at all.
9
+ *
10
+ * - 5xx / ServiceUnavailableError: transient server-side failure, safe to retry.
11
+ * - RateLimitError (429): retry, but respect Retry-After if the server gave one
12
+ * rather than our own backoff schedule (see retryWithBackoff below).
13
+ * - MajikahError with no APIError subtype (timeout, network failure, JSON
14
+ * parse failure before a status code was even assigned): also transient.
15
+ * - Anything else — 4xx like 400/401/402/404, or a client-side ValidationError
16
+ * that never left the machine — is not retryable. Retrying a malformed
17
+ * request or a bad API key just wastes attempts and delays surfacing a
18
+ * fixable problem.
19
+ */
20
+ function isRetryable(err) {
21
+ if (err instanceof RateLimitError)
22
+ return true;
23
+ if (err instanceof APIError)
24
+ return err.status >= 500;
25
+ if (err instanceof MajikahError)
26
+ return true; // timeout / network-level failure
27
+ return false;
28
+ }
29
+ /**
30
+ * Retries an async function with exponential backoff + jitter.
31
+ * Only retries errors that pass `isRetryable` — caller still decides
32
+ * (via HttpClient) whether this method should be attempted more than
33
+ * once at all, e.g. non-idempotent POSTs like tsa.issue never call this.
34
+ */
35
+ export async function retryWithBackoff(fn, options, onRetry) {
36
+ const { maxAttempts, initialDelayMs, jitterFactor, capDelayMs } = options;
37
+ let attempt = 0;
38
+ let delay = initialDelayMs;
39
+ while (attempt < maxAttempts) {
40
+ try {
41
+ return await fn();
42
+ }
43
+ catch (err) {
44
+ attempt++;
45
+ if (!isRetryable(err) || attempt >= maxAttempts) {
46
+ throw err;
47
+ }
48
+ // A server-specified Retry-After overrides our own schedule for 429s —
49
+ // it's the server telling us exactly when it'll accept work again,
50
+ // more reliable than guessing with backoff math.
51
+ const waitMs = err instanceof RateLimitError && err.retryAfterMs !== undefined
52
+ ? err.retryAfterMs
53
+ : delay;
54
+ onRetry?.(attempt, waitMs, err);
55
+ await sleep(waitMs);
56
+ delay = Math.min(Math.round(delay * (1.5 + Math.random() * jitterFactor)), capDelayMs);
57
+ }
58
+ }
59
+ // Unreachable — loop always returns or throws — but keeps TS satisfied.
60
+ throw new MajikahError(`Retry loop exited unexpectedly after ${maxAttempts} attempts`);
61
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Supported service groups exposed by the Majikah API.
3
+ */
4
+ export type ServiceGroup = "tsa" | "notary" | "slink" | "muid";
5
+ /**
6
+ * Maps each service group to the URL path segment used by the API.
7
+ */
8
+ export declare const SERVICE_ROUTE_SEGMENT: Record<ServiceGroup, string>;
9
+ /**
10
+ * Configuration options for the Majikah API client.
11
+ */
12
+ export interface MajikahClientOptions {
13
+ /** API key used to authenticate requests. */
14
+ apiKey: string;
15
+ /** Base URL of the Majikah public API. */
16
+ baseUrl?: string;
17
+ /**
18
+ * API version to use for all services, or individual versions per service.
19
+ *
20
+ * A numeric value applies to every service unless overridden by a
21
+ * service-specific version.
22
+ */
23
+ version?: number | Partial<Record<ServiceGroup, number>>;
24
+ /** Maximum time, in milliseconds, allowed for a single request. */
25
+ timeoutMs?: number;
26
+ /** Additional HTTP headers sent with every request. */
27
+ headers?: Record<string, string>;
28
+ /** Fetch implementation used for HTTP requests. Defaults to the environment's `fetch`. */
29
+ fetch?: typeof fetch;
30
+ /** Request retry behavior. */
31
+ retry?: RetryOptions;
32
+ }
33
+ /**
34
+ * Controls how failed API requests are retried.
35
+ */
36
+ export interface RetryOptions {
37
+ /** Maximum number of request attempts, including the initial request. */
38
+ maxAttempts?: number;
39
+ /** Initial delay before the first retry, in milliseconds. */
40
+ initialDelayMs?: number;
41
+ /** Randomization factor applied to retry delays to reduce request synchronization. */
42
+ jitterFactor?: number;
43
+ /** Maximum retry delay, in milliseconds. */
44
+ capDelayMs?: number;
45
+ }
46
+ /**
47
+ * Default configuration used by the Majikah API client when options are omitted.
48
+ */
49
+ export declare const MAJIKAH_CLIENT_DEFAULTS: {
50
+ readonly baseUrl: "https://api-public.majikah.solutions";
51
+ readonly version: 1;
52
+ readonly timeoutMs: 15000;
53
+ readonly retry: Required<RetryOptions>;
54
+ };
55
+ /**
56
+ * Resolves the effective API version for every supported service.
57
+ *
58
+ * A single numeric version is used as the default for all services.
59
+ * Service-specific versions override that default when provided.
60
+ *
61
+ * @param version Global API version or per-service version overrides.
62
+ * @returns A complete version map containing a version for every service.
63
+ */
64
+ export declare function resolveVersions(version: MajikahClientOptions["version"]): Record<ServiceGroup, number>;
65
+ /**
66
+ * Standard success response returned by the Majikah API.
67
+ *
68
+ * @typeParam T Type of the successful response data.
69
+ */
70
+ export interface ApiSuccessBody<T> {
71
+ /** Indicates that the API request completed successfully. */
72
+ success: true;
73
+ /** Human-readable description of the operation result. */
74
+ message: string;
75
+ /** Data returned by the requested operation. */
76
+ data: T;
77
+ }
78
+ /**
79
+ * Standard error response returned by the Majikah API.
80
+ */
81
+ export interface ApiErrorBody {
82
+ /** Indicates that the API request failed. */
83
+ success: false;
84
+ /** Human-readable description of the error. */
85
+ error: string;
86
+ /** Optional machine-readable error code. */
87
+ code?: string;
88
+ }
89
+ /**
90
+ * Union of the standard successful and failed Majikah API responses.
91
+ *
92
+ * Use the `success` property as the discriminator when handling a response.
93
+ *
94
+ * @typeParam T Type of the successful response data.
95
+ */
96
+ export type ApiResponseBody<T> = ApiSuccessBody<T> | ApiErrorBody;
97
+ /**
98
+ * Cursor-based pagination result returned by a Majikah API list endpoint.
99
+ *
100
+ * The `next_cursor` value is opaque and should be passed back unchanged as
101
+ * the `cursor` parameter when requesting the next page.
102
+ *
103
+ * @typeParam T Type of items returned in the current page.
104
+ */
105
+ export interface PageResult<T> {
106
+ /** Items returned for the current page. */
107
+ items: T[];
108
+ /**
109
+ * Opaque cursor for retrieving the next page.
110
+ *
111
+ * `null` indicates that there is no next page.
112
+ */
113
+ next_cursor: string | null;
114
+ /** Indicates whether another page of results is available. */
115
+ has_more: boolean;
116
+ /** Number of items returned in the current page. */
117
+ count: number;
118
+ /** Maximum number of items requested for the current page. */
119
+ limit: number;
120
+ }
121
+ /**
122
+ * Parameters used to request a page of cursor-based results.
123
+ */
124
+ export interface PaginationParams {
125
+ /**
126
+ * Opaque cursor returned by the previous page.
127
+ *
128
+ * Omit this value to request the first page.
129
+ */
130
+ cursor?: string;
131
+ /** Maximum number of items to return in the page. */
132
+ limit?: number;
133
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Maps each service group to the URL path segment used by the API.
3
+ */
4
+ export const SERVICE_ROUTE_SEGMENT = {
5
+ tsa: "tsa",
6
+ notary: "notary",
7
+ slink: "slink",
8
+ muid: "muid",
9
+ };
10
+ /**
11
+ * Default configuration used by the Majikah API client when options are omitted.
12
+ */
13
+ export const MAJIKAH_CLIENT_DEFAULTS = {
14
+ baseUrl: "https://api-public.majikah.solutions",
15
+ version: 1,
16
+ timeoutMs: 15_000,
17
+ retry: {
18
+ maxAttempts: 3,
19
+ initialDelayMs: 300,
20
+ jitterFactor: 0.4,
21
+ capDelayMs: 5_000,
22
+ },
23
+ };
24
+ /**
25
+ * Resolves the effective API version for every supported service.
26
+ *
27
+ * A single numeric version is used as the default for all services.
28
+ * Service-specific versions override that default when provided.
29
+ *
30
+ * @param version Global API version or per-service version overrides.
31
+ * @returns A complete version map containing a version for every service.
32
+ */
33
+ export function resolveVersions(version) {
34
+ const fallback = typeof version === "number" ? version : MAJIKAH_CLIENT_DEFAULTS.version;
35
+ const overrides = typeof version === "object" && version !== null ? version : {};
36
+ return {
37
+ tsa: overrides.tsa ?? fallback,
38
+ notary: overrides.notary ?? fallback,
39
+ slink: overrides.slink ?? fallback,
40
+ muid: overrides.muid ?? fallback,
41
+ };
42
+ }
@@ -0,0 +1,4 @@
1
+ export type * from "./tsa";
2
+ export type * from "./muid";
3
+ export type * from "./slink";
4
+ export * from "./common";
@@ -0,0 +1 @@
1
+ export * from "./common";
@@ -0,0 +1,80 @@
1
+ export type { MajikID, MajikIDPublicView, MajikKeyPublicBundle, KeyGenerationRecord, } from "@majikah/majik-universal-id";
2
+ export type { MajikSignatureJSON } from "@majikah/majik-signature";
3
+ import type { KeyGenerationRecord, MajikIDPublicView, SignatureTrustLevel } from "@majikah/majik-universal-id";
4
+ import type { MajikSignatureJSON } from "@majikah/majik-signature";
5
+ /**
6
+ * Options for verifying an embedded file signature against a MUID.
7
+ */
8
+ export interface VerifyFileOptions {
9
+ /**
10
+ * Signer ID to verify when the file contains multiple signatures.
11
+ *
12
+ * Required for multi-signature files to explicitly identify the target
13
+ * signature.
14
+ */
15
+ expectedSignerId?: string;
16
+ /**
17
+ * MUID to verify the signature against.
18
+ *
19
+ * Accepts either a MUID ID or username. When omitted, the API verifies
20
+ * against the MUID associated with the caller's API key.
21
+ */
22
+ muid?: string;
23
+ /** MIME type of the file being inspected. */
24
+ mimeType?: string;
25
+ }
26
+ /**
27
+ * Options for verifying a signature from a detached envelope against a MUID.
28
+ */
29
+ export interface VerifyFileDetachedOptions {
30
+ /**
31
+ * Signer ID to verify when the envelope contains multiple signatures.
32
+ *
33
+ * Required for multi-signature envelopes to explicitly identify the target
34
+ * signature.
35
+ */
36
+ expectedSignerId?: string;
37
+ /**
38
+ * MUID to verify the signature against.
39
+ *
40
+ * Accepts either a MUID ID or username. When omitted, the API verifies
41
+ * against the MUID associated with the caller's API key.
42
+ */
43
+ muid?: string;
44
+ }
45
+ /**
46
+ * Request/response shapes for /muid/verify — this exact pairing (an
47
+ * optional id + a signature envelope, returning a trust verdict) only
48
+ * exists as an API contract. Neither library models "verify this over
49
+ * the wire against a resolved identity" as a single object.
50
+ */
51
+ export interface MuidVerifyRequestBody {
52
+ id?: string;
53
+ signature: MajikSignatureJSON;
54
+ }
55
+ export interface MuidVerifyResult {
56
+ valid: boolean;
57
+ trustLevel?: SignatureTrustLevel;
58
+ reason?: string;
59
+ signerId?: string;
60
+ timestamp?: string;
61
+ }
62
+ /**
63
+ * Projected subset of KeyGenerationRecord returned by /muid/:id/public.
64
+ * Built with Pick against the library type instead of hand-typing the
65
+ * same five fields again — if the library adds/renames a field, this
66
+ * either still compiles (safe) or breaks loudly at the Pick (safe),
67
+ * never silently drifts.
68
+ */
69
+ export type PublicKeyGeneration = Pick<KeyGenerationRecord, "fingerprint" | "bundle_hash" | "status" | "activated_at" | "deactivated_at">;
70
+ export type PublicSLinkSummary = unknown;
71
+ /**
72
+ * Composition of a public MUID view + its slinks + key history — this
73
+ * combined shape is purely the /muid/:id/public response contract, not
74
+ * something either library exposes as a single type.
75
+ */
76
+ export interface MuidPublicLookupResult {
77
+ muid: MajikIDPublicView;
78
+ slinks: PublicSLinkSummary[];
79
+ key_history: PublicKeyGeneration[];
80
+ }
@@ -0,0 +1 @@
1
+ export {};