@openspecui/server 6.0.0-beta.0 → 6.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +47 -13
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -8851,7 +8851,32 @@ var DashboardOverviewService = class {
8851
8851
 
8852
8852
  //#endregion
8853
8853
  //#region ../core/src/dashboard-display.ts
8854
+ /**
8855
+ * Orthogonal intents (updated 2026-07-28 Asia/Shanghai):
8856
+ * 1. Select bounded Dashboard rows with deterministic ordering.
8857
+ * 2. Resolve archive dates identically across Server and browser/static consumers.
8858
+ * 3. Apply the objective bounded archive-range policy without owning UI state.
8859
+ *
8860
+ * Original request (2026-07-28): replace Dashboard Workflow Progress with ReadonlyKanban.
8861
+ */
8854
8862
  const DASHBOARD_RECENT_LIST_LIMIT = 10;
8863
+ const ARCHIVE_DATE_PREFIX = /^(\d{4})-(\d{2})-(\d{2})(?:-|$)/;
8864
+ /** Parse a valid UTC calendar date from an archive id prefix. */
8865
+ function parseDatedArchiveIdTimestamp(id) {
8866
+ const match$1 = ARCHIVE_DATE_PREFIX.exec(id);
8867
+ if (!match$1) return null;
8868
+ const year = Number(match$1[1]);
8869
+ const month = Number(match$1[2]);
8870
+ const day = Number(match$1[3]);
8871
+ const timestamp = Date.UTC(year, month - 1, day);
8872
+ const parsed = new Date(timestamp);
8873
+ if (!Number.isFinite(timestamp) || parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== day) return null;
8874
+ return timestamp;
8875
+ }
8876
+ /** Resolve the archive timestamp from its dated id, then its physical update time. */
8877
+ function resolveArchiveTimestamp(archive) {
8878
+ return parseDatedArchiveIdTimestamp(archive.id) ?? archive.updatedAt;
8879
+ }
8855
8880
  function compareDashboardItemsByUpdatedAt(left, right) {
8856
8881
  return right.updatedAt - left.updatedAt || left.id.localeCompare(right.id);
8857
8882
  }
@@ -8861,6 +8886,10 @@ function sortDashboardItemsByUpdatedAt(items) {
8861
8886
  function selectRecentDashboardItems(items, limit = DASHBOARD_RECENT_LIST_LIMIT) {
8862
8887
  return sortDashboardItemsByUpdatedAt(items).slice(0, Math.max(0, Math.trunc(limit)));
8863
8888
  }
8889
+ /** Select recent archives by objective archive time rather than incidental directory mtime. */
8890
+ function selectRecentDashboardArchives(items, limit = DASHBOARD_RECENT_LIST_LIMIT) {
8891
+ return [...items].sort((left, right) => resolveArchiveTimestamp(right) - resolveArchiveTimestamp(left) || left.id.localeCompare(right.id)).slice(0, Math.max(0, Math.trunc(limit)));
8892
+ }
8864
8893
 
8865
8894
  //#endregion
8866
8895
  //#region src/dashboard-summary.ts
@@ -8901,6 +8930,19 @@ function buildDashboardSummaryProjection(facts) {
8901
8930
  const archivedTasksCompleted = facts.archiveMetas.reduce((sum, archive) => sum + archive.trackedTaskProgress.completed, 0);
8902
8931
  const taskCompletionPercent = tasksTotal > 0 ? Math.round(tasksCompleted / tasksTotal * 100) : null;
8903
8932
  const inProgressChanges = facts.allActiveChanges.filter((change) => change.trackedTaskProgress.phase === "in-progress").length;
8933
+ const trackedTaskPhaseCounts = {
8934
+ "no-tasks": 0,
8935
+ "in-progress": 0,
8936
+ complete: 0
8937
+ };
8938
+ for (const change of facts.allActiveChanges) trackedTaskPhaseCounts[change.trackedTaskProgress.phase] += 1;
8939
+ const recentArchives = selectRecentDashboardArchives(facts.archiveMetas).map((archive) => ({
8940
+ id: archive.id,
8941
+ name: archive.name,
8942
+ trackedTaskProgress: archive.trackedTaskProgress,
8943
+ archivedAt: resolveArchiveTimestamp(archive),
8944
+ updatedAt: archive.updatedAt
8945
+ }));
8904
8946
  return {
8905
8947
  summary: {
8906
8948
  specifications: facts.allSpecifications.length,
@@ -8914,7 +8956,9 @@ function buildDashboardSummaryProjection(facts) {
8914
8956
  taskCompletionPercent
8915
8957
  },
8916
8958
  specifications: selectRecentDashboardItems(facts.allSpecifications),
8917
- activeChanges: selectRecentDashboardItems(facts.allActiveChanges)
8959
+ activeChanges: selectRecentDashboardItems(facts.allActiveChanges),
8960
+ trackedTaskPhaseCounts,
8961
+ recentArchives
8918
8962
  };
8919
8963
  }
8920
8964
  /** Load the first stable Dashboard projection. */
@@ -9020,6 +9064,7 @@ function buildDashboardTimeTrends(options) {
9020
9064
  * 1. Load optional Dashboard historical trend facts independently from first-screen Summary.
9021
9065
  * 2. Preserve objective availability and tracked-artifact semantics for each metric.
9022
9066
  * 3. Keep trend configuration and timeline calculation outside Git snapshot delivery.
9067
+ * 4. Reuse the shared archive-date parser used by Dashboard Kanban projections.
9023
9068
  *
9024
9069
  * Original request (2026-07-23): "现在页面数据的加载数据非常慢(比如dashboard页面、changes页面都要等待非常久,页面刷新后,似乎后台没有缓存一样,也要加载很久。"
9025
9070
  */
@@ -9031,17 +9076,6 @@ function resolveTrendTimestamp(primary, secondary) {
9031
9076
  if (typeof secondary === "number" && Number.isFinite(secondary) && secondary > 0) return secondary;
9032
9077
  return null;
9033
9078
  }
9034
- function parseDatedIdTimestamp(id) {
9035
- const match$1 = /^(\d{4})-(\d{2})-(\d{2})(?:-|$)/.exec(id);
9036
- if (!match$1) return null;
9037
- const year = Number(match$1[1]);
9038
- const month = Number(match$1[2]);
9039
- const day = Number(match$1[3]);
9040
- if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) return null;
9041
- if (month < 1 || month > 12 || day < 1 || day > 31) return null;
9042
- const timestamp = Date.UTC(year, month - 1, day);
9043
- return Number.isFinite(timestamp) ? timestamp : null;
9044
- }
9045
9079
  /** Load optional historical Dashboard facts without waiting for a Git snapshot. */
9046
9080
  async function loadDashboardTrends(ctx) {
9047
9081
  const [facts, config] = await Promise.all([loadDashboardPlanningFacts({ adapter: ctx.adapter }), ctx.configManager.readConfig()]);
@@ -9053,7 +9087,7 @@ async function loadDashboardTrends(ctx) {
9053
9087
  }];
9054
9088
  });
9055
9089
  const completedTrendEvents = facts.archiveMetas.flatMap((archive) => {
9056
- const timestamp = parseDatedIdTimestamp(archive.id) ?? resolveTrendTimestamp(archive.updatedAt, archive.createdAt);
9090
+ const timestamp = parseDatedArchiveIdTimestamp(archive.id) ?? resolveTrendTimestamp(archive.updatedAt, archive.createdAt);
9057
9091
  return timestamp === null ? [] : [{
9058
9092
  ts: timestamp,
9059
9093
  value: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "6.0.0-beta.0",
3
+ "version": "6.0.0-beta.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {
@@ -28,8 +28,8 @@
28
28
  "@hono/node-server": "^1.14.1",
29
29
  "@huggingface/hub": "^2.12.0",
30
30
  "@lydell/node-pty": "^1.1.0",
31
- "@openspecui/core": "6.0.0-beta.0",
32
- "@openspecui/search": "6.0.0-beta.0",
31
+ "@openspecui/core": "6.0.0-beta.1",
32
+ "@openspecui/search": "6.0.0-beta.1",
33
33
  "@trpc/server": "^11.0.0",
34
34
  "better-sqlite3": "^12.5.0",
35
35
  "hono": "^4.7.3",