@gravito/constellation 1.0.0-alpha.6 → 1.0.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.
@@ -1,76 +1,9 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __export = (target, all) => {
19
- for (var name in all)
20
- __defProp(target, name, {
21
- get: all[name],
22
- enumerable: true,
23
- configurable: true,
24
- set: (newValue) => all[name] = () => newValue
25
- });
26
- };
27
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
28
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
29
-
30
- // src/storage/DiskSitemapStorage.ts
31
- var exports_DiskSitemapStorage = {};
32
- __export(exports_DiskSitemapStorage, {
33
- DiskSitemapStorage: () => DiskSitemapStorage
34
- });
35
- import fs from "node:fs/promises";
36
- import path from "node:path";
37
-
38
- class DiskSitemapStorage {
39
- outDir;
40
- baseUrl;
41
- constructor(outDir, baseUrl) {
42
- this.outDir = outDir;
43
- this.baseUrl = baseUrl;
44
- }
45
- async write(filename, content) {
46
- await fs.mkdir(this.outDir, { recursive: true });
47
- await fs.writeFile(path.join(this.outDir, filename), content);
48
- }
49
- async read(filename) {
50
- try {
51
- return await fs.readFile(path.join(this.outDir, filename), "utf-8");
52
- } catch {
53
- return null;
54
- }
55
- }
56
- async exists(filename) {
57
- try {
58
- await fs.access(path.join(this.outDir, filename));
59
- return true;
60
- } catch {
61
- return false;
62
- }
63
- }
64
- getUrl(filename) {
65
- const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
66
- const file = filename.startsWith("/") ? filename.slice(1) : filename;
67
- return `${base}/${file}`;
68
- }
69
- }
70
- var init_DiskSitemapStorage = () => {};
1
+ import {
2
+ DiskSitemapStorage
3
+ } from "./chunk-7WHLC3OJ.js";
71
4
 
72
5
  // src/core/ChangeTracker.ts
73
- class MemoryChangeTracker {
6
+ var MemoryChangeTracker = class {
74
7
  changes = [];
75
8
  maxChanges;
76
9
  constructor(options = {}) {
@@ -98,9 +31,8 @@ class MemoryChangeTracker {
98
31
  }
99
32
  this.changes = this.changes.filter((change) => change.timestamp < since);
100
33
  }
101
- }
102
-
103
- class RedisChangeTracker {
34
+ };
35
+ var RedisChangeTracker = class {
104
36
  client;
105
37
  keyPrefix;
106
38
  ttl;
@@ -175,18 +107,23 @@ class RedisChangeTracker {
175
107
  await this.client.zrem(listKey, url);
176
108
  }
177
109
  }
178
- } catch {}
110
+ } catch {
111
+ }
179
112
  }
180
- }
113
+ };
114
+
181
115
  // src/core/DiffCalculator.ts
