@lark-apaas/db-schema-sync 0.1.0-alpha.1
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/bin.d.ts +1 -0
- package/dist/bin.js +442 -0
- package/dist/chunk-QO7QQCAM.js +2209 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +15 -0
- package/package.json +42 -0
|
@@ -0,0 +1,2209 @@
|
|
|
1
|
+
import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
|
|
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 __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// ../../server/http-client/dist/index.js
|
|
35
|
+
var require_dist = __commonJS({
|
|
36
|
+
"../../server/http-client/dist/index.js"(exports, module) {
|
|
37
|
+
"use strict";
|
|
38
|
+
var __defProp2 = Object.defineProperty;
|
|
39
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
40
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
41
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
42
|
+
var __export = (target, all) => {
|
|
43
|
+
for (var name in all)
|
|
44
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
45
|
+
};
|
|
46
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
47
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
48
|
+
for (let key of __getOwnPropNames2(from))
|
|
49
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
50
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
51
|
+
}
|
|
52
|
+
return to;
|
|
53
|
+
};
|
|
54
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
55
|
+
var index_exports = {};
|
|
56
|
+
__export(index_exports, {
|
|
57
|
+
DEFAULT_CLOCK_TOLERANCE_SEC: () => DEFAULT_CLOCK_TOLERANCE_SEC,
|
|
58
|
+
DEFAULT_JWT_EXPIRE_TIME_MS: () => DEFAULT_JWT_EXPIRE_TIME_MS,
|
|
59
|
+
HttpClient: () => HttpClient2,
|
|
60
|
+
HttpError: () => HttpError,
|
|
61
|
+
generateJWTToken: () => generateJWTToken,
|
|
62
|
+
parseJWTTokenWithVerify: () => parseJWTTokenWithVerify,
|
|
63
|
+
registerPlatformPlugin: () => registerPlatformPlugin,
|
|
64
|
+
resolvePlatformBaseURL: () => resolvePlatformBaseURL
|
|
65
|
+
});
|
|
66
|
+
module.exports = __toCommonJS(index_exports);
|
|
67
|
+
var import_crypto = __require("crypto");
|
|
68
|
+
var DEFAULT_JWT_EXPIRE_TIME_MS = 30 * 60 * 1e3;
|
|
69
|
+
var DEFAULT_CLOCK_TOLERANCE_SEC = 60;
|
|
70
|
+
var JWT_HEADER = {
|
|
71
|
+
alg: "HS256",
|
|
72
|
+
typ: "JWT"
|
|
73
|
+
};
|
|
74
|
+
function generateJWTToken(customClaims, config) {
|
|
75
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
76
|
+
const payload = {
|
|
77
|
+
...customClaims,
|
|
78
|
+
iss: customClaims.access_key,
|
|
79
|
+
iat: nowSeconds,
|
|
80
|
+
nbf: nowSeconds,
|
|
81
|
+
exp: nowSeconds + Math.floor(config.expireTimeMs / 1e3),
|
|
82
|
+
jti: (0, import_crypto.randomUUID)()
|
|
83
|
+
};
|
|
84
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(JWT_HEADER));
|
|
85
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
|
|
86
|
+
const signature = sign(`${encodedHeader}.${encodedPayload}`, config.secretKey);
|
|
87
|
+
return `${encodedHeader}.${encodedPayload}.${signature}`;
|
|
88
|
+
}
|
|
89
|
+
function parseJWTTokenWithVerify(tokenString, config, options) {
|
|
90
|
+
const segments = tokenString.split(".");
|
|
91
|
+
if (segments.length !== 3) {
|
|
92
|
+
throw new Error("invalid JWT token format");
|
|
93
|
+
}
|
|
94
|
+
const [encodedHeader, encodedPayload, signature] = segments;
|
|
95
|
+
const headerJson = JSON.parse(base64UrlDecode(encodedHeader));
|
|
96
|
+
if (headerJson.alg !== "HS256") {
|
|
97
|
+
throw new Error("unsupported JWT alg");
|
|
98
|
+
}
|
|
99
|
+
const expectedSignature = sign(`${encodedHeader}.${encodedPayload}`, config.secretKey);
|
|
100
|
+
const expectedBuffer = Buffer.from(expectedSignature);
|
|
101
|
+
const signatureBuffer = Buffer.from(signature);
|
|
102
|
+
if (expectedBuffer.length !== signatureBuffer.length || !(0, import_crypto.timingSafeEqual)(expectedBuffer, signatureBuffer)) {
|
|
103
|
+
throw new Error("JWT signature verification failed");
|
|
104
|
+
}
|
|
105
|
+
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
106
|
+
if (!options?.skipExpiration) {
|
|
107
|
+
const clockTolerance = options?.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SEC;
|
|
108
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
109
|
+
if (payload.exp !== void 0) {
|
|
110
|
+
if (payload.exp + clockTolerance < now) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`JWT token expired at ${new Date(payload.exp * 1e3).toISOString()}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (payload.nbf !== void 0) {
|
|
117
|
+
if (payload.nbf - clockTolerance > now) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`JWT token not yet valid, will be valid at ${new Date(payload.nbf * 1e3).toISOString()}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (payload.iat !== void 0) {
|
|
124
|
+
if (payload.iat - clockTolerance > now) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`JWT token issued in the future at ${new Date(payload.iat * 1e3).toISOString()}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return payload;
|
|
132
|
+
}
|
|
133
|
+
function sign(input, secret) {
|
|
134
|
+
return (0, import_crypto.createHmac)("sha256", secret).update(input).digest("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
135
|
+
}
|
|
136
|
+
function base64UrlEncode(input) {
|
|
137
|
+
return Buffer.from(input).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
138
|
+
}
|
|
139
|
+
function base64UrlDecode(input) {
|
|
140
|
+
const padLength = (4 - (input.length % 4 || 4)) % 4;
|
|
141
|
+
const padded = `${input}${"=".repeat(padLength)}`.replace(/-/g, "+").replace(/_/g, "/");
|
|
142
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
143
|
+
}
|
|
144
|
+
var JWTTokenManager = class {
|
|
145
|
+
cache = /* @__PURE__ */ new Map();
|
|
146
|
+
config;
|
|
147
|
+
constructor(config) {
|
|
148
|
+
this.config = {
|
|
149
|
+
refreshBeforeMs: 5 * 60 * 1e3,
|
|
150
|
+
// 默认 5 分钟
|
|
151
|
+
...config
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 获取 JWT token,优先使用缓存
|
|
156
|
+
*
|
|
157
|
+
* 如果缓存的 token 即将过期(距离过期时间小于 refreshBeforeMs),
|
|
158
|
+
* 则会生成新的 token 并更新缓存
|
|
159
|
+
*
|
|
160
|
+
* @param claims - JWT claims
|
|
161
|
+
* @returns JWT token 字符串
|
|
162
|
+
*/
|
|
163
|
+
getToken(claims) {
|
|
164
|
+
const cacheKey = this.getCacheKey(claims);
|
|
165
|
+
const cached = this.cache.get(cacheKey);
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
if (cached && cached.expiresAtMs - now > this.config.refreshBeforeMs) {
|
|
168
|
+
return cached.token;
|
|
169
|
+
}
|
|
170
|
+
const token = generateJWTToken(claims, this.config);
|
|
171
|
+
const expiresAtMs = now + this.config.expireTimeMs;
|
|
172
|
+
this.cache.set(cacheKey, { token, expiresAtMs });
|
|
173
|
+
return token;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 清除所有缓存的 token
|
|
177
|
+
*/
|
|
178
|
+
clearCache() {
|
|
179
|
+
this.cache.clear();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 清除特定 claims 的缓存
|
|
183
|
+
*
|
|
184
|
+
* @param claims - 要清除的 claims
|
|
185
|
+
*/
|
|
186
|
+
clearCacheFor(claims) {
|
|
187
|
+
const cacheKey = this.getCacheKey(claims);
|
|
188
|
+
this.cache.delete(cacheKey);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 生成缓存 key(基于关键的 claims 字段)
|
|
192
|
+
*
|
|
193
|
+
* 使用稳定的字段顺序确保相同 claims 生成相同 key
|
|
194
|
+
*
|
|
195
|
+
* @private
|
|
196
|
+
*/
|
|
197
|
+
getCacheKey(claims) {
|
|
198
|
+
const parts = [
|
|
199
|
+
claims.access_key,
|
|
200
|
+
claims.tenant_id?.toString() || "",
|
|
201
|
+
claims.user_id || "",
|
|
202
|
+
claims.app_id || "",
|
|
203
|
+
claims.app_env || "",
|
|
204
|
+
claims.sandbox_id || ""
|
|
205
|
+
];
|
|
206
|
+
return parts.join(":");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 获取缓存统计信息(用于监控和调试)
|
|
210
|
+
*
|
|
211
|
+
* @returns 缓存统计信息
|
|
212
|
+
*/
|
|
213
|
+
getCacheStats() {
|
|
214
|
+
const now = Date.now();
|
|
215
|
+
let validCount = 0;
|
|
216
|
+
let expiredCount = 0;
|
|
217
|
+
for (const cached of this.cache.values()) {
|
|
218
|
+
if (cached.expiresAtMs > now) {
|
|
219
|
+
validCount++;
|
|
220
|
+
} else {
|
|
221
|
+
expiredCount++;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
total: this.cache.size,
|
|
226
|
+
valid: validCount,
|
|
227
|
+
expired: expiredCount
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var DEFAULT_DOMAIN_ENV = "FORCE_AUTHN_INNERAPI_DOMAIN";
|
|
232
|
+
var DEFAULT_ACCESS_KEY_ENV = "FORCE_AUTHN_ACCESS_KEY";
|
|
233
|
+
var DEFAULT_SECRET_KEY_ENV = "FORCE_AUTHN_ACCESS_SECRET";
|
|
234
|
+
function resolvePlatformBaseURL(options) {
|
|
235
|
+
if (!options?.enabled) {
|
|
236
|
+
return options?.baseURL;
|
|
237
|
+
}
|
|
238
|
+
if (options.baseURL) {
|
|
239
|
+
return options.baseURL;
|
|
240
|
+
}
|
|
241
|
+
const envName = options.domainEnv || DEFAULT_DOMAIN_ENV;
|
|
242
|
+
const domain = process.env[envName];
|
|
243
|
+
if (!domain) {
|
|
244
|
+
throw new Error(`\u5E73\u53F0\u6A21\u5F0F\u9700\u8981\u57FA\u7840\u57DF\u540D\uFF0C\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${envName}`);
|
|
245
|
+
}
|
|
246
|
+
return domain;
|
|
247
|
+
}
|
|
248
|
+
function registerPlatformPlugin(interceptors, options) {
|
|
249
|
+
if (!options.enabled) {
|
|
250
|
+
return void 0;
|
|
251
|
+
}
|
|
252
|
+
ensureNoAccessKeyOverride("platform.defaultClaims", options.defaultClaims);
|
|
253
|
+
const accessKeyEnv = options.accessKeyEnv || DEFAULT_ACCESS_KEY_ENV;
|
|
254
|
+
const secretKeyEnv = options.secretKeyEnv || DEFAULT_SECRET_KEY_ENV;
|
|
255
|
+
const accessKey = options.accessKey ?? (process.env[accessKeyEnv] || "");
|
|
256
|
+
const secretKey = options.secretKey ?? (process.env[secretKeyEnv] || "");
|
|
257
|
+
const expireTimeMs = options.expireTimeMs ?? DEFAULT_JWT_EXPIRE_TIME_MS;
|
|
258
|
+
const tokenManager = new JWTTokenManager({
|
|
259
|
+
accessKey,
|
|
260
|
+
secretKey,
|
|
261
|
+
expireTimeMs,
|
|
262
|
+
refreshBeforeMs: options.refreshBeforeMs
|
|
263
|
+
});
|
|
264
|
+
interceptors.request.use((config) => {
|
|
265
|
+
ensureNoAccessKeyOverride("request.platformAuth.customClaims", config.platformAuth?.customClaims);
|
|
266
|
+
const claims = {
|
|
267
|
+
...options.defaultClaims || {},
|
|
268
|
+
...config.platformAuth?.customClaims || {},
|
|
269
|
+
access_key: accessKey
|
|
270
|
+
};
|
|
271
|
+
const token = tokenManager.getToken(claims);
|
|
272
|
+
const headers = {
|
|
273
|
+
...config.headers,
|
|
274
|
+
"Authorization": `Bearer ${token}`,
|
|
275
|
+
"x-api-key": accessKey
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
...config,
|
|
279
|
+
headers
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
return tokenManager;
|
|
283
|
+
}
|
|
284
|
+
function ensureNoAccessKeyOverride(name, claims) {
|
|
285
|
+
if (claims && Object.prototype.hasOwnProperty.call(claims, "access_key")) {
|
|
286
|
+
throw new Error(`${name} \u4E0D\u5141\u8BB8\u8BBE\u7F6E access_key`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
var InterceptorManager = class {
|
|
290
|
+
interceptors = [];
|
|
291
|
+
/**
|
|
292
|
+
* 添加拦截器
|
|
293
|
+
* @returns 拦截器 ID,用于后续移除
|
|
294
|
+
*/
|
|
295
|
+
use(onFulfilled, onRejected) {
|
|
296
|
+
this.interceptors.push({ onFulfilled, onRejected });
|
|
297
|
+
return this.interceptors.length - 1;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 移除拦截器
|
|
301
|
+
*/
|
|
302
|
+
eject(id) {
|
|
303
|
+
if (this.interceptors[id]) {
|
|
304
|
+
this.interceptors[id] = null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 清空所有拦截器
|
|
309
|
+
*/
|
|
310
|
+
clear() {
|
|
311
|
+
this.interceptors = [];
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 遍历所有拦截器
|
|
315
|
+
*/
|
|
316
|
+
forEach(fn) {
|
|
317
|
+
this.interceptors.forEach((interceptor) => {
|
|
318
|
+
if (interceptor !== null) {
|
|
319
|
+
fn(interceptor);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
function isPlainObject(value) {
|
|
325
|
+
if (typeof value !== "object" || value === null) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
const proto = Object.getPrototypeOf(value);
|
|
329
|
+
return proto === Object.prototype || proto === null;
|
|
330
|
+
}
|
|
331
|
+
function normalizeHeaders(headers) {
|
|
332
|
+
if (!headers) {
|
|
333
|
+
return {};
|
|
334
|
+
}
|
|
335
|
+
if (headers instanceof Headers) {
|
|
336
|
+
const normalized2 = {};
|
|
337
|
+
headers.forEach((value, key) => {
|
|
338
|
+
normalized2[key] = value;
|
|
339
|
+
});
|
|
340
|
+
return normalized2;
|
|
341
|
+
}
|
|
342
|
+
if (Array.isArray(headers)) {
|
|
343
|
+
return headers.reduce((acc, [key, value]) => {
|
|
344
|
+
acc[key] = value;
|
|
345
|
+
return acc;
|
|
346
|
+
}, {});
|
|
347
|
+
}
|
|
348
|
+
const normalized = {};
|
|
349
|
+
Object.entries(headers).forEach(
|
|
350
|
+
([key, value]) => {
|
|
351
|
+
const normalizedValue = Array.isArray(value) ? value.join(",") : value;
|
|
352
|
+
normalized[key] = normalizedValue;
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
return normalized;
|
|
356
|
+
}
|
|
357
|
+
var HttpError = class _HttpError extends Error {
|
|
358
|
+
isHttpError = true;
|
|
359
|
+
response;
|
|
360
|
+
config;
|
|
361
|
+
constructor(response, config, message) {
|
|
362
|
+
super(message || `Request failed with status ${response?.status || "unknown"}`);
|
|
363
|
+
this.name = "HttpError";
|
|
364
|
+
this.response = response;
|
|
365
|
+
this.config = sanitizeConfig(config);
|
|
366
|
+
Object.setPrototypeOf(this, _HttpError.prototype);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
function sanitizeConfig(config) {
|
|
370
|
+
const { headers, ...rest } = config;
|
|
371
|
+
return {
|
|
372
|
+
...rest,
|
|
373
|
+
headers: sanitizeHeaders(headers)
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function sanitizeHeaders(headers) {
|
|
377
|
+
if (!headers) {
|
|
378
|
+
return void 0;
|
|
379
|
+
}
|
|
380
|
+
const normalized = normalizeHeaders(headers);
|
|
381
|
+
const sanitized = {};
|
|
382
|
+
const sensitiveHeaders = ["authorization", "x-api-key", "cookie", "x-secret"];
|
|
383
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
384
|
+
const lowerKey = key.toLowerCase();
|
|
385
|
+
if (sensitiveHeaders.includes(lowerKey)) {
|
|
386
|
+
sanitized[key] = "[REDACTED]";
|
|
387
|
+
} else {
|
|
388
|
+
sanitized[key] = value;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return sanitized;
|
|
392
|
+
}
|
|
393
|
+
var HttpClient2 = class {
|
|
394
|
+
defaultConfig;
|
|
395
|
+
securityConfig;
|
|
396
|
+
/**
|
|
397
|
+
* 拦截器管理器(API 与 Axios 一致)
|
|
398
|
+
*/
|
|
399
|
+
interceptors = {
|
|
400
|
+
request: new InterceptorManager(),
|
|
401
|
+
response: new InterceptorManager()
|
|
402
|
+
};
|
|
403
|
+
constructor(config) {
|
|
404
|
+
const { platform, security, ...restConfig } = config || {};
|
|
405
|
+
this.defaultConfig = {
|
|
406
|
+
timeout: 5e3,
|
|
407
|
+
...restConfig
|
|
408
|
+
};
|
|
409
|
+
const strictMode = security?.strictMode ?? false;
|
|
410
|
+
this.securityConfig = {
|
|
411
|
+
allowedProtocols: strictMode ? ["http:", "https:"] : null,
|
|
412
|
+
maxResponseSize: strictMode ? 50 * 1024 * 1024 : 0,
|
|
413
|
+
// 50MB
|
|
414
|
+
strictMode,
|
|
415
|
+
...security
|
|
416
|
+
};
|
|
417
|
+
if (platform?.enabled) {
|
|
418
|
+
if (!this.defaultConfig.baseURL) {
|
|
419
|
+
this.defaultConfig.baseURL = resolvePlatformBaseURL(platform);
|
|
420
|
+
}
|
|
421
|
+
registerPlatformPlugin(this.interceptors, platform);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* GET 请求
|
|
426
|
+
*/
|
|
427
|
+
async get(url, config) {
|
|
428
|
+
return this.request({ ...config, url, method: "GET" });
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* POST 请求
|
|
432
|
+
*/
|
|
433
|
+
async post(url, data, config) {
|
|
434
|
+
const headers = { ...config?.headers };
|
|
435
|
+
let body = data;
|
|
436
|
+
if (isPlainObject(data)) {
|
|
437
|
+
body = JSON.stringify(data);
|
|
438
|
+
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
|
|
439
|
+
headers["Content-Type"] = "application/json";
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return this.request({
|
|
443
|
+
...config,
|
|
444
|
+
url,
|
|
445
|
+
method: "POST",
|
|
446
|
+
body,
|
|
447
|
+
headers
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* PUT 请求
|
|
452
|
+
*/
|
|
453
|
+
async put(url, data, config) {
|
|
454
|
+
const headers = { ...config?.headers };
|
|
455
|
+
let body = data;
|
|
456
|
+
if (isPlainObject(data)) {
|
|
457
|
+
body = JSON.stringify(data);
|
|
458
|
+
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
|
|
459
|
+
headers["Content-Type"] = "application/json";
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return this.request({
|
|
463
|
+
...config,
|
|
464
|
+
url,
|
|
465
|
+
method: "PUT",
|
|
466
|
+
body,
|
|
467
|
+
headers
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* DELETE 请求
|
|
472
|
+
*/
|
|
473
|
+
async delete(url, config) {
|
|
474
|
+
return this.request({ ...config, url, method: "DELETE" });
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* PATCH 请求
|
|
478
|
+
*/
|
|
479
|
+
async patch(url, data, config) {
|
|
480
|
+
const headers = { ...config?.headers };
|
|
481
|
+
let body = data;
|
|
482
|
+
if (isPlainObject(data)) {
|
|
483
|
+
body = JSON.stringify(data);
|
|
484
|
+
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
|
|
485
|
+
headers["Content-Type"] = "application/json";
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return this.request({
|
|
489
|
+
...config,
|
|
490
|
+
url,
|
|
491
|
+
method: "PATCH",
|
|
492
|
+
body,
|
|
493
|
+
headers
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* 核心请求方法(fetch 风格)
|
|
498
|
+
*/
|
|
499
|
+
async request(config) {
|
|
500
|
+
const baseHeaders = normalizeHeaders(this.defaultConfig.headers);
|
|
501
|
+
const overrideHeaders = normalizeHeaders(config.headers);
|
|
502
|
+
const mergedKeyMap = {};
|
|
503
|
+
for (const key in baseHeaders) {
|
|
504
|
+
mergedKeyMap[key.toLowerCase()] = { key, value: baseHeaders[key] };
|
|
505
|
+
}
|
|
506
|
+
for (const key in overrideHeaders) {
|
|
507
|
+
mergedKeyMap[key.toLowerCase()] = { key, value: overrideHeaders[key] };
|
|
508
|
+
}
|
|
509
|
+
const finalHeaders = {};
|
|
510
|
+
for (const lowerKey in mergedKeyMap) {
|
|
511
|
+
const { key, value } = mergedKeyMap[lowerKey];
|
|
512
|
+
finalHeaders[key] = value;
|
|
513
|
+
}
|
|
514
|
+
let mergedConfig = {
|
|
515
|
+
...this.defaultConfig,
|
|
516
|
+
...config,
|
|
517
|
+
headers: finalHeaders
|
|
518
|
+
};
|
|
519
|
+
try {
|
|
520
|
+
mergedConfig = await this.runRequestInterceptors(mergedConfig);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
return Promise.reject(error);
|
|
523
|
+
}
|
|
524
|
+
mergedConfig.headers = normalizeHeaders(mergedConfig.headers);
|
|
525
|
+
const fullUrl = this.buildUrl(mergedConfig.url, mergedConfig.params);
|
|
526
|
+
const timeout = mergedConfig.timeout || this.defaultConfig.timeout || 5e3;
|
|
527
|
+
const controller = new AbortController();
|
|
528
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
529
|
+
const externalSignal = mergedConfig.signal;
|
|
530
|
+
if (externalSignal) {
|
|
531
|
+
if (externalSignal.aborted) {
|
|
532
|
+
clearTimeout(timeoutId);
|
|
533
|
+
controller.abort();
|
|
534
|
+
} else {
|
|
535
|
+
externalSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
const { platformAuth, params, timeout: _timeout, baseURL, url, ...fetchOptions } = mergedConfig;
|
|
539
|
+
try {
|
|
540
|
+
const response = await fetch(fullUrl, {
|
|
541
|
+
...fetchOptions,
|
|
542
|
+
signal: controller.signal
|
|
543
|
+
// 传递我们自己的 controller signal
|
|
544
|
+
});
|
|
545
|
+
clearTimeout(timeoutId);
|
|
546
|
+
if (this.securityConfig.maxResponseSize > 0) {
|
|
547
|
+
const contentLength = response.headers.get("content-length");
|
|
548
|
+
if (contentLength) {
|
|
549
|
+
const size = parseInt(contentLength, 10);
|
|
550
|
+
if (size > this.securityConfig.maxResponseSize) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Response size ${size} bytes exceeds limit of ${this.securityConfig.maxResponseSize} bytes`
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (!response.ok) {
|
|
558
|
+
const error = new HttpError(response, mergedConfig);
|
|
559
|
+
return this.runResponseInterceptors(Promise.reject(error));
|
|
560
|
+
}
|
|
561
|
+
return this.runResponseInterceptors(Promise.resolve(response));
|
|
562
|
+
} catch (error) {
|
|
563
|
+
clearTimeout(timeoutId);
|
|
564
|
+
const httpError = error instanceof HttpError ? error : new HttpError(
|
|
565
|
+
void 0,
|
|
566
|
+
mergedConfig,
|
|
567
|
+
error.name === "AbortError" ? "Request aborted" : error.message
|
|
568
|
+
);
|
|
569
|
+
return this.runResponseInterceptors(Promise.reject(httpError));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* 执行请求拦截器链
|
|
574
|
+
*/
|
|
575
|
+
runRequestInterceptors(config) {
|
|
576
|
+
let promise = Promise.resolve(config);
|
|
577
|
+
const interceptors = [];
|
|
578
|
+
this.interceptors.request.forEach((interceptor) => {
|
|
579
|
+
interceptors.push(interceptor);
|
|
580
|
+
});
|
|
581
|
+
for (const interceptor of interceptors) {
|
|
582
|
+
promise = promise.then(
|
|
583
|
+
interceptor.onFulfilled,
|
|
584
|
+
interceptor.onRejected
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
return promise;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* 执行响应拦截器链
|
|
591
|
+
*/
|
|
592
|
+
async runResponseInterceptors(promise) {
|
|
593
|
+
let currentPromise = promise;
|
|
594
|
+
const interceptors = [];
|
|
595
|
+
this.interceptors.response.forEach((interceptor) => {
|
|
596
|
+
interceptors.push(interceptor);
|
|
597
|
+
});
|
|
598
|
+
for (const interceptor of interceptors) {
|
|
599
|
+
currentPromise = currentPromise.then(
|
|
600
|
+
interceptor.onFulfilled,
|
|
601
|
+
interceptor.onRejected
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return currentPromise;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* 构建完整 URL(包含 baseURL 和 query params)
|
|
608
|
+
*/
|
|
609
|
+
buildUrl(url, params) {
|
|
610
|
+
const baseURL = this.defaultConfig.baseURL;
|
|
611
|
+
let combinedUrl;
|
|
612
|
+
if (!baseURL || /^https?:\/\//i.test(url)) {
|
|
613
|
+
combinedUrl = url;
|
|
614
|
+
} else {
|
|
615
|
+
combinedUrl = baseURL.replace(/\/+$/, "") + "/" + url.replace(/^\/+/, "");
|
|
616
|
+
}
|
|
617
|
+
const fullUrl = new URL(combinedUrl);
|
|
618
|
+
const { allowedProtocols } = this.securityConfig;
|
|
619
|
+
if (allowedProtocols && !allowedProtocols.includes(fullUrl.protocol)) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
`Protocol ${fullUrl.protocol} is not allowed. Allowed: ${allowedProtocols.join(", ")}`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (params) {
|
|
625
|
+
for (const [key, value] of Object.entries(params)) {
|
|
626
|
+
if (value === void 0 || value === null) continue;
|
|
627
|
+
fullUrl.searchParams.set(key, String(value));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return fullUrl.href;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
// ../../../node_modules/tiny-pinyin/dist/dict.js
|
|
637
|
+
var require_dict = __commonJS({
|
|
638
|
+
"../../../node_modules/tiny-pinyin/dist/dict.js"(exports, module) {
|
|
639
|
+
"use strict";
|
|
640
|
+
var UNIHANS = ["\u963F", "\u54CE", "\u5B89", "\u80AE", "\u51F9", "\u516B", "\u6300", "\u6273", "\u90A6", "\u52F9", "\u9642", "\u5954", "\u4F3B", "\u5C44", "\u8FB9", "\u706C", "\u618B", "\u6C43", "\u51AB", "\u7676", "\u5CEC", "\u5693", "\u5072", "\u53C2", "\u4ED3", "\u64A1", "\u518A", "\u5D7E", "\u66FD", "\u53C9", "\u8286", "\u8FBF", "\u4F25", "\u6284", "\u8F66", "\u62BB", "\u9637", "\u5403", "\u5145", "\u62BD", "\u51FA", "\u6B3B", "\u63E3", "\u5DDB", "\u5205", "\u5439", "\u65FE", "\u9034", "\u5472", "\u5306", "\u51D1", "\u7C97", "\u6C46", "\u5D14", "\u90A8", "\u6413", "\u5491", "\u5446", "\u4E39", "\u5F53", "\u5200", "\u561A", "\u6265", "\u706F", "\u6C10", "\u7538", "\u5201", "\u7239", "\u4E01", "\u4E1F", "\u4E1C", "\u543A", "\u53BE", "\u8011", "\u5796", "\u5428", "\u591A", "\u59B8", "\u8BF6", "\u5940", "\u97A5", "\u513F", "\u53D1", "\u5E06", "\u531A", "\u98DE", "\u5206", "\u4E30", "\u8985", "\u4ECF", "\u7D11", "\u592B", "\u65EE", "\u4F85", "\u7518", "\u5188", "\u768B", "\u6208", "\u7ED9", "\u6839", "\u522F", "\u5DE5", "\u52FE", "\u4F30", "\u74DC", "\u4E56", "\u5173", "\u5149", "\u5F52", "\u4E28", "\u5459", "\u54C8", "\u548D", "\u4F44", "\u592F", "\u8320", "\u8BC3", "\u9ED2", "\u62EB", "\u4EA8", "\u5677", "\u53FF", "\u9F41", "\u4E4E", "\u82B1", "\u6000", "\u6B22", "\u5DDF", "\u7070", "\u660F", "\u5419", "\u4E0C", "\u52A0", "\u620B", "\u6C5F", "\u827D", "\u9636", "\u5DFE", "\u5755", "\u5182", "\u4E29", "\u51E5", "\u59E2", "\u5658", "\u519B", "\u5494", "\u5F00", "\u520A", "\u5FFC", "\u5C3B", "\u533C", "\u808E", "\u52A5", "\u7A7A", "\u62A0", "\u625D", "\u5938", "\u84AF", "\u5BBD", "\u5321", "\u4E8F", "\u5764", "\u6269", "\u5783", "\u6765", "\u5170", "\u5577", "\u635E", "\u808B", "\u52D2", "\u5D1A", "\u54E9", "\u4FE9", "\u5941", "\u826F", "\u64A9", "\u6BDF", "\u62CE", "\u4F36", "\u6E9C", "\u56D6", "\u9F99", "\u779C", "\u565C", "\u9A74", "\u5A08", "\u63A0", "\u62A1", "\u7F57", "\u5463", "\u5988", "\u57CB", "\u5ADA", "\u7264", "\u732B", "\u4E48", "\u5445", "\u95E8", "\u753F", "\u54AA", "\u5B80", "\u55B5", "\u4E5C", "\u6C11", "\u540D", "\u8C2C", "\u6478", "\u54DE", "\u6BEA", "\u55EF", "\u62CF", "\u8149", "\u56E1", "\u56D4", "\u5B6C", "\u7592", "\u5A1E", "\u6041", "\u80FD", "\u59AE", "\u62C8", "\u5A18", "\u9E1F", "\u634F", "\u56DC", "\u5B81", "\u599E", "\u519C", "\u7FBA", "\u5974", "\u5973", "\u597B", "\u759F", "\u9EC1", "\u632A", "\u5594", "\u8BB4", "\u5991", "\u62CD", "\u7705", "\u4E53", "\u629B", "\u5478", "\u55B7", "\u5309", "\u4E15", "\u56E8", "\u527D", "\u6C15", "\u59D8", "\u4E52", "\u948B", "\u5256", "\u4EC6", "\u4E03", "\u6390", "\u5343", "\u545B", "\u6084", "\u767F", "\u4EB2", "\u9751", "\u536D", "\u4E18", "\u533A", "\u5CD1", "\u7F3A", "\u590B", "\u5465", "\u7A63", "\u5A06", "\u60F9", "\u4EBA", "\u6254", "\u65E5", "\u8338", "\u53B9", "\u909A", "\u633C", "\u5827", "\u5A51", "\u77A4", "\u637C", "\u4EE8", "\u6BE2", "\u4E09", "\u6852", "\u63BB", "\u95AA", "\u68EE", "\u50E7", "\u6740", "\u7B5B", "\u5C71", "\u4F24", "\u5F30", "\u5962", "\u7533", "\u5347", "\u5C38", "\u53CE", "\u4E66", "\u5237", "\u8870", "\u95E9", "\u53CC", "\u813D", "\u542E", "\u8BF4", "\u53B6", "\u5FEA", "\u635C", "\u82CF", "\u72FB", "\u590A", "\u5B59", "\u5506", "\u4ED6", "\u56FC", "\u574D", "\u6C64", "\u5932", "\u5FD1", "\u71A5", "\u5254", "\u5929", "\u65EB", "\u5E16", "\u5385", "\u56F2", "\u5077", "\u51F8", "\u6E4D", "\u63A8", "\u541E", "\u4E47", "\u7A75", "\u6B6A", "\u5F2F", "\u5C23", "\u5371", "\u6637", "\u7FC1", "\u631D", "\u4E4C", "\u5915", "\u8672", "\u4ED9", "\u4E61", "\u7071", "\u4E9B", "\u5FC3", "\u661F", "\u51F6", "\u4F11", "\u5401", "\u5405", "\u524A", "\u5743", "\u4E2B", "\u6079", "\u592E", "\u5E7A", "\u503B", "\u4E00", "\u56D9", "\u5E94", "\u54DF", "\u4F63", "\u4F18", "\u625C", "\u56E6", "\u66F0", "\u6655", "\u5E00", "\u707D", "\u5142", "\u5328", "\u50AE", "\u5219", "\u8D3C", "\u600E", "\u5897", "\u624E", "\u635A", "\u6CBE", "\u5F20", "\u4F4B", "\u8707", "\u8D1E", "\u4E89", "\u4E4B", "\u4E2D", "\u5DDE", "\u6731", "\u6293", "\u62FD", "\u4E13", "\u5986", "\u96B9", "\u5B92", "\u5353", "\u4E72", "\u5B97", "\u90B9", "\u79DF", "\u94BB", "\u539C", "\u5C0A", "\u6628", "\u5159"];
|
|
641
|
+
var PINYINS = ["A", "AI", "AN", "ANG", "AO", "BA", "BAI", "BAN", "BANG", "BAO", "BEI", "BEN", "BENG", "BI", "BIAN", "BIAO", "BIE", "BIN", "BING", "BO", "BU", "CA", "CAI", "CAN", "CANG", "CAO", "CE", "CEN", "CENG", "CHA", "CHAI", "CHAN", "CHANG", "CHAO", "CHE", "CHEN", "CHENG", "CHI", "CHONG", "CHOU", "CHU", "CHUA", "CHUAI", "CHUAN", "CHUANG", "CHUI", "CHUN", "CHUO", "CI", "CONG", "COU", "CU", "CUAN", "CUI", "CUN", "CUO", "DA", "DAI", "DAN", "DANG", "DAO", "DE", "DEN", "DENG", "DI", "DIAN", "DIAO", "DIE", "DING", "DIU", "DONG", "DOU", "DU", "DUAN", "DUI", "DUN", "DUO", "E", "EI", "EN", "ENG", "ER", "FA", "FAN", "FANG", "FEI", "FEN", "FENG", "FIAO", "FO", "FOU", "FU", "GA", "GAI", "GAN", "GANG", "GAO", "GE", "GEI", "GEN", "GENG", "GONG", "GOU", "GU", "GUA", "GUAI", "GUAN", "GUANG", "GUI", "GUN", "GUO", "HA", "HAI", "HAN", "HANG", "HAO", "HE", "HEI", "HEN", "HENG", "HM", "HONG", "HOU", "HU", "HUA", "HUAI", "HUAN", "HUANG", "HUI", "HUN", "HUO", "JI", "JIA", "JIAN", "JIANG", "JIAO", "JIE", "JIN", "JING", "JIONG", "JIU", "JU", "JUAN", "JUE", "JUN", "KA", "KAI", "KAN", "KANG", "KAO", "KE", "KEN", "KENG", "KONG", "KOU", "KU", "KUA", "KUAI", "KUAN", "KUANG", "KUI", "KUN", "KUO", "LA", "LAI", "LAN", "LANG", "LAO", "LE", "LEI", "LENG", "LI", "LIA", "LIAN", "LIANG", "LIAO", "LIE", "LIN", "LING", "LIU", "LO", "LONG", "LOU", "LU", "LV", "LUAN", "LVE", "LUN", "LUO", "M", "MA", "MAI", "MAN", "MANG", "MAO", "ME", "MEI", "MEN", "MENG", "MI", "MIAN", "MIAO", "MIE", "MIN", "MING", "MIU", "MO", "MOU", "MU", "N", "NA", "NAI", "NAN", "NANG", "NAO", "NE", "NEI", "NEN", "NENG", "NI", "NIAN", "NIANG", "NIAO", "NIE", "NIN", "NING", "NIU", "NONG", "NOU", "NU", "NV", "NUAN", "NVE", "NUN", "NUO", "O", "OU", "PA", "PAI", "PAN", "PANG", "PAO", "PEI", "PEN", "PENG", "PI", "PIAN", "PIAO", "PIE", "PIN", "PING", "PO", "POU", "PU", "QI", "QIA", "QIAN", "QIANG", "QIAO", "QIE", "QIN", "QING", "QIONG", "QIU", "QU", "QUAN", "QUE", "QUN", "RAN", "RANG", "RAO", "RE", "REN", "RENG", "RI", "RONG", "ROU", "RU", "RUA", "RUAN", "RUI", "RUN", "RUO", "SA", "SAI", "SAN", "SANG", "SAO", "SE", "SEN", "SENG", "SHA", "SHAI", "SHAN", "SHANG", "SHAO", "SHE", "SHEN", "SHENG", "SHI", "SHOU", "SHU", "SHUA", "SHUAI", "SHUAN", "SHUANG", "SHUI", "SHUN", "SHUO", "SI", "SONG", "SOU", "SU", "SUAN", "SUI", "SUN", "SUO", "TA", "TAI", "TAN", "TANG", "TAO", "TE", "TENG", "TI", "TIAN", "TIAO", "TIE", "TING", "TONG", "TOU", "TU", "TUAN", "TUI", "TUN", "TUO", "WA", "WAI", "WAN", "WANG", "WEI", "WEN", "WENG", "WO", "WU", "XI", "XIA", "XIAN", "XIANG", "XIAO", "XIE", "XIN", "XING", "XIONG", "XIU", "XU", "XUAN", "XUE", "XUN", "YA", "YAN", "YANG", "YAO", "YE", "YI", "YIN", "YING", "YO", "YONG", "YOU", "YU", "YUAN", "YUE", "YUN", "ZA", "ZAI", "ZAN", "ZANG", "ZAO", "ZE", "ZEI", "ZEN", "ZENG", "ZHA", "ZHAI", "ZHAN", "ZHANG", "ZHAO", "ZHE", "ZHEN", "ZHENG", "ZHI", "ZHONG", "ZHOU", "ZHU", "ZHUA", "ZHUAI", "ZHUAN", "ZHUANG", "ZHUI", "ZHUN", "ZHUO", "ZI", "ZONG", "ZOU", "ZU", "ZUAN", "ZUI", "ZUN", "ZUO", ""];
|
|
642
|
+
var EXCEPTIONS = {
|
|
643
|
+
"\u66FE": "ZENG",
|
|
644
|
+
// CENG 曾
|
|
645
|
+
"\u6C88": "SHEN",
|
|
646
|
+
// CHEN 沈
|
|
647
|
+
"\u55F2": "DIA",
|
|
648
|
+
// DIE 嗲
|
|
649
|
+
"\u78A1": "ZHOU",
|
|
650
|
+
// DU 碡
|
|
651
|
+
"\u8052": "GUO",
|
|
652
|
+
// GUA 聒
|
|
653
|
+
"\u7094": "QUE",
|
|
654
|
+
// GUI 炔
|
|
655
|
+
"\u86B5": "KE",
|
|
656
|
+
// HE 蚵
|
|
657
|
+
"\u7809": "HUA",
|
|
658
|
+
// HUO 砉
|
|
659
|
+
"\u5B24": "MO",
|
|
660
|
+
// MA 嬤
|
|
661
|
+
"\u5B37": "MO",
|
|
662
|
+
// MA 嬷
|
|
663
|
+
"\u8E52": "PAN",
|
|
664
|
+
// MAN 蹒
|
|
665
|
+
"\u8E4A": "XI",
|
|
666
|
+
// QI 蹊
|
|
667
|
+
"\u4E2C": "PAN",
|
|
668
|
+
// QIANG 丬
|
|
669
|
+
"\u9730": "XIAN",
|
|
670
|
+
// SAN 霰
|
|
671
|
+
"\u8398": "XIN",
|
|
672
|
+
// SHEN 莘
|
|
673
|
+
"\u8C49": "CHI",
|
|
674
|
+
// SHI 豉
|
|
675
|
+
"\u9967": "XING",
|
|
676
|
+
// TANG 饧
|
|
677
|
+
"\u7B60": "JUN",
|
|
678
|
+
// YUN 筠
|
|
679
|
+
"\u957F": "CHANG",
|
|
680
|
+
// ZHANG 长
|
|
681
|
+
"\u5E27": "ZHEN",
|
|
682
|
+
// ZHENG 帧
|
|
683
|
+
"\u5CD9": "SHI",
|
|
684
|
+
// ZHI 峙
|
|
685
|
+
"\u90CD": "NA",
|
|
686
|
+
"\u828E": "XIONG",
|
|
687
|
+
"\u8C01": "SHUI"
|
|
688
|
+
};
|
|
689
|
+
module.exports = {
|
|
690
|
+
PINYINS,
|
|
691
|
+
UNIHANS,
|
|
692
|
+
EXCEPTIONS
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// ../../../node_modules/tiny-pinyin/dist/core.js
|
|
698
|
+
var require_core = __commonJS({
|
|
699
|
+
"../../../node_modules/tiny-pinyin/dist/core.js"(exports, module) {
|
|
700
|
+
"use strict";
|
|
701
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
|
|
702
|
+
return typeof obj;
|
|
703
|
+
} : function(obj) {
|
|
704
|
+
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
705
|
+
};
|
|
706
|
+
var DICT = require_dict();
|
|
707
|
+
var FIRST_PINYIN_UNIHAN = "\u963F";
|
|
708
|
+
var LAST_PINYIN_UNIHAN = "\u9FFF";
|
|
709
|
+
var LATIN = 1;
|
|
710
|
+
var PINYIN = 2;
|
|
711
|
+
var UNKNOWN = 3;
|
|
712
|
+
var supported = null;
|
|
713
|
+
var COLLATOR = void 0;
|
|
714
|
+
function patchDict(patchers) {
|
|
715
|
+
if (!patchers) return;
|
|
716
|
+
if (typeof patchers === "function") {
|
|
717
|
+
patchers = [patchers];
|
|
718
|
+
}
|
|
719
|
+
if (patchers.forEach) {
|
|
720
|
+
patchers.forEach(function(p) {
|
|
721
|
+
typeof p === "function" && p(DICT);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function isSupported(force) {
|
|
726
|
+
if (!force && supported !== null) {
|
|
727
|
+
return supported;
|
|
728
|
+
}
|
|
729
|
+
if ((typeof Intl === "undefined" ? "undefined" : _typeof(Intl)) === "object" && Intl.Collator) {
|
|
730
|
+
COLLATOR = new Intl.Collator(["zh-Hans-CN", "zh-CN"]);
|
|
731
|
+
supported = Intl.Collator.supportedLocalesOf(["zh-CN"]).length === 1;
|
|
732
|
+
} else {
|
|
733
|
+
supported = false;
|
|
734
|
+
}
|
|
735
|
+
return supported;
|
|
736
|
+
}
|
|
737
|
+
function genToken(ch) {
|
|
738
|
+
var UNIHANS = DICT.UNIHANS;
|
|
739
|
+
var PINYINS = DICT.PINYINS;
|
|
740
|
+
var EXCEPTIONS = DICT.EXCEPTIONS;
|
|
741
|
+
var token = {
|
|
742
|
+
source: ch
|
|
743
|
+
// First check EXCEPTIONS map, then search with UNIHANS table.
|
|
744
|
+
};
|
|
745
|
+
if (ch in EXCEPTIONS) {
|
|
746
|
+
token.type = PINYIN;
|
|
747
|
+
token.target = EXCEPTIONS[ch];
|
|
748
|
+
return token;
|
|
749
|
+
}
|
|
750
|
+
var offset = -1;
|
|
751
|
+
var cmp = void 0;
|
|
752
|
+
if (ch.charCodeAt(0) < 256) {
|
|
753
|
+
token.type = LATIN;
|
|
754
|
+
token.target = ch;
|
|
755
|
+
return token;
|
|
756
|
+
} else {
|
|
757
|
+
cmp = COLLATOR.compare(ch, FIRST_PINYIN_UNIHAN);
|
|
758
|
+
if (cmp < 0) {
|
|
759
|
+
token.type = UNKNOWN;
|
|
760
|
+
token.target = ch;
|
|
761
|
+
return token;
|
|
762
|
+
} else if (cmp === 0) {
|
|
763
|
+
token.type = PINYIN;
|
|
764
|
+
offset = 0;
|
|
765
|
+
} else {
|
|
766
|
+
cmp = COLLATOR.compare(ch, LAST_PINYIN_UNIHAN);
|
|
767
|
+
if (cmp > 0) {
|
|
768
|
+
token.type = UNKNOWN;
|
|
769
|
+
token.target = ch;
|
|
770
|
+
return token;
|
|
771
|
+
} else if (cmp === 0) {
|
|
772
|
+
token.type = PINYIN;
|
|
773
|
+
offset = UNIHANS.length - 1;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
token.type = PINYIN;
|
|
778
|
+
if (offset < 0) {
|
|
779
|
+
var begin = 0;
|
|
780
|
+
var end = UNIHANS.length - 1;
|
|
781
|
+
while (begin <= end) {
|
|
782
|
+
offset = ~~((begin + end) / 2);
|
|
783
|
+
var unihan = UNIHANS[offset];
|
|
784
|
+
cmp = COLLATOR.compare(ch, unihan);
|
|
785
|
+
if (cmp === 0) {
|
|
786
|
+
break;
|
|
787
|
+
} else if (cmp > 0) {
|
|
788
|
+
begin = offset + 1;
|
|
789
|
+
} else {
|
|
790
|
+
end = offset - 1;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (cmp < 0) {
|
|
795
|
+
offset--;
|
|
796
|
+
}
|
|
797
|
+
token.target = PINYINS[offset];
|
|
798
|
+
if (!token.target) {
|
|
799
|
+
token.type = UNKNOWN;
|
|
800
|
+
token.target = token.source;
|
|
801
|
+
}
|
|
802
|
+
return token;
|
|
803
|
+
}
|
|
804
|
+
function parse(str) {
|
|
805
|
+
if (typeof str !== "string") {
|
|
806
|
+
throw new Error("argument should be string.");
|
|
807
|
+
}
|
|
808
|
+
if (!isSupported()) {
|
|
809
|
+
throw new Error("not support Intl or zh-CN language.");
|
|
810
|
+
}
|
|
811
|
+
return str.split("").map(function(v) {
|
|
812
|
+
return genToken(v);
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
module.exports = {
|
|
816
|
+
isSupported,
|
|
817
|
+
parse,
|
|
818
|
+
patchDict,
|
|
819
|
+
genToken,
|
|
820
|
+
// inner usage
|
|
821
|
+
convertToPinyin: function convertToPinyin(str, separator, lowerCase) {
|
|
822
|
+
return parse(str).map(function(v) {
|
|
823
|
+
if (lowerCase && v.type === PINYIN) {
|
|
824
|
+
return v.target.toLowerCase();
|
|
825
|
+
}
|
|
826
|
+
return v.target;
|
|
827
|
+
}).join(separator || "");
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// ../../../node_modules/tiny-pinyin/dist/patchers/56l.js
|
|
834
|
+
var require_l = __commonJS({
|
|
835
|
+
"../../../node_modules/tiny-pinyin/dist/patchers/56l.js"(exports, module) {
|
|
836
|
+
"use strict";
|
|
837
|
+
exports = module.exports = function patcher(DICT) {
|
|
838
|
+
DICT.EXCEPTIONS = {
|
|
839
|
+
"\u55F2": "DIA",
|
|
840
|
+
// DIE 嗲
|
|
841
|
+
"\u78A1": "ZHOU",
|
|
842
|
+
// DU 碡
|
|
843
|
+
"\u8052": "GUO",
|
|
844
|
+
// GUA 聒
|
|
845
|
+
"\u7094": "QUE",
|
|
846
|
+
// GUI 炔
|
|
847
|
+
"\u86B5": "KE",
|
|
848
|
+
// HE 蚵
|
|
849
|
+
"\u7809": "HUA",
|
|
850
|
+
// HUO 砉
|
|
851
|
+
"\u5B37": "MO",
|
|
852
|
+
// MA 嬷 新增
|
|
853
|
+
"\u8E4A": "XI",
|
|
854
|
+
// QI 蹊
|
|
855
|
+
"\u4E2C": "PAN",
|
|
856
|
+
// QIANG 丬
|
|
857
|
+
"\u9730": "XIAN",
|
|
858
|
+
// SAN 霰
|
|
859
|
+
"\u8C49": "CHI",
|
|
860
|
+
// SHI 豉
|
|
861
|
+
"\u9967": "XING",
|
|
862
|
+
// TANG 饧
|
|
863
|
+
"\u5E27": "ZHEN",
|
|
864
|
+
// ZHENG 帧
|
|
865
|
+
"\u828E": "XIONG",
|
|
866
|
+
// 芎
|
|
867
|
+
"\u8C01": "SHUI",
|
|
868
|
+
// 谁
|
|
869
|
+
"\u94B6": "KE"
|
|
870
|
+
// 钶
|
|
871
|
+
// Update UNIHANS dict.
|
|
872
|
+
};
|
|
873
|
+
DICT.UNIHANS[91] = "\u4F15";
|
|
874
|
+
DICT.UNIHANS[347] = "\u4EDA";
|
|
875
|
+
DICT.UNIHANS[393] = "\u8BCC";
|
|
876
|
+
DICT.UNIHANS[39] = "\u5A64";
|
|
877
|
+
DICT.UNIHANS[50] = "\u8160";
|
|
878
|
+
DICT.UNIHANS[369] = "\u6538";
|
|
879
|
+
DICT.UNIHANS[123] = "\u4E6F";
|
|
880
|
+
DICT.UNIHANS[171] = "\u5215";
|
|
881
|
+
DICT.UNIHANS[102] = "\u4F5D";
|
|
882
|
+
DICT.UNIHANS[126] = "\u72BF";
|
|
883
|
+
DICT.UNIHANS[176] = "\u5217";
|
|
884
|
+
DICT.UNIHANS[178] = "\u5222";
|
|
885
|
+
DICT.UNIHANS[252] = "\u5A1D";
|
|
886
|
+
DICT.UNIHANS[330] = "\u5078";
|
|
887
|
+
};
|
|
888
|
+
exports.shouldPatch = function shouldPatch(toToken) {
|
|
889
|
+
if (typeof toToken !== "function") return false;
|
|
890
|
+
if (toToken("\u4F15").target === "FOU" && toToken("\u4EDA").target === "XIA" && toToken("\u8BCC").target === "ZHONG" && toToken("\u5A64").target === "CHONG" && toToken("\u8160").target === "CONG" && toToken("\u6538").target === "YONG" && toToken("\u4E6F").target === "HOU" && toToken("\u5215").target === "LENG" && toToken("\u4F5D").target === "GONG" && toToken("\u72BF").target === "HUAI" && toToken("\u5217").target === "LIAO" && toToken("\u5222").target === "LIN" && toToken("\u94B6").target === "E") {
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
return false;
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
// ../../../node_modules/tiny-pinyin/dist/index.js
|
|
899
|
+
var require_dist2 = __commonJS({
|
|
900
|
+
"../../../node_modules/tiny-pinyin/dist/index.js"(exports, module) {
|
|
901
|
+
"use strict";
|
|
902
|
+
var pinyin = require_core();
|
|
903
|
+
var patcher56L = require_l();
|
|
904
|
+
if (pinyin.isSupported() && patcher56L.shouldPatch(pinyin.genToken)) {
|
|
905
|
+
pinyin.patchDict(patcher56L);
|
|
906
|
+
}
|
|
907
|
+
module.exports = pinyin;
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
// src/fetcher/api-client.ts
|
|
912
|
+
var import_http_client = __toESM(require_dist(), 1);
|
|
913
|
+
var clientInstance = null;
|
|
914
|
+
function getHttpClient() {
|
|
915
|
+
if (!clientInstance) {
|
|
916
|
+
clientInstance = new import_http_client.HttpClient({
|
|
917
|
+
timeout: 3e4,
|
|
918
|
+
platform: { enabled: true }
|
|
919
|
+
});
|
|
920
|
+
const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
|
|
921
|
+
if (canaryEnv) {
|
|
922
|
+
clientInstance.interceptors.request.use((req) => {
|
|
923
|
+
req.headers["x-tt-env"] = canaryEnv;
|
|
924
|
+
return req;
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return clientInstance;
|
|
929
|
+
}
|
|
930
|
+
function resolveEnvOptions(env = process.env) {
|
|
931
|
+
const appId = env.app_id;
|
|
932
|
+
const workspace = env.suda_workspace_id;
|
|
933
|
+
if (!appId) {
|
|
934
|
+
throw new Error("[db-schema-sync] Error: app_id environment variable is required. Set it in your .env file or shell environment.");
|
|
935
|
+
}
|
|
936
|
+
if (!workspace) {
|
|
937
|
+
throw new Error("[db-schema-sync] Error: suda_workspace_id environment variable is required. Set it in your .env file or shell environment.");
|
|
938
|
+
}
|
|
939
|
+
return {
|
|
940
|
+
appId,
|
|
941
|
+
workspace,
|
|
942
|
+
dbBranch: env.FORCE_DB_BRANCH || "main",
|
|
943
|
+
timeoutMs: env.SCHEMA_API_TIMEOUT_MS ? Number(env.SCHEMA_API_TIMEOUT_MS) : 3e4
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
async function fetchListTableView(options) {
|
|
947
|
+
const { appId, workspace, dbBranch = "main", timeoutMs = 3e4 } = options;
|
|
948
|
+
const client = getHttpClient();
|
|
949
|
+
const controller = new AbortController();
|
|
950
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
951
|
+
try {
|
|
952
|
+
const ttEnv = process.env.X_TT_ENV || process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV || "";
|
|
953
|
+
const webUser = process.env.X_LARKGW_SUDA_WEBUSER || "";
|
|
954
|
+
const headers = {
|
|
955
|
+
"x-supaas-bizsource": "miaoda"
|
|
956
|
+
};
|
|
957
|
+
if (ttEnv) headers["x-tt-env"] = ttEnv;
|
|
958
|
+
if (webUser) headers["X-Larkgw-Suda-Webuser"] = webUser;
|
|
959
|
+
console.log(`[db-schema-sync] GET /v1/app/${appId}/dataloom/schema?dbBranch=${dbBranch}&workspace=${workspace}`);
|
|
960
|
+
if (ttEnv) console.log(`[db-schema-sync] x-tt-env: ${ttEnv}`);
|
|
961
|
+
const response = await client.get(
|
|
962
|
+
`/v1/app/${appId}/dataloom/schema`,
|
|
963
|
+
{
|
|
964
|
+
params: { dbBranch },
|
|
965
|
+
headers,
|
|
966
|
+
signal: controller.signal
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
if (!response.ok) {
|
|
970
|
+
let body = "";
|
|
971
|
+
try {
|
|
972
|
+
body = await response.text();
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
throw new Error(`listTableView API failed: ${response.status} ${response.statusText}
|
|
976
|
+
URL: ${response.url}
|
|
977
|
+
Body: ${body.slice(0, 500)}`);
|
|
978
|
+
}
|
|
979
|
+
const json = await response.json();
|
|
980
|
+
const data = json.data;
|
|
981
|
+
return data?.data ?? data?.schema ?? data;
|
|
982
|
+
} catch (err) {
|
|
983
|
+
if (err?.response) {
|
|
984
|
+
const headers = err.response.headers;
|
|
985
|
+
const logId = headers?.get?.("x-tt-logid") ?? headers?.["x-tt-logid"] ?? "";
|
|
986
|
+
let body = "";
|
|
987
|
+
try {
|
|
988
|
+
body = typeof err.response.text === "function" ? await err.response.text() : JSON.stringify(err.response.data ?? err.response.body ?? "");
|
|
989
|
+
} catch {
|
|
990
|
+
}
|
|
991
|
+
throw new Error(`listTableView API failed: ${err.message}
|
|
992
|
+
LogID: ${logId}
|
|
993
|
+
Body: ${body.slice(0, 500)}`);
|
|
994
|
+
}
|
|
995
|
+
throw err;
|
|
996
|
+
} finally {
|
|
997
|
+
clearTimeout(timer);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// src/fetcher/identifier.ts
|
|
1002
|
+
var import_tiny_pinyin = __toESM(require_dist2(), 1);
|
|
1003
|
+
function toAsciiName(name) {
|
|
1004
|
+
if (!/[^\x00-\x7F]/.test(name)) {
|
|
1005
|
+
return name;
|
|
1006
|
+
}
|
|
1007
|
+
try {
|
|
1008
|
+
const result = [];
|
|
1009
|
+
for (const char of name) {
|
|
1010
|
+
if (/[^\x00-\x7F]/.test(char) && import_tiny_pinyin.default.isSupported()) {
|
|
1011
|
+
result.push(import_tiny_pinyin.default.convertToPinyin(char, "", true));
|
|
1012
|
+
} else {
|
|
1013
|
+
result.push(char);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
return result.join("_") || name;
|
|
1017
|
+
} catch {
|
|
1018
|
+
return name;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function toCamelCase(str) {
|
|
1022
|
+
const words = str.split(/[_\-\s]+/).filter(Boolean);
|
|
1023
|
+
if (words.length === 0) return "";
|
|
1024
|
+
return words.map((word, index) => {
|
|
1025
|
+
if (index === 0) return word.toLowerCase();
|
|
1026
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
1027
|
+
}).join("");
|
|
1028
|
+
}
|
|
1029
|
+
function sanitizeIdentifier(name) {
|
|
1030
|
+
const asciiName = toAsciiName(name);
|
|
1031
|
+
let sanitized = asciiName.replace(/[^A-Za-z0-9_]/g, "_");
|
|
1032
|
+
sanitized = sanitized.replace(/_+/g, "_");
|
|
1033
|
+
sanitized = sanitized.replace(/^_|_$/g, "");
|
|
1034
|
+
sanitized = toCamelCase(sanitized);
|
|
1035
|
+
if (!sanitized) sanitized = "table";
|
|
1036
|
+
if (!/^[A-Za-z_]/.test(sanitized)) sanitized = `_${sanitized}`;
|
|
1037
|
+
return sanitized;
|
|
1038
|
+
}
|
|
1039
|
+
function sanitizePropertyName(name) {
|
|
1040
|
+
const leadingUnderscore = name.startsWith("_") ? "_" : "";
|
|
1041
|
+
const stripped = leadingUnderscore ? name.slice(1) : name;
|
|
1042
|
+
const base = sanitizeIdentifier(stripped);
|
|
1043
|
+
return `${leadingUnderscore}${base}`;
|
|
1044
|
+
}
|
|
1045
|
+
function getUniqueIdentifier(name, usedIdentifiers) {
|
|
1046
|
+
const base = sanitizeIdentifier(name);
|
|
1047
|
+
if (!usedIdentifiers.has(base)) {
|
|
1048
|
+
usedIdentifiers.add(base);
|
|
1049
|
+
return base;
|
|
1050
|
+
}
|
|
1051
|
+
let suffix = 2;
|
|
1052
|
+
while (usedIdentifiers.has(`${base}${suffix}`)) suffix++;
|
|
1053
|
+
const unique = `${base}${suffix}`;
|
|
1054
|
+
usedIdentifiers.add(unique);
|
|
1055
|
+
return unique;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/fetcher/normalize.ts
|
|
1059
|
+
var SYSTEM_FIELD_NAMES = /* @__PURE__ */ new Set(["_created_at", "_created_by", "_updated_at", "_updated_by"]);
|
|
1060
|
+
function normalizeField(field, systemFieldNamesToRemove) {
|
|
1061
|
+
if (systemFieldNamesToRemove.has(field.fieldName)) {
|
|
1062
|
+
return null;
|
|
1063
|
+
}
|
|
1064
|
+
return {
|
|
1065
|
+
fieldName: field.fieldName,
|
|
1066
|
+
type: field.type,
|
|
1067
|
+
isPrimary: field.isPrimary,
|
|
1068
|
+
isNullable: field.isNullable,
|
|
1069
|
+
isUnique: field.isUnique,
|
|
1070
|
+
isArray: field.isArray,
|
|
1071
|
+
isEnum: field.isEnum,
|
|
1072
|
+
isSystem: SYSTEM_FIELD_NAMES.has(field.fieldName),
|
|
1073
|
+
defaultValue: field.defaultValue ?? void 0,
|
|
1074
|
+
comment: field.comment || void 0,
|
|
1075
|
+
extraInfo: field.extraInfo
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function normalizeRelationship(rel) {
|
|
1079
|
+
return {
|
|
1080
|
+
fieldNames: rel.fieldName,
|
|
1081
|
+
targetTableName: rel.fkTableName,
|
|
1082
|
+
targetFieldNames: rel.fkFieldName,
|
|
1083
|
+
updatePolicy: rel.updateMethod,
|
|
1084
|
+
removePolicy: rel.removeMethod
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function normalizeIndex(index) {
|
|
1088
|
+
return {
|
|
1089
|
+
indexName: index.indexName,
|
|
1090
|
+
indexColumns: index.indexColumns ?? [],
|
|
1091
|
+
indexType: index.indexType,
|
|
1092
|
+
indexCreationType: index.indexCreationType,
|
|
1093
|
+
indexDef: index.indexDef
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function resolveSystemFieldConflicts(fields) {
|
|
1097
|
+
const businessJsNames = /* @__PURE__ */ new Set();
|
|
1098
|
+
for (const field of fields) {
|
|
1099
|
+
if (!SYSTEM_FIELD_NAMES.has(field.fieldName)) {
|
|
1100
|
+
businessJsNames.add(sanitizeIdentifier(field.fieldName));
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
1104
|
+
for (const field of fields) {
|
|
1105
|
+
if (SYSTEM_FIELD_NAMES.has(field.fieldName)) {
|
|
1106
|
+
const jsName = sanitizeIdentifier(field.fieldName);
|
|
1107
|
+
if (businessJsNames.has(jsName)) {
|
|
1108
|
+
toRemove.add(field.fieldName);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return toRemove;
|
|
1113
|
+
}
|
|
1114
|
+
function normalizeTable(table, usedIdentifiers) {
|
|
1115
|
+
const identifier = getUniqueIdentifier(table.tableName, usedIdentifiers);
|
|
1116
|
+
const systemFieldsToRemove = resolveSystemFieldConflicts(table.fields ?? []);
|
|
1117
|
+
const fields = [];
|
|
1118
|
+
for (const field of table.fields ?? []) {
|
|
1119
|
+
const normalized = normalizeField(field, systemFieldsToRemove);
|
|
1120
|
+
if (normalized !== null) {
|
|
1121
|
+
fields.push(normalized);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const relationships = (table.relationships ?? []).map(normalizeRelationship);
|
|
1125
|
+
const indexes = (table.indexes ?? []).map(normalizeIndex);
|
|
1126
|
+
return {
|
|
1127
|
+
tableName: table.tableName,
|
|
1128
|
+
identifier,
|
|
1129
|
+
comment: table.comment || void 0,
|
|
1130
|
+
fields,
|
|
1131
|
+
relationships,
|
|
1132
|
+
indexes
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
function normalizeView(view, usedIdentifiers) {
|
|
1136
|
+
const identifier = getUniqueIdentifier(view.tableName, usedIdentifiers);
|
|
1137
|
+
const systemFieldsToRemove = resolveSystemFieldConflicts(view.fields ?? []);
|
|
1138
|
+
const fields = [];
|
|
1139
|
+
for (const field of view.fields ?? []) {
|
|
1140
|
+
const normalized = normalizeField(field, systemFieldsToRemove);
|
|
1141
|
+
if (normalized !== null) {
|
|
1142
|
+
fields.push(normalized);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return {
|
|
1146
|
+
viewName: view.tableName,
|
|
1147
|
+
identifier,
|
|
1148
|
+
comment: view.comment || void 0,
|
|
1149
|
+
fields,
|
|
1150
|
+
viewDef: view.tableDef || void 0
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
function extractSyncedTableMap(tables) {
|
|
1154
|
+
const map = /* @__PURE__ */ new Map();
|
|
1155
|
+
for (const table of tables) {
|
|
1156
|
+
if (table.bitableSyncTask?.fieldApiNameList) {
|
|
1157
|
+
map.set(table.tableName, new Set(table.bitableSyncTask.fieldApiNameList));
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
return map;
|
|
1161
|
+
}
|
|
1162
|
+
function normalizeApiResponse(response) {
|
|
1163
|
+
const usedTableIdentifiers = /* @__PURE__ */ new Set();
|
|
1164
|
+
const usedViewIdentifiers = /* @__PURE__ */ new Set();
|
|
1165
|
+
const usedMViewIdentifiers = /* @__PURE__ */ new Set();
|
|
1166
|
+
const usedEnumIdentifiers = /* @__PURE__ */ new Set();
|
|
1167
|
+
const usedSeqIdentifiers = /* @__PURE__ */ new Set();
|
|
1168
|
+
const tableItems = response.tables?.data ?? response.table?.data ?? [];
|
|
1169
|
+
const viewItems = response.views?.data ?? response.view?.data ?? [];
|
|
1170
|
+
const mViewItems = response.materializedViews?.data ?? response.materializedView?.data ?? [];
|
|
1171
|
+
const tables = tableItems.map((t) => normalizeTable(t, usedTableIdentifiers));
|
|
1172
|
+
const views = viewItems.map((v) => normalizeView(v, usedViewIdentifiers));
|
|
1173
|
+
const materializedViews = mViewItems.map((v) => normalizeView(v, usedMViewIdentifiers));
|
|
1174
|
+
const enums = (response.enums ?? []).map((e) => {
|
|
1175
|
+
let values;
|
|
1176
|
+
if (e.enumValues?.length) {
|
|
1177
|
+
values = e.enumValues.map((v) => v.enumValueName);
|
|
1178
|
+
} else if (e.values?.length) {
|
|
1179
|
+
values = e.values;
|
|
1180
|
+
} else {
|
|
1181
|
+
values = [];
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
enumName: e.enumName,
|
|
1185
|
+
identifier: getUniqueIdentifier(e.enumName, usedEnumIdentifiers),
|
|
1186
|
+
values
|
|
1187
|
+
};
|
|
1188
|
+
});
|
|
1189
|
+
const sequences = (response.sequences ?? []).map((s) => ({
|
|
1190
|
+
sequenceName: s.sequenceName,
|
|
1191
|
+
identifier: getUniqueIdentifier(s.sequenceName, usedSeqIdentifiers)
|
|
1192
|
+
}));
|
|
1193
|
+
const allRawTables = [...tableItems, ...viewItems, ...mViewItems];
|
|
1194
|
+
const syncedTableMap = extractSyncedTableMap(allRawTables);
|
|
1195
|
+
return {
|
|
1196
|
+
tables,
|
|
1197
|
+
views,
|
|
1198
|
+
materializedViews,
|
|
1199
|
+
enums,
|
|
1200
|
+
sequences,
|
|
1201
|
+
syncedTableMap
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// src/fetcher/index.ts
|
|
1206
|
+
async function fetchSchemaData(options) {
|
|
1207
|
+
const raw = await fetchListTableView(options);
|
|
1208
|
+
return normalizeApiResponse(raw);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// src/generator/type-mapper-registry.ts
|
|
1212
|
+
var TypeMapperRegistry = class {
|
|
1213
|
+
mappers = [];
|
|
1214
|
+
register(mapper) {
|
|
1215
|
+
this.mappers.push(mapper);
|
|
1216
|
+
}
|
|
1217
|
+
resolve(field) {
|
|
1218
|
+
for (const mapper of this.mappers) {
|
|
1219
|
+
if (mapper.match(field)) return mapper;
|
|
1220
|
+
}
|
|
1221
|
+
throw new Error(`No mapper found for type: ${field.type}`);
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
// src/generator/utils.ts
|
|
1226
|
+
function escapeDoubleQuote(s) {
|
|
1227
|
+
return s.replace(/"/g, '\\"');
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// src/generator/mappers/primitives.ts
|
|
1231
|
+
var PG_CORE = "drizzle-orm/pg-core";
|
|
1232
|
+
var primitiveMappers = [
|
|
1233
|
+
// uuid
|
|
1234
|
+
{
|
|
1235
|
+
name: "uuid",
|
|
1236
|
+
match: (f) => f.type === "uuid",
|
|
1237
|
+
generate: (f) => `uuid("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1238
|
+
imports: () => [{ name: "uuid", from: PG_CORE }]
|
|
1239
|
+
},
|
|
1240
|
+
// varchar
|
|
1241
|
+
{
|
|
1242
|
+
name: "varchar",
|
|
1243
|
+
match: (f) => f.type === "varchar",
|
|
1244
|
+
generate: (f) => `varchar("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1245
|
+
imports: () => [{ name: "varchar", from: PG_CORE }]
|
|
1246
|
+
},
|
|
1247
|
+
// text
|
|
1248
|
+
{
|
|
1249
|
+
name: "text",
|
|
1250
|
+
match: (f) => f.type === "text",
|
|
1251
|
+
generate: (f) => `text("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1252
|
+
imports: () => [{ name: "text", from: PG_CORE }]
|
|
1253
|
+
},
|
|
1254
|
+
// integer (int2, int4, integer)
|
|
1255
|
+
{
|
|
1256
|
+
name: "integer",
|
|
1257
|
+
match: (f) => ["int2", "int4", "integer"].includes(f.type),
|
|
1258
|
+
generate: (f) => `integer("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1259
|
+
imports: () => [{ name: "integer", from: PG_CORE }]
|
|
1260
|
+
},
|
|
1261
|
+
// bigint (int8, bigint)
|
|
1262
|
+
{
|
|
1263
|
+
name: "bigint",
|
|
1264
|
+
match: (f) => ["int8", "bigint"].includes(f.type),
|
|
1265
|
+
generate: (f) => `bigint("${escapeDoubleQuote(f.fieldName)}", { mode: 'number' })`,
|
|
1266
|
+
imports: () => [{ name: "bigint", from: PG_CORE }]
|
|
1267
|
+
},
|
|
1268
|
+
// real (float4, real)
|
|
1269
|
+
{
|
|
1270
|
+
name: "real",
|
|
1271
|
+
match: (f) => ["float4", "real"].includes(f.type),
|
|
1272
|
+
generate: (f) => `real("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1273
|
+
imports: () => [{ name: "real", from: PG_CORE }]
|
|
1274
|
+
},
|
|
1275
|
+
// doublePrecision (float8, double precision)
|
|
1276
|
+
{
|
|
1277
|
+
name: "doublePrecision",
|
|
1278
|
+
match: (f) => ["float8", "double precision"].includes(f.type),
|
|
1279
|
+
generate: (f) => `doublePrecision("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1280
|
+
imports: () => [{ name: "doublePrecision", from: PG_CORE }]
|
|
1281
|
+
},
|
|
1282
|
+
// numeric (numeric, decimal)
|
|
1283
|
+
{
|
|
1284
|
+
name: "numeric",
|
|
1285
|
+
match: (f) => ["numeric", "decimal"].includes(f.type),
|
|
1286
|
+
generate: (f) => `numeric("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1287
|
+
imports: () => [{ name: "numeric", from: PG_CORE }]
|
|
1288
|
+
},
|
|
1289
|
+
// boolean (bool, boolean)
|
|
1290
|
+
{
|
|
1291
|
+
name: "boolean",
|
|
1292
|
+
match: (f) => ["bool", "boolean"].includes(f.type),
|
|
1293
|
+
generate: (f) => `boolean("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1294
|
+
imports: () => [{ name: "boolean", from: PG_CORE }]
|
|
1295
|
+
},
|
|
1296
|
+
// json
|
|
1297
|
+
{
|
|
1298
|
+
name: "json",
|
|
1299
|
+
match: (f) => f.type === "json",
|
|
1300
|
+
generate: (f) => `json("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1301
|
+
imports: () => [{ name: "json", from: PG_CORE }]
|
|
1302
|
+
},
|
|
1303
|
+
// jsonb
|
|
1304
|
+
{
|
|
1305
|
+
name: "jsonb",
|
|
1306
|
+
match: (f) => f.type === "jsonb",
|
|
1307
|
+
generate: (f) => `jsonb("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1308
|
+
imports: () => [{ name: "jsonb", from: PG_CORE }]
|
|
1309
|
+
},
|
|
1310
|
+
// date
|
|
1311
|
+
{
|
|
1312
|
+
name: "date",
|
|
1313
|
+
match: (f) => f.type === "date",
|
|
1314
|
+
generate: (f) => `date("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1315
|
+
imports: () => [{ name: "date", from: PG_CORE }]
|
|
1316
|
+
},
|
|
1317
|
+
// time
|
|
1318
|
+
{
|
|
1319
|
+
name: "time",
|
|
1320
|
+
match: (f) => f.type === "time",
|
|
1321
|
+
generate: (f) => `time("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1322
|
+
imports: () => [{ name: "time", from: PG_CORE }]
|
|
1323
|
+
},
|
|
1324
|
+
// serial
|
|
1325
|
+
{
|
|
1326
|
+
name: "serial",
|
|
1327
|
+
match: (f) => f.type === "serial",
|
|
1328
|
+
generate: (f) => `serial("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1329
|
+
imports: () => [{ name: "serial", from: PG_CORE }]
|
|
1330
|
+
}
|
|
1331
|
+
];
|
|
1332
|
+
|
|
1333
|
+
// src/generator/mappers/custom-types.ts
|
|
1334
|
+
var TIMESTAMP_TYPES = ["timestamptz", "timestamp"];
|
|
1335
|
+
var CUSTOM_TYPE_MAP = {
|
|
1336
|
+
user_profile: "userProfile",
|
|
1337
|
+
file_attachment: "fileAttachment"
|
|
1338
|
+
};
|
|
1339
|
+
var CUSTOM_ARRAY_TYPE_MAP = {
|
|
1340
|
+
user_profile: "userProfileArray",
|
|
1341
|
+
file_attachment: "fileAttachmentArray"
|
|
1342
|
+
};
|
|
1343
|
+
var customTypeMappers = [
|
|
1344
|
+
// timestamptz/timestamp array — must come before non-array check
|
|
1345
|
+
{
|
|
1346
|
+
name: "customTimestamptz-array",
|
|
1347
|
+
match: (f) => TIMESTAMP_TYPES.includes(f.type) && f.isArray,
|
|
1348
|
+
generate: (f) => {
|
|
1349
|
+
const precision = f.extraInfo?.timestamp_precision;
|
|
1350
|
+
if (precision !== void 0) {
|
|
1351
|
+
return `customTimestamptz("${escapeDoubleQuote(f.fieldName)}", { precision: ${precision} }).array()`;
|
|
1352
|
+
}
|
|
1353
|
+
return `customTimestamptz("${escapeDoubleQuote(f.fieldName)}").array()`;
|
|
1354
|
+
},
|
|
1355
|
+
imports: () => []
|
|
1356
|
+
},
|
|
1357
|
+
// timestamptz/timestamp (non-array)
|
|
1358
|
+
{
|
|
1359
|
+
name: "customTimestamptz",
|
|
1360
|
+
match: (f) => TIMESTAMP_TYPES.includes(f.type) && !f.isArray,
|
|
1361
|
+
generate: (f) => {
|
|
1362
|
+
const precision = f.extraInfo?.timestamp_precision;
|
|
1363
|
+
if (precision !== void 0) {
|
|
1364
|
+
return `customTimestamptz("${escapeDoubleQuote(f.fieldName)}", { precision: ${precision} })`;
|
|
1365
|
+
}
|
|
1366
|
+
return `customTimestamptz("${escapeDoubleQuote(f.fieldName)}")`;
|
|
1367
|
+
},
|
|
1368
|
+
imports: () => []
|
|
1369
|
+
},
|
|
1370
|
+
// user_profile / file_attachment array — must come before non-array
|
|
1371
|
+
{
|
|
1372
|
+
name: "custom-type-array",
|
|
1373
|
+
match: (f) => f.isArray && f.type in CUSTOM_ARRAY_TYPE_MAP,
|
|
1374
|
+
generate: (f) => `${CUSTOM_ARRAY_TYPE_MAP[f.type]}("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1375
|
+
imports: () => []
|
|
1376
|
+
// provided by inline-types enhancer
|
|
1377
|
+
},
|
|
1378
|
+
// user_profile / file_attachment (non-array)
|
|
1379
|
+
{
|
|
1380
|
+
name: "custom-type",
|
|
1381
|
+
match: (f) => !f.isArray && f.type in CUSTOM_TYPE_MAP,
|
|
1382
|
+
generate: (f) => `${CUSTOM_TYPE_MAP[f.type]}("${escapeDoubleQuote(f.fieldName)}")`,
|
|
1383
|
+
imports: () => []
|
|
1384
|
+
// provided by inline-types enhancer
|
|
1385
|
+
}
|
|
1386
|
+
];
|
|
1387
|
+
|
|
1388
|
+
// src/generator/mappers/fallback.ts
|
|
1389
|
+
var fallbackMapper = {
|
|
1390
|
+
name: "fallback-text",
|
|
1391
|
+
match: () => true,
|
|
1392
|
+
generate: (field) => `text("${escapeDoubleQuote(field.fieldName)}")`,
|
|
1393
|
+
imports: () => [{ name: "text", from: "drizzle-orm/pg-core" }]
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
// src/generator/mappers/index.ts
|
|
1397
|
+
function createDefaultRegistry() {
|
|
1398
|
+
const registry = new TypeMapperRegistry();
|
|
1399
|
+
for (const mapper of customTypeMappers) registry.register(mapper);
|
|
1400
|
+
for (const mapper of primitiveMappers) registry.register(mapper);
|
|
1401
|
+
registry.register(fallbackMapper);
|
|
1402
|
+
return registry;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/generator/import-collector.ts
|
|
1406
|
+
var ImportCollector = class {
|
|
1407
|
+
imports = /* @__PURE__ */ new Map();
|
|
1408
|
+
add(spec) {
|
|
1409
|
+
if (!this.imports.has(spec.from)) {
|
|
1410
|
+
this.imports.set(spec.from, /* @__PURE__ */ new Set());
|
|
1411
|
+
}
|
|
1412
|
+
this.imports.get(spec.from).add(spec.name);
|
|
1413
|
+
}
|
|
1414
|
+
addAll(specs) {
|
|
1415
|
+
for (const spec of specs) this.add(spec);
|
|
1416
|
+
}
|
|
1417
|
+
/** Generate import statements, sorted by source path */
|
|
1418
|
+
generate() {
|
|
1419
|
+
const lines = [];
|
|
1420
|
+
const sortedPaths = [...this.imports.keys()].sort();
|
|
1421
|
+
for (const from of sortedPaths) {
|
|
1422
|
+
const names = [...this.imports.get(from)].sort();
|
|
1423
|
+
lines.push(`import { ${names.join(", ")} } from '${from}';`);
|
|
1424
|
+
}
|
|
1425
|
+
return lines.join("\n");
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// src/generator/default-value.ts
|
|
1430
|
+
function formatDefaultValue(value) {
|
|
1431
|
+
if (value == null || value === "") return "";
|
|
1432
|
+
const trimmed = value.trim();
|
|
1433
|
+
if (!trimmed || trimmed.toUpperCase() === "NULL") return "";
|
|
1434
|
+
if (/^gen_random_uuid\(\)$/i.test(trimmed)) return ".defaultRandom()";
|
|
1435
|
+
if (/^CURRENT_TIMESTAMP$/i.test(trimmed)) return ".default(sql`CURRENT_TIMESTAMP`)";
|
|
1436
|
+
if (trimmed === "true") return ".default(true)";
|
|
1437
|
+
if (trimmed === "false") return ".default(false)";
|
|
1438
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) return `.default(${trimmed})`;
|
|
1439
|
+
if (/::|\(|CASE|current_setting/i.test(trimmed)) {
|
|
1440
|
+
const escaped = trimmed.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1441
|
+
return `.default(sql\`${escaped}\`)`;
|
|
1442
|
+
}
|
|
1443
|
+
const escapedStr = trimmed.replace(/'/g, "\\'");
|
|
1444
|
+
return `.default('${escapedStr}')`;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// src/generator/field-modifier.ts
|
|
1448
|
+
var defaultModifiers = [
|
|
1449
|
+
{
|
|
1450
|
+
name: "primaryKey",
|
|
1451
|
+
match: (f) => f.isPrimary,
|
|
1452
|
+
modify: () => ".primaryKey()"
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
name: "notNull",
|
|
1456
|
+
match: (f) => !f.isNullable && !f.isPrimary,
|
|
1457
|
+
modify: () => ".notNull()"
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
name: "unique",
|
|
1461
|
+
match: (f) => f.isUnique,
|
|
1462
|
+
modify: () => ".unique()"
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
name: "defaultValue",
|
|
1466
|
+
match: (f) => f.defaultValue != null && f.defaultValue !== "",
|
|
1467
|
+
modify: (f) => formatDefaultValue(f.defaultValue)
|
|
1468
|
+
}
|
|
1469
|
+
];
|
|
1470
|
+
function applyModifiers(field, modifiers) {
|
|
1471
|
+
let result = "";
|
|
1472
|
+
for (const mod of modifiers) {
|
|
1473
|
+
if (mod.match(field)) {
|
|
1474
|
+
result += mod.modify(field);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
return result;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// src/generator/structures/enum.ts
|
|
1481
|
+
function generateEnumCode(schema) {
|
|
1482
|
+
const values = schema.values.map((v) => `'${v.replace(/'/g, "\\'")}'`).join(", ");
|
|
1483
|
+
return `export const ${schema.identifier} = pgEnum("${escapeDoubleQuote(schema.enumName)}", [${values}]);`;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// src/generator/structures/index-gen.ts
|
|
1487
|
+
function generateIndexCode(idx, tableIdentifier) {
|
|
1488
|
+
const fn = idx.indexType === "unique" ? "uniqueIndex" : "index";
|
|
1489
|
+
const columns = idx.indexColumns.map((col) => `table.${sanitizePropertyName(col)}`).join(", ");
|
|
1490
|
+
const escapedName = escapeDoubleQuote(idx.indexName);
|
|
1491
|
+
const creationType = idx.indexCreationType || "btree";
|
|
1492
|
+
if (creationType !== "btree" && columns) {
|
|
1493
|
+
return `${fn}("${escapedName}").using("${creationType}", ${columns})`;
|
|
1494
|
+
}
|
|
1495
|
+
if (!columns) {
|
|
1496
|
+
return `// Complex index: ${idx.indexDef || idx.indexName}`;
|
|
1497
|
+
}
|
|
1498
|
+
return `${fn}("${escapedName}").on(${columns})`;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// src/generator/structures/foreign-key.ts
|
|
1502
|
+
function generateForeignKeyCode(rel, tableIdentifier, tableIdentifierMap) {
|
|
1503
|
+
const columns = rel.fieldNames.map((f) => `table.${sanitizePropertyName(f)}`).join(", ");
|
|
1504
|
+
const targetIdentifier = tableIdentifierMap?.get(rel.targetTableName) ?? sanitizeIdentifier(rel.targetTableName);
|
|
1505
|
+
const foreignColumns = rel.targetFieldNames.map((f) => `${targetIdentifier}.${sanitizePropertyName(f)}`).join(", ");
|
|
1506
|
+
let code = `foreignKey({
|
|
1507
|
+
columns: [${columns}],
|
|
1508
|
+
foreignColumns: [${foreignColumns}],
|
|
1509
|
+
})`;
|
|
1510
|
+
if (rel.removePolicy && rel.removePolicy !== "NO_ACTION") {
|
|
1511
|
+
code += `.onDelete("${escapeDoubleQuote(rel.removePolicy.toLowerCase())}")`;
|
|
1512
|
+
}
|
|
1513
|
+
if (rel.updatePolicy && rel.updatePolicy !== "NO_ACTION") {
|
|
1514
|
+
code += `.onUpdate("${escapeDoubleQuote(rel.updatePolicy.toLowerCase())}")`;
|
|
1515
|
+
}
|
|
1516
|
+
return code;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// src/generator/structures/table.ts
|
|
1520
|
+
function generateTableCode(table, schemaData, registry, modifiers, tableIdentifierMap) {
|
|
1521
|
+
const fieldLines = table.fields.map((field) => {
|
|
1522
|
+
const mapper = registry.resolve(field);
|
|
1523
|
+
const typeExpr = mapper.generate(field);
|
|
1524
|
+
const mods = applyModifiers(field, modifiers);
|
|
1525
|
+
const propName = sanitizePropertyName(field.fieldName);
|
|
1526
|
+
return ` ${propName}: ${typeExpr}${mods},`;
|
|
1527
|
+
});
|
|
1528
|
+
const thirdArgParts = [];
|
|
1529
|
+
for (const idx of table.indexes) {
|
|
1530
|
+
thirdArgParts.push(generateIndexCode(idx, table.identifier));
|
|
1531
|
+
}
|
|
1532
|
+
for (const rel of table.relationships) {
|
|
1533
|
+
thirdArgParts.push(generateForeignKeyCode(rel, table.identifier, tableIdentifierMap));
|
|
1534
|
+
}
|
|
1535
|
+
let code = `export const ${table.identifier} = pgTable("${escapeDoubleQuote(table.tableName)}", {
|
|
1536
|
+
${fieldLines.join("\n")}
|
|
1537
|
+
}`;
|
|
1538
|
+
if (thirdArgParts.length > 0) {
|
|
1539
|
+
const parts = thirdArgParts.map((p) => ` ${p},`).join("\n");
|
|
1540
|
+
code += `, (table) => [
|
|
1541
|
+
${parts}
|
|
1542
|
+
]`;
|
|
1543
|
+
}
|
|
1544
|
+
code += ");";
|
|
1545
|
+
return code;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/generator/structures/view.ts
|
|
1549
|
+
function generateViewCode(view, viewFn, registry, modifiers) {
|
|
1550
|
+
if (view.viewDef) {
|
|
1551
|
+
const escaped = view.viewDef.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1552
|
+
return `export const ${view.identifier} = ${viewFn}("${escapeDoubleQuote(view.viewName)}").as(sql\`${escaped}\`);`;
|
|
1553
|
+
}
|
|
1554
|
+
const fieldLines = view.fields.map((field) => {
|
|
1555
|
+
const mapper = registry.resolve(field);
|
|
1556
|
+
const typeExpr = mapper.generate(field);
|
|
1557
|
+
const mods = applyModifiers(field, modifiers);
|
|
1558
|
+
return ` ${sanitizePropertyName(field.fieldName)}: ${typeExpr}${mods},`;
|
|
1559
|
+
});
|
|
1560
|
+
return `export const ${view.identifier} = ${viewFn}("${escapeDoubleQuote(view.viewName)}", {
|
|
1561
|
+
${fieldLines.join("\n")}
|
|
1562
|
+
});`;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// src/generator/index.ts
|
|
1566
|
+
function generateSchemaCode(data) {
|
|
1567
|
+
const registry = createDefaultRegistry();
|
|
1568
|
+
const collector = new ImportCollector();
|
|
1569
|
+
if (data.enums.length > 0) {
|
|
1570
|
+
collector.add({ name: "pgEnum", from: "drizzle-orm/pg-core" });
|
|
1571
|
+
}
|
|
1572
|
+
if (data.sequences.length > 0) {
|
|
1573
|
+
collector.add({ name: "pgSequence", from: "drizzle-orm/pg-core" });
|
|
1574
|
+
}
|
|
1575
|
+
if (data.tables.length > 0) {
|
|
1576
|
+
collector.add({ name: "pgTable", from: "drizzle-orm/pg-core" });
|
|
1577
|
+
}
|
|
1578
|
+
if (data.views.length > 0) {
|
|
1579
|
+
collector.add({ name: "pgView", from: "drizzle-orm/pg-core" });
|
|
1580
|
+
}
|
|
1581
|
+
if (data.materializedViews.length > 0) {
|
|
1582
|
+
collector.add({ name: "pgMaterializedView", from: "drizzle-orm/pg-core" });
|
|
1583
|
+
}
|
|
1584
|
+
for (const table of data.tables) {
|
|
1585
|
+
for (const field of table.fields) {
|
|
1586
|
+
collector.addAll(registry.resolve(field).imports());
|
|
1587
|
+
}
|
|
1588
|
+
if (table.indexes.length > 0) {
|
|
1589
|
+
const hasNonComplex = table.indexes.some(
|
|
1590
|
+
(idx) => idx.indexColumns && idx.indexColumns.length > 0
|
|
1591
|
+
);
|
|
1592
|
+
if (hasNonComplex) {
|
|
1593
|
+
collector.add({ name: "index", from: "drizzle-orm/pg-core" });
|
|
1594
|
+
if (table.indexes.some((i) => i.indexType === "unique")) {
|
|
1595
|
+
collector.add({ name: "uniqueIndex", from: "drizzle-orm/pg-core" });
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (table.relationships.length > 0) {
|
|
1600
|
+
collector.add({ name: "foreignKey", from: "drizzle-orm/pg-core" });
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
for (const view of [...data.views, ...data.materializedViews]) {
|
|
1604
|
+
for (const field of view.fields) {
|
|
1605
|
+
collector.addAll(registry.resolve(field).imports());
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
const tableIdentifierMap = /* @__PURE__ */ new Map();
|
|
1609
|
+
for (const table of data.tables) {
|
|
1610
|
+
tableIdentifierMap.set(table.tableName, table.identifier);
|
|
1611
|
+
}
|
|
1612
|
+
const bodyParts = [];
|
|
1613
|
+
for (const enumSchema of data.enums) {
|
|
1614
|
+
bodyParts.push(generateEnumCode(enumSchema));
|
|
1615
|
+
}
|
|
1616
|
+
for (const seq of data.sequences) {
|
|
1617
|
+
bodyParts.push(`export const ${seq.identifier} = pgSequence("${escapeDoubleQuote(seq.sequenceName)}");`);
|
|
1618
|
+
}
|
|
1619
|
+
for (const table of data.tables) {
|
|
1620
|
+
bodyParts.push(generateTableCode(table, data, registry, defaultModifiers, tableIdentifierMap));
|
|
1621
|
+
}
|
|
1622
|
+
for (const view of data.views) {
|
|
1623
|
+
bodyParts.push(generateViewCode(view, "pgView", registry, defaultModifiers));
|
|
1624
|
+
}
|
|
1625
|
+
for (const mview of data.materializedViews) {
|
|
1626
|
+
bodyParts.push(generateViewCode(mview, "pgMaterializedView", registry, defaultModifiers));
|
|
1627
|
+
}
|
|
1628
|
+
const body = bodyParts.join("\n\n");
|
|
1629
|
+
if (body.includes("sql`")) {
|
|
1630
|
+
collector.add({ name: "sql", from: "drizzle-orm" });
|
|
1631
|
+
}
|
|
1632
|
+
const importSection = collector.generate();
|
|
1633
|
+
if (!importSection) {
|
|
1634
|
+
return body;
|
|
1635
|
+
}
|
|
1636
|
+
return `${importSection}
|
|
1637
|
+
|
|
1638
|
+
${body}`;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// src/enhancer/index.ts
|
|
1642
|
+
function runEnhancers(source, enhancers, context) {
|
|
1643
|
+
let current = source;
|
|
1644
|
+
const allStats = {};
|
|
1645
|
+
for (const enhancer of enhancers) {
|
|
1646
|
+
const result = enhancer.enhance(current, context);
|
|
1647
|
+
current = result.source;
|
|
1648
|
+
if (result.stats) {
|
|
1649
|
+
allStats[enhancer.name] = result.stats;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return { source: current, allStats };
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/enhancer/enhancers/header.ts
|
|
1656
|
+
var ESLINT_DISABLE = "/* eslint-disable */";
|
|
1657
|
+
var HEADER_COMMENT = "/** auto generated, do not edit */";
|
|
1658
|
+
var FULL_HEADER = `${ESLINT_DISABLE}
|
|
1659
|
+
${HEADER_COMMENT}`;
|
|
1660
|
+
var headerEnhancer = {
|
|
1661
|
+
name: "header",
|
|
1662
|
+
enhance(source, _context) {
|
|
1663
|
+
let trimmed = source;
|
|
1664
|
+
const headerPatterns = [
|
|
1665
|
+
/^\/\*\s*eslint-disable\s*\*\/\s*\n?/,
|
|
1666
|
+
/^\/\*\*\s*auto generated[^*]*\*\/\s*\n?/
|
|
1667
|
+
];
|
|
1668
|
+
for (const pattern of headerPatterns) {
|
|
1669
|
+
while (pattern.test(trimmed)) {
|
|
1670
|
+
trimmed = trimmed.replace(pattern, "");
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
trimmed = trimmed.trimStart();
|
|
1674
|
+
return { source: `${FULL_HEADER}
|
|
1675
|
+
${trimmed}` };
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
// src/template/types.ts
|
|
1680
|
+
var CUSTOM_TIMESTAMPTZ_DEFINITION = `const customTimestamptz = customType<{
|
|
1681
|
+
data: Date;
|
|
1682
|
+
driverData: string;
|
|
1683
|
+
config: { precision?: number };
|
|
1684
|
+
}>({
|
|
1685
|
+
dataType(config) {
|
|
1686
|
+
const precision = typeof config?.precision !== 'undefined'
|
|
1687
|
+
? \` (\${config.precision})\`
|
|
1688
|
+
: '';
|
|
1689
|
+
return \`timestamptz\${precision}\`;
|
|
1690
|
+
},
|
|
1691
|
+
toDriver(value: Date | string | number) {
|
|
1692
|
+
if (value == null) return value as any;
|
|
1693
|
+
if (typeof value === 'number') return new Date(value).toISOString();
|
|
1694
|
+
if (typeof value === 'string') return value;
|
|
1695
|
+
if (value instanceof Date) return value.toISOString();
|
|
1696
|
+
throw new Error('Invalid timestamp value');
|
|
1697
|
+
},
|
|
1698
|
+
fromDriver(value: string | Date): Date {
|
|
1699
|
+
if (value instanceof Date) return value;
|
|
1700
|
+
return new Date(value);
|
|
1701
|
+
},
|
|
1702
|
+
});`;
|
|
1703
|
+
var USER_PROFILE_DEFINITION = `const userProfile = customType<{
|
|
1704
|
+
data: string;
|
|
1705
|
+
driverData: string;
|
|
1706
|
+
}>({
|
|
1707
|
+
dataType() {
|
|
1708
|
+
return 'user_profile';
|
|
1709
|
+
},
|
|
1710
|
+
toDriver(value: string) {
|
|
1711
|
+
return sql\`ROW(\${value})::user_profile\`;
|
|
1712
|
+
},
|
|
1713
|
+
fromDriver(value: string) {
|
|
1714
|
+
const [userId] = value.slice(1, -1).split(',');
|
|
1715
|
+
return userId.trim();
|
|
1716
|
+
},
|
|
1717
|
+
});`;
|
|
1718
|
+
var FILE_ATTACHMENT_DEFINITION = `type FileAttachment = {
|
|
1719
|
+
bucket_id: string;
|
|
1720
|
+
file_path: string;
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
const fileAttachment = customType<{
|
|
1724
|
+
data: FileAttachment;
|
|
1725
|
+
driverData: string;
|
|
1726
|
+
}>({
|
|
1727
|
+
dataType() {
|
|
1728
|
+
return 'file_attachment';
|
|
1729
|
+
},
|
|
1730
|
+
toDriver(value: FileAttachment) {
|
|
1731
|
+
return sql\`ROW(\${value.bucket_id},\${value.file_path})::file_attachment\`;
|
|
1732
|
+
},
|
|
1733
|
+
fromDriver(value: string): FileAttachment {
|
|
1734
|
+
const [bucketId, filePath] = value.slice(1, -1).split(',');
|
|
1735
|
+
return { bucket_id: bucketId.trim(), file_path: filePath.trim() };
|
|
1736
|
+
},
|
|
1737
|
+
});`;
|
|
1738
|
+
var ESCAPE_LITERAL_DEFINITION = `function escapeLiteral(str: string): string {
|
|
1739
|
+
return "'" + str.replace(/'/g, "''") + "'";
|
|
1740
|
+
}`;
|
|
1741
|
+
var USER_PROFILE_ARRAY_DEFINITION = `const userProfileArray = customType<{
|
|
1742
|
+
data: string[];
|
|
1743
|
+
driverData: string;
|
|
1744
|
+
}>({
|
|
1745
|
+
dataType() {
|
|
1746
|
+
return 'user_profile[]';
|
|
1747
|
+
},
|
|
1748
|
+
toDriver(value: string[]) {
|
|
1749
|
+
if (!value || value.length === 0) {
|
|
1750
|
+
return sql\`'{}'::user_profile[]\`;
|
|
1751
|
+
}
|
|
1752
|
+
const elements = value.map(id => \`ROW(\${escapeLiteral(id)})::user_profile\`).join(',');
|
|
1753
|
+
return sql.raw(\`ARRAY[\${elements}]::user_profile[]\`);
|
|
1754
|
+
},
|
|
1755
|
+
fromDriver(value: string): string[] {
|
|
1756
|
+
if (!value || value === '{}') return [];
|
|
1757
|
+
const inner = value.slice(1, -1);
|
|
1758
|
+
const matches = inner.match(/\\([^)]*\\)/g) || [];
|
|
1759
|
+
return matches.map(m => m.slice(1, -1).split(',')[0].trim());
|
|
1760
|
+
},
|
|
1761
|
+
});`;
|
|
1762
|
+
var FILE_ATTACHMENT_ARRAY_DEFINITION = `const fileAttachmentArray = customType<{
|
|
1763
|
+
data: FileAttachment[];
|
|
1764
|
+
driverData: string;
|
|
1765
|
+
}>({
|
|
1766
|
+
dataType() {
|
|
1767
|
+
return 'file_attachment[]';
|
|
1768
|
+
},
|
|
1769
|
+
toDriver(value: FileAttachment[]) {
|
|
1770
|
+
if (!value || value.length === 0) {
|
|
1771
|
+
return sql\`'{}'::file_attachment[]\`;
|
|
1772
|
+
}
|
|
1773
|
+
const elements = value.map(f =>
|
|
1774
|
+
\`ROW(\${escapeLiteral(f.bucket_id)},\${escapeLiteral(f.file_path)})::file_attachment\`
|
|
1775
|
+
).join(',');
|
|
1776
|
+
return sql.raw(\`ARRAY[\${elements}]::file_attachment[]\`);
|
|
1777
|
+
},
|
|
1778
|
+
fromDriver(value: string): FileAttachment[] {
|
|
1779
|
+
if (!value || value === '{}') return [];
|
|
1780
|
+
const inner = value.slice(1, -1);
|
|
1781
|
+
const matches = inner.match(/\\([^)]*\\)/g) || [];
|
|
1782
|
+
return matches.map(m => {
|
|
1783
|
+
const [bucketId, filePath] = m.slice(1, -1).split(',');
|
|
1784
|
+
return { bucket_id: bucketId.trim(), file_path: filePath.trim() };
|
|
1785
|
+
});
|
|
1786
|
+
},
|
|
1787
|
+
});`;
|
|
1788
|
+
|
|
1789
|
+
// src/enhancer/enhancers/inline-types.ts
|
|
1790
|
+
var TYPE_RULES = [
|
|
1791
|
+
{
|
|
1792
|
+
usageName: "customTimestamptz",
|
|
1793
|
+
definitionCheck: "const customTimestamptz = customType<",
|
|
1794
|
+
definition: CUSTOM_TIMESTAMPTZ_DEFINITION
|
|
1795
|
+
},
|
|
1796
|
+
{
|
|
1797
|
+
usageName: "userProfile",
|
|
1798
|
+
definitionCheck: "const userProfile = customType<",
|
|
1799
|
+
definition: USER_PROFILE_DEFINITION
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
usageName: "fileAttachment",
|
|
1803
|
+
definitionCheck: "const fileAttachment = customType<",
|
|
1804
|
+
definition: FILE_ATTACHMENT_DEFINITION
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
usageName: "escapeLiteral",
|
|
1808
|
+
definitionCheck: "function escapeLiteral(",
|
|
1809
|
+
definition: ESCAPE_LITERAL_DEFINITION
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
usageName: "userProfileArray",
|
|
1813
|
+
definitionCheck: "const userProfileArray = customType<",
|
|
1814
|
+
definition: USER_PROFILE_ARRAY_DEFINITION,
|
|
1815
|
+
deps: ["userProfile", "escapeLiteral"]
|
|
1816
|
+
},
|
|
1817
|
+
{
|
|
1818
|
+
usageName: "fileAttachmentArray",
|
|
1819
|
+
definitionCheck: "const fileAttachmentArray = customType<",
|
|
1820
|
+
definition: FILE_ATTACHMENT_ARRAY_DEFINITION,
|
|
1821
|
+
deps: ["fileAttachment", "escapeLiteral"]
|
|
1822
|
+
}
|
|
1823
|
+
];
|
|
1824
|
+
var inlineTypesEnhancer = {
|
|
1825
|
+
name: "inline-types",
|
|
1826
|
+
enhance(source, context) {
|
|
1827
|
+
const exportCustomTypes = context.exportCustomTypes ?? false;
|
|
1828
|
+
let text = source;
|
|
1829
|
+
text = text.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
|
|
1830
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
1831
|
+
for (const rule of TYPE_RULES) {
|
|
1832
|
+
if (text.includes(rule.usageName + "(") && !text.includes(rule.definitionCheck)) {
|
|
1833
|
+
usedTypes.add(rule.usageName);
|
|
1834
|
+
if (rule.deps) {
|
|
1835
|
+
for (const dep of rule.deps) usedTypes.add(dep);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (usedTypes.size === 0) {
|
|
1840
|
+
return { source: text };
|
|
1841
|
+
}
|
|
1842
|
+
text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
|
|
1843
|
+
if (!text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
|
|
1844
|
+
const pgCoreImportMatch = text.match(
|
|
1845
|
+
/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m
|
|
1846
|
+
);
|
|
1847
|
+
if (pgCoreImportMatch) {
|
|
1848
|
+
const insertPoint = text.indexOf(pgCoreImportMatch[0]) + pgCoreImportMatch[0].length;
|
|
1849
|
+
text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
const definitions = [];
|
|
1853
|
+
for (const rule of TYPE_RULES) {
|
|
1854
|
+
if (usedTypes.has(rule.usageName) && !text.includes(rule.definitionCheck)) {
|
|
1855
|
+
let def = rule.definition;
|
|
1856
|
+
if (exportCustomTypes) {
|
|
1857
|
+
def = def.replace(/^(const |function |type )/gm, "export $1");
|
|
1858
|
+
}
|
|
1859
|
+
definitions.push(def);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
if (definitions.length === 0) {
|
|
1863
|
+
return { source: text };
|
|
1864
|
+
}
|
|
1865
|
+
const headerPrefix = `${FULL_HEADER}
|
|
1866
|
+
`;
|
|
1867
|
+
let insertionPoint = 0;
|
|
1868
|
+
if (text.startsWith(headerPrefix)) {
|
|
1869
|
+
insertionPoint = headerPrefix.length;
|
|
1870
|
+
}
|
|
1871
|
+
const importSectionMatch = text.slice(insertionPoint).match(/^(?:import [^\n]+\n)+/);
|
|
1872
|
+
if (importSectionMatch) {
|
|
1873
|
+
insertionPoint += importSectionMatch[0].length;
|
|
1874
|
+
}
|
|
1875
|
+
const typeBlock = `
|
|
1876
|
+
${definitions.join("\n\n")}
|
|
1877
|
+
|
|
1878
|
+
`;
|
|
1879
|
+
text = text.slice(0, insertionPoint) + typeBlock + text.slice(insertionPoint);
|
|
1880
|
+
return { source: text, stats: { typesInlined: definitions.length } };
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
function ensureImportIdentifier(source, packageName, identifier) {
|
|
1884
|
+
const escapedPackage = packageName.replace(/\//g, "\\/");
|
|
1885
|
+
const importRegex = new RegExp(
|
|
1886
|
+
`import \\{([^}]*)\\} from ["']${escapedPackage}["'];?`
|
|
1887
|
+
);
|
|
1888
|
+
const match = source.match(importRegex);
|
|
1889
|
+
if (!match) {
|
|
1890
|
+
return source;
|
|
1891
|
+
}
|
|
1892
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean);
|
|
1893
|
+
if (identifiers.includes(identifier)) {
|
|
1894
|
+
return source;
|
|
1895
|
+
}
|
|
1896
|
+
identifiers.push(identifier);
|
|
1897
|
+
const unique = Array.from(new Set(identifiers));
|
|
1898
|
+
const replacement = `import { ${unique.join(", ")} } from "${packageName}"`;
|
|
1899
|
+
return source.replace(importRegex, replacement);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// src/enhancer/enhancers/system-comments.ts
|
|
1903
|
+
var SYSTEM_FIELD_COMMENTS = {
|
|
1904
|
+
_created_at: "Creation time",
|
|
1905
|
+
_created_by: "Creator",
|
|
1906
|
+
_updated_at: "Update time",
|
|
1907
|
+
_updated_by: "Updater"
|
|
1908
|
+
};
|
|
1909
|
+
var systemCommentsEnhancer = {
|
|
1910
|
+
name: "system-comments",
|
|
1911
|
+
enhance(source, _context) {
|
|
1912
|
+
const lines = source.split("\n");
|
|
1913
|
+
let commentsAdded = 0;
|
|
1914
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
1915
|
+
const line = lines[i];
|
|
1916
|
+
const entry = Object.entries(SYSTEM_FIELD_COMMENTS).find(
|
|
1917
|
+
([key]) => line.includes(`"${key}"`) || line.includes(`'${key}'`)
|
|
1918
|
+
);
|
|
1919
|
+
if (!entry) {
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
const [, description] = entry;
|
|
1923
|
+
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
1924
|
+
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
const indentMatch = line.match(/^\s*/);
|
|
1928
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
1929
|
+
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
1930
|
+
lines.splice(i, 0, comment);
|
|
1931
|
+
i += 1;
|
|
1932
|
+
commentsAdded += 1;
|
|
1933
|
+
}
|
|
1934
|
+
return {
|
|
1935
|
+
source: lines.join("\n"),
|
|
1936
|
+
stats: { commentsAdded }
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1941
|
+
// src/enhancer/enhancers/jsonb-comments.ts
|
|
1942
|
+
var TABLE_DEF_REGEX = /export const\s+\w+\s*=\s*pgTable\(\s*["'`]([^"'`]+)["'`]/;
|
|
1943
|
+
var JSON_FIELD_WITH_NAME_REGEX = /^\s*(\w+):\s*(?:json|jsonb)\(\s*["'`]([^"'`]+)["'`]\)/;
|
|
1944
|
+
var JSON_FIELD_NO_NAME_REGEX = /^\s*(\w+):\s*(?:json|jsonb)\(\s*\)/;
|
|
1945
|
+
var jsonbCommentsEnhancer = {
|
|
1946
|
+
name: "jsonb-comments",
|
|
1947
|
+
enhance(source, context) {
|
|
1948
|
+
const columnComments = buildColumnCommentsMap(context);
|
|
1949
|
+
if (columnComments.size === 0) {
|
|
1950
|
+
return { source, stats: { commentsAdded: 0 } };
|
|
1951
|
+
}
|
|
1952
|
+
const lines = source.split("\n");
|
|
1953
|
+
const result = [];
|
|
1954
|
+
let commentsAdded = 0;
|
|
1955
|
+
let currentTableName = null;
|
|
1956
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1957
|
+
const line = lines[i];
|
|
1958
|
+
const tableMatch = line.match(TABLE_DEF_REGEX);
|
|
1959
|
+
if (tableMatch) {
|
|
1960
|
+
currentTableName = tableMatch[1];
|
|
1961
|
+
}
|
|
1962
|
+
let columnName = null;
|
|
1963
|
+
const jsonMatchWithName = line.match(JSON_FIELD_WITH_NAME_REGEX);
|
|
1964
|
+
const jsonMatchNoName = line.match(JSON_FIELD_NO_NAME_REGEX);
|
|
1965
|
+
if (jsonMatchWithName) {
|
|
1966
|
+
columnName = jsonMatchWithName[2];
|
|
1967
|
+
} else if (jsonMatchNoName) {
|
|
1968
|
+
columnName = jsonMatchNoName[1];
|
|
1969
|
+
}
|
|
1970
|
+
if (columnName && currentTableName) {
|
|
1971
|
+
const commentKey = `${currentTableName}.${columnName}`;
|
|
1972
|
+
const comment = columnComments.get(commentKey);
|
|
1973
|
+
if (comment) {
|
|
1974
|
+
const parsed = parseColumnComment(comment);
|
|
1975
|
+
const indentMatch = line.match(/^\s*/);
|
|
1976
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
1977
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
1978
|
+
if (!prevLine.startsWith("/**") && !prevLine.startsWith("*") && !prevLine.startsWith("//")) {
|
|
1979
|
+
const commentLines = [];
|
|
1980
|
+
commentLines.push(`${indent}/**`);
|
|
1981
|
+
if (parsed.description) {
|
|
1982
|
+
const safeDesc = parsed.description.replace(/[\r\n]+/g, " ").trim();
|
|
1983
|
+
commentLines.push(`${indent} * ${safeDesc}`);
|
|
1984
|
+
}
|
|
1985
|
+
if (parsed.type) {
|
|
1986
|
+
if (parsed.description) {
|
|
1987
|
+
commentLines.push(`${indent} *`);
|
|
1988
|
+
}
|
|
1989
|
+
const safeType = parsed.type.replace(/[\r\n]+/g, " ").trim();
|
|
1990
|
+
commentLines.push(`${indent} * @type ${safeType}`);
|
|
1991
|
+
}
|
|
1992
|
+
commentLines.push(`${indent} */`);
|
|
1993
|
+
result.push(...commentLines);
|
|
1994
|
+
commentsAdded++;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
if (line.match(/^\}\);?\s*$/) || line.match(/^}\s*,\s*\{/)) {
|
|
1999
|
+
currentTableName = null;
|
|
2000
|
+
}
|
|
2001
|
+
result.push(line);
|
|
2002
|
+
}
|
|
2003
|
+
return {
|
|
2004
|
+
source: result.join("\n"),
|
|
2005
|
+
stats: { commentsAdded }
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
function buildColumnCommentsMap(context) {
|
|
2010
|
+
const map = /* @__PURE__ */ new Map();
|
|
2011
|
+
for (const table of context.schemaData.tables) {
|
|
2012
|
+
for (const field of table.fields) {
|
|
2013
|
+
if (field.comment) {
|
|
2014
|
+
map.set(`${table.tableName}.${field.fieldName}`, field.comment);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return map;
|
|
2019
|
+
}
|
|
2020
|
+
function extractTypeAnnotation(comment) {
|
|
2021
|
+
const typeStart = comment.indexOf("@type");
|
|
2022
|
+
if (typeStart === -1) return null;
|
|
2023
|
+
const afterType = comment.slice(typeStart + 5).trimStart();
|
|
2024
|
+
if (!afterType.startsWith("{")) return null;
|
|
2025
|
+
let depth = 0;
|
|
2026
|
+
let endIndex = 0;
|
|
2027
|
+
for (let i = 0; i < afterType.length; i++) {
|
|
2028
|
+
if (afterType[i] === "{") depth++;
|
|
2029
|
+
if (afterType[i] === "}") depth--;
|
|
2030
|
+
if (depth === 0) {
|
|
2031
|
+
endIndex = i + 1;
|
|
2032
|
+
break;
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (endIndex === 0) return null;
|
|
2036
|
+
return afterType.slice(0, endIndex);
|
|
2037
|
+
}
|
|
2038
|
+
function parseColumnComment(comment) {
|
|
2039
|
+
const typeValue = extractTypeAnnotation(comment);
|
|
2040
|
+
if (typeValue) {
|
|
2041
|
+
const descMatch = comment.match(/@description\s+([^@]+)/);
|
|
2042
|
+
return {
|
|
2043
|
+
type: typeValue,
|
|
2044
|
+
description: descMatch?.[1]?.trim()
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
return { description: comment.trim() };
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// src/enhancer/enhancers/synced-comments.ts
|
|
2051
|
+
var TABLE_COMMENT = "Synced table: data is auto-synced from external source. Do not rename or delete this table.";
|
|
2052
|
+
var FIELD_COMMENT = "Synced field: auto-synced, do not modify or delete";
|
|
2053
|
+
var TABLE_DEF_REGEX2 = /^(export const\s+\w+\s*=\s*(?:pgTable|pgView|pgMaterializedView)\(\s*["'`])([^"'`]+)(["'`])/;
|
|
2054
|
+
var FIELD_WITH_NAME_REGEX = /^\s*[\w"']+\s*:\s*\w+\(\s*["'`]([^"'`]+)["'`]/;
|
|
2055
|
+
var FIELD_PROP_NAME_REGEX = /^\s*([\w]+)\s*:/;
|
|
2056
|
+
var syncedCommentsEnhancer = {
|
|
2057
|
+
name: "synced-comments",
|
|
2058
|
+
enhance(source, context) {
|
|
2059
|
+
const syncedTableMap = context.schemaData.syncedTableMap;
|
|
2060
|
+
if (!syncedTableMap || syncedTableMap.size === 0) {
|
|
2061
|
+
return { source };
|
|
2062
|
+
}
|
|
2063
|
+
const lines = source.split("\n");
|
|
2064
|
+
const result = [];
|
|
2065
|
+
let commentsAdded = 0;
|
|
2066
|
+
let currentSyncedFields = null;
|
|
2067
|
+
let insideTableBody = false;
|
|
2068
|
+
let braceDepth = 0;
|
|
2069
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2070
|
+
const line = lines[i];
|
|
2071
|
+
const tableMatch = line.match(TABLE_DEF_REGEX2);
|
|
2072
|
+
if (tableMatch) {
|
|
2073
|
+
const tableName = tableMatch[2];
|
|
2074
|
+
const syncedFields = syncedTableMap.get(tableName);
|
|
2075
|
+
if (syncedFields) {
|
|
2076
|
+
currentSyncedFields = syncedFields;
|
|
2077
|
+
insideTableBody = true;
|
|
2078
|
+
braceDepth = 0;
|
|
2079
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
2080
|
+
if (!prevLine.includes("Synced table")) {
|
|
2081
|
+
const indentMatch = line.match(/^\s*/);
|
|
2082
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
2083
|
+
result.push(`${indent}// ${TABLE_COMMENT}`);
|
|
2084
|
+
commentsAdded++;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (insideTableBody) {
|
|
2089
|
+
for (const ch of line) {
|
|
2090
|
+
if (ch === "{") braceDepth++;
|
|
2091
|
+
if (ch === "}") braceDepth--;
|
|
2092
|
+
}
|
|
2093
|
+
if (braceDepth <= 0) {
|
|
2094
|
+
insideTableBody = false;
|
|
2095
|
+
currentSyncedFields = null;
|
|
2096
|
+
}
|
|
2097
|
+
if (currentSyncedFields && braceDepth >= 1 && !tableMatch) {
|
|
2098
|
+
const columnName = extractColumnName(line);
|
|
2099
|
+
if (columnName && currentSyncedFields.has(columnName)) {
|
|
2100
|
+
const prevLine = result[result.length - 1]?.trim() ?? "";
|
|
2101
|
+
if (!prevLine.includes("Synced field")) {
|
|
2102
|
+
const indentMatch = line.match(/^\s*/);
|
|
2103
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
2104
|
+
result.push(`${indent}// ${FIELD_COMMENT}`);
|
|
2105
|
+
commentsAdded++;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
result.push(line);
|
|
2111
|
+
}
|
|
2112
|
+
return {
|
|
2113
|
+
source: result.join("\n"),
|
|
2114
|
+
stats: { commentsAdded }
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
function extractColumnName(line) {
|
|
2119
|
+
const withNameMatch = line.match(FIELD_WITH_NAME_REGEX);
|
|
2120
|
+
if (withNameMatch) {
|
|
2121
|
+
return withNameMatch[1];
|
|
2122
|
+
}
|
|
2123
|
+
const propMatch = line.match(FIELD_PROP_NAME_REGEX);
|
|
2124
|
+
if (propMatch) {
|
|
2125
|
+
return propMatch[1];
|
|
2126
|
+
}
|
|
2127
|
+
return null;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// src/enhancer/enhancers/table-aliases.ts
|
|
2131
|
+
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
2132
|
+
var tableAliasesEnhancer = {
|
|
2133
|
+
name: "table-aliases",
|
|
2134
|
+
enhance(source, _context) {
|
|
2135
|
+
const markerIndex = source.indexOf(`
|
|
2136
|
+
${TABLE_ALIAS_MARKER}`);
|
|
2137
|
+
const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
|
|
2138
|
+
const exportRegex = /export const\s+([A-Za-z_$][\w$]*)\s*=\s*(?:pgTable|pgView|pgMaterializedView)\s*\(/g;
|
|
2139
|
+
const tableExports = /* @__PURE__ */ new Set();
|
|
2140
|
+
for (const match of base.matchAll(exportRegex)) {
|
|
2141
|
+
tableExports.add(match[1]);
|
|
2142
|
+
}
|
|
2143
|
+
if (tableExports.size === 0) {
|
|
2144
|
+
return { source: base, stats: { aliasesGenerated: 0 } };
|
|
2145
|
+
}
|
|
2146
|
+
const aliasLines = Array.from(tableExports).sort().map((name) => `export const ${name}Table = ${name};`).join("\n");
|
|
2147
|
+
const prefix = base.trimEnd();
|
|
2148
|
+
return {
|
|
2149
|
+
source: `${prefix}
|
|
2150
|
+
|
|
2151
|
+
${TABLE_ALIAS_MARKER}
|
|
2152
|
+
${aliasLines}
|
|
2153
|
+
`,
|
|
2154
|
+
stats: { aliasesGenerated: tableExports.size }
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2159
|
+
// src/enhancer/enhancers/format.ts
|
|
2160
|
+
var formatEnhancer = {
|
|
2161
|
+
name: "format",
|
|
2162
|
+
enhance(source, _context) {
|
|
2163
|
+
let text = source;
|
|
2164
|
+
text = text.replace(/\r\n/g, "\n");
|
|
2165
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
2166
|
+
if (!text.endsWith("\n")) {
|
|
2167
|
+
text += "\n";
|
|
2168
|
+
}
|
|
2169
|
+
return { source: text };
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
// src/enhancer/enhancers/index.ts
|
|
2174
|
+
var defaultEnhancers = [
|
|
2175
|
+
headerEnhancer,
|
|
2176
|
+
inlineTypesEnhancer,
|
|
2177
|
+
systemCommentsEnhancer,
|
|
2178
|
+
jsonbCommentsEnhancer,
|
|
2179
|
+
syncedCommentsEnhancer,
|
|
2180
|
+
tableAliasesEnhancer,
|
|
2181
|
+
formatEnhancer
|
|
2182
|
+
];
|
|
2183
|
+
|
|
2184
|
+
// src/index.ts
|
|
2185
|
+
async function generateSchema(options) {
|
|
2186
|
+
const schemaData = await fetchSchemaData(options);
|
|
2187
|
+
return generateSchemaFromData(schemaData, options);
|
|
2188
|
+
}
|
|
2189
|
+
function generateSchemaFromData(schemaData, options) {
|
|
2190
|
+
const rawCode = generateSchemaCode(schemaData);
|
|
2191
|
+
const enhancers = options?.skipEnhancers ? defaultEnhancers.filter((e) => !options.skipEnhancers.includes(e.name)) : defaultEnhancers;
|
|
2192
|
+
const context = {
|
|
2193
|
+
schemaData,
|
|
2194
|
+
exportCustomTypes: options?.exportCustomTypes ?? false
|
|
2195
|
+
};
|
|
2196
|
+
const { source } = runEnhancers(rawCode, enhancers, context);
|
|
2197
|
+
return source;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
export {
|
|
2201
|
+
__require,
|
|
2202
|
+
__commonJS,
|
|
2203
|
+
__toESM,
|
|
2204
|
+
resolveEnvOptions,
|
|
2205
|
+
normalizeApiResponse,
|
|
2206
|
+
generateSchemaCode,
|
|
2207
|
+
generateSchema,
|
|
2208
|
+
generateSchemaFromData
|
|
2209
|
+
};
|