@majikah/majik-api 0.1.0 → 0.1.1
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/dist/constants.d.ts +11 -0
- package/dist/constants.js +23 -0
- package/dist/majik-api.d.ts +0 -8
- package/dist/majik-api.js +2 -108
- package/dist/utils.d.ts +14 -0
- package/dist/utils.js +82 -0
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RateLimit, RateLimitFrequency } from "./types";
|
|
2
|
+
export declare const DEFAULT_RATE_LIMIT: RateLimit;
|
|
3
|
+
/**
|
|
4
|
+
* Hard ceiling for any rate limit set on a MajikAPI key.
|
|
5
|
+
* No key — regardless of trust level — may exceed this without bypassSafeLimit.
|
|
6
|
+
* Expressed in req/min for normalisation purposes; stored as a RateLimit for
|
|
7
|
+
* consistency with the rest of the API.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_RATE_LIMIT: RateLimit;
|
|
10
|
+
/** Multipliers to convert each frequency unit into requests-per-minute. */
|
|
11
|
+
export declare const TO_MINUTES: Record<RateLimitFrequency, number>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────
|
|
2
|
+
// Constants
|
|
3
|
+
// ─────────────────────────────────────────────
|
|
4
|
+
export const DEFAULT_RATE_LIMIT = {
|
|
5
|
+
amount: 100,
|
|
6
|
+
frequency: "minutes",
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Hard ceiling for any rate limit set on a MajikAPI key.
|
|
10
|
+
* No key — regardless of trust level — may exceed this without bypassSafeLimit.
|
|
11
|
+
* Expressed in req/min for normalisation purposes; stored as a RateLimit for
|
|
12
|
+
* consistency with the rest of the API.
|
|
13
|
+
*/
|
|
14
|
+
export const MAX_RATE_LIMIT = {
|
|
15
|
+
amount: 500,
|
|
16
|
+
frequency: "minutes",
|
|
17
|
+
};
|
|
18
|
+
/** Multipliers to convert each frequency unit into requests-per-minute. */
|
|
19
|
+
export const TO_MINUTES = {
|
|
20
|
+
seconds: 1 / 60,
|
|
21
|
+
minutes: 1,
|
|
22
|
+
hours: 60,
|
|
23
|
+
};
|
package/dist/majik-api.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import type { DomainWhitelist, IPWhitelist, MajikAPICreateOptions, MajikAPIJSON, MajikAPISettings, RateLimit, RateLimitFrequency } from "./types";
|
|
2
|
-
export declare const DEFAULT_RATE_LIMIT: RateLimit;
|
|
3
|
-
/**
|
|
4
|
-
* Hard ceiling for any rate limit set on a MajikAPI key.
|
|
5
|
-
* No key — regardless of trust level — may exceed this without bypassSafeLimit.
|
|
6
|
-
* Expressed in req/min for normalisation purposes; stored as a RateLimit for
|
|
7
|
-
* consistency with the rest of the API.
|
|
8
|
-
*/
|
|
9
|
-
export declare const MAX_RATE_LIMIT: RateLimit;
|
|
10
2
|
export declare class MajikAPI {
|
|
11
3
|
private readonly _id;
|
|
12
4
|
private readonly _owner_id;
|
package/dist/majik-api.js
CHANGED
|
@@ -1,108 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
// Constants
|
|
4
|
-
// ─────────────────────────────────────────────
|
|
5
|
-
export const DEFAULT_RATE_LIMIT = {
|
|
6
|
-
amount: 100,
|
|
7
|
-
frequency: "minutes",
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Hard ceiling for any rate limit set on a MajikAPI key.
|
|
11
|
-
* No key — regardless of trust level — may exceed this without bypassSafeLimit.
|
|
12
|
-
* Expressed in req/min for normalisation purposes; stored as a RateLimit for
|
|
13
|
-
* consistency with the rest of the API.
|
|
14
|
-
*/
|
|
15
|
-
export const MAX_RATE_LIMIT = {
|
|
16
|
-
amount: 500,
|
|
17
|
-
frequency: "minutes",
|
|
18
|
-
};
|
|
19
|
-
/** Multipliers to convert each frequency unit into requests-per-minute. */
|
|
20
|
-
const TO_MINUTES = {
|
|
21
|
-
seconds: 1 / 60,
|
|
22
|
-
minutes: 1,
|
|
23
|
-
hours: 60,
|
|
24
|
-
};
|
|
25
|
-
// ─────────────────────────────────────────────
|
|
26
|
-
// Validation Helpers
|
|
27
|
-
// ─────────────────────────────────────────────
|
|
28
|
-
function assertString(value, label) {
|
|
29
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
30
|
-
throw new TypeError(`[MajikAPI] "${label}" must be a non-empty string. Received: ${JSON.stringify(value)}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function assertPositiveInteger(value, label) {
|
|
34
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
35
|
-
throw new RangeError(`[MajikAPI] "${label}" must be a positive integer. Received: ${JSON.stringify(value)}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function assertRateLimitFrequency(value, label) {
|
|
39
|
-
const valid = ["seconds", "minutes", "hours"];
|
|
40
|
-
if (!valid.includes(value)) {
|
|
41
|
-
throw new TypeError(`[MajikAPI] "${label}" must be one of: ${valid.join(", ")}. Received: ${JSON.stringify(value)}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function assertBoolean(value, label) {
|
|
45
|
-
if (typeof value !== "boolean") {
|
|
46
|
-
throw new TypeError(`[MajikAPI] "${label}" must be a boolean. Received: ${JSON.stringify(value)}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function assertStringArray(value, label) {
|
|
50
|
-
if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
|
|
51
|
-
throw new TypeError(`[MajikAPI] "${label}" must be an array of strings. Received: ${JSON.stringify(value)}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function isValidIPv4(ip) {
|
|
55
|
-
return (/^(\d{1,3}\.){3}\d{1,3}$/.test(ip) &&
|
|
56
|
-
ip.split(".").every((o) => parseInt(o) <= 255));
|
|
57
|
-
}
|
|
58
|
-
function isValidIPv6(ip) {
|
|
59
|
-
return /^[0-9a-fA-F:]+$/.test(ip) && ip.includes(":");
|
|
60
|
-
}
|
|
61
|
-
function isValidCIDR(cidr) {
|
|
62
|
-
const [ip, prefix] = cidr.split("/");
|
|
63
|
-
if (!prefix)
|
|
64
|
-
return false;
|
|
65
|
-
const p = parseInt(prefix);
|
|
66
|
-
return ((isValidIPv4(ip) && p >= 0 && p <= 32) ||
|
|
67
|
-
(isValidIPv6(ip) && p >= 0 && p <= 128));
|
|
68
|
-
}
|
|
69
|
-
function validateIP(ip) {
|
|
70
|
-
if (!isValidIPv4(ip) && !isValidIPv6(ip) && !isValidCIDR(ip)) {
|
|
71
|
-
throw new Error(`[MajikAPI] Invalid IP address or CIDR: "${ip}"`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function isValidDomain(domain) {
|
|
75
|
-
return (/^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$/.test(domain) || /^\*$/.test(domain));
|
|
76
|
-
}
|
|
77
|
-
function validateDomain(domain) {
|
|
78
|
-
if (!isValidDomain(domain)) {
|
|
79
|
-
throw new Error(`[MajikAPI] Invalid domain: "${domain}"`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function isValidISODate(value) {
|
|
83
|
-
const d = new Date(value);
|
|
84
|
-
return !isNaN(d.getTime());
|
|
85
|
-
}
|
|
86
|
-
// ─────────────────────────────────────────────
|
|
87
|
-
// Default Settings Factory
|
|
88
|
-
// ─────────────────────────────────────────────
|
|
89
|
-
function buildDefaultSettings(overrides) {
|
|
90
|
-
return {
|
|
91
|
-
rateLimit: { ...DEFAULT_RATE_LIMIT, ...(overrides?.rateLimit ?? {}) },
|
|
92
|
-
ipWhitelist: {
|
|
93
|
-
enabled: false,
|
|
94
|
-
addresses: [],
|
|
95
|
-
...(overrides?.ipWhitelist ?? {}),
|
|
96
|
-
},
|
|
97
|
-
domainWhitelist: {
|
|
98
|
-
enabled: false,
|
|
99
|
-
domains: [],
|
|
100
|
-
...(overrides?.domainWhitelist ?? {}),
|
|
101
|
-
},
|
|
102
|
-
allowedMethods: overrides?.allowedMethods ?? [],
|
|
103
|
-
metadata: overrides?.metadata ?? {},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
1
|
+
import { DEFAULT_RATE_LIMIT, MAX_RATE_LIMIT, TO_MINUTES } from "./constants";
|
|
2
|
+
import { assertBoolean, assertPositiveInteger, assertRateLimitFrequency, assertString, assertStringArray, buildDefaultSettings, generateID, isValidISODate, sha256, validateDomain, validateIP, } from "./utils";
|
|
106
3
|
// ─────────────────────────────────────────────
|
|
107
4
|
// MajikAPI Class
|
|
108
5
|
// ─────────────────────────────────────────────
|
|
@@ -262,9 +159,6 @@ export class MajikAPI {
|
|
|
262
159
|
assertString(this._owner_id, "owner_id");
|
|
263
160
|
assertString(this._name, "name");
|
|
264
161
|
assertString(this._api_key, "api_key");
|
|
265
|
-
if (!/^[a-f0-9]{64}$/.test(this._api_key)) {
|
|
266
|
-
throw new Error("[MajikAPI] validate(): 'api_key' does not appear to be a valid SHA-256 hash.");
|
|
267
|
-
}
|
|
268
162
|
if (!(this._timestamp instanceof Date) ||
|
|
269
163
|
isNaN(this._timestamp.getTime())) {
|
|
270
164
|
throw new TypeError("[MajikAPI] validate(): 'timestamp' is not a valid Date.");
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
import { MajikAPISettings, RateLimitFrequency } from "./types";
|
|
1
2
|
export declare function sha256(input: string): string;
|
|
2
3
|
export declare function arrayToBase64(data: Uint8Array): string;
|
|
3
4
|
/**
|
|
4
5
|
* Generate a Random v4 UUID
|
|
5
6
|
*/
|
|
6
7
|
export declare function generateID(): string;
|
|
8
|
+
export declare function assertString(value: unknown, label: string): asserts value is string;
|
|
9
|
+
export declare function assertPositiveInteger(value: unknown, label: string): asserts value is number;
|
|
10
|
+
export declare function assertRateLimitFrequency(value: unknown, label: string): asserts value is RateLimitFrequency;
|
|
11
|
+
export declare function assertBoolean(value: unknown, label: string): asserts value is boolean;
|
|
12
|
+
export declare function assertStringArray(value: unknown, label: string): asserts value is string[];
|
|
13
|
+
export declare function isValidIPv4(ip: string): boolean;
|
|
14
|
+
export declare function isValidIPv6(ip: string): boolean;
|
|
15
|
+
export declare function isValidCIDR(cidr: string): boolean;
|
|
16
|
+
export declare function validateIP(ip: string): void;
|
|
17
|
+
export declare function isValidDomain(domain: string): boolean;
|
|
18
|
+
export declare function validateDomain(domain: string): void;
|
|
19
|
+
export declare function isValidISODate(value: string): boolean;
|
|
20
|
+
export declare function buildDefaultSettings(overrides?: Partial<MajikAPISettings>): MajikAPISettings;
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hash } from "@stablelib/sha256";
|
|
2
2
|
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { DEFAULT_RATE_LIMIT } from "./constants";
|
|
3
4
|
export function sha256(input) {
|
|
4
5
|
const hashed = hash(new TextEncoder().encode(input));
|
|
5
6
|
return arrayToBase64(hashed);
|
|
@@ -25,3 +26,84 @@ export function generateID() {
|
|
|
25
26
|
throw new Error(`Failed to generate ID: ${error}`);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
// ─────────────────────────────────────────────
|
|
30
|
+
// Validation Helpers
|
|
31
|
+
// ─────────────────────────────────────────────
|
|
32
|
+
export function assertString(value, label) {
|
|
33
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
34
|
+
throw new TypeError(`[MajikAPI] "${label}" must be a non-empty string. Received: ${JSON.stringify(value)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function assertPositiveInteger(value, label) {
|
|
38
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
39
|
+
throw new RangeError(`[MajikAPI] "${label}" must be a positive integer. Received: ${JSON.stringify(value)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function assertRateLimitFrequency(value, label) {
|
|
43
|
+
const valid = ["seconds", "minutes", "hours"];
|
|
44
|
+
if (!valid.includes(value)) {
|
|
45
|
+
throw new TypeError(`[MajikAPI] "${label}" must be one of: ${valid.join(", ")}. Received: ${JSON.stringify(value)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function assertBoolean(value, label) {
|
|
49
|
+
if (typeof value !== "boolean") {
|
|
50
|
+
throw new TypeError(`[MajikAPI] "${label}" must be a boolean. Received: ${JSON.stringify(value)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function assertStringArray(value, label) {
|
|
54
|
+
if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
|
|
55
|
+
throw new TypeError(`[MajikAPI] "${label}" must be an array of strings. Received: ${JSON.stringify(value)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function isValidIPv4(ip) {
|
|
59
|
+
return (/^(\d{1,3}\.){3}\d{1,3}$/.test(ip) &&
|
|
60
|
+
ip.split(".").every((o) => parseInt(o) <= 255));
|
|
61
|
+
}
|
|
62
|
+
export function isValidIPv6(ip) {
|
|
63
|
+
return /^[0-9a-fA-F:]+$/.test(ip) && ip.includes(":");
|
|
64
|
+
}
|
|
65
|
+
export function isValidCIDR(cidr) {
|
|
66
|
+
const [ip, prefix] = cidr.split("/");
|
|
67
|
+
if (!prefix)
|
|
68
|
+
return false;
|
|
69
|
+
const p = parseInt(prefix);
|
|
70
|
+
return ((isValidIPv4(ip) && p >= 0 && p <= 32) ||
|
|
71
|
+
(isValidIPv6(ip) && p >= 0 && p <= 128));
|
|
72
|
+
}
|
|
73
|
+
export function validateIP(ip) {
|
|
74
|
+
if (!isValidIPv4(ip) && !isValidIPv6(ip) && !isValidCIDR(ip)) {
|
|
75
|
+
throw new Error(`[MajikAPI] Invalid IP address or CIDR: "${ip}"`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function isValidDomain(domain) {
|
|
79
|
+
return (/^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$/.test(domain) || /^\*$/.test(domain));
|
|
80
|
+
}
|
|
81
|
+
export function validateDomain(domain) {
|
|
82
|
+
if (!isValidDomain(domain)) {
|
|
83
|
+
throw new Error(`[MajikAPI] Invalid domain: "${domain}"`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function isValidISODate(value) {
|
|
87
|
+
const d = new Date(value);
|
|
88
|
+
return !isNaN(d.getTime());
|
|
89
|
+
}
|
|
90
|
+
// ─────────────────────────────────────────────
|
|
91
|
+
// Default Settings Factory
|
|
92
|
+
// ─────────────────────────────────────────────
|
|
93
|
+
export function buildDefaultSettings(overrides) {
|
|
94
|
+
return {
|
|
95
|
+
rateLimit: { ...DEFAULT_RATE_LIMIT, ...(overrides?.rateLimit ?? {}) },
|
|
96
|
+
ipWhitelist: {
|
|
97
|
+
enabled: false,
|
|
98
|
+
addresses: [],
|
|
99
|
+
...(overrides?.ipWhitelist ?? {}),
|
|
100
|
+
},
|
|
101
|
+
domainWhitelist: {
|
|
102
|
+
enabled: false,
|
|
103
|
+
domains: [],
|
|
104
|
+
...(overrides?.domainWhitelist ?? {}),
|
|
105
|
+
},
|
|
106
|
+
allowedMethods: overrides?.allowedMethods ?? [],
|
|
107
|
+
metadata: overrides?.metadata ?? {},
|
|
108
|
+
};
|
|
109
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@majikah/majik-api",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "A high-security API key management library for TypeScript. Handles generation, SHA-256 hashing, and lifecycle management including rate limiting, IP/domain whitelisting, and secure key rotation.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.1",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "Zelijah",
|
|
8
8
|
"main": "./dist/index.js",
|