fix(web): lazy-init extraction + blob clients, add use-client to notes-client
- extraction-client.ts: lazy singleton (SSR crash fix) [A1] - blob-client.ts: lazy singleton + remove dead re-export [A1] - notes-client.ts: add "use client" directive [A6] - next.config.ts: add output: "standalone" [A2] - Delete mock-data.ts and review-data.ts (dead code) [D3, D4]
This commit is contained in:
parent
e50df779da
commit
dbb1a84dba
@ -21,6 +21,7 @@ const securityHeaders = [
|
||||
];
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
outputFileTracingRoot: path.join(process.cwd(), ".."),
|
||||
async headers() {
|
||||
return [
|
||||
|
||||
@ -8,19 +8,25 @@ function getAccessToken(): string | null {
|
||||
return localStorage.getItem(`${PRODUCT_ID}_access_token`);
|
||||
}
|
||||
|
||||
const blobClient = createBlobClient({
|
||||
baseUrl: PLATFORM_SERVICE_URL,
|
||||
productId: PRODUCT_ID,
|
||||
getAccessToken,
|
||||
});
|
||||
let _blobClient: ReturnType<typeof createBlobClient> | null = null;
|
||||
function getBlobClient() {
|
||||
if (!_blobClient) {
|
||||
_blobClient = createBlobClient({
|
||||
baseUrl: PLATFORM_SERVICE_URL,
|
||||
productId: PRODUCT_ID,
|
||||
getAccessToken,
|
||||
});
|
||||
}
|
||||
return _blobClient;
|
||||
}
|
||||
|
||||
export async function getArtifactReadUrl(blobPath: string): Promise<string> {
|
||||
const sas = await blobClient.getSasUrl("attachments", blobPath, "r", 15);
|
||||
const sas = await getBlobClient().getSasUrl("attachments", blobPath, "r", 15);
|
||||
return sas.sasUrl;
|
||||
}
|
||||
|
||||
export async function getArtifactUploadUrl(blobPath: string): Promise<string> {
|
||||
const sas = await blobClient.getSasUrl("attachments", blobPath, "rw", 30);
|
||||
const sas = await getBlobClient().getSasUrl("attachments", blobPath, "rw", 30);
|
||||
return sas.sasUrl;
|
||||
}
|
||||
|
||||
@ -28,7 +34,7 @@ export async function uploadArtifact(
|
||||
file: File,
|
||||
blobPath: string,
|
||||
): Promise<{ blobPath: string; contentType: string; sizeBytes: number }> {
|
||||
await blobClient.upload("attachments", file, {
|
||||
await getBlobClient().upload("attachments", file, {
|
||||
contentType: file.type || "application/octet-stream",
|
||||
blobName: blobPath,
|
||||
});
|
||||
@ -41,8 +47,6 @@ export async function uploadArtifact(
|
||||
}
|
||||
|
||||
export async function downloadArtifact(blobPath: string): Promise<Blob> {
|
||||
const response = await blobClient.download("attachments", blobPath);
|
||||
const response = await getBlobClient().download("attachments", blobPath);
|
||||
return response.blob();
|
||||
}
|
||||
|
||||
export { blobClient };
|
||||
|
||||
@ -22,10 +22,16 @@ function getAccessToken(): string | null {
|
||||
return localStorage.getItem(`${PRODUCT_ID}_access_token`);
|
||||
}
|
||||
|
||||
const extractionApi = createApiClient({
|
||||
baseUrl: EXTRACTION_SERVICE_URL,
|
||||
getToken: getAccessToken,
|
||||
});
|
||||
let _extractionApi: ReturnType<typeof createApiClient> | null = null;
|
||||
function getExtractionApi() {
|
||||
if (!_extractionApi) {
|
||||
_extractionApi = createApiClient({
|
||||
baseUrl: EXTRACTION_SERVICE_URL,
|
||||
getToken: getAccessToken,
|
||||
});
|
||||
}
|
||||
return _extractionApi;
|
||||
}
|
||||
|
||||
function slugify(value: string): string {
|
||||
return value
|
||||
@ -36,7 +42,7 @@ function slugify(value: string): string {
|
||||
}
|
||||
|
||||
export async function extractSuggestedTasks(text: string): Promise<NoteTask[]> {
|
||||
const response = await extractionApi.fetch<ExtractResponse>("/api/extract", {
|
||||
const response = await getExtractionApi().fetch<ExtractResponse>("/api/extract", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
text,
|
||||
|
||||
@ -1,227 +0,0 @@
|
||||
import type {
|
||||
AgentTimelineItem,
|
||||
NoteDetail,
|
||||
NoteSummary,
|
||||
OperatorWorkflowSummary,
|
||||
SavedViewSummary,
|
||||
WorkspaceSummary,
|
||||
} from "@/lib/types";
|
||||
|
||||
export const mockWorkspaces: WorkspaceSummary[] = [
|
||||
{
|
||||
id: "workspace-product",
|
||||
name: "Product Strategy",
|
||||
description: "PRDs, roadmap cuts, launch tradeoffs, and operating decisions.",
|
||||
owner: "Product Lead",
|
||||
noteCount: 18,
|
||||
visibility: "shared",
|
||||
updatedAt: "2026-03-10T14:35:00.000Z",
|
||||
tags: ["strategy", "launch", "roadmap"],
|
||||
},
|
||||
{
|
||||
id: "workspace-research",
|
||||
name: "Research Ops",
|
||||
description: "Source notes, citations, summaries, and extracted action items.",
|
||||
owner: "Research Lead",
|
||||
noteCount: 42,
|
||||
visibility: "private",
|
||||
updatedAt: "2026-03-10T13:10:00.000Z",
|
||||
tags: ["research", "sources", "agent-ready"],
|
||||
},
|
||||
{
|
||||
id: "workspace-platform",
|
||||
name: "Platform Coordination",
|
||||
description: "Shared-platform integration notes and handoffs for other agents.",
|
||||
owner: "Platform Agent",
|
||||
noteCount: 11,
|
||||
visibility: "shared",
|
||||
updatedAt: "2026-03-10T11:05:00.000Z",
|
||||
tags: ["platform", "handoff", "integration"],
|
||||
},
|
||||
];
|
||||
|
||||
export const mockNotes: NoteSummary[] = [
|
||||
{
|
||||
id: "note-prd-cutline",
|
||||
workspaceId: "workspace-product",
|
||||
title: "MVP cut line for agentic notes launch",
|
||||
excerpt: "Define which note, task, search, and approval flows must exist before wider rollout.",
|
||||
status: "active",
|
||||
tags: ["mvp", "launch", "scope"],
|
||||
updatedAt: "2026-03-10T14:30:00.000Z",
|
||||
updatedBy: "Product Lead",
|
||||
},
|
||||
{
|
||||
id: "note-web-shell",
|
||||
workspaceId: "workspace-product",
|
||||
title: "Web shell execution plan",
|
||||
excerpt: "Scaffold authenticated layout, notes routes, search, settings, and operator affordances.",
|
||||
status: "draft",
|
||||
tags: ["web", "execution", "ui"],
|
||||
updatedAt: "2026-03-10T14:10:00.000Z",
|
||||
updatedBy: "Web Agent",
|
||||
},
|
||||
{
|
||||
id: "note-platform-deps",
|
||||
workspaceId: "workspace-platform",
|
||||
title: "Platform integration dependencies for v1",
|
||||
excerpt: "Identify auth, telemetry, diagnostics, blob, and extraction dependencies that must be wired from day one.",
|
||||
status: "active",
|
||||
tags: ["platform", "telemetry", "auth"],
|
||||
updatedAt: "2026-03-10T13:00:00.000Z",
|
||||
updatedBy: "Platform Agent",
|
||||
},
|
||||
{
|
||||
id: "note-research-sources",
|
||||
workspaceId: "workspace-research",
|
||||
title: "Source summary ingestion workflow",
|
||||
excerpt: "Map external research capture into note creation, citation linking, and downstream task extraction.",
|
||||
status: "active",
|
||||
tags: ["research", "sources", "workflow"],
|
||||
updatedAt: "2026-03-10T12:15:00.000Z",
|
||||
updatedBy: "Research Agent",
|
||||
},
|
||||
];
|
||||
|
||||
export const mockSavedViews: SavedViewSummary[] = [
|
||||
{
|
||||
id: "view-all-workspaces",
|
||||
name: "All workspaces",
|
||||
scope: "workspace",
|
||||
description: "Default operational workspace listing across all note domains.",
|
||||
query: "visibility:any sort:updated",
|
||||
resultCount: 3,
|
||||
},
|
||||
{
|
||||
id: "view-launch-readiness",
|
||||
name: "Launch readiness",
|
||||
scope: "search",
|
||||
description: "Notes tagged for launch, cut-line, approval, and release follow-up.",
|
||||
query: "tag:launch tag:mvp status:active",
|
||||
resultCount: 2,
|
||||
},
|
||||
{
|
||||
id: "view-agent-review",
|
||||
name: "Agent review queue",
|
||||
scope: "review",
|
||||
description: "Proposals and approval items requiring operator attention.",
|
||||
query: "review:proposed severity:medium+",
|
||||
resultCount: 2,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockOperatorWorkflows: OperatorWorkflowSummary[] = [
|
||||
{
|
||||
id: "workflow-approvals",
|
||||
name: "Approval triage",
|
||||
queueCount: 2,
|
||||
owner: "Operator",
|
||||
sla: "< 4h",
|
||||
status: "healthy",
|
||||
},
|
||||
{
|
||||
id: "workflow-artifacts",
|
||||
name: "Artifact follow-up",
|
||||
queueCount: 3,
|
||||
owner: "Knowledge Ops",
|
||||
sla: "< 1d",
|
||||
status: "at_risk",
|
||||
},
|
||||
{
|
||||
id: "workflow-search-gaps",
|
||||
name: "Search quality review",
|
||||
queueCount: 1,
|
||||
owner: "Web Agent",
|
||||
sla: "< 2d",
|
||||
status: "healthy",
|
||||
},
|
||||
];
|
||||
|
||||
const mockTimeline: AgentTimelineItem[] = [
|
||||
{
|
||||
id: "event-1",
|
||||
actor: "Research Agent",
|
||||
action: "Proposed metadata enrichment",
|
||||
timestamp: "2026-03-10 08:11",
|
||||
status: "proposed",
|
||||
summary: "Suggested new tags, source metadata, and a research confidence note.",
|
||||
},
|
||||
{
|
||||
id: "event-2",
|
||||
actor: "Workflow Agent",
|
||||
action: "Extracted task candidates",
|
||||
timestamp: "2026-03-10 08:08",
|
||||
status: "draft",
|
||||
summary: "Produced three task candidates linked to the current note body.",
|
||||
},
|
||||
{
|
||||
id: "event-3",
|
||||
actor: "Operator",
|
||||
action: "Approved editorial summary",
|
||||
timestamp: "2026-03-10 08:03",
|
||||
status: "approved",
|
||||
summary: "Accepted a summary rewrite and preserved original source wording in history.",
|
||||
},
|
||||
];
|
||||
|
||||
export const mockNoteDetails: Record<string, NoteDetail> = {
|
||||
"note-prd-cutline": {
|
||||
...mockNotes[0],
|
||||
body: "# MVP Cut Line\n\nThe initial release must feel trustworthy for both humans and agents.\n\n## Must ship\n- Web authoring shell\n- Notes and workspace routes\n- Search and retrieval entry points\n- Agent review and approval placeholders\n\n## Defer if needed\n- Rich artifact upload orchestration\n- Advanced semantic ranking\n- Deep operator analytics",
|
||||
metadata: {
|
||||
owner: "Product Lead",
|
||||
source: "manual",
|
||||
reviewState: "approved",
|
||||
taskCount: 3,
|
||||
artifactCount: 2,
|
||||
},
|
||||
linkedNotes: [
|
||||
{ id: "note-web-shell", title: "Web shell execution plan", relationship: "implements" },
|
||||
{ id: "note-platform-deps", title: "Platform integration dependencies for v1", relationship: "depends_on" },
|
||||
],
|
||||
tasks: [
|
||||
{ id: "task-w0", title: "Stand up web shell", status: "in_progress", source: "manual" },
|
||||
{ id: "task-w1", title: "Define backend integration boundary", status: "todo", source: "manual" },
|
||||
{ id: "task-w2", title: "Review approval UX cut line", status: "todo", source: "agent" },
|
||||
],
|
||||
artifacts: [
|
||||
{ id: "artifact-prd", name: "PRD v2.0", type: "document", status: "ready" },
|
||||
{ id: "artifact-roadmap", name: "Roadmap execution sheet", type: "document", status: "ready" },
|
||||
],
|
||||
timeline: mockTimeline,
|
||||
},
|
||||
"note-web-shell": {
|
||||
...mockNotes[1],
|
||||
body: "# Web Shell Execution Plan\n\nUse existing ByteLyst web patterns.\n\n- App Router shell\n- Shared clients and tokens\n- Workspace-first navigation\n- Mock data until backend contracts stabilize",
|
||||
metadata: {
|
||||
owner: "Web Agent",
|
||||
source: "agent",
|
||||
reviewState: "proposed",
|
||||
taskCount: 2,
|
||||
artifactCount: 1,
|
||||
},
|
||||
linkedNotes: [
|
||||
{ id: "note-prd-cutline", title: "MVP cut line for agentic notes launch", relationship: "supports" },
|
||||
],
|
||||
tasks: [
|
||||
{ id: "task-shell-1", title: "Create route skeleton", status: "done", source: "agent" },
|
||||
{ id: "task-shell-2", title: "Wire providers", status: "in_progress", source: "agent" },
|
||||
],
|
||||
artifacts: [
|
||||
{ id: "artifact-shell", name: "UI scaffold", type: "ui", status: "processing" },
|
||||
],
|
||||
timeline: mockTimeline,
|
||||
},
|
||||
};
|
||||
|
||||
export function getWorkspaceById(id: string) {
|
||||
return mockWorkspaces.find((workspace) => workspace.id === id) ?? null;
|
||||
}
|
||||
|
||||
export function getNoteById(id: string) {
|
||||
return mockNoteDetails[id] ?? null;
|
||||
}
|
||||
|
||||
export function getNotesForWorkspace(workspaceId: string) {
|
||||
return mockNotes.filter((note) => note.workspaceId === workspaceId);
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { extractSuggestedTasks } from "@/lib/extraction-client";
|
||||
import { createNotesApiClient } from "@/lib/api-helpers";
|
||||
import type { AgentTimelineItem, ArtifactSummary, LinkedNote, NoteDetail, NoteSummary, NoteTask, WorkspaceSummary } from "@/lib/types";
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import type { AgentTimelineItem } from "@/lib/types";
|
||||
|
||||
export const mockAgentTimeline: AgentTimelineItem[] = [
|
||||
{
|
||||
id: "event-1",
|
||||
actor: "Research Agent",
|
||||
action: "Proposed metadata enrichment",
|
||||
timestamp: "2026-03-10 08:11",
|
||||
status: "proposed",
|
||||
summary: "Suggested new tags, source metadata, and a research confidence note.",
|
||||
},
|
||||
{
|
||||
id: "event-2",
|
||||
actor: "Workflow Agent",
|
||||
action: "Extracted task candidates",
|
||||
timestamp: "2026-03-10 08:08",
|
||||
status: "draft",
|
||||
summary: "Produced three task candidates linked to the current note body.",
|
||||
},
|
||||
{
|
||||
id: "event-3",
|
||||
actor: "Operator",
|
||||
action: "Approved editorial summary",
|
||||
timestamp: "2026-03-10 08:03",
|
||||
status: "approved",
|
||||
summary: "Accepted a summary rewrite and preserved original source wording in history.",
|
||||
},
|
||||
];
|
||||
|
||||
export const mockApprovalQueue = [
|
||||
{
|
||||
id: "approval-1",
|
||||
title: "Apply extracted task set to MVP cut line note",
|
||||
owner: "Workflow Agent",
|
||||
severity: "medium",
|
||||
status: "awaiting_review",
|
||||
},
|
||||
{
|
||||
id: "approval-2",
|
||||
title: "Approve note summary rewrite for Product Strategy workspace",
|
||||
owner: "Research Agent",
|
||||
severity: "low",
|
||||
status: "awaiting_review",
|
||||
},
|
||||
];
|
||||
Loading…
Reference in New Issue
Block a user