@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.
@@ -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
+ };