connectbase-client 0.12.2 → 0.13.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/connect-base.umd.js +3 -3
- package/dist/index.d.mts +293 -31
- package/dist/index.d.ts +293 -31
- package/dist/index.js +172 -27
- package/dist/index.mjs +172 -27
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -37,10 +37,12 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
|
|
38
38
|
// src/types/error.ts
|
|
39
39
|
var ApiError = class extends Error {
|
|
40
|
-
constructor(statusCode, message) {
|
|
40
|
+
constructor(statusCode, message, code, details) {
|
|
41
41
|
super(message);
|
|
42
42
|
this.statusCode = statusCode;
|
|
43
43
|
this.name = "ApiError";
|
|
44
|
+
this.code = code;
|
|
45
|
+
this.details = details;
|
|
44
46
|
}
|
|
45
47
|
};
|
|
46
48
|
var AuthError = class extends Error {
|
|
@@ -193,7 +195,17 @@ var HttpClient = class {
|
|
|
193
195
|
const errorData = await response.json().catch(() => ({
|
|
194
196
|
message: response.statusText
|
|
195
197
|
}));
|
|
196
|
-
|
|
198
|
+
const rawError = errorData.error;
|
|
199
|
+
if (rawError && typeof rawError === "object" && "message" in rawError) {
|
|
200
|
+
throw new ApiError(
|
|
201
|
+
response.status,
|
|
202
|
+
rawError.message || "Unknown error",
|
|
203
|
+
rawError.code,
|
|
204
|
+
rawError.details
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const message = typeof rawError === "string" ? rawError : errorData.message || "Unknown error";
|
|
208
|
+
throw new ApiError(response.status, message);
|
|
197
209
|
}
|
|
198
210
|
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
199
211
|
return {};
|
|
@@ -550,10 +562,15 @@ var DatabaseAPI = class {
|
|
|
550
562
|
this.http = http;
|
|
551
563
|
}
|
|
552
564
|
/**
|
|
553
|
-
*
|
|
565
|
+
* Database 공개 API 접두사.
|
|
566
|
+
*
|
|
567
|
+
* 서버의 테이블/컬럼/데이터 CRUD 는 모두 `/v1/public/*` 경로에서만 제공되며
|
|
568
|
+
* (core-server → data-server 프록시), JWT 기반 `/v1/*` 경로는 존재하지 않습니다.
|
|
569
|
+
* 따라서 항상 `/v1/public` 을 사용합니다. 인증은 `X-Public-Key` 헤더에
|
|
570
|
+
* `publicKey` (`cb_pk_*`) 를 실어 서버가 검증합니다.
|
|
554
571
|
*/
|
|
555
572
|
getPublicPrefix() {
|
|
556
|
-
return
|
|
573
|
+
return "/v1/public";
|
|
557
574
|
}
|
|
558
575
|
// ============ Table Methods ============
|
|
559
576
|
/**
|
|
@@ -574,18 +591,39 @@ var DatabaseAPI = class {
|
|
|
574
591
|
return this.http.get(`${prefix}/tables/${tableId}`);
|
|
575
592
|
}
|
|
576
593
|
/**
|
|
577
|
-
* 테이블
|
|
594
|
+
* 테이블 생성.
|
|
595
|
+
*
|
|
596
|
+
* 서버 DTO (`{title, schema, access_level}`) 로 변환하여 전송합니다.
|
|
597
|
+
* `schema` 가 비어있으면 요청 본문에서 키 자체를 생략하여 빈 테이블을
|
|
598
|
+
* 생성합니다. (서버는 explicit 빈 맵을 거부)
|
|
599
|
+
*
|
|
600
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
601
|
+
* 생성된 테이블 메타데이터가 필요하면 이어서 `getTables()` 로 조회하세요.
|
|
578
602
|
*/
|
|
579
603
|
async createTable(data) {
|
|
580
604
|
const prefix = this.getPublicPrefix();
|
|
581
|
-
|
|
605
|
+
const body = {
|
|
606
|
+
title: data.name,
|
|
607
|
+
access_level: data.accessLevel ?? "Creator"
|
|
608
|
+
};
|
|
609
|
+
if (data.schema && Object.keys(data.schema).length > 0) {
|
|
610
|
+
body.schema = data.schema;
|
|
611
|
+
}
|
|
612
|
+
await this.http.post(`${prefix}/tables`, body);
|
|
582
613
|
}
|
|
583
614
|
/**
|
|
584
|
-
* 테이블
|
|
615
|
+
* 테이블 수정.
|
|
616
|
+
*
|
|
617
|
+
* `name` 은 서버의 `title` 로, `accessLevel` 은 `access_level` 로 매핑됩니다.
|
|
585
618
|
*/
|
|
586
619
|
async updateTable(tableId, data) {
|
|
587
620
|
const prefix = this.getPublicPrefix();
|
|
588
|
-
|
|
621
|
+
const body = {};
|
|
622
|
+
if (data.name !== void 0) body.title = data.name;
|
|
623
|
+
if (data.schema !== void 0) body.schema = data.schema;
|
|
624
|
+
if (data.accessLevel !== void 0) body.access_level = data.accessLevel;
|
|
625
|
+
if (data.description !== void 0) body.description = data.description;
|
|
626
|
+
await this.http.patch(`${prefix}/tables/${tableId}`, body);
|
|
589
627
|
}
|
|
590
628
|
/**
|
|
591
629
|
* 테이블 삭제
|
|
@@ -594,48 +632,139 @@ var DatabaseAPI = class {
|
|
|
594
632
|
const prefix = this.getPublicPrefix();
|
|
595
633
|
await this.http.delete(`${prefix}/tables/${tableId}`);
|
|
596
634
|
}
|
|
635
|
+
// ============ Validation Schema Methods (table-level 검증 규칙) ============
|
|
636
|
+
/**
|
|
637
|
+
* 테이블 검증 스키마 조회.
|
|
638
|
+
*
|
|
639
|
+
* 검증 스키마가 설정되어 있지 않으면 `null` 을 반환합니다.
|
|
640
|
+
* Public Key 인증으로 호출 가능 (조회 권한).
|
|
641
|
+
*/
|
|
642
|
+
async getValidationSchema(tableId) {
|
|
643
|
+
const prefix = this.getPublicPrefix();
|
|
644
|
+
const response = await this.http.get(
|
|
645
|
+
`${prefix}/tables/${tableId}/validation-schema`
|
|
646
|
+
);
|
|
647
|
+
return response.validation_schema;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* 테이블 검증 스키마 설정/교체.
|
|
651
|
+
*
|
|
652
|
+
* **권한**: 콘솔 (앱 소유자 JWT) 전용. Public Key 로는 설정 불가.
|
|
653
|
+
* SDK 에서는 `accessToken` 이 설정되어 있어야 하며, 콘솔에서 사용하는 패턴입니다.
|
|
654
|
+
*
|
|
655
|
+
* 빈 fields 는 거부됩니다. 검증 스키마를 제거하려면 `deleteValidationSchema` 사용.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* await cb.database.setValidationSchema(tableId, {
|
|
660
|
+
* fields: {
|
|
661
|
+
* email: { type: 'string', pattern: '^.+@.+$', required: true },
|
|
662
|
+
* age: { type: 'int', min: 0, max: 150 }
|
|
663
|
+
* },
|
|
664
|
+
* required: ['email'],
|
|
665
|
+
* immutable: ['email']
|
|
666
|
+
* })
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
async setValidationSchema(tableId, schema) {
|
|
670
|
+
await this.http.put(
|
|
671
|
+
`/v1/apps/${this.requireAppId()}/databases/tables/${tableId}/validation-schema`,
|
|
672
|
+
{ validation_schema: schema }
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* 테이블 검증 스키마 제거 (schemaless 모드로 복귀).
|
|
677
|
+
*
|
|
678
|
+
* **권한**: 콘솔 (앱 소유자 JWT) 전용.
|
|
679
|
+
*/
|
|
680
|
+
async deleteValidationSchema(tableId) {
|
|
681
|
+
await this.http.delete(
|
|
682
|
+
`/v1/apps/${this.requireAppId()}/databases/tables/${tableId}/validation-schema`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* appId 가 설정되어 있어야 콘솔 경로를 호출할 수 있습니다 (validation_schema set/delete).
|
|
687
|
+
*/
|
|
688
|
+
requireAppId() {
|
|
689
|
+
const appId = this.http.config?.appId;
|
|
690
|
+
if (!appId) {
|
|
691
|
+
throw new Error(
|
|
692
|
+
"setValidationSchema/deleteValidationSchema \uB294 \uCF58\uC194 (JWT) \uC778\uC99D\uC774 \uD544\uC694\uD558\uBA70 ConnectBase config \uC5D0 appId \uAC00 \uC124\uC815\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4."
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
return appId;
|
|
696
|
+
}
|
|
597
697
|
// ============ Column Methods ============
|
|
598
698
|
/**
|
|
599
|
-
* 컬럼 목록 조회 (Table.schema
|
|
699
|
+
* 컬럼 목록 조회 (`Table.schema` 맵을 컬럼 객체 배열로 변환).
|
|
700
|
+
*
|
|
701
|
+
* 서버는 컬럼을 다음 두 가지 중 하나로 저장합니다:
|
|
702
|
+
* - 플랫: `"email": "string"` (추가 속성이 없는 경우)
|
|
703
|
+
* - 중첩: `"email": { "type": "string", "required": true, ... }` (추가 속성이 있는 경우)
|
|
704
|
+
*
|
|
705
|
+
* `$required` 키는 별도 배열로 필수 컬럼을 지정할 수 있으며, 중첩 객체의
|
|
706
|
+
* `required: true` 와 병합되어 판정됩니다.
|
|
600
707
|
*/
|
|
601
708
|
async getColumns(tableId) {
|
|
602
709
|
const table = await this.getTable(tableId);
|
|
603
|
-
const schema = table.schema;
|
|
604
|
-
|
|
710
|
+
const schema = table.schema ?? {};
|
|
711
|
+
const requiredSet = new Set(
|
|
712
|
+
Array.isArray(schema.$required) ? schema.$required : []
|
|
713
|
+
);
|
|
605
714
|
const columns = [];
|
|
715
|
+
let order = 0;
|
|
606
716
|
for (const [name, def] of Object.entries(schema)) {
|
|
607
|
-
if (name.startsWith("$")) continue;
|
|
608
|
-
|
|
717
|
+
if (name.startsWith("$") || def === void 0) continue;
|
|
718
|
+
if (Array.isArray(def)) continue;
|
|
719
|
+
let data_type = "string";
|
|
720
|
+
let is_required = requiredSet.has(name);
|
|
721
|
+
let default_value;
|
|
722
|
+
let description;
|
|
723
|
+
let encrypted;
|
|
724
|
+
if (typeof def === "string") {
|
|
725
|
+
data_type = def;
|
|
726
|
+
} else {
|
|
727
|
+
data_type = def.type;
|
|
728
|
+
if (def.required === true) is_required = true;
|
|
729
|
+
if (def.default !== void 0) default_value = def.default;
|
|
730
|
+
if (def.description !== void 0) description = def.description;
|
|
731
|
+
if (def.encrypted !== void 0) encrypted = def.encrypted;
|
|
732
|
+
}
|
|
609
733
|
columns.push({
|
|
610
734
|
id: name,
|
|
611
735
|
name,
|
|
612
|
-
data_type
|
|
613
|
-
is_required
|
|
614
|
-
default_value
|
|
615
|
-
description
|
|
616
|
-
encrypted
|
|
617
|
-
order:
|
|
618
|
-
created_at:
|
|
736
|
+
data_type,
|
|
737
|
+
is_required,
|
|
738
|
+
default_value,
|
|
739
|
+
description,
|
|
740
|
+
encrypted,
|
|
741
|
+
order: order++,
|
|
742
|
+
created_at: table.created_at
|
|
619
743
|
});
|
|
620
744
|
}
|
|
621
745
|
return columns;
|
|
622
746
|
}
|
|
623
747
|
/**
|
|
624
|
-
* 컬럼
|
|
748
|
+
* 컬럼 생성.
|
|
749
|
+
*
|
|
750
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
751
|
+
* 생성 결과 컬럼을 얻으려면 이어서 `getColumns(tableId)` 로 조회하세요.
|
|
625
752
|
*/
|
|
626
753
|
async createColumn(tableId, data) {
|
|
627
754
|
const prefix = this.getPublicPrefix();
|
|
628
|
-
|
|
755
|
+
await this.http.post(
|
|
629
756
|
`${prefix}/tables/${tableId}/columns`,
|
|
630
757
|
data
|
|
631
758
|
);
|
|
632
759
|
}
|
|
633
760
|
/**
|
|
634
|
-
* 컬럼
|
|
761
|
+
* 컬럼 수정.
|
|
762
|
+
*
|
|
763
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
635
764
|
*/
|
|
636
765
|
async updateColumn(tableId, columnName, data) {
|
|
637
766
|
const prefix = this.getPublicPrefix();
|
|
638
|
-
|
|
767
|
+
await this.http.patch(
|
|
639
768
|
`${prefix}/tables/${tableId}/columns/${columnName}`,
|
|
640
769
|
data
|
|
641
770
|
);
|
|
@@ -689,11 +818,27 @@ var DatabaseAPI = class {
|
|
|
689
818
|
return this.http.get(`${prefix}/tables/${tableId}/data/${dataId}`);
|
|
690
819
|
}
|
|
691
820
|
/**
|
|
692
|
-
* 데이터
|
|
821
|
+
* 데이터 생성.
|
|
822
|
+
*
|
|
823
|
+
* `tableIdOrName` 은 테이블 UUID 또는 이름. UUID 형식이면 기존 경로
|
|
824
|
+
* (`/tables/:tableId/data`), 그렇지 않으면 이름 기반 경로
|
|
825
|
+
* (`/tables/name/:tableName/data`) 를 사용합니다.
|
|
826
|
+
*
|
|
827
|
+
* `options.autoCreate: true` 시 테이블이 없으면 빈 schemaless 테이블을
|
|
828
|
+
* 자동으로 생성한 뒤 insert. opt-in 이며 프로토타입/AI 자동화 워크플로에
|
|
829
|
+
* 권장합니다. production 앱은 명시적 `createTable` 호출 권장.
|
|
693
830
|
*/
|
|
694
|
-
async createData(
|
|
831
|
+
async createData(tableIdOrName, data, options) {
|
|
695
832
|
const prefix = this.getPublicPrefix();
|
|
696
|
-
|
|
833
|
+
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tableIdOrName);
|
|
834
|
+
if (isUUID) {
|
|
835
|
+
return this.http.post(`${prefix}/tables/${tableIdOrName}/data`, data);
|
|
836
|
+
}
|
|
837
|
+
const query = options?.autoCreate ? "?auto_create=true" : "";
|
|
838
|
+
return this.http.post(
|
|
839
|
+
`${prefix}/tables/name/${encodeURIComponent(tableIdOrName)}/data${query}`,
|
|
840
|
+
data
|
|
841
|
+
);
|
|
697
842
|
}
|
|
698
843
|
/**
|
|
699
844
|
* 데이터 수정 (부분 업데이트)
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// src/types/error.ts
|
|
2
2
|
var ApiError = class extends Error {
|
|
3
|
-
constructor(statusCode, message) {
|
|
3
|
+
constructor(statusCode, message, code, details) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.statusCode = statusCode;
|
|
6
6
|
this.name = "ApiError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.details = details;
|
|
7
9
|
}
|
|
8
10
|
};
|
|
9
11
|
var AuthError = class extends Error {
|
|
@@ -156,7 +158,17 @@ var HttpClient = class {
|
|
|
156
158
|
const errorData = await response.json().catch(() => ({
|
|
157
159
|
message: response.statusText
|
|
158
160
|
}));
|
|
159
|
-
|
|
161
|
+
const rawError = errorData.error;
|
|
162
|
+
if (rawError && typeof rawError === "object" && "message" in rawError) {
|
|
163
|
+
throw new ApiError(
|
|
164
|
+
response.status,
|
|
165
|
+
rawError.message || "Unknown error",
|
|
166
|
+
rawError.code,
|
|
167
|
+
rawError.details
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const message = typeof rawError === "string" ? rawError : errorData.message || "Unknown error";
|
|
171
|
+
throw new ApiError(response.status, message);
|
|
160
172
|
}
|
|
161
173
|
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
162
174
|
return {};
|
|
@@ -513,10 +525,15 @@ var DatabaseAPI = class {
|
|
|
513
525
|
this.http = http;
|
|
514
526
|
}
|
|
515
527
|
/**
|
|
516
|
-
*
|
|
528
|
+
* Database 공개 API 접두사.
|
|
529
|
+
*
|
|
530
|
+
* 서버의 테이블/컬럼/데이터 CRUD 는 모두 `/v1/public/*` 경로에서만 제공되며
|
|
531
|
+
* (core-server → data-server 프록시), JWT 기반 `/v1/*` 경로는 존재하지 않습니다.
|
|
532
|
+
* 따라서 항상 `/v1/public` 을 사용합니다. 인증은 `X-Public-Key` 헤더에
|
|
533
|
+
* `publicKey` (`cb_pk_*`) 를 실어 서버가 검증합니다.
|
|
517
534
|
*/
|
|
518
535
|
getPublicPrefix() {
|
|
519
|
-
return
|
|
536
|
+
return "/v1/public";
|
|
520
537
|
}
|
|
521
538
|
// ============ Table Methods ============
|
|
522
539
|
/**
|
|
@@ -537,18 +554,39 @@ var DatabaseAPI = class {
|
|
|
537
554
|
return this.http.get(`${prefix}/tables/${tableId}`);
|
|
538
555
|
}
|
|
539
556
|
/**
|
|
540
|
-
* 테이블
|
|
557
|
+
* 테이블 생성.
|
|
558
|
+
*
|
|
559
|
+
* 서버 DTO (`{title, schema, access_level}`) 로 변환하여 전송합니다.
|
|
560
|
+
* `schema` 가 비어있으면 요청 본문에서 키 자체를 생략하여 빈 테이블을
|
|
561
|
+
* 생성합니다. (서버는 explicit 빈 맵을 거부)
|
|
562
|
+
*
|
|
563
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
564
|
+
* 생성된 테이블 메타데이터가 필요하면 이어서 `getTables()` 로 조회하세요.
|
|
541
565
|
*/
|
|
542
566
|
async createTable(data) {
|
|
543
567
|
const prefix = this.getPublicPrefix();
|
|
544
|
-
|
|
568
|
+
const body = {
|
|
569
|
+
title: data.name,
|
|
570
|
+
access_level: data.accessLevel ?? "Creator"
|
|
571
|
+
};
|
|
572
|
+
if (data.schema && Object.keys(data.schema).length > 0) {
|
|
573
|
+
body.schema = data.schema;
|
|
574
|
+
}
|
|
575
|
+
await this.http.post(`${prefix}/tables`, body);
|
|
545
576
|
}
|
|
546
577
|
/**
|
|
547
|
-
* 테이블
|
|
578
|
+
* 테이블 수정.
|
|
579
|
+
*
|
|
580
|
+
* `name` 은 서버의 `title` 로, `accessLevel` 은 `access_level` 로 매핑됩니다.
|
|
548
581
|
*/
|
|
549
582
|
async updateTable(tableId, data) {
|
|
550
583
|
const prefix = this.getPublicPrefix();
|
|
551
|
-
|
|
584
|
+
const body = {};
|
|
585
|
+
if (data.name !== void 0) body.title = data.name;
|
|
586
|
+
if (data.schema !== void 0) body.schema = data.schema;
|
|
587
|
+
if (data.accessLevel !== void 0) body.access_level = data.accessLevel;
|
|
588
|
+
if (data.description !== void 0) body.description = data.description;
|
|
589
|
+
await this.http.patch(`${prefix}/tables/${tableId}`, body);
|
|
552
590
|
}
|
|
553
591
|
/**
|
|
554
592
|
* 테이블 삭제
|
|
@@ -557,48 +595,139 @@ var DatabaseAPI = class {
|
|
|
557
595
|
const prefix = this.getPublicPrefix();
|
|
558
596
|
await this.http.delete(`${prefix}/tables/${tableId}`);
|
|
559
597
|
}
|
|
598
|
+
// ============ Validation Schema Methods (table-level 검증 규칙) ============
|
|
599
|
+
/**
|
|
600
|
+
* 테이블 검증 스키마 조회.
|
|
601
|
+
*
|
|
602
|
+
* 검증 스키마가 설정되어 있지 않으면 `null` 을 반환합니다.
|
|
603
|
+
* Public Key 인증으로 호출 가능 (조회 권한).
|
|
604
|
+
*/
|
|
605
|
+
async getValidationSchema(tableId) {
|
|
606
|
+
const prefix = this.getPublicPrefix();
|
|
607
|
+
const response = await this.http.get(
|
|
608
|
+
`${prefix}/tables/${tableId}/validation-schema`
|
|
609
|
+
);
|
|
610
|
+
return response.validation_schema;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* 테이블 검증 스키마 설정/교체.
|
|
614
|
+
*
|
|
615
|
+
* **권한**: 콘솔 (앱 소유자 JWT) 전용. Public Key 로는 설정 불가.
|
|
616
|
+
* SDK 에서는 `accessToken` 이 설정되어 있어야 하며, 콘솔에서 사용하는 패턴입니다.
|
|
617
|
+
*
|
|
618
|
+
* 빈 fields 는 거부됩니다. 검증 스키마를 제거하려면 `deleteValidationSchema` 사용.
|
|
619
|
+
*
|
|
620
|
+
* @example
|
|
621
|
+
* ```ts
|
|
622
|
+
* await cb.database.setValidationSchema(tableId, {
|
|
623
|
+
* fields: {
|
|
624
|
+
* email: { type: 'string', pattern: '^.+@.+$', required: true },
|
|
625
|
+
* age: { type: 'int', min: 0, max: 150 }
|
|
626
|
+
* },
|
|
627
|
+
* required: ['email'],
|
|
628
|
+
* immutable: ['email']
|
|
629
|
+
* })
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
async setValidationSchema(tableId, schema) {
|
|
633
|
+
await this.http.put(
|
|
634
|
+
`/v1/apps/${this.requireAppId()}/databases/tables/${tableId}/validation-schema`,
|
|
635
|
+
{ validation_schema: schema }
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* 테이블 검증 스키마 제거 (schemaless 모드로 복귀).
|
|
640
|
+
*
|
|
641
|
+
* **권한**: 콘솔 (앱 소유자 JWT) 전용.
|
|
642
|
+
*/
|
|
643
|
+
async deleteValidationSchema(tableId) {
|
|
644
|
+
await this.http.delete(
|
|
645
|
+
`/v1/apps/${this.requireAppId()}/databases/tables/${tableId}/validation-schema`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* appId 가 설정되어 있어야 콘솔 경로를 호출할 수 있습니다 (validation_schema set/delete).
|
|
650
|
+
*/
|
|
651
|
+
requireAppId() {
|
|
652
|
+
const appId = this.http.config?.appId;
|
|
653
|
+
if (!appId) {
|
|
654
|
+
throw new Error(
|
|
655
|
+
"setValidationSchema/deleteValidationSchema \uB294 \uCF58\uC194 (JWT) \uC778\uC99D\uC774 \uD544\uC694\uD558\uBA70 ConnectBase config \uC5D0 appId \uAC00 \uC124\uC815\uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4."
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
return appId;
|
|
659
|
+
}
|
|
560
660
|
// ============ Column Methods ============
|
|
561
661
|
/**
|
|
562
|
-
* 컬럼 목록 조회 (Table.schema
|
|
662
|
+
* 컬럼 목록 조회 (`Table.schema` 맵을 컬럼 객체 배열로 변환).
|
|
663
|
+
*
|
|
664
|
+
* 서버는 컬럼을 다음 두 가지 중 하나로 저장합니다:
|
|
665
|
+
* - 플랫: `"email": "string"` (추가 속성이 없는 경우)
|
|
666
|
+
* - 중첩: `"email": { "type": "string", "required": true, ... }` (추가 속성이 있는 경우)
|
|
667
|
+
*
|
|
668
|
+
* `$required` 키는 별도 배열로 필수 컬럼을 지정할 수 있으며, 중첩 객체의
|
|
669
|
+
* `required: true` 와 병합되어 판정됩니다.
|
|
563
670
|
*/
|
|
564
671
|
async getColumns(tableId) {
|
|
565
672
|
const table = await this.getTable(tableId);
|
|
566
|
-
const schema = table.schema;
|
|
567
|
-
|
|
673
|
+
const schema = table.schema ?? {};
|
|
674
|
+
const requiredSet = new Set(
|
|
675
|
+
Array.isArray(schema.$required) ? schema.$required : []
|
|
676
|
+
);
|
|
568
677
|
const columns = [];
|
|
678
|
+
let order = 0;
|
|
569
679
|
for (const [name, def] of Object.entries(schema)) {
|
|
570
|
-
if (name.startsWith("$")) continue;
|
|
571
|
-
|
|
680
|
+
if (name.startsWith("$") || def === void 0) continue;
|
|
681
|
+
if (Array.isArray(def)) continue;
|
|
682
|
+
let data_type = "string";
|
|
683
|
+
let is_required = requiredSet.has(name);
|
|
684
|
+
let default_value;
|
|
685
|
+
let description;
|
|
686
|
+
let encrypted;
|
|
687
|
+
if (typeof def === "string") {
|
|
688
|
+
data_type = def;
|
|
689
|
+
} else {
|
|
690
|
+
data_type = def.type;
|
|
691
|
+
if (def.required === true) is_required = true;
|
|
692
|
+
if (def.default !== void 0) default_value = def.default;
|
|
693
|
+
if (def.description !== void 0) description = def.description;
|
|
694
|
+
if (def.encrypted !== void 0) encrypted = def.encrypted;
|
|
695
|
+
}
|
|
572
696
|
columns.push({
|
|
573
697
|
id: name,
|
|
574
698
|
name,
|
|
575
|
-
data_type
|
|
576
|
-
is_required
|
|
577
|
-
default_value
|
|
578
|
-
description
|
|
579
|
-
encrypted
|
|
580
|
-
order:
|
|
581
|
-
created_at:
|
|
699
|
+
data_type,
|
|
700
|
+
is_required,
|
|
701
|
+
default_value,
|
|
702
|
+
description,
|
|
703
|
+
encrypted,
|
|
704
|
+
order: order++,
|
|
705
|
+
created_at: table.created_at
|
|
582
706
|
});
|
|
583
707
|
}
|
|
584
708
|
return columns;
|
|
585
709
|
}
|
|
586
710
|
/**
|
|
587
|
-
* 컬럼
|
|
711
|
+
* 컬럼 생성.
|
|
712
|
+
*
|
|
713
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
714
|
+
* 생성 결과 컬럼을 얻으려면 이어서 `getColumns(tableId)` 로 조회하세요.
|
|
588
715
|
*/
|
|
589
716
|
async createColumn(tableId, data) {
|
|
590
717
|
const prefix = this.getPublicPrefix();
|
|
591
|
-
|
|
718
|
+
await this.http.post(
|
|
592
719
|
`${prefix}/tables/${tableId}/columns`,
|
|
593
720
|
data
|
|
594
721
|
);
|
|
595
722
|
}
|
|
596
723
|
/**
|
|
597
|
-
* 컬럼
|
|
724
|
+
* 컬럼 수정.
|
|
725
|
+
*
|
|
726
|
+
* 서버는 `{message}` 만 돌려주므로 반환 타입은 `void` 입니다.
|
|
598
727
|
*/
|
|
599
728
|
async updateColumn(tableId, columnName, data) {
|
|
600
729
|
const prefix = this.getPublicPrefix();
|
|
601
|
-
|
|
730
|
+
await this.http.patch(
|
|
602
731
|
`${prefix}/tables/${tableId}/columns/${columnName}`,
|
|
603
732
|
data
|
|
604
733
|
);
|
|
@@ -652,11 +781,27 @@ var DatabaseAPI = class {
|
|
|
652
781
|
return this.http.get(`${prefix}/tables/${tableId}/data/${dataId}`);
|
|
653
782
|
}
|
|
654
783
|
/**
|
|
655
|
-
* 데이터
|
|
784
|
+
* 데이터 생성.
|
|
785
|
+
*
|
|
786
|
+
* `tableIdOrName` 은 테이블 UUID 또는 이름. UUID 형식이면 기존 경로
|
|
787
|
+
* (`/tables/:tableId/data`), 그렇지 않으면 이름 기반 경로
|
|
788
|
+
* (`/tables/name/:tableName/data`) 를 사용합니다.
|
|
789
|
+
*
|
|
790
|
+
* `options.autoCreate: true` 시 테이블이 없으면 빈 schemaless 테이블을
|
|
791
|
+
* 자동으로 생성한 뒤 insert. opt-in 이며 프로토타입/AI 자동화 워크플로에
|
|
792
|
+
* 권장합니다. production 앱은 명시적 `createTable` 호출 권장.
|
|
656
793
|
*/
|
|
657
|
-
async createData(
|
|
794
|
+
async createData(tableIdOrName, data, options) {
|
|
658
795
|
const prefix = this.getPublicPrefix();
|
|
659
|
-
|
|
796
|
+
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tableIdOrName);
|
|
797
|
+
if (isUUID) {
|
|
798
|
+
return this.http.post(`${prefix}/tables/${tableIdOrName}/data`, data);
|
|
799
|
+
}
|
|
800
|
+
const query = options?.autoCreate ? "?auto_create=true" : "";
|
|
801
|
+
return this.http.post(
|
|
802
|
+
`${prefix}/tables/name/${encodeURIComponent(tableIdOrName)}/data${query}`,
|
|
803
|
+
data
|
|
804
|
+
);
|
|
660
805
|
}
|
|
661
806
|
/**
|
|
662
807
|
* 데이터 수정 (부분 업데이트)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connectbase-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"build": "tsup",
|
|
34
34
|
"dev": "tsup --watch",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
|
+
"test:types": "tsc --noEmit -p test/tsconfig.json",
|
|
36
37
|
"release": "pnpm build && npm publish --access public"
|
|
37
38
|
},
|
|
38
39
|
"keywords": [
|