@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.
- package/dist/index.mjs +47 -13
- 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 =
|
|
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.
|
|
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.
|
|
32
|
-
"@openspecui/search": "6.0.0-beta.
|
|
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",
|