@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.
@@ -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
+ };
@@ -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 { generateID, sha256 } from "./utils";
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.0",
5
+ "version": "0.1.1",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",