@lark-apaas/fullstack-nestjs-core 1.1.32 → 1.1.33-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1371,8 +1371,8 @@ var require_node = __commonJS({
1371
1371
  }
1372
1372
  break;
1373
1373
  case "FILE":
1374
- var fs2 = require("fs");
1375
- stream2 = new fs2.SyncWriteStream(fd2, {
1374
+ var fs3 = require("fs");
1375
+ stream2 = new fs3.SyncWriteStream(fd2, {
1376
1376
  autoClose: false
1377
1377
  });
1378
1378
  stream2._type = "fs";
@@ -18069,8 +18069,8 @@ var require_node2 = __commonJS({
18069
18069
  }
18070
18070
  break;
18071
18071
  case "FILE":
18072
- var fs2 = require("fs");
18073
- stream2 = new fs2.SyncWriteStream(fd2, {
18072
+ var fs3 = require("fs");
18073
+ stream2 = new fs3.SyncWriteStream(fd2, {
18074
18074
  autoClose: false
18075
18075
  });
18076
18076
  stream2._type = "fs";
@@ -18839,8 +18839,8 @@ var require_node3 = __commonJS({
18839
18839
  }
18840
18840
  break;
18841
18841
  case "FILE":
18842
- var fs2 = require("fs");
18843
- stream2 = new fs2.SyncWriteStream(fd2, {
18842
+ var fs3 = require("fs");
18843
+ stream2 = new fs3.SyncWriteStream(fd2, {
18844
18844
  autoClose: false
18845
18845
  });
18846
18846
  stream2._type = "fs";
@@ -20634,11 +20634,11 @@ var require_view = __commonJS({
20634
20634
  "use strict";
20635
20635
  var debug = require_src3()("express:view");
20636
20636
  var path2 = require("path");
20637
- var fs2 = require("fs");
20638
- var dirname = path2.dirname;
20637
+ var fs3 = require("fs");
20638
+ var dirname2 = path2.dirname;
20639
20639
  var basename = path2.basename;
20640
20640
  var extname2 = path2.extname;
20641
- var join2 = path2.join;
20641
+ var join3 = path2.join;
20642
20642
  var resolve2 = path2.resolve;
20643
20643
  module2.exports = View;
20644
20644
  function View(name, options) {
@@ -20675,7 +20675,7 @@ var require_view = __commonJS({
20675
20675
  for (var i = 0; i < roots.length && !path3; i++) {
20676
20676
  var root = roots[i];
20677
20677
  var loc = resolve2(root, name);
20678
- var dir = dirname(loc);
20678
+ var dir = dirname2(loc);
20679
20679
  var file = basename(loc);
20680
20680
  path3 = this.resolve(dir, file);
20681
20681
  }
@@ -20687,12 +20687,12 @@ var require_view = __commonJS({
20687
20687
  }, "render");
20688
20688
  View.prototype.resolve = /* @__PURE__ */ __name(function resolve3(dir, file) {
20689
20689
  var ext = this.ext;
20690
- var path3 = join2(dir, file);
20690
+ var path3 = join3(dir, file);
20691
20691
  var stat = tryStat(path3);
20692
20692
  if (stat && stat.isFile()) {
20693
20693
  return path3;
20694
20694
  }
20695
- path3 = join2(dir, basename(file, ext), "index" + ext);
20695
+ path3 = join3(dir, basename(file, ext), "index" + ext);
20696
20696
  stat = tryStat(path3);
20697
20697
  if (stat && stat.isFile()) {
20698
20698
  return path3;
@@ -20701,7 +20701,7 @@ var require_view = __commonJS({
20701
20701
  function tryStat(path3) {
20702
20702
  debug('stat "%s"', path3);
20703
20703
  try {
20704
- return fs2.statSync(path3);
20704
+ return fs3.statSync(path3);
20705
20705
  } catch (e) {
20706
20706
  return void 0;
20707
20707
  }
@@ -21350,8 +21350,8 @@ var require_node4 = __commonJS({
21350
21350
  }
21351
21351
  break;
21352
21352
  case "FILE":
21353
- var fs2 = require("fs");
21354
- stream2 = new fs2.SyncWriteStream(fd2, {
21353
+ var fs3 = require("fs");
21354
+ stream2 = new fs3.SyncWriteStream(fd2, {
21355
21355
  autoClose: false
21356
21356
  });
21357
21357
  stream2._type = "fs";
@@ -21537,7 +21537,7 @@ var require_mime = __commonJS({
21537
21537
  "../../../node_modules/mime/mime.js"(exports2, module2) {
21538
21538
  "use strict";
21539
21539
  var path2 = require("path");
21540
- var fs2 = require("fs");
21540
+ var fs3 = require("fs");
21541
21541
  function Mime() {
21542
21542
  this.types = /* @__PURE__ */ Object.create(null);
21543
21543
  this.extensions = /* @__PURE__ */ Object.create(null);
@@ -21559,7 +21559,7 @@ var require_mime = __commonJS({
21559
21559
  };
21560
21560
  Mime.prototype.load = function(file) {
21561
21561
  this._loading = file;
21562
- var map = {}, content = fs2.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
21562
+ var map = {}, content = fs3.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
21563
21563
  lines.forEach(function(line) {
21564
21564
  var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
21565
21565
  map[fields.shift()] = fields;
@@ -21804,7 +21804,7 @@ var require_send = __commonJS({
21804
21804
  var escapeHtml = require_escape_html();
21805
21805
  var etag = require_etag();
21806
21806
  var fresh = require_fresh();
21807
- var fs2 = require("fs");
21807
+ var fs3 = require("fs");
21808
21808
  var mime = require_mime();
21809
21809
  var ms = require_ms5();
21810
21810
  var onFinished = require_on_finished();
@@ -21814,7 +21814,7 @@ var require_send = __commonJS({
21814
21814
  var Stream = require("stream");
21815
21815
  var util = require("util");
21816
21816
  var extname2 = path2.extname;
21817
- var join2 = path2.join;
21817
+ var join3 = path2.join;
21818
21818
  var normalize2 = path2.normalize;
21819
21819
  var resolve2 = path2.resolve;
21820
21820
  var sep = path2.sep;
@@ -22031,7 +22031,7 @@ var require_send = __commonJS({
22031
22031
  return res;
22032
22032
  }
22033
22033
  parts = path3.split(sep);
22034
- path3 = normalize2(join2(root, path3));
22034
+ path3 = normalize2(join3(root, path3));
22035
22035
  } else {
22036
22036
  if (UP_PATH_REGEXP.test(path3)) {
22037
22037
  debug('malicious path "%s"', path3);
@@ -22137,7 +22137,7 @@ var require_send = __commonJS({
22137
22137
  var i = 0;
22138
22138
  var self = this;
22139
22139
  debug('stat "%s"', path3);
22140
- fs2.stat(path3, /* @__PURE__ */ __name(function onstat(err, stat) {
22140
+ fs3.stat(path3, /* @__PURE__ */ __name(function onstat(err, stat) {
22141
22141
  if (err && err.code === "ENOENT" && !extname2(path3) && path3[path3.length - 1] !== sep) {
22142
22142
  return next(err);
22143
22143
  }
@@ -22152,7 +22152,7 @@ var require_send = __commonJS({
22152
22152
  }
22153
22153
  var p = path3 + "." + self._extensions[i++];
22154
22154
  debug('stat "%s"', p);
22155
- fs2.stat(p, function(err2, stat) {
22155
+ fs3.stat(p, function(err2, stat) {
22156
22156
  if (err2) return next(err2);
22157
22157
  if (stat.isDirectory()) return next();
22158
22158
  self.emit("file", p, stat);
@@ -22169,9 +22169,9 @@ var require_send = __commonJS({
22169
22169
  if (err) return self.onStatError(err);
22170
22170
  return self.error(404);
22171
22171
  }
22172
- var p = join2(path3, self._index[i]);
22172
+ var p = join3(path3, self._index[i]);
22173
22173
  debug('stat "%s"', p);
22174
- fs2.stat(p, function(err2, stat) {
22174
+ fs3.stat(p, function(err2, stat) {
22175
22175
  if (err2) return next(err2);
22176
22176
  if (stat.isDirectory()) return next();
22177
22177
  self.emit("file", p, stat);
@@ -22184,7 +22184,7 @@ var require_send = __commonJS({
22184
22184
  SendStream.prototype.stream = /* @__PURE__ */ __name(function stream(path3, options) {
22185
22185
  var self = this;
22186
22186
  var res = this.res;
22187
- var stream2 = fs2.createReadStream(path3, options);
22187
+ var stream2 = fs3.createReadStream(path3, options);
22188
22188
  this.emit("stream", stream2);
22189
22189
  stream2.pipe(res);
22190
22190
  function cleanup() {
@@ -34404,13 +34404,15 @@ var require_express2 = __commonJS({
34404
34404
  // src/index.ts
34405
34405
  var index_exports = {};
34406
34406
  __export(index_exports, {
34407
- AutoTrace: () => import_nestjs_common7.AutoTrace,
34407
+ AutoTrace: () => import_nestjs_common8.AutoTrace,
34408
34408
  CanRole: () => import_nestjs_authzpaas2.CanRole,
34409
34409
  CsrfMiddleware: () => CsrfMiddleware,
34410
34410
  CsrfTokenMiddleware: () => CsrfTokenMiddleware,
34411
34411
  DevToolsModule: () => import_nestjs_openapi_devtools2.DevToolsModule,
34412
34412
  DevToolsV2Module: () => import_nestjs_openapi_devtools2.DevToolsV2Module,
34413
34413
  FileService: () => FileService,
34414
+ HtmlHotUpdateModule: () => HtmlHotUpdateModule,
34415
+ HtmlHotUpdateService: () => HtmlHotUpdateService,
34414
34416
  PlatformHttpClientService: () => PlatformHttpClientService,
34415
34417
  PlatformModule: () => PlatformModule,
34416
34418
  StaticModule: () => StaticModule,
@@ -34422,9 +34424,9 @@ __export(index_exports, {
34422
34424
  module.exports = __toCommonJS(index_exports);
34423
34425
 
34424
34426
  // src/modules/platform/module.ts
34425
- var import_common12 = require("@nestjs/common");
34427
+ var import_common15 = require("@nestjs/common");
34426
34428
  var import_core2 = require("@nestjs/core");
34427
- var import_nestjs_common5 = require("@lark-apaas/nestjs-common");
34429
+ var import_nestjs_common6 = require("@lark-apaas/nestjs-common");
34428
34430
  var import_config2 = require("@nestjs/config");
34429
34431
  var import_nestjs_observable = require("@lark-apaas/nestjs-observable");
34430
34432
  var import_axios2 = require("@nestjs/axios");
@@ -34432,7 +34434,7 @@ var import_nestjs_logger2 = require("@lark-apaas/nestjs-logger");
34432
34434
  var import_nestjs_datapaas = require("@lark-apaas/nestjs-datapaas");
34433
34435
  var import_nestjs_authnpaas = require("@lark-apaas/nestjs-authnpaas");
34434
34436
  var import_nestjs_trigger = require("@lark-apaas/nestjs-trigger");
34435
- var import_nestjs_common6 = require("@lark-apaas/nestjs-common");
34437
+ var import_nestjs_common7 = require("@lark-apaas/nestjs-common");
34436
34438
  var import_nestjs_capability = require("@lark-apaas/nestjs-capability");
34437
34439
 
34438
34440
  // src/middlewares/user-context/index.ts
@@ -35819,7 +35821,18 @@ StaticModule = _ts_decorate11([
35819
35821
  })
35820
35822
  ], StaticModule);
35821
35823
 
35822
- // src/modules/platform/module.ts
35824
+ // src/modules/html-hot-update/html-hot-update.module.ts
35825
+ var import_common14 = require("@nestjs/common");
35826
+
35827
+ // src/modules/html-hot-update/html-hot-update.controller.ts
35828
+ var import_common13 = require("@nestjs/common");
35829
+ var import_swagger2 = require("@nestjs/swagger");
35830
+
35831
+ // src/modules/html-hot-update/html-hot-update.service.ts
35832
+ var import_common12 = require("@nestjs/common");
35833
+ var import_node_fs = require("fs");
35834
+ var import_node_path = require("path");
35835
+ var import_nestjs_common5 = require("@lark-apaas/nestjs-common");
35823
35836
  function _ts_decorate12(decorators, target, key, desc) {
35824
35837
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35825
35838
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -35827,6 +35840,291 @@ function _ts_decorate12(decorators, target, key, desc) {
35827
35840
  return c > 3 && r && Object.defineProperty(target, key, r), r;
35828
35841
  }
35829
35842
  __name(_ts_decorate12, "_ts_decorate");
35843
+ function _ts_metadata8(k, v) {
35844
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
35845
+ }
35846
+ __name(_ts_metadata8, "_ts_metadata");
35847
+ function _ts_param4(paramIndex, decorator) {
35848
+ return function(target, key) {
35849
+ decorator(target, key, paramIndex);
35850
+ };
35851
+ }
35852
+ __name(_ts_param4, "_ts_param");
35853
+ var Mutex = class Mutex2 {
35854
+ static {
35855
+ __name(this, "Mutex");
35856
+ }
35857
+ locked = false;
35858
+ waiting = [];
35859
+ async acquire() {
35860
+ if (!this.locked) {
35861
+ this.locked = true;
35862
+ return;
35863
+ }
35864
+ return new Promise((resolve2) => {
35865
+ this.waiting.push(() => {
35866
+ this.locked = true;
35867
+ resolve2();
35868
+ });
35869
+ });
35870
+ }
35871
+ release() {
35872
+ if (this.waiting.length > 0) {
35873
+ const next = this.waiting.shift();
35874
+ next();
35875
+ } else {
35876
+ this.locked = false;
35877
+ }
35878
+ }
35879
+ };
35880
+ var HtmlHotUpdateService = class _HtmlHotUpdateService {
35881
+ static {
35882
+ __name(this, "HtmlHotUpdateService");
35883
+ }
35884
+ httpClient;
35885
+ logger = new import_common12.Logger(_HtmlHotUpdateService.name);
35886
+ /** 文件锁:冷启动更新和 innerapi 主动更新共用 */
35887
+ fileLock = new Mutex();
35888
+ /** 本地 HTML 文件目录(dist/dist/client,与 build.sh [4/5] 对齐) */
35889
+ localHtmlDir = (0, import_node_path.join)(process.cwd(), "dist", "client");
35890
+ constructor(httpClient) {
35891
+ this.httpClient = httpClient;
35892
+ }
35893
+ /**
35894
+ * 任务一:冷启动时尝试更新 HTML 文件列表
35895
+ *
35896
+ * - 生产环境默认启用(模块仅在生产环境加载)
35897
+ * - 失败则降级,使用部署时打包的 HTML 正常启动
35898
+ * - 超时 3s,避免拉长冷启动耗时
35899
+ */
35900
+ async onModuleInit() {
35901
+ const appID = this.getAppID();
35902
+ if (!appID) {
35903
+ this.logger.warn("Skip HTML hot update on startup: appID not found");
35904
+ return;
35905
+ }
35906
+ try {
35907
+ this.logger.log(`Startup HTML update: appID=${appID}`);
35908
+ const result = await this.updateFromRemote(appID);
35909
+ this.logger.log(`Startup HTML update complete: ${result.updatedFiles.length} updated, ${result.failedFiles.length} failed`);
35910
+ } catch (error) {
35911
+ this.logger.warn(`Startup HTML update failed (degraded to bundled HTML): ${error instanceof Error ? error.message : String(error)}, appID=${appID}`);
35912
+ }
35913
+ }
35914
+ /**
35915
+ * 从远端获取最新 HTML 文件并更新本地
35916
+ *
35917
+ * 调用方:
35918
+ * 1) 冷启动更新(onModuleInit)
35919
+ * 2) innerapi 主动更新(controller 调用)
35920
+ */
35921
+ async updateFromRemote(appID, commitID) {
35922
+ const fileMap = await this.fetchLatestHtmlFiles(appID, commitID);
35923
+ return this.writeHtmlFiles(fileMap);
35924
+ }
35925
+ /**
35926
+ * 将文件内容写入本地 HTML 目录(带文件锁)
35927
+ */
35928
+ async writeHtmlFiles(fileMap) {
35929
+ const result = {
35930
+ updatedFiles: [],
35931
+ failedFiles: []
35932
+ };
35933
+ const entries = Object.entries(fileMap);
35934
+ if (entries.length === 0) {
35935
+ return result;
35936
+ }
35937
+ await this.fileLock.acquire();
35938
+ try {
35939
+ for (const [filePath, content] of entries) {
35940
+ try {
35941
+ const localPath = (0, import_node_path.join)(this.localHtmlDir, filePath);
35942
+ await import_node_fs.promises.mkdir((0, import_node_path.dirname)(localPath), {
35943
+ recursive: true
35944
+ });
35945
+ await import_node_fs.promises.writeFile(localPath, content, "utf-8");
35946
+ result.updatedFiles.push(filePath);
35947
+ } catch (error) {
35948
+ const reason = error instanceof Error ? error.message : String(error);
35949
+ result.failedFiles.push({
35950
+ filePath,
35951
+ reason
35952
+ });
35953
+ this.logger.warn(`Failed to write HTML file ${filePath}: ${reason}`);
35954
+ }
35955
+ }
35956
+ } finally {
35957
+ this.fileLock.release();
35958
+ }
35959
+ return result;
35960
+ }
35961
+ /**
35962
+ * 调用后端 GetLatestArtifactHtmlFiles 接口
35963
+ */
35964
+ async fetchLatestHtmlFiles(appID, commitID) {
35965
+ const url = `/v1/app/${appID}/pipeline/frontend/latest_artifact/html_files`;
35966
+ const body = {};
35967
+ if (commitID) {
35968
+ body.commitID = commitID;
35969
+ }
35970
+ const response = await this.httpClient.post(url, body, {
35971
+ timeout: 3e3
35972
+ });
35973
+ if (!response.ok) {
35974
+ throw new Error(`GetLatestArtifactHtmlFiles failed: ${response.status} ${response.statusText}`);
35975
+ }
35976
+ const data = await response.json();
35977
+ if (data.BaseResp?.StatusCode && data.BaseResp.StatusCode !== 0) {
35978
+ throw new Error(`GetLatestArtifactHtmlFiles error: ${data.BaseResp.StatusMessage}`);
35979
+ }
35980
+ if (!data.fileMap || Object.keys(data.fileMap).length === 0) {
35981
+ throw new Error("GetLatestArtifactHtmlFiles returned empty fileMap");
35982
+ }
35983
+ return data.fileMap;
35984
+ }
35985
+ /**
35986
+ * 从 CLIENT_BASE_PATH 提取 appID
35987
+ * CLIENT_BASE_PATH 格式: /app/{appId}
35988
+ */
35989
+ getAppID() {
35990
+ const basePath = process.env.CLIENT_BASE_PATH || "";
35991
+ const match = basePath.match(/\/app\/([^/]+)/);
35992
+ return match?.[1] ?? "";
35993
+ }
35994
+ };
35995
+ HtmlHotUpdateService = _ts_decorate12([
35996
+ (0, import_common12.Injectable)(),
35997
+ _ts_param4(0, (0, import_common12.Inject)(import_nestjs_common5.PLATFORM_HTTP_CLIENT)),
35998
+ _ts_metadata8("design:type", Function),
35999
+ _ts_metadata8("design:paramtypes", [
36000
+ typeof PlatformHttpClient === "undefined" ? Object : PlatformHttpClient
36001
+ ])
36002
+ ], HtmlHotUpdateService);
36003
+
36004
+ // src/modules/html-hot-update/html-hot-update.controller.ts
36005
+ function _ts_decorate13(decorators, target, key, desc) {
36006
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36007
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36008
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
36009
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36010
+ }
36011
+ __name(_ts_decorate13, "_ts_decorate");
36012
+ function _ts_metadata9(k, v) {
36013
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
36014
+ }
36015
+ __name(_ts_metadata9, "_ts_metadata");
36016
+ function _ts_param5(paramIndex, decorator) {
36017
+ return function(target, key) {
36018
+ decorator(target, key, paramIndex);
36019
+ };
36020
+ }
36021
+ __name(_ts_param5, "_ts_param");
36022
+ var UpdateHtmlFilesDto = class UpdateHtmlFilesDto2 {
36023
+ static {
36024
+ __name(this, "UpdateHtmlFilesDto");
36025
+ }
36026
+ commitID;
36027
+ appID;
36028
+ };
36029
+ var HtmlHotUpdateController = class {
36030
+ static {
36031
+ __name(this, "HtmlHotUpdateController");
36032
+ }
36033
+ htmlHotUpdateService;
36034
+ constructor(htmlHotUpdateService) {
36035
+ this.htmlHotUpdateService = htmlHotUpdateService;
36036
+ }
36037
+ /**
36038
+ * POST /__innerapi__/update_html_files
36039
+ *
36040
+ * 请求体: { commitID: string, appID: string }
36041
+ * 响应:
36042
+ * 成功: { code: 0, msg: "success" }
36043
+ * 失败: { code: -1, msg: "update html failed: <error>" }
36044
+ */
36045
+ async updateHtmlFiles(body) {
36046
+ const { commitID, appID } = body;
36047
+ if (!commitID || !appID) {
36048
+ return {
36049
+ code: -1,
36050
+ msg: "commitID and appID are required"
36051
+ };
36052
+ }
36053
+ try {
36054
+ const result = await this.htmlHotUpdateService.updateFromRemote(appID, commitID);
36055
+ if (result.updatedFiles.length === 0 && result.failedFiles.length > 0) {
36056
+ return {
36057
+ code: -1,
36058
+ msg: `update html failed: all ${result.failedFiles.length} files failed to write`
36059
+ };
36060
+ }
36061
+ return {
36062
+ code: 0,
36063
+ msg: "success"
36064
+ };
36065
+ } catch (error) {
36066
+ const errorMsg = error instanceof Error ? error.message : String(error);
36067
+ return {
36068
+ code: -1,
36069
+ msg: `update html failed: ${errorMsg}`
36070
+ };
36071
+ }
36072
+ }
36073
+ };
36074
+ _ts_decorate13([
36075
+ (0, import_common13.Post)("update_html_files"),
36076
+ (0, import_common13.HttpCode)(import_common13.HttpStatus.OK),
36077
+ _ts_param5(0, (0, import_common13.Body)()),
36078
+ _ts_metadata9("design:type", Function),
36079
+ _ts_metadata9("design:paramtypes", [
36080
+ typeof UpdateHtmlFilesDto === "undefined" ? Object : UpdateHtmlFilesDto
36081
+ ]),
36082
+ _ts_metadata9("design:returntype", Promise)
36083
+ ], HtmlHotUpdateController.prototype, "updateHtmlFiles", null);
36084
+ HtmlHotUpdateController = _ts_decorate13([
36085
+ (0, import_swagger2.ApiExcludeController)(),
36086
+ (0, import_common13.Controller)("__innerapi__"),
36087
+ _ts_metadata9("design:type", Function),
36088
+ _ts_metadata9("design:paramtypes", [
36089
+ typeof HtmlHotUpdateService === "undefined" ? Object : HtmlHotUpdateService
36090
+ ])
36091
+ ], HtmlHotUpdateController);
36092
+
36093
+ // src/modules/html-hot-update/html-hot-update.module.ts
36094
+ function _ts_decorate14(decorators, target, key, desc) {
36095
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36096
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36097
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
36098
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36099
+ }
36100
+ __name(_ts_decorate14, "_ts_decorate");
36101
+ var HtmlHotUpdateModule = class {
36102
+ static {
36103
+ __name(this, "HtmlHotUpdateModule");
36104
+ }
36105
+ };
36106
+ HtmlHotUpdateModule = _ts_decorate14([
36107
+ (0, import_common14.Module)({
36108
+ controllers: [
36109
+ HtmlHotUpdateController
36110
+ ],
36111
+ providers: [
36112
+ HtmlHotUpdateService
36113
+ ],
36114
+ exports: [
36115
+ HtmlHotUpdateService
36116
+ ]
36117
+ })
36118
+ ], HtmlHotUpdateModule);
36119
+
36120
+ // src/modules/platform/module.ts
36121
+ function _ts_decorate15(decorators, target, key, desc) {
36122
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36123
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36124
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
36125
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36126
+ }
36127
+ __name(_ts_decorate15, "_ts_decorate");
35830
36128
  var PLATFORM_MODULE_OPTIONS = /* @__PURE__ */ Symbol("PLATFORM_MODULE_OPTIONS");
35831
36129
  var PlatformModule = class _PlatformModule {
35832
36130
  static {
@@ -35848,7 +36146,7 @@ var PlatformModule = class _PlatformModule {
35848
36146
  app_config_default
35849
36147
  ]
35850
36148
  }),
35851
- import_nestjs_common5.CommonModule,
36149
+ import_nestjs_common6.CommonModule,
35852
36150
  import_nestjs_observable.NestjsObservableModule,
35853
36151
  import_nestjs_logger2.LoggerModule,
35854
36152
  import_axios2.HttpModule.register({
@@ -35897,6 +36195,11 @@ var PlatformModule = class _PlatformModule {
35897
36195
  // 开发环境由 Vite/Rspack dev server 的中间件处理 /static/* 请求
35898
36196
  ...process.env.NODE_ENV === "development" ? [] : [
35899
36197
  StaticModule
36198
+ ],
36199
+ // HTML 热更新模块(仅生产环境启用)
36200
+ // 提供 innerapi 供 tce 服务触发 HTML 热更新,冷启动时也会尝试拉取最新 HTML
36201
+ ...process.env.NODE_ENV === "development" ? [] : [
36202
+ HtmlHotUpdateModule
35900
36203
  ]
35901
36204
  ],
35902
36205
  providers: [
@@ -35906,7 +36209,7 @@ var PlatformModule = class _PlatformModule {
35906
36209
  },
35907
36210
  {
35908
36211
  provide: import_core2.APP_PIPE,
35909
- useValue: new import_common12.ValidationPipe({
36212
+ useValue: new import_common15.ValidationPipe({
35910
36213
  transform: true,
35911
36214
  transformOptions: {
35912
36215
  enableImplicitConversion: true
@@ -35914,19 +36217,19 @@ var PlatformModule = class _PlatformModule {
35914
36217
  })
35915
36218
  },
35916
36219
  {
35917
- provide: import_nestjs_common5.OBSERVABLE_SERVICE,
36220
+ provide: import_nestjs_common6.OBSERVABLE_SERVICE,
35918
36221
  useClass: import_nestjs_observable.Observable
35919
36222
  },
35920
36223
  PlatformHttpClientService,
35921
36224
  {
35922
- provide: import_nestjs_common6.PLATFORM_HTTP_CLIENT,
36225
+ provide: import_nestjs_common7.PLATFORM_HTTP_CLIENT,
35923
36226
  useFactory: /* @__PURE__ */ __name((svc) => svc.instance, "useFactory"),
35924
36227
  inject: [
35925
36228
  PlatformHttpClientService
35926
36229
  ]
35927
36230
  },
35928
36231
  {
35929
- provide: import_nestjs_common5.HTTP_CLIENT_FACTORY,
36232
+ provide: import_nestjs_common6.HTTP_CLIENT_FACTORY,
35930
36233
  useFactory: /* @__PURE__ */ __name((svc) => ({
35931
36234
  create: /* @__PURE__ */ __name((options2) => svc.createWithGlobalInterceptors(options2), "create")
35932
36235
  }), "useFactory"),
@@ -35944,10 +36247,10 @@ var PlatformModule = class _PlatformModule {
35944
36247
  exports: [
35945
36248
  import_config2.ConfigModule,
35946
36249
  import_nestjs_logger2.LoggerModule,
35947
- import_nestjs_common5.CommonModule,
35948
- import_nestjs_common5.OBSERVABLE_SERVICE,
35949
- import_nestjs_common6.PLATFORM_HTTP_CLIENT,
35950
- import_nestjs_common5.HTTP_CLIENT_FACTORY,
36250
+ import_nestjs_common6.CommonModule,
36251
+ import_nestjs_common6.OBSERVABLE_SERVICE,
36252
+ import_nestjs_common7.PLATFORM_HTTP_CLIENT,
36253
+ import_nestjs_common6.HTTP_CLIENT_FACTORY,
35951
36254
  PlatformHttpClientService,
35952
36255
  import_nestjs_capability.CapabilityModule,
35953
36256
  FileService
@@ -35979,9 +36282,9 @@ var PlatformModule = class _PlatformModule {
35979
36282
  }
35980
36283
  }
35981
36284
  };
35982
- PlatformModule = _ts_decorate12([
35983
- (0, import_common12.Global)(),
35984
- (0, import_common12.Module)({})
36285
+ PlatformModule = _ts_decorate15([
36286
+ (0, import_common15.Global)(),
36287
+ (0, import_common15.Module)({})
35985
36288
  ], PlatformModule);
35986
36289
 
35987
36290
  // src/setup.ts
@@ -36069,7 +36372,7 @@ __reExport(index_exports, require("@lark-apaas/nestjs-datapaas"), module.exports
36069
36372
  __reExport(index_exports, require("@lark-apaas/nestjs-observable"), module.exports);
36070
36373
  __reExport(index_exports, require("@lark-apaas/nestjs-trigger"), module.exports);
36071
36374
  __reExport(index_exports, require("@lark-apaas/file-service"), module.exports);
36072
- var import_nestjs_common7 = require("@lark-apaas/nestjs-common");
36375
+ var import_nestjs_common8 = require("@lark-apaas/nestjs-common");
36073
36376
  var import_nestjs_authzpaas2 = require("@lark-apaas/nestjs-authzpaas");
36074
36377
  // Annotate the CommonJS export names for ESM import in node:
36075
36378
  0 && (module.exports = {
@@ -36080,6 +36383,8 @@ var import_nestjs_authzpaas2 = require("@lark-apaas/nestjs-authzpaas");
36080
36383
  DevToolsModule,
36081
36384
  DevToolsV2Module,
36082
36385
  FileService,
36386
+ HtmlHotUpdateModule,
36387
+ HtmlHotUpdateService,
36083
36388
  PlatformHttpClientService,
36084
36389
  PlatformModule,
36085
36390
  StaticModule,
package/dist/index.d.cts CHANGED
@@ -1,10 +1,10 @@
1
- import { NestModule, DynamicModule, MiddlewareConsumer, NestMiddleware } from '@nestjs/common';
1
+ import { NestModule, DynamicModule, MiddlewareConsumer, OnModuleInit, NestMiddleware } from '@nestjs/common';
2
2
  import { HttpClientConfig, PlatformPluginOptions, HttpClient } from '@lark-apaas/http-client';
3
3
  import { NestExpressApplication } from '@nestjs/platform-express';
4
- export { DevToolsModule, DevToolsOptions, DevToolsV2Module, DevToolsV2Options } from '@lark-apaas/nestjs-openapi-devtools';
5
- import { Request, Response, NextFunction } from 'express';
6
4
  import { PlatformHttpClient, RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';
7
5
  export { AutoTrace } from '@lark-apaas/nestjs-common';
6
+ export { DevToolsModule, DevToolsOptions, DevToolsV2Module, DevToolsV2Options } from '@lark-apaas/nestjs-openapi-devtools';
7
+ import { Request, Response, NextFunction } from 'express';
8
8
  import * as _lark_apaas_file_service from '@lark-apaas/file-service';
9
9
  import { FileBody, UploadOptions, FileDownloadBuilder, SearchOptions } from '@lark-apaas/file-service';
10
10
  export * from '@lark-apaas/file-service';
@@ -123,6 +123,67 @@ declare function configureApp(app: NestExpressApplication, perms?: {
123
123
  declare class StaticModule {
124
124
  }
125
125
 
126
+ /**
127
+ * HTML 热更新模块
128
+ *
129
+ * 提供两个能力:
130
+ * 1. 冷启动时主动拉取最新 HTML 文件列表并替换本地文件(任务一)
131
+ * 2. 对外暴露 innerapi 接口,供 tce 服务主动触发热更新(任务二)
132
+ *
133
+ * 仅在生产环境加载(PlatformModule 中控制),两个能力默认都启用。
134
+ */
135
+ declare class HtmlHotUpdateModule {
136
+ }
137
+
138
+ /**
139
+ * HTML 热更新结果
140
+ */
141
+ interface HtmlHotUpdateResult {
142
+ updatedFiles: string[];
143
+ failedFiles: {
144
+ filePath: string;
145
+ reason: string;
146
+ }[];
147
+ }
148
+ declare class HtmlHotUpdateService implements OnModuleInit {
149
+ private readonly httpClient;
150
+ private readonly logger;
151
+ /** 文件锁:冷启动更新和 innerapi 主动更新共用 */
152
+ private readonly fileLock;
153
+ /** 本地 HTML 文件目录(dist/dist/client,与 build.sh [4/5] 对齐) */
154
+ private readonly localHtmlDir;
155
+ constructor(httpClient: PlatformHttpClient);
156
+ /**
157
+ * 任务一:冷启动时尝试更新 HTML 文件列表
158
+ *
159
+ * - 生产环境默认启用(模块仅在生产环境加载)
160
+ * - 失败则降级,使用部署时打包的 HTML 正常启动
161
+ * - 超时 3s,避免拉长冷启动耗时
162
+ */
163
+ onModuleInit(): Promise<void>;
164
+ /**
165
+ * 从远端获取最新 HTML 文件并更新本地
166
+ *
167
+ * 调用方:
168
+ * 1) 冷启动更新(onModuleInit)
169
+ * 2) innerapi 主动更新(controller 调用)
170
+ */
171
+ updateFromRemote(appID: string, commitID?: string): Promise<HtmlHotUpdateResult>;
172
+ /**
173
+ * 将文件内容写入本地 HTML 目录(带文件锁)
174
+ */
175
+ writeHtmlFiles(fileMap: Record<string, string>): Promise<HtmlHotUpdateResult>;
176
+ /**
177
+ * 调用后端 GetLatestArtifactHtmlFiles 接口
178
+ */
179
+ private fetchLatestHtmlFiles;
180
+ /**
181
+ * 从 CLIENT_BASE_PATH 提取 appID
182
+ * CLIENT_BASE_PATH 格式: /app/{appId}
183
+ */
184
+ private getAppID;
185
+ }
186
+
126
187
  interface CsrfTokenOptions {
127
188
  cookieKey?: string;
128
189
  cookieMaxAge?: number;
@@ -354,4 +415,4 @@ declare class PlatformHttpClientService {
354
415
  private registerInterceptorsForClient;
355
416
  }
356
417
 
357
- export { type ApiNotFoundResponse, CsrfMiddleware, CsrfTokenMiddleware, FileService, type PlatformHttpClientOptions, PlatformHttpClientService, PlatformModule, type PlatformModuleOptions, StaticModule, UserContextMiddleware, ViewContextMiddleware, configureApp, createLegacyPathRedirectMiddleware };
418
+ export { type ApiNotFoundResponse, CsrfMiddleware, CsrfTokenMiddleware, FileService, HtmlHotUpdateModule, HtmlHotUpdateService, type PlatformHttpClientOptions, PlatformHttpClientService, PlatformModule, type PlatformModuleOptions, StaticModule, UserContextMiddleware, ViewContextMiddleware, configureApp, createLegacyPathRedirectMiddleware };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { NestModule, DynamicModule, MiddlewareConsumer, NestMiddleware } from '@nestjs/common';
1
+ import { NestModule, DynamicModule, MiddlewareConsumer, OnModuleInit, NestMiddleware } from '@nestjs/common';
2
2
  import { HttpClientConfig, PlatformPluginOptions, HttpClient } from '@lark-apaas/http-client';
3
3
  import { NestExpressApplication } from '@nestjs/platform-express';
4
- export { DevToolsModule, DevToolsOptions, DevToolsV2Module, DevToolsV2Options } from '@lark-apaas/nestjs-openapi-devtools';
5
- import { Request, Response, NextFunction } from 'express';
6
4
  import { PlatformHttpClient, RequestContextService, ObservableService } from '@lark-apaas/nestjs-common';
7
5
  export { AutoTrace } from '@lark-apaas/nestjs-common';
6
+ export { DevToolsModule, DevToolsOptions, DevToolsV2Module, DevToolsV2Options } from '@lark-apaas/nestjs-openapi-devtools';
7
+ import { Request, Response, NextFunction } from 'express';
8
8
  import * as _lark_apaas_file_service from '@lark-apaas/file-service';
9
9
  import { FileBody, UploadOptions, FileDownloadBuilder, SearchOptions } from '@lark-apaas/file-service';
10
10
  export * from '@lark-apaas/file-service';
@@ -123,6 +123,67 @@ declare function configureApp(app: NestExpressApplication, perms?: {
123
123
  declare class StaticModule {
124
124
  }
125
125
 
126
+ /**
127
+ * HTML 热更新模块
128
+ *
129
+ * 提供两个能力:
130
+ * 1. 冷启动时主动拉取最新 HTML 文件列表并替换本地文件(任务一)
131
+ * 2. 对外暴露 innerapi 接口,供 tce 服务主动触发热更新(任务二)
132
+ *
133
+ * 仅在生产环境加载(PlatformModule 中控制),两个能力默认都启用。
134
+ */
135
+ declare class HtmlHotUpdateModule {
136
+ }
137
+
138
+ /**
139
+ * HTML 热更新结果
140
+ */
141
+ interface HtmlHotUpdateResult {
142
+ updatedFiles: string[];
143
+ failedFiles: {
144
+ filePath: string;
145
+ reason: string;
146
+ }[];
147
+ }
148
+ declare class HtmlHotUpdateService implements OnModuleInit {
149
+ private readonly httpClient;
150
+ private readonly logger;
151
+ /** 文件锁:冷启动更新和 innerapi 主动更新共用 */
152
+ private readonly fileLock;
153
+ /** 本地 HTML 文件目录(dist/dist/client,与 build.sh [4/5] 对齐) */
154
+ private readonly localHtmlDir;
155
+ constructor(httpClient: PlatformHttpClient);
156
+ /**
157
+ * 任务一:冷启动时尝试更新 HTML 文件列表
158
+ *
159
+ * - 生产环境默认启用(模块仅在生产环境加载)
160
+ * - 失败则降级,使用部署时打包的 HTML 正常启动
161
+ * - 超时 3s,避免拉长冷启动耗时
162
+ */
163
+ onModuleInit(): Promise<void>;
164
+ /**
165
+ * 从远端获取最新 HTML 文件并更新本地
166
+ *
167
+ * 调用方:
168
+ * 1) 冷启动更新(onModuleInit)
169
+ * 2) innerapi 主动更新(controller 调用)
170
+ */
171
+ updateFromRemote(appID: string, commitID?: string): Promise<HtmlHotUpdateResult>;
172
+ /**
173
+ * 将文件内容写入本地 HTML 目录(带文件锁)
174
+ */
175
+ writeHtmlFiles(fileMap: Record<string, string>): Promise<HtmlHotUpdateResult>;
176
+ /**
177
+ * 调用后端 GetLatestArtifactHtmlFiles 接口
178
+ */
179
+ private fetchLatestHtmlFiles;
180
+ /**
181
+ * 从 CLIENT_BASE_PATH 提取 appID
182
+ * CLIENT_BASE_PATH 格式: /app/{appId}
183
+ */
184
+ private getAppID;
185
+ }
186
+
126
187
  interface CsrfTokenOptions {
127
188
  cookieKey?: string;
128
189
  cookieMaxAge?: number;
@@ -354,4 +415,4 @@ declare class PlatformHttpClientService {
354
415
  private registerInterceptorsForClient;
355
416
  }
356
417
 
357
- export { type ApiNotFoundResponse, CsrfMiddleware, CsrfTokenMiddleware, FileService, type PlatformHttpClientOptions, PlatformHttpClientService, PlatformModule, type PlatformModuleOptions, StaticModule, UserContextMiddleware, ViewContextMiddleware, configureApp, createLegacyPathRedirectMiddleware };
418
+ export { type ApiNotFoundResponse, CsrfMiddleware, CsrfTokenMiddleware, FileService, HtmlHotUpdateModule, HtmlHotUpdateService, type PlatformHttpClientOptions, PlatformHttpClientService, PlatformModule, type PlatformModuleOptions, StaticModule, UserContextMiddleware, ViewContextMiddleware, configureApp, createLegacyPathRedirectMiddleware };
package/dist/index.js CHANGED
@@ -1370,8 +1370,8 @@ var require_node = __commonJS({
1370
1370
  }
1371
1371
  break;
1372
1372
  case "FILE":
1373
- var fs2 = __require("fs");
1374
- stream2 = new fs2.SyncWriteStream(fd2, {
1373
+ var fs3 = __require("fs");
1374
+ stream2 = new fs3.SyncWriteStream(fd2, {
1375
1375
  autoClose: false
1376
1376
  });
1377
1377
  stream2._type = "fs";
@@ -18068,8 +18068,8 @@ var require_node2 = __commonJS({
18068
18068
  }
18069
18069
  break;
18070
18070
  case "FILE":
18071
- var fs2 = __require("fs");
18072
- stream2 = new fs2.SyncWriteStream(fd2, {
18071
+ var fs3 = __require("fs");
18072
+ stream2 = new fs3.SyncWriteStream(fd2, {
18073
18073
  autoClose: false
18074
18074
  });
18075
18075
  stream2._type = "fs";
@@ -18838,8 +18838,8 @@ var require_node3 = __commonJS({
18838
18838
  }
18839
18839
  break;
18840
18840
  case "FILE":
18841
- var fs2 = __require("fs");
18842
- stream2 = new fs2.SyncWriteStream(fd2, {
18841
+ var fs3 = __require("fs");
18842
+ stream2 = new fs3.SyncWriteStream(fd2, {
18843
18843
  autoClose: false
18844
18844
  });
18845
18845
  stream2._type = "fs";
@@ -20633,11 +20633,11 @@ var require_view = __commonJS({
20633
20633
  "use strict";
20634
20634
  var debug = require_src3()("express:view");
20635
20635
  var path2 = __require("path");
20636
- var fs2 = __require("fs");
20637
- var dirname = path2.dirname;
20636
+ var fs3 = __require("fs");
20637
+ var dirname2 = path2.dirname;
20638
20638
  var basename = path2.basename;
20639
20639
  var extname2 = path2.extname;
20640
- var join2 = path2.join;
20640
+ var join3 = path2.join;
20641
20641
  var resolve2 = path2.resolve;
20642
20642
  module.exports = View;
20643
20643
  function View(name, options) {
@@ -20674,7 +20674,7 @@ var require_view = __commonJS({
20674
20674
  for (var i = 0; i < roots.length && !path3; i++) {
20675
20675
  var root = roots[i];
20676
20676
  var loc = resolve2(root, name);
20677
- var dir = dirname(loc);
20677
+ var dir = dirname2(loc);
20678
20678
  var file = basename(loc);
20679
20679
  path3 = this.resolve(dir, file);
20680
20680
  }
@@ -20686,12 +20686,12 @@ var require_view = __commonJS({
20686
20686
  }, "render");
20687
20687
  View.prototype.resolve = /* @__PURE__ */ __name(function resolve3(dir, file) {
20688
20688
  var ext = this.ext;
20689
- var path3 = join2(dir, file);
20689
+ var path3 = join3(dir, file);
20690
20690
  var stat = tryStat(path3);
20691
20691
  if (stat && stat.isFile()) {
20692
20692
  return path3;
20693
20693
  }
20694
- path3 = join2(dir, basename(file, ext), "index" + ext);
20694
+ path3 = join3(dir, basename(file, ext), "index" + ext);
20695
20695
  stat = tryStat(path3);
20696
20696
  if (stat && stat.isFile()) {
20697
20697
  return path3;
@@ -20700,7 +20700,7 @@ var require_view = __commonJS({
20700
20700
  function tryStat(path3) {
20701
20701
  debug('stat "%s"', path3);
20702
20702
  try {
20703
- return fs2.statSync(path3);
20703
+ return fs3.statSync(path3);
20704
20704
  } catch (e) {
20705
20705
  return void 0;
20706
20706
  }
@@ -21349,8 +21349,8 @@ var require_node4 = __commonJS({
21349
21349
  }
21350
21350
  break;
21351
21351
  case "FILE":
21352
- var fs2 = __require("fs");
21353
- stream2 = new fs2.SyncWriteStream(fd2, {
21352
+ var fs3 = __require("fs");
21353
+ stream2 = new fs3.SyncWriteStream(fd2, {
21354
21354
  autoClose: false
21355
21355
  });
21356
21356
  stream2._type = "fs";
@@ -21536,7 +21536,7 @@ var require_mime = __commonJS({
21536
21536
  "../../../node_modules/mime/mime.js"(exports, module) {
21537
21537
  "use strict";
21538
21538
  var path2 = __require("path");
21539
- var fs2 = __require("fs");
21539
+ var fs3 = __require("fs");
21540
21540
  function Mime() {
21541
21541
  this.types = /* @__PURE__ */ Object.create(null);
21542
21542
  this.extensions = /* @__PURE__ */ Object.create(null);
@@ -21558,7 +21558,7 @@ var require_mime = __commonJS({
21558
21558
  };
21559
21559
  Mime.prototype.load = function(file) {
21560
21560
  this._loading = file;
21561
- var map = {}, content = fs2.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
21561
+ var map = {}, content = fs3.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
21562
21562
  lines.forEach(function(line) {
21563
21563
  var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
21564
21564
  map[fields.shift()] = fields;
@@ -21803,7 +21803,7 @@ var require_send = __commonJS({
21803
21803
  var escapeHtml = require_escape_html();
21804
21804
  var etag = require_etag();
21805
21805
  var fresh = require_fresh();
21806
- var fs2 = __require("fs");
21806
+ var fs3 = __require("fs");
21807
21807
  var mime = require_mime();
21808
21808
  var ms = require_ms5();
21809
21809
  var onFinished = require_on_finished();
@@ -21813,7 +21813,7 @@ var require_send = __commonJS({
21813
21813
  var Stream = __require("stream");
21814
21814
  var util = __require("util");
21815
21815
  var extname2 = path2.extname;
21816
- var join2 = path2.join;
21816
+ var join3 = path2.join;
21817
21817
  var normalize2 = path2.normalize;
21818
21818
  var resolve2 = path2.resolve;
21819
21819
  var sep = path2.sep;
@@ -22030,7 +22030,7 @@ var require_send = __commonJS({
22030
22030
  return res;
22031
22031
  }
22032
22032
  parts = path3.split(sep);
22033
- path3 = normalize2(join2(root, path3));
22033
+ path3 = normalize2(join3(root, path3));
22034
22034
  } else {
22035
22035
  if (UP_PATH_REGEXP.test(path3)) {
22036
22036
  debug('malicious path "%s"', path3);
@@ -22136,7 +22136,7 @@ var require_send = __commonJS({
22136
22136
  var i = 0;
22137
22137
  var self = this;
22138
22138
  debug('stat "%s"', path3);
22139
- fs2.stat(path3, /* @__PURE__ */ __name(function onstat(err, stat) {
22139
+ fs3.stat(path3, /* @__PURE__ */ __name(function onstat(err, stat) {
22140
22140
  if (err && err.code === "ENOENT" && !extname2(path3) && path3[path3.length - 1] !== sep) {
22141
22141
  return next(err);
22142
22142
  }
@@ -22151,7 +22151,7 @@ var require_send = __commonJS({
22151
22151
  }
22152
22152
  var p = path3 + "." + self._extensions[i++];
22153
22153
  debug('stat "%s"', p);
22154
- fs2.stat(p, function(err2, stat) {
22154
+ fs3.stat(p, function(err2, stat) {
22155
22155
  if (err2) return next(err2);
22156
22156
  if (stat.isDirectory()) return next();
22157
22157
  self.emit("file", p, stat);
@@ -22168,9 +22168,9 @@ var require_send = __commonJS({
22168
22168
  if (err) return self.onStatError(err);
22169
22169
  return self.error(404);
22170
22170
  }
22171
- var p = join2(path3, self._index[i]);
22171
+ var p = join3(path3, self._index[i]);
22172
22172
  debug('stat "%s"', p);
22173
- fs2.stat(p, function(err2, stat) {
22173
+ fs3.stat(p, function(err2, stat) {
22174
22174
  if (err2) return next(err2);
22175
22175
  if (stat.isDirectory()) return next();
22176
22176
  self.emit("file", p, stat);
@@ -22183,7 +22183,7 @@ var require_send = __commonJS({
22183
22183
  SendStream.prototype.stream = /* @__PURE__ */ __name(function stream(path3, options) {
22184
22184
  var self = this;
22185
22185
  var res = this.res;
22186
- var stream2 = fs2.createReadStream(path3, options);
22186
+ var stream2 = fs3.createReadStream(path3, options);
22187
22187
  this.emit("stream", stream2);
22188
22188
  stream2.pipe(res);
22189
22189
  function cleanup() {
@@ -34401,7 +34401,7 @@ var require_express2 = __commonJS({
34401
34401
  });
34402
34402
 
34403
34403
  // src/modules/platform/module.ts
34404
- import { Global, Module as Module2, ValidationPipe } from "@nestjs/common";
34404
+ import { Global, Module as Module3, ValidationPipe } from "@nestjs/common";
34405
34405
  import { APP_INTERCEPTOR, APP_PIPE } from "@nestjs/core";
34406
34406
  import { CommonModule, OBSERVABLE_SERVICE as OBSERVABLE_SERVICE2, HTTP_CLIENT_FACTORY } from "@lark-apaas/nestjs-common";
34407
34407
  import { ConfigModule, ConfigService } from "@nestjs/config";
@@ -34411,7 +34411,7 @@ import { LoggerModule, AppLogger as AppLogger2, LoggerContextMiddleware } from "
34411
34411
  import { DataPaasModule, SqlExecutionContextMiddleware } from "@lark-apaas/nestjs-datapaas";
34412
34412
  import { AuthNPaasModule } from "@lark-apaas/nestjs-authnpaas";
34413
34413
  import { AutomationModule } from "@lark-apaas/nestjs-trigger";
34414
- import { PLATFORM_HTTP_CLIENT as PLATFORM_HTTP_CLIENT3 } from "@lark-apaas/nestjs-common";
34414
+ import { PLATFORM_HTTP_CLIENT as PLATFORM_HTTP_CLIENT4 } from "@lark-apaas/nestjs-common";
34415
34415
  import { CapabilityModule } from "@lark-apaas/nestjs-capability";
34416
34416
 
34417
34417
  // src/middlewares/user-context/index.ts
@@ -35798,7 +35798,18 @@ StaticModule = _ts_decorate11([
35798
35798
  })
35799
35799
  ], StaticModule);
35800
35800
 
35801
- // src/modules/platform/module.ts
35801
+ // src/modules/html-hot-update/html-hot-update.module.ts
35802
+ import { Module as Module2 } from "@nestjs/common";
35803
+
35804
+ // src/modules/html-hot-update/html-hot-update.controller.ts
35805
+ import { Controller as Controller2, Post, Body, HttpCode, HttpStatus as HttpStatus2 } from "@nestjs/common";
35806
+ import { ApiExcludeController as ApiExcludeController2 } from "@nestjs/swagger";
35807
+
35808
+ // src/modules/html-hot-update/html-hot-update.service.ts
35809
+ import { Injectable as Injectable10, Logger as Logger5, Inject as Inject3 } from "@nestjs/common";
35810
+ import { promises as fs2 } from "fs";
35811
+ import { join as join2, dirname } from "path";
35812
+ import { PLATFORM_HTTP_CLIENT as PLATFORM_HTTP_CLIENT3 } from "@lark-apaas/nestjs-common";
35802
35813
  function _ts_decorate12(decorators, target, key, desc) {
35803
35814
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35804
35815
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -35806,6 +35817,291 @@ function _ts_decorate12(decorators, target, key, desc) {
35806
35817
  return c > 3 && r && Object.defineProperty(target, key, r), r;
35807
35818
  }
35808
35819
  __name(_ts_decorate12, "_ts_decorate");
35820
+ function _ts_metadata8(k, v) {
35821
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
35822
+ }
35823
+ __name(_ts_metadata8, "_ts_metadata");
35824
+ function _ts_param4(paramIndex, decorator) {
35825
+ return function(target, key) {
35826
+ decorator(target, key, paramIndex);
35827
+ };
35828
+ }
35829
+ __name(_ts_param4, "_ts_param");
35830
+ var Mutex = class Mutex2 {
35831
+ static {
35832
+ __name(this, "Mutex");
35833
+ }
35834
+ locked = false;
35835
+ waiting = [];
35836
+ async acquire() {
35837
+ if (!this.locked) {
35838
+ this.locked = true;
35839
+ return;
35840
+ }
35841
+ return new Promise((resolve2) => {
35842
+ this.waiting.push(() => {
35843
+ this.locked = true;
35844
+ resolve2();
35845
+ });
35846
+ });
35847
+ }
35848
+ release() {
35849
+ if (this.waiting.length > 0) {
35850
+ const next = this.waiting.shift();
35851
+ next();
35852
+ } else {
35853
+ this.locked = false;
35854
+ }
35855
+ }
35856
+ };
35857
+ var HtmlHotUpdateService = class _HtmlHotUpdateService {
35858
+ static {
35859
+ __name(this, "HtmlHotUpdateService");
35860
+ }
35861
+ httpClient;
35862
+ logger = new Logger5(_HtmlHotUpdateService.name);
35863
+ /** 文件锁:冷启动更新和 innerapi 主动更新共用 */
35864
+ fileLock = new Mutex();
35865
+ /** 本地 HTML 文件目录(dist/dist/client,与 build.sh [4/5] 对齐) */
35866
+ localHtmlDir = join2(process.cwd(), "dist", "client");
35867
+ constructor(httpClient) {
35868
+ this.httpClient = httpClient;
35869
+ }
35870
+ /**
35871
+ * 任务一:冷启动时尝试更新 HTML 文件列表
35872
+ *
35873
+ * - 生产环境默认启用(模块仅在生产环境加载)
35874
+ * - 失败则降级,使用部署时打包的 HTML 正常启动
35875
+ * - 超时 3s,避免拉长冷启动耗时
35876
+ */
35877
+ async onModuleInit() {
35878
+ const appID = this.getAppID();
35879
+ if (!appID) {
35880
+ this.logger.warn("Skip HTML hot update on startup: appID not found");
35881
+ return;
35882
+ }
35883
+ try {
35884
+ this.logger.log(`Startup HTML update: appID=${appID}`);
35885
+ const result = await this.updateFromRemote(appID);
35886
+ this.logger.log(`Startup HTML update complete: ${result.updatedFiles.length} updated, ${result.failedFiles.length} failed`);
35887
+ } catch (error) {
35888
+ this.logger.warn(`Startup HTML update failed (degraded to bundled HTML): ${error instanceof Error ? error.message : String(error)}, appID=${appID}`);
35889
+ }
35890
+ }
35891
+ /**
35892
+ * 从远端获取最新 HTML 文件并更新本地
35893
+ *
35894
+ * 调用方:
35895
+ * 1) 冷启动更新(onModuleInit)
35896
+ * 2) innerapi 主动更新(controller 调用)
35897
+ */
35898
+ async updateFromRemote(appID, commitID) {
35899
+ const fileMap = await this.fetchLatestHtmlFiles(appID, commitID);
35900
+ return this.writeHtmlFiles(fileMap);
35901
+ }
35902
+ /**
35903
+ * 将文件内容写入本地 HTML 目录(带文件锁)
35904
+ */
35905
+ async writeHtmlFiles(fileMap) {
35906
+ const result = {
35907
+ updatedFiles: [],
35908
+ failedFiles: []
35909
+ };
35910
+ const entries = Object.entries(fileMap);
35911
+ if (entries.length === 0) {
35912
+ return result;
35913
+ }
35914
+ await this.fileLock.acquire();
35915
+ try {
35916
+ for (const [filePath, content] of entries) {
35917
+ try {
35918
+ const localPath = join2(this.localHtmlDir, filePath);
35919
+ await fs2.mkdir(dirname(localPath), {
35920
+ recursive: true
35921
+ });
35922
+ await fs2.writeFile(localPath, content, "utf-8");
35923
+ result.updatedFiles.push(filePath);
35924
+ } catch (error) {
35925
+ const reason = error instanceof Error ? error.message : String(error);
35926
+ result.failedFiles.push({
35927
+ filePath,
35928
+ reason
35929
+ });
35930
+ this.logger.warn(`Failed to write HTML file ${filePath}: ${reason}`);
35931
+ }
35932
+ }
35933
+ } finally {
35934
+ this.fileLock.release();
35935
+ }
35936
+ return result;
35937
+ }
35938
+ /**
35939
+ * 调用后端 GetLatestArtifactHtmlFiles 接口
35940
+ */
35941
+ async fetchLatestHtmlFiles(appID, commitID) {
35942
+ const url = `/v1/app/${appID}/pipeline/frontend/latest_artifact/html_files`;
35943
+ const body = {};
35944
+ if (commitID) {
35945
+ body.commitID = commitID;
35946
+ }
35947
+ const response = await this.httpClient.post(url, body, {
35948
+ timeout: 3e3
35949
+ });
35950
+ if (!response.ok) {
35951
+ throw new Error(`GetLatestArtifactHtmlFiles failed: ${response.status} ${response.statusText}`);
35952
+ }
35953
+ const data = await response.json();
35954
+ if (data.BaseResp?.StatusCode && data.BaseResp.StatusCode !== 0) {
35955
+ throw new Error(`GetLatestArtifactHtmlFiles error: ${data.BaseResp.StatusMessage}`);
35956
+ }
35957
+ if (!data.fileMap || Object.keys(data.fileMap).length === 0) {
35958
+ throw new Error("GetLatestArtifactHtmlFiles returned empty fileMap");
35959
+ }
35960
+ return data.fileMap;
35961
+ }
35962
+ /**
35963
+ * 从 CLIENT_BASE_PATH 提取 appID
35964
+ * CLIENT_BASE_PATH 格式: /app/{appId}
35965
+ */
35966
+ getAppID() {
35967
+ const basePath = process.env.CLIENT_BASE_PATH || "";
35968
+ const match = basePath.match(/\/app\/([^/]+)/);
35969
+ return match?.[1] ?? "";
35970
+ }
35971
+ };
35972
+ HtmlHotUpdateService = _ts_decorate12([
35973
+ Injectable10(),
35974
+ _ts_param4(0, Inject3(PLATFORM_HTTP_CLIENT3)),
35975
+ _ts_metadata8("design:type", Function),
35976
+ _ts_metadata8("design:paramtypes", [
35977
+ typeof PlatformHttpClient === "undefined" ? Object : PlatformHttpClient
35978
+ ])
35979
+ ], HtmlHotUpdateService);
35980
+
35981
+ // src/modules/html-hot-update/html-hot-update.controller.ts
35982
+ function _ts_decorate13(decorators, target, key, desc) {
35983
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
35984
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
35985
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
35986
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
35987
+ }
35988
+ __name(_ts_decorate13, "_ts_decorate");
35989
+ function _ts_metadata9(k, v) {
35990
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
35991
+ }
35992
+ __name(_ts_metadata9, "_ts_metadata");
35993
+ function _ts_param5(paramIndex, decorator) {
35994
+ return function(target, key) {
35995
+ decorator(target, key, paramIndex);
35996
+ };
35997
+ }
35998
+ __name(_ts_param5, "_ts_param");
35999
+ var UpdateHtmlFilesDto = class UpdateHtmlFilesDto2 {
36000
+ static {
36001
+ __name(this, "UpdateHtmlFilesDto");
36002
+ }
36003
+ commitID;
36004
+ appID;
36005
+ };
36006
+ var HtmlHotUpdateController = class {
36007
+ static {
36008
+ __name(this, "HtmlHotUpdateController");
36009
+ }
36010
+ htmlHotUpdateService;
36011
+ constructor(htmlHotUpdateService) {
36012
+ this.htmlHotUpdateService = htmlHotUpdateService;
36013
+ }
36014
+ /**
36015
+ * POST /__innerapi__/update_html_files
36016
+ *
36017
+ * 请求体: { commitID: string, appID: string }
36018
+ * 响应:
36019
+ * 成功: { code: 0, msg: "success" }
36020
+ * 失败: { code: -1, msg: "update html failed: <error>" }
36021
+ */
36022
+ async updateHtmlFiles(body) {
36023
+ const { commitID, appID } = body;
36024
+ if (!commitID || !appID) {
36025
+ return {
36026
+ code: -1,
36027
+ msg: "commitID and appID are required"
36028
+ };
36029
+ }
36030
+ try {
36031
+ const result = await this.htmlHotUpdateService.updateFromRemote(appID, commitID);
36032
+ if (result.updatedFiles.length === 0 && result.failedFiles.length > 0) {
36033
+ return {
36034
+ code: -1,
36035
+ msg: `update html failed: all ${result.failedFiles.length} files failed to write`
36036
+ };
36037
+ }
36038
+ return {
36039
+ code: 0,
36040
+ msg: "success"
36041
+ };
36042
+ } catch (error) {
36043
+ const errorMsg = error instanceof Error ? error.message : String(error);
36044
+ return {
36045
+ code: -1,
36046
+ msg: `update html failed: ${errorMsg}`
36047
+ };
36048
+ }
36049
+ }
36050
+ };
36051
+ _ts_decorate13([
36052
+ Post("update_html_files"),
36053
+ HttpCode(HttpStatus2.OK),
36054
+ _ts_param5(0, Body()),
36055
+ _ts_metadata9("design:type", Function),
36056
+ _ts_metadata9("design:paramtypes", [
36057
+ typeof UpdateHtmlFilesDto === "undefined" ? Object : UpdateHtmlFilesDto
36058
+ ]),
36059
+ _ts_metadata9("design:returntype", Promise)
36060
+ ], HtmlHotUpdateController.prototype, "updateHtmlFiles", null);
36061
+ HtmlHotUpdateController = _ts_decorate13([
36062
+ ApiExcludeController2(),
36063
+ Controller2("__innerapi__"),
36064
+ _ts_metadata9("design:type", Function),
36065
+ _ts_metadata9("design:paramtypes", [
36066
+ typeof HtmlHotUpdateService === "undefined" ? Object : HtmlHotUpdateService
36067
+ ])
36068
+ ], HtmlHotUpdateController);
36069
+
36070
+ // src/modules/html-hot-update/html-hot-update.module.ts
36071
+ function _ts_decorate14(decorators, target, key, desc) {
36072
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36073
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36074
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
36075
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36076
+ }
36077
+ __name(_ts_decorate14, "_ts_decorate");
36078
+ var HtmlHotUpdateModule = class {
36079
+ static {
36080
+ __name(this, "HtmlHotUpdateModule");
36081
+ }
36082
+ };
36083
+ HtmlHotUpdateModule = _ts_decorate14([
36084
+ Module2({
36085
+ controllers: [
36086
+ HtmlHotUpdateController
36087
+ ],
36088
+ providers: [
36089
+ HtmlHotUpdateService
36090
+ ],
36091
+ exports: [
36092
+ HtmlHotUpdateService
36093
+ ]
36094
+ })
36095
+ ], HtmlHotUpdateModule);
36096
+
36097
+ // src/modules/platform/module.ts
36098
+ function _ts_decorate15(decorators, target, key, desc) {
36099
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
36100
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
36101
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
36102
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36103
+ }
36104
+ __name(_ts_decorate15, "_ts_decorate");
35809
36105
  var PLATFORM_MODULE_OPTIONS = /* @__PURE__ */ Symbol("PLATFORM_MODULE_OPTIONS");
35810
36106
  var PlatformModule = class _PlatformModule {
35811
36107
  static {
@@ -35876,6 +36172,11 @@ var PlatformModule = class _PlatformModule {
35876
36172
  // 开发环境由 Vite/Rspack dev server 的中间件处理 /static/* 请求
35877
36173
  ...process.env.NODE_ENV === "development" ? [] : [
35878
36174
  StaticModule
36175
+ ],
36176
+ // HTML 热更新模块(仅生产环境启用)
36177
+ // 提供 innerapi 供 tce 服务触发 HTML 热更新,冷启动时也会尝试拉取最新 HTML
36178
+ ...process.env.NODE_ENV === "development" ? [] : [
36179
+ HtmlHotUpdateModule
35879
36180
  ]
35880
36181
  ],
35881
36182
  providers: [
@@ -35898,7 +36199,7 @@ var PlatformModule = class _PlatformModule {
35898
36199
  },
35899
36200
  PlatformHttpClientService,
35900
36201
  {
35901
- provide: PLATFORM_HTTP_CLIENT3,
36202
+ provide: PLATFORM_HTTP_CLIENT4,
35902
36203
  useFactory: /* @__PURE__ */ __name((svc) => svc.instance, "useFactory"),
35903
36204
  inject: [
35904
36205
  PlatformHttpClientService
@@ -35925,7 +36226,7 @@ var PlatformModule = class _PlatformModule {
35925
36226
  LoggerModule,
35926
36227
  CommonModule,
35927
36228
  OBSERVABLE_SERVICE2,
35928
- PLATFORM_HTTP_CLIENT3,
36229
+ PLATFORM_HTTP_CLIENT4,
35929
36230
  HTTP_CLIENT_FACTORY,
35930
36231
  PlatformHttpClientService,
35931
36232
  CapabilityModule,
@@ -35958,9 +36259,9 @@ var PlatformModule = class _PlatformModule {
35958
36259
  }
35959
36260
  }
35960
36261
  };
35961
- PlatformModule = _ts_decorate12([
36262
+ PlatformModule = _ts_decorate15([
35962
36263
  Global(),
35963
- Module2({})
36264
+ Module3({})
35964
36265
  ], PlatformModule);
35965
36266
 
35966
36267
  // src/setup.ts
@@ -36058,6 +36359,8 @@ export {
36058
36359
  DevToolsModule,
36059
36360
  DevToolsV2Module2 as DevToolsV2Module,
36060
36361
  FileService,
36362
+ HtmlHotUpdateModule,
36363
+ HtmlHotUpdateService,
36061
36364
  PlatformHttpClientService,
36062
36365
  PlatformModule,
36063
36366
  StaticModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-nestjs-core",
3
- "version": "1.1.32",
3
+ "version": "1.1.33-alpha.0",
4
4
  "description": "FullStack Nestjs Core",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,7 +43,7 @@
43
43
  "@lark-apaas/http-client": "^0.1.2",
44
44
  "@lark-apaas/nestjs-authnpaas": "^1.0.2",
45
45
  "@lark-apaas/nestjs-authzpaas": "^0.1.2",
46
- "@lark-apaas/nestjs-capability": "^0.1.10",
46
+ "@lark-apaas/nestjs-capability": "^0.1.6",
47
47
  "@lark-apaas/nestjs-common": "^0.1.4",
48
48
  "@lark-apaas/nestjs-datapaas": "^1.0.11",
49
49
  "@lark-apaas/nestjs-logger": "^1.0.10",