182
- class DiffCalculator {
116
+ var DiffCalculator = class {
183
117
  batchSize;
184
118
  constructor(options = {}) {
185
119
  this.batchSize = options.batchSize || 1e4;
186
120
  }
121
+ /**
122
+ * 計算兩個 sitemap 狀態的差異
123
+ */
187
124
  calculate(oldEntries, newEntries) {
188
- const oldMap = new Map;
189
- const newMap = new Map;
125
+ const oldMap = /* @__PURE__ */ new Map();
126
+ const newMap = /* @__PURE__ */ new Map();
190
127
  for (const entry of oldEntries) {
191
128
  oldMap.set(entry.url, entry);
192
129
  }
@@ -211,9 +148,12 @@ class DiffCalculator {
211
148
  }
212
149
  return { added, updated, removed };
213
150
  }
151
+ /**
152
+ * 批次計算差異(用於大量 URL)
153
+ */
214
154
  async calculateBatch(oldEntries, newEntries) {
215
- const oldMap = new Map;
216
- const newMap = new Map;
155
+ const oldMap = /* @__PURE__ */ new Map();
156
+ const newMap = /* @__PURE__ */ new Map();
217
157
  for await (const entry of oldEntries) {
218
158
  oldMap.set(entry.url, entry);
219
159
  }
@@ -222,8 +162,11 @@ class DiffCalculator {
222
162
  }
223
163
  return this.calculate(Array.from(oldMap.values()), Array.from(newMap.values()));
224
164
  }
165
+ /**
166
+ * 從變更記錄計算差異
167
+ */
225
168
  calculateFromChanges(baseEntries, changes) {
226
- const entryMap = new Map;
169
+ const entryMap = /* @__PURE__ */ new Map();
227
170
  for (const entry of baseEntries) {
228
171
  entryMap.set(entry.url, entry);
229
172
  }
@@ -239,6 +182,9 @@ class DiffCalculator {
239
182
  const newEntries = Array.from(entryMap.values());
240
183
  return this.calculate(baseEntries, newEntries);
241
184
  }
185
+ /**
186
+ * 檢查 entry 是否有變更
187
+ */
242
188
  hasChanged(oldEntry, newEntry) {
243
189
  if (oldEntry.lastmod !== newEntry.lastmod) {
244
190
  return true;
@@ -256,9 +202,10 @@ class DiffCalculator {
256
202
  }
257
203
  return false;
258
204
  }
259
- }
205
+ };
206
+
260
207
  // src/core/ShadowProcessor.ts
261
- class ShadowProcessor {
208
+ var ShadowProcessor = class {
262
209
  options;
263
210
  shadowId;
264
211
  operations = [];
@@ -266,6 +213,9 @@ class ShadowProcessor {
266
213
  this.options = options;
267
214
  this.shadowId = `shadow-${Date.now()}-${Math.random().toString(36).substring(7)}`;
268
215
  }
216
+ /**
217
+ * 添加一個影子操作
218
+ */
269
219
  async addOperation(operation) {
270
220
  if (!this.options.enabled) {
271
221
  await this.options.storage.write(operation.filename, operation.content);
@@ -281,6 +231,9 @@ class ShadowProcessor {
281
231
  await this.options.storage.write(operation.filename, operation.content);
282
232
  }
283
233
  }
234
+ /**
235
+ * 提交所有影子操作
236
+ */
284
237
  async commit() {
285
238
  if (!this.options.enabled) {
286
239
  return;
@@ -298,22 +251,31 @@ class ShadowProcessor {
298
251
  }
299
252
  this.operations = [];
300
253
  }
254
+ /**
255
+ * 取消所有影子操作
256
+ */
301
257
  async rollback() {
302
258
  if (!this.options.enabled) {
303
259
  return;
304
260
  }
305
261
  this.operations = [];
306
262
  }
263
+ /**
264
+ * 獲取當前影子 ID
265
+ */
307
266
  getShadowId() {
308
267
  return this.shadowId;
309
268
  }
269
+ /**
270
+ * 獲取所有操作
271
+ */
310
272
  getOperations() {
311
273
  return [...this.operations];
312
274
  }
313
- }
275
+ };
314
276
 
315
277
  // src/core/SitemapIndex.ts
316
- class SitemapIndex {
278
+ var SitemapIndex = class {
317
279
  options;
318
280
  entries = [];
319
281
  constructor(options) {
@@ -344,8 +306,7 @@ class SitemapIndex {
344
306
  `;
345
307
  const indent = pretty ? " " : "";
346
308
  const subIndent = pretty ? " " : "";
347
- const nl = pretty ? `
348
- ` : "";
309
+ const nl = pretty ? "\n" : "";
349
310
  for (const entry of this.entries) {
350
311
  let loc = entry.url;
351
312
  if (!loc.startsWith("http")) {
@@ -368,10 +329,10 @@ class SitemapIndex {
368
329
  escape(str) {
369
330
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
370
331
  }
371
- }
332
+ };
372
333
 
373
334
  // src/core/SitemapStream.ts
374
- class SitemapStream {
335
+ var SitemapStream = class {
375
336
  options;
376
337
  entries = [];
377
338
  constructor(options) {
@@ -422,8 +383,7 @@ class SitemapStream {
422
383
  renderUrl(entry, baseUrl, pretty) {
423
384
  const indent = pretty ? " " : "";
424
385
  const subIndent = pretty ? " " : "";
425
- const nl = pretty ? `
426
- ` : "";
386
+ const nl = pretty ? "\n" : "";
427
387
  let loc = entry.url;
428
388
  if (!loc.startsWith("http")) {
429
389
  if (!loc.startsWith("/")) {
@@ -440,7 +400,7 @@ class SitemapStream {
440
400
  if (entry.changefreq) {
441
401
  item += `${subIndent}<changefreq>${entry.changefreq}</changefreq>${nl}`;
442
402
  }
443
- if (entry.priority !== undefined) {
403
+ if (entry.priority !== void 0) {
444
404
  item += `${subIndent}<priority>${entry.priority.toFixed(1)}</priority>${nl}`;
445
405
  }
446
406
  if (entry.alternates) {
@@ -466,7 +426,7 @@ class SitemapStream {
466
426
  item += `${subIndent}<xhtml:link rel="canonical" href="${this.escape(canonicalUrl)}"/>${nl}`;
467
427
  }
468
428
  if (entry.redirect && !entry.redirect.canonical) {
469
- item += `${subIndent}<!-- Redirect: ${entry.redirect.from} ${entry.redirect.to} (${entry.redirect.type}) -->${nl}`;
429
+ item += `${subIndent}<!-- Redirect: ${entry.redirect.from} \u2192 ${entry.redirect.to} (${entry.redirect.type}) -->${nl}`;
470
430
  }
471
431
  if (entry.images) {
472
432
  for (const img of entry.images) {
@@ -562,15 +522,15 @@ class SitemapStream {
562
522
  escape(str) {
563
523
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
564
524
  }
565
- }
525
+ };
566
526
 
567
527
  // src/core/SitemapGenerator.ts
568
- class SitemapGenerator {
528
+ var SitemapGenerator = class {
569
529
  options;
570
530
  shadowProcessor = null;
571
531
  constructor(options) {
572
532
  this.options = {
573
- maxEntriesPerFile: 50000,
533
+ maxEntriesPerFile: 5e4,
574
534
  filename: "sitemap.xml",
575
535
  ...options
576
536
  };
@@ -608,7 +568,7 @@ class SitemapGenerator {
608
568
  const url = this.options.storage.getUrl(filename);
609
569
  index.add({
610
570
  url,
611
- lastmod: new Date
571
+ lastmod: /* @__PURE__ */ new Date()
612
572
  });
613
573
  shardIndex++;
614
574
  currentCount = 0;
@@ -649,13 +609,16 @@ class SitemapGenerator {
649
609
  await this.options.storage.write(this.options.filename, indexXml);
650
610
  }
651
611
  }
612
+ /**
613
+ * 獲取影子處理器(如果啟用)
614
+ */
652
615
  getShadowProcessor() {
653
616
  return this.shadowProcessor;
654
617
  }
655
- }
618
+ };
656
619
 
657
620
  // src/core/IncrementalGenerator.ts
658
- class IncrementalGenerator {
621
+ var IncrementalGenerator = class {
659
622
  options;
660
623
  changeTracker;
661
624
  diffCalculator;
@@ -663,9 +626,12 @@ class IncrementalGenerator {
663
626
  constructor(options) {
664
627
  this.options = options;
665
628
  this.changeTracker = options.changeTracker;
666
- this.diffCalculator = options.diffCalculator || new DiffCalculator;
629
+ this.diffCalculator = options.diffCalculator || new DiffCalculator();
667
630
  this.generator = new SitemapGenerator(options);
668
631
  }
632
+ /**
633
+ * 生成完整的 sitemap(首次生成)
634
+ */
669
635
  async generateFull() {
670
636
  await this.generator.run();
671
637
  if (this.options.autoTrack) {
@@ -678,12 +644,15 @@ class IncrementalGenerator {
678
644
  type: "add",
679
645
  url: entry.url,
680
646
  entry,
681
- timestamp: new Date
647
+ timestamp: /* @__PURE__ */ new Date()
682
648
  });
683
649
  }
684
650
  }
685
651
  }
686
652
  }
653
+ /**
654
+ * 增量生成(只更新變更的部分)
655
+ */
687
656
  async generateIncremental(since) {
688
657
  const changes = await this.changeTracker.getChanges(since);
689
658
  if (changes.length === 0) {
@@ -693,12 +662,21 @@ class IncrementalGenerator {
693
662
  const diff = this.diffCalculator.calculateFromChanges(baseEntries, changes);
694
663
  await this.generateDiff(diff);
695
664
  }
665
+ /**
666
+ * 手動追蹤變更
667
+ */
696
668
  async trackChange(change) {
697
669
  await this.changeTracker.track(change);
698
670
  }
671
+ /**
672
+ * 獲取變更記錄
673
+ */
699
674
  async getChanges(since) {
700
675
  return this.changeTracker.getChanges(since);
701
676
  }
677
+ /**
678
+ * 載入基礎 entries(從現有 sitemap)
679
+ */
702
680
  async loadBaseEntries() {
703
681
  const entries = [];
704
682
  const { providers } = this.options;
@@ -709,9 +687,15 @@ class IncrementalGenerator {
709
687
  }
710
688
  return entries;
711
689
  }
690
+ /**
691
+ * 生成差異部分
692
+ */
712
693
  async generateDiff(_diff) {
713
694
  await this.generator.run();
714
695
  }
696
+ /**
697
+ * 將 AsyncIterable 轉換為陣列
698
+ */
715
699
  async toArray(iterable) {
716
700
  const array = [];
717
701
  for await (const item of iterable) {
@@ -719,17 +703,21 @@ class IncrementalGenerator {
719
703
  }
720
704
  return array;
721
705
  }
722
- }
706
+ };
707
+
723
708
  // src/core/ProgressTracker.ts
724
- class ProgressTracker {
709
+ var ProgressTracker = class {
725
710
  storage;
726
711
  updateInterval;
727
712
  currentProgress = null;
728
713
  updateTimer = null;
729
714
  constructor(options) {
730
715
  this.storage = options.storage;
731
- this.updateInterval = options.updateInterval || 1000;
716
+ this.updateInterval = options.updateInterval || 1e3;
732
717
  }
718
+ /**
719
+ * 初始化進度追蹤
720
+ */
733
721
  async init(jobId, total) {
734
722
  this.currentProgress = {
735
723
  jobId,
@@ -737,10 +725,13 @@ class ProgressTracker {
737
725
  total,
738
726
  processed: 0,
739
727
  percentage: 0,
740
- startTime: new Date
728
+ startTime: /* @__PURE__ */ new Date()
741
729
  };
742
730
  await this.storage.set(jobId, this.currentProgress);
743
731
  }
732
+ /**
733
+ * 更新進度
734
+ */
744
735
  async update(processed, status) {
745
736
  if (!this.currentProgress) {
746
737
  return;
@@ -760,26 +751,35 @@ class ProgressTracker {
760
751
  }, this.updateInterval);
761
752
  }
762
753
  }
754
+ /**
755
+ * 完成進度追蹤
756
+ */
763
757
  async complete() {
764
758
  if (!this.currentProgress) {
765
759
  return;
766
760
  }
767
761
  this.currentProgress.status = "completed";
768
- this.currentProgress.endTime = new Date;
762
+ this.currentProgress.endTime = /* @__PURE__ */ new Date();
769
763
  this.currentProgress.percentage = 100;
770
764
  await this.flush();
771
765
  this.stop();
772
766
  }
767
+ /**
768
+ * 標記為失敗
769
+ */
773
770
  async fail(error) {
774
771
  if (!this.currentProgress) {
775
772
  return;
776
773
  }
777
774
  this.currentProgress.status = "failed";
778
- this.currentProgress.endTime = new Date;
775
+ this.currentProgress.endTime = /* @__PURE__ */ new Date();
779
776
  this.currentProgress.error = error;
780
777
  await this.flush();
781
778
  this.stop();
782
779
  }
780
+ /**
781
+ * 刷新進度到儲存
782
+ */
783
783
  async flush() {
784
784
  if (!this.currentProgress) {
785
785
  return;
@@ -792,16 +792,23 @@ class ProgressTracker {
792
792
  error: this.currentProgress.error
793
793
  });
794
794
  }
795
+ /**
796
+ * 停止更新計時器
797
+ */
795
798
  stop() {
796
799
  if (this.updateTimer) {
797
800
  clearInterval(this.updateTimer);
798
801
  this.updateTimer = null;
799
802
  }
800
803
  }
804
+ /**
805
+ * 獲取當前進度
806
+ */
801
807
  getCurrentProgress() {
802
808
  return this.currentProgress ? { ...this.currentProgress } : null;
803
809
  }
804
- }
810
+ };
811
+
805
812
  // src/helpers/I18nSitemap.ts
806
813
  function generateI18nEntries(path, locales, baseUrl = "", options = {}) {
807
814
  const cleanPath = path.startsWith("/") ? path : `/${path}`;
@@ -820,9 +827,10 @@ function generateI18nEntries(path, locales, baseUrl = "", options = {}) {
820
827
  };
821
828
  });
822
829
  }
830
+
823
831
  // src/jobs/GenerateSitemapJob.ts
824
832
  import { Job } from "@gravito/stream";
825
- class GenerateSitemapJob extends Job {
833
+ var GenerateSitemapJob = class extends Job {
826
834
  options;
827
835
  generator;
828
836
  totalEntries = 0;
@@ -861,6 +869,9 @@ class GenerateSitemapJob extends Job {
861
869
  throw err;
862
870
  }
863
871
  }
872
+ /**
873
+ * 計算總 URL 數
874
+ */
864
875
  async calculateTotal() {
865
876
  let total = 0;
866
877
  const { providers } = this.options.generatorOptions;
@@ -876,11 +887,14 @@ class GenerateSitemapJob extends Job {
876
887
  }
877
888
  return total;
878
889
  }
890
+ /**
891
+ * 帶進度追蹤的生成
892
+ */
879
893
  async generateWithProgress() {
880
894
  const { progressTracker, shadowProcessor, onProgress } = this.options;
881
895
  const {
882
896
  providers,
883
- maxEntriesPerFile = 50000,
897
+ maxEntriesPerFile = 5e4,
884
898
  storage,
885
899
  baseUrl,
886
900
  pretty,
@@ -899,20 +913,24 @@ class GenerateSitemapJob extends Job {
899
913
  });
900
914
  }
901
915
  }
902
- }
916
+ };
917
+
903
918
  // src/OrbitSitemap.ts
904
- import { randomUUID } from "node:crypto";
919
+ import { randomUUID } from "crypto";
905
920
 
906
921
  // src/redirect/RedirectHandler.ts
907
- class RedirectHandler {
922
+ var RedirectHandler = class {
908
923
  options;
909
924
  constructor(options) {
910
925
  this.options = options;
911
926
  }
927
+ /**
928
+ * 處理 entries 中的轉址
929
+ */
912
930
  async processEntries(entries) {
913
931
  const { manager, strategy, followChains, maxChainLength } = this.options;
914
932
  const _processedEntries = [];
915
- const redirectMap = new Map;
933
+ const redirectMap = /* @__PURE__ */ new Map();
916
934
  for (const entry of entries) {
917
935
  const redirectTarget = await manager.resolve(entry.url, followChains, maxChainLength);
918
936
  if (redirectTarget && entry.url !== redirectTarget) {
@@ -920,6 +938,7 @@ class RedirectHandler {
920
938
  from: entry.url,
921
939
  to: redirectTarget,
922
940
  type: 301
941
+ // Default to 301 for resolved chains
923
942
  });
924
943
  }
925
944
  }
@@ -936,9 +955,12 @@ class RedirectHandler {
936
955
  return entries;
937
956
  }
938
957
  }
958
+ /**
959
+ * 策略一:移除舊 URL,加入新 URL
960
+ */
939
961
  handleRemoveOldAddNew(entries, redirectMap) {
940
962
  const processed = [];
941
- const redirectedUrls = new Set;
963
+ const redirectedUrls = /* @__PURE__ */ new Set();
942
964
  for (const entry of entries) {
943
965
  const redirect = redirectMap.get(entry.url);
944
966
  if (redirect) {
@@ -958,6 +980,9 @@ class RedirectHandler {
958
980
  }
959
981
  return processed;
960
982
  }
983
+ /**
984
+ * 策略二:保留關聯,使用 canonical link
985
+ */
961
986
  handleKeepRelation(entries, redirectMap) {
962
987
  const processed = [];
963
988
  for (const entry of entries) {
@@ -978,6 +1003,9 @@ class RedirectHandler {
978
1003
  }
979
1004
  return processed;
980
1005
  }
1006
+ /**
1007
+ * 策略三:僅更新 URL
1008
+ */
981
1009
  handleUpdateUrl(entries, redirectMap) {
982
1010
  return entries.map((entry) => {
983
1011
  const redirect = redirectMap.get(entry.url);
@@ -995,9 +1023,12 @@ class RedirectHandler {
995
1023
  return entry;
996
1024
  });
997
1025
  }
1026
+ /**
1027
+ * 策略四:雙重標記
1028
+ */
998
1029
  handleDualMark(entries, redirectMap) {
999
1030
  const processed = [];
1000
- const addedUrls = new Set;
1031
+ const addedUrls = /* @__PURE__ */ new Set();
1001
1032
  for (const entry of entries) {
1002
1033
  const redirect = redirectMap.get(entry.url);
1003
1034
  if (redirect) {
@@ -1027,15 +1058,14 @@ class RedirectHandler {
1027
1058
  }
1028
1059
  return processed;
1029
1060
  }
1030
- }
1061
+ };
1031
1062
 
1032
1063
  // src/storage/MemorySitemapStorage.ts
1033
- class MemorySitemapStorage {
1034
- baseUrl;
1035
- files = new Map;
1064
+ var MemorySitemapStorage = class {
1036
1065
  constructor(baseUrl) {
1037
1066
  this.baseUrl = baseUrl;
1038
1067
  }
1068
+ files = /* @__PURE__ */ new Map();
1039
1069
  async write(filename, content) {
1040
1070
  this.files.set(filename, content);
1041
1071
  }
@@ -1050,28 +1080,60 @@ class MemorySitemapStorage {
1050
1080
  const file = filename.startsWith("/") ? filename.slice(1) : filename;
1051
1081
  return `${base}/${file}`;
1052
1082
  }
1053
- }
1083
+ };
1054
1084
 
1055
1085
  // src/OrbitSitemap.ts
1056
- class OrbitSitemap {
1086
+ function sanitizeFilename(value) {
1087
+ if (!value) {
1088
+ return null;
1089
+ }
1090
+ if (value.includes("\0")) {
1091
+ return null;
1092
+ }
1093
+ if (value.includes("/") || value.includes("\\")) {
1094
+ return null;
1095
+ }
1096
+ if (value.includes("..")) {
1097
+ return null;
1098
+ }
1099
+ return value;
1100
+ }
1101
+ var OrbitSitemap = class _OrbitSitemap {
1057
1102
  options;
1058
1103
  mode;
1059
1104
  constructor(mode, options) {
1060
1105
  this.mode = mode;
1061
1106
  this.options = options;
1062
1107
  }
1108
+ /**
1109
+ * Create a dynamic sitemap configuration.
1110
+ *
1111
+ * @param options - The dynamic sitemap options.
1112
+ * @returns An OrbitSitemap instance configured for dynamic generation.
1113
+ */
1063
1114
  static dynamic(options) {
1064
- return new OrbitSitemap("dynamic", {
1115
+ return new _OrbitSitemap("dynamic", {
1065
1116
  path: "/sitemap.xml",
1066
1117
  ...options
1067
1118
  });
1068
1119
  }
1120
+ /**
1121
+ * Create a static sitemap configuration.
1122
+ *
1123
+ * @param options - The static sitemap options.
1124
+ * @returns An OrbitSitemap instance configured for static generation.
1125
+ */
1069
1126
  static static(options) {
1070
- return new OrbitSitemap("static", {
1127
+ return new _OrbitSitemap("static", {
1071
1128
  filename: "sitemap.xml",
1072
1129
  ...options
1073
1130
  });
1074
1131
  }
1132
+ /**
1133
+ * Install the sitemap module into PlanetCore.
1134
+ *
1135
+ * @param core - The PlanetCore instance.
1136
+ */
1075
1137
  install(core) {
1076
1138
  if (this.mode === "dynamic") {
1077
1139
  this.installDynamic(core);
@@ -1083,10 +1145,14 @@ class OrbitSitemap {
1083
1145
  const opts = this.options;
1084
1146
  const storage = opts.storage ?? new MemorySitemapStorage(opts.baseUrl);
1085
1147
  const indexFilename = opts.path?.split("/").pop() ?? "sitemap.xml";
1086
- const baseDir = opts.path ? opts.path.substring(0, opts.path.lastIndexOf("/")) : undefined;
1148
+ const baseDir = opts.path ? opts.path.substring(0, opts.path.lastIndexOf("/")) : void 0;
1087
1149
  const handler = async (ctx) => {
1088
1150
  const reqPath = ctx.req.path;
1089
- const filename = reqPath.split("/").pop() || indexFilename;
1151
+ const rawName = reqPath.split("/").pop() || indexFilename;
1152
+ const filename = sanitizeFilename(rawName);
1153
+ if (!filename) {
1154
+ return ctx.text("Not Found", 404);
1155
+ }
1090
1156
  const isIndex = filename === indexFilename;
1091
1157
  let content = await storage.read(filename);
1092
1158
  if (!content && isIndex) {
@@ -1131,6 +1197,12 @@ class OrbitSitemap {
1131
1197
  const shardRoute = `${baseDir}/${basename}-:shard.xml`;
1132
1198
  core.router.get(shardRoute, handler);
1133
1199
  }
1200
+ /**
1201
+ * Generate the sitemap (static mode only).
1202
+ *
1203
+ * @returns A promise that resolves when generation is complete.
1204
+ * @throws {Error} If called in dynamic mode.
1205
+ */
1134
1206
  async generate() {
1135
1207
  if (this.mode !== "static") {
1136
1208
  throw new Error("generate() can only be called in static mode");
@@ -1138,7 +1210,7 @@ class OrbitSitemap {
1138
1210
  const opts = this.options;
1139
1211
  let storage = opts.storage;
1140
1212
  if (!storage) {
1141
- const { DiskSitemapStorage: DiskSitemapStorage2 } = await Promise.resolve().then(() => (init_DiskSitemapStorage(), exports_DiskSitemapStorage));
1213
+ const { DiskSitemapStorage: DiskSitemapStorage2 } = await import("./DiskSitemapStorage-7ZZMGC4K.js");
1142
1214
  storage = new DiskSitemapStorage2(opts.outDir, opts.baseUrl);
1143
1215
  }
1144
1216
  let providers = opts.providers;
@@ -1167,6 +1239,13 @@ class OrbitSitemap {
1167
1239
  await generator.run();
1168
1240
  console.log(`[OrbitSitemap] Generated sitemap in ${opts.outDir}`);
1169
1241
  }
1242
+ /**
1243
+ * Generate incremental sitemap updates (static mode only).
1244
+ *
1245
+ * @param since - Only include items modified since this date.
1246
+ * @returns A promise that resolves when incremental generation is complete.
1247
+ * @throws {Error} If called in dynamic mode, or if incremental generation is not enabled/configured.
1248
+ */
1170
1249
  async generateIncremental(since) {
1171
1250
  if (this.mode !== "static") {
1172
1251
  throw new Error("generateIncremental() can only be called in static mode");
@@ -1177,7 +1256,7 @@ class OrbitSitemap {
1177
1256
  }
1178
1257
  let storage = opts.storage;
1179
1258
  if (!storage) {
1180
- const { DiskSitemapStorage: DiskSitemapStorage2 } = await Promise.resolve().then(() => (init_DiskSitemapStorage(), exports_DiskSitemapStorage));
1259
+ const { DiskSitemapStorage: DiskSitemapStorage2 } = await import("./DiskSitemapStorage-7ZZMGC4K.js");
1181
1260
  storage = new DiskSitemapStorage2(opts.outDir, opts.baseUrl);
1182
1261
  }
1183
1262
  const incrementalGenerator = new IncrementalGenerator({
@@ -1191,6 +1270,13 @@ class OrbitSitemap {
1191
1270
  await incrementalGenerator.generateIncremental(since);
1192
1271
  console.log(`[OrbitSitemap] Generated incremental sitemap in ${opts.outDir}`);
1193
1272
  }
1273
+ /**
1274
+ * Generate sitemap asynchronously in the background (static mode only).
1275
+ *
1276
+ * @param options - Options for the async generation job.
1277
+ * @returns A promise resolving to the job ID.
1278
+ * @throws {Error} If called in dynamic mode.
1279
+ */
1194
1280
  async generateAsync(options) {
1195
1281
  if (this.mode !== "static") {
1196
1282
  throw new Error("generateAsync() can only be called in static mode");
@@ -1199,7 +1285,7 @@ class OrbitSitemap {
1199
1285
  const jobId = randomUUID();
1200
1286
  let storage = opts.storage;
1201
1287
  if (!storage) {
1202
- const { DiskSitemapStorage: DiskSitemapStorage2 } = await Promise.resolve().then(() => (init_DiskSitemapStorage(), exports_DiskSitemapStorage));
1288
+ const { DiskSitemapStorage: DiskSitemapStorage2 } = await import("./DiskSitemapStorage-7ZZMGC4K.js");
1203
1289
  storage = new DiskSitemapStorage2(opts.outDir, opts.baseUrl);
1204
1290
  }
1205
1291
  let providers = opts.providers;
@@ -1245,6 +1331,12 @@ class OrbitSitemap {
1245
1331
  });
1246
1332
  return jobId;
1247
1333
  }
1334
+ /**
1335
+ * Install API endpoints for triggering and monitoring sitemap generation.
1336
+ *
1337
+ * @param core - The PlanetCore instance.
1338
+ * @param basePath - The base path for the API endpoints (default: '/admin/sitemap').
1339
+ */
1248
1340
  installApiEndpoints(core, basePath = "/admin/sitemap") {
1249
1341
  const opts = this.options;
1250
1342
  core.router.post(`${basePath}/generate`, async (ctx) => {
@@ -1252,7 +1344,7 @@ class OrbitSitemap {
1252
1344
  const body = await ctx.req.json().catch(() => ({}));
1253
1345
  const jobId = await this.generateAsync({
1254
1346
  incremental: body.incremental,
1255
- since: body.since ? new Date(body.since) : undefined
1347
+ since: body.since ? new Date(body.since) : void 0
1256
1348
  });
1257
1349
  return ctx.json({ jobId, status: "started" });
1258
1350
  } catch (error) {
@@ -1282,6 +1374,9 @@ class OrbitSitemap {
1282
1374
  return ctx.json(history);
1283
1375
  });
1284
1376
  }
1377
+ /**
1378
+ * Convert an AsyncIterable to an array.
1379
+ */
1285
1380
  async toArray(iterable) {
1286
1381
  const array = [];
1287
1382
  for await (const item of iterable) {
@@ -1289,16 +1384,17 @@ class OrbitSitemap {
1289
1384
  }
1290
1385
  return array;
1291
1386
  }
1292
- }
1387
+ };
1388
+
1293
1389
  // src/providers/RouteScanner.ts
1294
1390
  function matchGlob(str, pattern) {
1295
1391
  const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
1296
1392
  const regex = new RegExp(`^${regexPattern}$`);
1297
1393
  return regex.test(str);
1298
1394
  }
1299
-
1300
- class RouteScanner {
1395
+ var RouteScanner = class {
1301
1396
  router;
1397
+ // Using any to key access internal routes
1302
1398
  options;
1303
1399
  constructor(router, options = {}) {
1304
1400
  this.router = router;
@@ -1335,10 +1431,10 @@ class RouteScanner {
1335
1431
  }
1336
1432
  return routes;
1337
1433
  }
1338
- shouldInclude(path2) {
1434
+ shouldInclude(path) {
1339
1435
  if (this.options.exclude) {
1340
1436
  for (const pattern of this.options.exclude) {
1341
- if (matchGlob(path2, pattern)) {
1437
+ if (matchGlob(path, pattern)) {
1342
1438
  return false;
1343
1439
  }
1344
1440
  }
@@ -1346,7 +1442,7 @@ class RouteScanner {
1346
1442
  if (this.options.include) {
1347
1443
  let matched = false;
1348
1444
  for (const pattern of this.options.include) {
1349
- if (matchGlob(path2, pattern)) {
1445
+ if (matchGlob(path, pattern)) {
1350
1446
  matched = true;
1351
1447
  break;
1352
1448
  }
@@ -1355,17 +1451,21 @@ class RouteScanner {
1355
1451
  }
1356
1452
  return true;
1357
1453
  }
1358
- }
1454
+ };
1359
1455
  function routeScanner(router, options) {
1360
1456
  return new RouteScanner(router, options);
1361
1457
  }
1458
+
1362
1459
  // src/redirect/RedirectDetector.ts
1363
- class RedirectDetector {
1460
+ var RedirectDetector = class {
1364
1461
  options;
1365
- cache = new Map;
1462
+ cache = /* @__PURE__ */ new Map();
1366
1463
  constructor(options) {
1367
1464
  this.options = options;
1368
1465
  }
1466
+ /**
1467
+ * 偵測單一 URL 的轉址
1468
+ */
1369
1469
  async detect(url) {
1370
1470
  if (this.options.autoDetect?.cache) {
1371
1471
  const cached = this.cache.get(url);
@@ -1395,21 +1495,29 @@ class RedirectDetector {
1395
1495
  }
1396
1496
  return null;
1397
1497
  }
1498
+ /**
1499
+ * 批次偵測轉址
1500
+ */
1398
1501
  async detectBatch(urls) {
1399
- const results = new Map;
1502
+ const results = /* @__PURE__ */ new Map();
1400
1503
  const maxConcurrent = this.options.autoDetect?.maxConcurrent || 10;
1401
1504
  const batches = [];
1402
- for (let i = 0;i < urls.length; i += maxConcurrent) {
1505
+ for (let i = 0; i < urls.length; i += maxConcurrent) {
1403
1506
  batches.push(urls.slice(i, i + maxConcurrent));
1404
1507
  }
1405
1508
  for (const batch of batches) {
1406
- const promises = batch.map((url) => this.detect(url).then((rule) => {
1407
- results.set(url, rule);
1408
- }));
1509
+ const promises = batch.map(
1510
+ (url) => this.detect(url).then((rule) => {
1511
+ results.set(url, rule);
1512
+ })
1513
+ );
1409
1514
  await Promise.all(promises);
1410
1515
  }
1411
1516
  return results;
1412
1517
  }
1518
+ /**
1519
+ * 從資料庫偵測
1520
+ */
1413
1521
  async detectFromDatabase(url) {
1414
1522
  const { database } = this.options;
1415
1523
  if (!database?.enabled) {
@@ -1432,14 +1540,17 @@ class RedirectDetector {
1432
1540
  return null;
1433
1541
  }
1434
1542
  }
1543
+ /**
1544
+ * 從設定檔偵測
1545
+ */
1435
1546
  async detectFromConfig(url) {
1436
1547
  const { config } = this.options;
1437
1548
  if (!config?.enabled) {
1438
1549
  return null;
1439
1550
  }
1440
1551
  try {
1441
- const fs2 = await import("node:fs/promises");
1442
- const data = await fs2.readFile(config.path, "utf-8");
1552
+ const fs = await import("fs/promises");
1553
+ const data = await fs.readFile(config.path, "utf-8");
1443
1554
  const redirects = JSON.parse(data);
1444
1555
  const rule = redirects.find((r) => r.from === url);
1445
1556
  return rule || null;
@@ -1447,6 +1558,9 @@ class RedirectDetector {
1447
1558
  return null;
1448
1559
  }
1449
1560
  }
1561
+ /**
1562
+ * 自動偵測(透過 HTTP 請求)
1563
+ */
1450
1564
  async detectAuto(url) {
1451
1565
  const { autoDetect, baseUrl } = this.options;
1452
1566
  if (!autoDetect?.enabled) {
@@ -1454,14 +1568,15 @@ class RedirectDetector {
1454
1568
  }
1455
1569
  try {
1456
1570
  const fullUrl = url.startsWith("http") ? url : `${baseUrl}${url}`;
1457
- const timeout = autoDetect.timeout || 5000;
1458
- const controller = new AbortController;
1571
+ const timeout = autoDetect.timeout || 5e3;
1572
+ const controller = new AbortController();
1459
1573
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1460
1574
  try {
1461
1575
  const response = await fetch(fullUrl, {
1462
1576
  method: "HEAD",
1463
1577
  signal: controller.signal,
1464
1578
  redirect: "manual"
1579
+ // 手動處理轉址
1465
1580
  });
1466
1581
  clearTimeout(timeoutId);
1467
1582
  if (response.status === 301 || response.status === 302) {
@@ -1480,23 +1595,28 @@ class RedirectDetector {
1480
1595
  throw error;
1481
1596
  }
1482
1597
  }
1483
- } catch {}
1598
+ } catch {
1599
+ }
1484
1600
  return null;
1485
1601
  }
1602
+ /**
1603
+ * 快取結果
1604
+ */
1486
1605
  cacheResult(url, rule) {
1487
1606
  if (!this.options.autoDetect?.cache) {
1488
1607
  return;
1489
1608
  }
1490
- const ttl = (this.options.autoDetect.cacheTtl || 3600) * 1000;
1609
+ const ttl = (this.options.autoDetect.cacheTtl || 3600) * 1e3;
1491
1610
  this.cache.set(url, {
1492
1611
  rule,
1493
1612
  expires: Date.now() + ttl
1494
1613
  });
1495
1614
  }
1496
- }
1615
+ };
1616
+
1497
1617
  // src/redirect/RedirectManager.ts
1498
- class MemoryRedirectManager {
1499
- rules = new Map;
1618
+ var MemoryRedirectManager = class {
1619
+ rules = /* @__PURE__ */ new Map();
1500
1620
  maxRules;
1501
1621
  constructor(options = {}) {
1502
1622
  this.maxRules = options.maxRules || 1e5;
@@ -1537,9 +1657,8 @@ class MemoryRedirectManager {
1537
1657
  }
1538
1658
  return current;
1539
1659
  }
1540
- }
1541
-
1542
- class RedisRedirectManager {
1660
+ };
1661
+ var RedisRedirectManager = class {
1543
1662
  client;
1544
1663
  keyPrefix;
1545
1664
  ttl;
@@ -1618,19 +1737,17 @@ class RedisRedirectManager {
1618
1737
  }
1619
1738
  return current;
1620
1739
  }
1621
- }
1622
-
1623
- // src/index.ts
1624
- init_DiskSitemapStorage();
1740
+ };
1625
1741
 
1626
1742
  // src/storage/GCPSitemapStorage.ts
1627
- class GCPSitemapStorage {
1743
+ var GCPSitemapStorage = class {
1628
1744
  bucket;
1629
1745
  prefix;
1630
1746
  baseUrl;
1631
1747
  shadowEnabled;
1632
1748
  shadowMode;
1633
1749
  storageClient;
1750
+ // 動態載入 @google-cloud/storage
1634
1751
  bucketInstance;
1635
1752
  constructor(options) {
1636
1753
  this.bucket = options.bucket;
@@ -1646,12 +1763,15 @@ class GCPSitemapStorage {
1646
1763
  try {
1647
1764
  const { Storage } = await import("@google-cloud/storage");
1648
1765
  const clientOptions = {};
1649
- if (this.constructor.name === "GCPSitemapStorage") {}
1766
+ if (this.constructor.name === "GCPSitemapStorage") {
1767
+ }
1650
1768
  this.storageClient = new Storage(clientOptions);
1651
1769
  this.bucketInstance = this.storageClient.bucket(this.bucket);
1652
1770
  return { client: this.storageClient, bucket: this.bucketInstance };
1653
1771
  } catch (error) {
1654
- throw new Error(`Failed to load Google Cloud Storage. Please install @google-cloud/storage: ${error instanceof Error ? error.message : String(error)}`);
1772
+ throw new Error(
1773
+ `Failed to load Google Cloud Storage. Please install @google-cloud/storage: ${error instanceof Error ? error.message : String(error)}`
1774
+ );
1655
1775
  }
1656
1776
  }
1657
1777
  getKey(filename) {
@@ -1703,6 +1823,7 @@ class GCPSitemapStorage {
1703
1823
  const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
1704
1824
  return `${base}/${key}`;
1705
1825
  }
1826
+ // 影子處理方法
1706
1827
  async writeShadow(filename, content, shadowId) {
1707
1828
  if (!this.shadowEnabled) {
1708
1829
  return this.write(filename, content);
@@ -1779,10 +1900,11 @@ class GCPSitemapStorage {
1779
1900
  }
1780
1901
  await versionedFile.copy(bucket.file(key));
1781
1902
  }
1782
- }
1903
+ };
1904
+
1783
1905
  // src/storage/MemoryProgressStorage.ts
1784
- class MemoryProgressStorage {
1785
- storage = new Map;
1906
+ var MemoryProgressStorage = class {
1907
+ storage = /* @__PURE__ */ new Map();
1786
1908
  async get(jobId) {
1787
1909
  const progress = this.storage.get(jobId);
1788
1910
  return progress ? { ...progress } : null;
@@ -1808,9 +1930,10 @@ class MemoryProgressStorage {
1808
1930
  });
1809
1931
  return limit ? sorted.slice(0, limit) : sorted;
1810
1932
  }
1811
- }
1933
+ };
1934
+
1812
1935
  // src/storage/RedisProgressStorage.ts
1813
- class RedisProgressStorage {
1936
+ var RedisProgressStorage = class {
1814
1937
  client;
1815
1938
  keyPrefix;
1816
1939
  ttl;
@@ -1880,9 +2003,10 @@ class RedisProgressStorage {
1880
2003
  return [];
1881
2004
  }
1882
2005
  }
1883
- }
2006
+ };
2007
+
1884
2008
  // src/storage/S3SitemapStorage.ts
1885
- class S3SitemapStorage {
2009
+ var S3SitemapStorage = class {
1886
2010
  bucket;
1887
2011
  region;
1888
2012
  prefix;
@@ -1890,6 +2014,7 @@ class S3SitemapStorage {
1890
2014
  shadowEnabled;
1891
2015
  shadowMode;
1892
2016
  s3Client;
2017
+ // 動態載入 @aws-sdk/client-s3
1893
2018
  constructor(options) {
1894
2019
  this.bucket = options.bucket;
1895
2020
  this.region = options.region || "us-east-1";
@@ -1915,7 +2040,8 @@ class S3SitemapStorage {
1915
2040
  const clientOptions = {
1916
2041
  region: this.region
1917
2042
  };
1918
- if (this.constructor.name === "S3SitemapStorage") {}
2043
+ if (this.constructor.name === "S3SitemapStorage") {
2044
+ }
1919
2045
  this.s3Client = {
1920
2046
  S3Client,
1921
2047
  PutObjectCommand,
@@ -1928,7 +2054,9 @@ class S3SitemapStorage {
1928
2054
  };
1929
2055
  return this.s3Client;
1930
2056
  } catch (error) {
1931
- throw new Error(`Failed to load AWS SDK. Please install @aws-sdk/client-s3: ${error instanceof Error ? error.message : String(error)}`);
2057
+ throw new Error(
2058
+ `Failed to load AWS SDK. Please install @aws-sdk/client-s3: ${error instanceof Error ? error.message : String(error)}`
2059
+ );
1932
2060
  }
1933
2061
  }
1934
2062
  getKey(filename) {
@@ -1938,21 +2066,25 @@ class S3SitemapStorage {
1938
2066
  async write(filename, content) {
1939
2067
  const s3 = await this.getS3Client();
1940
2068
  const key = this.getKey(filename);
1941
- await s3.client.send(new s3.PutObjectCommand({
1942
- Bucket: this.bucket,
1943
- Key: key,
1944
- Body: content,
1945
- ContentType: "application/xml"
1946
- }));
2069
+ await s3.client.send(
2070
+ new s3.PutObjectCommand({
2071
+ Bucket: this.bucket,
2072
+ Key: key,
2073
+ Body: content,
2074
+ ContentType: "application/xml"
2075
+ })
2076
+ );
1947
2077
  }
1948
2078
  async read(filename) {
1949
2079
  try {
1950
2080
  const s3 = await this.getS3Client();
1951
2081
  const key = this.getKey(filename);
1952
- const response = await s3.client.send(new s3.GetObjectCommand({
1953
- Bucket: this.bucket,
1954
- Key: key
1955
- }));
2082
+ const response = await s3.client.send(
2083
+ new s3.GetObjectCommand({
2084
+ Bucket: this.bucket,
2085
+ Key: key
2086
+ })
2087
+ );
1956
2088
  if (!response.Body) {
1957
2089
  return null;
1958
2090
  }
@@ -1973,10 +2105,12 @@ class S3SitemapStorage {
1973
2105
  try {
1974
2106
  const s3 = await this.getS3Client();
1975
2107
  const key = this.getKey(filename);
1976
- await s3.client.send(new s3.HeadObjectCommand({
1977
- Bucket: this.bucket,
1978
- Key: key
1979
- }));
2108
+ await s3.client.send(
2109
+ new s3.HeadObjectCommand({
2110
+ Bucket: this.bucket,
2111
+ Key: key
2112
+ })
2113
+ );
1980
2114
  return true;
1981
2115
  } catch (error) {
1982
2116
  if (error.name === "NotFound" || error.$metadata?.httpStatusCode === 404) {
@@ -1990,6 +2124,7 @@ class S3SitemapStorage {
1990
2124
  const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
1991
2125
  return `${base}/${key}`;
1992
2126
  }
2127
+ // 影子處理方法
1993
2128
  async writeShadow(filename, content, shadowId) {
1994
2129
  if (!this.shadowEnabled) {
1995
2130
  return this.write(filename, content);
@@ -1997,12 +2132,14 @@ class S3SitemapStorage {
1997
2132
  const s3 = await this.getS3Client();
1998
2133
  const id = shadowId || `shadow-${Date.now()}-${Math.random().toString(36).substring(7)}`;
1999
2134
  const shadowKey = this.getKey(`${filename}.shadow.${id}`);
2000
- await s3.client.send(new s3.PutObjectCommand({
2001
- Bucket: this.bucket,
2002
- Key: shadowKey,
2003
- Body: content,
2004
- ContentType: "application/xml"
2005
- }));
2135
+ await s3.client.send(
2136
+ new s3.PutObjectCommand({
2137
+ Bucket: this.bucket,
2138
+ Key: shadowKey,
2139
+ Body: content,
2140
+ ContentType: "application/xml"
2141
+ })
2142
+ );
2006
2143
  }
2007
2144
  async commitShadow(shadowId) {
2008
2145
  if (!this.shadowEnabled) {
@@ -2010,14 +2147,18 @@ class S3SitemapStorage {
2010
2147
  }
2011
2148
  const s3 = await this.getS3Client();
2012
2149
  const prefix = this.prefix ? `${this.prefix}/` : "";
2013
- const listResponse = await s3.client.send(new s3.ListObjectsV2Command({
2014
- Bucket: this.bucket,
2015
- Prefix: prefix
2016
- }));
2150
+ const listResponse = await s3.client.send(
2151
+ new s3.ListObjectsV2Command({
2152
+ Bucket: this.bucket,
2153
+ Prefix: prefix
2154
+ })
2155
+ );
2017
2156
  if (!listResponse.Contents) {
2018
2157
  return;
2019
2158
  }
2020
- const shadowFiles = listResponse.Contents.filter((obj) => obj.Key?.includes(`.shadow.${shadowId}`));
2159
+ const shadowFiles = listResponse.Contents.filter(
2160
+ (obj) => obj.Key?.includes(`.shadow.${shadowId}`)
2161
+ );
2021
2162
  for (const shadowFile of shadowFiles) {
2022
2163
  if (!shadowFile.Key) {
2023
2164
  continue;
@@ -2025,35 +2166,45 @@ class S3SitemapStorage {
2025
2166
  const originalKey = shadowFile.Key.replace(/\.shadow\.[^/]+$/, "");
2026
2167
  const _originalFilename = originalKey.replace(prefix, "");
2027
2168
  if (this.shadowMode === "atomic") {
2028
- await s3.client.send(new s3.CopyObjectCommand({
2029
- Bucket: this.bucket,
2030
- CopySource: `${this.bucket}/${shadowFile.Key}`,
2031
- Key: originalKey,
2032
- ContentType: "application/xml"
2033
- }));
2034
- await s3.client.send(new s3.DeleteObjectCommand({
2035
- Bucket: this.bucket,
2036
- Key: shadowFile.Key
2037
- }));
2169
+ await s3.client.send(
2170
+ new s3.CopyObjectCommand({
2171
+ Bucket: this.bucket,
2172
+ CopySource: `${this.bucket}/${shadowFile.Key}`,
2173
+ Key: originalKey,
2174
+ ContentType: "application/xml"
2175
+ })
2176
+ );
2177
+ await s3.client.send(
2178
+ new s3.DeleteObjectCommand({
2179
+ Bucket: this.bucket,
2180
+ Key: shadowFile.Key
2181
+ })
2182
+ );
2038
2183
  } else {
2039
2184
  const version = shadowId;
2040
2185
  const versionedKey = `${originalKey}.v${version}`;
2041
- await s3.client.send(new s3.CopyObjectCommand({
2042
- Bucket: this.bucket,
2043
- CopySource: `${this.bucket}/${shadowFile.Key}`,
2044
- Key: versionedKey,
2045
- ContentType: "application/xml"
2046
- }));
2047
- await s3.client.send(new s3.CopyObjectCommand({
2048
- Bucket: this.bucket,
2049
- CopySource: `${this.bucket}/${shadowFile.Key}`,
2050
- Key: originalKey,
2051
- ContentType: "application/xml"
2052
- }));
2053
- await s3.client.send(new s3.DeleteObjectCommand({
2054
- Bucket: this.bucket,
2055
- Key: shadowFile.Key
2056
- }));
2186
+ await s3.client.send(
2187
+ new s3.CopyObjectCommand({
2188
+ Bucket: this.bucket,
2189
+ CopySource: `${this.bucket}/${shadowFile.Key}`,
2190
+ Key: versionedKey,
2191
+ ContentType: "application/xml"
2192
+ })
2193
+ );
2194
+ await s3.client.send(
2195
+ new s3.CopyObjectCommand({
2196
+ Bucket: this.bucket,
2197
+ CopySource: `${this.bucket}/${shadowFile.Key}`,
2198
+ Key: originalKey,
2199
+ ContentType: "application/xml"
2200
+ })
2201
+ );
2202
+ await s3.client.send(
2203
+ new s3.DeleteObjectCommand({
2204
+ Bucket: this.bucket,
2205
+ Key: shadowFile.Key
2206
+ })
2207
+ );
2057
2208
  }
2058
2209
  }
2059
2210
  }
@@ -2065,10 +2216,12 @@ class S3SitemapStorage {
2065
2216
  const s3 = await this.getS3Client();
2066
2217
  const key = this.getKey(filename);
2067
2218
  const prefix = key.replace(/\.xml$/, "");
2068
- const listResponse = await s3.client.send(new s3.ListObjectsV2Command({
2069
- Bucket: this.bucket,
2070
- Prefix: prefix
2071
- }));
2219
+ const listResponse = await s3.client.send(
2220
+ new s3.ListObjectsV2Command({
2221
+ Bucket: this.bucket,
2222
+ Prefix: prefix
2223
+ })
2224
+ );
2072
2225
  if (!listResponse.Contents) {
2073
2226
  return [];
2074
2227
  }
@@ -2098,37 +2251,39 @@ class S3SitemapStorage {
2098
2251
  if (!exists) {
2099
2252
  throw new Error(`Version ${version} not found for ${filename}`);
2100
2253
  }
2101
- await s3.client.send(new s3.CopyObjectCommand({
2102
- Bucket: this.bucket,
2103
- CopySource: `${this.bucket}/${versionedKey}`,
2104
- Key: key,
2105
- ContentType: "application/xml"
2106
- }));
2254
+ await s3.client.send(
2255
+ new s3.CopyObjectCommand({
2256
+ Bucket: this.bucket,
2257
+ CopySource: `${this.bucket}/${versionedKey}`,
2258
+ Key: key,
2259
+ ContentType: "application/xml"
2260
+ })
2261
+ );
2107
2262
  }
2108
- }
2263
+ };
2109
2264
  export {
2110
- routeScanner,
2111
- generateI18nEntries,
2112
- SitemapStream,
2113
- SitemapIndex,
2114
- SitemapGenerator,
2115
- ShadowProcessor,
2116
- S3SitemapStorage,
2117
- RouteScanner,
2118
- RedisRedirectManager,
2119
- RedisProgressStorage,
2120
- RedisChangeTracker,
2121
- RedirectHandler,
2122
- RedirectDetector,
2123
- ProgressTracker,
2124
- OrbitSitemap,
2125
- MemorySitemapStorage,
2126
- MemoryRedirectManager,
2127
- MemoryProgressStorage,
2128
- MemoryChangeTracker,
2129
- IncrementalGenerator,
2130
- GenerateSitemapJob,
2131
- GCPSitemapStorage,
2265
+ DiffCalculator,
2132
2266
  DiskSitemapStorage,
2133
- DiffCalculator
2267
+ GCPSitemapStorage,
2268
+ GenerateSitemapJob,
2269
+ IncrementalGenerator,
2270
+ MemoryChangeTracker,
2271
+ MemoryProgressStorage,
2272
+ MemoryRedirectManager,
2273
+ MemorySitemapStorage,
2274
+ OrbitSitemap,
2275
+ ProgressTracker,
2276
+ RedirectDetector,
2277
+ RedirectHandler,
2278
+ RedisChangeTracker,
2279
+ RedisProgressStorage,
2280
+ RedisRedirectManager,
2281
+ RouteScanner,
2282
+ S3SitemapStorage,
2283
+ ShadowProcessor,
2284
+ SitemapGenerator,
2285
+ SitemapIndex,
2286
+ SitemapStream,
2287
+ generateI18nEntries,
2288
+ routeScanner
2134
2289
  };