postgresdk 0.1.2-alpha.3 → 0.1.2-alpha.4
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/cli.js +170 -100
- package/dist/emit-base-client.d.ts +5 -0
- package/dist/index.js +170 -100
- package/package.json +1 -1
package/dist/cli.js
CHANGED
@@ -958,13 +958,116 @@ function emitClient(table) {
|
|
958
958
|
const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
|
959
959
|
const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
|
960
960
|
return `/* Generated. Do not edit. */
|
961
|
+
import { BaseClient } from "./base-client";
|
961
962
|
import type { ${Type}IncludeSpec } from "./include-spec";
|
962
963
|
import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}";
|
963
964
|
|
964
|
-
|
965
|
-
|
965
|
+
/**
|
966
|
+
* Client for ${table.name} table operations
|
967
|
+
*/
|
968
|
+
export class ${Type}Client extends BaseClient {
|
969
|
+
private readonly resource = "/v1/${table.name}";
|
970
|
+
|
971
|
+
async create(data: Insert${Type}): Promise<Select${Type}> {
|
972
|
+
return this.post<Select${Type}>(this.resource, data);
|
973
|
+
}
|
974
|
+
|
975
|
+
async getByPk(pk: ${pkType}): Promise<Select${Type} | null> {
|
976
|
+
const path = ${pkPathExpr};
|
977
|
+
return this.get<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
978
|
+
}
|
979
|
+
|
980
|
+
async list(params?: {
|
981
|
+
include?: ${Type}IncludeSpec;
|
982
|
+
limit?: number;
|
983
|
+
offset?: number;
|
984
|
+
where?: any;
|
985
|
+
orderBy?: string;
|
986
|
+
order?: "asc" | "desc";
|
987
|
+
}): Promise<Select${Type}[]> {
|
988
|
+
return this.post<Select${Type}[]>(\`\${this.resource}/list\`, params ?? {});
|
989
|
+
}
|
990
|
+
|
991
|
+
async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null> {
|
992
|
+
const path = ${pkPathExpr};
|
993
|
+
return this.patch<Select${Type} | null>(\`\${this.resource}/\${path}\`, patch);
|
994
|
+
}
|
995
|
+
|
996
|
+
async delete(pk: ${pkType}): Promise<Select${Type} | null> {
|
997
|
+
const path = ${pkPathExpr};
|
998
|
+
return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
`;
|
1002
|
+
}
|
1003
|
+
function emitClientIndex(tables) {
|
1004
|
+
let out = `/* Generated. Do not edit. */
|
1005
|
+
`;
|
1006
|
+
out += `import { BaseClient, AuthConfig } from "./base-client";
|
1007
|
+
`;
|
1008
|
+
for (const t of tables) {
|
1009
|
+
out += `import { ${pascal(t.name)}Client } from "./${t.name}";
|
1010
|
+
`;
|
1011
|
+
}
|
1012
|
+
out += `
|
1013
|
+
// Re-export auth types for convenience
|
1014
|
+
`;
|
1015
|
+
out += `export type { AuthConfig as SDKAuth, AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client";
|
1016
|
+
|
1017
|
+
`;
|
1018
|
+
out += `/**
|
1019
|
+
`;
|
1020
|
+
out += ` * Main SDK class that provides access to all table clients
|
1021
|
+
`;
|
1022
|
+
out += ` */
|
1023
|
+
`;
|
1024
|
+
out += `export class SDK {
|
1025
|
+
`;
|
1026
|
+
for (const t of tables) {
|
1027
|
+
out += ` public ${t.name}: ${pascal(t.name)}Client;
|
1028
|
+
`;
|
1029
|
+
}
|
1030
|
+
out += `
|
1031
|
+
constructor(cfg: { baseUrl: string; fetch?: typeof fetch; auth?: AuthConfig }) {
|
1032
|
+
`;
|
1033
|
+
out += ` const f = cfg.fetch ?? fetch;
|
1034
|
+
`;
|
1035
|
+
for (const t of tables) {
|
1036
|
+
out += ` this.${t.name} = new ${pascal(t.name)}Client(cfg.baseUrl, f, cfg.auth);
|
1037
|
+
`;
|
1038
|
+
}
|
1039
|
+
out += ` }
|
1040
|
+
`;
|
1041
|
+
out += `}
|
966
1042
|
|
967
|
-
|
1043
|
+
`;
|
1044
|
+
out += `// Export individual table clients
|
1045
|
+
`;
|
1046
|
+
for (const t of tables) {
|
1047
|
+
out += `export { ${pascal(t.name)}Client } from "./${t.name}";
|
1048
|
+
`;
|
1049
|
+
}
|
1050
|
+
out += `
|
1051
|
+
// Export base client for custom extensions
|
1052
|
+
`;
|
1053
|
+
out += `export { BaseClient } from "./base-client";
|
1054
|
+
`;
|
1055
|
+
out += `
|
1056
|
+
// Export include specifications
|
1057
|
+
`;
|
1058
|
+
out += `export * from "./include-spec";
|
1059
|
+
`;
|
1060
|
+
return out;
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
// src/emit-base-client.ts
|
1064
|
+
function emitBaseClient() {
|
1065
|
+
return `/* Generated. Do not edit. */
|
1066
|
+
|
1067
|
+
export type HeaderMap = Record<string, string>;
|
1068
|
+
export type AuthHeadersProvider = () => Promise<HeaderMap> | HeaderMap;
|
1069
|
+
|
1070
|
+
export type AuthConfig =
|
968
1071
|
| AuthHeadersProvider
|
969
1072
|
| {
|
970
1073
|
apiKey?: string;
|
@@ -976,14 +1079,18 @@ type AuthConfig =
|
|
976
1079
|
headers?: AuthHeadersProvider;
|
977
1080
|
};
|
978
1081
|
|
979
|
-
|
1082
|
+
/**
|
1083
|
+
* Base client class with shared authentication and request handling logic.
|
1084
|
+
* All table-specific clients extend this class.
|
1085
|
+
*/
|
1086
|
+
export abstract class BaseClient {
|
980
1087
|
constructor(
|
981
|
-
|
982
|
-
|
983
|
-
|
1088
|
+
protected baseUrl: string,
|
1089
|
+
protected fetchFn: typeof fetch = fetch,
|
1090
|
+
protected auth?: AuthConfig
|
984
1091
|
) {}
|
985
1092
|
|
986
|
-
|
1093
|
+
protected async authHeaders(): Promise<HeaderMap> {
|
987
1094
|
if (!this.auth) return {};
|
988
1095
|
if (typeof this.auth === "function") {
|
989
1096
|
const h = await this.auth();
|
@@ -1009,129 +1116,91 @@ export class ${Type}Client {
|
|
1009
1116
|
return out;
|
1010
1117
|
}
|
1011
1118
|
|
1012
|
-
|
1119
|
+
protected async headers(json = false): Promise<HeaderMap> {
|
1013
1120
|
const extra = await this.authHeaders();
|
1014
1121
|
return json ? { "Content-Type": "application/json", ...extra } : extra;
|
1015
1122
|
}
|
1016
1123
|
|
1017
|
-
|
1124
|
+
protected async okOrThrow(res: Response, action: string, entity: string): Promise<void> {
|
1018
1125
|
if (!res.ok) {
|
1019
1126
|
let detail = "";
|
1020
1127
|
try { detail = await res.text(); } catch {}
|
1021
|
-
throw new Error(\`\${action}
|
1128
|
+
throw new Error(\`\${action} \${entity} failed: \${res.status} \${detail}\`);
|
1022
1129
|
}
|
1023
1130
|
}
|
1024
1131
|
|
1025
|
-
|
1026
|
-
|
1132
|
+
/**
|
1133
|
+
* Make a POST request
|
1134
|
+
*/
|
1135
|
+
protected async post<T>(path: string, body?: any): Promise<T> {
|
1136
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1027
1137
|
method: "POST",
|
1028
1138
|
headers: await this.headers(true),
|
1029
|
-
body: JSON.stringify(
|
1139
|
+
body: JSON.stringify(body),
|
1030
1140
|
});
|
1031
|
-
|
1032
|
-
|
1141
|
+
|
1142
|
+
// Handle 404 specially for operations that might return null
|
1143
|
+
if (res.status === 404) {
|
1144
|
+
return null as T;
|
1145
|
+
}
|
1146
|
+
|
1147
|
+
await this.okOrThrow(res, "POST", path);
|
1148
|
+
return (await res.json()) as T;
|
1033
1149
|
}
|
1034
1150
|
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1151
|
+
/**
|
1152
|
+
* Make a GET request
|
1153
|
+
*/
|
1154
|
+
protected async get<T>(path: string): Promise<T> {
|
1155
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1038
1156
|
headers: await this.headers(),
|
1039
1157
|
});
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
method: "POST",
|
1048
|
-
headers: await this.headers(true),
|
1049
|
-
body: JSON.stringify(params ?? {}),
|
1050
|
-
});
|
1051
|
-
await this.okOrThrow(res, "list");
|
1052
|
-
return (await res.json()) as Select${Type}[];
|
1158
|
+
|
1159
|
+
if (res.status === 404) {
|
1160
|
+
return null as T;
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
await this.okOrThrow(res, "GET", path);
|
1164
|
+
return (await res.json()) as T;
|
1053
1165
|
}
|
1054
1166
|
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1167
|
+
/**
|
1168
|
+
* Make a PATCH request
|
1169
|
+
*/
|
1170
|
+
protected async patch<T>(path: string, body?: any): Promise<T> {
|
1171
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1058
1172
|
method: "PATCH",
|
1059
1173
|
headers: await this.headers(true),
|
1060
|
-
body: JSON.stringify(
|
1174
|
+
body: JSON.stringify(body),
|
1061
1175
|
});
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1176
|
+
|
1177
|
+
if (res.status === 404) {
|
1178
|
+
return null as T;
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
await this.okOrThrow(res, "PATCH", path);
|
1182
|
+
return (await res.json()) as T;
|
1065
1183
|
}
|
1066
1184
|
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1185
|
+
/**
|
1186
|
+
* Make a DELETE request
|
1187
|
+
*/
|
1188
|
+
protected async del<T>(path: string): Promise<T> {
|
1189
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1070
1190
|
method: "DELETE",
|
1071
1191
|
headers: await this.headers(),
|
1072
1192
|
});
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1193
|
+
|
1194
|
+
if (res.status === 404) {
|
1195
|
+
return null as T;
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
await this.okOrThrow(res, "DELETE", path);
|
1199
|
+
return (await res.json()) as T;
|
1076
1200
|
}
|
1077
1201
|
}
|
1078
1202
|
`;
|
1079
1203
|
}
|
1080
|
-
function emitClientIndex(tables) {
|
1081
|
-
let out = `/* Generated. Do not edit. */
|
1082
|
-
`;
|
1083
|
-
for (const t of tables) {
|
1084
|
-
out += `import { ${pascal(t.name)}Client } from "./${t.name}";
|
1085
|
-
`;
|
1086
|
-
}
|
1087
|
-
out += `
|
1088
|
-
export type SDKAuthHeadersProvider = () => Promise<Record<string,string>> | Record<string,string>;
|
1089
|
-
`;
|
1090
|
-
out += `export type SDKAuth =
|
1091
|
-
`;
|
1092
|
-
out += ` | SDKAuthHeadersProvider
|
1093
|
-
`;
|
1094
|
-
out += ` | {
|
1095
|
-
`;
|
1096
|
-
out += ` apiKey?: string;
|
1097
|
-
`;
|
1098
|
-
out += ` /** defaults to "x-api-key" */
|
1099
|
-
`;
|
1100
|
-
out += ` apiKeyHeader?: string;
|
1101
|
-
`;
|
1102
|
-
out += ` jwt?: string | (() => Promise<string>);
|
1103
|
-
`;
|
1104
|
-
out += ` headers?: SDKAuthHeadersProvider;
|
1105
|
-
`;
|
1106
|
-
out += ` };
|
1107
|
-
|
1108
|
-
`;
|
1109
|
-
out += `export class SDK {
|
1110
|
-
`;
|
1111
|
-
for (const t of tables) {
|
1112
|
-
out += ` public ${t.name}: ${pascal(t.name)}Client;
|
1113
|
-
`;
|
1114
|
-
}
|
1115
|
-
out += `
|
1116
|
-
constructor(cfg: { baseUrl: string; fetch?: typeof fetch; auth?: SDKAuth }) {
|
1117
|
-
`;
|
1118
|
-
out += ` const f = cfg.fetch ?? fetch;
|
1119
|
-
`;
|
1120
|
-
for (const t of tables) {
|
1121
|
-
out += ` this.${t.name} = new ${pascal(t.name)}Client(cfg.baseUrl, f, cfg.auth);
|
1122
|
-
`;
|
1123
|
-
}
|
1124
|
-
out += ` }
|
1125
|
-
`;
|
1126
|
-
out += `}
|
1127
|
-
`;
|
1128
|
-
for (const t of tables)
|
1129
|
-
out += `export { ${pascal(t.name)}Client } from "./${t.name}";
|
1130
|
-
`;
|
1131
|
-
out += `export * from "./include-spec";
|
1132
|
-
`;
|
1133
|
-
return out;
|
1134
|
-
}
|
1135
1204
|
|
1136
1205
|
// src/emit-include-loader.ts
|
1137
1206
|
function emitIncludeLoader(graph, model, maxDepth) {
|
@@ -1804,6 +1873,7 @@ async function generate(configPath) {
|
|
1804
1873
|
const includeSpec = emitIncludeSpec(graph);
|
1805
1874
|
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
1806
1875
|
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
1876
|
+
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|
1807
1877
|
files.push({
|
1808
1878
|
path: join(serverDir, "include-builder.ts"),
|
1809
1879
|
content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
|
package/dist/index.js
CHANGED
@@ -957,13 +957,116 @@ function emitClient(table) {
|
|
957
957
|
const pkType = hasCompositePk ? `{ ${safePk.map((c) => `${c}: string`).join("; ")} }` : `string`;
|
958
958
|
const pkPathExpr = hasCompositePk ? safePk.map((c) => `pk.${c}`).join(` + "/" + `) : `pk`;
|
959
959
|
return `/* Generated. Do not edit. */
|
960
|
+
import { BaseClient } from "./base-client";
|
960
961
|
import type { ${Type}IncludeSpec } from "./include-spec";
|
961
962
|
import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}";
|
962
963
|
|
963
|
-
|
964
|
-
|
964
|
+
/**
|
965
|
+
* Client for ${table.name} table operations
|
966
|
+
*/
|
967
|
+
export class ${Type}Client extends BaseClient {
|
968
|
+
private readonly resource = "/v1/${table.name}";
|
969
|
+
|
970
|
+
async create(data: Insert${Type}): Promise<Select${Type}> {
|
971
|
+
return this.post<Select${Type}>(this.resource, data);
|
972
|
+
}
|
973
|
+
|
974
|
+
async getByPk(pk: ${pkType}): Promise<Select${Type} | null> {
|
975
|
+
const path = ${pkPathExpr};
|
976
|
+
return this.get<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
977
|
+
}
|
978
|
+
|
979
|
+
async list(params?: {
|
980
|
+
include?: ${Type}IncludeSpec;
|
981
|
+
limit?: number;
|
982
|
+
offset?: number;
|
983
|
+
where?: any;
|
984
|
+
orderBy?: string;
|
985
|
+
order?: "asc" | "desc";
|
986
|
+
}): Promise<Select${Type}[]> {
|
987
|
+
return this.post<Select${Type}[]>(\`\${this.resource}/list\`, params ?? {});
|
988
|
+
}
|
989
|
+
|
990
|
+
async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null> {
|
991
|
+
const path = ${pkPathExpr};
|
992
|
+
return this.patch<Select${Type} | null>(\`\${this.resource}/\${path}\`, patch);
|
993
|
+
}
|
994
|
+
|
995
|
+
async delete(pk: ${pkType}): Promise<Select${Type} | null> {
|
996
|
+
const path = ${pkPathExpr};
|
997
|
+
return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
|
998
|
+
}
|
999
|
+
}
|
1000
|
+
`;
|
1001
|
+
}
|
1002
|
+
function emitClientIndex(tables) {
|
1003
|
+
let out = `/* Generated. Do not edit. */
|
1004
|
+
`;
|
1005
|
+
out += `import { BaseClient, AuthConfig } from "./base-client";
|
1006
|
+
`;
|
1007
|
+
for (const t of tables) {
|
1008
|
+
out += `import { ${pascal(t.name)}Client } from "./${t.name}";
|
1009
|
+
`;
|
1010
|
+
}
|
1011
|
+
out += `
|
1012
|
+
// Re-export auth types for convenience
|
1013
|
+
`;
|
1014
|
+
out += `export type { AuthConfig as SDKAuth, AuthConfig, HeaderMap, AuthHeadersProvider } from "./base-client";
|
1015
|
+
|
1016
|
+
`;
|
1017
|
+
out += `/**
|
1018
|
+
`;
|
1019
|
+
out += ` * Main SDK class that provides access to all table clients
|
1020
|
+
`;
|
1021
|
+
out += ` */
|
1022
|
+
`;
|
1023
|
+
out += `export class SDK {
|
1024
|
+
`;
|
1025
|
+
for (const t of tables) {
|
1026
|
+
out += ` public ${t.name}: ${pascal(t.name)}Client;
|
1027
|
+
`;
|
1028
|
+
}
|
1029
|
+
out += `
|
1030
|
+
constructor(cfg: { baseUrl: string; fetch?: typeof fetch; auth?: AuthConfig }) {
|
1031
|
+
`;
|
1032
|
+
out += ` const f = cfg.fetch ?? fetch;
|
1033
|
+
`;
|
1034
|
+
for (const t of tables) {
|
1035
|
+
out += ` this.${t.name} = new ${pascal(t.name)}Client(cfg.baseUrl, f, cfg.auth);
|
1036
|
+
`;
|
1037
|
+
}
|
1038
|
+
out += ` }
|
1039
|
+
`;
|
1040
|
+
out += `}
|
965
1041
|
|
966
|
-
|
1042
|
+
`;
|
1043
|
+
out += `// Export individual table clients
|
1044
|
+
`;
|
1045
|
+
for (const t of tables) {
|
1046
|
+
out += `export { ${pascal(t.name)}Client } from "./${t.name}";
|
1047
|
+
`;
|
1048
|
+
}
|
1049
|
+
out += `
|
1050
|
+
// Export base client for custom extensions
|
1051
|
+
`;
|
1052
|
+
out += `export { BaseClient } from "./base-client";
|
1053
|
+
`;
|
1054
|
+
out += `
|
1055
|
+
// Export include specifications
|
1056
|
+
`;
|
1057
|
+
out += `export * from "./include-spec";
|
1058
|
+
`;
|
1059
|
+
return out;
|
1060
|
+
}
|
1061
|
+
|
1062
|
+
// src/emit-base-client.ts
|
1063
|
+
function emitBaseClient() {
|
1064
|
+
return `/* Generated. Do not edit. */
|
1065
|
+
|
1066
|
+
export type HeaderMap = Record<string, string>;
|
1067
|
+
export type AuthHeadersProvider = () => Promise<HeaderMap> | HeaderMap;
|
1068
|
+
|
1069
|
+
export type AuthConfig =
|
967
1070
|
| AuthHeadersProvider
|
968
1071
|
| {
|
969
1072
|
apiKey?: string;
|
@@ -975,14 +1078,18 @@ type AuthConfig =
|
|
975
1078
|
headers?: AuthHeadersProvider;
|
976
1079
|
};
|
977
1080
|
|
978
|
-
|
1081
|
+
/**
|
1082
|
+
* Base client class with shared authentication and request handling logic.
|
1083
|
+
* All table-specific clients extend this class.
|
1084
|
+
*/
|
1085
|
+
export abstract class BaseClient {
|
979
1086
|
constructor(
|
980
|
-
|
981
|
-
|
982
|
-
|
1087
|
+
protected baseUrl: string,
|
1088
|
+
protected fetchFn: typeof fetch = fetch,
|
1089
|
+
protected auth?: AuthConfig
|
983
1090
|
) {}
|
984
1091
|
|
985
|
-
|
1092
|
+
protected async authHeaders(): Promise<HeaderMap> {
|
986
1093
|
if (!this.auth) return {};
|
987
1094
|
if (typeof this.auth === "function") {
|
988
1095
|
const h = await this.auth();
|
@@ -1008,129 +1115,91 @@ export class ${Type}Client {
|
|
1008
1115
|
return out;
|
1009
1116
|
}
|
1010
1117
|
|
1011
|
-
|
1118
|
+
protected async headers(json = false): Promise<HeaderMap> {
|
1012
1119
|
const extra = await this.authHeaders();
|
1013
1120
|
return json ? { "Content-Type": "application/json", ...extra } : extra;
|
1014
1121
|
}
|
1015
1122
|
|
1016
|
-
|
1123
|
+
protected async okOrThrow(res: Response, action: string, entity: string): Promise<void> {
|
1017
1124
|
if (!res.ok) {
|
1018
1125
|
let detail = "";
|
1019
1126
|
try { detail = await res.text(); } catch {}
|
1020
|
-
throw new Error(\`\${action}
|
1127
|
+
throw new Error(\`\${action} \${entity} failed: \${res.status} \${detail}\`);
|
1021
1128
|
}
|
1022
1129
|
}
|
1023
1130
|
|
1024
|
-
|
1025
|
-
|
1131
|
+
/**
|
1132
|
+
* Make a POST request
|
1133
|
+
*/
|
1134
|
+
protected async post<T>(path: string, body?: any): Promise<T> {
|
1135
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1026
1136
|
method: "POST",
|
1027
1137
|
headers: await this.headers(true),
|
1028
|
-
body: JSON.stringify(
|
1138
|
+
body: JSON.stringify(body),
|
1029
1139
|
});
|
1030
|
-
|
1031
|
-
|
1140
|
+
|
1141
|
+
// Handle 404 specially for operations that might return null
|
1142
|
+
if (res.status === 404) {
|
1143
|
+
return null as T;
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
await this.okOrThrow(res, "POST", path);
|
1147
|
+
return (await res.json()) as T;
|
1032
1148
|
}
|
1033
1149
|
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1150
|
+
/**
|
1151
|
+
* Make a GET request
|
1152
|
+
*/
|
1153
|
+
protected async get<T>(path: string): Promise<T> {
|
1154
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1037
1155
|
headers: await this.headers(),
|
1038
1156
|
});
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
method: "POST",
|
1047
|
-
headers: await this.headers(true),
|
1048
|
-
body: JSON.stringify(params ?? {}),
|
1049
|
-
});
|
1050
|
-
await this.okOrThrow(res, "list");
|
1051
|
-
return (await res.json()) as Select${Type}[];
|
1157
|
+
|
1158
|
+
if (res.status === 404) {
|
1159
|
+
return null as T;
|
1160
|
+
}
|
1161
|
+
|
1162
|
+
await this.okOrThrow(res, "GET", path);
|
1163
|
+
return (await res.json()) as T;
|
1052
1164
|
}
|
1053
1165
|
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1166
|
+
/**
|
1167
|
+
* Make a PATCH request
|
1168
|
+
*/
|
1169
|
+
protected async patch<T>(path: string, body?: any): Promise<T> {
|
1170
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1057
1171
|
method: "PATCH",
|
1058
1172
|
headers: await this.headers(true),
|
1059
|
-
body: JSON.stringify(
|
1173
|
+
body: JSON.stringify(body),
|
1060
1174
|
});
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1175
|
+
|
1176
|
+
if (res.status === 404) {
|
1177
|
+
return null as T;
|
1178
|
+
}
|
1179
|
+
|
1180
|
+
await this.okOrThrow(res, "PATCH", path);
|
1181
|
+
return (await res.json()) as T;
|
1064
1182
|
}
|
1065
1183
|
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1184
|
+
/**
|
1185
|
+
* Make a DELETE request
|
1186
|
+
*/
|
1187
|
+
protected async del<T>(path: string): Promise<T> {
|
1188
|
+
const res = await this.fetchFn(\`\${this.baseUrl}\${path}\`, {
|
1069
1189
|
method: "DELETE",
|
1070
1190
|
headers: await this.headers(),
|
1071
1191
|
});
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1192
|
+
|
1193
|
+
if (res.status === 404) {
|
1194
|
+
return null as T;
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
await this.okOrThrow(res, "DELETE", path);
|
1198
|
+
return (await res.json()) as T;
|
1075
1199
|
}
|
1076
1200
|
}
|
1077
1201
|
`;
|
1078
1202
|
}
|
1079
|
-
function emitClientIndex(tables) {
|
1080
|
-
let out = `/* Generated. Do not edit. */
|
1081
|
-
`;
|
1082
|
-
for (const t of tables) {
|
1083
|
-
out += `import { ${pascal(t.name)}Client } from "./${t.name}";
|
1084
|
-
`;
|
1085
|
-
}
|
1086
|
-
out += `
|
1087
|
-
export type SDKAuthHeadersProvider = () => Promise<Record<string,string>> | Record<string,string>;
|
1088
|
-
`;
|
1089
|
-
out += `export type SDKAuth =
|
1090
|
-
`;
|
1091
|
-
out += ` | SDKAuthHeadersProvider
|
1092
|
-
`;
|
1093
|
-
out += ` | {
|
1094
|
-
`;
|
1095
|
-
out += ` apiKey?: string;
|
1096
|
-
`;
|
1097
|
-
out += ` /** defaults to "x-api-key" */
|
1098
|
-
`;
|
1099
|
-
out += ` apiKeyHeader?: string;
|
1100
|
-
`;
|
1101
|
-
out += ` jwt?: string | (() => Promise<string>);
|
1102
|
-
`;
|
1103
|
-
out += ` headers?: SDKAuthHeadersProvider;
|
1104
|
-
`;
|
1105
|
-
out += ` };
|
1106
|
-
|
1107
|
-
`;
|
1108
|
-
out += `export class SDK {
|
1109
|
-
`;
|
1110
|
-
for (const t of tables) {
|
1111
|
-
out += ` public ${t.name}: ${pascal(t.name)}Client;
|
1112
|
-
`;
|
1113
|
-
}
|
1114
|
-
out += `
|
1115
|
-
constructor(cfg: { baseUrl: string; fetch?: typeof fetch; auth?: SDKAuth }) {
|
1116
|
-
`;
|
1117
|
-
out += ` const f = cfg.fetch ?? fetch;
|
1118
|
-
`;
|
1119
|
-
for (const t of tables) {
|
1120
|
-
out += ` this.${t.name} = new ${pascal(t.name)}Client(cfg.baseUrl, f, cfg.auth);
|
1121
|
-
`;
|
1122
|
-
}
|
1123
|
-
out += ` }
|
1124
|
-
`;
|
1125
|
-
out += `}
|
1126
|
-
`;
|
1127
|
-
for (const t of tables)
|
1128
|
-
out += `export { ${pascal(t.name)}Client } from "./${t.name}";
|
1129
|
-
`;
|
1130
|
-
out += `export * from "./include-spec";
|
1131
|
-
`;
|
1132
|
-
return out;
|
1133
|
-
}
|
1134
1203
|
|
1135
1204
|
// src/emit-include-loader.ts
|
1136
1205
|
function emitIncludeLoader(graph, model, maxDepth) {
|
@@ -1803,6 +1872,7 @@ async function generate(configPath) {
|
|
1803
1872
|
const includeSpec = emitIncludeSpec(graph);
|
1804
1873
|
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
1805
1874
|
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
1875
|
+
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|
1806
1876
|
files.push({
|
1807
1877
|
path: join(serverDir, "include-builder.ts"),
|
1808
1878
|
content: emitIncludeBuilder(graph, cfg.includeDepthLimit || 3)
|