@moogodev/client 0.1.4 → 0.1.6
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/client.d.ts +22 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +84 -86
- package/dist/errors.js +5 -5
- package/dist/esm/client.d.ts +22 -7
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +82 -86
- package/dist/esm/errors.js +5 -5
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/client.ts +97 -108
- package/src/errors.ts +5 -5
- package/src/index.ts +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -44,13 +44,11 @@ export declare class MoogoClient {
|
|
|
44
44
|
private getCatalogPath;
|
|
45
45
|
/** Build the credentials path */
|
|
46
46
|
private getCredentialsPath;
|
|
47
|
-
/**
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
private hmacSha256;
|
|
53
|
-
/** Execute a storage request with HMAC auth and error handling */
|
|
47
|
+
/** Detect auth mode from secret key prefix */
|
|
48
|
+
private detectAuthMode;
|
|
49
|
+
/** Get storage auth headers (Bearer or HMAC) */
|
|
50
|
+
private getStorageAuthHeaders;
|
|
51
|
+
/** Execute a storage request with Bearer/HMAC auth and error handling */
|
|
54
52
|
private requestStorage;
|
|
55
53
|
/** Execute a SQL request with Bearer token auth and error handling */
|
|
56
54
|
private requestSQL;
|
|
@@ -176,6 +174,23 @@ export declare class MoogoClient {
|
|
|
176
174
|
reclaimed_bytes: number;
|
|
177
175
|
}>;
|
|
178
176
|
}
|
|
177
|
+
/** Create a client for storage operations (upload, download, list, bucket mgmt) */
|
|
178
|
+
export declare function createStorageClient(config: {
|
|
179
|
+
baseUrl: string;
|
|
180
|
+
projectId: string;
|
|
181
|
+
accessKeyId: string;
|
|
182
|
+
secretKey: string;
|
|
183
|
+
fetch?: typeof fetch;
|
|
184
|
+
timeout?: number;
|
|
185
|
+
}): MoogoClient;
|
|
186
|
+
/** Create a client for SQL operations (query, exec, transaction, maintenance) */
|
|
187
|
+
export declare function createSQLClient(config: {
|
|
188
|
+
baseUrl: string;
|
|
189
|
+
projectId: string;
|
|
190
|
+
projectKey: string;
|
|
191
|
+
fetch?: typeof fetch;
|
|
192
|
+
timeout?: number;
|
|
193
|
+
}): MoogoClient;
|
|
179
194
|
/** Convenience function to create a client from environment variables */
|
|
180
195
|
export declare function createClientFromEnv(env?: Record<string, string | undefined>): MoogoClient;
|
|
181
196
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EAEjB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EAGxB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAUpB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,YAAY;IAsBhC,2DAA2D;IAC3D,OAAO,CAAC,aAAa;IAIrB,8DAA8D;IAC9D,OAAO,CAAC,qBAAqB;IAI7B,oCAAoC;IACpC,OAAO,CAAC,cAAc;IAItB,iCAAiC;IACjC,OAAO,CAAC,kBAAkB;IAI1B,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.MoogoClient = void 0;
|
|
8
|
+
exports.createStorageClient = createStorageClient;
|
|
9
|
+
exports.createSQLClient = createSQLClient;
|
|
8
10
|
exports.createClientFromEnv = createClientFromEnv;
|
|
9
11
|
const errors_js_1 = require("./errors.js");
|
|
10
12
|
/** Default timeout: 30 seconds */
|
|
@@ -79,71 +81,35 @@ class MoogoClient {
|
|
|
79
81
|
getCredentialsPath() {
|
|
80
82
|
return `${this.baseUrl}/storage-credentials/${this.projectId}`;
|
|
81
83
|
}
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let bodyHash;
|
|
95
|
-
if (body instanceof Uint8Array) {
|
|
96
|
-
bodyHash = await this.hashBody(body);
|
|
97
|
-
}
|
|
98
|
-
else if (typeof body === 'string') {
|
|
99
|
-
bodyHash = await this.hashBody(new TextEncoder().encode(body));
|
|
84
|
+
/** Detect auth mode from secret key prefix */
|
|
85
|
+
detectAuthMode() {
|
|
86
|
+
// moogo_sk_ = Bearer token (current API)
|
|
87
|
+
// moogo_hmac_ = HMAC-SHA256 (legacy/enterprise)
|
|
88
|
+
if (this.secretKey?.startsWith('moogo_hmac_'))
|
|
89
|
+
return 'hmac';
|
|
90
|
+
return 'bearer';
|
|
91
|
+
}
|
|
92
|
+
/** Get storage auth headers (Bearer or HMAC) */
|
|
93
|
+
getStorageAuthHeaders() {
|
|
94
|
+
if (!this.hasStorageCredentials()) {
|
|
95
|
+
throw new Error('Storage operations require accessKeyId and secretKey');
|
|
100
96
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
'
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
].join('\n');
|
|
118
|
-
const signingKey = await this.getSigningKey(date);
|
|
119
|
-
const signature = await this.hmacSha256(signingKey, stringToSign);
|
|
120
|
-
return `MOOGO-HMAC-SHA256 Credential=${this.accessKeyId}/${date}/moogo/storage/moogo_request, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
121
|
-
}
|
|
122
|
-
async hashBody(body) {
|
|
123
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', body);
|
|
124
|
-
return Array.from(new Uint8Array(hashBuffer))
|
|
125
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
126
|
-
.join('');
|
|
127
|
-
}
|
|
128
|
-
async hashString(str) {
|
|
129
|
-
return this.hashBody(new TextEncoder().encode(str));
|
|
130
|
-
}
|
|
131
|
-
async getSigningKey(date) {
|
|
132
|
-
const encoder = new TextEncoder();
|
|
133
|
-
const secretKey = await crypto.subtle.importKey('raw', encoder.encode(this.secretKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
134
|
-
const kDate = await crypto.subtle.sign('HMAC', secretKey, encoder.encode(date));
|
|
135
|
-
const kRegion = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kDate, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo'));
|
|
136
|
-
const kService = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kRegion, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('storage'));
|
|
137
|
-
const kSigning = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kService, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo_request'));
|
|
138
|
-
return crypto.subtle.importKey('raw', kSigning, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
139
|
-
}
|
|
140
|
-
async hmacSha256(key, data) {
|
|
141
|
-
const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));
|
|
142
|
-
return Array.from(new Uint8Array(signature))
|
|
143
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
144
|
-
.join('');
|
|
145
|
-
}
|
|
146
|
-
/** Execute a storage request with HMAC auth and error handling */
|
|
97
|
+
const mode = this.detectAuthMode();
|
|
98
|
+
if (mode === 'hmac') {
|
|
99
|
+
// Legacy HMAC - would need async, but we keep sync for headers
|
|
100
|
+
// For HMAC, we'd compute in requestStorage instead
|
|
101
|
+
return {
|
|
102
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
103
|
+
'Authorization': `Bearer ${this.secretKey}`, // fallback, actual HMAC in request
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Simple Bearer (current API)
|
|
107
|
+
return {
|
|
108
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
109
|
+
'Authorization': `Bearer ${this.secretKey}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Execute a storage request with Bearer/HMAC auth and error handling */
|
|
147
113
|
async requestStorage(method, path, options = {}) {
|
|
148
114
|
if (!this.hasStorageCredentials()) {
|
|
149
115
|
throw new Error('Storage operations require accessKeyId and secretKey');
|
|
@@ -155,8 +121,8 @@ class MoogoClient {
|
|
|
155
121
|
url.searchParams.set(k, v);
|
|
156
122
|
});
|
|
157
123
|
}
|
|
124
|
+
// Base headers
|
|
158
125
|
const requestHeaders = {
|
|
159
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
160
126
|
'Content-Type': 'application/json',
|
|
161
127
|
...options.headers,
|
|
162
128
|
};
|
|
@@ -164,8 +130,11 @@ class MoogoClient {
|
|
|
164
130
|
if (options.body instanceof FormData) {
|
|
165
131
|
delete requestHeaders['Content-Type'];
|
|
166
132
|
}
|
|
167
|
-
|
|
168
|
-
|
|
133
|
+
// Add storage auth headers (Bearer or HMAC)
|
|
134
|
+
const authHeaders = this.getStorageAuthHeaders();
|
|
135
|
+
Object.assign(requestHeaders, authHeaders);
|
|
136
|
+
// For HMAC mode, we'd compute signature here (future)
|
|
137
|
+
// For now, Bearer mode uses secretKey directly
|
|
169
138
|
const controller = new AbortController();
|
|
170
139
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
171
140
|
const signal = options.signal
|
|
@@ -332,12 +301,12 @@ class MoogoClient {
|
|
|
332
301
|
}
|
|
333
302
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
334
303
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
304
|
+
// Auth headers for upload
|
|
335
305
|
const authHeaders = {
|
|
336
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
337
306
|
...requestHeaders,
|
|
338
307
|
};
|
|
339
|
-
const
|
|
340
|
-
authHeaders
|
|
308
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
309
|
+
Object.assign(authHeaders, storageAuth);
|
|
341
310
|
// Handle progress if callback provided and we have a known size
|
|
342
311
|
let finalBody = body;
|
|
343
312
|
if (onProgress && contentLength !== undefined && body instanceof Blob) {
|
|
@@ -400,17 +369,16 @@ class MoogoClient {
|
|
|
400
369
|
if (!this.hasStorageCredentials()) {
|
|
401
370
|
throw new Error('Download requires storage credentials (accessKeyId and secretKey)');
|
|
402
371
|
}
|
|
403
|
-
const requestHeaders = {
|
|
404
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
405
|
-
};
|
|
372
|
+
const requestHeaders = {};
|
|
406
373
|
if (range) {
|
|
407
374
|
const end = range.end !== undefined ? range.end : '';
|
|
408
375
|
requestHeaders['Range'] = `bytes=${range.start}-${end}`;
|
|
409
376
|
}
|
|
410
377
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
411
378
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
412
|
-
|
|
413
|
-
|
|
379
|
+
// Auth headers for download
|
|
380
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
381
|
+
Object.assign(requestHeaders, storageAuth);
|
|
414
382
|
const controller = new AbortController();
|
|
415
383
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
416
384
|
const reqSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
@@ -464,11 +432,8 @@ class MoogoClient {
|
|
|
464
432
|
}
|
|
465
433
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
466
434
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
};
|
|
470
|
-
const authHeader = await this.createAuthHeader('HEAD', url.pathname + url.search, requestHeaders);
|
|
471
|
-
requestHeaders['Authorization'] = `Bearer ${authHeader}`;
|
|
435
|
+
// Auth headers for HEAD
|
|
436
|
+
const requestHeaders = this.getStorageAuthHeaders();
|
|
472
437
|
const controller = new AbortController();
|
|
473
438
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
474
439
|
const reqSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
@@ -641,14 +606,46 @@ class MoogoClient {
|
|
|
641
606
|
}
|
|
642
607
|
}
|
|
643
608
|
exports.MoogoClient = MoogoClient;
|
|
609
|
+
/** Create a client for storage operations (upload, download, list, bucket mgmt) */
|
|
610
|
+
function createStorageClient(config) {
|
|
611
|
+
return new MoogoClient({
|
|
612
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
613
|
+
projectId: config.projectId,
|
|
614
|
+
accessKeyId: config.accessKeyId,
|
|
615
|
+
secretKey: config.secretKey,
|
|
616
|
+
fetch: config.fetch,
|
|
617
|
+
timeout: config.timeout,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
/** Create a client for SQL operations (query, exec, transaction, maintenance) */
|
|
621
|
+
function createSQLClient(config) {
|
|
622
|
+
return new MoogoClient({
|
|
623
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
624
|
+
projectId: config.projectId,
|
|
625
|
+
projectKey: config.projectKey,
|
|
626
|
+
fetch: config.fetch,
|
|
627
|
+
timeout: config.timeout,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
644
630
|
/** Convenience function to create a client from environment variables */
|
|
645
631
|
function createClientFromEnv(env) {
|
|
646
632
|
const getEnv = (key) => env?.[key] ?? process.env[key];
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
633
|
+
const warnDeprecated = (oldKey, newKey) => {
|
|
634
|
+
if (process.env[oldKey] && !process.env[newKey]) {
|
|
635
|
+
console.warn(`[moogo] Deprecation: ${oldKey} is deprecated, use ${newKey} instead`);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
// Deprecation warnings for old env var names
|
|
639
|
+
warnDeprecated('MOOGO_API_URL', 'MOOGO_BASE_URL');
|
|
640
|
+
warnDeprecated('MOOGO_BUCKET_ENDPOINT', 'MOOGO_BASE_URL');
|
|
641
|
+
warnDeprecated('MOOGO_BUCKET_ACCESS_KEY_ID', 'MOOGO_STORAGE_ACCESS_KEY_ID');
|
|
642
|
+
warnDeprecated('MOOGO_BUCKET_SECRET_KEY', 'MOOGO_STORAGE_SECRET_KEY');
|
|
643
|
+
warnDeprecated('MOOGO_PROJECT_KEY', 'MOOGO_SQL_KEY');
|
|
644
|
+
// Resolve base URL: new names first, then legacy
|
|
645
|
+
const rawBase = getEnv('MOOGO_BASE_URL') || getEnv('MOOGO_API_URL') || getEnv('MOOGO_BUCKET_ENDPOINT') || 'https://api.moogo.dev';
|
|
650
646
|
const baseUrl = rawBase.replace(/\/p\/[^/]+\/bucket.*$/, '').replace(/\/+$/, '');
|
|
651
|
-
|
|
647
|
+
// SQL key (new name first)
|
|
648
|
+
const projectKey = getEnv('MOOGO_SQL_KEY') || getEnv('MOOGO_PROJECT_KEY');
|
|
652
649
|
if (projectKey) {
|
|
653
650
|
return new MoogoClient({
|
|
654
651
|
baseUrl,
|
|
@@ -656,10 +653,11 @@ function createClientFromEnv(env) {
|
|
|
656
653
|
projectKey,
|
|
657
654
|
});
|
|
658
655
|
}
|
|
656
|
+
// Storage keys (new names first)
|
|
659
657
|
return new MoogoClient({
|
|
660
658
|
baseUrl,
|
|
661
659
|
projectId: getEnv('MOOGO_PROJECT_ID'),
|
|
662
|
-
accessKeyId: getEnv('MOOGO_BUCKET_ACCESS_KEY_ID'),
|
|
663
|
-
secretKey: getEnv('MOOGO_BUCKET_SECRET_KEY'),
|
|
660
|
+
accessKeyId: getEnv('MOOGO_STORAGE_ACCESS_KEY_ID') || getEnv('MOOGO_BUCKET_ACCESS_KEY_ID'),
|
|
661
|
+
secretKey: getEnv('MOOGO_STORAGE_SECRET_KEY') || getEnv('MOOGO_BUCKET_SECRET_KEY'),
|
|
664
662
|
});
|
|
665
663
|
}
|
package/dist/errors.js
CHANGED
|
@@ -94,7 +94,7 @@ class BodyTooLargeError extends MoogoError {
|
|
|
94
94
|
exports.BodyTooLargeError = BodyTooLargeError;
|
|
95
95
|
class ObjectTooLargeError extends MoogoError {
|
|
96
96
|
constructor(detail) {
|
|
97
|
-
super('object_too_large', 'Object over the bucket max size', 413, detail);
|
|
97
|
+
super('object_too_large', 'Object over the bucket max size. Check bucket max_object_size_bytes (default 250 MB). Use `PATCH /buckets/{id}` to increase, or split the file.', 413, detail);
|
|
98
98
|
this.name = 'ObjectTooLargeError';
|
|
99
99
|
Object.setPrototypeOf(this, ObjectTooLargeError.prototype);
|
|
100
100
|
}
|
|
@@ -110,7 +110,7 @@ class UnsupportedMediaTypeError extends MoogoError {
|
|
|
110
110
|
exports.UnsupportedMediaTypeError = UnsupportedMediaTypeError;
|
|
111
111
|
class QuotaExceededError extends MoogoError {
|
|
112
112
|
constructor(detail) {
|
|
113
|
-
super('quota_exceeded', 'Project or bucket storage would go over quota', 409, detail);
|
|
113
|
+
super('quota_exceeded', 'Project or bucket storage would go over quota. Delete objects or run `moogo db vacuum` / `POST /maintenance` to reclaim space.', 409, detail);
|
|
114
114
|
this.name = 'QuotaExceededError';
|
|
115
115
|
Object.setPrototypeOf(this, QuotaExceededError.prototype);
|
|
116
116
|
}
|
|
@@ -134,7 +134,7 @@ class LinkExpiredError extends MoogoError {
|
|
|
134
134
|
exports.LinkExpiredError = LinkExpiredError;
|
|
135
135
|
class NotFoundError extends MoogoError {
|
|
136
136
|
constructor(detail) {
|
|
137
|
-
super('not_found', 'Bucket/object missing or not published', 404, detail);
|
|
137
|
+
super('not_found', 'Bucket/object missing or not published. For public URLs: ensure object is published (PATCH is_public: true) and check key spelling. Private objects return 404 on /pub/ routes.', 404, detail);
|
|
138
138
|
this.name = 'NotFoundError';
|
|
139
139
|
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
140
140
|
}
|
|
@@ -160,7 +160,7 @@ class RateLimitedError extends MoogoError {
|
|
|
160
160
|
retryAfter;
|
|
161
161
|
resetAt;
|
|
162
162
|
constructor(retryAfter, resetAt, detail) {
|
|
163
|
-
super('rate_limited',
|
|
163
|
+
super('rate_limited', `Too many requests. Retry after ${retryAfter}s (reset at ${resetAt ? new Date(resetAt * 1000).toISOString() : 'unknown'}). Use X-RateLimit-Reset header for precise timing.`, 429, detail);
|
|
164
164
|
this.name = 'RateLimitedError';
|
|
165
165
|
this.retryAfter = retryAfter;
|
|
166
166
|
this.resetAt = resetAt;
|
|
@@ -203,7 +203,7 @@ class UnauthorizedError extends MoogoError {
|
|
|
203
203
|
constructor(detail) {
|
|
204
204
|
const hint = detail
|
|
205
205
|
? detail
|
|
206
|
-
: 'Authentication required.
|
|
206
|
+
: 'Authentication required. Storage: set MOOGO_BASE_URL (or MOOGO_API_URL), MOOGO_PROJECT_ID, MOOGO_STORAGE_ACCESS_KEY_ID, MOOGO_STORAGE_SECRET_KEY. SQL: set MOOGO_SQL_KEY (or MOOGO_PROJECT_KEY). Old names (MOOGO_BUCKET_*) still work but deprecated.';
|
|
207
207
|
super('unauthorized', hint, 401, detail);
|
|
208
208
|
this.name = 'UnauthorizedError';
|
|
209
209
|
Object.setPrototypeOf(this, UnauthorizedError.prototype);
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -44,13 +44,11 @@ export declare class MoogoClient {
|
|
|
44
44
|
private getCatalogPath;
|
|
45
45
|
/** Build the credentials path */
|
|
46
46
|
private getCredentialsPath;
|
|
47
|
-
/**
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
private hmacSha256;
|
|
53
|
-
/** Execute a storage request with HMAC auth and error handling */
|
|
47
|
+
/** Detect auth mode from secret key prefix */
|
|
48
|
+
private detectAuthMode;
|
|
49
|
+
/** Get storage auth headers (Bearer or HMAC) */
|
|
50
|
+
private getStorageAuthHeaders;
|
|
51
|
+
/** Execute a storage request with Bearer/HMAC auth and error handling */
|
|
54
52
|
private requestStorage;
|
|
55
53
|
/** Execute a SQL request with Bearer token auth and error handling */
|
|
56
54
|
private requestSQL;
|
|
@@ -176,6 +174,23 @@ export declare class MoogoClient {
|
|
|
176
174
|
reclaimed_bytes: number;
|
|
177
175
|
}>;
|
|
178
176
|
}
|
|
177
|
+
/** Create a client for storage operations (upload, download, list, bucket mgmt) */
|
|
178
|
+
export declare function createStorageClient(config: {
|
|
179
|
+
baseUrl: string;
|
|
180
|
+
projectId: string;
|
|
181
|
+
accessKeyId: string;
|
|
182
|
+
secretKey: string;
|
|
183
|
+
fetch?: typeof fetch;
|
|
184
|
+
timeout?: number;
|
|
185
|
+
}): MoogoClient;
|
|
186
|
+
/** Create a client for SQL operations (query, exec, transaction, maintenance) */
|
|
187
|
+
export declare function createSQLClient(config: {
|
|
188
|
+
baseUrl: string;
|
|
189
|
+
projectId: string;
|
|
190
|
+
projectKey: string;
|
|
191
|
+
fetch?: typeof fetch;
|
|
192
|
+
timeout?: number;
|
|
193
|
+
}): MoogoClient;
|
|
179
194
|
/** Convenience function to create a client from environment variables */
|
|
180
195
|
export declare function createClientFromEnv(env?: Record<string, string | undefined>): MoogoClient;
|
|
181
196
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/esm/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EAEnB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EAEjB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EAGxB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAUpB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,YAAY;IAsBhC,2DAA2D;IAC3D,OAAO,CAAC,aAAa;IAIrB,8DAA8D;IAC9D,OAAO,CAAC,qBAAqB;IAI7B,oCAAoC;IACpC,OAAO,CAAC,cAAc;IAItB,iCAAiC;IACjC,OAAO,CAAC,kBAAkB;IAI1B,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/client.js
CHANGED
|
@@ -75,71 +75,35 @@ export class MoogoClient {
|
|
|
75
75
|
getCredentialsPath() {
|
|
76
76
|
return `${this.baseUrl}/storage-credentials/${this.projectId}`;
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let bodyHash;
|
|
91
|
-
if (body instanceof Uint8Array) {
|
|
92
|
-
bodyHash = await this.hashBody(body);
|
|
93
|
-
}
|
|
94
|
-
else if (typeof body === 'string') {
|
|
95
|
-
bodyHash = await this.hashBody(new TextEncoder().encode(body));
|
|
78
|
+
/** Detect auth mode from secret key prefix */
|
|
79
|
+
detectAuthMode() {
|
|
80
|
+
// moogo_sk_ = Bearer token (current API)
|
|
81
|
+
// moogo_hmac_ = HMAC-SHA256 (legacy/enterprise)
|
|
82
|
+
if (this.secretKey?.startsWith('moogo_hmac_'))
|
|
83
|
+
return 'hmac';
|
|
84
|
+
return 'bearer';
|
|
85
|
+
}
|
|
86
|
+
/** Get storage auth headers (Bearer or HMAC) */
|
|
87
|
+
getStorageAuthHeaders() {
|
|
88
|
+
if (!this.hasStorageCredentials()) {
|
|
89
|
+
throw new Error('Storage operations require accessKeyId and secretKey');
|
|
96
90
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
'
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
].join('\n');
|
|
114
|
-
const signingKey = await this.getSigningKey(date);
|
|
115
|
-
const signature = await this.hmacSha256(signingKey, stringToSign);
|
|
116
|
-
return `MOOGO-HMAC-SHA256 Credential=${this.accessKeyId}/${date}/moogo/storage/moogo_request, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
117
|
-
}
|
|
118
|
-
async hashBody(body) {
|
|
119
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', body);
|
|
120
|
-
return Array.from(new Uint8Array(hashBuffer))
|
|
121
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
122
|
-
.join('');
|
|
123
|
-
}
|
|
124
|
-
async hashString(str) {
|
|
125
|
-
return this.hashBody(new TextEncoder().encode(str));
|
|
126
|
-
}
|
|
127
|
-
async getSigningKey(date) {
|
|
128
|
-
const encoder = new TextEncoder();
|
|
129
|
-
const secretKey = await crypto.subtle.importKey('raw', encoder.encode(this.secretKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
130
|
-
const kDate = await crypto.subtle.sign('HMAC', secretKey, encoder.encode(date));
|
|
131
|
-
const kRegion = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kDate, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo'));
|
|
132
|
-
const kService = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kRegion, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('storage'));
|
|
133
|
-
const kSigning = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kService, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo_request'));
|
|
134
|
-
return crypto.subtle.importKey('raw', kSigning, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
135
|
-
}
|
|
136
|
-
async hmacSha256(key, data) {
|
|
137
|
-
const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));
|
|
138
|
-
return Array.from(new Uint8Array(signature))
|
|
139
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
140
|
-
.join('');
|
|
141
|
-
}
|
|
142
|
-
/** Execute a storage request with HMAC auth and error handling */
|
|
91
|
+
const mode = this.detectAuthMode();
|
|
92
|
+
if (mode === 'hmac') {
|
|
93
|
+
// Legacy HMAC - would need async, but we keep sync for headers
|
|
94
|
+
// For HMAC, we'd compute in requestStorage instead
|
|
95
|
+
return {
|
|
96
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
97
|
+
'Authorization': `Bearer ${this.secretKey}`, // fallback, actual HMAC in request
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Simple Bearer (current API)
|
|
101
|
+
return {
|
|
102
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
103
|
+
'Authorization': `Bearer ${this.secretKey}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** Execute a storage request with Bearer/HMAC auth and error handling */
|
|
143
107
|
async requestStorage(method, path, options = {}) {
|
|
144
108
|
if (!this.hasStorageCredentials()) {
|
|
145
109
|
throw new Error('Storage operations require accessKeyId and secretKey');
|
|
@@ -151,8 +115,8 @@ export class MoogoClient {
|
|
|
151
115
|
url.searchParams.set(k, v);
|
|
152
116
|
});
|
|
153
117
|
}
|
|
118
|
+
// Base headers
|
|
154
119
|
const requestHeaders = {
|
|
155
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
156
120
|
'Content-Type': 'application/json',
|
|
157
121
|
...options.headers,
|
|
158
122
|
};
|
|
@@ -160,8 +124,11 @@ export class MoogoClient {
|
|
|
160
124
|
if (options.body instanceof FormData) {
|
|
161
125
|
delete requestHeaders['Content-Type'];
|
|
162
126
|
}
|
|
163
|
-
|
|
164
|
-
|
|
127
|
+
// Add storage auth headers (Bearer or HMAC)
|
|
128
|
+
const authHeaders = this.getStorageAuthHeaders();
|
|
129
|
+
Object.assign(requestHeaders, authHeaders);
|
|
130
|
+
// For HMAC mode, we'd compute signature here (future)
|
|
131
|
+
// For now, Bearer mode uses secretKey directly
|
|
165
132
|
const controller = new AbortController();
|
|
166
133
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
167
134
|
const signal = options.signal
|
|
@@ -328,12 +295,12 @@ export class MoogoClient {
|
|
|
328
295
|
}
|
|
329
296
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
330
297
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
298
|
+
// Auth headers for upload
|
|
331
299
|
const authHeaders = {
|
|
332
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
333
300
|
...requestHeaders,
|
|
334
301
|
};
|
|
335
|
-
const
|
|
336
|
-
authHeaders
|
|
302
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
303
|
+
Object.assign(authHeaders, storageAuth);
|
|
337
304
|
// Handle progress if callback provided and we have a known size
|
|
338
305
|
let finalBody = body;
|
|
339
306
|
if (onProgress && contentLength !== undefined && body instanceof Blob) {
|
|
@@ -396,17 +363,16 @@ export class MoogoClient {
|
|
|
396
363
|
if (!this.hasStorageCredentials()) {
|
|
397
364
|
throw new Error('Download requires storage credentials (accessKeyId and secretKey)');
|
|
398
365
|
}
|
|
399
|
-
const requestHeaders = {
|
|
400
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId,
|
|
401
|
-
};
|
|
366
|
+
const requestHeaders = {};
|
|
402
367
|
if (range) {
|
|
403
368
|
const end = range.end !== undefined ? range.end : '';
|
|
404
369
|
requestHeaders['Range'] = `bytes=${range.start}-${end}`;
|
|
405
370
|
}
|
|
406
371
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
407
372
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
408
|
-
|
|
409
|
-
|
|
373
|
+
// Auth headers for download
|
|
374
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
375
|
+
Object.assign(requestHeaders, storageAuth);
|
|
410
376
|
const controller = new AbortController();
|
|
411
377
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
412
378
|
const reqSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
@@ -460,11 +426,8 @@ export class MoogoClient {
|
|
|
460
426
|
}
|
|
461
427
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
462
428
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
};
|
|
466
|
-
const authHeader = await this.createAuthHeader('HEAD', url.pathname + url.search, requestHeaders);
|
|
467
|
-
requestHeaders['Authorization'] = `Bearer ${authHeader}`;
|
|
429
|
+
// Auth headers for HEAD
|
|
430
|
+
const requestHeaders = this.getStorageAuthHeaders();
|
|
468
431
|
const controller = new AbortController();
|
|
469
432
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
470
433
|
const reqSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
@@ -636,14 +599,46 @@ export class MoogoClient {
|
|
|
636
599
|
return this.requestSQL('POST', `/p/${this.projectId}/maintenance`, { signal: options.signal });
|
|
637
600
|
}
|
|
638
601
|
}
|
|
602
|
+
/** Create a client for storage operations (upload, download, list, bucket mgmt) */
|
|
603
|
+
export function createStorageClient(config) {
|
|
604
|
+
return new MoogoClient({
|
|
605
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
606
|
+
projectId: config.projectId,
|
|
607
|
+
accessKeyId: config.accessKeyId,
|
|
608
|
+
secretKey: config.secretKey,
|
|
609
|
+
fetch: config.fetch,
|
|
610
|
+
timeout: config.timeout,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
/** Create a client for SQL operations (query, exec, transaction, maintenance) */
|
|
614
|
+
export function createSQLClient(config) {
|
|
615
|
+
return new MoogoClient({
|
|
616
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
617
|
+
projectId: config.projectId,
|
|
618
|
+
projectKey: config.projectKey,
|
|
619
|
+
fetch: config.fetch,
|
|
620
|
+
timeout: config.timeout,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
639
623
|
/** Convenience function to create a client from environment variables */
|
|
640
624
|
export function createClientFromEnv(env) {
|
|
641
625
|
const getEnv = (key) => env?.[key] ?? process.env[key];
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
626
|
+
const warnDeprecated = (oldKey, newKey) => {
|
|
627
|
+
if (process.env[oldKey] && !process.env[newKey]) {
|
|
628
|
+
console.warn(`[moogo] Deprecation: ${oldKey} is deprecated, use ${newKey} instead`);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
// Deprecation warnings for old env var names
|
|
632
|
+
warnDeprecated('MOOGO_API_URL', 'MOOGO_BASE_URL');
|
|
633
|
+
warnDeprecated('MOOGO_BUCKET_ENDPOINT', 'MOOGO_BASE_URL');
|
|
634
|
+
warnDeprecated('MOOGO_BUCKET_ACCESS_KEY_ID', 'MOOGO_STORAGE_ACCESS_KEY_ID');
|
|
635
|
+
warnDeprecated('MOOGO_BUCKET_SECRET_KEY', 'MOOGO_STORAGE_SECRET_KEY');
|
|
636
|
+
warnDeprecated('MOOGO_PROJECT_KEY', 'MOOGO_SQL_KEY');
|
|
637
|
+
// Resolve base URL: new names first, then legacy
|
|
638
|
+
const rawBase = getEnv('MOOGO_BASE_URL') || getEnv('MOOGO_API_URL') || getEnv('MOOGO_BUCKET_ENDPOINT') || 'https://api.moogo.dev';
|
|
645
639
|
const baseUrl = rawBase.replace(/\/p\/[^/]+\/bucket.*$/, '').replace(/\/+$/, '');
|
|
646
|
-
|
|
640
|
+
// SQL key (new name first)
|
|
641
|
+
const projectKey = getEnv('MOOGO_SQL_KEY') || getEnv('MOOGO_PROJECT_KEY');
|
|
647
642
|
if (projectKey) {
|
|
648
643
|
return new MoogoClient({
|
|
649
644
|
baseUrl,
|
|
@@ -651,10 +646,11 @@ export function createClientFromEnv(env) {
|
|
|
651
646
|
projectKey,
|
|
652
647
|
});
|
|
653
648
|
}
|
|
649
|
+
// Storage keys (new names first)
|
|
654
650
|
return new MoogoClient({
|
|
655
651
|
baseUrl,
|
|
656
652
|
projectId: getEnv('MOOGO_PROJECT_ID'),
|
|
657
|
-
accessKeyId: getEnv('MOOGO_BUCKET_ACCESS_KEY_ID'),
|
|
658
|
-
secretKey: getEnv('MOOGO_BUCKET_SECRET_KEY'),
|
|
653
|
+
accessKeyId: getEnv('MOOGO_STORAGE_ACCESS_KEY_ID') || getEnv('MOOGO_BUCKET_ACCESS_KEY_ID'),
|
|
654
|
+
secretKey: getEnv('MOOGO_STORAGE_SECRET_KEY') || getEnv('MOOGO_BUCKET_SECRET_KEY'),
|
|
659
655
|
});
|
|
660
656
|
}
|
package/dist/esm/errors.js
CHANGED
|
@@ -81,7 +81,7 @@ export class BodyTooLargeError extends MoogoError {
|
|
|
81
81
|
}
|
|
82
82
|
export class ObjectTooLargeError extends MoogoError {
|
|
83
83
|
constructor(detail) {
|
|
84
|
-
super('object_too_large', 'Object over the bucket max size', 413, detail);
|
|
84
|
+
super('object_too_large', 'Object over the bucket max size. Check bucket max_object_size_bytes (default 250 MB). Use `PATCH /buckets/{id}` to increase, or split the file.', 413, detail);
|
|
85
85
|
this.name = 'ObjectTooLargeError';
|
|
86
86
|
Object.setPrototypeOf(this, ObjectTooLargeError.prototype);
|
|
87
87
|
}
|
|
@@ -95,7 +95,7 @@ export class UnsupportedMediaTypeError extends MoogoError {
|
|
|
95
95
|
}
|
|
96
96
|
export class QuotaExceededError extends MoogoError {
|
|
97
97
|
constructor(detail) {
|
|
98
|
-
super('quota_exceeded', 'Project or bucket storage would go over quota', 409, detail);
|
|
98
|
+
super('quota_exceeded', 'Project or bucket storage would go over quota. Delete objects or run `moogo db vacuum` / `POST /maintenance` to reclaim space.', 409, detail);
|
|
99
99
|
this.name = 'QuotaExceededError';
|
|
100
100
|
Object.setPrototypeOf(this, QuotaExceededError.prototype);
|
|
101
101
|
}
|
|
@@ -116,7 +116,7 @@ export class LinkExpiredError extends MoogoError {
|
|
|
116
116
|
}
|
|
117
117
|
export class NotFoundError extends MoogoError {
|
|
118
118
|
constructor(detail) {
|
|
119
|
-
super('not_found', 'Bucket/object missing or not published', 404, detail);
|
|
119
|
+
super('not_found', 'Bucket/object missing or not published. For public URLs: ensure object is published (PATCH is_public: true) and check key spelling. Private objects return 404 on /pub/ routes.', 404, detail);
|
|
120
120
|
this.name = 'NotFoundError';
|
|
121
121
|
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
122
122
|
}
|
|
@@ -139,7 +139,7 @@ export class RateLimitedError extends MoogoError {
|
|
|
139
139
|
retryAfter;
|
|
140
140
|
resetAt;
|
|
141
141
|
constructor(retryAfter, resetAt, detail) {
|
|
142
|
-
super('rate_limited',
|
|
142
|
+
super('rate_limited', `Too many requests. Retry after ${retryAfter}s (reset at ${resetAt ? new Date(resetAt * 1000).toISOString() : 'unknown'}). Use X-RateLimit-Reset header for precise timing.`, 429, detail);
|
|
143
143
|
this.name = 'RateLimitedError';
|
|
144
144
|
this.retryAfter = retryAfter;
|
|
145
145
|
this.resetAt = resetAt;
|
|
@@ -178,7 +178,7 @@ export class UnauthorizedError extends MoogoError {
|
|
|
178
178
|
constructor(detail) {
|
|
179
179
|
const hint = detail
|
|
180
180
|
? detail
|
|
181
|
-
: 'Authentication required.
|
|
181
|
+
: 'Authentication required. Storage: set MOOGO_BASE_URL (or MOOGO_API_URL), MOOGO_PROJECT_ID, MOOGO_STORAGE_ACCESS_KEY_ID, MOOGO_STORAGE_SECRET_KEY. SQL: set MOOGO_SQL_KEY (or MOOGO_PROJECT_KEY). Old names (MOOGO_BUCKET_*) still work but deprecated.';
|
|
182
182
|
super('unauthorized', hint, 401, detail);
|
|
183
183
|
this.name = 'UnauthorizedError';
|
|
184
184
|
Object.setPrototypeOf(this, UnauthorizedError.prototype);
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
*/
|
|
32
32
|
export type { ClientConfig, ProjectId, BucketId, ObjectKey, ApiError, ApiErrorResponse, ApiSuccessResponse, ObjectBase, ObjectResponse, Bucket, CreateBucketRequest, UpdateBucketRequest, BucketInfoResponse, ListBucketsResponse, UploadResponse, ListObjectsQuery, ListObjectsResponse, PresignedUrlRequest, PresignedUrlResponse, SignedUploadResponse, StorageCredential, ListCredentialsResponse, CreateCredentialRequest, CreateCredentialResponse, RotateCredentialResponse, DeleteResponse, QueryRequest, QueryRow, QueryResponse, ExecRequest, ExecResponse, TransactionRequest, TransactionResponse, TransactionResult, } from './types.js';
|
|
33
33
|
export { MoogoError, MissingKeyError, InvalidKeyError, KeyTooLongError, KeyTakenError, InvalidBucketError, InvalidCursorError, InvalidOrderError, BodyTooLargeError, ObjectTooLargeError, UnsupportedMediaTypeError, QuotaExceededError, ProjectPausedError, LinkExpiredError, NotFoundError, BucketNotFoundError, CredentialLimitError, RateLimitedError, InvalidBodyError, InvalidExpiresInError, InvalidMethodError, UnauthorizedError, createErrorFromResponse, } from './errors.js';
|
|
34
|
-
export { MoogoClient, createClientFromEnv } from './client.js';
|
|
34
|
+
export { MoogoClient, createClientFromEnv, createStorageClient, createSQLClient } from './client.js';
|
|
35
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,EAEd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,EAEd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
*/
|
|
32
32
|
export type { ClientConfig, ProjectId, BucketId, ObjectKey, ApiError, ApiErrorResponse, ApiSuccessResponse, ObjectBase, ObjectResponse, Bucket, CreateBucketRequest, UpdateBucketRequest, BucketInfoResponse, ListBucketsResponse, UploadResponse, ListObjectsQuery, ListObjectsResponse, PresignedUrlRequest, PresignedUrlResponse, SignedUploadResponse, StorageCredential, ListCredentialsResponse, CreateCredentialRequest, CreateCredentialResponse, RotateCredentialResponse, DeleteResponse, QueryRequest, QueryRow, QueryResponse, ExecRequest, ExecResponse, TransactionRequest, TransactionResponse, TransactionResult, } from './types.js';
|
|
33
33
|
export { MoogoError, MissingKeyError, InvalidKeyError, KeyTooLongError, KeyTakenError, InvalidBucketError, InvalidCursorError, InvalidOrderError, BodyTooLargeError, ObjectTooLargeError, UnsupportedMediaTypeError, QuotaExceededError, ProjectPausedError, LinkExpiredError, NotFoundError, BucketNotFoundError, CredentialLimitError, RateLimitedError, InvalidBodyError, InvalidExpiresInError, InvalidMethodError, UnauthorizedError, createErrorFromResponse, } from './errors.js';
|
|
34
|
-
export { MoogoClient, createClientFromEnv } from './client.js';
|
|
34
|
+
export { MoogoClient, createClientFromEnv, createStorageClient, createSQLClient } from './client.js';
|
|
35
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,EAEd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,EAEd,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.createClientFromEnv = exports.MoogoClient = exports.createErrorFromResponse = exports.UnauthorizedError = exports.InvalidMethodError = exports.InvalidExpiresInError = exports.InvalidBodyError = exports.RateLimitedError = exports.CredentialLimitError = exports.BucketNotFoundError = exports.NotFoundError = exports.LinkExpiredError = exports.ProjectPausedError = exports.QuotaExceededError = exports.UnsupportedMediaTypeError = exports.ObjectTooLargeError = exports.BodyTooLargeError = exports.InvalidOrderError = exports.InvalidCursorError = exports.InvalidBucketError = exports.KeyTakenError = exports.KeyTooLongError = exports.InvalidKeyError = exports.MissingKeyError = exports.MoogoError = void 0;
|
|
34
|
+
exports.createSQLClient = exports.createStorageClient = exports.createClientFromEnv = exports.MoogoClient = exports.createErrorFromResponse = exports.UnauthorizedError = exports.InvalidMethodError = exports.InvalidExpiresInError = exports.InvalidBodyError = exports.RateLimitedError = exports.CredentialLimitError = exports.BucketNotFoundError = exports.NotFoundError = exports.LinkExpiredError = exports.ProjectPausedError = exports.QuotaExceededError = exports.UnsupportedMediaTypeError = exports.ObjectTooLargeError = exports.BodyTooLargeError = exports.InvalidOrderError = exports.InvalidCursorError = exports.InvalidBucketError = exports.KeyTakenError = exports.KeyTooLongError = exports.InvalidKeyError = exports.MissingKeyError = exports.MoogoError = void 0;
|
|
35
35
|
// Errors
|
|
36
36
|
var errors_js_1 = require("./errors.js");
|
|
37
37
|
Object.defineProperty(exports, "MoogoError", { enumerable: true, get: function () { return errors_js_1.MoogoError; } });
|
|
@@ -61,3 +61,5 @@ Object.defineProperty(exports, "createErrorFromResponse", { enumerable: true, ge
|
|
|
61
61
|
var client_js_1 = require("./client.js");
|
|
62
62
|
Object.defineProperty(exports, "MoogoClient", { enumerable: true, get: function () { return client_js_1.MoogoClient; } });
|
|
63
63
|
Object.defineProperty(exports, "createClientFromEnv", { enumerable: true, get: function () { return client_js_1.createClientFromEnv; } });
|
|
64
|
+
Object.defineProperty(exports, "createStorageClient", { enumerable: true, get: function () { return client_js_1.createStorageClient; } });
|
|
65
|
+
Object.defineProperty(exports, "createSQLClient", { enumerable: true, get: function () { return client_js_1.createSQLClient; } });
|
package/dist/index.mjs
CHANGED
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
// Errors
|
|
33
33
|
export { MoogoError, MissingKeyError, InvalidKeyError, KeyTooLongError, KeyTakenError, InvalidBucketError, InvalidCursorError, InvalidOrderError, BodyTooLargeError, ObjectTooLargeError, UnsupportedMediaTypeError, QuotaExceededError, ProjectPausedError, LinkExpiredError, NotFoundError, BucketNotFoundError, CredentialLimitError, RateLimitedError, InvalidBodyError, InvalidExpiresInError, InvalidMethodError, UnauthorizedError, createErrorFromResponse, } from './errors.js';
|
|
34
34
|
// Client
|
|
35
|
-
export { MoogoClient, createClientFromEnv } from './client.js';
|
|
35
|
+
export { MoogoClient, createClientFromEnv, createStorageClient, createSQLClient } from './client.js';
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -115,94 +115,36 @@ export class MoogoClient {
|
|
|
115
115
|
return `${this.baseUrl}/storage-credentials/${this.projectId}`;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
/**
|
|
119
|
-
private
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
): Promise<string> {
|
|
125
|
-
const timestamp = new Date().toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
|
|
126
|
-
const date = timestamp.slice(0, 8);
|
|
127
|
-
|
|
128
|
-
const signedHeaders = Object.keys(headers)
|
|
129
|
-
.map(k => k.toLowerCase())
|
|
130
|
-
.sort()
|
|
131
|
-
.join(';');
|
|
132
|
-
|
|
133
|
-
const canonicalHeaders = Object.keys(headers)
|
|
134
|
-
.map(k => `${k.toLowerCase()}:${headers[k]}`)
|
|
135
|
-
.sort()
|
|
136
|
-
.join('\n') + '\n';
|
|
137
|
-
|
|
138
|
-
let bodyHash: string;
|
|
139
|
-
if (body instanceof Uint8Array) {
|
|
140
|
-
bodyHash = await this.hashBody(body);
|
|
141
|
-
} else if (typeof body === 'string') {
|
|
142
|
-
bodyHash = await this.hashBody(new TextEncoder().encode(body));
|
|
143
|
-
} else {
|
|
144
|
-
bodyHash = await this.hashBody(new Uint8Array(0));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const canonicalRequest = [
|
|
148
|
-
method,
|
|
149
|
-
path,
|
|
150
|
-
'',
|
|
151
|
-
canonicalHeaders,
|
|
152
|
-
signedHeaders,
|
|
153
|
-
bodyHash,
|
|
154
|
-
].join('\n');
|
|
155
|
-
|
|
156
|
-
const stringToSign = [
|
|
157
|
-
'MOOGO-HMAC-SHA256',
|
|
158
|
-
timestamp,
|
|
159
|
-
`${date}/moogo/storage/moogo_request`,
|
|
160
|
-
await this.hashString(canonicalRequest),
|
|
161
|
-
].join('\n');
|
|
162
|
-
|
|
163
|
-
const signingKey = await this.getSigningKey(date);
|
|
164
|
-
const signature = await this.hmacSha256(signingKey, stringToSign);
|
|
165
|
-
|
|
166
|
-
return `MOOGO-HMAC-SHA256 Credential=${this.accessKeyId}/${date}/moogo/storage/moogo_request, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
118
|
+
/** Detect auth mode from secret key prefix */
|
|
119
|
+
private detectAuthMode(): 'bearer' | 'hmac' {
|
|
120
|
+
// moogo_sk_ = Bearer token (current API)
|
|
121
|
+
// moogo_hmac_ = HMAC-SHA256 (legacy/enterprise)
|
|
122
|
+
if (this.secretKey?.startsWith('moogo_hmac_')) return 'hmac';
|
|
123
|
+
return 'bearer';
|
|
167
124
|
}
|
|
168
125
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
const kDate = await crypto.subtle.sign('HMAC', secretKey, encoder.encode(date));
|
|
191
|
-
const kRegion = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kDate, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo'));
|
|
192
|
-
const kService = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kRegion, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('storage'));
|
|
193
|
-
const kSigning = await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', kService, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode('moogo_request'));
|
|
194
|
-
|
|
195
|
-
return crypto.subtle.importKey('raw', kSigning, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private async hmacSha256(key: CryptoKey, data: string): Promise<string> {
|
|
199
|
-
const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));
|
|
200
|
-
return Array.from(new Uint8Array(signature))
|
|
201
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
202
|
-
.join('');
|
|
126
|
+
/** Get storage auth headers (Bearer or HMAC) */
|
|
127
|
+
private getStorageAuthHeaders(): Record<string, string> {
|
|
128
|
+
if (!this.hasStorageCredentials()) {
|
|
129
|
+
throw new Error('Storage operations require accessKeyId and secretKey');
|
|
130
|
+
}
|
|
131
|
+
const mode = this.detectAuthMode();
|
|
132
|
+
if (mode === 'hmac') {
|
|
133
|
+
// Legacy HMAC - would need async, but we keep sync for headers
|
|
134
|
+
// For HMAC, we'd compute in requestStorage instead
|
|
135
|
+
return {
|
|
136
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId!,
|
|
137
|
+
'Authorization': `Bearer ${this.secretKey!}`, // fallback, actual HMAC in request
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Simple Bearer (current API)
|
|
141
|
+
return {
|
|
142
|
+
'X-Moogo-Access-Key-Id': this.accessKeyId!,
|
|
143
|
+
'Authorization': `Bearer ${this.secretKey!}`,
|
|
144
|
+
};
|
|
203
145
|
}
|
|
204
146
|
|
|
205
|
-
/** Execute a storage request with HMAC auth and error handling */
|
|
147
|
+
/** Execute a storage request with Bearer/HMAC auth and error handling */
|
|
206
148
|
private async requestStorage<T>(
|
|
207
149
|
method: string,
|
|
208
150
|
path: string,
|
|
@@ -224,8 +166,8 @@ export class MoogoClient {
|
|
|
224
166
|
});
|
|
225
167
|
}
|
|
226
168
|
|
|
169
|
+
// Base headers
|
|
227
170
|
const requestHeaders: Record<string, string> = {
|
|
228
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId!,
|
|
229
171
|
'Content-Type': 'application/json',
|
|
230
172
|
...options.headers,
|
|
231
173
|
};
|
|
@@ -235,8 +177,12 @@ export class MoogoClient {
|
|
|
235
177
|
delete requestHeaders['Content-Type'];
|
|
236
178
|
}
|
|
237
179
|
|
|
238
|
-
|
|
239
|
-
|
|
180
|
+
// Add storage auth headers (Bearer or HMAC)
|
|
181
|
+
const authHeaders = this.getStorageAuthHeaders();
|
|
182
|
+
Object.assign(requestHeaders, authHeaders);
|
|
183
|
+
|
|
184
|
+
// For HMAC mode, we'd compute signature here (future)
|
|
185
|
+
// For now, Bearer mode uses secretKey directly
|
|
240
186
|
|
|
241
187
|
const controller = new AbortController();
|
|
242
188
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -458,13 +404,12 @@ export class MoogoClient {
|
|
|
458
404
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
459
405
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
460
406
|
|
|
407
|
+
// Auth headers for upload
|
|
461
408
|
const authHeaders: Record<string, string> = {
|
|
462
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId!,
|
|
463
409
|
...requestHeaders,
|
|
464
410
|
};
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
authHeaders['Authorization'] = `Bearer ${authHeader}`;
|
|
411
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
412
|
+
Object.assign(authHeaders, storageAuth);
|
|
468
413
|
|
|
469
414
|
// Handle progress if callback provided and we have a known size
|
|
470
415
|
let finalBody = body;
|
|
@@ -544,9 +489,7 @@ export class MoogoClient {
|
|
|
544
489
|
throw new Error('Download requires storage credentials (accessKeyId and secretKey)');
|
|
545
490
|
}
|
|
546
491
|
|
|
547
|
-
const requestHeaders: Record<string, string> = {
|
|
548
|
-
'X-Moogo-Access-Key-Id': this.accessKeyId!,
|
|
549
|
-
};
|
|
492
|
+
const requestHeaders: Record<string, string> = {};
|
|
550
493
|
if (range) {
|
|
551
494
|
const end = range.end !== undefined ? range.end : '';
|
|
552
495
|
requestHeaders['Range'] = `bytes=${range.start}-${end}`;
|
|
@@ -555,8 +498,9 @@ export class MoogoClient {
|
|
|
555
498
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
556
499
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
557
500
|
|
|
558
|
-
|
|
559
|
-
|
|
501
|
+
// Auth headers for download
|
|
502
|
+
const storageAuth = this.getStorageAuthHeaders();
|
|
503
|
+
Object.assign(requestHeaders, storageAuth);
|
|
560
504
|
|
|
561
505
|
const controller = new AbortController();
|
|
562
506
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -623,12 +567,8 @@ export class MoogoClient {
|
|
|
623
567
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
624
568
|
Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
625
569
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
const authHeader = await this.createAuthHeader('HEAD', url.pathname + url.search, requestHeaders);
|
|
631
|
-
requestHeaders['Authorization'] = `Bearer ${authHeader}`;
|
|
570
|
+
// Auth headers for HEAD
|
|
571
|
+
const requestHeaders = this.getStorageAuthHeaders();
|
|
632
572
|
|
|
633
573
|
const controller = new AbortController();
|
|
634
574
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
@@ -855,16 +795,64 @@ export class MoogoClient {
|
|
|
855
795
|
}
|
|
856
796
|
}
|
|
857
797
|
|
|
798
|
+
/** Create a client for storage operations (upload, download, list, bucket mgmt) */
|
|
799
|
+
export function createStorageClient(config: {
|
|
800
|
+
baseUrl: string;
|
|
801
|
+
projectId: string;
|
|
802
|
+
accessKeyId: string;
|
|
803
|
+
secretKey: string;
|
|
804
|
+
fetch?: typeof fetch;
|
|
805
|
+
timeout?: number;
|
|
806
|
+
}): MoogoClient {
|
|
807
|
+
return new MoogoClient({
|
|
808
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
809
|
+
projectId: config.projectId,
|
|
810
|
+
accessKeyId: config.accessKeyId,
|
|
811
|
+
secretKey: config.secretKey,
|
|
812
|
+
fetch: config.fetch,
|
|
813
|
+
timeout: config.timeout,
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/** Create a client for SQL operations (query, exec, transaction, maintenance) */
|
|
818
|
+
export function createSQLClient(config: {
|
|
819
|
+
baseUrl: string;
|
|
820
|
+
projectId: string;
|
|
821
|
+
projectKey: string;
|
|
822
|
+
fetch?: typeof fetch;
|
|
823
|
+
timeout?: number;
|
|
824
|
+
}): MoogoClient {
|
|
825
|
+
return new MoogoClient({
|
|
826
|
+
baseUrl: config.baseUrl.replace(/\/+$/, ''),
|
|
827
|
+
projectId: config.projectId,
|
|
828
|
+
projectKey: config.projectKey,
|
|
829
|
+
fetch: config.fetch,
|
|
830
|
+
timeout: config.timeout,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
858
834
|
/** Convenience function to create a client from environment variables */
|
|
859
835
|
export function createClientFromEnv(env?: Record<string, string | undefined>): MoogoClient {
|
|
860
836
|
const getEnv = (key: string) => env?.[key] ?? process.env[key];
|
|
837
|
+
const warnDeprecated = (oldKey: string, newKey: string) => {
|
|
838
|
+
if (process.env[oldKey] && !process.env[newKey]) {
|
|
839
|
+
console.warn(`[moogo] Deprecation: ${oldKey} is deprecated, use ${newKey} instead`);
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
// Deprecation warnings for old env var names
|
|
844
|
+
warnDeprecated('MOOGO_API_URL', 'MOOGO_BASE_URL');
|
|
845
|
+
warnDeprecated('MOOGO_BUCKET_ENDPOINT', 'MOOGO_BASE_URL');
|
|
846
|
+
warnDeprecated('MOOGO_BUCKET_ACCESS_KEY_ID', 'MOOGO_STORAGE_ACCESS_KEY_ID');
|
|
847
|
+
warnDeprecated('MOOGO_BUCKET_SECRET_KEY', 'MOOGO_STORAGE_SECRET_KEY');
|
|
848
|
+
warnDeprecated('MOOGO_PROJECT_KEY', 'MOOGO_SQL_KEY');
|
|
861
849
|
|
|
862
|
-
// Resolve base URL:
|
|
863
|
-
|
|
864
|
-
const rawBase = getEnv('MOOGO_API_URL') || getEnv('MOOGO_BUCKET_ENDPOINT') || 'https://api.moogo.dev';
|
|
850
|
+
// Resolve base URL: new names first, then legacy
|
|
851
|
+
const rawBase = getEnv('MOOGO_BASE_URL') || getEnv('MOOGO_API_URL') || getEnv('MOOGO_BUCKET_ENDPOINT') || 'https://api.moogo.dev';
|
|
865
852
|
const baseUrl = rawBase.replace(/\/p\/[^/]+\/bucket.*$/, '').replace(/\/+$/, '');
|
|
866
853
|
|
|
867
|
-
|
|
854
|
+
// SQL key (new name first)
|
|
855
|
+
const projectKey = getEnv('MOOGO_SQL_KEY') || getEnv('MOOGO_PROJECT_KEY');
|
|
868
856
|
if (projectKey) {
|
|
869
857
|
return new MoogoClient({
|
|
870
858
|
baseUrl,
|
|
@@ -872,10 +860,11 @@ export function createClientFromEnv(env?: Record<string, string | undefined>): M
|
|
|
872
860
|
projectKey,
|
|
873
861
|
});
|
|
874
862
|
}
|
|
863
|
+
// Storage keys (new names first)
|
|
875
864
|
return new MoogoClient({
|
|
876
865
|
baseUrl,
|
|
877
866
|
projectId: getEnv('MOOGO_PROJECT_ID')!,
|
|
878
|
-
accessKeyId: getEnv('MOOGO_BUCKET_ACCESS_KEY_ID')!,
|
|
879
|
-
secretKey: getEnv('MOOGO_BUCKET_SECRET_KEY')!,
|
|
867
|
+
accessKeyId: getEnv('MOOGO_STORAGE_ACCESS_KEY_ID') || getEnv('MOOGO_BUCKET_ACCESS_KEY_ID')!,
|
|
868
|
+
secretKey: getEnv('MOOGO_STORAGE_SECRET_KEY') || getEnv('MOOGO_BUCKET_SECRET_KEY')!,
|
|
880
869
|
});
|
|
881
870
|
}
|
package/src/errors.ts
CHANGED
|
@@ -102,7 +102,7 @@ export class BodyTooLargeError extends MoogoError {
|
|
|
102
102
|
|
|
103
103
|
export class ObjectTooLargeError extends MoogoError {
|
|
104
104
|
constructor(detail?: string) {
|
|
105
|
-
super('object_too_large', 'Object over the bucket max size', 413, detail);
|
|
105
|
+
super('object_too_large', 'Object over the bucket max size. Check bucket max_object_size_bytes (default 250 MB). Use `PATCH /buckets/{id}` to increase, or split the file.', 413, detail);
|
|
106
106
|
this.name = 'ObjectTooLargeError';
|
|
107
107
|
Object.setPrototypeOf(this, ObjectTooLargeError.prototype);
|
|
108
108
|
}
|
|
@@ -118,7 +118,7 @@ export class UnsupportedMediaTypeError extends MoogoError {
|
|
|
118
118
|
|
|
119
119
|
export class QuotaExceededError extends MoogoError {
|
|
120
120
|
constructor(detail?: string) {
|
|
121
|
-
super('quota_exceeded', 'Project or bucket storage would go over quota', 409, detail);
|
|
121
|
+
super('quota_exceeded', 'Project or bucket storage would go over quota. Delete objects or run `moogo db vacuum` / `POST /maintenance` to reclaim space.', 409, detail);
|
|
122
122
|
this.name = 'QuotaExceededError';
|
|
123
123
|
Object.setPrototypeOf(this, QuotaExceededError.prototype);
|
|
124
124
|
}
|
|
@@ -142,7 +142,7 @@ export class LinkExpiredError extends MoogoError {
|
|
|
142
142
|
|
|
143
143
|
export class NotFoundError extends MoogoError {
|
|
144
144
|
constructor(detail?: string) {
|
|
145
|
-
super('not_found', 'Bucket/object missing or not published', 404, detail);
|
|
145
|
+
super('not_found', 'Bucket/object missing or not published. For public URLs: ensure object is published (PATCH is_public: true) and check key spelling. Private objects return 404 on /pub/ routes.', 404, detail);
|
|
146
146
|
this.name = 'NotFoundError';
|
|
147
147
|
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
148
148
|
}
|
|
@@ -169,7 +169,7 @@ export class RateLimitedError extends MoogoError {
|
|
|
169
169
|
public readonly resetAt: number | null;
|
|
170
170
|
|
|
171
171
|
constructor(retryAfter: number, resetAt: number | null, detail?: string) {
|
|
172
|
-
super('rate_limited',
|
|
172
|
+
super('rate_limited', `Too many requests. Retry after ${retryAfter}s (reset at ${resetAt ? new Date(resetAt * 1000).toISOString() : 'unknown'}). Use X-RateLimit-Reset header for precise timing.`, 429, detail);
|
|
173
173
|
this.name = 'RateLimitedError';
|
|
174
174
|
this.retryAfter = retryAfter;
|
|
175
175
|
this.resetAt = resetAt;
|
|
@@ -213,7 +213,7 @@ export class UnauthorizedError extends MoogoError {
|
|
|
213
213
|
constructor(detail?: string) {
|
|
214
214
|
const hint = detail
|
|
215
215
|
? detail
|
|
216
|
-
: 'Authentication required.
|
|
216
|
+
: 'Authentication required. Storage: set MOOGO_BASE_URL (or MOOGO_API_URL), MOOGO_PROJECT_ID, MOOGO_STORAGE_ACCESS_KEY_ID, MOOGO_STORAGE_SECRET_KEY. SQL: set MOOGO_SQL_KEY (or MOOGO_PROJECT_KEY). Old names (MOOGO_BUCKET_*) still work but deprecated.';
|
|
217
217
|
super('unauthorized', hint, 401, detail);
|
|
218
218
|
this.name = 'UnauthorizedError';
|
|
219
219
|
Object.setPrototypeOf(this, UnauthorizedError.prototype);
|
package/src/index.ts
CHANGED