@astroway/sdk 0.1.0-alpha.1 → 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/CHANGELOG.md +350 -0
- package/README.md +59 -31
- package/dist/cache.d.ts +101 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +198 -0
- package/dist/cache.js.map +1 -0
- package/dist/errors.d.ts +30 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +45 -14
- package/dist/errors.js.map +1 -1
- package/dist/helpers/birth-date-time.d.ts +76 -0
- package/dist/helpers/birth-date-time.d.ts.map +1 -0
- package/dist/helpers/birth-date-time.js +112 -0
- package/dist/helpers/birth-date-time.js.map +1 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/idempotency.d.ts +10 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +41 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +97 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +191 -3
- package/dist/index.js.map +1 -1
- package/dist/namespaces.generated.d.ts +1513 -0
- package/dist/namespaces.generated.d.ts.map +1 -0
- package/dist/namespaces.generated.js +865 -0
- package/dist/namespaces.generated.js.map +1 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +31 -0
- package/dist/runtime.js.map +1 -0
- package/dist/stream.d.ts +71 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +199 -0
- package/dist/stream.js.map +1 -0
- package/dist/testing.d.ts +133 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +132 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.generated.d.ts +784 -176
- package/dist/types.generated.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/with-response.d.ts +36 -0
- package/dist/with-response.d.ts.map +1 -0
- package/dist/with-response.js +38 -0
- package/dist/with-response.js.map +1 -0
- package/package.json +19 -3
package/dist/cache.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic response cache.
|
|
3
|
+
*
|
|
4
|
+
* Charts are pure functions of `(date, time, lat, lon, tz)`. Caching them
|
|
5
|
+
* client-side saves credits and makes dev loops instant. None of the public
|
|
6
|
+
* astrology APIs do this — pure differentiator vs Prokerala / Astrologer.
|
|
7
|
+
*
|
|
8
|
+
* ## Storage
|
|
9
|
+
*
|
|
10
|
+
* Pluggable via the {@link CacheStore} interface:
|
|
11
|
+
* - {@link MemoryStore}: in-process Map (default when `cache: 'memory'`)
|
|
12
|
+
* - {@link LocalStorageStore}: browser/edge `Storage` adapter
|
|
13
|
+
* - bring-your-own: pass any object satisfying `CacheStore` (Redis, IndexedDB, etc.)
|
|
14
|
+
*
|
|
15
|
+
* ## Policy
|
|
16
|
+
*
|
|
17
|
+
* Two lists baked into the SDK (override per-call via `{cache: true|false}`):
|
|
18
|
+
*
|
|
19
|
+
* - {@link DETERMINISTIC_PATH_PREFIXES} — pure functions (cached by default)
|
|
20
|
+
* - {@link NON_DETERMINISTIC_PATH_PREFIXES} — time-sensitive (skipped by default)
|
|
21
|
+
*
|
|
22
|
+
* Unknown endpoints are skipped by default. Force per-call when known safe.
|
|
23
|
+
*
|
|
24
|
+
* ## Key
|
|
25
|
+
*
|
|
26
|
+
* `astroway_v1_<sha256(canonical-json(method, path, body))>` — order-insensitive
|
|
27
|
+
* on object keys, order-preserving on lists.
|
|
28
|
+
*/
|
|
29
|
+
const VERSION_PATH_RE = /^\/v\d+(\/.*)?$/;
|
|
30
|
+
export const CACHE_KEY_PREFIX = 'astroway_v1_';
|
|
31
|
+
export const DETERMINISTIC_PATH_PREFIXES = [
|
|
32
|
+
'/chart',
|
|
33
|
+
'/synastry',
|
|
34
|
+
'/composite',
|
|
35
|
+
'/midpoints',
|
|
36
|
+
'/aspects',
|
|
37
|
+
'/houses',
|
|
38
|
+
'/planets',
|
|
39
|
+
'/vedic/',
|
|
40
|
+
'/numerology/',
|
|
41
|
+
'/tarot/',
|
|
42
|
+
'/hd/',
|
|
43
|
+
'/human-design/',
|
|
44
|
+
'/dasha/',
|
|
45
|
+
];
|
|
46
|
+
export const NON_DETERMINISTIC_PATH_PREFIXES = [
|
|
47
|
+
'/transits',
|
|
48
|
+
'/horoscope',
|
|
49
|
+
'/interpret',
|
|
50
|
+
'/ai/',
|
|
51
|
+
'/mcp/',
|
|
52
|
+
'/stream/',
|
|
53
|
+
'/now',
|
|
54
|
+
'/today',
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Whether `path` is safe to cache by default. The denylist wins over the
|
|
58
|
+
* allowlist — `/horoscope/daily` is never cached even if `/horoscope` is on
|
|
59
|
+
* a custom allowlist.
|
|
60
|
+
*/
|
|
61
|
+
export function isDeterministicPath(path) {
|
|
62
|
+
const normalised = stripVersionPrefix(path);
|
|
63
|
+
for (const prefix of NON_DETERMINISTIC_PATH_PREFIXES) {
|
|
64
|
+
if (normalised.startsWith(prefix))
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
for (const prefix of DETERMINISTIC_PATH_PREFIXES) {
|
|
68
|
+
if (normalised.startsWith(prefix))
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function stripVersionPrefix(path) {
|
|
74
|
+
const m = path.match(VERSION_PATH_RE);
|
|
75
|
+
if (m)
|
|
76
|
+
return m[1] ?? '/';
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Recursively sort object keys; preserve list order. After canonicalisation,
|
|
81
|
+
* two requests with the same logical body but different field order produce
|
|
82
|
+
* identical JSON.
|
|
83
|
+
*/
|
|
84
|
+
export function canonicalise(value) {
|
|
85
|
+
if (Array.isArray(value))
|
|
86
|
+
return value.map(canonicalise);
|
|
87
|
+
if (value !== null && typeof value === 'object') {
|
|
88
|
+
const obj = value;
|
|
89
|
+
const sorted = {};
|
|
90
|
+
for (const k of Object.keys(obj).sort())
|
|
91
|
+
sorted[k] = canonicalise(obj[k]);
|
|
92
|
+
return sorted;
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* SHA-256 via Web Crypto. Available in every modern runtime — Node 20+, Deno,
|
|
98
|
+
* Bun, browsers, Cloudflare Workers, Vercel Edge.
|
|
99
|
+
*/
|
|
100
|
+
async function sha256Hex(input) {
|
|
101
|
+
const enc = new TextEncoder().encode(input);
|
|
102
|
+
const buf = await globalThis.crypto.subtle.digest('SHA-256', enc);
|
|
103
|
+
const bytes = new Uint8Array(buf);
|
|
104
|
+
let hex = '';
|
|
105
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
106
|
+
hex += bytes[i].toString(16).padStart(2, '0');
|
|
107
|
+
}
|
|
108
|
+
return hex;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build a cache key for a request. Two semantically-equivalent calls produce
|
|
112
|
+
* the same key — `{ date, lat }` and `{ lat, date }` collide, by design.
|
|
113
|
+
*/
|
|
114
|
+
export async function buildCacheKey(method, path, body) {
|
|
115
|
+
const canonical = canonicalise({ m: method.toUpperCase(), p: path, b: body ?? null });
|
|
116
|
+
const json = JSON.stringify(canonical);
|
|
117
|
+
return CACHE_KEY_PREFIX + (await sha256Hex(json));
|
|
118
|
+
}
|
|
119
|
+
/** In-process `Map`-backed store. Use this for tests and short-lived processes. */
|
|
120
|
+
export class MemoryStore {
|
|
121
|
+
map = new Map();
|
|
122
|
+
get(key) {
|
|
123
|
+
return this.map.get(key) ?? null;
|
|
124
|
+
}
|
|
125
|
+
set(key, entry) {
|
|
126
|
+
this.map.set(key, entry);
|
|
127
|
+
}
|
|
128
|
+
delete(key) {
|
|
129
|
+
this.map.delete(key);
|
|
130
|
+
}
|
|
131
|
+
/** Discard all entries — useful in test teardown. */
|
|
132
|
+
clear() {
|
|
133
|
+
this.map.clear();
|
|
134
|
+
}
|
|
135
|
+
/** Number of cached entries (regardless of expiry). */
|
|
136
|
+
get size() {
|
|
137
|
+
return this.map.size;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Browser / edge-runtime `Storage` adapter. Falls back to no-op on
|
|
142
|
+
* quota / private-mode errors.
|
|
143
|
+
*/
|
|
144
|
+
export class LocalStorageStore {
|
|
145
|
+
storage;
|
|
146
|
+
constructor(storage = globalThis.localStorage) {
|
|
147
|
+
this.storage = storage;
|
|
148
|
+
}
|
|
149
|
+
get(key) {
|
|
150
|
+
try {
|
|
151
|
+
const raw = this.storage.getItem(key);
|
|
152
|
+
if (raw === null)
|
|
153
|
+
return null;
|
|
154
|
+
return JSON.parse(raw);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
set(key, entry) {
|
|
161
|
+
try {
|
|
162
|
+
this.storage.setItem(key, JSON.stringify(entry));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Quota exceeded / private browsing — silently drop.
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
delete(key) {
|
|
169
|
+
try {
|
|
170
|
+
this.storage.removeItem(key);
|
|
171
|
+
}
|
|
172
|
+
catch { /* noop */ }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Default 24h TTL — long enough that pure-function endpoints feel "permanent". */
|
|
176
|
+
export const DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
177
|
+
/**
|
|
178
|
+
* Turn the user-facing `cache` option into a `{store, defaultTtlMs}` pair.
|
|
179
|
+
* `undefined` / `false` → no cache.
|
|
180
|
+
*/
|
|
181
|
+
export function resolveCacheOption(option) {
|
|
182
|
+
if (option === undefined || option === false)
|
|
183
|
+
return null;
|
|
184
|
+
if (option === 'memory')
|
|
185
|
+
return { store: new MemoryStore(), defaultTtlMs: DEFAULT_CACHE_TTL_MS };
|
|
186
|
+
if (option === 'localStorage') {
|
|
187
|
+
if (typeof globalThis.localStorage === 'undefined') {
|
|
188
|
+
throw new Error('AstroWay SDK: cache: "localStorage" requires a global `localStorage`. '
|
|
189
|
+
+ 'Use cache: "memory" outside the browser, or pass a custom CacheStore.');
|
|
190
|
+
}
|
|
191
|
+
return { store: new LocalStorageStore(), defaultTtlMs: DEFAULT_CACHE_TTL_MS };
|
|
192
|
+
}
|
|
193
|
+
if ('store' in option && option.store) {
|
|
194
|
+
return { store: option.store, defaultTtlMs: option.ttlMs ?? DEFAULT_CACHE_TTL_MS };
|
|
195
|
+
}
|
|
196
|
+
return { store: option, defaultTtlMs: DEFAULT_CACHE_TTL_MS };
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -4,16 +4,24 @@
|
|
|
4
4
|
* Catch order recommendation in user code:
|
|
5
5
|
* try { ... } catch (e) {
|
|
6
6
|
* if (e instanceof RateLimitError) { ... await sleep ... }
|
|
7
|
+
* else if (e instanceof QuotaExceededError) { ... top up credits ... }
|
|
7
8
|
* else if (e instanceof AuthenticationError) { ... rotate key ... }
|
|
8
9
|
* else if (e instanceof ApiError) { ... generic 4xx/5xx ... }
|
|
9
10
|
* else throw e;
|
|
10
11
|
* }
|
|
12
|
+
*
|
|
13
|
+
* Every `ApiError` carries `requestId`, `creditsRemaining`, and (when applicable)
|
|
14
|
+
* `retryAfterSeconds` so user code can build support tickets and debug uniformly.
|
|
11
15
|
*/
|
|
12
16
|
interface ApiErrorInit {
|
|
13
17
|
status?: number;
|
|
14
18
|
code?: string;
|
|
15
19
|
body?: unknown;
|
|
16
20
|
requestId?: string;
|
|
21
|
+
/** Credits left in the caller's account, when surfaced via `X-Credits-Remaining`. */
|
|
22
|
+
creditsRemaining?: number;
|
|
23
|
+
/** Seconds to wait before retrying — set on 429 and quota-exceeded responses. */
|
|
24
|
+
retryAfterSeconds?: number;
|
|
17
25
|
cause?: unknown;
|
|
18
26
|
}
|
|
19
27
|
export declare class ApiError extends Error {
|
|
@@ -26,6 +34,10 @@ export declare class ApiError extends Error {
|
|
|
26
34
|
readonly body?: unknown;
|
|
27
35
|
/** AstroWay request ID, when present in `X-Request-Id` response header. */
|
|
28
36
|
readonly requestId?: string;
|
|
37
|
+
/** Credits remaining on the caller's account, surfaced from `X-Credits-Remaining`. */
|
|
38
|
+
readonly creditsRemaining?: number;
|
|
39
|
+
/** Seconds to wait before retrying (429, quota-exceeded). */
|
|
40
|
+
readonly retryAfterSeconds?: number;
|
|
29
41
|
constructor(message: string, init?: ApiErrorInit);
|
|
30
42
|
}
|
|
31
43
|
export declare class APIConnectionError extends ApiError {
|
|
@@ -49,14 +61,25 @@ export declare class NotFoundError extends ApiError {
|
|
|
49
61
|
export declare class UnprocessableEntityError extends ApiError {
|
|
50
62
|
readonly name: string;
|
|
51
63
|
}
|
|
52
|
-
interface RateLimitErrorInit extends ApiErrorInit {
|
|
53
|
-
retryAfterSeconds?: number;
|
|
54
|
-
}
|
|
55
64
|
export declare class RateLimitError extends ApiError {
|
|
56
65
|
readonly name: string;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Account ran out of credits / quota for the current period. HTTP 402 or
|
|
69
|
+
* `code: OUT_OF_CREDITS` / `QUOTA_EXCEEDED`. Distinct from RateLimitError
|
|
70
|
+
* (which is short-window throttling — backing off helps; for quota you need
|
|
71
|
+
* to top up or wait until the period resets).
|
|
72
|
+
*/
|
|
73
|
+
export declare class QuotaExceededError extends ApiError {
|
|
74
|
+
readonly name: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Server-side calculation failure for an otherwise-valid request — usually
|
|
78
|
+
* means a Swiss Ephemeris boundary, missing dataset, or unsupported house
|
|
79
|
+
* system for high latitudes. `code: CALCULATION_ERROR` from the API.
|
|
80
|
+
*/
|
|
81
|
+
export declare class CalculationError extends ApiError {
|
|
82
|
+
readonly name: string;
|
|
60
83
|
}
|
|
61
84
|
export declare class InternalServerError extends ApiError {
|
|
62
85
|
readonly name: string;
|
|
@@ -67,6 +90,7 @@ interface ClassifyArgs {
|
|
|
67
90
|
message: string;
|
|
68
91
|
body?: unknown;
|
|
69
92
|
requestId?: string;
|
|
93
|
+
creditsRemaining?: number;
|
|
70
94
|
retryAfterSeconds?: number;
|
|
71
95
|
}
|
|
72
96
|
/**
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAkB,IAAI,EAAE,MAAM,CAAc;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;gBAExB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CASjD;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,SAAkB,IAAI,EAAE,MAAM,CAAwB;CACvD;AAED,qBAAa,eAAgB,SAAQ,kBAAkB;IACrD,SAAkB,IAAI,EAAE,MAAM,CAAqB;CACpD;AAED,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,SAAkB,IAAI,EAAE,MAAM,CAAqB;CACpD;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,SAAkB,IAAI,EAAE,MAAM,CAAyB;CACxD;AAED,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,SAAkB,IAAI,EAAE,MAAM,CAA2B;CAC1D;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,SAAkB,IAAI,EAAE,MAAM,CAAmB;CAClD;AAED,qBAAa,wBAAyB,SAAQ,QAAQ;IACpD,SAAkB,IAAI,EAAE,MAAM,CAA8B;CAC7D;AAED,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,SAAkB,IAAI,EAAE,MAAM,CAAoB;CACnD;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,SAAkB,IAAI,EAAE,MAAM,CAAwB;CACvD;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,SAAkB,IAAI,EAAE,MAAM,CAAsB;CACrD;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,SAAkB,IAAI,EAAE,MAAM,CAAyB;CACxD;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CA0B9D"}
|
package/dist/errors.js
CHANGED
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
* Catch order recommendation in user code:
|
|
5
5
|
* try { ... } catch (e) {
|
|
6
6
|
* if (e instanceof RateLimitError) { ... await sleep ... }
|
|
7
|
+
* else if (e instanceof QuotaExceededError) { ... top up credits ... }
|
|
7
8
|
* else if (e instanceof AuthenticationError) { ... rotate key ... }
|
|
8
9
|
* else if (e instanceof ApiError) { ... generic 4xx/5xx ... }
|
|
9
10
|
* else throw e;
|
|
10
11
|
* }
|
|
12
|
+
*
|
|
13
|
+
* Every `ApiError` carries `requestId`, `creditsRemaining`, and (when applicable)
|
|
14
|
+
* `retryAfterSeconds` so user code can build support tickets and debug uniformly.
|
|
11
15
|
*/
|
|
12
16
|
export class ApiError extends Error {
|
|
13
17
|
name = 'ApiError';
|
|
@@ -19,6 +23,10 @@ export class ApiError extends Error {
|
|
|
19
23
|
body;
|
|
20
24
|
/** AstroWay request ID, when present in `X-Request-Id` response header. */
|
|
21
25
|
requestId;
|
|
26
|
+
/** Credits remaining on the caller's account, surfaced from `X-Credits-Remaining`. */
|
|
27
|
+
creditsRemaining;
|
|
28
|
+
/** Seconds to wait before retrying (429, quota-exceeded). */
|
|
29
|
+
retryAfterSeconds;
|
|
22
30
|
constructor(message, init) {
|
|
23
31
|
super(message, init?.cause !== undefined ? { cause: init.cause } : undefined);
|
|
24
32
|
if (init?.status !== undefined)
|
|
@@ -29,6 +37,10 @@ export class ApiError extends Error {
|
|
|
29
37
|
this.body = init.body;
|
|
30
38
|
if (init?.requestId !== undefined)
|
|
31
39
|
this.requestId = init.requestId;
|
|
40
|
+
if (init?.creditsRemaining !== undefined)
|
|
41
|
+
this.creditsRemaining = init.creditsRemaining;
|
|
42
|
+
if (init?.retryAfterSeconds !== undefined)
|
|
43
|
+
this.retryAfterSeconds = init.retryAfterSeconds;
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
46
|
export class APIConnectionError extends ApiError {
|
|
@@ -54,23 +66,35 @@ export class UnprocessableEntityError extends ApiError {
|
|
|
54
66
|
}
|
|
55
67
|
export class RateLimitError extends ApiError {
|
|
56
68
|
name = 'RateLimitError';
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Account ran out of credits / quota for the current period. HTTP 402 or
|
|
72
|
+
* `code: OUT_OF_CREDITS` / `QUOTA_EXCEEDED`. Distinct from RateLimitError
|
|
73
|
+
* (which is short-window throttling — backing off helps; for quota you need
|
|
74
|
+
* to top up or wait until the period resets).
|
|
75
|
+
*/
|
|
76
|
+
export class QuotaExceededError extends ApiError {
|
|
77
|
+
name = 'QuotaExceededError';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Server-side calculation failure for an otherwise-valid request — usually
|
|
81
|
+
* means a Swiss Ephemeris boundary, missing dataset, or unsupported house
|
|
82
|
+
* system for high latitudes. `code: CALCULATION_ERROR` from the API.
|
|
83
|
+
*/
|
|
84
|
+
export class CalculationError extends ApiError {
|
|
85
|
+
name = 'CalculationError';
|
|
64
86
|
}
|
|
65
87
|
export class InternalServerError extends ApiError {
|
|
66
88
|
name = 'InternalServerError';
|
|
67
89
|
}
|
|
90
|
+
const QUOTA_CODES = new Set(['OUT_OF_CREDITS', 'QUOTA_EXCEEDED', 'CREDIT_LIMIT_REACHED']);
|
|
91
|
+
const CALCULATION_CODES = new Set(['CALCULATION_ERROR', 'EPHEMERIS_ERROR']);
|
|
68
92
|
/**
|
|
69
93
|
* Maps an HTTP status + optional server error code to the most specific
|
|
70
94
|
* subclass. Used by the openapi-fetch error path.
|
|
71
95
|
*/
|
|
72
96
|
export function classifyHttpError(args) {
|
|
73
|
-
const { status, code, message, body, requestId, retryAfterSeconds } = args;
|
|
97
|
+
const { status, code, message, body, requestId, creditsRemaining, retryAfterSeconds } = args;
|
|
74
98
|
const init = { status };
|
|
75
99
|
if (code !== undefined)
|
|
76
100
|
init.code = code;
|
|
@@ -78,18 +102,25 @@ export function classifyHttpError(args) {
|
|
|
78
102
|
init.body = body;
|
|
79
103
|
if (requestId !== undefined)
|
|
80
104
|
init.requestId = requestId;
|
|
105
|
+
if (creditsRemaining !== undefined)
|
|
106
|
+
init.creditsRemaining = creditsRemaining;
|
|
107
|
+
if (retryAfterSeconds !== undefined)
|
|
108
|
+
init.retryAfterSeconds = retryAfterSeconds;
|
|
109
|
+
// Code-first dispatch for app-level errors that may ride on multiple HTTP statuses.
|
|
110
|
+
if (code !== undefined) {
|
|
111
|
+
if (QUOTA_CODES.has(code))
|
|
112
|
+
return new QuotaExceededError(message, init);
|
|
113
|
+
if (CALCULATION_CODES.has(code))
|
|
114
|
+
return new CalculationError(message, init);
|
|
115
|
+
}
|
|
81
116
|
switch (status) {
|
|
82
117
|
case 400: return new BadRequestError(message, init);
|
|
83
118
|
case 401: return new AuthenticationError(message, init);
|
|
119
|
+
case 402: return new QuotaExceededError(message, init);
|
|
84
120
|
case 403: return new PermissionDeniedError(message, init);
|
|
85
121
|
case 404: return new NotFoundError(message, init);
|
|
86
122
|
case 422: return new UnprocessableEntityError(message, init);
|
|
87
|
-
case 429:
|
|
88
|
-
const rlInit = { ...init };
|
|
89
|
-
if (retryAfterSeconds !== undefined)
|
|
90
|
-
rlInit.retryAfterSeconds = retryAfterSeconds;
|
|
91
|
-
return new RateLimitError(message, rlInit);
|
|
92
|
-
}
|
|
123
|
+
case 429: return new RateLimitError(message, init);
|
|
93
124
|
}
|
|
94
125
|
if (status >= 500)
|
|
95
126
|
return new InternalServerError(message, init);
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `BirthDateTime` — typed builder + validator for the (date, time, lat, lon, tz)
|
|
3
|
+
* tuple that every chart-style endpoint takes. Reduces boilerplate when constructing
|
|
4
|
+
* request bodies and catches malformed dates before the network round-trip.
|
|
5
|
+
*
|
|
6
|
+
* Tree-shakeable: import directly from `@astroway/sdk/helpers` so the helper
|
|
7
|
+
* doesn't bloat the core bundle when you don't use it.
|
|
8
|
+
*
|
|
9
|
+
* import { BirthDateTime } from '@astroway/sdk/helpers';
|
|
10
|
+
*
|
|
11
|
+
* const birth = BirthDateTime.fromCoordinates({
|
|
12
|
+
* date: '1990-07-14', time: '14:30:00',
|
|
13
|
+
* latitude: 50.45, longitude: 30.52, timezoneOffset: 3,
|
|
14
|
+
* });
|
|
15
|
+
* const chart = await aw.chart.compute(birth.toBody());
|
|
16
|
+
*/
|
|
17
|
+
export interface BirthDateTimeInit {
|
|
18
|
+
/** ISO date `YYYY-MM-DD`. */
|
|
19
|
+
date: string;
|
|
20
|
+
/** Time `HH:MM:SS`. */
|
|
21
|
+
time: string;
|
|
22
|
+
/** UTC offset in hours. Defaults to 0 if omitted. */
|
|
23
|
+
timezoneOffset?: number;
|
|
24
|
+
/** Decimal latitude, north positive. Defaults to 0. */
|
|
25
|
+
latitude?: number;
|
|
26
|
+
/** Decimal longitude, east positive. Defaults to 0. */
|
|
27
|
+
longitude?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface BirthDateTimeBody {
|
|
30
|
+
date: string;
|
|
31
|
+
time: string;
|
|
32
|
+
timezoneOffset: number;
|
|
33
|
+
latitude: number;
|
|
34
|
+
longitude: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class BirthDateTime {
|
|
37
|
+
readonly date: string;
|
|
38
|
+
readonly time: string;
|
|
39
|
+
readonly timezoneOffset: number;
|
|
40
|
+
readonly latitude: number;
|
|
41
|
+
readonly longitude: number;
|
|
42
|
+
private constructor();
|
|
43
|
+
/**
|
|
44
|
+
* Build from explicit `{ date, time, latitude, longitude, timezoneOffset }`.
|
|
45
|
+
* Validates date / time format eagerly.
|
|
46
|
+
*/
|
|
47
|
+
static fromCoordinates(init: BirthDateTimeInit): BirthDateTime;
|
|
48
|
+
/**
|
|
49
|
+
* Build from a `Date` (assumed to be a "local birth moment in the user's
|
|
50
|
+
* birth timezone") plus the corresponding `(lat, lon, tzOffset)`.
|
|
51
|
+
*
|
|
52
|
+
* The Date is split into `YYYY-MM-DD` + `HH:MM:SS` using its UTC components
|
|
53
|
+
* — the caller is responsible for passing a Date that already represents the
|
|
54
|
+
* birth-place local time (use `new Date(Date.UTC(year, month-1, day, hour, min, sec))`).
|
|
55
|
+
*/
|
|
56
|
+
static fromDate(date: Date, geo: {
|
|
57
|
+
latitude: number;
|
|
58
|
+
longitude: number;
|
|
59
|
+
timezoneOffset?: number;
|
|
60
|
+
}): BirthDateTime;
|
|
61
|
+
/**
|
|
62
|
+
* Build from a full ISO 8601 string (`1990-07-14T14:30:00`) plus geo data.
|
|
63
|
+
* The trailing `Z` / `+HH:MM` offset, if present, is stripped — the API
|
|
64
|
+
* separately tracks `timezoneOffset`.
|
|
65
|
+
*/
|
|
66
|
+
static parse(iso: string, geo: {
|
|
67
|
+
latitude: number;
|
|
68
|
+
longitude: number;
|
|
69
|
+
timezoneOffset?: number;
|
|
70
|
+
}): BirthDateTime;
|
|
71
|
+
/** Wire shape suitable for `aw.chart.compute(birth.toBody())` etc. */
|
|
72
|
+
toBody(): BirthDateTimeBody;
|
|
73
|
+
/** Same fields, but as a JS `Date` (constructed in UTC for determinism). */
|
|
74
|
+
toDate(): Date;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=birth-date-time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"birth-date-time.d.ts","sourceRoot":"","sources":["../../src/helpers/birth-date-time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,OAAO;IAQP;;;OAGG;IACH,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa;IAgB9D;;;;;;;OAOG;IACH,MAAM,CAAC,QAAQ,CACb,IAAI,EAAE,IAAI,EACV,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,aAAa;IAgBhB;;;;OAIG;IACH,MAAM,CAAC,KAAK,CACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,aAAa;IAgBhB,sEAAsE;IACtE,MAAM,IAAI,iBAAiB;IAU3B,4EAA4E;IAC5E,MAAM,IAAI,IAAI;CAKf"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `BirthDateTime` — typed builder + validator for the (date, time, lat, lon, tz)
|
|
3
|
+
* tuple that every chart-style endpoint takes. Reduces boilerplate when constructing
|
|
4
|
+
* request bodies and catches malformed dates before the network round-trip.
|
|
5
|
+
*
|
|
6
|
+
* Tree-shakeable: import directly from `@astroway/sdk/helpers` so the helper
|
|
7
|
+
* doesn't bloat the core bundle when you don't use it.
|
|
8
|
+
*
|
|
9
|
+
* import { BirthDateTime } from '@astroway/sdk/helpers';
|
|
10
|
+
*
|
|
11
|
+
* const birth = BirthDateTime.fromCoordinates({
|
|
12
|
+
* date: '1990-07-14', time: '14:30:00',
|
|
13
|
+
* latitude: 50.45, longitude: 30.52, timezoneOffset: 3,
|
|
14
|
+
* });
|
|
15
|
+
* const chart = await aw.chart.compute(birth.toBody());
|
|
16
|
+
*/
|
|
17
|
+
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
18
|
+
const TIME_RE = /^\d{2}:\d{2}:\d{2}$/;
|
|
19
|
+
const ISO_RE = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2})?)/;
|
|
20
|
+
export class BirthDateTime {
|
|
21
|
+
date;
|
|
22
|
+
time;
|
|
23
|
+
timezoneOffset;
|
|
24
|
+
latitude;
|
|
25
|
+
longitude;
|
|
26
|
+
constructor(init) {
|
|
27
|
+
this.date = init.date;
|
|
28
|
+
this.time = init.time;
|
|
29
|
+
this.timezoneOffset = init.timezoneOffset;
|
|
30
|
+
this.latitude = init.latitude;
|
|
31
|
+
this.longitude = init.longitude;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build from explicit `{ date, time, latitude, longitude, timezoneOffset }`.
|
|
35
|
+
* Validates date / time format eagerly.
|
|
36
|
+
*/
|
|
37
|
+
static fromCoordinates(init) {
|
|
38
|
+
if (!DATE_RE.test(init.date)) {
|
|
39
|
+
throw new Error(`BirthDateTime: date must be YYYY-MM-DD, got '${init.date}'`);
|
|
40
|
+
}
|
|
41
|
+
if (!TIME_RE.test(init.time)) {
|
|
42
|
+
throw new Error(`BirthDateTime: time must be HH:MM:SS, got '${init.time}'`);
|
|
43
|
+
}
|
|
44
|
+
return new BirthDateTime({
|
|
45
|
+
date: init.date,
|
|
46
|
+
time: init.time,
|
|
47
|
+
timezoneOffset: init.timezoneOffset ?? 0,
|
|
48
|
+
latitude: init.latitude ?? 0,
|
|
49
|
+
longitude: init.longitude ?? 0,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build from a `Date` (assumed to be a "local birth moment in the user's
|
|
54
|
+
* birth timezone") plus the corresponding `(lat, lon, tzOffset)`.
|
|
55
|
+
*
|
|
56
|
+
* The Date is split into `YYYY-MM-DD` + `HH:MM:SS` using its UTC components
|
|
57
|
+
* — the caller is responsible for passing a Date that already represents the
|
|
58
|
+
* birth-place local time (use `new Date(Date.UTC(year, month-1, day, hour, min, sec))`).
|
|
59
|
+
*/
|
|
60
|
+
static fromDate(date, geo) {
|
|
61
|
+
const yyyy = date.getUTCFullYear().toString().padStart(4, '0');
|
|
62
|
+
const mm = (date.getUTCMonth() + 1).toString().padStart(2, '0');
|
|
63
|
+
const dd = date.getUTCDate().toString().padStart(2, '0');
|
|
64
|
+
const hh = date.getUTCHours().toString().padStart(2, '0');
|
|
65
|
+
const mi = date.getUTCMinutes().toString().padStart(2, '0');
|
|
66
|
+
const ss = date.getUTCSeconds().toString().padStart(2, '0');
|
|
67
|
+
return BirthDateTime.fromCoordinates({
|
|
68
|
+
date: `${yyyy}-${mm}-${dd}`,
|
|
69
|
+
time: `${hh}:${mi}:${ss}`,
|
|
70
|
+
latitude: geo.latitude,
|
|
71
|
+
longitude: geo.longitude,
|
|
72
|
+
timezoneOffset: geo.timezoneOffset ?? 0,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build from a full ISO 8601 string (`1990-07-14T14:30:00`) plus geo data.
|
|
77
|
+
* The trailing `Z` / `+HH:MM` offset, if present, is stripped — the API
|
|
78
|
+
* separately tracks `timezoneOffset`.
|
|
79
|
+
*/
|
|
80
|
+
static parse(iso, geo) {
|
|
81
|
+
const match = ISO_RE.exec(iso);
|
|
82
|
+
if (!match) {
|
|
83
|
+
throw new Error(`BirthDateTime: cannot parse ISO datetime '${iso}'`);
|
|
84
|
+
}
|
|
85
|
+
const [, date, timeRaw] = match;
|
|
86
|
+
const time = timeRaw.length === 5 ? `${timeRaw}:00` : timeRaw;
|
|
87
|
+
return BirthDateTime.fromCoordinates({
|
|
88
|
+
date: date,
|
|
89
|
+
time,
|
|
90
|
+
latitude: geo.latitude,
|
|
91
|
+
longitude: geo.longitude,
|
|
92
|
+
timezoneOffset: geo.timezoneOffset ?? 0,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** Wire shape suitable for `aw.chart.compute(birth.toBody())` etc. */
|
|
96
|
+
toBody() {
|
|
97
|
+
return {
|
|
98
|
+
date: this.date,
|
|
99
|
+
time: this.time,
|
|
100
|
+
timezoneOffset: this.timezoneOffset,
|
|
101
|
+
latitude: this.latitude,
|
|
102
|
+
longitude: this.longitude,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** Same fields, but as a JS `Date` (constructed in UTC for determinism). */
|
|
106
|
+
toDate() {
|
|
107
|
+
const [y, m, d] = this.date.split('-').map(Number);
|
|
108
|
+
const [h, mi, s] = this.time.split(':').map(Number);
|
|
109
|
+
return new Date(Date.UTC(y, m - 1, d, h, mi, s));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=birth-date-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"birth-date-time.js","sourceRoot":"","sources":["../../src/helpers/birth-date-time.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAEhG,OAAO,EACL,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAEhG,OAAO,EACL,aAAa,GAGd,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Idempotency key generation + policy. */
|
|
2
|
+
export type IdempotencyMode = 'auto' | 'off' | {
|
|
3
|
+
generator: () => string;
|
|
4
|
+
};
|
|
5
|
+
/** RFC 4122 v4 UUID. Uses Web Crypto when available, falls back to Math.random. */
|
|
6
|
+
export declare function generateIdempotencyKey(): string;
|
|
7
|
+
/** True for the methods we auto-attach an idempotency key on (POST only). */
|
|
8
|
+
export declare function shouldAttachIdempotency(mode: IdempotencyMode | undefined, method: string): boolean;
|
|
9
|
+
export declare function resolveKeyGenerator(mode: IdempotencyMode | undefined): () => string;
|
|
10
|
+
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,KAAK,GACL;IAAE,SAAS,EAAE,MAAM,MAAM,CAAA;CAAE,CAAC;AAIhC,mFAAmF;AACnF,wBAAgB,sBAAsB,IAAI,MAAM,CAsB/C;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGlG;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,MAAM,CAKnF"}
|