get-db9 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,624 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ Db9AuthError: () => Db9AuthError,
34
+ Db9ConflictError: () => Db9ConflictError,
35
+ Db9Error: () => Db9Error,
36
+ Db9NotFoundError: () => Db9NotFoundError,
37
+ FileCredentialStore: () => FileCredentialStore,
38
+ MemoryCredentialStore: () => MemoryCredentialStore,
39
+ createAdminClient: () => createAdminClient,
40
+ createCustomerClient: () => createCustomerClient,
41
+ defaultCredentialStore: () => defaultCredentialStore,
42
+ instantDatabase: () => instantDatabase
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/errors.ts
47
+ var Db9Error = class _Db9Error extends Error {
48
+ statusCode;
49
+ response;
50
+ constructor(message, statusCode, response) {
51
+ super(message);
52
+ this.name = "Db9Error";
53
+ this.statusCode = statusCode;
54
+ this.response = response;
55
+ }
56
+ static async fromResponse(response) {
57
+ let message;
58
+ try {
59
+ const body = await response.json();
60
+ message = body.message || response.statusText;
61
+ } catch {
62
+ message = response.statusText;
63
+ }
64
+ switch (response.status) {
65
+ case 401:
66
+ return new Db9AuthError(message, response);
67
+ case 404:
68
+ return new Db9NotFoundError(message, response);
69
+ case 409:
70
+ return new Db9ConflictError(message, response);
71
+ default:
72
+ return new _Db9Error(message, response.status, response);
73
+ }
74
+ }
75
+ };
76
+ var Db9AuthError = class extends Db9Error {
77
+ constructor(message, response) {
78
+ super(message, 401, response);
79
+ this.name = "Db9AuthError";
80
+ }
81
+ };
82
+ var Db9NotFoundError = class extends Db9Error {
83
+ constructor(message, response) {
84
+ super(message, 404, response);
85
+ this.name = "Db9NotFoundError";
86
+ }
87
+ };
88
+ var Db9ConflictError = class extends Db9Error {
89
+ constructor(message, response) {
90
+ super(message, 409, response);
91
+ this.name = "Db9ConflictError";
92
+ }
93
+ };
94
+
95
+ // src/http.ts
96
+ function createHttpClient(options) {
97
+ const fetchFn = options.fetch ?? globalThis.fetch;
98
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
99
+ async function request(method, path, body, params) {
100
+ let url = `${baseUrl}${path}`;
101
+ if (params) {
102
+ const searchParams = new URLSearchParams();
103
+ for (const [key, value] of Object.entries(params)) {
104
+ if (value !== void 0) {
105
+ searchParams.set(key, value);
106
+ }
107
+ }
108
+ const qs = searchParams.toString();
109
+ if (qs) url += `?${qs}`;
110
+ }
111
+ const headers = {
112
+ "Content-Type": "application/json",
113
+ ...options.headers
114
+ };
115
+ const init = { method, headers };
116
+ if (body !== void 0) {
117
+ init.body = JSON.stringify(body);
118
+ }
119
+ const response = await fetchFn(url, init);
120
+ if (!response.ok) {
121
+ throw await Db9Error.fromResponse(response);
122
+ }
123
+ if (response.status === 204) {
124
+ return void 0;
125
+ }
126
+ return response.json();
127
+ }
128
+ return {
129
+ get: (path, params) => request("GET", path, void 0, params),
130
+ post: (path, body) => request("POST", path, body),
131
+ put: (path, body) => request("PUT", path, body),
132
+ del: (path) => request("DELETE", path)
133
+ };
134
+ }
135
+
136
+ // src/customer.ts
137
+ function createCustomerClient(options = {}) {
138
+ const baseUrl = options.baseUrl ?? "https://db9.shared.aws.tidbcloud.com/api";
139
+ let token = options.token;
140
+ let tokenLoaded = !!token;
141
+ const publicClient = createHttpClient({
142
+ baseUrl,
143
+ fetch: options.fetch
144
+ });
145
+ async function getAuthClient() {
146
+ if (!token && !tokenLoaded && options.credentialStore) {
147
+ const creds = await options.credentialStore.load();
148
+ if (creds) token = creds.token;
149
+ tokenLoaded = true;
150
+ }
151
+ if (!token) throw new Error("No authentication token available");
152
+ return createHttpClient({
153
+ baseUrl,
154
+ fetch: options.fetch,
155
+ headers: { Authorization: `Bearer ${token}` }
156
+ });
157
+ }
158
+ return {
159
+ auth: {
160
+ // Public endpoints (no token required)
161
+ register: (req) => publicClient.post("/customer/register", req),
162
+ login: (req) => publicClient.post("/customer/login", req),
163
+ anonymousRegister: () => publicClient.post(
164
+ "/customer/anonymous-register"
165
+ ),
166
+ anonymousRefresh: (req) => publicClient.post(
167
+ "/customer/anonymous-refresh",
168
+ req
169
+ ),
170
+ // Authenticated endpoints
171
+ me: async () => {
172
+ const client = await getAuthClient();
173
+ return client.get("/customer/me");
174
+ },
175
+ getAnonymousSecret: async () => {
176
+ const client = await getAuthClient();
177
+ return client.get(
178
+ "/customer/anonymous-secret"
179
+ );
180
+ },
181
+ claim: async (req) => {
182
+ const client = await getAuthClient();
183
+ return client.post("/customer/claim", req);
184
+ }
185
+ },
186
+ tokens: {
187
+ list: async () => {
188
+ const client = await getAuthClient();
189
+ return client.get("/customer/tokens");
190
+ },
191
+ revoke: async (tokenId) => {
192
+ const client = await getAuthClient();
193
+ return client.del(`/customer/tokens/${tokenId}`);
194
+ }
195
+ },
196
+ databases: {
197
+ // ── CRUD ──────────────────────────────────────────────────
198
+ create: async (req) => {
199
+ const client = await getAuthClient();
200
+ return client.post("/customer/databases", req);
201
+ },
202
+ list: async () => {
203
+ const client = await getAuthClient();
204
+ return client.get("/customer/databases");
205
+ },
206
+ get: async (databaseId) => {
207
+ const client = await getAuthClient();
208
+ return client.get(
209
+ `/customer/databases/${databaseId}`
210
+ );
211
+ },
212
+ delete: async (databaseId) => {
213
+ const client = await getAuthClient();
214
+ return client.del(
215
+ `/customer/databases/${databaseId}`
216
+ );
217
+ },
218
+ resetPassword: async (databaseId) => {
219
+ const client = await getAuthClient();
220
+ return client.post(
221
+ `/customer/databases/${databaseId}/reset-password`
222
+ );
223
+ },
224
+ observability: async (databaseId) => {
225
+ const client = await getAuthClient();
226
+ return client.get(
227
+ `/customer/databases/${databaseId}/observability`
228
+ );
229
+ },
230
+ // ── SQL Execution ─────────────────────────────────────────
231
+ sql: async (databaseId, query) => {
232
+ const client = await getAuthClient();
233
+ return client.post(
234
+ `/customer/databases/${databaseId}/sql`,
235
+ { query }
236
+ );
237
+ },
238
+ sqlFile: async (databaseId, fileContent) => {
239
+ const client = await getAuthClient();
240
+ return client.post(
241
+ `/customer/databases/${databaseId}/sql`,
242
+ { file_content: fileContent }
243
+ );
244
+ },
245
+ // ── Schema & Dump ─────────────────────────────────────────
246
+ schema: async (databaseId) => {
247
+ const client = await getAuthClient();
248
+ return client.get(
249
+ `/customer/databases/${databaseId}/schema`
250
+ );
251
+ },
252
+ dump: async (databaseId, req) => {
253
+ const client = await getAuthClient();
254
+ return client.post(
255
+ `/customer/databases/${databaseId}/dump`,
256
+ req
257
+ );
258
+ },
259
+ // ── Migrations ────────────────────────────────────────────
260
+ applyMigration: async (databaseId, req) => {
261
+ const client = await getAuthClient();
262
+ return client.post(
263
+ `/customer/databases/${databaseId}/migrations`,
264
+ req
265
+ );
266
+ },
267
+ listMigrations: async (databaseId) => {
268
+ const client = await getAuthClient();
269
+ return client.get(
270
+ `/customer/databases/${databaseId}/migrations`
271
+ );
272
+ },
273
+ // ── Branching ─────────────────────────────────────────────
274
+ branch: async (databaseId, req) => {
275
+ const client = await getAuthClient();
276
+ return client.post(
277
+ `/customer/databases/${databaseId}/branch`,
278
+ req
279
+ );
280
+ },
281
+ // ── User Management ───────────────────────────────────────
282
+ users: {
283
+ list: async (databaseId) => {
284
+ const client = await getAuthClient();
285
+ return client.get(
286
+ `/customer/databases/${databaseId}/users`
287
+ );
288
+ },
289
+ create: async (databaseId, req) => {
290
+ const client = await getAuthClient();
291
+ return client.post(
292
+ `/customer/databases/${databaseId}/users`,
293
+ req
294
+ );
295
+ },
296
+ delete: async (databaseId, username) => {
297
+ const client = await getAuthClient();
298
+ return client.del(
299
+ `/customer/databases/${databaseId}/users/${username}`
300
+ );
301
+ }
302
+ }
303
+ }
304
+ };
305
+ }
306
+
307
+ // src/credentials.ts
308
+ var import_toml = require("@iarna/toml");
309
+ var FileCredentialStore = class {
310
+ customPath;
311
+ /**
312
+ * @param path — Override the credential file location.
313
+ * Defaults to `~/.db9/credentials` (resolved lazily).
314
+ */
315
+ constructor(path) {
316
+ this.customPath = path;
317
+ }
318
+ /** Resolve the credential file path (lazy to avoid top-level `os` import). */
319
+ async resolvePath() {
320
+ if (this.customPath) return this.customPath;
321
+ const os = await import("os");
322
+ const nodePath = await import("path");
323
+ return nodePath.join(os.homedir(), ".db9", "credentials");
324
+ }
325
+ async load() {
326
+ const fs = await import("fs/promises");
327
+ const filePath = await this.resolvePath();
328
+ let content;
329
+ try {
330
+ content = await fs.readFile(filePath, "utf-8");
331
+ } catch (err) {
332
+ if (err.code === "ENOENT") return null;
333
+ throw err;
334
+ }
335
+ const parsed = (0, import_toml.parse)(content);
336
+ const token = parsed["token"];
337
+ if (typeof token !== "string") return null;
338
+ const creds = { token };
339
+ if (typeof parsed["is_anonymous"] === "boolean") {
340
+ creds.is_anonymous = parsed["is_anonymous"];
341
+ }
342
+ if (typeof parsed["anonymous_id"] === "string") {
343
+ creds.anonymous_id = parsed["anonymous_id"];
344
+ }
345
+ if (typeof parsed["anonymous_secret"] === "string") {
346
+ creds.anonymous_secret = parsed["anonymous_secret"];
347
+ }
348
+ return creds;
349
+ }
350
+ async save(credentials) {
351
+ const fs = await import("fs/promises");
352
+ const nodePath = await import("path");
353
+ const filePath = await this.resolvePath();
354
+ const dir = nodePath.dirname(filePath);
355
+ await fs.mkdir(dir, { recursive: true, mode: 448 });
356
+ const data = {};
357
+ try {
358
+ const raw = await fs.readFile(filePath, "utf-8");
359
+ const parsed = (0, import_toml.parse)(raw);
360
+ for (const [k, v] of Object.entries(parsed)) {
361
+ if (typeof v === "string" || typeof v === "boolean" || typeof v === "number") {
362
+ data[k] = v;
363
+ }
364
+ }
365
+ } catch (err) {
366
+ if (err.code !== "ENOENT") throw err;
367
+ }
368
+ data["token"] = credentials.token;
369
+ if (credentials.is_anonymous !== void 0) {
370
+ data["is_anonymous"] = credentials.is_anonymous;
371
+ }
372
+ if (credentials.anonymous_id !== void 0) {
373
+ data["anonymous_id"] = credentials.anonymous_id;
374
+ }
375
+ if (credentials.anonymous_secret !== void 0) {
376
+ data["anonymous_secret"] = credentials.anonymous_secret;
377
+ }
378
+ const toml = (0, import_toml.stringify)(
379
+ data
380
+ );
381
+ await fs.writeFile(filePath, toml, { mode: 384 });
382
+ }
383
+ async clear() {
384
+ const fs = await import("fs/promises");
385
+ const filePath = await this.resolvePath();
386
+ try {
387
+ await fs.unlink(filePath);
388
+ } catch (err) {
389
+ if (err.code !== "ENOENT") throw err;
390
+ }
391
+ }
392
+ };
393
+ var MemoryCredentialStore = class {
394
+ credentials = null;
395
+ async load() {
396
+ return this.credentials ? { ...this.credentials } : null;
397
+ }
398
+ async save(credentials) {
399
+ this.credentials = {
400
+ token: credentials.token,
401
+ is_anonymous: credentials.is_anonymous ?? this.credentials?.is_anonymous,
402
+ anonymous_id: credentials.anonymous_id ?? this.credentials?.anonymous_id,
403
+ anonymous_secret: credentials.anonymous_secret ?? this.credentials?.anonymous_secret
404
+ };
405
+ }
406
+ async clear() {
407
+ this.credentials = null;
408
+ }
409
+ };
410
+ function defaultCredentialStore() {
411
+ return new FileCredentialStore();
412
+ }
413
+
414
+ // src/admin.ts
415
+ function createAdminClient(options = {}) {
416
+ const baseUrl = options.baseUrl ?? "https://db9.shared.aws.tidbcloud.com/api";
417
+ const headers = {};
418
+ if (options.apiKey) {
419
+ headers["X-API-Key"] = options.apiKey;
420
+ }
421
+ const client = createHttpClient({
422
+ baseUrl,
423
+ fetch: options.fetch,
424
+ headers
425
+ });
426
+ return {
427
+ tenants: {
428
+ list: (params) => {
429
+ const queryParams = {};
430
+ if (params) {
431
+ if (params.page !== void 0)
432
+ queryParams.page = String(params.page);
433
+ if (params.size !== void 0)
434
+ queryParams.size = String(params.size);
435
+ if (params.state !== void 0) queryParams.state = params.state;
436
+ if (params.q !== void 0) queryParams.q = params.q;
437
+ if (params.cursor !== void 0) queryParams.cursor = params.cursor;
438
+ if (params.tag !== void 0) queryParams.tag = params.tag;
439
+ }
440
+ return client.get("/tenants", queryParams);
441
+ },
442
+ create: (req) => client.post("/tenants", req),
443
+ get: (tenantId) => client.get(`/tenants/${tenantId}`),
444
+ update: (tenantId, req) => client.put(`/tenants/${tenantId}`, req),
445
+ delete: (tenantId) => client.del(`/tenants/${tenantId}`),
446
+ remove: (tenantId) => client.post(`/tenants/${tenantId}/remove`),
447
+ batchCreate: (req) => client.post("/tenants/batch", req),
448
+ batchDelete: (req) => client.post("/tenants/batch/delete", req),
449
+ batchUpdate: (req) => client.put("/tenants/batch", req),
450
+ connect: (tenantId, req) => client.post(
451
+ `/tenants/${tenantId}/connect`,
452
+ req
453
+ ),
454
+ // query() needs X-Tenant-Session header — create a one-off client
455
+ query: async (tenantId, sessionId, req) => {
456
+ const sessionClient = createHttpClient({
457
+ baseUrl,
458
+ fetch: options.fetch,
459
+ headers: {
460
+ ...headers,
461
+ "X-Tenant-Session": sessionId
462
+ }
463
+ });
464
+ return sessionClient.post(
465
+ `/tenants/${tenantId}/query`,
466
+ req
467
+ );
468
+ }
469
+ },
470
+ system: {
471
+ health: () => client.get("/health"),
472
+ info: () => client.get("/info")
473
+ },
474
+ users: {
475
+ list: async (tenantId, sessionId) => {
476
+ const sessionClient = createHttpClient({
477
+ baseUrl,
478
+ fetch: options.fetch,
479
+ headers: {
480
+ ...headers,
481
+ "X-Tenant-Session": sessionId
482
+ }
483
+ });
484
+ return sessionClient.get(
485
+ `/tenants/${tenantId}/users`
486
+ );
487
+ },
488
+ create: async (tenantId, sessionId, req) => {
489
+ const sessionClient = createHttpClient({
490
+ baseUrl,
491
+ fetch: options.fetch,
492
+ headers: {
493
+ ...headers,
494
+ "X-Tenant-Session": sessionId
495
+ }
496
+ });
497
+ return sessionClient.post(
498
+ `/tenants/${tenantId}/users`,
499
+ req
500
+ );
501
+ },
502
+ delete: async (tenantId, sessionId, username) => {
503
+ const sessionClient = createHttpClient({
504
+ baseUrl,
505
+ fetch: options.fetch,
506
+ headers: {
507
+ ...headers,
508
+ "X-Tenant-Session": sessionId
509
+ }
510
+ });
511
+ return sessionClient.del(
512
+ `/tenants/${tenantId}/users/${username}`
513
+ );
514
+ },
515
+ resetPassword: async (tenantId, sessionId, username) => {
516
+ const sessionClient = createHttpClient({
517
+ baseUrl,
518
+ fetch: options.fetch,
519
+ headers: {
520
+ ...headers,
521
+ "X-Tenant-Session": sessionId
522
+ }
523
+ });
524
+ return sessionClient.post(
525
+ `/tenants/${tenantId}/users/${username}/password`
526
+ );
527
+ }
528
+ },
529
+ observability: {
530
+ get: (tenantId) => client.get(
531
+ `/tenants/${tenantId}/observability`
532
+ ),
533
+ bootstrap: (tenantId, req) => client.post(
534
+ `/tenants/${tenantId}/observability/bootstrap`,
535
+ req
536
+ )
537
+ },
538
+ audit: {
539
+ list: (params) => {
540
+ const queryParams = {};
541
+ if (params) {
542
+ if (params.tenant_id !== void 0)
543
+ queryParams.tenant_id = params.tenant_id;
544
+ if (params.operation_type !== void 0)
545
+ queryParams.operation_type = params.operation_type;
546
+ if (params.resource_type !== void 0)
547
+ queryParams.resource_type = params.resource_type;
548
+ if (params.success !== void 0)
549
+ queryParams.success = String(params.success);
550
+ if (params.limit !== void 0)
551
+ queryParams.limit = String(params.limit);
552
+ if (params.offset !== void 0)
553
+ queryParams.offset = String(params.offset);
554
+ }
555
+ return client.get("/audit-logs", queryParams);
556
+ }
557
+ }
558
+ };
559
+ }
560
+
561
+ // src/index.ts
562
+ async function instantDatabase(options = {}) {
563
+ const store = options.credentialStore ?? defaultCredentialStore();
564
+ const dbName = options.name ?? "default";
565
+ const creds = await store.load();
566
+ let token;
567
+ if (creds?.token) {
568
+ token = creds.token;
569
+ } else {
570
+ const publicClient = createCustomerClient({
571
+ baseUrl: options.baseUrl,
572
+ fetch: options.fetch
573
+ });
574
+ const regResult = await publicClient.auth.anonymousRegister();
575
+ token = regResult.token;
576
+ await store.save({
577
+ token: regResult.token,
578
+ is_anonymous: regResult.is_anonymous,
579
+ anonymous_id: regResult.anonymous_id,
580
+ anonymous_secret: regResult.anonymous_secret
581
+ });
582
+ }
583
+ const client = createCustomerClient({
584
+ baseUrl: options.baseUrl,
585
+ fetch: options.fetch,
586
+ token
587
+ });
588
+ const existing = await client.databases.list();
589
+ const found = existing.find((db) => db.name === dbName);
590
+ if (found) {
591
+ return toResult(found);
592
+ }
593
+ const created = await client.databases.create({ name: dbName });
594
+ if (options.seed) {
595
+ await client.databases.sql(created.id, options.seed);
596
+ } else if (options.seedFile) {
597
+ await client.databases.sqlFile(created.id, options.seedFile);
598
+ }
599
+ return toResult(created);
600
+ }
601
+ function toResult(db) {
602
+ return {
603
+ databaseId: db.id,
604
+ connectionString: db.connection_string ?? "",
605
+ adminUser: db.admin_user ?? "",
606
+ adminPassword: db.admin_password ?? "",
607
+ state: db.state,
608
+ createdAt: db.created_at
609
+ };
610
+ }
611
+ // Annotate the CommonJS export names for ESM import in node:
612
+ 0 && (module.exports = {
613
+ Db9AuthError,
614
+ Db9ConflictError,
615
+ Db9Error,
616
+ Db9NotFoundError,
617
+ FileCredentialStore,
618
+ MemoryCredentialStore,
619
+ createAdminClient,
620
+ createCustomerClient,
621
+ defaultCredentialStore,
622
+ instantDatabase
623
+ });
624
+ //# sourceMappingURL=index.cjs.map