mysticapi 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/dist/index.mjs ADDED
@@ -0,0 +1,247 @@
1
+ // src/errors.ts
2
+ var MysticApiError = class _MysticApiError extends Error {
3
+ /** HTTP status code. `0` for a network/transport failure. */
4
+ status;
5
+ /** Machine-readable error code (from the body's `error` field, or a synthetic one). */
6
+ code;
7
+ /** Documentation URL surfaced by the API, when present. */
8
+ docs;
9
+ /** For `429`: the `Retry-After` value in seconds, when the header is present. */
10
+ retryAfter;
11
+ /** The parsed JSON error body, or raw text when the body was not JSON. */
12
+ body;
13
+ constructor(init) {
14
+ super(init.message);
15
+ this.name = "MysticApiError";
16
+ this.status = init.status;
17
+ this.code = init.code;
18
+ this.docs = init.docs;
19
+ this.retryAfter = init.retryAfter;
20
+ this.body = init.body;
21
+ Object.setPrototypeOf(this, new.target.prototype);
22
+ }
23
+ /** True for a `401` (missing/invalid API key). */
24
+ get isUnauthorized() {
25
+ return this.status === 401;
26
+ }
27
+ /** True for a `402` (payment required — no key and no valid x402 payment). */
28
+ get isPaymentRequired() {
29
+ return this.status === 402;
30
+ }
31
+ /** True for a `429` (monthly allotment reached or rate limited). */
32
+ get isRateLimited() {
33
+ return this.status === 429;
34
+ }
35
+ /**
36
+ * Build a {@link MysticApiError} from a failed {@link Response}, parsing the
37
+ * JSON error envelope and the `Retry-After` header.
38
+ */
39
+ static async fromResponse(res, path) {
40
+ const raw = await res.text().catch(() => "");
41
+ let body;
42
+ if (raw) {
43
+ try {
44
+ body = JSON.parse(raw);
45
+ } catch {
46
+ body = raw;
47
+ }
48
+ }
49
+ const envelope = typeof body === "object" && body !== null ? body : void 0;
50
+ const code = typeof envelope?.error === "string" && envelope.error ? envelope.error : `http_${res.status}`;
51
+ const message = typeof envelope?.message === "string" && envelope.message ? envelope.message : `MysticAPI request to ${path} failed with status ${res.status}`;
52
+ const docs = typeof envelope?.docs === "string" ? envelope.docs : void 0;
53
+ let retryAfter;
54
+ const retryHeader = res.headers.get("retry-after");
55
+ if (retryHeader) {
56
+ const seconds = Number(retryHeader);
57
+ if (Number.isFinite(seconds)) {
58
+ retryAfter = seconds;
59
+ } else {
60
+ const when = Date.parse(retryHeader);
61
+ if (Number.isFinite(when)) {
62
+ retryAfter = Math.max(0, Math.round((when - Date.now()) / 1e3));
63
+ }
64
+ }
65
+ }
66
+ return new _MysticApiError({ status: res.status, code, message, docs, retryAfter, body });
67
+ }
68
+ };
69
+
70
+ // src/client.ts
71
+ var DEFAULT_BASE_URL = "https://mysticapi.com";
72
+ function resolveFetch(injected) {
73
+ const impl = injected ?? globalThis.fetch;
74
+ if (typeof impl !== "function") {
75
+ throw new MysticApiError({
76
+ status: 0,
77
+ code: "no_fetch",
78
+ message: "No global `fetch` found. Pass a `fetch` implementation in the options, or run on Node 18+, Bun, a browser, or Cloudflare Workers."
79
+ });
80
+ }
81
+ return impl;
82
+ }
83
+ function normalizeBaseUrl(baseUrl) {
84
+ return (baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
85
+ }
86
+ function birthQuery(birth, options) {
87
+ const params = new URLSearchParams({
88
+ year: String(birth.year),
89
+ month: String(birth.month),
90
+ day: String(birth.day),
91
+ hour: String(birth.hour),
92
+ minute: String(birth.minute)
93
+ });
94
+ if (birth.second !== void 0) params.set("second", String(birth.second));
95
+ if (options?.animate !== void 0) params.set("animate", options.animate ? "true" : "false");
96
+ return params.toString();
97
+ }
98
+ async function requestRaw(fetchImpl, url, init, path) {
99
+ let res;
100
+ try {
101
+ res = await fetchImpl(url, init);
102
+ } catch (cause) {
103
+ const detail = cause instanceof Error ? cause.message : "network error";
104
+ throw new MysticApiError({
105
+ status: 0,
106
+ code: "network_error",
107
+ message: `MysticAPI request to ${path} failed: ${detail}`
108
+ });
109
+ }
110
+ if (!res.ok) {
111
+ throw await MysticApiError.fromResponse(res, path);
112
+ }
113
+ return res;
114
+ }
115
+ var MysticApi = class {
116
+ apiKey;
117
+ baseUrl;
118
+ fetchImpl;
119
+ constructor(options) {
120
+ if (!options || !options.apiKey) {
121
+ throw new MysticApiError({
122
+ status: 0,
123
+ code: "missing_api_key",
124
+ message: "A MysticAPI `apiKey` is required. Get a free one with MysticApi.getFreeKey(email)."
125
+ });
126
+ }
127
+ this.apiKey = options.apiKey;
128
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
129
+ this.fetchImpl = resolveFetch(options.fetch);
130
+ }
131
+ authHeaders(extra) {
132
+ const headers = new Headers(extra);
133
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
134
+ return headers;
135
+ }
136
+ /**
137
+ * Today's universal sky: active retrogrades, moon phase + illumination,
138
+ * geocentric positions for 13 bodies (zodiac + Energy Blueprint gate/line),
139
+ * and a 30-day forward scan of retrograde stations and new/full moons.
140
+ *
141
+ * `GET /v1/sky/today` — metered (1 call).
142
+ */
143
+ async skyToday() {
144
+ const res = await requestRaw(
145
+ this.fetchImpl,
146
+ `${this.baseUrl}/v1/sky/today`,
147
+ { method: "GET", headers: this.authHeaders({ Accept: "application/json" }) },
148
+ "/v1/sky/today"
149
+ );
150
+ return await res.json();
151
+ }
152
+ /**
153
+ * Compute an Energy Blueprint (Human Design) chart from a birth instant and
154
+ * return the full chart JSON plus a rendered body-graph SVG. Location is not
155
+ * required — the body-graph is location-independent.
156
+ *
157
+ * `POST /v1/bodygraph` — metered (1 call).
158
+ */
159
+ async bodygraph(birth) {
160
+ const res = await requestRaw(
161
+ this.fetchImpl,
162
+ `${this.baseUrl}/v1/bodygraph`,
163
+ {
164
+ method: "POST",
165
+ headers: this.authHeaders({ "Content-Type": "application/json", Accept: "application/json" }),
166
+ body: JSON.stringify(birth)
167
+ },
168
+ "/v1/bodygraph"
169
+ );
170
+ return await res.json();
171
+ }
172
+ /**
173
+ * Render a personal-sky chart from a birth instant. Returns the SVG markup as
174
+ * a string (`image/svg+xml`). Deterministic: the same birth always yields the
175
+ * same sky.
176
+ *
177
+ * `POST /v1/sky/personal` — metered (1 call).
178
+ */
179
+ async skyPersonal(birth, options) {
180
+ const body = {
181
+ year: birth.year,
182
+ month: birth.month,
183
+ day: birth.day,
184
+ hour: birth.hour,
185
+ minute: birth.minute
186
+ };
187
+ if (birth.second !== void 0) body.second = birth.second;
188
+ const query = options?.animate !== void 0 ? `?animate=${options.animate ? "true" : "false"}` : "";
189
+ const res = await requestRaw(
190
+ this.fetchImpl,
191
+ `${this.baseUrl}/v1/sky/personal${query}`,
192
+ {
193
+ method: "POST",
194
+ headers: this.authHeaders({ "Content-Type": "application/json", Accept: "image/svg+xml" }),
195
+ body: JSON.stringify(body)
196
+ },
197
+ "/v1/sky/personal"
198
+ );
199
+ return await res.text();
200
+ }
201
+ /**
202
+ * Render a personal-sky chart via `GET /v1/sky/personal` (birth as query
203
+ * params). Equivalent to {@link MysticApi.skyPersonal}; useful when you want a
204
+ * shareable URL shape. Returns the SVG markup as a string.
205
+ */
206
+ async skyPersonalGet(birth, options) {
207
+ const res = await requestRaw(
208
+ this.fetchImpl,
209
+ `${this.baseUrl}/v1/sky/personal?${birthQuery(birth, options)}`,
210
+ { method: "GET", headers: this.authHeaders({ Accept: "image/svg+xml" }) },
211
+ "/v1/sky/personal"
212
+ );
213
+ return await res.text();
214
+ }
215
+ /**
216
+ * Request a free, no-card API key. The key is emailed (the API responds `202`
217
+ * with a confirmation message — the secret itself is never returned in the
218
+ * body). One active key per email; 50 calls/month.
219
+ *
220
+ * `POST /v1/keys/free` — no auth required.
221
+ */
222
+ static async getFreeKey(email, options) {
223
+ return getFreeKey(email, options);
224
+ }
225
+ };
226
+ async function getFreeKey(email, options) {
227
+ const fetchImpl = resolveFetch(options?.fetch);
228
+ const baseUrl = normalizeBaseUrl(options?.baseUrl);
229
+ const res = await requestRaw(
230
+ fetchImpl,
231
+ `${baseUrl}/v1/keys/free`,
232
+ {
233
+ method: "POST",
234
+ headers: new Headers({ "Content-Type": "application/json", Accept: "application/json" }),
235
+ body: JSON.stringify({ email })
236
+ },
237
+ "/v1/keys/free"
238
+ );
239
+ return await res.json();
240
+ }
241
+ export {
242
+ DEFAULT_BASE_URL,
243
+ MysticApi,
244
+ MysticApiError,
245
+ getFreeKey
246
+ };
247
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["import type { ApiErrorBody } from './types.js';\n\n/** Fields used to construct a {@link MysticApiError}. */\nexport interface MysticApiErrorInit {\n status: number;\n code: string;\n message: string;\n docs?: string;\n retryAfter?: number;\n body?: ApiErrorBody | string;\n}\n\n/**\n * Error thrown for any non-2xx MysticAPI response (and for network failures,\n * which surface as `status: 0`, `code: 'network_error'`).\n *\n * The parsed JSON error envelope (`{ error, message, docs }`) is exposed on\n * {@link MysticApiError.body}, the machine-readable code on\n * {@link MysticApiError.code}, and — for `429` — the `Retry-After` value in\n * seconds on {@link MysticApiError.retryAfter}.\n */\nexport class MysticApiError extends Error {\n /** HTTP status code. `0` for a network/transport failure. */\n readonly status: number;\n /** Machine-readable error code (from the body's `error` field, or a synthetic one). */\n readonly code: string;\n /** Documentation URL surfaced by the API, when present. */\n readonly docs?: string;\n /** For `429`: the `Retry-After` value in seconds, when the header is present. */\n readonly retryAfter?: number;\n /** The parsed JSON error body, or raw text when the body was not JSON. */\n readonly body?: ApiErrorBody | string;\n\n constructor(init: MysticApiErrorInit) {\n super(init.message);\n this.name = 'MysticApiError';\n this.status = init.status;\n this.code = init.code;\n this.docs = init.docs;\n this.retryAfter = init.retryAfter;\n this.body = init.body;\n // Restore the prototype chain so `instanceof MysticApiError` holds after\n // transpilation to older targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n /** True for a `401` (missing/invalid API key). */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** True for a `402` (payment required — no key and no valid x402 payment). */\n get isPaymentRequired(): boolean {\n return this.status === 402;\n }\n\n /** True for a `429` (monthly allotment reached or rate limited). */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /**\n * Build a {@link MysticApiError} from a failed {@link Response}, parsing the\n * JSON error envelope and the `Retry-After` header.\n */\n static async fromResponse(res: Response, path: string): Promise<MysticApiError> {\n const raw = await res.text().catch(() => '');\n let body: ApiErrorBody | string | undefined;\n if (raw) {\n try {\n body = JSON.parse(raw) as ApiErrorBody;\n } catch {\n body = raw;\n }\n }\n\n const envelope = typeof body === 'object' && body !== null ? body : undefined;\n const code =\n typeof envelope?.error === 'string' && envelope.error ? envelope.error : `http_${res.status}`;\n const message =\n typeof envelope?.message === 'string' && envelope.message\n ? envelope.message\n : `MysticAPI request to ${path} failed with status ${res.status}`;\n const docs = typeof envelope?.docs === 'string' ? envelope.docs : undefined;\n\n let retryAfter: number | undefined;\n const retryHeader = res.headers.get('retry-after');\n if (retryHeader) {\n const seconds = Number(retryHeader);\n if (Number.isFinite(seconds)) {\n retryAfter = seconds;\n } else {\n const when = Date.parse(retryHeader);\n if (Number.isFinite(when)) {\n retryAfter = Math.max(0, Math.round((when - Date.now()) / 1000));\n }\n }\n }\n\n return new MysticApiError({ status: res.status, code, message, docs, retryAfter, body });\n }\n}\n","import { MysticApiError } from './errors.js';\nimport type {\n BirthInput,\n BodygraphResult,\n FreeKeyResponse,\n KeylessOptions,\n MysticApiOptions,\n SkyPersonalOptions,\n SkyToday,\n} from './types.js';\n\n/** Default MysticAPI base URL. */\nexport const DEFAULT_BASE_URL = 'https://mysticapi.com';\n\nfunction resolveFetch(injected?: typeof globalThis.fetch): typeof globalThis.fetch {\n const impl = injected ?? globalThis.fetch;\n if (typeof impl !== 'function') {\n throw new MysticApiError({\n status: 0,\n code: 'no_fetch',\n message:\n 'No global `fetch` found. Pass a `fetch` implementation in the options, or run on Node 18+, Bun, a browser, or Cloudflare Workers.',\n });\n }\n return impl;\n}\n\nfunction normalizeBaseUrl(baseUrl?: string): string {\n return (baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n}\n\nfunction birthQuery(birth: BirthInput, options?: SkyPersonalOptions): string {\n const params = new URLSearchParams({\n year: String(birth.year),\n month: String(birth.month),\n day: String(birth.day),\n hour: String(birth.hour),\n minute: String(birth.minute),\n });\n if (birth.second !== undefined) params.set('second', String(birth.second));\n if (options?.animate !== undefined) params.set('animate', options.animate ? 'true' : 'false');\n return params.toString();\n}\n\nasync function requestRaw(\n fetchImpl: typeof globalThis.fetch,\n url: string,\n init: RequestInit,\n path: string,\n): Promise<Response> {\n let res: Response;\n try {\n res = await fetchImpl(url, init);\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : 'network error';\n throw new MysticApiError({\n status: 0,\n code: 'network_error',\n message: `MysticAPI request to ${path} failed: ${detail}`,\n });\n }\n if (!res.ok) {\n throw await MysticApiError.fromResponse(res, path);\n }\n return res;\n}\n\n/**\n * Isomorphic MysticAPI client. Zero dependencies — uses the runtime's global\n * `fetch`, so it runs unchanged on Node 18+, Bun, browsers, and Cloudflare\n * Workers.\n *\n * @example\n * ```ts\n * const mystic = new MysticApi({ apiKey: 'mk_live_...' });\n * const sky = await mystic.skyToday();\n * console.log(sky.moon.phaseName);\n * ```\n */\nexport class MysticApi {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n\n constructor(options: MysticApiOptions) {\n if (!options || !options.apiKey) {\n throw new MysticApiError({\n status: 0,\n code: 'missing_api_key',\n message: 'A MysticAPI `apiKey` is required. Get a free one with MysticApi.getFreeKey(email).',\n });\n }\n this.apiKey = options.apiKey;\n this.baseUrl = normalizeBaseUrl(options.baseUrl);\n this.fetchImpl = resolveFetch(options.fetch);\n }\n\n private authHeaders(extra?: Record<string, string>): Headers {\n const headers = new Headers(extra);\n headers.set('Authorization', `Bearer ${this.apiKey}`);\n return headers;\n }\n\n /**\n * Today's universal sky: active retrogrades, moon phase + illumination,\n * geocentric positions for 13 bodies (zodiac + Energy Blueprint gate/line),\n * and a 30-day forward scan of retrograde stations and new/full moons.\n *\n * `GET /v1/sky/today` — metered (1 call).\n */\n async skyToday(): Promise<SkyToday> {\n const res = await requestRaw(\n this.fetchImpl,\n `${this.baseUrl}/v1/sky/today`,\n { method: 'GET', headers: this.authHeaders({ Accept: 'application/json' }) },\n '/v1/sky/today',\n );\n return (await res.json()) as SkyToday;\n }\n\n /**\n * Compute an Energy Blueprint (Human Design) chart from a birth instant and\n * return the full chart JSON plus a rendered body-graph SVG. Location is not\n * required — the body-graph is location-independent.\n *\n * `POST /v1/bodygraph` — metered (1 call).\n */\n async bodygraph(birth: BirthInput): Promise<BodygraphResult> {\n const res = await requestRaw(\n this.fetchImpl,\n `${this.baseUrl}/v1/bodygraph`,\n {\n method: 'POST',\n headers: this.authHeaders({ 'Content-Type': 'application/json', Accept: 'application/json' }),\n body: JSON.stringify(birth),\n },\n '/v1/bodygraph',\n );\n return (await res.json()) as BodygraphResult;\n }\n\n /**\n * Render a personal-sky chart from a birth instant. Returns the SVG markup as\n * a string (`image/svg+xml`). Deterministic: the same birth always yields the\n * same sky.\n *\n * `POST /v1/sky/personal` — metered (1 call).\n */\n async skyPersonal(birth: BirthInput, options?: SkyPersonalOptions): Promise<string> {\n const body: Record<string, number> = {\n year: birth.year,\n month: birth.month,\n day: birth.day,\n hour: birth.hour,\n minute: birth.minute,\n };\n if (birth.second !== undefined) body.second = birth.second;\n const query = options?.animate !== undefined ? `?animate=${options.animate ? 'true' : 'false'}` : '';\n const res = await requestRaw(\n this.fetchImpl,\n `${this.baseUrl}/v1/sky/personal${query}`,\n {\n method: 'POST',\n headers: this.authHeaders({ 'Content-Type': 'application/json', Accept: 'image/svg+xml' }),\n body: JSON.stringify(body),\n },\n '/v1/sky/personal',\n );\n return await res.text();\n }\n\n /**\n * Render a personal-sky chart via `GET /v1/sky/personal` (birth as query\n * params). Equivalent to {@link MysticApi.skyPersonal}; useful when you want a\n * shareable URL shape. Returns the SVG markup as a string.\n */\n async skyPersonalGet(birth: BirthInput, options?: SkyPersonalOptions): Promise<string> {\n const res = await requestRaw(\n this.fetchImpl,\n `${this.baseUrl}/v1/sky/personal?${birthQuery(birth, options)}`,\n { method: 'GET', headers: this.authHeaders({ Accept: 'image/svg+xml' }) },\n '/v1/sky/personal',\n );\n return await res.text();\n }\n\n /**\n * Request a free, no-card API key. The key is emailed (the API responds `202`\n * with a confirmation message — the secret itself is never returned in the\n * body). One active key per email; 50 calls/month.\n *\n * `POST /v1/keys/free` — no auth required.\n */\n static async getFreeKey(email: string, options?: KeylessOptions): Promise<FreeKeyResponse> {\n return getFreeKey(email, options);\n }\n}\n\n/**\n * Standalone helper to request a free, no-card API key (emailed to `email`).\n * Same as {@link MysticApi.getFreeKey}; exported for keyless call sites.\n *\n * `POST /v1/keys/free` — no auth required.\n */\nexport async function getFreeKey(email: string, options?: KeylessOptions): Promise<FreeKeyResponse> {\n const fetchImpl = resolveFetch(options?.fetch);\n const baseUrl = normalizeBaseUrl(options?.baseUrl);\n const res = await requestRaw(\n fetchImpl,\n `${baseUrl}/v1/keys/free`,\n {\n method: 'POST',\n headers: new Headers({ 'Content-Type': 'application/json', Accept: 'application/json' }),\n body: JSON.stringify({ email }),\n },\n '/v1/keys/free',\n );\n return (await res.json()) as FreeKeyResponse;\n}\n"],"mappings":";AAqBO,IAAM,iBAAN,MAAM,wBAAuB,MAAM;AAAA;AAAA,EAE/B;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,MAA0B;AACpC,UAAM,KAAK,OAAO;AAClB,SAAK,OAAO;AACZ,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO,KAAK;AACjB,SAAK,OAAO,KAAK;AACjB,SAAK,aAAa,KAAK;AACvB,SAAK,OAAO,KAAK;AAGjB,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AAAA;AAAA,EAGA,IAAI,iBAA0B;AAC5B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,oBAA6B;AAC/B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,gBAAyB;AAC3B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,aAAa,KAAe,MAAuC;AAC9E,UAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC3C,QAAI;AACJ,QAAI,KAAK;AACP,UAAI;AACF,eAAO,KAAK,MAAM,GAAG;AAAA,MACvB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,SAAS,YAAY,SAAS,OAAO,OAAO;AACpE,UAAM,OACJ,OAAO,UAAU,UAAU,YAAY,SAAS,QAAQ,SAAS,QAAQ,QAAQ,IAAI,MAAM;AAC7F,UAAM,UACJ,OAAO,UAAU,YAAY,YAAY,SAAS,UAC9C,SAAS,UACT,wBAAwB,IAAI,uBAAuB,IAAI,MAAM;AACnE,UAAM,OAAO,OAAO,UAAU,SAAS,WAAW,SAAS,OAAO;AAElE,QAAI;AACJ,UAAM,cAAc,IAAI,QAAQ,IAAI,aAAa;AACjD,QAAI,aAAa;AACf,YAAM,UAAU,OAAO,WAAW;AAClC,UAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,qBAAa;AAAA,MACf,OAAO;AACL,cAAM,OAAO,KAAK,MAAM,WAAW;AACnC,YAAI,OAAO,SAAS,IAAI,GAAG;AACzB,uBAAa,KAAK,IAAI,GAAG,KAAK,OAAO,OAAO,KAAK,IAAI,KAAK,GAAI,CAAC;AAAA,QACjE;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI,gBAAe,EAAE,QAAQ,IAAI,QAAQ,MAAM,SAAS,MAAM,YAAY,KAAK,CAAC;AAAA,EACzF;AACF;;;ACzFO,IAAM,mBAAmB;AAEhC,SAAS,aAAa,UAA6D;AACjF,QAAM,OAAO,YAAY,WAAW;AACpC,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI,eAAe;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAA0B;AAClD,UAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACzD;AAEA,SAAS,WAAW,OAAmB,SAAsC;AAC3E,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,MAAM,OAAO,MAAM,IAAI;AAAA,IACvB,OAAO,OAAO,MAAM,KAAK;AAAA,IACzB,KAAK,OAAO,MAAM,GAAG;AAAA,IACrB,MAAM,OAAO,MAAM,IAAI;AAAA,IACvB,QAAQ,OAAO,MAAM,MAAM;AAAA,EAC7B,CAAC;AACD,MAAI,MAAM,WAAW,OAAW,QAAO,IAAI,UAAU,OAAO,MAAM,MAAM,CAAC;AACzE,MAAI,SAAS,YAAY,OAAW,QAAO,IAAI,WAAW,QAAQ,UAAU,SAAS,OAAO;AAC5F,SAAO,OAAO,SAAS;AACzB;AAEA,eAAe,WACb,WACA,KACA,MACA,MACmB;AACnB,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,UAAU,KAAK,IAAI;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AACxD,UAAM,IAAI,eAAe;AAAA,MACvB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,wBAAwB,IAAI,YAAY,MAAM;AAAA,IACzD,CAAC;AAAA,EACH;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAM,eAAe,aAAa,KAAK,IAAI;AAAA,EACnD;AACA,SAAO;AACT;AAcO,IAAM,YAAN,MAAgB;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA2B;AACrC,QAAI,CAAC,WAAW,CAAC,QAAQ,QAAQ;AAC/B,YAAM,IAAI,eAAe;AAAA,QACvB,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU,iBAAiB,QAAQ,OAAO;AAC/C,SAAK,YAAY,aAAa,QAAQ,KAAK;AAAA,EAC7C;AAAA,EAEQ,YAAY,OAAyC;AAC3D,UAAM,UAAU,IAAI,QAAQ,KAAK;AACjC,YAAQ,IAAI,iBAAiB,UAAU,KAAK,MAAM,EAAE;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAA8B;AAClC,UAAM,MAAM,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf,EAAE,QAAQ,OAAO,SAAS,KAAK,YAAY,EAAE,QAAQ,mBAAmB,CAAC,EAAE;AAAA,MAC3E;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAU,OAA6C;AAC3D,UAAM,MAAM,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,KAAK,YAAY,EAAE,gBAAgB,oBAAoB,QAAQ,mBAAmB,CAAC;AAAA,QAC5F,MAAM,KAAK,UAAU,KAAK;AAAA,MAC5B;AAAA,MACA;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAY,OAAmB,SAA+C;AAClF,UAAM,OAA+B;AAAA,MACnC,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,KAAK,MAAM;AAAA,MACX,MAAM,MAAM;AAAA,MACZ,QAAQ,MAAM;AAAA,IAChB;AACA,QAAI,MAAM,WAAW,OAAW,MAAK,SAAS,MAAM;AACpD,UAAM,QAAQ,SAAS,YAAY,SAAY,YAAY,QAAQ,UAAU,SAAS,OAAO,KAAK;AAClG,UAAM,MAAM,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,GAAG,KAAK,OAAO,mBAAmB,KAAK;AAAA,MACvC;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,KAAK,YAAY,EAAE,gBAAgB,oBAAoB,QAAQ,gBAAgB,CAAC;AAAA,QACzF,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,OAAmB,SAA+C;AACrF,UAAM,MAAM,MAAM;AAAA,MAChB,KAAK;AAAA,MACL,GAAG,KAAK,OAAO,oBAAoB,WAAW,OAAO,OAAO,CAAC;AAAA,MAC7D,EAAE,QAAQ,OAAO,SAAS,KAAK,YAAY,EAAE,QAAQ,gBAAgB,CAAC,EAAE;AAAA,MACxE;AAAA,IACF;AACA,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,WAAW,OAAe,SAAoD;AACzF,WAAO,WAAW,OAAO,OAAO;AAAA,EAClC;AACF;AAQA,eAAsB,WAAW,OAAe,SAAoD;AAClG,QAAM,YAAY,aAAa,SAAS,KAAK;AAC7C,QAAM,UAAU,iBAAiB,SAAS,OAAO;AACjD,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,GAAG,OAAO;AAAA,IACV;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,IAAI,QAAQ,EAAE,gBAAgB,oBAAoB,QAAQ,mBAAmB,CAAC;AAAA,MACvF,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,IAChC;AAAA,IACA;AAAA,EACF;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;","names":[]}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "mysticapi",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Official isomorphic TypeScript/JavaScript client for MysticAPI — deterministic Human Design–compatible bodygraphs, personal sky charts, and ephemeris. Zero dependencies; runs on Node, Bun, browsers, and Cloudflare Workers.",
6
+ "keywords": [
7
+ "mysticapi",
8
+ "human-design",
9
+ "energy-blueprint",
10
+ "bodygraph",
11
+ "astrology",
12
+ "ephemeris",
13
+ "sky",
14
+ "moon-phase",
15
+ "retrograde",
16
+ "api-client",
17
+ "sdk",
18
+ "isomorphic",
19
+ "cloudflare-workers"
20
+ ],
21
+ "license": "MIT",
22
+ "author": "Latimer Woods Tech",
23
+ "homepage": "https://mysticapi.com",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/Latimer-Woods-Tech/Factory.git",
27
+ "directory": "packages/mysticapi"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/Latimer-Woods-Tech/Factory/issues"
31
+ },
32
+ "main": "./dist/index.js",
33
+ "module": "./dist/index.mjs",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "import": {
38
+ "types": "./dist/index.d.mts",
39
+ "default": "./dist/index.mjs"
40
+ },
41
+ "require": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ }
45
+ }
46
+ },
47
+ "sideEffects": false,
48
+ "files": [
49
+ "dist",
50
+ "README.md",
51
+ "CHANGELOG.md",
52
+ "LICENSE"
53
+ ],
54
+ "engines": {
55
+ "node": ">=18"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup src/index.ts --format esm,cjs --dts",
59
+ "test": "vitest run --coverage",
60
+ "lint": "eslint src --max-warnings 0",
61
+ "typecheck": "tsc --noEmit",
62
+ "prepublish": "npm run lint && npm run typecheck && npm run test && npm run build"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "^25.6.0",
66
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
67
+ "@typescript-eslint/parser": "^7.0.0",
68
+ "@vitest/coverage-v8": "^4.1.8",
69
+ "eslint": "^8.57.0",
70
+ "tsup": "^8.1.0",
71
+ "typescript": "^5.4.0",
72
+ "vitest": "^4.1.8"
73
+ }
74
+ }