diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 00000000..ad64a635 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,3 @@ +{ + "hooks": {} +} \ No newline at end of file diff --git a/.codex/hooks/omx/post-tool-use.mjs b/.codex/hooks/omx/post-tool-use.mjs new file mode 100644 index 00000000..adbc44b3 --- /dev/null +++ b/.codex/hooks/omx/post-tool-use.mjs @@ -0,0 +1,49 @@ +#!/usr/bin/env node +import { appendFileSync, mkdirSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { execSync } from "node:child_process"; + +function rootDir() { + try { + return execSync("git rev-parse --show-toplevel", { + cwd: process.cwd(), + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + } catch { + return process.cwd(); + } +} + +function readStdin() { + try { + return JSON.parse(readFileSync(0, "utf8") || "{}"); + } catch { + return {}; + } +} + +const payload = readStdin(); +const root = rootDir(); +mkdirSync(join(root, ".omx", "logs"), { recursive: true }); +const exitCode = + typeof payload?.tool_output?.exit_code === "number" + ? payload.tool_output.exit_code + : typeof payload?.exit_code === "number" + ? payload.exit_code + : null; +const status = exitCode ?? "unknown"; +const isExplicitBashFailure = payload?.tool_name === "Bash" && exitCode !== null && exitCode !== 0; +appendFileSync( + join(root, ".omx", "logs", "hooks.log"), + `${new Date().toISOString()} PostToolUse status=${status} ${JSON.stringify(payload)}\n`, + "utf8", +); + +process.stdout.write(JSON.stringify({ + continue: true, + hookSpecificOutput: { + hookEventName: "PostToolUse", + additionalContext: isExplicitBashFailure ? "OMX noticed a failing Bash command. Check the team inbox or diagnostics." : "", + }, +})); diff --git a/.codex/hooks/omx/pre-tool-use.mjs b/.codex/hooks/omx/pre-tool-use.mjs new file mode 100644 index 00000000..0def4813 --- /dev/null +++ b/.codex/hooks/omx/pre-tool-use.mjs @@ -0,0 +1,37 @@ +#!/usr/bin/env node +import { readFileSync } from "node:fs"; + +function readStdin() { + try { + return JSON.parse(readFileSync(0, "utf8") || "{}"); + } catch { + return {}; + } +} + +const payload = readStdin(); +const command = JSON.stringify(payload).toLowerCase(); +const blocked = [ + "rm -rf /", + "mkfs", + "dd if=", + "shutdown", + "reboot", + "poweroff", + "git reset --hard", +]; + +if (blocked.some((token) => command.includes(token))) { + process.stdout.write(JSON.stringify({ + decision: "block", + reason: "OMX safety hook blocked an obviously destructive command.", + })); + process.exit(0); +} + +process.stdout.write(JSON.stringify({ + continue: true, + hookSpecificOutput: { + hookEventName: "PreToolUse", + }, +})); diff --git a/.codex/hooks/omx/session-start.mjs b/.codex/hooks/omx/session-start.mjs new file mode 100644 index 00000000..354631f4 --- /dev/null +++ b/.codex/hooks/omx/session-start.mjs @@ -0,0 +1,58 @@ +#!/usr/bin/env node +import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { execSync } from "node:child_process"; + +function rootDir() { + try { + return execSync("git rev-parse --show-toplevel", { + cwd: process.cwd(), + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + } catch { + return process.cwd(); + } +} + +function readStdin() { + try { + return JSON.parse(readFileSync(0, "utf8") || "{}"); + } catch { + return {}; + } +} + +function readJson(path, fallback) { + try { + return JSON.parse(readFileSync(path, "utf8")); + } catch { + return fallback; + } +} + +function writeJson(path, value) { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, JSON.stringify(value, null, 2), "utf8"); +} + +const root = rootDir(); +const payload = readStdin(); +const omx = join(root, ".omx"); +mkdirSync(join(omx, "logs"), { recursive: true }); +appendFileSync(join(omx, "logs", "hooks.log"), `${new Date().toISOString()} SessionStart ${JSON.stringify(payload)}\n`, "utf8"); + +const runtimePath = join(omx, "state", "hook-runtime.json"); +const runtime = readJson(runtimePath, {}); +writeJson(runtimePath, { + ...runtime, + lastSessionStartAt: new Date().toISOString(), +}); + +process.stdout.write(JSON.stringify({ + continue: true, + hookSpecificOutput: { + hookEventName: "SessionStart", + additionalContext: "", + }, +})); diff --git a/.codex/hooks/omx/stop.mjs b/.codex/hooks/omx/stop.mjs new file mode 100644 index 00000000..a11f5eb9 --- /dev/null +++ b/.codex/hooks/omx/stop.mjs @@ -0,0 +1,54 @@ +#!/usr/bin/env node +import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { execSync } from "node:child_process"; + +function rootDir() { + try { + return execSync("git rev-parse --show-toplevel", { + cwd: process.cwd(), + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + } catch { + return process.cwd(); + } +} + +function readStdin() { + try { + return JSON.parse(readFileSync(0, "utf8") || "{}"); + } catch { + return {}; + } +} + +function readJson(path, fallback) { + try { + return JSON.parse(readFileSync(path, "utf8")); + } catch { + return fallback; + } +} + +function writeJson(path, value) { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, JSON.stringify(value, null, 2), "utf8"); +} + +const root = rootDir(); +const payload = readStdin(); +const omx = join(root, ".omx"); +mkdirSync(join(omx, "logs"), { recursive: true }); +appendFileSync(join(omx, "logs", "hooks.log"), `${new Date().toISOString()} Stop ${JSON.stringify(payload)}\n`, "utf8"); + +const runtimePath = join(omx, "state", "hook-runtime.json"); +const runtime = readJson(runtimePath, {}); +writeJson(runtimePath, { + ...runtime, + lastStopAt: new Date().toISOString(), +}); + +process.stdout.write(JSON.stringify({ + continue: true, +})); diff --git a/.codex/hooks/omx/user-prompt-submit.mjs b/.codex/hooks/omx/user-prompt-submit.mjs new file mode 100644 index 00000000..ce6c3cef --- /dev/null +++ b/.codex/hooks/omx/user-prompt-submit.mjs @@ -0,0 +1,58 @@ +#!/usr/bin/env node +import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { execSync } from "node:child_process"; + +function rootDir() { + try { + return execSync("git rev-parse --show-toplevel", { + cwd: process.cwd(), + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }).trim(); + } catch { + return process.cwd(); + } +} + +function readStdin() { + try { + return JSON.parse(readFileSync(0, "utf8") || "{}"); + } catch { + return {}; + } +} + +function readJson(path, fallback) { + try { + return JSON.parse(readFileSync(path, "utf8")); + } catch { + return fallback; + } +} + +function writeJson(path, value) { + mkdirSync(dirname(path), { recursive: true }); + writeFileSync(path, JSON.stringify(value, null, 2), "utf8"); +} + +const root = rootDir(); +const payload = readStdin(); +const omx = join(root, ".omx"); +mkdirSync(join(omx, "logs"), { recursive: true }); +appendFileSync(join(omx, "logs", "hooks.log"), `${new Date().toISOString()} UserPromptSubmit ${JSON.stringify(payload)}\n`, "utf8"); + +const runtimePath = join(omx, "state", "hook-runtime.json"); +const runtime = readJson(runtimePath, {}); +writeJson(runtimePath, { + ...runtime, + lastPromptAt: new Date().toISOString(), +}); + +process.stdout.write(JSON.stringify({ + continue: true, + hookSpecificOutput: { + hookEventName: "UserPromptSubmit", + additionalContext: "", + }, +})); diff --git a/.gitignore b/.gitignore index 9cc43a4e..18b367b1 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ wolai-frontend/public/documents/** artifacts/ artifacts/** tmp +design \ No newline at end of file diff --git a/.harness-active b/.harness-active new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.harness-active @@ -0,0 +1 @@ + diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 00000000..206b5f4a --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,309 @@ +# Design System Inspired by Notion + +## 1. Visual Theme & Atmosphere + +Notion's website embodies the philosophy of the tool itself: a blank canvas that gets out of your way. The design system is built on warm neutrals rather than cold grays, creating a distinctly approachable minimalism that feels like quality paper rather than sterile glass. The page canvas is pure white (`#ffffff`) but the text isn't pure black -- it's a warm near-black (`rgba(0,0,0,0.95)`) that softens the reading experience imperceptibly. The warm gray scale (`#f6f5f4`, `#31302e`, `#615d59`, `#a39e98`) carries subtle yellow-brown undertones, giving the interface a tactile, almost analog warmth. + +The custom NotionInter font (a modified Inter) is the backbone of the system. At display sizes (64px), it uses aggressive negative letter-spacing (-2.125px), creating headlines that feel compressed and precise. The weight range is broader than typical systems: 400 for body, 500 for UI elements, 600 for semi-bold labels, and 700 for display headings. OpenType features `"lnum"` (lining numerals) and `"locl"` (localized forms) are enabled on larger text, adding typographic sophistication that rewards close reading. + +What makes Notion's visual language distinctive is its border philosophy. Rather than heavy borders or shadows, Notion uses ultra-thin `1px solid rgba(0,0,0,0.1)` borders -- borders that exist as whispers, barely perceptible division lines that create structure without weight. The shadow system is equally restrained: multi-layer stacks with cumulative opacity never exceeding 0.05, creating depth that's felt rather than seen. + +**Key Characteristics:** +- NotionInter (modified Inter) with negative letter-spacing at display sizes (-2.125px at 64px) +- Warm neutral palette: grays carry yellow-brown undertones (`#f6f5f4` warm white, `#31302e` warm dark) +- Near-black text via `rgba(0,0,0,0.95)` -- not pure black, creating micro-warmth +- Ultra-thin borders: `1px solid rgba(0,0,0,0.1)` throughout -- whisper-weight division +- Multi-layer shadow stacks with sub-0.05 opacity for barely-there depth +- Notion Blue (`#0075de`) as the singular accent color for CTAs and interactive elements +- Pill badges (9999px radius) with tinted blue backgrounds for status indicators +- 8px base spacing unit with an organic, non-rigid scale + +## 2. Color Palette & Roles + +### Primary +- **Notion Black** (`rgba(0,0,0,0.95)` / `#000000f2`): Primary text, headings, body copy. The 95% opacity softens pure black without sacrificing readability. +- **Pure White** (`#ffffff`): Page background, card surfaces, button text on blue. +- **Notion Blue** (`#0075de`): Primary CTA, link color, interactive accent -- the only saturated color in the core UI chrome. + +### Brand Secondary +- **Deep Navy** (`#213183`): Secondary brand color, used sparingly for emphasis and dark feature sections. +- **Active Blue** (`#005bab`): Button active/pressed state -- darker variant of Notion Blue. + +### Warm Neutral Scale +- **Warm White** (`#f6f5f4`): Background surface tint, section alternation, subtle card fill. The yellow undertone is key. +- **Warm Dark** (`#31302e`): Dark surface background, dark section text. Warmer than standard grays. +- **Warm Gray 500** (`#615d59`): Secondary text, descriptions, muted labels. +- **Warm Gray 300** (`#a39e98`): Placeholder text, disabled states, caption text. + +### Semantic Accent Colors +- **Teal** (`#2a9d99`): Success states, positive indicators. +- **Green** (`#1aae39`): Confirmation, completion badges. +- **Orange** (`#dd5b00`): Warning states, attention indicators. +- **Pink** (`#ff64c8`): Decorative accent, feature highlights. +- **Purple** (`#391c57`): Premium features, deep accents. +- **Brown** (`#523410`): Earthy accent, warm feature sections. + +### Interactive +- **Link Blue** (`#0075de`): Primary link color with underline-on-hover. +- **Link Light Blue** (`#62aef0`): Lighter link variant for dark backgrounds. +- **Focus Blue** (`#097fe8`): Focus ring on interactive elements. +- **Badge Blue Bg** (`#f2f9ff`): Pill badge background, tinted blue surface. +- **Badge Blue Text** (`#097fe8`): Pill badge text, darker blue for readability. + +### Shadows & Depth +- **Card Shadow** (`rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px`): Multi-layer card elevation. +- **Deep Shadow** (`rgba(0,0,0,0.01) 0px 1px 3px, rgba(0,0,0,0.02) 0px 3px 7px, rgba(0,0,0,0.02) 0px 7px 15px, rgba(0,0,0,0.04) 0px 14px 28px, rgba(0,0,0,0.05) 0px 23px 52px`): Five-layer deep elevation for modals and featured content. +- **Whisper Border** (`1px solid rgba(0,0,0,0.1)`): Standard division border -- cards, dividers, sections. + +## 3. Typography Rules + +### Font Family +- **Primary**: `NotionInter`, with fallbacks: `Inter, -apple-system, system-ui, Segoe UI, Helvetica, Apple Color Emoji, Arial, Segoe UI Emoji, Segoe UI Symbol` +- **OpenType Features**: `"lnum"` (lining numerals) and `"locl"` (localized forms) enabled on display and heading text. + +### Hierarchy + +| Role | Font | Size | Weight | Line Height | Letter Spacing | Notes | +|------|------|------|--------|-------------|----------------|-------| +| Display Hero | NotionInter | 64px (4.00rem) | 700 | 1.00 (tight) | -2.125px | Maximum compression, billboard headlines | +| Display Secondary | NotionInter | 54px (3.38rem) | 700 | 1.04 (tight) | -1.875px | Secondary hero, feature headlines | +| Section Heading | NotionInter | 48px (3.00rem) | 700 | 1.00 (tight) | -1.5px | Feature section titles, with `"lnum"` | +| Sub-heading Large | NotionInter | 40px (2.50rem) | 700 | 1.50 | normal | Card headings, feature sub-sections | +| Sub-heading | NotionInter | 26px (1.63rem) | 700 | 1.23 (tight) | -0.625px | Section sub-titles, content headers | +| Card Title | NotionInter | 22px (1.38rem) | 700 | 1.27 (tight) | -0.25px | Feature cards, list titles | +| Body Large | NotionInter | 20px (1.25rem) | 600 | 1.40 | -0.125px | Introductions, feature descriptions | +| Body | NotionInter | 16px (1.00rem) | 400 | 1.50 | normal | Standard reading text | +| Body Medium | NotionInter | 16px (1.00rem) | 500 | 1.50 | normal | Navigation, emphasized UI text | +| Body Semibold | NotionInter | 16px (1.00rem) | 600 | 1.50 | normal | Strong labels, active states | +| Body Bold | NotionInter | 16px (1.00rem) | 700 | 1.50 | normal | Headlines at body size | +| Nav / Button | NotionInter | 15px (0.94rem) | 600 | 1.33 | normal | Navigation links, button text | +| Caption | NotionInter | 14px (0.88rem) | 500 | 1.43 | normal | Metadata, secondary labels | +| Caption Light | NotionInter | 14px (0.88rem) | 400 | 1.43 | normal | Body captions, descriptions | +| Badge | NotionInter | 12px (0.75rem) | 600 | 1.33 | 0.125px | Pill badges, tags, status labels | +| Micro Label | NotionInter | 12px (0.75rem) | 400 | 1.33 | 0.125px | Small metadata, timestamps | + +### Principles +- **Compression at scale**: NotionInter at display sizes uses -2.125px letter-spacing at 64px, progressively relaxing to -0.625px at 26px and normal at 16px. The compression creates density at headlines while maintaining readability at body sizes. +- **Four-weight system**: 400 (body/reading), 500 (UI/interactive), 600 (emphasis/navigation), 700 (headings/display). The broader weight range compared to most systems allows nuanced hierarchy. +- **Warm scaling**: Line height tightens as size increases -- 1.50 at body (16px), 1.23-1.27 at sub-headings, 1.00-1.04 at display. This creates denser, more impactful headlines. +- **Badge micro-tracking**: The 12px badge text uses positive letter-spacing (0.125px) -- the only positive tracking in the system, creating wider, more legible small text. + +## 4. Component Stylings + +### Buttons + +**Primary Blue** +- Background: `#0075de` (Notion Blue) +- Text: `#ffffff` +- Padding: 8px 16px +- Radius: 4px (subtle) +- Border: `1px solid transparent` +- Hover: background darkens to `#005bab` +- Active: scale(0.9) transform +- Focus: `2px solid` focus outline, `var(--shadow-level-200)` shadow +- Use: Primary CTA ("Get Notion free", "Try it") + +**Secondary / Tertiary** +- Background: `rgba(0,0,0,0.05)` (translucent warm gray) +- Text: `#000000` (near-black) +- Padding: 8px 16px +- Radius: 4px +- Hover: text color shifts, scale(1.05) +- Active: scale(0.9) transform +- Use: Secondary actions, form submissions + +**Ghost / Link Button** +- Background: transparent +- Text: `rgba(0,0,0,0.95)` +- Decoration: underline on hover +- Use: Tertiary actions, inline links + +**Pill Badge Button** +- Background: `#f2f9ff` (tinted blue) +- Text: `#097fe8` +- Padding: 4px 8px +- Radius: 9999px (full pill) +- Font: 12px weight 600 +- Use: Status badges, feature labels, "New" tags + +### Cards & Containers +- Background: `#ffffff` +- Border: `1px solid rgba(0,0,0,0.1)` (whisper border) +- Radius: 12px (standard cards), 16px (featured/hero cards) +- Shadow: `rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px` +- Hover: subtle shadow intensification +- Image cards: 12px top radius, image fills top half + +### Inputs & Forms +- Background: `#ffffff` +- Text: `rgba(0,0,0,0.9)` +- Border: `1px solid #dddddd` +- Padding: 6px +- Radius: 4px +- Focus: blue outline ring +- Placeholder: warm gray `#a39e98` + +### Navigation +- Clean horizontal nav on white, not sticky +- Brand logo left-aligned (33x34px icon + wordmark) +- Links: NotionInter 15px weight 500-600, near-black text +- Hover: color shift to `var(--color-link-primary-text-hover)` +- CTA: blue pill button ("Get Notion free") right-aligned +- Mobile: hamburger menu collapse +- Product dropdowns with multi-level categorized menus + +### Image Treatment +- Product screenshots with `1px solid rgba(0,0,0,0.1)` border +- Top-rounded images: `12px 12px 0px 0px` radius +- Dashboard/workspace preview screenshots dominate feature sections +- Warm gradient backgrounds behind hero illustrations (decorative character illustrations) + +### Distinctive Components + +**Feature Cards with Illustrations** +- Large illustrative headers (The Great Wave, product UI screenshots) +- 12px radius card with whisper border +- Title at 22px weight 700, description at 16px weight 400 +- Warm white (`#f6f5f4`) background variant for alternating sections + +**Trust Bar / Logo Grid** +- Company logos (trusted teams section) in their brand colors +- Horizontal scroll or grid layout with team counts +- Metric display: large number + description pattern + +**Metric Cards** +- Large number display (e.g., "$4,200 ROI") +- NotionInter 40px+ weight 700 for the metric +- Description below in warm gray body text +- Whisper-bordered card container + +## 5. Layout Principles + +### Spacing System +- Base unit: 8px +- Scale: 2px, 3px, 4px, 5px, 6px, 7px, 8px, 11px, 12px, 14px, 16px, 24px, 32px +- Non-rigid organic scale with fractional values (5.6px, 6.4px) for micro-adjustments + +### Grid & Container +- Max content width: approximately 1200px +- Hero: centered single-column with generous top padding (80-120px) +- Feature sections: 2-3 column grids for cards +- Full-width warm white (`#f6f5f4`) section backgrounds for alternation +- Code/dashboard screenshots as contained with whisper border + +### Whitespace Philosophy +- **Generous vertical rhythm**: 64-120px between major sections. Notion lets content breathe with vast vertical padding. +- **Warm alternation**: White sections alternate with warm white (`#f6f5f4`) sections, creating gentle visual rhythm without harsh color breaks. +- **Content-first density**: Body text blocks are compact (line-height 1.50) but surrounded by ample margin, creating islands of readable content in a sea of white space. + +### Border Radius Scale +- Micro (4px): Buttons, inputs, functional interactive elements +- Subtle (5px): Links, list items, menu items +- Standard (8px): Small cards, containers, inline elements +- Comfortable (12px): Standard cards, feature containers, image tops +- Large (16px): Hero cards, featured content, promotional blocks +- Full Pill (9999px): Badges, pills, status indicators +- Circle (100%): Tab indicators, avatars + +## 6. Depth & Elevation + +| Level | Treatment | Use | +|-------|-----------|-----| +| Flat (Level 0) | No shadow, no border | Page background, text blocks | +| Whisper (Level 1) | `1px solid rgba(0,0,0,0.1)` | Standard borders, card outlines, dividers | +| Soft Card (Level 2) | 4-layer shadow stack (max opacity 0.04) | Content cards, feature blocks | +| Deep Card (Level 3) | 5-layer shadow stack (max opacity 0.05, 52px blur) | Modals, featured panels, hero elements | +| Focus (Accessibility) | `2px solid var(--focus-color)` outline | Keyboard focus on all interactive elements | + +**Shadow Philosophy**: Notion's shadow system uses multiple layers with extremely low individual opacity (0.01 to 0.05) that accumulate into soft, natural-looking elevation. The 4-layer card shadow spans from 1.04px to 18px blur, creating a gradient of depth rather than a single hard shadow. The 5-layer deep shadow extends to 52px blur at 0.05 opacity, producing ambient occlusion that feels like natural light rather than computer-generated depth. This layered approach makes elements feel embedded in the page rather than floating above it. + +### Decorative Depth +- Hero section: decorative character illustrations (playful, hand-drawn style) +- Section alternation: white to warm white (`#f6f5f4`) background shifts +- No hard section borders -- separation comes from background color changes and spacing + +## 7. Responsive Behavior + +### Breakpoints +| Name | Width | Key Changes | +|------|-------|-------------| +| Mobile Small | <400px | Tight single column, minimal padding | +| Mobile | 400-600px | Standard mobile, stacked layout | +| Tablet Small | 600-768px | 2-column grids begin | +| Tablet | 768-1080px | Full card grids, expanded padding | +| Desktop Small | 1080-1200px | Standard desktop layout | +| Desktop | 1200-1440px | Full layout, maximum content width | +| Large Desktop | >1440px | Centered, generous margins | + +### Touch Targets +- Buttons use comfortable padding (8px-16px vertical) +- Navigation links at 15px with adequate spacing +- Pill badges have 8px horizontal padding for tap targets +- Mobile menu toggle uses standard hamburger button + +### Collapsing Strategy +- Hero: 64px display -> scales to 40px -> 26px on mobile, maintains proportional letter-spacing +- Navigation: horizontal links + blue CTA -> hamburger menu +- Feature cards: 3-column -> 2-column -> single column stacked +- Product screenshots: maintain aspect ratio with responsive images +- Trust bar logos: grid -> horizontal scroll on mobile +- Footer: multi-column -> stacked single column +- Section spacing: 80px+ -> 48px on mobile + +### Image Behavior +- Workspace screenshots maintain whisper border at all sizes +- Hero illustrations scale proportionally +- Product screenshots use responsive images with consistent border radius +- Full-width warm white sections maintain edge-to-edge treatment + +## 8. Accessibility & States + +### Focus System +- All interactive elements receive visible focus indicators +- Focus outline: `2px solid` with focus color + shadow level 200 +- Tab navigation supported throughout all interactive components +- High contrast text: near-black on white exceeds WCAG AAA (>14:1 ratio) + +### Interactive States +- **Default**: Standard appearance with whisper borders +- **Hover**: Color shift on text, scale(1.05) on buttons, underline on links +- **Active/Pressed**: scale(0.9) transform, darker background variant +- **Focus**: Blue outline ring with shadow reinforcement +- **Disabled**: Warm gray (`#a39e98`) text, reduced opacity + +### Color Contrast +- Primary text (rgba(0,0,0,0.95)) on white: ~18:1 ratio +- Secondary text (#615d59) on white: ~5.5:1 ratio (WCAG AA) +- Blue CTA (#0075de) on white: ~4.6:1 ratio (WCAG AA for large text) +- Badge text (#097fe8) on badge bg (#f2f9ff): ~4.5:1 ratio (WCAG AA for large text) + +## 9. Agent Prompt Guide + +### Quick Color Reference +- Primary CTA: Notion Blue (`#0075de`) +- Background: Pure White (`#ffffff`) +- Alt Background: Warm White (`#f6f5f4`) +- Heading text: Near-Black (`rgba(0,0,0,0.95)`) +- Body text: Near-Black (`rgba(0,0,0,0.95)`) +- Secondary text: Warm Gray 500 (`#615d59`) +- Muted text: Warm Gray 300 (`#a39e98`) +- Border: `1px solid rgba(0,0,0,0.1)` +- Link: Notion Blue (`#0075de`) +- Focus ring: Focus Blue (`#097fe8`) + +### Example Component Prompts +- "Create a hero section on white background. Headline at 64px NotionInter weight 700, line-height 1.00, letter-spacing -2.125px, color rgba(0,0,0,0.95). Subtitle at 20px weight 600, line-height 1.40, color #615d59. Blue CTA button (#0075de, 4px radius, 8px 16px padding, white text) and ghost button (transparent bg, near-black text, underline on hover)." +- "Design a card: white background, 1px solid rgba(0,0,0,0.1) border, 12px radius. Use shadow stack: rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px, rgba(0,0,0,0.02) 0px 0.8px 2.93px, rgba(0,0,0,0.01) 0px 0.175px 1.04px. Title at 22px NotionInter weight 700, letter-spacing -0.25px. Body at 16px weight 400, color #615d59." +- "Build a pill badge: #f2f9ff background, #097fe8 text, 9999px radius, 4px 8px padding, 12px NotionInter weight 600, letter-spacing 0.125px." +- "Create navigation: white header. NotionInter 15px weight 600 for links, near-black text. Blue pill CTA 'Get Notion free' right-aligned (#0075de bg, white text, 4px radius)." +- "Design an alternating section layout: white sections alternate with warm white (#f6f5f4) sections. Each section has 64-80px vertical padding, max-width 1200px centered. Section heading at 48px weight 700, line-height 1.00, letter-spacing -1.5px." + +### Iteration Guide +1. Always use warm neutrals -- Notion's grays have yellow-brown undertones (#f6f5f4, #31302e, #615d59, #a39e98), never blue-gray +2. Letter-spacing scales with font size: -2.125px at 64px, -1.875px at 54px, -0.625px at 26px, normal at 16px +3. Four weights: 400 (read), 500 (interact), 600 (emphasize), 700 (announce) +4. Borders are whispers: 1px solid rgba(0,0,0,0.1) -- never heavier +5. Shadows use 4-5 layers with individual opacity never exceeding 0.05 +6. The warm white (#f6f5f4) section background is essential for visual rhythm +7. Pill badges (9999px) for status/tags, 4px radius for buttons and inputs +8. Notion Blue (#0075de) is the only saturated color in core UI -- use it sparingly for CTAs and links diff --git a/harness-init.sh b/harness-init.sh new file mode 100644 index 00000000..191fae5c --- /dev/null +++ b/harness-init.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Harness 会在每次会话启动时执行此脚本,这里只做轻量且可重复执行的环境检查。 +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT_DIR" + +command -v git >/dev/null || { + echo "ERROR: 缺少 git" + exit 1 +} + +command -v python3 >/dev/null || { + echo "ERROR: 缺少 python3" + exit 1 +} + +if [ -d "$ROOT_DIR/wolai-frontend" ]; then + if ! command -v node >/dev/null; then + echo "WARN: 缺少 node,前端相关任务可能无法执行" + fi + if ! command -v pnpm >/dev/null; then + echo "WARN: 缺少 pnpm,前端相关任务可能无法执行" + fi +fi + +echo "Harness 环境检查完成" diff --git a/harness-progress.txt b/harness-progress.txt new file mode 100644 index 00000000..4981d31e --- /dev/null +++ b/harness-progress.txt @@ -0,0 +1 @@ +[2026-04-14T05:21:15Z] [SESSION-0] INIT Harness initialized for project /mnt/Data1T/mnote diff --git a/harness-tasks.json b/harness-tasks.json new file mode 100644 index 00000000..35b2b738 --- /dev/null +++ b/harness-tasks.json @@ -0,0 +1,12 @@ +{ + "version": 2, + "created": "2026-04-14T05:21:15Z", + "session_config": { + "concurrency_mode": "exclusive", + "max_tasks_per_session": 20, + "max_sessions": 50 + }, + "tasks": [], + "session_count": 0, + "last_session": null +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 00000000..6ec4b4b3 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,38 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "core-domain" +version = "0.1.0" + +[[package]] +name = "core-protocol" +version = "0.1.0" +dependencies = [ + "core-domain", +] + +[[package]] +name = "event-log" +version = "0.1.0" +dependencies = [ + "core-domain", +] + +[[package]] +name = "index-fts" +version = "0.1.0" +dependencies = [ + "core-domain", + "event-log", +] + +[[package]] +name = "storage-convex-bridge" +version = "0.1.0" +dependencies = [ + "core-domain", + "core-protocol", + "event-log", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000..ade0b7bb --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,18 @@ +[workspace] +members = [ + "crates/core-domain", + "crates/core-protocol", + "crates/event-log", + "crates/storage-convex-bridge", + "crates/index-fts", +] +resolver = "2" + +[workspace.package] +edition = "2021" +license = "MIT" +version = "0.1.0" +authors = ["mnote"] + +[workspace.metadata] +description = "mnote 单仓收口阶段的 Rust 内核 workspace" diff --git a/rust/crates/core-domain/Cargo.toml b/rust/crates/core-domain/Cargo.toml new file mode 100644 index 00000000..f1e90c83 --- /dev/null +++ b/rust/crates/core-domain/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "core-domain" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] diff --git a/rust/crates/core-domain/README.md b/rust/crates/core-domain/README.md new file mode 100644 index 00000000..a6383279 --- /dev/null +++ b/rust/crates/core-domain/README.md @@ -0,0 +1,6 @@ +# core-domain + +阶段 1 骨架 crate。 + +职责说明请先看: +- ../../design/phase1-b-crate-responsibilities-v0.md diff --git a/rust/crates/core-domain/src/audit.rs b/rust/crates/core-domain/src/audit.rs new file mode 100644 index 00000000..bd56a3d9 --- /dev/null +++ b/rust/crates/core-domain/src/audit.rs @@ -0,0 +1,43 @@ +use crate::ids::{AssetId, AssetVersionId, BlockId, PageId, TaskId}; +use crate::time::Timestamp; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ActorRef { + pub actor_type: String, + pub actor_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SourceKind { + Web, + Cli, + Agent, + Job, + Import, + System, + Other(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ChangeReason { + pub code: String, + pub detail: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RefLink { + Page(PageId), + Block(BlockId), + Asset(AssetId), + AssetVersion(AssetVersionId), + Task(TaskId), + External(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AuditInfo { + pub created_at: Timestamp, + pub updated_at: Timestamp, + pub created_by: Option, + pub updated_by: Option, +} diff --git a/rust/crates/core-domain/src/entities.rs b/rust/crates/core-domain/src/entities.rs new file mode 100644 index 00000000..f0b960af --- /dev/null +++ b/rust/crates/core-domain/src/entities.rs @@ -0,0 +1,289 @@ +use crate::audit::{ActorRef, AuditInfo, ChangeReason, RefLink, SourceKind}; +use crate::ids::{ + AgentSessionId, AssetId, AssetVersionId, BlockId, CommandLogId, EventId, PageId, ReferenceId, + TaskId, WorkspaceId, +}; +use crate::time::Timestamp; +use crate::types::{Locale, Revision}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Workspace { + pub workspace_id: WorkspaceId, + pub slug: String, + pub title: String, + pub owner_actor_id: String, + pub default_locale: Locale, + pub storage_policy: String, + pub sync_policy: String, + pub feature_flags: Vec, + pub archived_at: Option, + pub audit: AuditInfo, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PageType { + Note, + Doc, + DatabaseRecord, + Inbox, + Template, + System, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PageStatus { + Active, + Archived, + Deleted, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Page { + pub page_id: PageId, + pub workspace_id: WorkspaceId, + pub parent_page_id: Option, + pub title: String, + pub slug: String, + pub icon: Option, + pub cover_asset_id: Option, + pub body_root_block_id: Option, + pub page_type: PageType, + pub status: PageStatus, + pub last_edited_at: Option, + pub last_edited_by: Option, + pub current_revision: Revision, + pub audit: AuditInfo, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BlockType { + Paragraph, + Heading, + BulletedListItem, + NumberedListItem, + Todo, + Quote, + CodeBlock, + Divider, + Callout, + PageReference, + BlockReference, + EmbedAsset, + EmbedView, + EmbedOnlyoffice, + EmbedMindmap, + EmbedTable, + Custom(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Block { + pub block_id: BlockId, + pub workspace_id: WorkspaceId, + pub page_id: PageId, + pub parent_block_id: Option, + pub prev_block_id: Option, + pub next_block_id: Option, + pub sort_key: String, + pub block_type: BlockType, + pub content: String, + pub props: Vec<(String, String)>, + pub annotations: Vec, + pub status: String, + pub revision: Revision, + pub deleted_at: Option, + pub audit: AuditInfo, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AssetKind { + Image, + Office, + Pdf, + Audio, + Video, + Export, + OcrDerived, + Other(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AssetStatus { + Active, + Archived, + Deleted, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Asset { + pub asset_id: AssetId, + pub workspace_id: WorkspaceId, + pub storage_key: String, + pub original_name: String, + pub mime_type: String, + pub size_bytes: u64, + pub checksum: Option, + pub origin: String, + pub asset_kind: AssetKind, + pub status: AssetStatus, + pub latest_version_id: Option, + pub audit: AuditInfo, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AssetVersion { + pub asset_version_id: AssetVersionId, + pub asset_id: AssetId, + pub version_no: u32, + pub storage_key: String, + pub checksum: Option, + pub derived_from_version_id: Option, + pub change_reason: Option, + pub created_at: Timestamp, + pub created_by: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ReferenceKind { + PageToPage, + BlockToBlock, + BlockToPage, + BlockToAssetFragment, + TaskToObject, + External, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Reference { + pub reference_id: ReferenceId, + pub workspace_id: WorkspaceId, + pub source_object_type: String, + pub source_object_id: String, + pub target_object_type: String, + pub target_object_id: String, + pub anchor: String, + pub label: Option, + pub snippet: Option, + pub confidence: Option, + pub ref_kind: ReferenceKind, + pub created_at: Timestamp, + pub created_by: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TaskType { + Import, + Ocr, + Summary, + Reindex, + Sync, + Transform, + Other(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TaskStatus { + Pending, + Running, + Succeeded, + Failed, + Cancelled, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TaskPriority { + Low, + Normal, + High, + Critical, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Task { + pub task_id: TaskId, + pub workspace_id: WorkspaceId, + pub title: String, + pub description: Option, + pub task_type: TaskType, + pub status: TaskStatus, + pub priority: TaskPriority, + pub assignee_actor_id: Option, + pub source_page_id: Option, + pub source_block_id: Option, + pub input_payload: Option, + pub output_payload: Option, + pub due_at: Option, + pub completed_at: Option, + pub audit: AuditInfo, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AgentSession { + pub agent_session_id: AgentSessionId, + pub workspace_id: WorkspaceId, + pub provider: String, + pub model: String, + pub initiator_actor_id: Option, + pub status: String, + pub started_at: Timestamp, + pub updated_at: Timestamp, + pub ended_at: Option, + pub tool_policy: Option, + pub confirmation_policy: Option, + pub summary: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CommandStatus { + Pending, + Succeeded, + Failed, + Cancelled, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CommandLog { + pub command_log_id: CommandLogId, + pub workspace_id: WorkspaceId, + pub command_name: String, + pub actor: ActorRef, + pub source: SourceKind, + pub target_objects: Vec, + pub payload_summary: String, + pub refs: Vec, + pub idempotency_key: Option, + pub dry_run: bool, + pub status: CommandStatus, + pub created_at: Timestamp, + pub finished_at: Option, + pub error_code: Option, + pub error_message: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EventType { + PageCreated, + PageUpdated, + BlockInserted, + BlockMoved, + BlockUpdated, + BlockDeleted, + AssetVersionAdded, + ReferenceCreated, + TaskUpdated, + Other(String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DomainEvent { + pub event_id: EventId, + pub workspace_id: WorkspaceId, + pub command_log_id: CommandLogId, + pub aggregate_type: String, + pub aggregate_id: String, + pub event_type: EventType, + pub before_revision: Option, + pub after_revision: Option, + pub payload_json: String, + pub created_at: Timestamp, +} diff --git a/rust/crates/core-domain/src/ids.rs b/rust/crates/core-domain/src/ids.rs new file mode 100644 index 00000000..d7d0048d --- /dev/null +++ b/rust/crates/core-domain/src/ids.rs @@ -0,0 +1,27 @@ +macro_rules! define_id { + ($name:ident) => { + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct $name(pub String); + + impl $name { + pub fn new(value: impl Into) -> Self { + Self(value.into()) + } + + pub fn as_str(&self) -> &str { + &self.0 + } + } + }; +} + +define_id!(WorkspaceId); +define_id!(PageId); +define_id!(BlockId); +define_id!(AssetId); +define_id!(AssetVersionId); +define_id!(ReferenceId); +define_id!(TaskId); +define_id!(AgentSessionId); +define_id!(CommandLogId); +define_id!(EventId); diff --git a/rust/crates/core-domain/src/lib.rs b/rust/crates/core-domain/src/lib.rs new file mode 100644 index 00000000..80be9616 --- /dev/null +++ b/rust/crates/core-domain/src/lib.rs @@ -0,0 +1,34 @@ +pub mod audit; +pub mod entities; +pub mod ids; +pub mod time; +pub mod types; + +pub use audit::{ActorRef, AuditInfo, ChangeReason, RefLink, SourceKind}; +pub use entities::{ + AgentSession, Asset, AssetKind, AssetStatus, AssetVersion, Block, BlockType, CommandLog, + CommandStatus, DomainEvent, EventType, Page, PageStatus, PageType, Reference, ReferenceKind, + Task, TaskPriority, TaskStatus, TaskType, Workspace, +}; +pub use ids::{ + AgentSessionId, AssetId, AssetVersionId, BlockId, CommandLogId, EventId, PageId, ReferenceId, + TaskId, WorkspaceId, +}; +pub use time::Timestamp; +pub use types::{Locale, Revision}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn revision_initial_is_one() { + assert_eq!(Revision::initial().0, 1); + } + + #[test] + fn workspace_id_keeps_original_value() { + let workspace_id = WorkspaceId::new("ws_demo"); + assert_eq!(workspace_id.as_str(), "ws_demo"); + } +} diff --git a/rust/crates/core-domain/src/time.rs b/rust/crates/core-domain/src/time.rs new file mode 100644 index 00000000..ea346d88 --- /dev/null +++ b/rust/crates/core-domain/src/time.rs @@ -0,0 +1,12 @@ +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Timestamp(pub String); + +impl Timestamp { + pub fn new(value: impl Into) -> Self { + Self(value.into()) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} diff --git a/rust/crates/core-domain/src/types.rs b/rust/crates/core-domain/src/types.rs new file mode 100644 index 00000000..58e9d3f4 --- /dev/null +++ b/rust/crates/core-domain/src/types.rs @@ -0,0 +1,17 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Revision(pub u64); + +impl Revision { + pub fn initial() -> Self { + Self(1) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Locale(pub String); + +impl Locale { + pub fn new(value: impl Into) -> Self { + Self(value.into()) + } +} diff --git a/rust/crates/core-protocol/Cargo.toml b/rust/crates/core-protocol/Cargo.toml new file mode 100644 index 00000000..23e56908 --- /dev/null +++ b/rust/crates/core-protocol/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "core-protocol" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +core-domain = { path = "../core-domain" } diff --git a/rust/crates/core-protocol/README.md b/rust/crates/core-protocol/README.md new file mode 100644 index 00000000..02aaff74 --- /dev/null +++ b/rust/crates/core-protocol/README.md @@ -0,0 +1,8 @@ +# core-protocol + +阶段 1 协议模型 crate。 + +职责说明请先看: +- ../../design/phase1-d-command-api-v0.md +- ../../design/phase1-d-query-api-v0.md +- ../../design/phase1-d-tool-api-constraints-v0.md diff --git a/rust/crates/core-protocol/src/command.rs b/rust/crates/core-protocol/src/command.rs new file mode 100644 index 00000000..b11e1ef6 --- /dev/null +++ b/rust/crates/core-protocol/src/command.rs @@ -0,0 +1,101 @@ +use crate::common::{ActorPayload, AffectedObject, SourcePayload, TargetRef}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CommandEnvelope { + pub name: String, + pub command_id: String, + pub idempotency_key: Option, + pub actor: ActorPayload, + pub source: SourcePayload, + pub target: Option, + pub payload: T, + pub reason: Option, + pub refs: Vec, + pub dry_run: bool, + pub validate_only: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CommandResult { + pub ok: bool, + pub command_id: String, + pub event_ids: Vec, + pub affected_objects: Vec, + pub revision: Option, + pub warnings: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CreateWorkspace { + pub name: String, + pub slug: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CreatePage { + pub title: String, + pub parent_page_id: Option, + pub position: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UpdatePageTitle { + pub page_id: String, + pub title: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UpdatePageStats { + pub page_id: String, + pub word_count: i64, + pub character_count: i64, + pub block_count: i64, + pub todo_total: i64, + pub todo_done: i64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UpdatePageOptions { + pub page_id: String, + pub wide_layout: Option, + pub small_text: Option, + pub show_heading_numbers: Option, + pub show_toc: Option, + pub show_structure: Option, + pub protect_editing: Option, + pub show_word_count: Option, + pub collapse_backlinks: Option, + pub page_font: Option, + pub layout_density: Option, + pub hide_child_pages: Option, + pub show_block_ref_count: Option, + pub embed_default_block_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InsertBlock { + pub page_id: String, + pub block_type: String, + pub content: String, + pub parent_block_id: Option, + pub prev_block_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UpdateBlock { + pub block_id: String, + pub patch_content: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MoveBlock { + pub block_id: String, + pub new_parent_block_id: Option, + pub new_page_id: Option, + pub prev_block_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeleteBlock { + pub block_id: String, +} diff --git a/rust/crates/core-protocol/src/common.rs b/rust/crates/core-protocol/src/common.rs new file mode 100644 index 00000000..d9957461 --- /dev/null +++ b/rust/crates/core-protocol/src/common.rs @@ -0,0 +1,59 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ActorPayload { + pub actor_type: String, + pub actor_id: String, + pub session_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SourcePayload { + pub channel: String, + pub client: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TargetRef { + pub workspace_id: Option, + pub page_id: Option, + pub block_id: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RequestMeta { + pub idempotency_key: Option, + pub validate_only: bool, + pub dry_run: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AffectedObject { + pub object_type: String, + pub object_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResponseMeta { + pub request_id: String, + pub trace_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OkPayload { + pub data: T, + pub meta: ResponseMeta, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ErrorDetail { + pub field: Option, + pub reason: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ErrorPayload { + pub code: String, + pub message: String, + pub details: Vec, + pub retryable: bool, + pub meta: ResponseMeta, +} diff --git a/rust/crates/core-protocol/src/governance.rs b/rust/crates/core-protocol/src/governance.rs new file mode 100644 index 00000000..03fd2303 --- /dev/null +++ b/rust/crates/core-protocol/src/governance.rs @@ -0,0 +1,116 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccessContext { + pub tenant_id: Option, + pub workspace_id: String, + pub actor_id: String, + pub actor_type: String, + pub source: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AccessDecision { + Allow, + Deny(AccessDenyReason), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AccessDenyReason { + MissingTenant, + MissingWorkspace, + PermissionDenied, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum JobStatus { + Pending, + Running, + Succeeded, + Failed, + RetryScheduled, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct JobTicket { + pub job_id: String, + pub job_type: String, + pub request_id: String, + pub trace_id: String, + pub workspace_id: String, + pub status: JobStatus, +} + +impl JobTicket { + pub fn new( + job_id: impl Into, + job_type: impl Into, + request_id: impl Into, + trace_id: impl Into, + workspace_id: impl Into, + status: JobStatus, + ) -> Self { + Self { + job_id: job_id.into(), + job_type: job_type.into(), + request_id: request_id.into(), + trace_id: trace_id.into(), + workspace_id: workspace_id.into(), + status, + } + } +} + +pub fn decide_access(context: &AccessContext) -> AccessDecision { + if context.tenant_id.is_none() { + return AccessDecision::Deny(AccessDenyReason::MissingTenant); + } + if context.workspace_id.trim().is_empty() { + return AccessDecision::Deny(AccessDenyReason::MissingWorkspace); + } + AccessDecision::Allow +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn access_requires_tenant() { + let context = AccessContext { + tenant_id: None, + workspace_id: "ws-1".to_string(), + actor_id: "user-1".to_string(), + actor_type: "human".to_string(), + source: "react-next".to_string(), + }; + assert_eq!( + decide_access(&context), + AccessDecision::Deny(AccessDenyReason::MissingTenant) + ); + } + + #[test] + fn access_allows_valid_context() { + let context = AccessContext { + tenant_id: Some("tenant-1".to_string()), + workspace_id: "ws-1".to_string(), + actor_id: "user-1".to_string(), + actor_type: "human".to_string(), + source: "react-next".to_string(), + }; + assert_eq!(decide_access(&context), AccessDecision::Allow); + } + + #[test] + fn job_ticket_keeps_trace_fields() { + let ticket = JobTicket::new( + "job-1", + "rebuild-index", + "req-1", + "trace-1", + "ws-1", + JobStatus::Pending, + ); + assert_eq!(ticket.trace_id, "trace-1"); + assert_eq!(ticket.status, JobStatus::Pending); + } +} diff --git a/rust/crates/core-protocol/src/lib.rs b/rust/crates/core-protocol/src/lib.rs new file mode 100644 index 00000000..1c4cd651 --- /dev/null +++ b/rust/crates/core-protocol/src/lib.rs @@ -0,0 +1,50 @@ +pub mod command; +pub mod common; +pub mod governance; +pub mod query; +pub mod tool; + +pub use command::{ + CommandEnvelope, CreatePage, CreateWorkspace, DeleteBlock, InsertBlock, MoveBlock, + UpdateBlock, UpdatePageStats, UpdatePageTitle, +}; +pub use common::{ + ActorPayload, AffectedObject, ErrorDetail, ErrorPayload, OkPayload, RequestMeta, ResponseMeta, + SourcePayload, TargetRef, +}; +pub use query::{GetPage, ListPageBlocks, QueryEnvelope, SearchBlocks, SearchPages}; +pub use tool::{InvocationKind, ToolInvocation}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn command_envelope_keeps_validate_only_flag() { + let envelope = CommandEnvelope:: { + name: "create_workspace".into(), + command_id: "cmd_1".into(), + idempotency_key: Some("idem_1".into()), + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "cli".into(), + client: "mnote-cli".into(), + }, + target: None, + payload: CreateWorkspace { + name: "demo".into(), + slug: Some("demo".into()), + }, + reason: Some("初始化工作区".into()), + refs: vec![], + dry_run: false, + validate_only: true, + }; + + assert!(envelope.validate_only); + } +} diff --git a/rust/crates/core-protocol/src/query.rs b/rust/crates/core-protocol/src/query.rs new file mode 100644 index 00000000..5e513939 --- /dev/null +++ b/rust/crates/core-protocol/src/query.rs @@ -0,0 +1,36 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QueryEnvelope { + pub name: String, + pub payload: T, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Pagination { + pub limit: u32, + pub cursor: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GetPage { + pub page_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ListPageBlocks { + pub page_id: String, + pub pagination: Pagination, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SearchPages { + pub query: String, + pub workspace_id: Option, + pub pagination: Pagination, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SearchBlocks { + pub query: String, + pub page_id: Option, + pub pagination: Pagination, +} diff --git a/rust/crates/core-protocol/src/tool.rs b/rust/crates/core-protocol/src/tool.rs new file mode 100644 index 00000000..26682fb8 --- /dev/null +++ b/rust/crates/core-protocol/src/tool.rs @@ -0,0 +1,13 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InvocationKind { + Command, + Query, + Job, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ToolInvocation { + pub tool: String, + pub kind: InvocationKind, + pub args_json: String, +} diff --git a/rust/crates/event-log/Cargo.toml b/rust/crates/event-log/Cargo.toml new file mode 100644 index 00000000..6baee6cb --- /dev/null +++ b/rust/crates/event-log/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "event-log" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +core-domain = { path = "../core-domain" } diff --git a/rust/crates/event-log/README.md b/rust/crates/event-log/README.md new file mode 100644 index 00000000..b0d222cf --- /dev/null +++ b/rust/crates/event-log/README.md @@ -0,0 +1,8 @@ +# event-log + +阶段 1 事件日志 crate。 + +职责说明请先看: +- ../../design/phase1-e-command-log-v0.md +- ../../design/phase1-e-domain-event-v0.md +- ../../design/phase1-e-event-generation-rules-v0.md diff --git a/rust/crates/event-log/src/command_log.rs b/rust/crates/event-log/src/command_log.rs new file mode 100644 index 00000000..c19049e7 --- /dev/null +++ b/rust/crates/event-log/src/command_log.rs @@ -0,0 +1,29 @@ +use core_domain::Timestamp; +use core_domain::{RefLink, WorkspaceId}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CommandLogStatus { + Pending, + Succeeded, + Failed, + RolledBack, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CommandLogRecord { + pub command_log_id: String, + pub command_name: String, + pub actor_type: String, + pub actor_id: String, + pub source: String, + pub workspace_id: WorkspaceId, + pub target_objects: Vec, + pub payload_summary: String, + pub refs_json: String, + pub idempotency_key: Option, + pub status: CommandLogStatus, + pub created_at: Timestamp, + pub finished_at: Option, + pub trace_id: String, + pub request_id: String, +} diff --git a/rust/crates/event-log/src/domain_event.rs b/rust/crates/event-log/src/domain_event.rs new file mode 100644 index 00000000..3a81e78f --- /dev/null +++ b/rust/crates/event-log/src/domain_event.rs @@ -0,0 +1,36 @@ +use core_domain::Timestamp; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EventStatus { + Pending, + Committed, + Rejected, + Failed, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DomainEventRecord { + pub event_id: String, + pub workspace_id: String, + pub aggregate_type: String, + pub aggregate_id: String, + pub event_type: String, + pub event_version: u32, + pub payload_json: String, + pub command_log_id: String, + pub actor_type: String, + pub created_at: Timestamp, + pub status: EventStatus, + pub trace_id: String, + pub request_id: String, + pub command_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventGenerationOutcome { + pub command_log_id: String, + pub emitted_event_ids: Vec, + pub generated_for_replay: bool, + pub generated_for_audit: bool, + pub generated_for_index_catchup: bool, +} diff --git a/rust/crates/event-log/src/lib.rs b/rust/crates/event-log/src/lib.rs new file mode 100644 index 00000000..7cca459e --- /dev/null +++ b/rust/crates/event-log/src/lib.rs @@ -0,0 +1,23 @@ +pub mod command_log; +pub mod domain_event; +pub mod rules; + +pub use command_log::{CommandLogRecord, CommandLogStatus}; +pub use domain_event::{DomainEventRecord, EventGenerationOutcome, EventStatus}; +pub use rules::{should_emit_domain_event, EventGenerationRule}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn failed_command_does_not_emit_success_event() { + let should_emit = should_emit_domain_event(&EventGenerationRule { + command_succeeded: false, + changed_domain_state: false, + rolled_back: false, + }); + + assert!(!should_emit); + } +} diff --git a/rust/crates/event-log/src/rules.rs b/rust/crates/event-log/src/rules.rs new file mode 100644 index 00000000..64601ecf --- /dev/null +++ b/rust/crates/event-log/src/rules.rs @@ -0,0 +1,10 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventGenerationRule { + pub command_succeeded: bool, + pub changed_domain_state: bool, + pub rolled_back: bool, +} + +pub fn should_emit_domain_event(rule: &EventGenerationRule) -> bool { + rule.command_succeeded && rule.changed_domain_state && !rule.rolled_back +} diff --git a/rust/crates/index-fts/Cargo.toml b/rust/crates/index-fts/Cargo.toml new file mode 100644 index 00000000..cfe4ac1f --- /dev/null +++ b/rust/crates/index-fts/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "index-fts" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +core-domain = { path = "../core-domain" } +event-log = { path = "../event-log" } diff --git a/rust/crates/index-fts/README.md b/rust/crates/index-fts/README.md new file mode 100644 index 00000000..78519b9e --- /dev/null +++ b/rust/crates/index-fts/README.md @@ -0,0 +1,6 @@ +# index-fts + +阶段 1 骨架 crate。 + +职责说明请先看: +- ../../design/phase1-b-crate-responsibilities-v0.md diff --git a/rust/crates/index-fts/src/lib.rs b/rust/crates/index-fts/src/lib.rs new file mode 100644 index 00000000..dc3b5922 --- /dev/null +++ b/rust/crates/index-fts/src/lib.rs @@ -0,0 +1,355 @@ +use event_log::DomainEventRecord; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum IndexedEntityKind { + PageTitle, + PageSummary, + BlockContent, + BlockPath, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IndexedDocument { + pub workspace_id: String, + pub entity_kind: IndexedEntityKind, + pub entity_id: String, + pub parent_id: Option, + pub title: Option, + pub content: String, + pub updated_at: String, + pub source_event_id: Option, + pub revision: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IndexCursor { + pub workspace_id: String, + pub last_processed_event_id: String, + pub last_processed_at: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SearchHit { + pub workspace_id: String, + pub entity_kind: IndexedEntityKind, + pub entity_id: String, + pub snippet: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SearchResultSet { + pub query: String, + pub workspace_id: Option, + pub page_id: Option, + pub hits: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectedDocumentBatch { + pub workspace_id: String, + pub source_event_id: String, + pub documents: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectionResult { + pub cursor: IndexCursor, + pub batches: Vec, +} + +pub trait DomainEventProjector { + fn project(&self, event: &DomainEventRecord) -> Vec; +} + +pub fn supported_index_objects() -> Vec { + vec![ + IndexedEntityKind::PageTitle, + IndexedEntityKind::PageSummary, + IndexedEntityKind::BlockContent, + IndexedEntityKind::BlockPath, + ] +} + +pub fn can_rebuild_from_events(cursor: &IndexCursor) -> bool { + !cursor.workspace_id.trim().is_empty() && !cursor.last_processed_event_id.trim().is_empty() +} + +pub fn advance_workspace_cursor( + cursor: &IndexCursor, + event_id: impl Into, + processed_at: impl Into, +) -> IndexCursor { + IndexCursor { + workspace_id: cursor.workspace_id.clone(), + last_processed_event_id: event_id.into(), + last_processed_at: processed_at.into(), + } +} + +pub fn project_domain_event( + projector: &P, + event: &DomainEventRecord, + cursor: &IndexCursor, +) -> ProjectionResult { + let documents = projector.project(event); + ProjectionResult { + cursor: advance_workspace_cursor( + cursor, + event.event_id.clone(), + event.created_at.as_str().to_string(), + ), + batches: if documents.is_empty() { + Vec::new() + } else { + vec![ProjectedDocumentBatch { + workspace_id: event.workspace_id.clone(), + source_event_id: event.event_id.clone(), + documents, + }] + }, + } +} + +pub fn rebuild_from_events( + projector: &P, + events: &[DomainEventRecord], + cursor: &IndexCursor, +) -> ProjectionResult { + let mut next_cursor = cursor.clone(); + let mut batches = Vec::new(); + for event in events { + if event.workspace_id != cursor.workspace_id { + continue; + } + let projected = projector.project(event); + next_cursor = advance_workspace_cursor( + &next_cursor, + event.event_id.clone(), + event.created_at.as_str().to_string(), + ); + if !projected.is_empty() { + batches.push(ProjectedDocumentBatch { + workspace_id: event.workspace_id.clone(), + source_event_id: event.event_id.clone(), + documents: projected, + }); + } + } + ProjectionResult { + cursor: next_cursor, + batches, + } +} + +pub fn search_pages( + query: &str, + workspace_id: Option<&str>, + documents: &[IndexedDocument], + limit: usize, +) -> SearchResultSet { + let query = query.trim().to_lowercase(); + let hits = documents + .iter() + .filter(|document| matches!(document.entity_kind, IndexedEntityKind::PageTitle | IndexedEntityKind::PageSummary)) + .filter(|document| workspace_id.map_or(true, |workspace| document.workspace_id == workspace)) + .filter(|document| document.title.as_deref().unwrap_or("").to_lowercase().contains(&query) + || document.content.to_lowercase().contains(&query)) + .take(limit) + .map(|document| SearchHit { + workspace_id: document.workspace_id.clone(), + entity_kind: document.entity_kind.clone(), + entity_id: document.entity_id.clone(), + snippet: Some(document.content.chars().take(120).collect()), + }) + .collect(); + + SearchResultSet { + query: query.into(), + workspace_id: workspace_id.map(|value| value.into()), + page_id: None, + hits, + } +} + +pub fn search_blocks( + query: &str, + page_id: Option<&str>, + documents: &[IndexedDocument], + limit: usize, +) -> SearchResultSet { + let query = query.trim().to_lowercase(); + let hits = documents + .iter() + .filter(|document| matches!(document.entity_kind, IndexedEntityKind::BlockContent | IndexedEntityKind::BlockPath)) + .filter(|document| page_id.map_or(true, |page| document.parent_id.as_deref() == Some(page) || document.entity_id == page)) + .filter(|document| document.content.to_lowercase().contains(&query)) + .take(limit) + .map(|document| SearchHit { + workspace_id: document.workspace_id.clone(), + entity_kind: document.entity_kind.clone(), + entity_id: document.entity_id.clone(), + snippet: Some(document.content.chars().take(120).collect()), + }) + .collect(); + + SearchResultSet { + query: query.into(), + workspace_id: None, + page_id: page_id.map(|value| value.into()), + hits, + } +} + +pub struct MinimalWorkspaceProjector; + +impl DomainEventProjector for MinimalWorkspaceProjector { + fn project(&self, event: &DomainEventRecord) -> Vec { + let content = event.payload_json.trim(); + let (entity_kind, title, parent_id) = match event.aggregate_type.as_str() { + "page" => ( + IndexedEntityKind::PageTitle, + Some(event.event_type.clone()), + None, + ), + "block" => ( + IndexedEntityKind::BlockContent, + None, + Some(event.aggregate_id.clone()), + ), + _ => return Vec::new(), + }; + + vec![IndexedDocument { + workspace_id: event.workspace_id.clone(), + entity_kind, + entity_id: event.aggregate_id.clone(), + parent_id, + title, + content: content.to_string(), + updated_at: event.created_at.as_str().to_string(), + source_event_id: Some(event.event_id.clone()), + revision: Some(event.event_version as u64), + }] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core_domain::Timestamp; + use event_log::EventStatus; + + fn cursor() -> IndexCursor { + IndexCursor { + workspace_id: "ws_1".into(), + last_processed_event_id: "evt_0".into(), + last_processed_at: "2026-04-11T00:00:00Z".into(), + } + } + + fn event( + workspace_id: &str, + aggregate_type: &str, + aggregate_id: &str, + event_id: &str, + event_type: &str, + ) -> DomainEventRecord { + DomainEventRecord { + event_id: event_id.into(), + workspace_id: workspace_id.into(), + aggregate_type: aggregate_type.into(), + aggregate_id: aggregate_id.into(), + event_type: event_type.into(), + event_version: 1, + payload_json: "{\"text\":\"hello\"}".into(), + command_log_id: "cmd_1".into(), + actor_type: "human".into(), + created_at: Timestamp::new("2026-04-11T00:00:01Z"), + status: EventStatus::Committed, + trace_id: "trace_1".into(), + request_id: "req_1".into(), + command_id: "cmd_1".into(), + } + } + + #[test] + fn supports_page_and_block_projection_objects() { + assert_eq!( + supported_index_objects(), + vec![ + IndexedEntityKind::PageTitle, + IndexedEntityKind::PageSummary, + IndexedEntityKind::BlockContent, + IndexedEntityKind::BlockPath + ] + ); + } + + #[test] + fn cursor_advances_incrementally() { + let next = advance_workspace_cursor(&cursor(), "evt_2", "2026-04-11T00:00:02Z"); + assert_eq!(next.last_processed_event_id, "evt_2"); + assert_eq!(next.workspace_id, "ws_1"); + } + + #[test] + fn projector_builds_real_documents_for_page_and_block() { + let projector = MinimalWorkspaceProjector; + let page_docs = + projector.project(&event("ws_1", "page", "page_1", "evt_1", "page.created")); + let block_docs = + projector.project(&event("ws_1", "block", "block_1", "evt_2", "block.created")); + assert_eq!(page_docs.len(), 1); + assert_eq!(block_docs.len(), 1); + assert_eq!(page_docs[0].entity_kind, IndexedEntityKind::PageTitle); + assert_eq!(block_docs[0].entity_kind, IndexedEntityKind::BlockContent); + } + + #[test] + fn rebuild_skips_other_workspaces_and_keeps_cursor() { + let projector = MinimalWorkspaceProjector; + let events = vec![ + event("ws_2", "page", "page_x", "evt_x", "page.created"), + event("ws_1", "block", "block_1", "evt_1", "block.created"), + ]; + let result = rebuild_from_events(&projector, &events, &cursor()); + assert_eq!(result.batches.len(), 1); + assert_eq!(result.cursor.last_processed_event_id, "evt_1"); + } + + #[test] + fn search_helpers_return_hits() { + let docs = vec![ + IndexedDocument { + workspace_id: "ws_1".into(), + entity_kind: IndexedEntityKind::PageTitle, + entity_id: "page_1".into(), + parent_id: None, + title: Some("Rust Notes".into()), + content: "Rust notes for phase 2".into(), + updated_at: "2026-04-11T00:00:01Z".into(), + source_event_id: Some("evt_1".into()), + revision: Some(1), + }, + IndexedDocument { + workspace_id: "ws_1".into(), + entity_kind: IndexedEntityKind::BlockContent, + entity_id: "block_1".into(), + parent_id: Some("page_1".into()), + title: None, + content: "A block about rust search indexing".into(), + updated_at: "2026-04-11T00:00:02Z".into(), + source_event_id: Some("evt_2".into()), + revision: Some(1), + }, + ]; + + let pages = search_pages("rust", Some("ws_1"), &docs, 10); + assert_eq!(pages.hits.len(), 1); + assert_eq!(pages.hits[0].entity_id, "page_1"); + + let blocks = search_blocks("search", Some("page_1"), &docs, 10); + assert_eq!(blocks.hits.len(), 1); + assert_eq!(blocks.hits[0].entity_id, "block_1"); + } +} diff --git a/rust/crates/storage-convex-bridge/Cargo.toml b/rust/crates/storage-convex-bridge/Cargo.toml new file mode 100644 index 00000000..ba213305 --- /dev/null +++ b/rust/crates/storage-convex-bridge/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "storage-convex-bridge" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +core-domain = { path = "../core-domain" } +core-protocol = { path = "../core-protocol" } +event-log = { path = "../event-log" } diff --git a/rust/crates/storage-convex-bridge/README.md b/rust/crates/storage-convex-bridge/README.md new file mode 100644 index 00000000..56fffc22 --- /dev/null +++ b/rust/crates/storage-convex-bridge/README.md @@ -0,0 +1,18 @@ +# storage-convex-bridge + +阶段 1 的 Convex 桥接 crate。 + +当前职责: +- 把 Rust 协议对象映射为 Convex 读写请求 +- 透传 request / actor / workspace / auth 上下文 +- 生成最小 CommandLog / DomainEvent 骨架 + +当前明确不做: +- 不复制主事实层 +- 不维护第二数据库 +- 不隐藏 tenant / auth / request context +- 不承载复杂业务裁决 + +相关设计文档: +- `../../design/phase1-f-bridge-responsibilities-v0.md` +- `../../design/phase1-f-write-read-flow-v0.md` diff --git a/rust/crates/storage-convex-bridge/src/context.rs b/rust/crates/storage-convex-bridge/src/context.rs new file mode 100644 index 00000000..435f47a5 --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/context.rs @@ -0,0 +1,18 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BridgeContext { + pub deployment_id: Option, + pub project_id: Option, + pub workspace_id: Option, + pub request_id: String, + pub trace_id: String, + pub actor_type: String, + pub actor_id: String, + pub session_id: Option, + pub tenant_id: Option, + pub auth_token: Option, + pub source_channel: String, + pub source_client: String, + pub idempotency_key: Option, + pub validate_only: bool, + pub dry_run: bool, +} diff --git a/rust/crates/storage-convex-bridge/src/lib.rs b/rust/crates/storage-convex-bridge/src/lib.rs new file mode 100644 index 00000000..93da3b81 --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/lib.rs @@ -0,0 +1,266 @@ +pub mod context; +pub mod mapping; +pub mod read_path; +pub mod types; +pub mod validation; +pub mod write_path; + +pub use context::BridgeContext; +pub use mapping::{ + map_command_name_to_convex, map_query_name_to_convex, ConvexMutationRequest, ConvexQueryRequest, +}; +pub use read_path::{build_query_request, QueryReadResult}; +pub use types::{BridgeError, BridgeErrorKind, BridgeResult}; +pub use validation::{validate_command_envelope, validate_query_envelope}; +pub use write_path::{ + build_command_log_record, build_domain_event_record, build_write_pipeline, build_write_request, + WritePipelineResult, +}; + +#[cfg(test)] +mod tests { + use super::*; + use core_protocol::{ + command::{CreatePage, CreateWorkspace, UpdatePageOptions, UpdatePageStats, UpdatePageTitle}, + query::GetPage, + ActorPayload, CommandEnvelope, QueryEnvelope, SourcePayload, TargetRef, + }; + + fn demo_context() -> BridgeContext { + BridgeContext { + deployment_id: Some("dep_1".into()), + project_id: Some("proj_1".into()), + request_id: "req_1".into(), + trace_id: "trace_1".into(), + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + workspace_id: Some("ws_1".into()), + tenant_id: Some("tenant_1".into()), + auth_token: Some("token_1".into()), + source_channel: "cli".into(), + source_client: "mnote-cli".into(), + idempotency_key: Some("idem_ctx".into()), + validate_only: false, + dry_run: false, + } + } + + #[test] + fn build_write_request_keeps_workspace_scope() { + let command = CommandEnvelope { + name: "create_page".into(), + command_id: "cmd_1".into(), + idempotency_key: Some("idem_cmd".into()), + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "cli".into(), + client: "mnote-cli".into(), + }, + target: Some(TargetRef { + workspace_id: Some("ws_1".into()), + page_id: None, + block_id: None, + }), + payload: CreatePage { + title: "第一页".into(), + parent_page_id: None, + position: None, + }, + reason: Some("初始化页面".into()), + refs: vec!["spec:phase1-f".into()], + dry_run: false, + validate_only: false, + }; + + let request = build_write_request(&demo_context(), &command).expect("write request should build"); + assert_eq!(request.function_name, "pages:create"); + assert_eq!(request.workspace_id.as_deref(), Some("ws_1")); + assert_eq!(request.deployment_id.as_deref(), Some("dep_1")); + assert_eq!(request.project_id.as_deref(), Some("proj_1")); + assert_eq!(request.idempotency_key.as_deref(), Some("idem_cmd")); + assert!(request.payload_json.contains("\"kind\":\"command\"")); + assert!(request.payload_json.contains("\"name\":\"create_page\"")); + } + + #[test] + fn build_query_request_includes_trace_and_workspace() { + let query = QueryEnvelope { + name: "get_page".into(), + payload: GetPage { + page_id: "page_1".into(), + }, + }; + + let request = build_query_request(&demo_context(), &query).expect("query request should build"); + assert_eq!(request.function_name, "pages:get"); + assert_eq!(request.workspace_id.as_deref(), Some("ws_1")); + assert_eq!(request.request_id, "req_1"); + assert_eq!(request.trace_id, "trace_1"); + assert!(request.payload_json.contains("\"kind\":\"query\"")); + assert!(request.payload_json.contains("\"name\":\"get_page\"")); + } + + #[test] + fn build_write_pipeline_sets_trace_fields() { + let command = CommandEnvelope { + name: "create_workspace".into(), + command_id: "cmd_2".into(), + idempotency_key: None, + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "cli".into(), + client: "mnote-cli".into(), + }, + target: None, + payload: CreateWorkspace { + name: "demo".into(), + slug: Some("demo".into()), + }, + reason: Some("初始化工作区".into()), + refs: vec![], + dry_run: false, + validate_only: false, + }; + + let pipeline = build_write_pipeline(&demo_context(), &command).expect("pipeline should build"); + assert_eq!(pipeline.command_log.command_name, "create_workspace"); + assert!(pipeline.command_log.payload_summary.contains("request_id=req_1")); + assert_eq!(pipeline.command_log.trace_id, "trace_1"); + assert_eq!(pipeline.command_log.request_id, "req_1"); + assert!(pipeline.domain_event.payload_json.contains("\"trace_id\":\"trace_1\"")); + assert_eq!(pipeline.result.event_ids, vec!["evt_cmd_2".to_string()]); + } + + #[test] + fn document_title_command_maps_to_documents_update_title() { + let command = CommandEnvelope { + name: "documents.title.update".into(), + command_id: "cmd_title_1".into(), + idempotency_key: Some("idem_title".into()), + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "next-route".into(), + client: "wolai-frontend".into(), + }, + target: Some(TargetRef { + workspace_id: Some("ws_1".into()), + page_id: Some("page_1".into()), + block_id: None, + }), + payload: UpdatePageTitle { + page_id: "page_1".into(), + title: "新标题".into(), + }, + reason: None, + refs: vec![], + dry_run: false, + validate_only: false, + }; + + let request = build_write_request(&demo_context(), &command).expect("write request should build"); + assert_eq!(request.function_name, "documents:updateTitle"); + assert_eq!(request.workspace_id.as_deref(), Some("ws_1")); + assert!(request.payload_json.contains("\"name\":\"documents.title.update\"")); + } + + #[test] + fn document_stats_command_maps_to_documents_update_stats() { + let command = CommandEnvelope { + name: "documents.stats.update".into(), + command_id: "cmd_stats_1".into(), + idempotency_key: Some("idem_stats".into()), + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "next-route".into(), + client: "wolai-frontend".into(), + }, + target: Some(TargetRef { + workspace_id: Some("ws_1".into()), + page_id: Some("page_1".into()), + block_id: None, + }), + payload: UpdatePageStats { + page_id: "page_1".into(), + word_count: 10, + character_count: 20, + block_count: 3, + todo_total: 4, + todo_done: 1, + }, + reason: None, + refs: vec![], + dry_run: false, + validate_only: false, + }; + + let request = build_write_request(&demo_context(), &command).expect("write request should build"); + assert_eq!(request.function_name, "documents:updateStats"); + assert_eq!(request.workspace_id.as_deref(), Some("ws_1")); + assert!(request.payload_json.contains("\"name\":\"documents.stats.update\"")); + } + + #[test] + fn document_options_command_maps_to_documents_update_options() { + let command = CommandEnvelope { + name: "documents.options.update".into(), + command_id: "cmd_options_1".into(), + idempotency_key: Some("idem_options".into()), + actor: ActorPayload { + actor_type: "human".into(), + actor_id: "user_1".into(), + session_id: Some("session_1".into()), + }, + source: SourcePayload { + channel: "next-route".into(), + client: "wolai-frontend".into(), + }, + target: Some(TargetRef { + workspace_id: Some("ws_1".into()), + page_id: Some("page_1".into()), + block_id: None, + }), + payload: UpdatePageOptions { + page_id: "page_1".into(), + wide_layout: None, + small_text: None, + show_heading_numbers: None, + show_toc: Some(true), + show_structure: None, + protect_editing: None, + show_word_count: None, + collapse_backlinks: None, + page_font: None, + layout_density: Some("compact".into()), + hide_child_pages: None, + show_block_ref_count: None, + embed_default_block_id: None, + }, + reason: None, + refs: vec![], + dry_run: false, + validate_only: false, + }; + + let request = build_write_request(&demo_context(), &command).expect("write request should build"); + assert_eq!(request.function_name, "documents:updateOptions"); + assert_eq!(request.workspace_id.as_deref(), Some("ws_1")); + assert!(request.payload_json.contains("\"name\":\"documents.options.update\"")); + } +} diff --git a/rust/crates/storage-convex-bridge/src/mapping.rs b/rust/crates/storage-convex-bridge/src/mapping.rs new file mode 100644 index 00000000..bbc3fc1b --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/mapping.rs @@ -0,0 +1,93 @@ +use crate::context::BridgeContext; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConvexMutationRequest { + pub function_name: String, + pub deployment_id: Option, + pub project_id: Option, + pub workspace_id: Option, + pub request_id: String, + pub trace_id: String, + pub idempotency_key: Option, + pub actor_id: String, + pub payload_json: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConvexQueryRequest { + pub function_name: String, + pub deployment_id: Option, + pub project_id: Option, + pub workspace_id: Option, + pub request_id: String, + pub trace_id: String, + pub actor_id: String, + pub payload_json: String, +} + +pub fn map_command_name_to_convex(command_name: &str) -> &'static str { + match command_name { + "create_workspace" => "workspaces:create", + "create_page" => "pages:create", + "documents.title.update" => "documents:updateTitle", + "documents.stats.update" => "documents:updateStats", + "documents.options.update" => "documents:updateOptions", + "insert_block" => "blocks:insert", + "update_block" => "blocks:update", + "move_block" => "blocks:move", + "delete_block" => "blocks:delete", + _ => "commands:unknown", + } +} + +pub fn map_query_name_to_convex(query_name: &str) -> &'static str { + match query_name { + "get_page" => "pages:get", + "list_page_blocks" => "blocks:list_by_page", + "search_pages" => "search:pages", + "search_blocks" => "search:blocks", + _ => "queries:unknown", + } +} + +fn json_opt(value: &Option) -> String { + match value { + Some(v) => format!("\"{}\"", v), + None => "null".into(), + } +} + +pub fn payload_json_for_command(context: &BridgeContext, command_name: &str) -> String { + format!( + "{{\"kind\":\"command\",\"name\":\"{}\",\"request_id\":\"{}\",\"trace_id\":\"{}\",\"deployment_id\":{},\"project_id\":{},\"workspace_id\":{},\"tenant_id\":{},\"idempotency_key\":{},\"actor\":{{\"type\":\"{}\",\"id\":\"{}\",\"session_id\":{}}},\"source\":{{\"channel\":\"{}\",\"client\":\"{}\"}}}}", + command_name, + context.request_id, + context.trace_id, + json_opt(&context.deployment_id), + json_opt(&context.project_id), + json_opt(&context.workspace_id), + json_opt(&context.tenant_id), + json_opt(&context.idempotency_key), + context.actor_type, + context.actor_id, + json_opt(&context.session_id), + context.source_channel, + context.source_client, + ) +} + +pub fn payload_json_for_query(context: &BridgeContext, query_name: &str) -> String { + format!( + "{{\"kind\":\"query\",\"name\":\"{}\",\"request_id\":\"{}\",\"trace_id\":\"{}\",\"deployment_id\":{},\"project_id\":{},\"workspace_id\":{},\"tenant_id\":{},\"actor_id\":\"{}\",\"source\":{{\"channel\":\"{}\",\"client\":\"{}\"}}}}", + query_name, + context.request_id, + context.trace_id, + json_opt(&context.deployment_id), + json_opt(&context.project_id), + json_opt(&context.workspace_id), + json_opt(&context.tenant_id), + context.actor_id, + context.source_channel, + context.source_client, + ) +} diff --git a/rust/crates/storage-convex-bridge/src/read_path.rs b/rust/crates/storage-convex-bridge/src/read_path.rs new file mode 100644 index 00000000..3633a7ac --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/read_path.rs @@ -0,0 +1,29 @@ +use crate::context::BridgeContext; +use crate::mapping::{map_query_name_to_convex, payload_json_for_query, ConvexQueryRequest}; +use crate::types::BridgeResult; +use crate::validation::validate_query_envelope; +use core_protocol::QueryEnvelope; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QueryReadResult { + pub workspace_id: Option, + pub request_id: String, + pub trace_id: String, +} + +pub fn build_query_request( + context: &BridgeContext, + query: &QueryEnvelope, +) -> BridgeResult { + validate_query_envelope(context, query)?; + Ok(ConvexQueryRequest { + function_name: map_query_name_to_convex(&query.name).to_string(), + deployment_id: context.deployment_id.clone(), + project_id: context.project_id.clone(), + workspace_id: context.workspace_id.clone(), + request_id: context.request_id.clone(), + trace_id: context.trace_id.clone(), + actor_id: context.actor_id.clone(), + payload_json: payload_json_for_query(context, &query.name), + }) +} diff --git a/rust/crates/storage-convex-bridge/src/types.rs b/rust/crates/storage-convex-bridge/src/types.rs new file mode 100644 index 00000000..1dfee082 --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/types.rs @@ -0,0 +1,26 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BridgeErrorKind { + Validation, + Unauthorized, + Conflict, + NotFound, + Transport, + Rejected, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BridgeError { + pub kind: BridgeErrorKind, + pub message: String, +} + +impl BridgeError { + pub fn validation(message: impl Into) -> Self { + Self { + kind: BridgeErrorKind::Validation, + message: message.into(), + } + } +} + +pub type BridgeResult = Result; diff --git a/rust/crates/storage-convex-bridge/src/validation.rs b/rust/crates/storage-convex-bridge/src/validation.rs new file mode 100644 index 00000000..16cc2427 --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/validation.rs @@ -0,0 +1,50 @@ +use crate::context::BridgeContext; +use crate::types::{BridgeError, BridgeResult}; +use core_protocol::{CommandEnvelope, QueryEnvelope}; + +pub fn validate_command_envelope( + context: &BridgeContext, + command: &CommandEnvelope, +) -> BridgeResult<()> { + if command.name.trim().is_empty() { + return Err(BridgeError::validation("command name 不能为空")); + } + if command.command_id.trim().is_empty() { + return Err(BridgeError::validation("command_id 不能为空")); + } + if context.request_id.trim().is_empty() { + return Err(BridgeError::validation("request_id 不能为空")); + } + if context.trace_id.trim().is_empty() { + return Err(BridgeError::validation("trace_id 不能为空")); + } + if context.actor_id.trim().is_empty() { + return Err(BridgeError::validation("actor_id 不能为空")); + } + if context + .workspace_id + .as_deref() + .unwrap_or("") + .trim() + .is_empty() + { + return Err(BridgeError::validation("workspace_id 不能为空")); + } + Ok(()) +} + +pub fn validate_query_envelope( + context: &BridgeContext, + query: &QueryEnvelope, +) -> BridgeResult<()> { + if query.name.trim().is_empty() { + return Err(BridgeError::validation("query name 不能为空")); + } + if context.request_id.trim().is_empty() { + return Err(BridgeError::validation("request_id 不能为空")); + } + if context.trace_id.trim().is_empty() { + return Err(BridgeError::validation("trace_id 不能为空")); + } + Ok(()) +} diff --git a/rust/crates/storage-convex-bridge/src/write_path.rs b/rust/crates/storage-convex-bridge/src/write_path.rs new file mode 100644 index 00000000..bef086c1 --- /dev/null +++ b/rust/crates/storage-convex-bridge/src/write_path.rs @@ -0,0 +1,131 @@ +use crate::context::BridgeContext; +use crate::mapping::{map_command_name_to_convex, payload_json_for_command, ConvexMutationRequest}; +use crate::types::BridgeResult; +use crate::validation::validate_command_envelope; +use core_domain::{RefLink, Timestamp, WorkspaceId}; +use core_protocol::{command::CommandResult, AffectedObject, CommandEnvelope}; +use event_log::{CommandLogRecord, CommandLogStatus, DomainEventRecord, EventStatus}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WritePipelineResult { + pub mutation: ConvexMutationRequest, + pub command_log: CommandLogRecord, + pub domain_event: DomainEventRecord, + pub result: CommandResult, +} + +pub fn build_write_request( + context: &BridgeContext, + command: &CommandEnvelope, +) -> BridgeResult { + validate_command_envelope(context, command)?; + let workspace_id = command + .target + .as_ref() + .and_then(|target| target.workspace_id.clone()) + .or_else(|| context.workspace_id.clone()); + Ok(ConvexMutationRequest { + function_name: map_command_name_to_convex(&command.name).to_string(), + deployment_id: context.deployment_id.clone(), + project_id: context.project_id.clone(), + workspace_id, + request_id: context.request_id.clone(), + trace_id: context.trace_id.clone(), + idempotency_key: command + .idempotency_key + .clone() + .or_else(|| context.idempotency_key.clone()), + actor_id: context.actor_id.clone(), + payload_json: payload_json_for_command(context, &command.name), + }) +} + +pub fn build_command_log_record( + command_id: &str, + command_name: &str, + context: &BridgeContext, + workspace_id: &str, +) -> CommandLogRecord { + CommandLogRecord { + command_log_id: format!("clog_{command_id}"), + command_name: command_name.into(), + actor_type: context.actor_type.clone(), + actor_id: context.actor_id.clone(), + source: context.source_channel.clone(), + workspace_id: WorkspaceId::new(workspace_id), + target_objects: vec![RefLink::External(format!("workspace:{workspace_id}"))], + payload_summary: format!( + "command={command_name};request_id={};trace_id={}", + context.request_id, context.trace_id + ), + refs_json: format!( + "[\"request:{}\",\"trace:{}\"]", + context.request_id, context.trace_id + ), + idempotency_key: context.idempotency_key.clone(), + status: CommandLogStatus::Pending, + created_at: Timestamp::new("2026-04-11T00:00:00Z"), + finished_at: None, + trace_id: context.trace_id.clone(), + request_id: context.request_id.clone(), + } +} + +pub fn build_domain_event_record( + command_id: &str, + command_name: &str, + context: &BridgeContext, + workspace_id: &str, +) -> DomainEventRecord { + DomainEventRecord { + event_id: format!("evt_{command_id}"), + workspace_id: workspace_id.into(), + aggregate_type: "workspace".into(), + aggregate_id: workspace_id.into(), + event_type: format!("{command_name}_requested"), + event_version: 1, + payload_json: format!( + "{{\"request_id\":\"{}\",\"trace_id\":\"{}\",\"command_id\":\"{}\",\"command_name\":\"{}\"}}", + context.request_id, context.trace_id, command_id, command_name + ), + command_log_id: format!("clog_{command_id}"), + actor_type: context.actor_type.clone(), + created_at: Timestamp::new("2026-04-11T00:00:00Z"), + status: EventStatus::Pending, + trace_id: context.trace_id.clone(), + request_id: context.request_id.clone(), + command_id: command_id.into(), + } +} + +pub fn build_write_pipeline( + context: &BridgeContext, + command: &CommandEnvelope, +) -> BridgeResult { + let mutation = build_write_request(context, command)?; + let workspace_id = mutation + .workspace_id + .clone() + .unwrap_or_else(|| "workspace_unknown".into()); + let command_log = + build_command_log_record(&command.command_id, &command.name, context, &workspace_id); + let domain_event = + build_domain_event_record(&command.command_id, &command.name, context, &workspace_id); + let result = CommandResult { + ok: true, + command_id: command.command_id.clone(), + event_ids: vec![domain_event.event_id.clone()], + affected_objects: vec![AffectedObject { + object_type: "workspace".into(), + object_id: workspace_id.clone(), + }], + revision: Some(1), + warnings: vec![], + }; + Ok(WritePipelineResult { + mutation, + command_log, + domain_event, + result, + }) +} diff --git a/rust/target/.rustc_info.json b/rust/target/.rustc_info.json new file mode 100644 index 00000000..98614926 --- /dev/null +++ b/rust/target/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":4704921907237318175,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/lix/.rustup/toolchains/1.88.0-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.88.0 (6b00bc388 2025-06-23)\nbinary: rustc\ncommit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc\ncommit-date: 2025-06-23\nhost: x86_64-unknown-linux-gnu\nrelease: 1.88.0\nLLVM version: 20.1.5\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/rust/target/CACHEDIR.TAG b/rust/target/CACHEDIR.TAG new file mode 100644 index 00000000..20d7c319 --- /dev/null +++ b/rust/target/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/rust/target/debug/.cargo-lock b/rust/target/debug/.cargo-lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/dep-lib-core_domain b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/dep-lib-core_domain new file mode 100644 index 00000000..c7cbc2cf Binary files /dev/null and b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/dep-lib-core_domain differ diff --git a/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/invoked.timestamp b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain new file mode 100644 index 00000000..45afcdd6 --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain @@ -0,0 +1 @@ +e053498f67f46e4d \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain.json b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain.json new file mode 100644 index 00000000..a8b3f54f --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/lib-core_domain.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":6167971972065703354,"profile":17672942494452627365,"path":7278239953193670039,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-domain-40e6f74f7ef1a9aa/dep-lib-core_domain","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/dep-test-lib-core_domain b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/dep-test-lib-core_domain new file mode 100644 index 00000000..3329da94 Binary files /dev/null and b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/dep-test-lib-core_domain differ diff --git a/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/invoked.timestamp b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain new file mode 100644 index 00000000..a21bd4fa --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain @@ -0,0 +1 @@ +31ec3f0d1a2e5472 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain.json b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain.json new file mode 100644 index 00000000..427bb59d --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-bd799d0a25dcc494/test-lib-core_domain.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":6167971972065703354,"profile":1722584277633009122,"path":7278239953193670039,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-domain-bd799d0a25dcc494/dep-test-lib-core_domain","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/dep-lib-core_domain b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/dep-lib-core_domain new file mode 100644 index 00000000..e6d36052 Binary files /dev/null and b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/dep-lib-core_domain differ diff --git a/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/invoked.timestamp b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain new file mode 100644 index 00000000..dedce44d --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain @@ -0,0 +1 @@ +4e803ee3d8e7c709 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain.json b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain.json new file mode 100644 index 00000000..4af8438f --- /dev/null +++ b/rust/target/debug/.fingerprint/core-domain-cdff95eec220868e/lib-core_domain.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":6167971972065703354,"profile":8731458305071235362,"path":7278239953193670039,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-domain-cdff95eec220868e/dep-lib-core_domain","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/dep-test-lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/dep-test-lib-core_protocol new file mode 100644 index 00000000..a99618be Binary files /dev/null and b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/dep-test-lib-core_protocol differ diff --git a/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/invoked.timestamp b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol new file mode 100644 index 00000000..c3f0c677 --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol @@ -0,0 +1 @@ +44b9888b46dbed5c \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol.json b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol.json new file mode 100644 index 00000000..166a41bb --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/test-lib-core_protocol.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":12929318708107190822,"profile":1722584277633009122,"path":17093070036165809465,"deps":[[12060949818064432287,"core_domain",false,704786785418248270]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-protocol-0fdc11ecf89ad5aa/dep-test-lib-core_protocol","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/dep-lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/dep-lib-core_protocol new file mode 100644 index 00000000..35891572 Binary files /dev/null and b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/dep-lib-core_protocol differ diff --git a/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/invoked.timestamp b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol new file mode 100644 index 00000000..c627bdd3 --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol @@ -0,0 +1 @@ +69002a3ff9918d3d \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol.json b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol.json new file mode 100644 index 00000000..74d9df0f --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-4592bf3d65d89282/lib-core_protocol.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":12929318708107190822,"profile":17672942494452627365,"path":17093070036165809465,"deps":[[12060949818064432287,"core_domain",false,5579665713981379552]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-protocol-4592bf3d65d89282/dep-lib-core_protocol","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/dep-lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/dep-lib-core_protocol new file mode 100644 index 00000000..272eb7b0 Binary files /dev/null and b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/dep-lib-core_protocol differ diff --git a/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/invoked.timestamp b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol new file mode 100644 index 00000000..034f078e --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol @@ -0,0 +1 @@ +49c72387560f483c \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol.json b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol.json new file mode 100644 index 00000000..0e5b0544 --- /dev/null +++ b/rust/target/debug/.fingerprint/core-protocol-f5d7351438cd4f82/lib-core_protocol.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":12929318708107190822,"profile":8731458305071235362,"path":17093070036165809465,"deps":[[12060949818064432287,"core_domain",false,704786785418248270]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/core-protocol-f5d7351438cd4f82/dep-lib-core_protocol","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/dep-lib-event_log b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/dep-lib-event_log new file mode 100644 index 00000000..0c4a847f Binary files /dev/null and b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/dep-lib-event_log differ diff --git a/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/invoked.timestamp b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log new file mode 100644 index 00000000..a0e9ae43 --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log @@ -0,0 +1 @@ +a1e984aa5f972227 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log.json b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log.json new file mode 100644 index 00000000..55dc0a30 --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-563618231cacaf7e/lib-event_log.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":2075902485319387119,"profile":17672942494452627365,"path":3721623360069338606,"deps":[[12060949818064432287,"core_domain",false,5579665713981379552]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/event-log-563618231cacaf7e/dep-lib-event_log","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/dep-lib-event_log b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/dep-lib-event_log new file mode 100644 index 00000000..c496fd8d Binary files /dev/null and b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/dep-lib-event_log differ diff --git a/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/invoked.timestamp b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log new file mode 100644 index 00000000..3a9d5154 --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log @@ -0,0 +1 @@ +8f57032f1a7da02a \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log.json b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log.json new file mode 100644 index 00000000..ae938a1b --- /dev/null +++ b/rust/target/debug/.fingerprint/event-log-d9b5c77d292421e8/lib-event_log.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":2075902485319387119,"profile":8731458305071235362,"path":3721623360069338606,"deps":[[12060949818064432287,"core_domain",false,704786785418248270]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/event-log-d9b5c77d292421e8/dep-lib-event_log","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/dep-lib-index_fts b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/dep-lib-index_fts new file mode 100644 index 00000000..024be490 Binary files /dev/null and b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/dep-lib-index_fts differ diff --git a/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/invoked.timestamp b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts new file mode 100644 index 00000000..369c61a6 --- /dev/null +++ b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts @@ -0,0 +1 @@ +bf54d8ba9752e161 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts.json b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts.json new file mode 100644 index 00000000..f53b28ce --- /dev/null +++ b/rust/target/debug/.fingerprint/index-fts-e444a40571811be1/lib-index_fts.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":1159665536100219803,"profile":17672942494452627365,"path":12914430766979976756,"deps":[[12060949818064432287,"core_domain",false,5579665713981379552],[16479709356797637466,"event_log",false,2819982753825876385]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/index-fts-e444a40571811be1/dep-lib-index_fts","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/dep-lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/dep-lib-storage_convex_bridge new file mode 100644 index 00000000..f400ff32 Binary files /dev/null and b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/dep-lib-storage_convex_bridge differ diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/invoked.timestamp b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge new file mode 100644 index 00000000..5163c7c7 --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge @@ -0,0 +1 @@ +d0e3930355d176f1 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge.json b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge.json new file mode 100644 index 00000000..47035403 --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/lib-storage_convex_bridge.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":16973354705979238807,"profile":8731458305071235362,"path":2510431785475190115,"deps":[[7016416594668144918,"core_protocol",false,4343738704907716425],[12060949818064432287,"core_domain",false,704786785418248270],[16479709356797637466,"event_log",false,3071592497278048143]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/storage-convex-bridge-45166d07753c36d7/dep-lib-storage_convex_bridge","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/dep-test-lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/dep-test-lib-storage_convex_bridge new file mode 100644 index 00000000..f97bc78e Binary files /dev/null and b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/dep-test-lib-storage_convex_bridge differ diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/invoked.timestamp b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge new file mode 100644 index 00000000..24938f59 --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge @@ -0,0 +1 @@ +b564adb2c82f1811 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge.json b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge.json new file mode 100644 index 00000000..5a97beba --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/test-lib-storage_convex_bridge.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":16973354705979238807,"profile":1722584277633009122,"path":2510431785475190115,"deps":[[7016416594668144918,"core_protocol",false,4343738704907716425],[12060949818064432287,"core_domain",false,704786785418248270],[16479709356797637466,"event_log",false,3071592497278048143]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/storage-convex-bridge-5bf1d06211b95150/dep-test-lib-storage_convex_bridge","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/dep-lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/dep-lib-storage_convex_bridge new file mode 100644 index 00000000..225f050c Binary files /dev/null and b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/dep-lib-storage_convex_bridge differ diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/invoked.timestamp b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/invoked.timestamp new file mode 100644 index 00000000..e00328da --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge new file mode 100644 index 00000000..02dcc426 --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge @@ -0,0 +1 @@ +8dd9c30bdd3eb769 \ No newline at end of file diff --git a/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge.json b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge.json new file mode 100644 index 00000000..6df5c50c --- /dev/null +++ b/rust/target/debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/lib-storage_convex_bridge.json @@ -0,0 +1 @@ +{"rustc":11410426090777951712,"features":"[]","declared_features":"[]","target":16973354705979238807,"profile":17672942494452627365,"path":2510431785475190115,"deps":[[7016416594668144918,"core_protocol",false,4435361707722408041],[12060949818064432287,"core_domain",false,5579665713981379552],[16479709356797637466,"event_log",false,2819982753825876385]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/storage-convex-bridge-cd4527dde6c0c1c6/dep-lib-storage_convex_bridge","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0} \ No newline at end of file diff --git a/rust/target/debug/deps/core_domain-40e6f74f7ef1a9aa.d b/rust/target/debug/deps/core_domain-40e6f74f7ef1a9aa.d new file mode 100644 index 00000000..c4a77d6e --- /dev/null +++ b/rust/target/debug/deps/core_domain-40e6f74f7ef1a9aa.d @@ -0,0 +1,10 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_domain-40e6f74f7ef1a9aa.d: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_domain-40e6f74f7ef1a9aa.rmeta: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +crates/core-domain/src/lib.rs: +crates/core-domain/src/audit.rs: +crates/core-domain/src/entities.rs: +crates/core-domain/src/ids.rs: +crates/core-domain/src/time.rs: +crates/core-domain/src/types.rs: diff --git a/rust/target/debug/deps/core_domain-bd799d0a25dcc494 b/rust/target/debug/deps/core_domain-bd799d0a25dcc494 new file mode 100644 index 00000000..1e68f2bd Binary files /dev/null and b/rust/target/debug/deps/core_domain-bd799d0a25dcc494 differ diff --git a/rust/target/debug/deps/core_domain-bd799d0a25dcc494.d b/rust/target/debug/deps/core_domain-bd799d0a25dcc494.d new file mode 100644 index 00000000..d6543e21 --- /dev/null +++ b/rust/target/debug/deps/core_domain-bd799d0a25dcc494.d @@ -0,0 +1,10 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_domain-bd799d0a25dcc494.d: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/core_domain-bd799d0a25dcc494: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +crates/core-domain/src/lib.rs: +crates/core-domain/src/audit.rs: +crates/core-domain/src/entities.rs: +crates/core-domain/src/ids.rs: +crates/core-domain/src/time.rs: +crates/core-domain/src/types.rs: diff --git a/rust/target/debug/deps/core_domain-cdff95eec220868e.d b/rust/target/debug/deps/core_domain-cdff95eec220868e.d new file mode 100644 index 00000000..6a171b68 --- /dev/null +++ b/rust/target/debug/deps/core_domain-cdff95eec220868e.d @@ -0,0 +1,12 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_domain-cdff95eec220868e.d: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rlib: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rmeta: crates/core-domain/src/lib.rs crates/core-domain/src/audit.rs crates/core-domain/src/entities.rs crates/core-domain/src/ids.rs crates/core-domain/src/time.rs crates/core-domain/src/types.rs + +crates/core-domain/src/lib.rs: +crates/core-domain/src/audit.rs: +crates/core-domain/src/entities.rs: +crates/core-domain/src/ids.rs: +crates/core-domain/src/time.rs: +crates/core-domain/src/types.rs: diff --git a/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa b/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa new file mode 100644 index 00000000..9394ac3c Binary files /dev/null and b/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa differ diff --git a/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa.d b/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa.d new file mode 100644 index 00000000..8c5f4fcd --- /dev/null +++ b/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa.d @@ -0,0 +1,10 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa.d: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/core_protocol-0fdc11ecf89ad5aa: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +crates/core-protocol/src/lib.rs: +crates/core-protocol/src/command.rs: +crates/core-protocol/src/common.rs: +crates/core-protocol/src/governance.rs: +crates/core-protocol/src/query.rs: +crates/core-protocol/src/tool.rs: diff --git a/rust/target/debug/deps/core_protocol-4592bf3d65d89282.d b/rust/target/debug/deps/core_protocol-4592bf3d65d89282.d new file mode 100644 index 00000000..c078e761 --- /dev/null +++ b/rust/target/debug/deps/core_protocol-4592bf3d65d89282.d @@ -0,0 +1,10 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_protocol-4592bf3d65d89282.d: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_protocol-4592bf3d65d89282.rmeta: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +crates/core-protocol/src/lib.rs: +crates/core-protocol/src/command.rs: +crates/core-protocol/src/common.rs: +crates/core-protocol/src/governance.rs: +crates/core-protocol/src/query.rs: +crates/core-protocol/src/tool.rs: diff --git a/rust/target/debug/deps/core_protocol-f5d7351438cd4f82.d b/rust/target/debug/deps/core_protocol-f5d7351438cd4f82.d new file mode 100644 index 00000000..d677ee54 --- /dev/null +++ b/rust/target/debug/deps/core_protocol-f5d7351438cd4f82.d @@ -0,0 +1,12 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/core_protocol-f5d7351438cd4f82.d: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rlib: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rmeta: crates/core-protocol/src/lib.rs crates/core-protocol/src/command.rs crates/core-protocol/src/common.rs crates/core-protocol/src/governance.rs crates/core-protocol/src/query.rs crates/core-protocol/src/tool.rs + +crates/core-protocol/src/lib.rs: +crates/core-protocol/src/command.rs: +crates/core-protocol/src/common.rs: +crates/core-protocol/src/governance.rs: +crates/core-protocol/src/query.rs: +crates/core-protocol/src/tool.rs: diff --git a/rust/target/debug/deps/event_log-563618231cacaf7e.d b/rust/target/debug/deps/event_log-563618231cacaf7e.d new file mode 100644 index 00000000..e37c8e9d --- /dev/null +++ b/rust/target/debug/deps/event_log-563618231cacaf7e.d @@ -0,0 +1,8 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/event_log-563618231cacaf7e.d: crates/event-log/src/lib.rs crates/event-log/src/command_log.rs crates/event-log/src/domain_event.rs crates/event-log/src/rules.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libevent_log-563618231cacaf7e.rmeta: crates/event-log/src/lib.rs crates/event-log/src/command_log.rs crates/event-log/src/domain_event.rs crates/event-log/src/rules.rs + +crates/event-log/src/lib.rs: +crates/event-log/src/command_log.rs: +crates/event-log/src/domain_event.rs: +crates/event-log/src/rules.rs: diff --git a/rust/target/debug/deps/event_log-d9b5c77d292421e8.d b/rust/target/debug/deps/event_log-d9b5c77d292421e8.d new file mode 100644 index 00000000..a25c2554 --- /dev/null +++ b/rust/target/debug/deps/event_log-d9b5c77d292421e8.d @@ -0,0 +1,10 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/event_log-d9b5c77d292421e8.d: crates/event-log/src/lib.rs crates/event-log/src/command_log.rs crates/event-log/src/domain_event.rs crates/event-log/src/rules.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rlib: crates/event-log/src/lib.rs crates/event-log/src/command_log.rs crates/event-log/src/domain_event.rs crates/event-log/src/rules.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rmeta: crates/event-log/src/lib.rs crates/event-log/src/command_log.rs crates/event-log/src/domain_event.rs crates/event-log/src/rules.rs + +crates/event-log/src/lib.rs: +crates/event-log/src/command_log.rs: +crates/event-log/src/domain_event.rs: +crates/event-log/src/rules.rs: diff --git a/rust/target/debug/deps/index_fts-e444a40571811be1.d b/rust/target/debug/deps/index_fts-e444a40571811be1.d new file mode 100644 index 00000000..ccbea169 --- /dev/null +++ b/rust/target/debug/deps/index_fts-e444a40571811be1.d @@ -0,0 +1,5 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/index_fts-e444a40571811be1.d: crates/index-fts/src/lib.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libindex_fts-e444a40571811be1.rmeta: crates/index-fts/src/lib.rs + +crates/index-fts/src/lib.rs: diff --git a/rust/target/debug/deps/libcore_domain-40e6f74f7ef1a9aa.rmeta b/rust/target/debug/deps/libcore_domain-40e6f74f7ef1a9aa.rmeta new file mode 100644 index 00000000..a8b3b904 Binary files /dev/null and b/rust/target/debug/deps/libcore_domain-40e6f74f7ef1a9aa.rmeta differ diff --git a/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rlib b/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rlib new file mode 100644 index 00000000..e8da72e4 Binary files /dev/null and b/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rlib differ diff --git a/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rmeta b/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rmeta new file mode 100644 index 00000000..afcd2c0f Binary files /dev/null and b/rust/target/debug/deps/libcore_domain-cdff95eec220868e.rmeta differ diff --git a/rust/target/debug/deps/libcore_protocol-4592bf3d65d89282.rmeta b/rust/target/debug/deps/libcore_protocol-4592bf3d65d89282.rmeta new file mode 100644 index 00000000..926c1b61 Binary files /dev/null and b/rust/target/debug/deps/libcore_protocol-4592bf3d65d89282.rmeta differ diff --git a/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rlib b/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rlib new file mode 100644 index 00000000..4e7eb063 Binary files /dev/null and b/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rlib differ diff --git a/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rmeta b/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rmeta new file mode 100644 index 00000000..317e752a Binary files /dev/null and b/rust/target/debug/deps/libcore_protocol-f5d7351438cd4f82.rmeta differ diff --git a/rust/target/debug/deps/libevent_log-563618231cacaf7e.rmeta b/rust/target/debug/deps/libevent_log-563618231cacaf7e.rmeta new file mode 100644 index 00000000..98bc0401 Binary files /dev/null and b/rust/target/debug/deps/libevent_log-563618231cacaf7e.rmeta differ diff --git a/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rlib b/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rlib new file mode 100644 index 00000000..cd41e23b Binary files /dev/null and b/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rlib differ diff --git a/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rmeta b/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rmeta new file mode 100644 index 00000000..646b6dbd Binary files /dev/null and b/rust/target/debug/deps/libevent_log-d9b5c77d292421e8.rmeta differ diff --git a/rust/target/debug/deps/libindex_fts-e444a40571811be1.rmeta b/rust/target/debug/deps/libindex_fts-e444a40571811be1.rmeta new file mode 100644 index 00000000..0e6b52c3 Binary files /dev/null and b/rust/target/debug/deps/libindex_fts-e444a40571811be1.rmeta differ diff --git a/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rlib b/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rlib new file mode 100644 index 00000000..79f126f4 Binary files /dev/null and b/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rlib differ diff --git a/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rmeta b/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rmeta new file mode 100644 index 00000000..77e96701 Binary files /dev/null and b/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rmeta differ diff --git a/rust/target/debug/deps/libstorage_convex_bridge-cd4527dde6c0c1c6.rmeta b/rust/target/debug/deps/libstorage_convex_bridge-cd4527dde6c0c1c6.rmeta new file mode 100644 index 00000000..a9060c54 Binary files /dev/null and b/rust/target/debug/deps/libstorage_convex_bridge-cd4527dde6c0c1c6.rmeta differ diff --git a/rust/target/debug/deps/storage_convex_bridge-45166d07753c36d7.d b/rust/target/debug/deps/storage_convex_bridge-45166d07753c36d7.d new file mode 100644 index 00000000..43f825ae --- /dev/null +++ b/rust/target/debug/deps/storage_convex_bridge-45166d07753c36d7.d @@ -0,0 +1,13 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/storage_convex_bridge-45166d07753c36d7.d: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rlib: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libstorage_convex_bridge-45166d07753c36d7.rmeta: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +crates/storage-convex-bridge/src/lib.rs: +crates/storage-convex-bridge/src/context.rs: +crates/storage-convex-bridge/src/mapping.rs: +crates/storage-convex-bridge/src/read_path.rs: +crates/storage-convex-bridge/src/types.rs: +crates/storage-convex-bridge/src/validation.rs: +crates/storage-convex-bridge/src/write_path.rs: diff --git a/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150 b/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150 new file mode 100644 index 00000000..e8a885ee Binary files /dev/null and b/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150 differ diff --git a/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150.d b/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150.d new file mode 100644 index 00000000..71cf3bd1 --- /dev/null +++ b/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150.d @@ -0,0 +1,11 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150.d: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/storage_convex_bridge-5bf1d06211b95150: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +crates/storage-convex-bridge/src/lib.rs: +crates/storage-convex-bridge/src/context.rs: +crates/storage-convex-bridge/src/mapping.rs: +crates/storage-convex-bridge/src/read_path.rs: +crates/storage-convex-bridge/src/types.rs: +crates/storage-convex-bridge/src/validation.rs: +crates/storage-convex-bridge/src/write_path.rs: diff --git a/rust/target/debug/deps/storage_convex_bridge-cd4527dde6c0c1c6.d b/rust/target/debug/deps/storage_convex_bridge-cd4527dde6c0c1c6.d new file mode 100644 index 00000000..8d8ad247 --- /dev/null +++ b/rust/target/debug/deps/storage_convex_bridge-cd4527dde6c0c1c6.d @@ -0,0 +1,11 @@ +/mnt/Data1T/mnote/rust/target/debug/deps/storage_convex_bridge-cd4527dde6c0c1c6.d: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +/mnt/Data1T/mnote/rust/target/debug/deps/libstorage_convex_bridge-cd4527dde6c0c1c6.rmeta: crates/storage-convex-bridge/src/lib.rs crates/storage-convex-bridge/src/context.rs crates/storage-convex-bridge/src/mapping.rs crates/storage-convex-bridge/src/read_path.rs crates/storage-convex-bridge/src/types.rs crates/storage-convex-bridge/src/validation.rs crates/storage-convex-bridge/src/write_path.rs + +crates/storage-convex-bridge/src/lib.rs: +crates/storage-convex-bridge/src/context.rs: +crates/storage-convex-bridge/src/mapping.rs: +crates/storage-convex-bridge/src/read_path.rs: +crates/storage-convex-bridge/src/types.rs: +crates/storage-convex-bridge/src/validation.rs: +crates/storage-convex-bridge/src/write_path.rs: diff --git a/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/dep-graph.bin b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/dep-graph.bin new file mode 100644 index 00000000..a2a5fb30 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/query-cache.bin b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/query-cache.bin new file mode 100644 index 00000000..7fcac01d Binary files /dev/null and b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/work-products.bin b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/work-products.bin new file mode 100644 index 00000000..682eda3a Binary files /dev/null and b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv-3kzezd4hd0mk2f4o1ylduzngb/work-products.bin differ diff --git a/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv.lock b/rust/target/debug/incremental/core_domain-1w4uhq086wliv/s-hhki6hdkwu-18wwgcv.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/1lutfx2bvefapz3ropcehvc47.o b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/1lutfx2bvefapz3ropcehvc47.o new file mode 100644 index 00000000..a7578bc7 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/1lutfx2bvefapz3ropcehvc47.o differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/24gh56g455shba13upd4hddtt.o b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/24gh56g455shba13upd4hddtt.o new file mode 100644 index 00000000..0351bf18 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/24gh56g455shba13upd4hddtt.o differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/70r1advwk10k72nazgdujtoq8.o b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/70r1advwk10k72nazgdujtoq8.o new file mode 100644 index 00000000..07dc8de5 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/70r1advwk10k72nazgdujtoq8.o differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/a12sgt0fh7untbnb9zowipuba.o b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/a12sgt0fh7untbnb9zowipuba.o new file mode 100644 index 00000000..3d8ff4d0 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/a12sgt0fh7untbnb9zowipuba.o differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/dep-graph.bin b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/dep-graph.bin new file mode 100644 index 00000000..ef5601d9 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/eo8yq02obvjr00oktl076hpvx.o b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/eo8yq02obvjr00oktl076hpvx.o new file mode 100644 index 00000000..a8b01659 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/eo8yq02obvjr00oktl076hpvx.o differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/query-cache.bin b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/query-cache.bin new file mode 100644 index 00000000..e0397f9c Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/work-products.bin b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/work-products.bin new file mode 100644 index 00000000..14e57c1a Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8-b10006pd3vyso6ocq4s5rkv51/work-products.bin differ diff --git a/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8.lock b/rust/target/debug/incremental/core_domain-2gho5383shz8t/s-hhksnqjffb-0zhaac8.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/0jrwn5al49neljnnvt6eeoitq.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/0jrwn5al49neljnnvt6eeoitq.o new file mode 100644 index 00000000..f461aa18 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/0jrwn5al49neljnnvt6eeoitq.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ev7swngcriasku9kh1xqvxl7.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ev7swngcriasku9kh1xqvxl7.o new file mode 100644 index 00000000..bf4eef87 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ev7swngcriasku9kh1xqvxl7.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1one962q1mg7gtwgz3c2qrojj.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1one962q1mg7gtwgz3c2qrojj.o new file mode 100644 index 00000000..00adb66e Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1one962q1mg7gtwgz3c2qrojj.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ta3552qo7k6kwslwki6wr7um.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ta3552qo7k6kwslwki6wr7um.o new file mode 100644 index 00000000..21ce8a91 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/1ta3552qo7k6kwslwki6wr7um.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/26a574cd9lt5gbge0hxfmdtxd.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/26a574cd9lt5gbge0hxfmdtxd.o new file mode 100644 index 00000000..efce30fa Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/26a574cd9lt5gbge0hxfmdtxd.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/2oh7przv2zfqkxtjgzd3whsh1.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/2oh7przv2zfqkxtjgzd3whsh1.o new file mode 100644 index 00000000..e41ff909 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/2oh7przv2zfqkxtjgzd3whsh1.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/47b9simkgic3vj9x63op1e1p6.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/47b9simkgic3vj9x63op1e1p6.o new file mode 100644 index 00000000..ccb65140 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/47b9simkgic3vj9x63op1e1p6.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/53efemgv2s72pml9l0iy327b8.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/53efemgv2s72pml9l0iy327b8.o new file mode 100644 index 00000000..50f622b7 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/53efemgv2s72pml9l0iy327b8.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5ccc9lid867rdczhq5fszm0u6.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5ccc9lid867rdczhq5fszm0u6.o new file mode 100644 index 00000000..8957b153 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5ccc9lid867rdczhq5fszm0u6.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5vi3lzhm125076f2r9wtr91lm.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5vi3lzhm125076f2r9wtr91lm.o new file mode 100644 index 00000000..a22e1c67 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/5vi3lzhm125076f2r9wtr91lm.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nt92fxt2as5rb47txmv2hod5.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nt92fxt2as5rb47txmv2hod5.o new file mode 100644 index 00000000..f6b4665c Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nt92fxt2as5rb47txmv2hod5.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nywfsem4u1ojuotmerxky74c.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nywfsem4u1ojuotmerxky74c.o new file mode 100644 index 00000000..f7491168 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6nywfsem4u1ojuotmerxky74c.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6z80gnw1o2s55gnd9tof0uzyq.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6z80gnw1o2s55gnd9tof0uzyq.o new file mode 100644 index 00000000..b3eef946 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/6z80gnw1o2s55gnd9tof0uzyq.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7373w20n0yue8whwqjaez1yyv.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7373w20n0yue8whwqjaez1yyv.o new file mode 100644 index 00000000..48581c52 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7373w20n0yue8whwqjaez1yyv.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7l4kx50474dwbm6pyf1fqucug.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7l4kx50474dwbm6pyf1fqucug.o new file mode 100644 index 00000000..a20e3cf9 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7l4kx50474dwbm6pyf1fqucug.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7omiu0ck3sqkvllam1jnmew2p.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7omiu0ck3sqkvllam1jnmew2p.o new file mode 100644 index 00000000..80b70225 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7omiu0ck3sqkvllam1jnmew2p.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7p0quek6yxerybr3i8jg48nhq.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7p0quek6yxerybr3i8jg48nhq.o new file mode 100644 index 00000000..1216d49f Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/7p0quek6yxerybr3i8jg48nhq.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8hqdt25i0whsidzbp7sucj5p3.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8hqdt25i0whsidzbp7sucj5p3.o new file mode 100644 index 00000000..17b0af14 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8hqdt25i0whsidzbp7sucj5p3.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8m36uzhzuuc5jeb58dpyl4x3b.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8m36uzhzuuc5jeb58dpyl4x3b.o new file mode 100644 index 00000000..db160baa Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/8m36uzhzuuc5jeb58dpyl4x3b.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/al8hz0j4n0t34wm8w5908jp96.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/al8hz0j4n0t34wm8w5908jp96.o new file mode 100644 index 00000000..5aac1da6 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/al8hz0j4n0t34wm8w5908jp96.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/aras4y6sncq0ipbz24y4vh3wi.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/aras4y6sncq0ipbz24y4vh3wi.o new file mode 100644 index 00000000..93f1d740 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/aras4y6sncq0ipbz24y4vh3wi.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bae4zrq0hpnyspnpkniwjvhhx.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bae4zrq0hpnyspnpkniwjvhhx.o new file mode 100644 index 00000000..2ed949d5 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bae4zrq0hpnyspnpkniwjvhhx.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bdxvnkwo7tb8ed1xdx3qjussf.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bdxvnkwo7tb8ed1xdx3qjussf.o new file mode 100644 index 00000000..6e488c52 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bdxvnkwo7tb8ed1xdx3qjussf.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bvaz6mx9ahydp1ywzwoyum17m.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bvaz6mx9ahydp1ywzwoyum17m.o new file mode 100644 index 00000000..7680f07b Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/bvaz6mx9ahydp1ywzwoyum17m.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/c3cwpxzw4vy258jijkljvwhb6.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/c3cwpxzw4vy258jijkljvwhb6.o new file mode 100644 index 00000000..c73937ef Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/c3cwpxzw4vy258jijkljvwhb6.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/dep-graph.bin b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/dep-graph.bin new file mode 100644 index 00000000..e0c917a0 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/eawwkapxxx85sj8wdc8vwq20m.o b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/eawwkapxxx85sj8wdc8vwq20m.o new file mode 100644 index 00000000..fdfdf141 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/eawwkapxxx85sj8wdc8vwq20m.o differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/query-cache.bin b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/query-cache.bin new file mode 100644 index 00000000..dac77fda Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/work-products.bin b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/work-products.bin new file mode 100644 index 00000000..cdeba693 Binary files /dev/null and b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5-d5gb2mozubofzw09evm1ioaqc/work-products.bin differ diff --git a/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5.lock b/rust/target/debug/incremental/core_domain-2qmngcoxas1q0/s-hhksnqjfea-0uncem5.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0fcot6m5tqusduxfvyt4k1vm7.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0fcot6m5tqusduxfvyt4k1vm7.o new file mode 100644 index 00000000..95bf741a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0fcot6m5tqusduxfvyt4k1vm7.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0h0l87vv5rqa6opj6fa6qocqf.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0h0l87vv5rqa6opj6fa6qocqf.o new file mode 100644 index 00000000..a04a73bc Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/0h0l87vv5rqa6opj6fa6qocqf.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/1qwuthztbwxa2mzndqytvoc5h.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/1qwuthztbwxa2mzndqytvoc5h.o new file mode 100644 index 00000000..c8d0698e Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/1qwuthztbwxa2mzndqytvoc5h.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/26h4q7f15x11ct4kqh6d9725k.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/26h4q7f15x11ct4kqh6d9725k.o new file mode 100644 index 00000000..a40cca78 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/26h4q7f15x11ct4kqh6d9725k.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/2zvaeouivcicgorrwzp3u5gxm.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/2zvaeouivcicgorrwzp3u5gxm.o new file mode 100644 index 00000000..7a46b26b Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/2zvaeouivcicgorrwzp3u5gxm.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3ez07f1d9coodxw021fq2jqq0.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3ez07f1d9coodxw021fq2jqq0.o new file mode 100644 index 00000000..84141074 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3ez07f1d9coodxw021fq2jqq0.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3hz2coioti6o6kjtssqtbfs55.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3hz2coioti6o6kjtssqtbfs55.o new file mode 100644 index 00000000..9ad8d545 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/3hz2coioti6o6kjtssqtbfs55.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/42wcvy1tjpo72yfvu18voirjh.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/42wcvy1tjpo72yfvu18voirjh.o new file mode 100644 index 00000000..614e0175 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/42wcvy1tjpo72yfvu18voirjh.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4hc4dd6xv0i2itvgywx1lahl8.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4hc4dd6xv0i2itvgywx1lahl8.o new file mode 100644 index 00000000..0c7f1bf6 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4hc4dd6xv0i2itvgywx1lahl8.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4itr5en1t999jjm3niw6ym315.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4itr5en1t999jjm3niw6ym315.o new file mode 100644 index 00000000..4d512adf Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/4itr5en1t999jjm3niw6ym315.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5n3qij2c5c65q1jwvpv712f86.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5n3qij2c5c65q1jwvpv712f86.o new file mode 100644 index 00000000..b307fde7 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5n3qij2c5c65q1jwvpv712f86.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5vfvnjfqatgj9tcy20466egul.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5vfvnjfqatgj9tcy20466egul.o new file mode 100644 index 00000000..53e27428 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/5vfvnjfqatgj9tcy20466egul.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/7sxist1itnqato11mzu8cb9s7.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/7sxist1itnqato11mzu8cb9s7.o new file mode 100644 index 00000000..dda62191 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/7sxist1itnqato11mzu8cb9s7.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/90p39xzijkgh4ywq1t9ijveu1.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/90p39xzijkgh4ywq1t9ijveu1.o new file mode 100644 index 00000000..888f7922 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/90p39xzijkgh4ywq1t9ijveu1.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/9y10rckkmegt70c4fic7d9pn0.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/9y10rckkmegt70c4fic7d9pn0.o new file mode 100644 index 00000000..483dbf4e Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/9y10rckkmegt70c4fic7d9pn0.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/agt77gq173zfkllhyqtt7g778.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/agt77gq173zfkllhyqtt7g778.o new file mode 100644 index 00000000..d4bcda88 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/agt77gq173zfkllhyqtt7g778.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/arqabmwpzh0w95srn7kwf39c3.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/arqabmwpzh0w95srn7kwf39c3.o new file mode 100644 index 00000000..fe6d3c49 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/arqabmwpzh0w95srn7kwf39c3.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/br9og3o8t217kpyg0k4twauic.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/br9og3o8t217kpyg0k4twauic.o new file mode 100644 index 00000000..476149cd Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/br9og3o8t217kpyg0k4twauic.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/cu53u8qm0wjh7s5p10jdqv6k9.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/cu53u8qm0wjh7s5p10jdqv6k9.o new file mode 100644 index 00000000..c9717928 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/cu53u8qm0wjh7s5p10jdqv6k9.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/d499c6yw1qx52w2i1ejat34b9.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/d499c6yw1qx52w2i1ejat34b9.o new file mode 100644 index 00000000..2cae0965 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/d499c6yw1qx52w2i1ejat34b9.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/dep-graph.bin b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/dep-graph.bin new file mode 100644 index 00000000..a15f3c55 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/e0ga13reow5buorr3ciykquxg.o b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/e0ga13reow5buorr3ciykquxg.o new file mode 100644 index 00000000..63e49e77 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/e0ga13reow5buorr3ciykquxg.o differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/query-cache.bin b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/query-cache.bin new file mode 100644 index 00000000..ba46c18a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/work-products.bin b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/work-products.bin new file mode 100644 index 00000000..9671121d Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3-2zrlzwyckzakklxxj4xgsli54/work-products.bin differ diff --git a/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3.lock b/rust/target/debug/incremental/core_protocol-1o9yw3jm5j175/s-hhl153wn9x-033msj3.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/dep-graph.bin b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/dep-graph.bin new file mode 100644 index 00000000..0e2840d9 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/query-cache.bin b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/query-cache.bin new file mode 100644 index 00000000..04411501 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/work-products.bin b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/work-products.bin new file mode 100644 index 00000000..682eda3a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a-bezcerp6vvc7fsyxbjhzch3yg/work-products.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a.lock b/rust/target/debug/incremental/core_protocol-3lnbdlbcnufjr/s-hhl4golkrm-0s09j4a.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/025xragbai5dta5plerat93f3.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/025xragbai5dta5plerat93f3.o new file mode 100644 index 00000000..73071f70 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/025xragbai5dta5plerat93f3.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0rv6ak5p0eb1rck5ctf2oaclr.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0rv6ak5p0eb1rck5ctf2oaclr.o new file mode 100644 index 00000000..59327207 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0rv6ak5p0eb1rck5ctf2oaclr.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0tmesi7kvcnj1zuun9t7x7d20.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0tmesi7kvcnj1zuun9t7x7d20.o new file mode 100644 index 00000000..982d0459 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/0tmesi7kvcnj1zuun9t7x7d20.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1gxilbg5v33c3sp3ixvyyyiw4.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1gxilbg5v33c3sp3ixvyyyiw4.o new file mode 100644 index 00000000..c0c5c5f2 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1gxilbg5v33c3sp3ixvyyyiw4.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1ku29ivzmdo8smejib2buet6y.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1ku29ivzmdo8smejib2buet6y.o new file mode 100644 index 00000000..d129840a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1ku29ivzmdo8smejib2buet6y.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1zlwgswl6mhaz1sc9tv7h6fvv.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1zlwgswl6mhaz1sc9tv7h6fvv.o new file mode 100644 index 00000000..c73c8e71 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/1zlwgswl6mhaz1sc9tv7h6fvv.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2esnpumhts8ffozf9l7u1cptl.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2esnpumhts8ffozf9l7u1cptl.o new file mode 100644 index 00000000..cd75db77 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2esnpumhts8ffozf9l7u1cptl.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2s1nr1urf1g6ngfu62hqx69z6.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2s1nr1urf1g6ngfu62hqx69z6.o new file mode 100644 index 00000000..9978726a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/2s1nr1urf1g6ngfu62hqx69z6.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/33z9d6jfu3y7tans15drunu25.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/33z9d6jfu3y7tans15drunu25.o new file mode 100644 index 00000000..898a2d4f Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/33z9d6jfu3y7tans15drunu25.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/3pc64ndxqojunfw70q4txk2ki.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/3pc64ndxqojunfw70q4txk2ki.o new file mode 100644 index 00000000..62a6a702 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/3pc64ndxqojunfw70q4txk2ki.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/4ml0g2qovdtyvw2yps2s2dm6r.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/4ml0g2qovdtyvw2yps2s2dm6r.o new file mode 100644 index 00000000..409e0cdc Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/4ml0g2qovdtyvw2yps2s2dm6r.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/54bzp638hogbizuf07vg0rggr.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/54bzp638hogbizuf07vg0rggr.o new file mode 100644 index 00000000..2e1d858a Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/54bzp638hogbizuf07vg0rggr.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5g3bohf2ogtx1my7ipgm599yn.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5g3bohf2ogtx1my7ipgm599yn.o new file mode 100644 index 00000000..c508d27f Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5g3bohf2ogtx1my7ipgm599yn.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5zqjxe9k5bged35r763nbfgpn.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5zqjxe9k5bged35r763nbfgpn.o new file mode 100644 index 00000000..97d92322 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/5zqjxe9k5bged35r763nbfgpn.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/63jauxjb83pcyu8jjqh4ghypm.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/63jauxjb83pcyu8jjqh4ghypm.o new file mode 100644 index 00000000..94fb48d7 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/63jauxjb83pcyu8jjqh4ghypm.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/6wvm03xo5lvrennz7ybu2s283.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/6wvm03xo5lvrennz7ybu2s283.o new file mode 100644 index 00000000..74ee57e0 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/6wvm03xo5lvrennz7ybu2s283.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/72ivnb41hqr5vquk5s9h2x4gp.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/72ivnb41hqr5vquk5s9h2x4gp.o new file mode 100644 index 00000000..602bd5b5 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/72ivnb41hqr5vquk5s9h2x4gp.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7akx8q2pqv304u4mdxpaopyln.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7akx8q2pqv304u4mdxpaopyln.o new file mode 100644 index 00000000..9e88c77c Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7akx8q2pqv304u4mdxpaopyln.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7wejlbsn3tz5io3mgwqdz4ovv.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7wejlbsn3tz5io3mgwqdz4ovv.o new file mode 100644 index 00000000..c16fbd77 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/7wejlbsn3tz5io3mgwqdz4ovv.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/82yozy4k7feq8bbi1vseniel7.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/82yozy4k7feq8bbi1vseniel7.o new file mode 100644 index 00000000..65dfafa6 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/82yozy4k7feq8bbi1vseniel7.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8o0c7rwox44axyop0raop2r4s.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8o0c7rwox44axyop0raop2r4s.o new file mode 100644 index 00000000..992cf347 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8o0c7rwox44axyop0raop2r4s.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8uu1j8tbu7j6ul9o1smyr2upp.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8uu1j8tbu7j6ul9o1smyr2upp.o new file mode 100644 index 00000000..adf5c0b6 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8uu1j8tbu7j6ul9o1smyr2upp.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8wyw3p7t7bdrbyidven5kh4gq.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8wyw3p7t7bdrbyidven5kh4gq.o new file mode 100644 index 00000000..b46e4c72 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/8wyw3p7t7bdrbyidven5kh4gq.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9im6cch2fucy3nppr4sfu093g.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9im6cch2fucy3nppr4sfu093g.o new file mode 100644 index 00000000..3320d1d4 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9im6cch2fucy3nppr4sfu093g.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9nwdk986c1d0zw8fzc2bfnye0.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9nwdk986c1d0zw8fzc2bfnye0.o new file mode 100644 index 00000000..097a30cb Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9nwdk986c1d0zw8fzc2bfnye0.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9o3ow6h6jvu4tkkzh5j9e61gj.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9o3ow6h6jvu4tkkzh5j9e61gj.o new file mode 100644 index 00000000..eeb8bbb3 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/9o3ow6h6jvu4tkkzh5j9e61gj.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/a72er3wilmofw3v5fnsa7kp65.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/a72er3wilmofw3v5fnsa7kp65.o new file mode 100644 index 00000000..c8c9ae52 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/a72er3wilmofw3v5fnsa7kp65.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aenvo7kqvtl9qaeaji8nij390.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aenvo7kqvtl9qaeaji8nij390.o new file mode 100644 index 00000000..ca03a2f8 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aenvo7kqvtl9qaeaji8nij390.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/agp74hvmm9anemzdgu5c4eie8.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/agp74hvmm9anemzdgu5c4eie8.o new file mode 100644 index 00000000..18d779be Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/agp74hvmm9anemzdgu5c4eie8.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aj47ukddv95bjbzrn5bvyz1h9.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aj47ukddv95bjbzrn5bvyz1h9.o new file mode 100644 index 00000000..5348ce8e Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/aj47ukddv95bjbzrn5bvyz1h9.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/c2ujcnpuynqloq2bbjb37lr2p.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/c2ujcnpuynqloq2bbjb37lr2p.o new file mode 100644 index 00000000..0fb8240d Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/c2ujcnpuynqloq2bbjb37lr2p.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ccvj0ia30jepvnekodna0ru87.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ccvj0ia30jepvnekodna0ru87.o new file mode 100644 index 00000000..31c4421f Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ccvj0ia30jepvnekodna0ru87.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ci2li1eqs9b4v5852pus33d8h.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ci2li1eqs9b4v5852pus33d8h.o new file mode 100644 index 00000000..04f5a8b8 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/ci2li1eqs9b4v5852pus33d8h.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/cmr9rt9pnm6fruxiyp44odv32.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/cmr9rt9pnm6fruxiyp44odv32.o new file mode 100644 index 00000000..d42addd4 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/cmr9rt9pnm6fruxiyp44odv32.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d28gnpgg7bbhmgzfk1osjhe0s.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d28gnpgg7bbhmgzfk1osjhe0s.o new file mode 100644 index 00000000..cd179e84 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d28gnpgg7bbhmgzfk1osjhe0s.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d58h402xos9hk3rx9gcqmyb1p.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d58h402xos9hk3rx9gcqmyb1p.o new file mode 100644 index 00000000..3cf7e6dc Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/d58h402xos9hk3rx9gcqmyb1p.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dcq0fbcz3ncygblpjt0unw25h.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dcq0fbcz3ncygblpjt0unw25h.o new file mode 100644 index 00000000..bd207d89 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dcq0fbcz3ncygblpjt0unw25h.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dep-graph.bin b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dep-graph.bin new file mode 100644 index 00000000..5e3eb7f8 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dep-graph.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dq6tl2tw5dy0s7ihejcdegb20.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dq6tl2tw5dy0s7ihejcdegb20.o new file mode 100644 index 00000000..74605f2c Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/dq6tl2tw5dy0s7ihejcdegb20.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/euksz75gktc7xl3ewasiq9j9f.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/euksz75gktc7xl3ewasiq9j9f.o new file mode 100644 index 00000000..e36bdb14 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/euksz75gktc7xl3ewasiq9j9f.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/extlmbh0w6fy52oq0oajtjmxs.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/extlmbh0w6fy52oq0oajtjmxs.o new file mode 100644 index 00000000..10709509 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/extlmbh0w6fy52oq0oajtjmxs.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/f56o08f5e10tpbpgl6c5wa84z.o b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/f56o08f5e10tpbpgl6c5wa84z.o new file mode 100644 index 00000000..8f476e47 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/f56o08f5e10tpbpgl6c5wa84z.o differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/query-cache.bin b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/query-cache.bin new file mode 100644 index 00000000..91fbca23 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/query-cache.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/work-products.bin b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/work-products.bin new file mode 100644 index 00000000..94233e30 Binary files /dev/null and b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v-dstal0gg3jqagk1ec937zhhu4/work-products.bin differ diff --git a/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v.lock b/rust/target/debug/incremental/core_protocol-3q2cdycf0f7lx/s-hhl153wn9x-0y8ia8v.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/dep-graph.bin b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/dep-graph.bin new file mode 100644 index 00000000..d4b8153e Binary files /dev/null and b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/dep-graph.bin differ diff --git a/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/query-cache.bin b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/query-cache.bin new file mode 100644 index 00000000..ef75c835 Binary files /dev/null and b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/query-cache.bin differ diff --git a/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/work-products.bin b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/work-products.bin new file mode 100644 index 00000000..682eda3a Binary files /dev/null and b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3-ben1k4rb8ypvn6bitb5259f94/work-products.bin differ diff --git a/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3.lock b/rust/target/debug/incremental/event_log-0anld0qxyinf4/s-hhki6hfrns-12mb6z3.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/4wb7w1lb93fxzltgdb710gj9g.o b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/4wb7w1lb93fxzltgdb710gj9g.o new file mode 100644 index 00000000..a8d587ae Binary files /dev/null and b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/4wb7w1lb93fxzltgdb710gj9g.o differ diff --git a/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/dep-graph.bin b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/dep-graph.bin new file mode 100644 index 00000000..f1a49cf3 Binary files /dev/null and b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/dep-graph.bin differ diff --git a/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/query-cache.bin b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/query-cache.bin new file mode 100644 index 00000000..5ebfad84 Binary files /dev/null and b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/query-cache.bin differ diff --git a/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/work-products.bin b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/work-products.bin new file mode 100644 index 00000000..e4eaf880 Binary files /dev/null and b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt-730u8jxckm2hurhk1cbl8tnfh/work-products.bin differ diff --git a/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt.lock b/rust/target/debug/incremental/event_log-0v4ig6loxtdgh/s-hhl0yawabb-0kc65bt.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/dep-graph.bin b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/dep-graph.bin new file mode 100644 index 00000000..5ea4da4c Binary files /dev/null and b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/dep-graph.bin differ diff --git a/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/query-cache.bin b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/query-cache.bin new file mode 100644 index 00000000..926948e4 Binary files /dev/null and b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/query-cache.bin differ diff --git a/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/work-products.bin b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/work-products.bin new file mode 100644 index 00000000..682eda3a Binary files /dev/null and b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8-33sgjrci90hg7k0adnn20fgpi/work-products.bin differ diff --git a/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8.lock b/rust/target/debug/incremental/index_fts-2vx0jk0nb8ny7/s-hhki6hgizw-0rnusq8.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/039j9bjwet2b6eymtajpqspae.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/039j9bjwet2b6eymtajpqspae.o new file mode 100644 index 00000000..7f16c8c8 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/039j9bjwet2b6eymtajpqspae.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/09z18wf0eyrpu2i3blwanmart.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/09z18wf0eyrpu2i3blwanmart.o new file mode 100644 index 00000000..f0be35e8 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/09z18wf0eyrpu2i3blwanmart.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/18t7x0jcyuirze7njzv4mzs8b.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/18t7x0jcyuirze7njzv4mzs8b.o new file mode 100644 index 00000000..804ac0d6 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/18t7x0jcyuirze7njzv4mzs8b.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/19qzxkk4lqr8r4db0i5h76fq3.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/19qzxkk4lqr8r4db0i5h76fq3.o new file mode 100644 index 00000000..2a5aea77 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/19qzxkk4lqr8r4db0i5h76fq3.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1a5rfuomcoppx9an8qau0pcd8.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1a5rfuomcoppx9an8qau0pcd8.o new file mode 100644 index 00000000..921d3e4e Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1a5rfuomcoppx9an8qau0pcd8.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1punlkaoos9vdtgnzpiag9p5b.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1punlkaoos9vdtgnzpiag9p5b.o new file mode 100644 index 00000000..ae2611c7 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1punlkaoos9vdtgnzpiag9p5b.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1twsrtejdqa95ns8dc447ugmk.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1twsrtejdqa95ns8dc447ugmk.o new file mode 100644 index 00000000..4f62c041 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/1twsrtejdqa95ns8dc447ugmk.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/29moyoh19l3ocdafgk3ycddo3.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/29moyoh19l3ocdafgk3ycddo3.o new file mode 100644 index 00000000..cc5c0d5d Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/29moyoh19l3ocdafgk3ycddo3.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ix173c3aak9lelyqkdtkxbpy.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ix173c3aak9lelyqkdtkxbpy.o new file mode 100644 index 00000000..b2bb0229 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ix173c3aak9lelyqkdtkxbpy.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2kzhqkavn8dsswy9ysyh5oewg.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2kzhqkavn8dsswy9ysyh5oewg.o new file mode 100644 index 00000000..ce2715d3 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2kzhqkavn8dsswy9ysyh5oewg.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ok1ddc65yrtb03f26wg644h6.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ok1ddc65yrtb03f26wg644h6.o new file mode 100644 index 00000000..858e3d83 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2ok1ddc65yrtb03f26wg644h6.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2olyvmyat3pwi9ezsjekvadbk.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2olyvmyat3pwi9ezsjekvadbk.o new file mode 100644 index 00000000..3deedb80 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/2olyvmyat3pwi9ezsjekvadbk.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3gp13b83hl3eq0mez5fw1yknx.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3gp13b83hl3eq0mez5fw1yknx.o new file mode 100644 index 00000000..83857e03 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3gp13b83hl3eq0mez5fw1yknx.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s008716ul3kduxewbnyffx3e.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s008716ul3kduxewbnyffx3e.o new file mode 100644 index 00000000..da9d8ec4 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s008716ul3kduxewbnyffx3e.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s8wvzmt0m4a3udn49ellx6hn.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s8wvzmt0m4a3udn49ellx6hn.o new file mode 100644 index 00000000..7a0525f2 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3s8wvzmt0m4a3udn49ellx6hn.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3tjo4oihrykjwnauqdlfuqglb.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3tjo4oihrykjwnauqdlfuqglb.o new file mode 100644 index 00000000..932d9f58 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/3tjo4oihrykjwnauqdlfuqglb.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4bj7cpqh1cnofa7sj1ipeiy90.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4bj7cpqh1cnofa7sj1ipeiy90.o new file mode 100644 index 00000000..4d1034a0 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4bj7cpqh1cnofa7sj1ipeiy90.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4iph6l9ely0uq9nzie174r6du.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4iph6l9ely0uq9nzie174r6du.o new file mode 100644 index 00000000..724394b8 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4iph6l9ely0uq9nzie174r6du.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pp1xixqvei00miy5zqcigbuj.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pp1xixqvei00miy5zqcigbuj.o new file mode 100644 index 00000000..7a7b9677 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pp1xixqvei00miy5zqcigbuj.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pr75hco5abfezynf0nv3la5w.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pr75hco5abfezynf0nv3la5w.o new file mode 100644 index 00000000..07c90f49 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4pr75hco5abfezynf0nv3la5w.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4qlgdkvlq20ke86q79mlt7xmh.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4qlgdkvlq20ke86q79mlt7xmh.o new file mode 100644 index 00000000..01f385c6 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/4qlgdkvlq20ke86q79mlt7xmh.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/52tfz4cpsm165ixl5dkktvnuf.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/52tfz4cpsm165ixl5dkktvnuf.o new file mode 100644 index 00000000..6d22c181 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/52tfz4cpsm165ixl5dkktvnuf.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5hzaxnqj2zanbrmzl25oylrmz.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5hzaxnqj2zanbrmzl25oylrmz.o new file mode 100644 index 00000000..f8f9e511 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5hzaxnqj2zanbrmzl25oylrmz.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5kpf7s2y33g4s8o1o18itf65x.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5kpf7s2y33g4s8o1o18itf65x.o new file mode 100644 index 00000000..458cc322 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5kpf7s2y33g4s8o1o18itf65x.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5m1blfctvyi38gs1jm9nkfw2k.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5m1blfctvyi38gs1jm9nkfw2k.o new file mode 100644 index 00000000..06495bfd Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5m1blfctvyi38gs1jm9nkfw2k.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5pgzxyh4z8vlz8xjusn0sxvpg.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5pgzxyh4z8vlz8xjusn0sxvpg.o new file mode 100644 index 00000000..0cf9bdad Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5pgzxyh4z8vlz8xjusn0sxvpg.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5tbpedyrevjzmxxv3yp1wru0n.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5tbpedyrevjzmxxv3yp1wru0n.o new file mode 100644 index 00000000..45b4550b Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5tbpedyrevjzmxxv3yp1wru0n.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5xjd1bln9i2go1vfr8pmchqg2.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5xjd1bln9i2go1vfr8pmchqg2.o new file mode 100644 index 00000000..c31a9624 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/5xjd1bln9i2go1vfr8pmchqg2.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/64blkpyf6fbpvrjoi8shlhr5r.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/64blkpyf6fbpvrjoi8shlhr5r.o new file mode 100644 index 00000000..82746f57 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/64blkpyf6fbpvrjoi8shlhr5r.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6bif9jyns5ze4iccrdxmuo9f8.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6bif9jyns5ze4iccrdxmuo9f8.o new file mode 100644 index 00000000..593d0b76 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6bif9jyns5ze4iccrdxmuo9f8.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6ktfz8h61eyhwlbyrq9vhjrfv.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6ktfz8h61eyhwlbyrq9vhjrfv.o new file mode 100644 index 00000000..0a6701b5 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/6ktfz8h61eyhwlbyrq9vhjrfv.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7fm0aq0x94wfa5o9njt72gl50.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7fm0aq0x94wfa5o9njt72gl50.o new file mode 100644 index 00000000..bd9dc644 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7fm0aq0x94wfa5o9njt72gl50.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7gidib5qjm86di9txvj4jm1m6.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7gidib5qjm86di9txvj4jm1m6.o new file mode 100644 index 00000000..f086dd02 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7gidib5qjm86di9txvj4jm1m6.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7hz9nbaz1erjw5uldpa24t64v.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7hz9nbaz1erjw5uldpa24t64v.o new file mode 100644 index 00000000..74cb870d Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7hz9nbaz1erjw5uldpa24t64v.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7pyps766k3raaitrcvplkpvg2.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7pyps766k3raaitrcvplkpvg2.o new file mode 100644 index 00000000..70f834fa Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7pyps766k3raaitrcvplkpvg2.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7s0j7hjcz3n078g2wf0f5pexn.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7s0j7hjcz3n078g2wf0f5pexn.o new file mode 100644 index 00000000..a5a0bf5c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/7s0j7hjcz3n078g2wf0f5pexn.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/8am3hphsgf5dc4hmqvntfa67t.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/8am3hphsgf5dc4hmqvntfa67t.o new file mode 100644 index 00000000..d288a3ac Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/8am3hphsgf5dc4hmqvntfa67t.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/97t0fgh74devoeh5tqag2smlo.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/97t0fgh74devoeh5tqag2smlo.o new file mode 100644 index 00000000..cbd82da3 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/97t0fgh74devoeh5tqag2smlo.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/987ebqkxc6dhhlw4b8i50tdud.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/987ebqkxc6dhhlw4b8i50tdud.o new file mode 100644 index 00000000..3bb29bc9 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/987ebqkxc6dhhlw4b8i50tdud.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/995xaow4io9qiti4glq351ild.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/995xaow4io9qiti4glq351ild.o new file mode 100644 index 00000000..a32ee2c1 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/995xaow4io9qiti4glq351ild.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9iguuvu9qpcujdydbu6dbf2dt.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9iguuvu9qpcujdydbu6dbf2dt.o new file mode 100644 index 00000000..6a8f89cf Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9iguuvu9qpcujdydbu6dbf2dt.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9knj76z9dhr1dghulv8s37zdm.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9knj76z9dhr1dghulv8s37zdm.o new file mode 100644 index 00000000..72f23492 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9knj76z9dhr1dghulv8s37zdm.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9qpfr6a0qymzd7er1rmv6idt3.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9qpfr6a0qymzd7er1rmv6idt3.o new file mode 100644 index 00000000..680f0931 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9qpfr6a0qymzd7er1rmv6idt3.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9x6qfllmyaoix2ualofwebx9k.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9x6qfllmyaoix2ualofwebx9k.o new file mode 100644 index 00000000..c789489c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/9x6qfllmyaoix2ualofwebx9k.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a6agzhbcy0uzmi6b1l2y5d7b5.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a6agzhbcy0uzmi6b1l2y5d7b5.o new file mode 100644 index 00000000..87eb8f31 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a6agzhbcy0uzmi6b1l2y5d7b5.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a8arkzzebc532vyxmwcj4vsie.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a8arkzzebc532vyxmwcj4vsie.o new file mode 100644 index 00000000..5cc42e68 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/a8arkzzebc532vyxmwcj4vsie.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ab256b7s9nvxsticqbtpvuw13.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ab256b7s9nvxsticqbtpvuw13.o new file mode 100644 index 00000000..c2fab29d Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ab256b7s9nvxsticqbtpvuw13.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ad8h67qmeqpgc4zrpzm3mgwm5.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ad8h67qmeqpgc4zrpzm3mgwm5.o new file mode 100644 index 00000000..58bfdd02 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ad8h67qmeqpgc4zrpzm3mgwm5.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/am50fpawpork44ywflixup381.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/am50fpawpork44ywflixup381.o new file mode 100644 index 00000000..3720dc32 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/am50fpawpork44ywflixup381.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/anzgznle8f828ntrm2jmo9nm1.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/anzgznle8f828ntrm2jmo9nm1.o new file mode 100644 index 00000000..e2f1cbfe Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/anzgznle8f828ntrm2jmo9nm1.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/aoir5cuzz7ekeexx77huwsfsk.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/aoir5cuzz7ekeexx77huwsfsk.o new file mode 100644 index 00000000..f4d4f18e Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/aoir5cuzz7ekeexx77huwsfsk.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b2yi7otxp00ywavsqhzw0ek41.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b2yi7otxp00ywavsqhzw0ek41.o new file mode 100644 index 00000000..187f22be Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b2yi7otxp00ywavsqhzw0ek41.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7b20y8gyaasph29qem26oud9.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7b20y8gyaasph29qem26oud9.o new file mode 100644 index 00000000..b8a19989 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7b20y8gyaasph29qem26oud9.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7uf5z0hef7k763bx1fb8ytiz.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7uf5z0hef7k763bx1fb8ytiz.o new file mode 100644 index 00000000..07dec119 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/b7uf5z0hef7k763bx1fb8ytiz.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi8dl23a9vjgxuj59jcqvts0c.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi8dl23a9vjgxuj59jcqvts0c.o new file mode 100644 index 00000000..9d89e499 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi8dl23a9vjgxuj59jcqvts0c.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi9zo6ryg22o0w44ogdww1ihr.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi9zo6ryg22o0w44ogdww1ihr.o new file mode 100644 index 00000000..5b1f89f1 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/bi9zo6ryg22o0w44ogdww1ihr.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/btiz9alr7q70vo1q39ql04hzo.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/btiz9alr7q70vo1q39ql04hzo.o new file mode 100644 index 00000000..3aa4a9d4 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/btiz9alr7q70vo1q39ql04hzo.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/crxbx0yn9r269lktyptq1varl.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/crxbx0yn9r269lktyptq1varl.o new file mode 100644 index 00000000..d3069c26 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/crxbx0yn9r269lktyptq1varl.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/cvwb5wzcnwqdblvd0s8skksi2.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/cvwb5wzcnwqdblvd0s8skksi2.o new file mode 100644 index 00000000..8ab54c05 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/cvwb5wzcnwqdblvd0s8skksi2.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/d6xiwtcawnrbv7j3a1lbu7rlp.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/d6xiwtcawnrbv7j3a1lbu7rlp.o new file mode 100644 index 00000000..64788c76 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/d6xiwtcawnrbv7j3a1lbu7rlp.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dbxx2vq3kgw3wte5uh4jozu4k.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dbxx2vq3kgw3wte5uh4jozu4k.o new file mode 100644 index 00000000..3411095b Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dbxx2vq3kgw3wte5uh4jozu4k.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dep-graph.bin b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dep-graph.bin new file mode 100644 index 00000000..a08f5755 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dep-graph.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/deurc8ur3qb1d9hi62x2u2vvj.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/deurc8ur3qb1d9hi62x2u2vvj.o new file mode 100644 index 00000000..5d660bba Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/deurc8ur3qb1d9hi62x2u2vvj.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/djj23swh60530h68mr2ep8vkl.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/djj23swh60530h68mr2ep8vkl.o new file mode 100644 index 00000000..a327741c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/djj23swh60530h68mr2ep8vkl.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dou4yjv5brytpj6bdbb9os2b6.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dou4yjv5brytpj6bdbb9os2b6.o new file mode 100644 index 00000000..16e950a5 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dou4yjv5brytpj6bdbb9os2b6.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dw9ri8rnswtzwwiwga3dznkbn.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dw9ri8rnswtzwwiwga3dznkbn.o new file mode 100644 index 00000000..8d481a78 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/dw9ri8rnswtzwwiwga3dznkbn.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ej5gdaf6m3mru7zk16q0uppsi.o b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ej5gdaf6m3mru7zk16q0uppsi.o new file mode 100644 index 00000000..dddd2451 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/ej5gdaf6m3mru7zk16q0uppsi.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/query-cache.bin b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/query-cache.bin new file mode 100644 index 00000000..4c0d00ed Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/query-cache.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/work-products.bin b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/work-products.bin new file mode 100644 index 00000000..80af8e5a Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi-9cs4zkb9gja4nhgp0d48uxc7j/work-products.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi.lock b/rust/target/debug/incremental/storage_convex_bridge-0j1pgeljtd9oc/s-hhl153yx3x-013wyvi.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/dep-graph.bin b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/dep-graph.bin new file mode 100644 index 00000000..de86298f Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/dep-graph.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/query-cache.bin b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/query-cache.bin new file mode 100644 index 00000000..8dd77fbc Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/query-cache.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/work-products.bin b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/work-products.bin new file mode 100644 index 00000000..682eda3a Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2-a3hftn2q4lwcw9q46s3a242x5/work-products.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2.lock b/rust/target/debug/incremental/storage_convex_bridge-2nhwyc2taiyfl/s-hhl4gon5ba-0dc3ld2.lock new file mode 100644 index 00000000..e69de29b diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/13w6ids9kwxyowlyi4cv0fts9.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/13w6ids9kwxyowlyi4cv0fts9.o new file mode 100644 index 00000000..092044fe Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/13w6ids9kwxyowlyi4cv0fts9.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wi9cpl761ip91vawypucpf06.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wi9cpl761ip91vawypucpf06.o new file mode 100644 index 00000000..a0294559 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wi9cpl761ip91vawypucpf06.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wwxal1thld4sm7luanhk8iih.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wwxal1thld4sm7luanhk8iih.o new file mode 100644 index 00000000..117e2dd7 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/1wwxal1thld4sm7luanhk8iih.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2d4krd4j74ep5srzjgu3uwdpn.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2d4krd4j74ep5srzjgu3uwdpn.o new file mode 100644 index 00000000..eb169d10 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2d4krd4j74ep5srzjgu3uwdpn.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2dab0btbq9l6wzm8lmgylanri.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2dab0btbq9l6wzm8lmgylanri.o new file mode 100644 index 00000000..ec537988 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2dab0btbq9l6wzm8lmgylanri.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2occogj3f8scaacw4lyc10rh0.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2occogj3f8scaacw4lyc10rh0.o new file mode 100644 index 00000000..a5225aeb Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/2occogj3f8scaacw4lyc10rh0.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/4ynu8gy2229oeipq7q1v16823.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/4ynu8gy2229oeipq7q1v16823.o new file mode 100644 index 00000000..45b4bc4b Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/4ynu8gy2229oeipq7q1v16823.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/68c5jwcfffz5w8mtw6x36qzo6.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/68c5jwcfffz5w8mtw6x36qzo6.o new file mode 100644 index 00000000..efddb78a Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/68c5jwcfffz5w8mtw6x36qzo6.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/6ubsoazkh8fi00pet05s247ia.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/6ubsoazkh8fi00pet05s247ia.o new file mode 100644 index 00000000..45e84f28 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/6ubsoazkh8fi00pet05s247ia.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/74bd3skqlvvusz6iqu54ue0eb.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/74bd3skqlvvusz6iqu54ue0eb.o new file mode 100644 index 00000000..3a4f0fbb Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/74bd3skqlvvusz6iqu54ue0eb.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/777tbtpnrhkqsp3gffbl227pd.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/777tbtpnrhkqsp3gffbl227pd.o new file mode 100644 index 00000000..55319561 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/777tbtpnrhkqsp3gffbl227pd.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7er606vm52wf6hjk0r41dpb7b.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7er606vm52wf6hjk0r41dpb7b.o new file mode 100644 index 00000000..5864e306 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7er606vm52wf6hjk0r41dpb7b.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7v99lrqhgj6r18np1jl767sfe.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7v99lrqhgj6r18np1jl767sfe.o new file mode 100644 index 00000000..f357cabb Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/7v99lrqhgj6r18np1jl767sfe.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/8jioojqfumfqfwe5fuybi65am.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/8jioojqfumfqfwe5fuybi65am.o new file mode 100644 index 00000000..f7664ed8 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/8jioojqfumfqfwe5fuybi65am.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/95j62296dpolhx29ub5fd9cgs.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/95j62296dpolhx29ub5fd9cgs.o new file mode 100644 index 00000000..7bff4575 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/95j62296dpolhx29ub5fd9cgs.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/99nyl2dazho939h7y2otxypjp.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/99nyl2dazho939h7y2otxypjp.o new file mode 100644 index 00000000..abb8f46c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/99nyl2dazho939h7y2otxypjp.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/9pn6sob3u1vz95kip7vs9v7hd.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/9pn6sob3u1vz95kip7vs9v7hd.o new file mode 100644 index 00000000..679b6ec7 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/9pn6sob3u1vz95kip7vs9v7hd.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a56snj3ckk44x511avopmoh4z.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a56snj3ckk44x511avopmoh4z.o new file mode 100644 index 00000000..c406863c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a56snj3ckk44x511avopmoh4z.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a5zrndxctfmrsg2lykrjqhizp.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a5zrndxctfmrsg2lykrjqhizp.o new file mode 100644 index 00000000..767a2d5a Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/a5zrndxctfmrsg2lykrjqhizp.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/affeyiiumor5gkolrd5pzchh7.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/affeyiiumor5gkolrd5pzchh7.o new file mode 100644 index 00000000..dd967070 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/affeyiiumor5gkolrd5pzchh7.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/aqw9z4oz8uhv0k3wydlqelnrc.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/aqw9z4oz8uhv0k3wydlqelnrc.o new file mode 100644 index 00000000..7703678c Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/aqw9z4oz8uhv0k3wydlqelnrc.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/c2te5zdmxch82eg0wmyr3e9lu.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/c2te5zdmxch82eg0wmyr3e9lu.o new file mode 100644 index 00000000..be20e533 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/c2te5zdmxch82eg0wmyr3e9lu.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/cf1561iteoxzxfqzoazm2kth7.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/cf1561iteoxzxfqzoazm2kth7.o new file mode 100644 index 00000000..b6348ace Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/cf1561iteoxzxfqzoazm2kth7.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dep-graph.bin b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dep-graph.bin new file mode 100644 index 00000000..983c92a6 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dep-graph.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dzlcfe90f8jr23dlz2xvl4omy.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dzlcfe90f8jr23dlz2xvl4omy.o new file mode 100644 index 00000000..f4489649 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/dzlcfe90f8jr23dlz2xvl4omy.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/edoai52vuhbbfbim4qqlo442j.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/edoai52vuhbbfbim4qqlo442j.o new file mode 100644 index 00000000..a3820ac2 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/edoai52vuhbbfbim4qqlo442j.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/eqgd91hpn5sfsyci6vbn568p7.o b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/eqgd91hpn5sfsyci6vbn568p7.o new file mode 100644 index 00000000..f235a4d3 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/eqgd91hpn5sfsyci6vbn568p7.o differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/query-cache.bin b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/query-cache.bin new file mode 100644 index 00000000..40ddffd1 Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/query-cache.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/work-products.bin b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/work-products.bin new file mode 100644 index 00000000..178dd88d Binary files /dev/null and b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0-c4hv4la80nrj6qaul61f0db1x/work-products.bin differ diff --git a/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0.lock b/rust/target/debug/incremental/storage_convex_bridge-2nozxgcvus2jt/s-hhl153y5f4-1mb28k0.lock new file mode 100644 index 00000000..e69de29b diff --git a/wolai-frontend/convex/_generated/api.d.ts b/wolai-frontend/convex/_generated/api.d.ts index fb94d2e5..3572c963 100644 --- a/wolai-frontend/convex/_generated/api.d.ts +++ b/wolai-frontend/convex/_generated/api.d.ts @@ -9,6 +9,7 @@ */ import type * as _utils_attachmentExtract from "../_utils/attachmentExtract.js"; +import type * as _utils_auth from "../_utils/auth.js"; import type * as _utils_documentTree from "../_utils/documentTree.js"; import type * as _utils_id from "../_utils/id.js"; import type * as _utils_ingestJobs from "../_utils/ingestJobs.js"; @@ -16,8 +17,10 @@ import type * as _utils_lightrag from "../_utils/lightrag.js"; import type * as _utils_text from "../_utils/text.js"; import type * as _utils_time from "../_utils/time.js"; import type * as agentActions from "../agentActions.js"; +import type * as audit from "../audit.js"; import type * as auth from "../auth.js"; import type * as blocks from "../blocks.js"; +import type * as bridgeLogs from "../bridgeLogs.js"; import type * as comments from "../comments.js"; import type * as crons from "../crons.js"; import type * as documentGroupShares from "../documentGroupShares.js"; @@ -49,6 +52,7 @@ import type { declare const fullApi: ApiFromModules<{ "_utils/attachmentExtract": typeof _utils_attachmentExtract; + "_utils/auth": typeof _utils_auth; "_utils/documentTree": typeof _utils_documentTree; "_utils/id": typeof _utils_id; "_utils/ingestJobs": typeof _utils_ingestJobs; @@ -56,8 +60,10 @@ declare const fullApi: ApiFromModules<{ "_utils/text": typeof _utils_text; "_utils/time": typeof _utils_time; agentActions: typeof agentActions; + audit: typeof audit; auth: typeof auth; blocks: typeof blocks; + bridgeLogs: typeof bridgeLogs; comments: typeof comments; crons: typeof crons; documentGroupShares: typeof documentGroupShares; diff --git a/wolai-frontend/convex/_utils/auth.ts b/wolai-frontend/convex/_utils/auth.ts new file mode 100644 index 00000000..b811da65 --- /dev/null +++ b/wolai-frontend/convex/_utils/auth.ts @@ -0,0 +1,25 @@ +import { getAuthUserId } from "@convex-dev/auth/server"; + +function readEnv(key: string): string | undefined { + const raw = process.env[key]; + if (!raw) return undefined; + const trimmed = raw.trim(); + return trimmed ? trimmed : undefined; +} + +function isDevAuthEnabled(): boolean { + return readEnv("MNOTE_DEV_AUTH") === "1" || readEnv("NEXT_PUBLIC_MNOTE_DEV_AUTH") === "1"; +} + +export async function requireUserId(ctx: any): Promise { + const userId = await getAuthUserId(ctx); + if (userId !== null) { + return String(userId); + } + + if (isDevAuthEnabled()) { + return readEnv("DEV_USER_ID") ?? "dev-user"; + } + + throw new Error("未登录"); +} diff --git a/wolai-frontend/convex/_utils/documentRecord.ts b/wolai-frontend/convex/_utils/documentRecord.ts new file mode 100644 index 00000000..08db3702 --- /dev/null +++ b/wolai-frontend/convex/_utils/documentRecord.ts @@ -0,0 +1,73 @@ +type DocumentRecordLike = { + _id: unknown; + id?: string | null; + user_id?: string | null; + parent_id?: string | null; + deleted_at?: string | null; + created_at?: string | null; + updated_at?: string | null; +}; + +export function compareDocumentCanonicalOrder(a: DocumentRecordLike, b: DocumentRecordLike): number { + const aDeleted = a?.deleted_at != null; + const bDeleted = b?.deleted_at != null; + if (aDeleted !== bDeleted) { + return aDeleted ? 1 : -1; + } + + const updatedA = String(a?.updated_at ?? ""); + const updatedB = String(b?.updated_at ?? ""); + if (updatedA !== updatedB) { + return updatedB.localeCompare(updatedA); + } + + const createdA = String(a?.created_at ?? ""); + const createdB = String(b?.created_at ?? ""); + if (createdA !== createdB) { + return createdB.localeCompare(createdA); + } + + return String(a?._id ?? "").localeCompare(String(b?._id ?? "")); +} + +export function pickCanonicalDocumentRecord(records: T[]): T | null { + if (records.length === 0) return null; + return [...records].sort(compareDocumentCanonicalOrder)[0] ?? null; +} + +export async function getCanonicalDocumentByBusinessId( + ctx: any, + documentId: string, +): Promise { + const records = await ctx.db + .query("documents") + .withIndex("by_document_id", (q: any) => q.eq("id", documentId)) + .collect(); + return pickCanonicalDocumentRecord(records) as T | null; +} + +export async function getCanonicalParentDocumentId( + ctx: any, + documentId: string | null | undefined, +): Promise { + const normalizedId = String(documentId ?? "").trim(); + if (!normalizedId) return null; + + const doc = await getCanonicalDocumentByBusinessId(ctx, normalizedId); + return doc?.parent_id ?? null; +} + +export async function requireCanonicalOwnedDocument( + ctx: any, + documentId: string, + userId: string, +): Promise { + const doc = await getCanonicalDocumentByBusinessId(ctx, documentId); + if (!doc) { + throw new Error("页面不存在"); + } + if (String(doc.user_id ?? "") !== String(userId)) { + throw new Error("无权限"); + } + return doc; +} diff --git a/wolai-frontend/convex/audit.ts b/wolai-frontend/convex/audit.ts new file mode 100644 index 00000000..7cedfed5 --- /dev/null +++ b/wolai-frontend/convex/audit.ts @@ -0,0 +1,63 @@ +import { query } from "./_generated/server"; +import { v } from "convex/values"; +import { requireUserId } from "./_utils/auth"; +import { nowIso } from "./_utils/time"; + +async function requireWorkspaceMember(ctx: any, workspaceId: string, userId: string) { + const member = await ctx.db + .query("workspace_members") + .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", workspaceId).eq("user_id", userId)) + .first(); + if (!member) { + throw new Error("无权限"); + } + return member; +} + +export const listByTrace = query({ + args: { workspaceId: v.string(), traceId: v.string() }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const commandLogs = await ctx.db + .query("command_logs") + .withIndex("by_workspace_trace", (q) => q.eq("workspace_id", args.workspaceId).eq("trace_id", args.traceId)) + .collect(); + const domainEvents = await ctx.db + .query("domain_events") + .withIndex("by_workspace_trace", (q) => q.eq("workspace_id", args.workspaceId).eq("trace_id", args.traceId)) + .collect(); + + return { + trace_id: args.traceId, + command_logs: commandLogs, + domain_events: domainEvents, + generated_at: nowIso(), + }; + }, +}); + +export const listByRequest = query({ + args: { workspaceId: v.string(), requestId: v.string() }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const commandLogs = await ctx.db + .query("command_logs") + .withIndex("by_workspace_request", (q) => q.eq("workspace_id", args.workspaceId).eq("request_id", args.requestId)) + .collect(); + const domainEvents = await ctx.db + .query("domain_events") + .withIndex("by_workspace_request", (q) => q.eq("workspace_id", args.workspaceId).eq("request_id", args.requestId)) + .collect(); + + return { + request_id: args.requestId, + command_logs: commandLogs, + domain_events: domainEvents, + generated_at: nowIso(), + }; + }, +}); diff --git a/wolai-frontend/convex/bridgeLogs.ts b/wolai-frontend/convex/bridgeLogs.ts new file mode 100644 index 00000000..6f4959f5 --- /dev/null +++ b/wolai-frontend/convex/bridgeLogs.ts @@ -0,0 +1,173 @@ +import { mutation, query } from "./_generated/server"; +import { v } from "convex/values"; +import { requireUserId } from "./_utils/auth"; +import { nowIso } from "./_utils/time"; + +async function requireWorkspaceMember(ctx: any, workspaceId: string, userId: string) { + const member = await ctx.db + .query("workspace_members") + .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", workspaceId).eq("user_id", userId)) + .first(); + if (!member) { + throw new Error("无权限"); + } + return member; +} + +export const recordCommandLog = mutation({ + args: { + workspaceId: v.string(), + id: v.string(), + requestId: v.string(), + traceId: v.string(), + commandId: v.string(), + commandName: v.string(), + actorId: v.string(), + actorType: v.string(), + sourceChannel: v.string(), + sourceClient: v.string(), + status: v.union(v.literal("pending"), v.literal("succeeded"), v.literal("failed"), v.literal("rolled_back")), + targetPageId: v.optional(v.union(v.string(), v.null())), + targetBlockId: v.optional(v.union(v.string(), v.null())), + payload: v.any(), + payloadSummary: v.string(), + refs: v.array(v.string()), + idempotencyKey: v.optional(v.union(v.string(), v.null())), + error: v.optional(v.union(v.string(), v.null())), + createdAt: v.string(), + finishedAt: v.optional(v.union(v.string(), v.null())), + }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const existing = await ctx.db + .query("command_logs") + .withIndex("by_command_log_id", (q) => q.eq("id", args.id)) + .first(); + if (existing) { + return { ok: true, id: existing.id, duplicated: true }; + } + + await ctx.db.insert("command_logs", { + id: args.id, + workspace_id: args.workspaceId, + request_id: args.requestId, + trace_id: args.traceId, + command_id: args.commandId, + command_name: args.commandName, + actor_id: args.actorId, + actor_type: args.actorType, + source_channel: args.sourceChannel, + source_client: args.sourceClient, + status: args.status, + target_page_id: args.targetPageId ?? null, + target_block_id: args.targetBlockId ?? null, + payload: args.payload, + payload_summary: args.payloadSummary, + refs: args.refs, + idempotency_key: args.idempotencyKey ?? null, + error: args.error ?? null, + created_at: args.createdAt, + finished_at: args.finishedAt ?? null, + }); + return { ok: true, id: args.id, duplicated: false }; + }, +}); + +export const recordDomainEvent = mutation({ + args: { + workspaceId: v.string(), + id: v.string(), + requestId: v.string(), + traceId: v.string(), + commandId: v.string(), + commandLogId: v.string(), + eventType: v.string(), + aggregateType: v.string(), + aggregateId: v.string(), + eventVersion: v.number(), + status: v.union(v.literal("pending"), v.literal("committed"), v.literal("rejected"), v.literal("failed")), + actorType: v.string(), + payload: v.any(), + createdAt: v.string(), + }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const existing = await ctx.db + .query("domain_events") + .withIndex("by_domain_event_id", (q) => q.eq("id", args.id)) + .first(); + if (existing) { + return { ok: true, id: existing.id, duplicated: true }; + } + + await ctx.db.insert("domain_events", { + id: args.id, + workspace_id: args.workspaceId, + request_id: args.requestId, + trace_id: args.traceId, + command_id: args.commandId, + command_log_id: args.commandLogId, + event_type: args.eventType, + aggregate_type: args.aggregateType, + aggregate_id: args.aggregateId, + event_version: args.eventVersion, + status: args.status, + actor_type: args.actorType, + payload: args.payload, + created_at: args.createdAt, + }); + return { ok: true, id: args.id, duplicated: false }; + }, +}); + +export const listByTrace = query({ + args: { workspaceId: v.string(), traceId: v.string() }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const commandLogs = await ctx.db + .query("command_logs") + .withIndex("by_workspace_trace", (q) => q.eq("workspace_id", args.workspaceId).eq("trace_id", args.traceId)) + .collect(); + const domainEvents = await ctx.db + .query("domain_events") + .withIndex("by_workspace_trace", (q) => q.eq("workspace_id", args.workspaceId).eq("trace_id", args.traceId)) + .collect(); + + return { + trace_id: args.traceId, + command_logs: commandLogs, + domain_events: domainEvents, + generated_at: nowIso(), + }; + }, +}); + +export const listByRequest = query({ + args: { workspaceId: v.string(), requestId: v.string() }, + handler: async (ctx, args) => { + const userId = await requireUserId(ctx); + await requireWorkspaceMember(ctx, args.workspaceId, userId); + + const commandLogs = await ctx.db + .query("command_logs") + .withIndex("by_workspace_request", (q) => q.eq("workspace_id", args.workspaceId).eq("request_id", args.requestId)) + .collect(); + const domainEvents = await ctx.db + .query("domain_events") + .withIndex("by_workspace_request", (q) => q.eq("workspace_id", args.workspaceId).eq("request_id", args.requestId)) + .collect(); + + return { + request_id: args.requestId, + command_logs: commandLogs, + domain_events: domainEvents, + generated_at: nowIso(), + }; + }, +}); diff --git a/wolai-frontend/convex/comments.ts b/wolai-frontend/convex/comments.ts index 67ce074b..6e897a5c 100644 --- a/wolai-frontend/convex/comments.ts +++ b/wolai-frontend/convex/comments.ts @@ -2,6 +2,7 @@ import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; import { nowIso } from "./_utils/time"; +import { getCanonicalDocumentByBusinessId, getCanonicalParentDocumentId } from "./_utils/documentRecord"; async function requireUserId(ctx: any): Promise { const userId = await getAuthUserId(ctx); @@ -43,11 +44,7 @@ async function resolveSharePermission(ctx: any, doc: any, userId: string): Promi return parentShare.permission as SharePermission; } - const parent = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } return null; @@ -83,11 +80,7 @@ async function resolveGroupSharePermission(ctx: any, doc: any, userId: string): return "read"; } } - const parent = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } return null; @@ -132,10 +125,7 @@ export const listThreadsByDocument = query({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", args.documentId)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.documentId); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireCanViewDocument(ctx, doc, userId); @@ -190,10 +180,7 @@ export const listMessagesByThread = query({ .first(); if (!thread) throw new Error("评论线程不存在"); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", thread.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, String(thread.document_id)); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireCanViewDocument(ctx, doc, userId); @@ -241,10 +228,7 @@ export const createThread = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", args.documentId)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.documentId); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireCanViewDocument(ctx, doc, userId); @@ -305,10 +289,7 @@ export const reply = mutation({ .first(); if (!thread) throw new Error("评论线程不存在"); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", thread.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, String(thread.document_id)); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireCanViewDocument(ctx, doc, userId); @@ -354,10 +335,7 @@ export const setResolved = mutation({ .first(); if (!thread) throw new Error("评论线程不存在"); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", thread.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, String(thread.document_id)); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); @@ -393,10 +371,7 @@ export const editMessage = mutation({ .first(); if (!thread) throw new Error("评论线程不存在"); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", message.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, String(message.document_id)); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireCanViewDocument(ctx, doc, userId); @@ -444,10 +419,7 @@ export const deleteMessage = mutation({ .first(); if (!thread) throw new Error("评论线程不存在"); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", message.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, String(message.document_id)); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); diff --git a/wolai-frontend/convex/documentGroupShares.ts b/wolai-frontend/convex/documentGroupShares.ts index 52d3ec9f..30a0cfd1 100644 --- a/wolai-frontend/convex/documentGroupShares.ts +++ b/wolai-frontend/convex/documentGroupShares.ts @@ -2,6 +2,7 @@ import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; import { nowIso } from "./_utils/time"; +import { requireCanonicalOwnedDocument } from "./_utils/documentRecord"; type Permission = "read" | "edit"; @@ -11,16 +12,6 @@ async function requireUserId(ctx: any): Promise { return String(userId); } -async function requireOwnedDocument(ctx: any, documentId: string, userId: string) { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", documentId)) - .first(); - if (!doc) throw new Error("页面不存在"); - if (doc.user_id !== userId) throw new Error("无权限"); - return doc; -} - async function requireGroup(ctx: any, groupId: string) { const group = await ctx.db .query("groups") @@ -43,7 +34,7 @@ export const listByDocument = query({ args: { documentId: v.string() }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const shares = await ctx.db .query("document_group_shares") @@ -94,7 +85,7 @@ export const upsert = mutation({ }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const group = await requireGroup(ctx, args.groupId); if (group.workspace_id !== doc.workspace_id) throw new Error("群组不属于当前工作空间"); await requireGroupMember(ctx, args.groupId, userId); @@ -178,7 +169,7 @@ export const remove = mutation({ args: { documentId: v.string(), groupId: v.string() }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const existingShare = await ctx.db .query("document_group_shares") diff --git a/wolai-frontend/convex/documentShares.ts b/wolai-frontend/convex/documentShares.ts index fac1c6c5..4bb2c105 100644 --- a/wolai-frontend/convex/documentShares.ts +++ b/wolai-frontend/convex/documentShares.ts @@ -3,6 +3,7 @@ import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; import { nowIso } from "./_utils/time"; import type { Id } from "./_generated/dataModel"; +import { requireCanonicalOwnedDocument, getCanonicalDocumentByBusinessId } from "./_utils/documentRecord"; const permission = v.union(v.literal("read"), v.literal("edit")); @@ -20,16 +21,6 @@ function normalizeUsername(raw: string): string { return username; } -async function requireOwnedDocument(ctx: any, documentId: string, userId: string) { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", documentId)) - .first(); - if (!doc) throw new Error("页面不存在"); - if (doc.user_id !== userId) throw new Error("无权限"); - return doc; -} - async function resolveUserIdByUsername(ctx: any, rawUsername: string): Promise<{ userId: string; username: string }> { const username = normalizeUsername(rawUsername); @@ -47,7 +38,7 @@ export const listByDocument = query({ args: { documentId: v.string() }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const shares = await ctx.db .query("document_shares") @@ -84,7 +75,7 @@ export const upsert = mutation({ }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const { userId: sharedWithUserId } = await resolveUserIdByUsername(ctx, args.username); if (sharedWithUserId === userId) throw new Error("不能共享给自己"); @@ -149,7 +140,7 @@ export const remove = mutation({ args: { documentId: v.string(), sharedWithUserId: v.string() }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await requireOwnedDocument(ctx, args.documentId, userId); + const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId); const existingShare = await ctx.db .query("document_shares") @@ -294,10 +285,7 @@ export const listMyShareRoots = query({ if (documentCache.has(documentId)) { return cached ?? { exists: false, deleted: true, title: null }; } - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", documentId)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, documentId); const info = doc ? { exists: true, deleted: doc.deleted_at != null, title: (doc.title ?? null) as string | null } : { exists: false, deleted: true, title: null }; diff --git a/wolai-frontend/convex/documentStars.ts b/wolai-frontend/convex/documentStars.ts index 043311d4..8383538e 100644 --- a/wolai-frontend/convex/documentStars.ts +++ b/wolai-frontend/convex/documentStars.ts @@ -2,6 +2,7 @@ import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { getAuthUserId } from "@convex-dev/auth/server"; import { nowIso } from "./_utils/time"; +import { getCanonicalDocumentByBusinessId, getCanonicalParentDocumentId } from "./_utils/documentRecord"; type SharePermission = "read" | "edit"; @@ -34,12 +35,7 @@ async function resolveSharePermission(ctx: any, doc: any, userId: string): Promi .withIndex("by_doc_user", (q: any) => q.eq("document_id", parentId).eq("shared_with_user_id", userId)) .first(); if (parentShare && parentShare.include_descendants) return parentShare.permission as SharePermission; - - const parent = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } return null; } @@ -89,12 +85,7 @@ async function resolveGroupSharePermission(ctx: any, doc: any, userId: string): let parentId: string | null = doc.parent_id ?? null; for (let depth = 0; depth < 60 && parentId; depth += 1) { if (await checkDocId(parentId, true)) return "edit"; - - const parent = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } return best; @@ -109,10 +100,7 @@ export const isStarred = query({ if (authUserId === null) return false; const userId = String(authUserId); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", args.documentId)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.documentId); if (!doc) return false; if (doc.deleted_at != null) return false; @@ -137,10 +125,7 @@ export const toggle = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", args.documentId)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.documentId); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); diff --git a/wolai-frontend/convex/documents.ts b/wolai-frontend/convex/documents.ts index 54f9f289..7aac2204 100644 --- a/wolai-frontend/convex/documents.ts +++ b/wolai-frontend/convex/documents.ts @@ -1,21 +1,14 @@ import { internalQuery, mutation, query } from "./_generated/server"; import { v } from "convex/values"; -import { getAuthUserId } from "@convex-dev/auth/server"; +import { requireUserId } from "./_utils/auth"; import { nowIso } from "./_utils/time"; import { collectSubtree } from "./_utils/documentTree"; import { enqueueIngestDocumentJob } from "./_utils/ingestJobs"; import { extractTextFromDocumentContent } from "./_utils/text"; +import { getCanonicalDocumentByBusinessId, getCanonicalParentDocumentId } from "./_utils/documentRecord"; const accessScope = v.union(v.literal("private"), v.literal("shared"), v.literal("public")); -async function requireUserId(ctx: any): Promise { - const userId = await getAuthUserId(ctx); - if (userId === null) { - throw new Error("未登录"); - } - return userId; -} - type SharePermission = "read" | "edit"; type SharePolicy = { permission: SharePermission; disableDownload: boolean; disableCopy: boolean }; @@ -58,11 +51,7 @@ async function resolveSharePolicy(ctx: any, doc: any, userId: string): Promise q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } return null; @@ -127,11 +116,7 @@ async function resolveGroupSharePolicy(ctx: any, doc: any, userId: string): Prom for (let depth = 0; depth < 60 && parentId; depth += 1) { await checkDocId(parentId, true); - const parent = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", parentId)) - .first(); - parentId = parent?.parent_id ?? null; + parentId = await getCanonicalParentDocumentId(ctx, parentId); } if (!best) return null; @@ -331,10 +316,7 @@ export const getMeta = query({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) return null; if (doc.deleted_at != null) return null; try { @@ -394,10 +376,7 @@ export const getMeta = query({ export const getPermissionForUser = query({ args: { userId: v.string(), id: v.string() }, handler: async (ctx, args) => { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) return null; if (doc.deleted_at != null) return null; @@ -425,10 +404,7 @@ export const getPermissionForUser = query({ export const getMetaForIngest = internalQuery({ args: { userId: v.string(), id: v.string() }, handler: async (ctx, args) => { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) return null; if (doc.user_id !== args.userId) return null; return { @@ -459,10 +435,7 @@ export const getContent = query({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) return null; if (doc.deleted_at != null) return null; try { @@ -486,10 +459,7 @@ export const getContent = query({ export const getContentForIngest = internalQuery({ args: { userId: v.string(), id: v.string() }, handler: async (ctx, args) => { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) return null; if (doc.user_id !== args.userId) return null; return { content: doc.content ?? null }; @@ -797,6 +767,10 @@ export const create = mutation({ }, handler: async (ctx, args) => { const userId = await requireUserId(ctx); + const existing = await getCanonicalDocumentByBusinessId(ctx, args.id); + if (existing && existing.deleted_at == null) { + throw new Error("页面已存在"); + } const siblings = await ctx.db .query("documents") @@ -867,10 +841,7 @@ export const updateContent = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireWorkspaceMember(ctx, doc.workspace_id, userId); @@ -896,10 +867,7 @@ export const updateTitle = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireWorkspaceMember(ctx, doc.workspace_id, userId); @@ -920,10 +888,7 @@ export const setTemplate = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireWorkspaceMember(ctx, doc.workspace_id, userId); @@ -950,10 +915,7 @@ export const move = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.user_id !== userId) throw new Error("无权限"); @@ -1040,10 +1002,7 @@ export const softDelete = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.user_id !== userId) throw new Error("无权限"); const ts = nowIso(); @@ -1075,10 +1034,7 @@ export const restore = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.user_id !== userId) throw new Error("无权限"); const ts = nowIso(); @@ -1115,10 +1071,7 @@ export const purge = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.user_id !== userId) throw new Error("无权限"); @@ -1194,10 +1147,7 @@ export const updateOptions = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireWorkspaceMember(ctx, doc.workspace_id, userId); @@ -1249,10 +1199,7 @@ export const updateStats = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.id); if (!doc) throw new Error("页面不存在"); if (doc.deleted_at != null) throw new Error("页面不存在"); await requireWorkspaceMember(ctx, doc.workspace_id, userId); @@ -1284,13 +1231,15 @@ export const duplicate = mutation({ handler: async (ctx, args) => { const userId = await requireUserId(ctx); - const source = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.sourceId)) - .first(); + const source = await getCanonicalDocumentByBusinessId(ctx, args.sourceId); if (!source) throw new Error("页面不存在或无权限访问"); if (source.user_id !== userId) throw new Error("页面不存在或无权限访问"); + const existingTarget = await getCanonicalDocumentByBusinessId(ctx, args.newId); + if (existingTarget && existingTarget.deleted_at == null) { + throw new Error("目标页面已存在"); + } + const siblings = await ctx.db .query("documents") .withIndex("by_workspace_parent", (q) => diff --git a/wolai-frontend/convex/mediaAssets.ts b/wolai-frontend/convex/mediaAssets.ts index a79fbffc..0dd0cd1e 100644 --- a/wolai-frontend/convex/mediaAssets.ts +++ b/wolai-frontend/convex/mediaAssets.ts @@ -4,6 +4,7 @@ import { nowIso } from "./_utils/time"; import { enqueueExtractMediaAssetTextJob, enqueueIngestMediaAssetJob } from "./_utils/ingestJobs"; import type { MutationCtx, QueryCtx } from "./_generated/server"; import { internal } from "./_generated/api"; +import { getCanonicalDocumentByBusinessId } from "./_utils/documentRecord"; async function assertWorkspaceMember(ctx: QueryCtx | MutationCtx, userId: string, workspaceId: string) { const membership = await ctx.db @@ -302,10 +303,7 @@ export const createWithStorage = mutation({ handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.asset.workspace_id); - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("id", args.asset.document_id)) - .first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, args.asset.document_id); if (!doc || doc.workspace_id !== args.asset.workspace_id) { throw new Error("目标页面不存在或不属于该工作空间"); diff --git a/wolai-frontend/convex/mindmaps.ts b/wolai-frontend/convex/mindmaps.ts index 9be4be93..10f19752 100644 --- a/wolai-frontend/convex/mindmaps.ts +++ b/wolai-frontend/convex/mindmaps.ts @@ -1,9 +1,10 @@ import { internalMutation, internalQuery, mutation, query } from "./_generated/server"; import { v } from "convex/values"; -import { getAuthUserId } from "@convex-dev/auth/server"; +import { requireUserId } from "./_utils/auth"; import { nowIso } from "./_utils/time"; import { enqueueIngestMindmapJob } from "./_utils/ingestJobs"; import { internal } from "./_generated/api"; +import { requireCanonicalOwnedDocument } from "./_utils/documentRecord"; const defaultMindmapData = { data: { text: "中心主题" }, @@ -19,14 +20,6 @@ function resolveGraceSeconds(): number { return Math.floor(parsed); } -async function requireUserId(ctx: any): Promise { - const userId = await getAuthUserId(ctx); - if (userId === null) { - throw new Error("未登录"); - } - return userId; -} - function normalizeMindmapId(docId: string, mindmapId: string): string { const raw = String(mindmapId ?? "").trim(); if (raw) return raw; @@ -35,17 +28,7 @@ function normalizeMindmapId(docId: string, mindmapId: string): string { } async function requireOwnedDocument(ctx: any, userId: string, docId: string) { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q: any) => q.eq("id", docId)) - .first(); - if (!doc) { - throw new Error("页面不存在"); - } - if (doc.user_id !== userId) { - throw new Error("无权限"); - } - return doc; + return await requireCanonicalOwnedDocument(ctx, docId, userId); } export const get = query({ diff --git a/wolai-frontend/convex/references.ts b/wolai-frontend/convex/references.ts index d6094983..8592b26f 100644 --- a/wolai-frontend/convex/references.ts +++ b/wolai-frontend/convex/references.ts @@ -2,6 +2,7 @@ import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { nowIso } from "./_utils/time"; import type { MutationCtx, QueryCtx } from "./_generated/server"; +import { getCanonicalDocumentByBusinessId } from "./_utils/documentRecord"; async function assertWorkspaceMember(ctx: QueryCtx | MutationCtx, userId: string, workspaceId: string) { const membership = await ctx.db @@ -120,7 +121,7 @@ export const listBacklinks = query({ const sourceTitleById = new Map(); for (const sid of sourceIds) { - const doc = await ctx.db.query("documents").withIndex("by_document_id", (q) => q.eq("id", sid)).first(); + const doc = await getCanonicalDocumentByBusinessId(ctx, sid); sourceTitleById.set(sid, doc ? (doc.title ?? null) : null); } diff --git a/wolai-frontend/convex/schema.ts b/wolai-frontend/convex/schema.ts index 9834436d..7ba5793f 100644 --- a/wolai-frontend/convex/schema.ts +++ b/wolai-frontend/convex/schema.ts @@ -446,4 +446,52 @@ export default defineSchema({ .index("by_workspace_session", ["workspace_id", "session_id"]) .index("by_session", ["session_id"]) .index("by_workspace_status", ["workspace_id", "status"]), + + command_logs: defineTable({ + id: v.string(), + workspace_id: v.string(), + request_id: v.string(), + trace_id: v.string(), + command_id: v.string(), + command_name: v.string(), + actor_id: v.string(), + actor_type: v.string(), + source_channel: v.string(), + source_client: v.string(), + status: v.union(v.literal("pending"), v.literal("succeeded"), v.literal("failed"), v.literal("rolled_back")), + target_page_id: v.optional(v.union(v.string(), v.null())), + target_block_id: v.optional(v.union(v.string(), v.null())), + payload: v.any(), + payload_summary: v.string(), + refs: v.array(v.string()), + idempotency_key: v.optional(v.union(v.string(), v.null())), + error: v.optional(v.union(v.string(), v.null())), + created_at: v.string(), + finished_at: v.optional(v.union(v.string(), v.null())), + }) + .index("by_command_log_id", ["id"]) + .index("by_workspace_request", ["workspace_id", "request_id"]) + .index("by_workspace_trace", ["workspace_id", "trace_id"]) + .index("by_workspace_command", ["workspace_id", "command_id"]), + + domain_events: defineTable({ + id: v.string(), + workspace_id: v.string(), + request_id: v.string(), + trace_id: v.string(), + command_id: v.string(), + command_log_id: v.string(), + event_type: v.string(), + aggregate_type: v.string(), + aggregate_id: v.string(), + event_version: v.number(), + status: v.union(v.literal("pending"), v.literal("committed"), v.literal("rejected"), v.literal("failed")), + actor_type: v.string(), + payload: v.any(), + created_at: v.string(), + }) + .index("by_domain_event_id", ["id"]) + .index("by_workspace_request", ["workspace_id", "request_id"]) + .index("by_workspace_trace", ["workspace_id", "trace_id"]) + .index("by_workspace_command", ["workspace_id", "command_id"]), }); diff --git a/wolai-frontend/convex/workspaces.ts b/wolai-frontend/convex/workspaces.ts index 1a33f729..9cb758c3 100644 --- a/wolai-frontend/convex/workspaces.ts +++ b/wolai-frontend/convex/workspaces.ts @@ -1,6 +1,6 @@ import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; -import { getAuthUserId } from "@convex-dev/auth/server"; +import { requireUserId } from "./_utils/auth"; import { nowIso } from "./_utils/time"; import type { MutationCtx, QueryCtx } from "./_generated/server"; @@ -13,14 +13,6 @@ type WorkspaceSummary = { isDefault: boolean; }; -async function requireUserId(ctx: any): Promise { - const userId = await getAuthUserId(ctx); - if (userId === null) { - throw new Error("未登录"); - } - return userId; -} - async function findWorkspaceById(ctx: QueryCtx | MutationCtx, workspaceId: string) { return await ctx.db .query("workspaces") diff --git a/wolai-frontend/src/app/(app)/documents/[id]/page.tsx b/wolai-frontend/src/app/(app)/documents/[id]/page.tsx index 9b8f29aa..3eb72f81 100644 --- a/wolai-frontend/src/app/(app)/documents/[id]/page.tsx +++ b/wolai-frontend/src/app/(app)/documents/[id]/page.tsx @@ -1,16 +1,44 @@ import { notFound, redirect } from "next/navigation"; import { DocumentShell } from "@/components/editor/document-shell"; -import type { PageOptionsState, DocumentStats } from "@/types/page-options"; +import type { PageFont, PageLayoutDensity, PageOptionsState, DocumentStats } from "@/types/page-options"; import { isConvexEnabled } from "@/lib/convex/enabled"; -import { getAuthedConvexClient } from "@/lib/convex/route"; -import { api } from "@/lib/convex/api"; +import { fetchDocumentMetaViaBridge } from "@/lib/documents/bridge-server"; interface DocumentPageProps { params: Promise<{ id: string }>; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - searchParams?: Promise>; + searchParams?: Promise>; } +type DocumentMetaPayload = { + id: string; + workspace_id: string; + title: string | null; + updated_at: string | null; + can_edit?: boolean | null; + disable_download?: boolean | null; + disable_copy?: boolean | null; + wide_layout?: boolean | null; + use_small_text?: boolean | null; + show_heading_numbers?: boolean | null; + show_toc?: boolean | null; + show_structure?: boolean | null; + protect_editing?: boolean | null; + show_word_count?: boolean | null; + collapse_backlinks?: boolean | null; + page_font?: PageFont | null; + layout_density?: PageLayoutDensity | null; + hide_child_pages?: boolean | null; + show_block_ref_count?: boolean | null; + embed_default_block_id?: string | null; + word_count?: number | null; + character_count?: number | null; + block_count?: number | null; + todo_total?: number | null; + todo_total_count?: number | null; + todo_done?: number | null; + todo_done_count?: number | null; +}; + export default async function DocumentPage({ params, searchParams }: DocumentPageProps) { const { id } = await params; const resolvedSearch = (await searchParams) ?? {}; @@ -18,14 +46,19 @@ export default async function DocumentPage({ params, searchParams }: DocumentPag const openTableId = typeof openTableIdRaw === "string" ? openTableIdRaw : null; if (isConvexEnabled()) { - const { client } = await getAuthedConvexClient(); - const doc = await client.query(api.documents.getMeta, { id }); + const workspaceIdRaw = resolvedSearch?.workspaceId; + const workspaceId = typeof workspaceIdRaw === "string" ? workspaceIdRaw : null; + const result = await fetchDocumentMetaViaBridge({ + documentId: id, + workspaceId, + }); + const doc = result?.doc; if (!doc) { notFound(); } - const readOnly = (doc as any).can_edit === false; - const disableDownload = Boolean((doc as any).disable_download); - const disableCopy = Boolean((doc as any).disable_copy); + const readOnly = doc.can_edit === false; + const disableDownload = Boolean(doc.disable_download); + const disableCopy = Boolean(doc.disable_copy); const initialOptions: PageOptionsState = { wideLayout: doc.wide_layout ?? false, @@ -35,20 +68,20 @@ export default async function DocumentPage({ params, searchParams }: DocumentPag showStructure: doc.show_structure ?? false, protectEditing: doc.protect_editing ?? false, showWordCount: doc.show_word_count ?? true, - collapseBacklinks: (doc as any).collapse_backlinks ?? false, - pageFont: (doc as any).page_font ?? "default", - layoutDensity: (doc as any).layout_density ?? "normal", - hideChildPages: (doc as any).hide_child_pages ?? false, - showBlockRefCount: (doc as any).show_block_ref_count ?? false, - embedDefaultBlockId: (doc as any).embed_default_block_id ?? null, + collapseBacklinks: doc.collapse_backlinks ?? false, + pageFont: doc.page_font ?? "default", + layoutDensity: doc.layout_density ?? "normal", + hideChildPages: doc.hide_child_pages ?? false, + showBlockRefCount: doc.show_block_ref_count ?? false, + embedDefaultBlockId: doc.embed_default_block_id ?? null, }; const initialStats: DocumentStats = { wordCount: doc.word_count ?? 0, characterCount: doc.character_count ?? 0, blockCount: doc.block_count ?? 0, - todoTotal: (doc as any).todo_total ?? (doc as any).todo_total_count ?? 0, - todoDone: (doc as any).todo_done ?? (doc as any).todo_done_count ?? 0, + todoTotal: doc.todo_total ?? doc.todo_total_count ?? 0, + todoDone: doc.todo_done ?? doc.todo_done_count ?? 0, }; return ( diff --git a/wolai-frontend/src/app/(app)/layout.tsx b/wolai-frontend/src/app/(app)/layout.tsx index 753f06fa..41309f6e 100644 --- a/wolai-frontend/src/app/(app)/layout.tsx +++ b/wolai-frontend/src/app/(app)/layout.tsx @@ -4,236 +4,23 @@ import { Sidebar } from "@/components/sidebar/sidebar"; import { GlobalAiAgentHost } from "@/components/ai-agent/GlobalAiAgentHost"; import { Breadcrumb } from "@/components/breadcrumb"; import { MobileSidebarTrigger } from "@/components/mobile-sidebar-trigger"; -import type { DocumentRecord } from "@/lib/documents"; -import type { SidebarInitialData } from "@/components/sidebar/types"; import { SearchPalette } from "@/components/search/search-palette"; import { isConvexEnabled } from "@/lib/convex/enabled"; import { getAuthedConvexClient } from "@/lib/convex/route"; -import { api } from "@/lib/convex/api"; -import type { MediaAsset } from "@/types/media"; - -function extractMindmapImageAssetIdsFromData(input: unknown): string[] { - const root = (() => { - if (!input || typeof input !== "object") return input; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const record = input as any; - return record && typeof record === "object" && "root" in record ? record.root : input; - })(); - - const ids: string[] = []; - const seen = new Set(); - - const push = (value: unknown) => { - if (typeof value !== "string") return; - if (!value.startsWith("asset:")) return; - const id = value.slice("asset:".length).trim(); - if (!id) return; - if (seen.has(id)) return; - seen.add(id); - ids.push(id); - }; - - const get = (obj: unknown, key: string): unknown => { - if (!obj || typeof obj !== "object") return undefined; - return (obj as Record)[key]; - }; - - const walk = (node: unknown) => { - if (!node || typeof node !== "object") return; - const data = get(node, "data"); - const image = get(node, "image"); - push(get(data, "image")); - push(image); - push(get(image, "url")); - push(get(get(data, "image"), "url")); - const children = get(node, "children"); - if (Array.isArray(children)) children.forEach(walk); - }; - - walk(root); - return ids; -} - -function makeId(): string { - return typeof crypto.randomUUID === "function" - ? crypto.randomUUID() - : `${Date.now()}_${Math.random().toString(16).slice(2)}`; -} +import { loadSidebarDataFromConvex } from "@/lib/server/sidebar-data"; export default async function AppLayout({ children }: { children: ReactNode }) { if (isConvexEnabled()) { const { auth, client } = await getAuthedConvexClient(); - - const ensured = await client.mutation(api.workspaces.ensureDefaultWorkspace, { + const { + documents, + sidebarInitialData, + } = await loadSidebarDataFromConvex({ + client, + userId: auth.userId, fallbackName: auth.name ?? auth.email ?? "我的空间", - workspaceIdIfCreate: makeId(), }); - const workspaces = ensured.workspaces; - const activeWorkspaceId = ensured.activeWorkspaceId; - - let documents: DocumentRecord[] = []; - let sidebarInitialData: SidebarInitialData | null = null; - - if (activeWorkspaceId) { - const [docRows, trashedDocs] = await Promise.all([ - client.query(api.documents.listByWorkspace, { - workspaceId: activeWorkspaceId, - }), - client.query(api.documents.listTrashedByWorkspace, { - workspaceId: activeWorkspaceId, - }), - ]); - - documents = docRows as unknown as DocumentRecord[]; - - const mindmapRows = await client.query(api.mindmaps.listByWorkspace, { - workspaceId: activeWorkspaceId, - includeDeleted: true, - }); - - const activeMindmaps = (mindmapRows ?? []).filter((r) => !r.deleted_at); - const trashedMindmaps = (mindmapRows ?? []).filter((r) => !!r.deleted_at); - - const mindmapDocs = Array.from(new Set(activeMindmaps.map((r) => r.document_id))); - - const mindmapAssetChildren: Record = {}; - activeMindmaps.forEach((r) => { - const ids = extractMindmapImageAssetIdsFromData(r.data); - if (ids.length > 0) mindmapAssetChildren[r.mindmap_id] = ids; - }); - - const mindmapAssets: MediaAsset[] = activeMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? activeWorkspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const trashedMindmapAssets: MediaAsset[] = trashedMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? activeWorkspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: r.deleted_at ?? null, - deleted_by: r.deleted_by ?? null, - purged_at: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const tables = await client.query(api.tables.listByWorkspaceForSearch, { - userId: auth.userId, - workspaceId: activeWorkspaceId, - includeArchived: true, - limit: 3000, - }); - - const tableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => !row.is_archived) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? activeWorkspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - const trashedTableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => Boolean(row.is_archived)) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? activeWorkspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: row.deleted_at ?? row.updated_at ?? null, - deleted_by: row.deleted_by ?? null, - purged_at: row.purged_at ?? null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - sidebarInitialData = { - activeWorkspaceId, - workspaces, - documents: docRows as unknown as DocumentRecord[], - trashedDocuments: trashedDocs as unknown as SidebarInitialData["trashedDocuments"], - trashedMediaAssets: [], - trashedMindmapAssets, - trashedTableAssets, - mediaAssets: [], - mindmapDocs, - mindmapAssets, - mindmapAssetChildren, - tableAssets, - }; - } - return (
{sidebarInitialData && } diff --git a/wolai-frontend/src/app/api/blocks/patch/route.ts b/wolai-frontend/src/app/api/blocks/patch/route.ts index 4c510a07..fc282f4e 100644 --- a/wolai-frontend/src/app/api/blocks/patch/route.ts +++ b/wolai-frontend/src/app/api/blocks/patch/route.ts @@ -3,32 +3,104 @@ import { isConvexEnabled } from "@/lib/convex/enabled"; import { getAuthedConvexClient } from "@/lib/convex/route"; import { api } from "@/lib/convex/api"; import { getBlocksFromDocumentContent, replaceBlockInTree, withBlocksWrittenBack } from "@/lib/blocks"; +import { + assertBlockId, + assertDocumentId, + assertNextBlock, + buildDocumentBridgeContext, + buildDocumentCommandEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log"; type PatchPayload = { sourceDocumentId: string; + workspaceId?: string | null; blockId: string; nextBlock: unknown; }; export async function POST(request: Request) { - const { sourceDocumentId, blockId, nextBlock }: PatchPayload = await request.json(); - - if (!sourceDocumentId || !blockId || !nextBlock) { - return NextResponse.json({ error: "缺少参数" }, { status: 400 }); - } - if (isConvexEnabled()) { - const { client } = await getAuthedConvexClient(); - const doc = await client.query(api.documents.getContent, { id: sourceDocumentId }); - if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 }); + try { + const { sourceDocumentId, workspaceId, blockId, nextBlock }: PatchPayload = await request.json(); + const normalizedDocumentId = assertDocumentId(sourceDocumentId); + const normalizedWorkspaceId = workspaceId?.trim() || null; + const normalizedBlockId = assertBlockId(blockId); + assertNextBlock(nextBlock); - const blocks = getBlocksFromDocumentContent(doc.content); - const replaced = replaceBlockInTree(blocks, blockId, nextBlock as any); - if (!replaced.ok) return NextResponse.json({ error: "块不存在或无权限" }, { status: 404 }); + const bridgeContext = await buildDocumentBridgeContext({ + request, + workspaceId: normalizedWorkspaceId, + }); + const envelope = buildDocumentCommandEnvelope({ + name: "blocks.patch", + payload: { + documentId: normalizedDocumentId, + workspaceId: normalizedWorkspaceId, + blockId: normalizedBlockId, + nextBlock, + }, + context: bridgeContext, + target: { + workspaceId: normalizedWorkspaceId, + pageId: normalizedDocumentId, + blockId: normalizedBlockId, + }, + }); - const payload = withBlocksWrittenBack(doc.content, replaced.nextBlocks); - await client.mutation(api.documents.updateContent, { id: sourceDocumentId, content: payload }); - return NextResponse.json({ ok: true }); + const { client } = await getAuthedConvexClient(); + const doc = await client.query(api.documents.getContent, { id: normalizedDocumentId }); + if (!doc) { + return NextResponse.json( + { + error: "页面不存在或无权限", + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + commandId: envelope.commandId, + commandName: envelope.name, + }, + }, + { status: 404 }, + ); + } + + const blocks = getBlocksFromDocumentContent(doc.content); + const replaced = replaceBlockInTree(blocks, normalizedBlockId, nextBlock as any); + if (!replaced.ok) { + return NextResponse.json( + { + error: "块不存在或无权限", + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + commandId: envelope.commandId, + commandName: envelope.name, + }, + }, + { status: 404 }, + ); + } + + const payload = withBlocksWrittenBack(doc.content, replaced.nextBlocks); + await client.mutation(api.documents.updateContent, { id: normalizedDocumentId, content: payload }); + await recordBridgeCommandArtifacts({ + context: bridgeContext, + envelope, + }); + return NextResponse.json({ + ok: true, + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + commandId: envelope.commandId, + commandName: envelope.name, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); + } } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/bridge/request/route.ts b/wolai-frontend/src/app/api/bridge/request/route.ts new file mode 100644 index 00000000..040260da --- /dev/null +++ b/wolai-frontend/src/app/api/bridge/request/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from "next/server"; +import { getAuthedConvexClient } from "@/lib/convex/route"; +import { api } from "@/lib/convex/api"; +import { documentBridgeErrorResponse } from "@/lib/documents/bridge"; + +const bridgeLogsApi = api as any; + +export async function GET(request: Request) { + try { + const url = new URL(request.url); + const workspaceId = url.searchParams.get("workspaceId")?.trim() ?? ""; + const requestId = url.searchParams.get("requestId")?.trim() ?? ""; + if (!workspaceId || !requestId) { + return NextResponse.json({ error: "缺少 workspaceId 或 requestId" }, { status: 400 }); + } + + const { client } = await getAuthedConvexClient(); + const result = await client.query(bridgeLogsApi.bridgeLogs.listByRequest, { + workspaceId, + requestId, + }); + + return NextResponse.json(result); + } catch (error) { + return documentBridgeErrorResponse(error); + } +} diff --git a/wolai-frontend/src/app/api/bridge/trace/route.ts b/wolai-frontend/src/app/api/bridge/trace/route.ts new file mode 100644 index 00000000..0a7e3364 --- /dev/null +++ b/wolai-frontend/src/app/api/bridge/trace/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from "next/server"; +import { getAuthedConvexClient } from "@/lib/convex/route"; +import { api } from "@/lib/convex/api"; +import { documentBridgeErrorResponse } from "@/lib/documents/bridge"; + +const bridgeLogsApi = api as any; + +export async function GET(request: Request) { + try { + const url = new URL(request.url); + const workspaceId = url.searchParams.get("workspaceId")?.trim() ?? ""; + const traceId = url.searchParams.get("traceId")?.trim() ?? ""; + if (!workspaceId || !traceId) { + return NextResponse.json({ error: "缺少 workspaceId 或 traceId" }, { status: 400 }); + } + + const { client } = await getAuthedConvexClient(); + const result = await client.query(bridgeLogsApi.bridgeLogs.listByTrace, { + workspaceId, + traceId, + }); + + return NextResponse.json(result); + } catch (error) { + return documentBridgeErrorResponse(error); + } +} diff --git a/wolai-frontend/src/app/api/documents/content/route.ts b/wolai-frontend/src/app/api/documents/content/route.ts index 05aead57..6d4f1aaf 100644 --- a/wolai-frontend/src/app/api/documents/content/route.ts +++ b/wolai-frontend/src/app/api/documents/content/route.ts @@ -2,28 +2,57 @@ import { NextResponse } from "next/server"; import { isConvexEnabled } from "@/lib/convex/enabled"; import { getAuthedConvexClient } from "@/lib/convex/route"; import { api } from "@/lib/convex/api"; +import { + assertDocumentId, + buildDocumentBridgeContext, + buildDocumentQueryEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; export const dynamic = "force-dynamic"; export async function GET(request: Request) { if (isConvexEnabled()) { - const url = new URL(request.url); - const documentId = url.searchParams.get("documentId") ?? ""; + try { + const url = new URL(request.url); + const documentId = assertDocumentId(url.searchParams.get("documentId")); + const workspaceId = url.searchParams.get("workspaceId")?.trim() || null; + const { client } = await getAuthedConvexClient(); + const bridgeContext = await buildDocumentBridgeContext({ request, workspaceId }); + const envelope = buildDocumentQueryEnvelope({ + name: "documents.content.get", + payload: { documentId, workspaceId }, + }); - if (!documentId) { - return NextResponse.json({ error: "缺少 documentId" }, { status: 400 }); + const result = await client.query(api.documents.getContent, { + id: documentId, + }); + + if (!result) { + return NextResponse.json( + { + error: "页面不存在", + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + queryName: envelope.name, + }, + }, + { status: 404 }, + ); + } + + return NextResponse.json({ + content: result.content ?? null, + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + queryName: envelope.name, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); } - - const { client } = await getAuthedConvexClient(); - const result = await client.query(api.documents.getContent, { - id: documentId, - }); - - if (!result) { - return NextResponse.json({ error: "页面不存在" }, { status: 404 }); - } - - return NextResponse.json({ content: result.content ?? null }); } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/documents/meta/route.ts b/wolai-frontend/src/app/api/documents/meta/route.ts new file mode 100644 index 00000000..ba843f2f --- /dev/null +++ b/wolai-frontend/src/app/api/documents/meta/route.ts @@ -0,0 +1,57 @@ +import { NextResponse } from "next/server"; +import { isConvexEnabled } from "@/lib/convex/enabled"; +import { getAuthedConvexClient } from "@/lib/convex/route"; +import { api } from "@/lib/convex/api"; +import { + assertDocumentId, + buildDocumentBridgeContext, + buildDocumentQueryEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + if (isConvexEnabled()) { + try { + const url = new URL(request.url); + const documentId = assertDocumentId(url.searchParams.get("documentId")); + const workspaceId = url.searchParams.get("workspaceId")?.trim() || null; + const { client } = await getAuthedConvexClient(); + const bridgeContext = await buildDocumentBridgeContext({ request, workspaceId }); + const envelope = buildDocumentQueryEnvelope({ + name: "documents.meta.get", + payload: { documentId, workspaceId }, + }); + + const doc = await client.query(api.documents.getMeta, { id: documentId }); + + if (!doc) { + return NextResponse.json( + { + error: "页面不存在", + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + queryName: envelope.name, + }, + }, + { status: 404 }, + ); + } + + return NextResponse.json({ + doc, + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + queryName: envelope.name, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); + } + } + + return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); +} diff --git a/wolai-frontend/src/app/api/documents/options/route.ts b/wolai-frontend/src/app/api/documents/options/route.ts index 5383a9a4..3addf0ca 100644 --- a/wolai-frontend/src/app/api/documents/options/route.ts +++ b/wolai-frontend/src/app/api/documents/options/route.ts @@ -1,42 +1,65 @@ import { NextResponse } from "next/server"; import { isConvexEnabled } from "@/lib/convex/enabled"; -import { getAuthedConvexClient } from "@/lib/convex/route"; -import { api } from "@/lib/convex/api"; import type { PageOptionsState } from "@/types/page-options"; +import { + assertDocumentId, + assertOptionsPatch, + buildDocumentBridgeContext, + buildDocumentCommandEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { + executeMetadataBridgeCommand, + type DocumentOptionsUpdatePayload, +} from "@/lib/documents/metadata-command-adapter"; type OptionsPayload = { documentId: string; + workspaceId?: string | null; options: Partial; }; export async function POST(request: Request) { if (isConvexEnabled()) { - const { documentId, options }: OptionsPayload = await request.json(); - if (!documentId || !options) { - return NextResponse.json({ error: "缺少参数" }, { status: 400 }); + try { + const { documentId, workspaceId, options }: OptionsPayload = await request.json(); + const normalizedDocumentId = assertDocumentId(documentId); + assertOptionsPatch(options); + const normalizedWorkspaceId = workspaceId?.trim() || null; + const bridgeContext = await buildDocumentBridgeContext({ + request, + workspaceId: normalizedWorkspaceId, + }); + const envelope = buildDocumentCommandEnvelope({ + name: "documents.options.update", + payload: { + documentId: normalizedDocumentId, + workspaceId: normalizedWorkspaceId, + options, + } satisfies DocumentOptionsUpdatePayload, + context: bridgeContext, + target: { + workspaceId: normalizedWorkspaceId, + pageId: normalizedDocumentId, + }, + }); + const result = await executeMetadataBridgeCommand({ + context: bridgeContext, + envelope, + }); + + return NextResponse.json({ + ok: true, + meta: { + requestId: result.requestId, + traceId: result.traceId, + commandId: result.commandId, + commandName: result.commandName, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); } - - const { client } = await getAuthedConvexClient(); - await client.mutation(api.documents.updateOptions, { - id: documentId, - options: { - wideLayout: options.wideLayout, - smallText: options.smallText, - showHeadingNumbers: options.showHeadingNumbers, - showToc: options.showToc, - showStructure: options.showStructure, - protectEditing: options.protectEditing, - showWordCount: options.showWordCount, - collapseBacklinks: options.collapseBacklinks, - pageFont: options.pageFont, - layoutDensity: options.layoutDensity, - hideChildPages: options.hideChildPages, - showBlockRefCount: options.showBlockRefCount, - embedDefaultBlockId: typeof options.embedDefaultBlockId === "string" ? options.embedDefaultBlockId : null, - }, - }); - - return NextResponse.json({ ok: true }); } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/documents/save/route.ts b/wolai-frontend/src/app/api/documents/save/route.ts index b0019621..f9afd86f 100644 --- a/wolai-frontend/src/app/api/documents/save/route.ts +++ b/wolai-frontend/src/app/api/documents/save/route.ts @@ -2,21 +2,62 @@ import { NextResponse } from "next/server"; import { isConvexEnabled } from "@/lib/convex/enabled"; import { getAuthedConvexClient } from "@/lib/convex/route"; import { api } from "@/lib/convex/api"; +import { + assertDocumentId, + buildDocumentBridgeContext, + buildDocumentCommandEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log"; interface SavePayload { documentId: string; + workspaceId?: string | null; content: unknown; } export async function POST(request: Request) { if (isConvexEnabled()) { - const { documentId, content }: SavePayload = await request.json(); - const { client } = await getAuthedConvexClient(); - await client.mutation(api.documents.updateContent, { - id: documentId, - content, - }); - return NextResponse.json({ ok: true }); + try { + const { documentId, workspaceId, content }: SavePayload = await request.json(); + const normalizedDocumentId = assertDocumentId(documentId); + const normalizedWorkspaceId = workspaceId?.trim() || null; + const bridgeContext = await buildDocumentBridgeContext({ request, workspaceId: normalizedWorkspaceId }); + const envelope = buildDocumentCommandEnvelope({ + name: "documents.save", + payload: { + documentId: normalizedDocumentId, + workspaceId: normalizedWorkspaceId, + content, + }, + context: bridgeContext, + target: { + workspaceId: normalizedWorkspaceId, + pageId: normalizedDocumentId, + }, + }); + + const { client } = await getAuthedConvexClient(); + await client.mutation(api.documents.updateContent, { + id: normalizedDocumentId, + content: envelope.payload.content, + }); + await recordBridgeCommandArtifacts({ + context: bridgeContext, + envelope, + }); + return NextResponse.json({ + ok: true, + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + commandId: envelope.commandId, + commandName: envelope.name, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); + } } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/documents/stats/route.ts b/wolai-frontend/src/app/api/documents/stats/route.ts index bf396472..d3490e7d 100644 --- a/wolai-frontend/src/app/api/documents/stats/route.ts +++ b/wolai-frontend/src/app/api/documents/stats/route.ts @@ -1,32 +1,65 @@ import { NextResponse } from "next/server"; import { isConvexEnabled } from "@/lib/convex/enabled"; -import { getAuthedConvexClient } from "@/lib/convex/route"; -import { api } from "@/lib/convex/api"; import type { DocumentStats } from "@/types/page-options"; +import { + assertDocumentId, + assertStats, + buildDocumentBridgeContext, + buildDocumentCommandEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { + executeMetadataBridgeCommand, + type DocumentStatsUpdatePayload, +} from "@/lib/documents/metadata-command-adapter"; interface StatsPayload { documentId: string; + workspaceId?: string | null; stats: DocumentStats; } export async function POST(request: Request) { if (isConvexEnabled()) { - const { documentId, stats }: StatsPayload = await request.json(); - if (!documentId || !stats) { - return NextResponse.json({ error: "缺少参数" }, { status: 400 }); + try { + const { documentId, workspaceId, stats }: StatsPayload = await request.json(); + const normalizedDocumentId = assertDocumentId(documentId); + assertStats(stats); + const normalizedWorkspaceId = workspaceId?.trim() || null; + const bridgeContext = await buildDocumentBridgeContext({ + request, + workspaceId: normalizedWorkspaceId, + }); + const envelope = buildDocumentCommandEnvelope({ + name: "documents.stats.update", + payload: { + documentId: normalizedDocumentId, + workspaceId: normalizedWorkspaceId, + stats, + } satisfies DocumentStatsUpdatePayload, + context: bridgeContext, + target: { + workspaceId: normalizedWorkspaceId, + pageId: normalizedDocumentId, + }, + }); + const result = await executeMetadataBridgeCommand({ + context: bridgeContext, + envelope, + }); + + return NextResponse.json({ + ok: true, + meta: { + requestId: result.requestId, + traceId: result.traceId, + commandId: result.commandId, + commandName: result.commandName, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); } - - const { client } = await getAuthedConvexClient(); - await client.mutation(api.documents.updateStats, { - id: documentId, - wordCount: stats.wordCount, - characterCount: stats.characterCount, - blockCount: stats.blockCount, - todoTotal: stats.todoTotal, - todoDone: stats.todoDone, - }); - - return NextResponse.json({ ok: true }); } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/documents/title/route.ts b/wolai-frontend/src/app/api/documents/title/route.ts index b5602086..63b1c088 100644 --- a/wolai-frontend/src/app/api/documents/title/route.ts +++ b/wolai-frontend/src/app/api/documents/title/route.ts @@ -1,22 +1,61 @@ import { NextResponse } from "next/server"; import { isConvexEnabled } from "@/lib/convex/enabled"; -import { getAuthedConvexClient } from "@/lib/convex/route"; -import { api } from "@/lib/convex/api"; +import { + assertDocumentId, + assertTitle, + buildDocumentBridgeContext, + buildDocumentCommandEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { + executeMetadataBridgeCommand, + type DocumentTitleUpdatePayload, +} from "@/lib/documents/metadata-command-adapter"; interface RenamePayload { documentId: string; + workspaceId?: string | null; title: string; } export async function POST(request: Request) { if (isConvexEnabled()) { - const { documentId, title }: RenamePayload = await request.json(); - const { client } = await getAuthedConvexClient(); - await client.mutation(api.documents.updateTitle, { - id: documentId, - title, - }); - return NextResponse.json({ ok: true }); + try { + const { documentId, workspaceId, title }: RenamePayload = await request.json(); + const normalizedDocumentId = assertDocumentId(documentId); + const normalizedTitle = assertTitle(title); + const normalizedWorkspaceId = workspaceId?.trim() || null; + const bridgeContext = await buildDocumentBridgeContext({ request, workspaceId: normalizedWorkspaceId }); + const envelope = buildDocumentCommandEnvelope({ + name: "documents.title.update", + payload: { + documentId: normalizedDocumentId, + workspaceId: normalizedWorkspaceId, + title: normalizedTitle, + } satisfies DocumentTitleUpdatePayload, + context: bridgeContext, + target: { + workspaceId: normalizedWorkspaceId, + pageId: normalizedDocumentId, + }, + }); + const result = await executeMetadataBridgeCommand({ + context: bridgeContext, + envelope, + }); + + return NextResponse.json({ + ok: true, + meta: { + requestId: result.requestId, + traceId: result.traceId, + commandId: result.commandId, + commandName: result.commandName, + }, + }); + } catch (error) { + return documentBridgeErrorResponse(error); + } } return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 }); diff --git a/wolai-frontend/src/app/api/sidebar/route.ts b/wolai-frontend/src/app/api/sidebar/route.ts index 3692c26b..d1226a49 100644 --- a/wolai-frontend/src/app/api/sidebar/route.ts +++ b/wolai-frontend/src/app/api/sidebar/route.ts @@ -1,263 +1,58 @@ import { NextResponse } from "next/server"; -import type { SidebarInitialData } from "@/components/sidebar/types"; import { isConvexEnabled } from "@/lib/convex/enabled"; -import { api } from "@/lib/convex/api"; import { getAuthedConvexClient } from "@/lib/convex/route"; -import { randomUUID } from "crypto"; -import type { MediaAsset } from "@/types/media"; +import { + buildDocumentBridgeContext, + buildDocumentQueryEnvelope, + documentBridgeErrorResponse, +} from "@/lib/documents/bridge"; +import { loadSidebarDataFromConvex } from "@/lib/server/sidebar-data"; export const dynamic = "force-dynamic"; -function extractMindmapImageAssetIdsFromData(input: unknown): string[] { - const root = (() => { - if (!input || typeof input !== "object") return input; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const record = input as any; - // 兼容:某些导图结构为 { root: ... } - return record && typeof record === "object" && "root" in record ? record.root : input; - })(); - - const ids: string[] = []; - const seen = new Set(); - - const push = (value: unknown) => { - if (typeof value !== "string") return; - if (!value.startsWith("asset:")) return; - const id = value.slice("asset:".length).trim(); - if (!id) return; - if (seen.has(id)) return; - seen.add(id); - ids.push(id); - }; - - const get = (obj: unknown, key: string): unknown => { - if (!obj || typeof obj !== "object") return undefined; - return (obj as Record)[key]; - }; - - const walk = (node: unknown) => { - if (!node || typeof node !== "object") return; - - const data = get(node, "data"); - const image = get(node, "image"); - - // 常见:node.data.image = "asset:xxx" - push(get(data, "image")); - // 兼容:node.image = "asset:xxx" - push(image); - // 兼容:node.image.url = "asset:xxx" - push(get(image, "url")); - // 兼容:node.data.image.url = "asset:xxx" - push(get(get(data, "image"), "url")); - - const children = get(node, "children"); - if (Array.isArray(children)) { - children.forEach(walk); - } - }; - - walk(root); - return ids; -} - export async function GET(request: Request) { if (isConvexEnabled()) { const { auth, client } = await getAuthedConvexClient(); const url = new URL(request.url); const workspaceIdParam = url.searchParams.get("workspaceId"); - - const bootstrap = await client.mutation(api.workspaces.ensureDefaultWorkspace, { + const { + targetWorkspaceId, + sidebarInitialData, + } = await loadSidebarDataFromConvex({ + client, + userId: auth.userId, fallbackName: auth.email ?? auth.name ?? "我的空间", - workspaceIdIfCreate: randomUUID(), + requestedWorkspaceId: workspaceIdParam, }); - const summaries = await client.query(api.workspaces.fetchWorkspaceSummaries, { - }); - - const workspaces = summaries.workspaces.length > 0 ? summaries.workspaces : bootstrap.workspaces; - const activeWorkspaceId = summaries.activeWorkspaceId || bootstrap.activeWorkspaceId; - const targetWorkspaceId = workspaceIdParam || activeWorkspaceId; - if (!targetWorkspaceId) { return NextResponse.json({ error: "暂无可用工作空间" }, { status: 404 }); } try { - const documents = await client.query(api.documents.listByWorkspace, { + const bridgeContext = await buildDocumentBridgeContext({ + request, workspaceId: targetWorkspaceId, }); - - const trashedDocuments = await client.query(api.documents.listTrashedByWorkspace, { - workspaceId: targetWorkspaceId, + const envelope = buildDocumentQueryEnvelope({ + name: "sidebar.dataset.list", + payload: { workspaceId: targetWorkspaceId }, }); + if (!sidebarInitialData) { + return NextResponse.json({ error: "暂无可用工作空间" }, { status: 404 }); + } - const mindmapRows = await client.query(api.mindmaps.listByWorkspace, { - workspaceId: targetWorkspaceId, - includeDeleted: true, + return NextResponse.json({ + ...sidebarInitialData, + meta: { + requestId: bridgeContext.requestId, + traceId: bridgeContext.traceId, + queryName: envelope.name, + }, }); - - const mediaAssets = await client.query(api.mediaAssets.listByWorkspace, { - userId: auth.userId, - workspaceId: targetWorkspaceId, - limit: 200, - }); - - const trashedMediaAssets = await client.query(api.mediaAssets.listDeletedByWorkspace, { - userId: auth.userId, - workspaceId: targetWorkspaceId, - limit: 2000, - }); - - const tables = await client.query(api.tables.listByWorkspaceForSearch, { - userId: auth.userId, - workspaceId: targetWorkspaceId, - includeArchived: true, - limit: 3000, - }); - - const activeMindmaps = (mindmapRows ?? []).filter((r) => !r.deleted_at); - const trashedMindmaps = (mindmapRows ?? []).filter((r) => !!r.deleted_at); - - const mindmapDocs = Array.from(new Set(activeMindmaps.map((r) => r.document_id))); - - const mindmapAssetChildren: Record = {}; - activeMindmaps.forEach((r) => { - const ids = extractMindmapImageAssetIdsFromData(r.data); - if (ids.length > 0) { - mindmapAssetChildren[r.mindmap_id] = ids; - } - }); - - const mindmapAssets: MediaAsset[] = activeMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? targetWorkspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const trashedMindmapAssets: MediaAsset[] = trashedMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? targetWorkspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: r.deleted_at ?? null, - deleted_by: r.deleted_by ?? null, - purged_at: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const tableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => !row.is_archived) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? targetWorkspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - const trashedTableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => Boolean(row.is_archived)) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? targetWorkspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: row.deleted_at ?? row.updated_at ?? null, - deleted_by: row.deleted_by ?? null, - purged_at: row.purged_at ?? null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - const payload: SidebarInitialData = { - activeWorkspaceId: targetWorkspaceId, - workspaces, - documents, - trashedDocuments, - trashedMediaAssets: (trashedMediaAssets ?? []) as MediaAsset[], - trashedMindmapAssets, - trashedTableAssets, - mindmapDocs, - mindmapAssets, - mindmapAssetChildren, - tableAssets, - mediaAssets: (mediaAssets ?? []) as MediaAsset[], - }; - - return NextResponse.json(payload); } catch (error) { - return NextResponse.json( - { error: error instanceof Error ? error.message : "拉取侧边栏数据失败" }, - { status: 500 }, - ); + return documentBridgeErrorResponse(error); } } @@ -268,22 +63,22 @@ export async function GET(request: Request) { const { data: { session }, } = await supabase.auth.getSession(); - - if (!session) { - return NextResponse.json({ error: "未登录" }, { status: 401 }); - } - - const url = new URL(request.url); - const workspaceIdParam = url.searchParams.get("workspaceId"); - - await ensureDefaultWorkspace(supabase, session.user.id, session.user.email ?? "我的空间"); - const { workspaces, activeWorkspaceId } = await fetchWorkspaceSummaries(supabase, session.user.id); - const targetWorkspaceId = workspaceIdParam || activeWorkspaceId; - - if (!targetWorkspaceId) { - return NextResponse.json({ error: "暂无可用工作空间" }, { status: 404 }); - } - + + if (!session) { + return NextResponse.json({ error: "未登录" }, { status: 401 }); + } + + const url = new URL(request.url); + const workspaceIdParam = url.searchParams.get("workspaceId"); + + await ensureDefaultWorkspace(supabase, session.user.id, session.user.email ?? "我的空间"); + const { workspaces, activeWorkspaceId } = await fetchWorkspaceSummaries(supabase, session.user.id); + const targetWorkspaceId = workspaceIdParam || activeWorkspaceId; + + if (!targetWorkspaceId) { + return NextResponse.json({ error: "暂无可用工作空间" }, { status: 404 }); + } + try { const dataset = await fetchSidebarDataset(supabase, targetWorkspaceId); const docIds = dataset.documents.map((d) => d.id); @@ -360,10 +155,10 @@ export async function GET(request: Request) { return NextResponse.json(payload); } catch (error) { - return NextResponse.json( - { error: error instanceof Error ? error.message : "拉取侧边栏数据失败" }, - { status: 500 }, - ); - } + return NextResponse.json( + { error: error instanceof Error ? error.message : "拉取侧边栏数据失败" }, + { status: 500 }, + ); + } */ } diff --git a/wolai-frontend/src/components/editor/blocknote-editor.tsx b/wolai-frontend/src/components/editor/blocknote-editor.tsx index eda831b4..2375e8e6 100644 --- a/wolai-frontend/src/components/editor/blocknote-editor.tsx +++ b/wolai-frontend/src/components/editor/blocknote-editor.tsx @@ -1,31 +1,31 @@ -"use client"; - -import "@blocknote/core/style.css"; -import "@blocknote/react/style.css"; -import "@blocknote/mantine/style.css"; - -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +"use client"; + +import "@blocknote/core/style.css"; +import "@blocknote/react/style.css"; +import "@blocknote/mantine/style.css"; + +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { BlockNoteView } from "@blocknote/mantine"; import { SideMenuController, useCreateBlockNote, type SideMenuProps, } from "@blocknote/react"; -import { HocuspocusProvider } from "@hocuspocus/provider"; -import * as Y from "yjs"; -import type { Block } from "@blocknote/core"; -import { cn } from "@/lib/utils"; -import type { Json } from "@/types/supabase"; -import type { DocumentStats, PageOptionsState } from "@/types/page-options"; -import type { MediaAsset } from "@/types/media"; -import { customBlockSchema, type CustomBlockSchema } from "./schema"; -import { CustomSideMenu } from "./menus/CustomSideMenu"; -import { CustomSlashMenu } from "./menus/CustomSlashMenu"; -import { MoveEmbedPickerHost } from "@/components/documents/move-embed-picker-host"; -import { useDebouncedCallback } from "@/hooks/use-debounced-callback"; -import { DocumentToc, type TocEntry } from "@/components/editor/document-toc"; -import { useSearchPaletteStore } from "@/store/search-palette"; -import { useEditorBridgeStore } from "@/store/editor-bridge"; +import { HocuspocusProvider } from "@hocuspocus/provider"; +import * as Y from "yjs"; +import type { Block } from "@blocknote/core"; +import { cn } from "@/lib/utils"; +import type { Json } from "@/types/supabase"; +import type { DocumentStats, PageOptionsState } from "@/types/page-options"; +import type { MediaAsset } from "@/types/media"; +import { customBlockSchema, type CustomBlockSchema } from "./schema"; +import { CustomSideMenu } from "./menus/CustomSideMenu"; +import { CustomSlashMenu } from "./menus/CustomSlashMenu"; +import { MoveEmbedPickerHost } from "@/components/documents/move-embed-picker-host"; +import { useDebouncedCallback } from "@/hooks/use-debounced-callback"; +import { DocumentToc, type TocEntry } from "@/components/editor/document-toc"; +import { useSearchPaletteStore } from "@/store/search-palette"; +import { useEditorBridgeStore } from "@/store/editor-bridge"; import type { ReferenceTarget } from "@/types/search"; import FullScreenTableEditor from "@/components/online-table/FullScreenTableEditor"; import { clamp, MIN_BLOCK_LEVEL, MAX_BLOCK_LEVEL } from "@/lib/constants"; @@ -35,7 +35,7 @@ import { useAppPreferencesStore } from "@/store/app-preferences"; import { useCommentsUiStore } from "@/store/comments-ui"; import { useConvexAuth, useQuery } from "convex/react"; import { api } from "@/lib/convex/api"; - + interface BlockNoteEditorProps { documentId: string; workspaceId: string; @@ -46,130 +46,130 @@ interface BlockNoteEditorProps { onSnapshot?: (payload: { blocks: Json; stats: DocumentStats }) => void; onCloseToc?: () => void; } - -const extractInitialBlocks = (content: unknown): Json | undefined => { - if (Array.isArray(content) && content.length > 0) { - return content as Json; - } - if (content && typeof content === "object") { - const maybeBlocks = (content as Record).blocks; - if (Array.isArray(maybeBlocks) && maybeBlocks.length > 0) { - return maybeBlocks as Json; - } - } - return undefined; -}; - -const extractInlineText = (block: Block): string => { - const inlineNodes = (block.content ?? []) as Array<{ text?: string }>; - return inlineNodes.map((node) => (typeof node.text === "string" ? node.text : "")).join("").trim(); -}; - -const buildHeadingToc = (blocks: Block[]): TocEntry[] => { - const counters = [0, 0, 0, 0, 0]; - const entries: TocEntry[] = []; - - const walk = (targetBlocks: Block[]) => { - targetBlocks.forEach((block) => { - if (block.type === "heading") { - const level = clamp(Number(block.props.level) || 1, MIN_BLOCK_LEVEL, MAX_BLOCK_LEVEL); - counters[level - 1] += 1; - for (let i = level; i < counters.length; i += 1) { - counters[i] = 0; - } - const numbering = counters.slice(0, level).filter((value) => value > 0).join("."); - entries.push({ - id: block.id, - level, - numbering, - title: extractInlineText(block), - }); - } - if (block.children && block.children.length > 0) { - walk(block.children as Block[]); - } - }); - }; - - walk(blocks); - return entries; -}; - -const findBlockById = ( - blocks: Block[], - id: string, -): Block | undefined => { - for (const block of blocks) { - if (block.id === id) return block; - if (block.children && block.children.length > 0) { - const child = findBlockById(block.children as Block[], id); - if (child) return child; - } - } - return undefined; -}; - -const syncProgressMeters = (editorInstance: ReturnType) => { - if (!editorInstance) { - return; - } - const blocks = editorInstance.topLevelBlocks as Block[]; - const progressStats = new Map< - string, - { done: number; doing: number; total: number } - >(); - let activeProgressId: string | null = null; - - const traverse = (targetBlocks: Block[]) => { - targetBlocks.forEach((block) => { - if (block.type === "progressMeter" && block.props.auto) { - activeProgressId = block.id; - progressStats.set(block.id, { done: 0, doing: 0, total: 0 }); - } else if (block.type === "progressMeter" && !block.props.auto) { - activeProgressId = null; - } else if (block.type === "heading") { - activeProgressId = null; - } else if (block.type === "advancedTodo" && activeProgressId) { - const currentStat = progressStats.get(activeProgressId); - if (!currentStat) return; - if (block.props.status === "cancelled") { - return; - } - currentStat.total += 1; - if (block.props.status === "done") { - currentStat.done += 1; - } else if (block.props.status === "doing") { - currentStat.doing += 1; - } - } - - if (block.children && block.children.length > 0) { - traverse(block.children as Block[]); - } - }); - }; - - traverse(blocks); - - progressStats.forEach((stat, progressId) => { - const block = findBlockById(blocks, progressId); - if (!block) return; - if (block.type !== "progressMeter") return; - const weightedDone = stat.done + stat.doing * 0.5; - const percent = stat.total === 0 ? 0 : Math.min(100, Math.round((weightedDone / stat.total) * 100)); - const summary = stat.total === 0 ? "暂无条目" : `${stat.done}/${stat.total} 完成`; - - if (block.props.percent !== percent || block.props.summary !== summary) { - editorInstance.updateBlock(block, { - props: { - percent, - summary, - }, - }); - } - }); -}; - + +const extractInitialBlocks = (content: unknown): Json | undefined => { + if (Array.isArray(content) && content.length > 0) { + return content as Json; + } + if (content && typeof content === "object") { + const maybeBlocks = (content as Record).blocks; + if (Array.isArray(maybeBlocks) && maybeBlocks.length > 0) { + return maybeBlocks as Json; + } + } + return undefined; +}; + +const extractInlineText = (block: Block): string => { + const inlineNodes = (block.content ?? []) as Array<{ text?: string }>; + return inlineNodes.map((node) => (typeof node.text === "string" ? node.text : "")).join("").trim(); +}; + +const buildHeadingToc = (blocks: Block[]): TocEntry[] => { + const counters = [0, 0, 0, 0, 0]; + const entries: TocEntry[] = []; + + const walk = (targetBlocks: Block[]) => { + targetBlocks.forEach((block) => { + if (block.type === "heading") { + const level = clamp(Number(block.props.level) || 1, MIN_BLOCK_LEVEL, MAX_BLOCK_LEVEL); + counters[level - 1] += 1; + for (let i = level; i < counters.length; i += 1) { + counters[i] = 0; + } + const numbering = counters.slice(0, level).filter((value) => value > 0).join("."); + entries.push({ + id: block.id, + level, + numbering, + title: extractInlineText(block), + }); + } + if (block.children && block.children.length > 0) { + walk(block.children as Block[]); + } + }); + }; + + walk(blocks); + return entries; +}; + +const findBlockById = ( + blocks: Block[], + id: string, +): Block | undefined => { + for (const block of blocks) { + if (block.id === id) return block; + if (block.children && block.children.length > 0) { + const child = findBlockById(block.children as Block[], id); + if (child) return child; + } + } + return undefined; +}; + +const syncProgressMeters = (editorInstance: ReturnType) => { + if (!editorInstance) { + return; + } + const blocks = editorInstance.topLevelBlocks as Block[]; + const progressStats = new Map< + string, + { done: number; doing: number; total: number } + >(); + let activeProgressId: string | null = null; + + const traverse = (targetBlocks: Block[]) => { + targetBlocks.forEach((block) => { + if (block.type === "progressMeter" && block.props.auto) { + activeProgressId = block.id; + progressStats.set(block.id, { done: 0, doing: 0, total: 0 }); + } else if (block.type === "progressMeter" && !block.props.auto) { + activeProgressId = null; + } else if (block.type === "heading") { + activeProgressId = null; + } else if (block.type === "advancedTodo" && activeProgressId) { + const currentStat = progressStats.get(activeProgressId); + if (!currentStat) return; + if (block.props.status === "cancelled") { + return; + } + currentStat.total += 1; + if (block.props.status === "done") { + currentStat.done += 1; + } else if (block.props.status === "doing") { + currentStat.doing += 1; + } + } + + if (block.children && block.children.length > 0) { + traverse(block.children as Block[]); + } + }); + }; + + traverse(blocks); + + progressStats.forEach((stat, progressId) => { + const block = findBlockById(blocks, progressId); + if (!block) return; + if (block.type !== "progressMeter") return; + const weightedDone = stat.done + stat.doing * 0.5; + const percent = stat.total === 0 ? 0 : Math.min(100, Math.round((weightedDone / stat.total) * 100)); + const summary = stat.total === 0 ? "暂无条目" : `${stat.done}/${stat.total} 完成`; + + if (block.props.percent !== percent || block.props.summary !== summary) { + editorInstance.updateBlock(block, { + props: { + percent, + summary, + }, + }); + } + }); +}; + export function BlockNoteEditor({ documentId, workspaceId, @@ -180,11 +180,11 @@ export function BlockNoteEditor({ onSnapshot, onCloseToc, }: BlockNoteEditorProps) { - const [isSaving, setIsSaving] = useState(false); - const [tocEntries, setTocEntries] = useState([]); - const [fullScreenTableId, setFullScreenTableId] = useState(null); - const isFullScreenTableOpen = fullScreenTableId !== null; - + const [isSaving, setIsSaving] = useState(false); + const [tocEntries, setTocEntries] = useState([]); + const [fullScreenTableId, setFullScreenTableId] = useState(null); + const isFullScreenTableOpen = fullScreenTableId !== null; + const openReferencePalette = useSearchPaletteStore((state) => state.openReference); const registerEditorBridge = useEditorBridgeStore((state) => state.registerBridge); const spellCheck = useAppPreferencesStore((s) => s.spellCheck); @@ -207,24 +207,24 @@ export function BlockNoteEditor({ } return map; }, [threads]); - - const normalizedInitialContent = useMemo( - () => extractInitialBlocks(initialContent), - [initialContent], - ); - - const collaboration = useMemo(() => { - const url = process.env.NEXT_PUBLIC_HOCUSPOCUS_URL; - if (!url) return null; - const doc = new Y.Doc(); - const provider = new HocuspocusProvider({ - url, - name: `document.${documentId}`, - document: doc, - }); - return { doc, provider }; - }, [documentId]); - + + const normalizedInitialContent = useMemo( + () => extractInitialBlocks(initialContent), + [initialContent], + ); + + const collaboration = useMemo(() => { + const url = process.env.NEXT_PUBLIC_HOCUSPOCUS_URL; + if (!url) return null; + const doc = new Y.Doc(); + const provider = new HocuspocusProvider({ + url, + name: `document.${documentId}`, + document: doc, + }); + return { doc, provider }; + }, [documentId]); + const editor = useCreateBlockNote( { initialContent: normalizedInitialContent as never, @@ -238,39 +238,39 @@ export function BlockNoteEditor({ provider: collaboration.provider, fragment: collaboration.doc.getXmlFragment("wolai"), user: { - name: "访客", - color: "#2563eb", - }, - } - : undefined, - }, - [documentId, normalizedInitialContent], - ); - - useEffect( - () => () => { - collaboration?.provider.destroy(); - collaboration?.doc.destroy(); - }, - [collaboration], - ); - - const saveContent = useCallback( - async (content: Json) => { - setIsSaving(true); - try { - await fetch("/api/documents/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ documentId, content }), - }); - } finally { - setIsSaving(false); - } - }, - [documentId], - ); - + name: "访客", + color: "#2563eb", + }, + } + : undefined, + }, + [documentId, normalizedInitialContent], + ); + + useEffect( + () => () => { + collaboration?.provider.destroy(); + collaboration?.doc.destroy(); + }, + [collaboration], + ); + + const saveContent = useCallback( + async (content: Json) => { + setIsSaving(true); + try { + await fetch("/api/documents/save", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ documentId, workspaceId, content }), + }); + } finally { + setIsSaving(false); + } + }, + [documentId, workspaceId], + ); + const debouncedSave = useDebouncedCallback(saveContent, 800); const previousAssetsRef = useRef>(new Set()); const previousMindmapBlockIdsRef = useRef>(new Set()); @@ -279,50 +279,50 @@ export function BlockNoteEditor({ const onlineTableRestoreRetryCountRef = useRef>(new Map()); const onlineTableRestoreRetryTimerRef = useRef>(new Map()); const onlineTableRestoringRef = useRef>(new Set()); - const clearMindmapAutosaveCache = useCallback((targetDocumentId: string, mindmapId?: string) => { - if (typeof window === "undefined") return; - try { - const prefix = "wolai-mindmap-autosave-"; - const targetPrefix = `${prefix}${targetDocumentId}`; - const directKey = mindmapId ? `${targetPrefix}:${mindmapId}` : targetPrefix; - const keys: string[] = []; - for (let i = 0; i < window.localStorage.length; i += 1) { - const k = window.localStorage.key(i); - if (!k) continue; - if (mindmapId) { - if (k === directKey) keys.push(k); - } else if (k === targetPrefix || k.startsWith(`${targetPrefix}:`)) { - keys.push(k); - } - } - keys.forEach((k) => window.localStorage.removeItem(k)); - } catch { - // ignore - } - }, []); - const markMindmapDeleting = useCallback((targetDocumentId: string, mindmapId?: string) => { - if (typeof window === "undefined") return; - try { - const w = window as unknown as { - __wolaiMindmapDeletingKeys?: Set; - }; - if (!w.__wolaiMindmapDeletingKeys) { - w.__wolaiMindmapDeletingKeys = new Set(); - } - const key = mindmapId ? `${targetDocumentId}:${mindmapId}` : targetDocumentId; - w.__wolaiMindmapDeletingKeys.add(key); - window.setTimeout(() => { - try { - w.__wolaiMindmapDeletingKeys?.delete(key); - } catch { - // ignore - } - }, 8000); - } catch { - // ignore - } - }, []); - + const clearMindmapAutosaveCache = useCallback((targetDocumentId: string, mindmapId?: string) => { + if (typeof window === "undefined") return; + try { + const prefix = "wolai-mindmap-autosave-"; + const targetPrefix = `${prefix}${targetDocumentId}`; + const directKey = mindmapId ? `${targetPrefix}:${mindmapId}` : targetPrefix; + const keys: string[] = []; + for (let i = 0; i < window.localStorage.length; i += 1) { + const k = window.localStorage.key(i); + if (!k) continue; + if (mindmapId) { + if (k === directKey) keys.push(k); + } else if (k === targetPrefix || k.startsWith(`${targetPrefix}:`)) { + keys.push(k); + } + } + keys.forEach((k) => window.localStorage.removeItem(k)); + } catch { + // ignore + } + }, []); + const markMindmapDeleting = useCallback((targetDocumentId: string, mindmapId?: string) => { + if (typeof window === "undefined") return; + try { + const w = window as unknown as { + __wolaiMindmapDeletingKeys?: Set; + }; + if (!w.__wolaiMindmapDeletingKeys) { + w.__wolaiMindmapDeletingKeys = new Set(); + } + const key = mindmapId ? `${targetDocumentId}:${mindmapId}` : targetDocumentId; + w.__wolaiMindmapDeletingKeys.add(key); + window.setTimeout(() => { + try { + w.__wolaiMindmapDeletingKeys?.delete(key); + } catch { + // ignore + } + }, 8000); + } catch { + // ignore + } + }, []); + const collectAssets = useCallback((blocks: Block[]) => { const assetIds = new Set(); const mindmapBlockIds = new Set(); @@ -348,27 +348,27 @@ export function BlockNoteEditor({ walk(blocks); return { assetIds, mindmapBlockIds, onlineTableIds }; }, []); - - const deleteAssets = useCallback( - async (assetIds: string[]) => { - if (assetIds.length === 0) return; - const resp = await fetch("/api/media/batch", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ action: "delete", assetIds }), - }); - if (!resp.ok) { - // 如果后端返回未找到,说明已被其他端删除,忽略即可 - if (resp.status !== 404) { - console.error("删除附件失败", await resp.text()); - } - return; - } - emitAssetsChanged(documentId); - }, - [documentId], - ); - + + const deleteAssets = useCallback( + async (assetIds: string[]) => { + if (assetIds.length === 0) return; + const resp = await fetch("/api/media/batch", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "delete", assetIds }), + }); + if (!resp.ok) { + // 如果后端返回未找到,说明已被其他端删除,忽略即可 + if (resp.status !== 404) { + console.error("删除附件失败", await resp.text()); + } + return; + } + emitAssetsChanged(documentId); + }, + [documentId], + ); + const deleteMindmapAssets = useCallback( async (mindmapIds: string[]) => { if (mindmapIds.length === 0) return; @@ -512,70 +512,70 @@ export function BlockNoteEditor({ // 监听侧边栏删除事件,主动移除编辑区遗留块 useEffect(() => { - const handler = (event: Event) => { - const detail = (event as CustomEvent)?.detail as { - docId?: string; - assetIds?: string[]; - mindmapDeleted?: boolean; - mindmapAssetIds?: string[]; - }; - if (!detail || detail.docId !== documentId) return; - const assetIds = detail.assetIds ?? []; - const mindmapDeleted = Boolean(detail.mindmapDeleted); - const mindmapAssetIds = Array.isArray(detail.mindmapAssetIds) - ? (detail.mindmapAssetIds.filter((id) => typeof id === "string") as string[]) - : []; - if (assetIds.length === 0 && !mindmapDeleted && mindmapAssetIds.length === 0) return; - if (mindmapDeleted || mindmapAssetIds.length > 0) { - // 标记“删除中”,避免 MindmapBlock 卸载清理里把 autosave 写回导致“复活” - if (mindmapAssetIds.length > 0) { - mindmapAssetIds.forEach((id) => markMindmapDeleting(documentId, id)); - } else { - markMindmapDeleting(documentId); - } - // 双保险:等卸载完成后再清一次 autosave,避免删除后再次插入出现旧内容(重复) - window.setTimeout(() => { - if (mindmapAssetIds.length > 0) { - mindmapAssetIds.forEach((id) => clearMindmapAutosaveCache(documentId, id)); - } else { - clearMindmapAutosaveCache(documentId); - } - }, 0); - } - const blocks = editor?.topLevelBlocks as Block[] | undefined; - if (!blocks || blocks.length === 0 || !editor) return; - const toRemove: string[] = []; - const walk = (target: Block[]) => { - target.forEach((b) => { - if (b.type === "mindmap") { - if (mindmapDeleted) { - toRemove.push(b.id); - } else if (mindmapAssetIds.length > 0 && mindmapAssetIds.includes(b.id)) { - toRemove.push(b.id); - } - } - if (assetIds.length > 0 && b.type === "media") { - const id = (b.props as { assetId?: string })?.assetId; - if (id && assetIds.includes(id)) { - toRemove.push(b.id); - } - } - if (Array.isArray(b.children) && b.children.length > 0) { - walk(b.children as Block[]); - } - }); - }; - walk(blocks); - if (toRemove.length > 0) { - try { - editor.removeBlocks(toRemove); - } catch { - // ignore:可能已被其它链路先行删除(例如块菜单/工具栏触发的 removeBlocks) - } - } - }; - window.addEventListener(ASSETS_CHANGED_EVENT, handler); - return () => window.removeEventListener(ASSETS_CHANGED_EVENT, handler); + const handler = (event: Event) => { + const detail = (event as CustomEvent)?.detail as { + docId?: string; + assetIds?: string[]; + mindmapDeleted?: boolean; + mindmapAssetIds?: string[]; + }; + if (!detail || detail.docId !== documentId) return; + const assetIds = detail.assetIds ?? []; + const mindmapDeleted = Boolean(detail.mindmapDeleted); + const mindmapAssetIds = Array.isArray(detail.mindmapAssetIds) + ? (detail.mindmapAssetIds.filter((id) => typeof id === "string") as string[]) + : []; + if (assetIds.length === 0 && !mindmapDeleted && mindmapAssetIds.length === 0) return; + if (mindmapDeleted || mindmapAssetIds.length > 0) { + // 标记“删除中”,避免 MindmapBlock 卸载清理里把 autosave 写回导致“复活” + if (mindmapAssetIds.length > 0) { + mindmapAssetIds.forEach((id) => markMindmapDeleting(documentId, id)); + } else { + markMindmapDeleting(documentId); + } + // 双保险:等卸载完成后再清一次 autosave,避免删除后再次插入出现旧内容(重复) + window.setTimeout(() => { + if (mindmapAssetIds.length > 0) { + mindmapAssetIds.forEach((id) => clearMindmapAutosaveCache(documentId, id)); + } else { + clearMindmapAutosaveCache(documentId); + } + }, 0); + } + const blocks = editor?.topLevelBlocks as Block[] | undefined; + if (!blocks || blocks.length === 0 || !editor) return; + const toRemove: string[] = []; + const walk = (target: Block[]) => { + target.forEach((b) => { + if (b.type === "mindmap") { + if (mindmapDeleted) { + toRemove.push(b.id); + } else if (mindmapAssetIds.length > 0 && mindmapAssetIds.includes(b.id)) { + toRemove.push(b.id); + } + } + if (assetIds.length > 0 && b.type === "media") { + const id = (b.props as { assetId?: string })?.assetId; + if (id && assetIds.includes(id)) { + toRemove.push(b.id); + } + } + if (Array.isArray(b.children) && b.children.length > 0) { + walk(b.children as Block[]); + } + }); + }; + walk(blocks); + if (toRemove.length > 0) { + try { + editor.removeBlocks(toRemove); + } catch { + // ignore:可能已被其它链路先行删除(例如块菜单/工具栏触发的 removeBlocks) + } + } + }; + window.addEventListener(ASSETS_CHANGED_EVENT, handler); + return () => window.removeEventListener(ASSETS_CHANGED_EVENT, handler); }, [clearMindmapAutosaveCache, documentId, editor, markMindmapDeleting]); // 监听在线表格删除事件(文件树/其它页面触发),主动移除编辑区的 onlineTable 块,并关闭全屏窗口 @@ -619,24 +619,24 @@ export function BlockNoteEditor({ window.addEventListener("online-table-deleted", handler as EventListener); return () => window.removeEventListener("online-table-deleted", handler as EventListener); }, [editor]); - - useEffect(() => { - if (!editor) { - return undefined; - } - - let disposed = false; - const runSync = () => { - if (disposed) { - return; - } - const blocks = editor.topLevelBlocks; - debouncedSave(blocks as Json); - const typedBlocks = blocks as Block[]; - setTocEntries(buildHeadingToc(typedBlocks)); - syncProgressMeters(editor); - const stats = computeDocumentStats(typedBlocks); - onStatsChange?.(stats); + + useEffect(() => { + if (!editor) { + return undefined; + } + + let disposed = false; + const runSync = () => { + if (disposed) { + return; + } + const blocks = editor.topLevelBlocks; + debouncedSave(blocks as Json); + const typedBlocks = blocks as Block[]; + setTocEntries(buildHeadingToc(typedBlocks)); + syncProgressMeters(editor); + const stats = computeDocumentStats(typedBlocks); + onStatsChange?.(stats); onSnapshot?.({ blocks: blocks as Json, stats }); // 检测块删除 -> 同步删除附件/思维导图并刷新侧边栏 @@ -667,74 +667,74 @@ export function BlockNoteEditor({ } previousOnlineTableIdsRef.current = onlineTableIds; }; - - runSync(); - const unsubscribe = editor.onEditorContentChange(runSync) as unknown as - | undefined - | (() => void); - return () => { - disposed = true; - unsubscribe?.(); - }; + + runSync(); + const unsubscribe = editor.onEditorContentChange(runSync) as unknown as + | undefined + | (() => void); + return () => { + disposed = true; + unsubscribe?.(); + }; }, [collectAssets, debouncedSave, deleteAssets, deleteMindmapAssets, deleteOnlineTables, editor, onSnapshot, onStatsChange, restoreOnlineTableIfNeeded]); - - const jumpToHeading = useCallback((headingId: string) => { - const target = document.querySelector(`[data-id="${headingId}"]`); - if (target) { - target.scrollIntoView({ behavior: "smooth", block: "center" }); - } - }, []); - - const editorWrapperClass = cn( - "relative min-h-[60vh] rounded-2xl border border-transparent bg-white p-4 shadow-sm", - pageOptions.smallText ? "text-[15px]" : "text-[16px]", - ); - + + const jumpToHeading = useCallback((headingId: string) => { + const target = document.querySelector(`[data-id="${headingId}"]`); + if (target) { + target.scrollIntoView({ behavior: "smooth", block: "center" }); + } + }, []); + + const editorWrapperClass = cn( + "relative min-h-[60vh] rounded-2xl border border-transparent bg-white p-4 shadow-sm", + pageOptions.smallText ? "text-[15px]" : "text-[16px]", + ); + const blocknoteClass = cn( "wolai-editor min-h-full", (showStructure || pageOptions.showStructure) && "wolai-editor-show-structure", pageOptions.showHeadingNumbers && "wolai-heading-numbering", isFullScreenTableOpen && "pointer-events-none select-none", ); - -const buildDocumentPath = (documentId: string): string => { - if (typeof window === "undefined" || !window.location) { - return `/documents/${documentId}`; - } - return `${window.location.origin}/documents/${documentId}`; -}; - -const trimTrailingCharacter = ( - editorInstance: ReturnType | null, - block: Block, - char: string, -) => { - if (!editorInstance) { - return; - } - const content = (Array.isArray(block.content) ? [...block.content] : []) as any[]; - for (let index = content.length - 1; index >= 0; index -= 1) { - const node = content[index] as any; - if (typeof node?.text === "string" && node.text.endsWith(char)) { - const nextText = node.text.slice(0, -1); - if (nextText.length === 0) { - content.splice(index, 1); - } else { - content[index] = { ...(node as any), text: nextText } as any; - } - editorInstance.updateBlock(block, { content }); - break; - } - } -}; - -const generateBlockId = () => { - if (typeof crypto !== "undefined" && "randomUUID" in crypto) { - return crypto.randomUUID(); - } - return `ref_${Math.random().toString(36).slice(2, 10)}`; -}; - + +const buildDocumentPath = (documentId: string): string => { + if (typeof window === "undefined" || !window.location) { + return `/documents/${documentId}`; + } + return `${window.location.origin}/documents/${documentId}`; +}; + +const trimTrailingCharacter = ( + editorInstance: ReturnType | null, + block: Block, + char: string, +) => { + if (!editorInstance) { + return; + } + const content = (Array.isArray(block.content) ? [...block.content] : []) as any[]; + for (let index = content.length - 1; index >= 0; index -= 1) { + const node = content[index] as any; + if (typeof node?.text === "string" && node.text.endsWith(char)) { + const nextText = node.text.slice(0, -1); + if (nextText.length === 0) { + content.splice(index, 1); + } else { + content[index] = { ...(node as any), text: nextText } as any; + } + editorInstance.updateBlock(block, { content }); + break; + } + } +}; + +const generateBlockId = () => { + if (typeof crypto !== "undefined" && "randomUUID" in crypto) { + return crypto.randomUUID(); + } + return `ref_${Math.random().toString(36).slice(2, 10)}`; +}; + const computeDocumentStats = (blocks: Block[]): DocumentStats => { let characterCount = 0; let wordCount = 0; @@ -745,18 +745,18 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats if (Array.isArray(block.content)) { (block.content as any[]).forEach((node: any) => { if (typeof node.text === "string") { - const text = node.text; - characterCount += text.length; - const trimmed = text.trim(); - if (trimmed.length === 0) { - return; - } - const tokens = trimmed.split(/\s+/).filter(Boolean); - if (tokens.length > 1) { - wordCount += tokens.length; - } else { - wordCount += trimmed.replace(/\s+/g, "").length; - } + const text = node.text; + characterCount += text.length; + const trimmed = text.trim(); + if (trimmed.length === 0) { + return; + } + const tokens = trimmed.split(/\s+/).filter(Boolean); + if (tokens.length > 1) { + wordCount += tokens.length; + } else { + wordCount += trimmed.replace(/\s+/g, "").length; + } } }); } @@ -792,7 +792,7 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats todoDone, }; }; - + const insertMediaAssetBlock = useCallback( (asset: MediaAsset) => { if (!editor) { @@ -829,23 +829,23 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats [ { type: "media", - props: { - fileUrl, - thumbnailUrl: asset.thumbnail_url ?? fileUrl, - assetId: asset.id, - assetType: asset.asset_type ?? "image", - fileName: asset.file_name ?? "", - fileSize: asset.file_size ?? undefined, - mimeType: asset.mime_type ?? "", - ocrStatus: asset.ocr_status ?? "idle", - documentId, - }, - }, - ], - referenceBlock, - "after", - ); - }, + props: { + fileUrl, + thumbnailUrl: asset.thumbnail_url ?? fileUrl, + assetId: asset.id, + assetType: asset.asset_type ?? "image", + fileName: asset.file_name ?? "", + fileSize: asset.file_size ?? undefined, + mimeType: asset.mime_type ?? "", + ocrStatus: asset.ocr_status ?? "idle", + documentId, + }, + }, + ], + referenceBlock, + "after", + ); + }, [documentId, editor], ); @@ -999,33 +999,33 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats if (!workspaceId) { throw new Error("缺少空间信息,无法上传文件"); } - const form = new FormData(); - form.append("file", file); - form.append("workspaceId", workspaceId); - form.append("documentId", documentId); - const response = await fetch("/api/media/upload", { - method: "POST", - body: form, - }); - if (!response.ok) { - const payload = await response.json().catch(() => null); - throw new Error(payload?.error ?? "上传失败"); - } - const payload = (await response.json()) as { asset: MediaAsset }; - if (!payload.asset) { - throw new Error("上传返回数据缺失"); - } - emitAssetsChanged(documentId, payload.asset); - return payload.asset; - }, - [documentId, workspaceId], - ); - - useEffect(() => { - if (!editor) { - registerEditorBridge(null); - return; - } + const form = new FormData(); + form.append("file", file); + form.append("workspaceId", workspaceId); + form.append("documentId", documentId); + const response = await fetch("/api/media/upload", { + method: "POST", + body: form, + }); + if (!response.ok) { + const payload = await response.json().catch(() => null); + throw new Error(payload?.error ?? "上传失败"); + } + const payload = (await response.json()) as { asset: MediaAsset }; + if (!payload.asset) { + throw new Error("上传返回数据缺失"); + } + emitAssetsChanged(documentId, payload.asset); + return payload.asset; + }, + [documentId, workspaceId], + ); + + useEffect(() => { + if (!editor) { + registerEditorBridge(null); + return; + } const bridge = { undo: () => { try { @@ -1059,39 +1059,39 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats editor.focus(); const cursor = editor.getTextCursorPosition(); const blockId = cursor?.block?.id ?? null; - const text = aliasText || target.title || "无标题"; - editor.insertInlineContent([ - { - type: "link", - href: buildDocumentPath(target.id), - content: text, - }, - " ", - ]); - return { blockId }; - }, - insertEmbedReference: (target: ReferenceTarget) => { - editor.focus(); - const cursor = editor.getTextCursorPosition(); - const referenceBlock = - cursor?.block ?? editor.topLevelBlocks[editor.topLevelBlocks.length - 1]; - const blockId = generateBlockId(); - editor.insertBlocks( - [ - { - id: blockId, - type: "pageReference", - props: { - pageId: target.id, - title: target.title ?? "无标题", - }, - }, - ], - referenceBlock, - "after", - ); - return { blockId }; - }, + const text = aliasText || target.title || "无标题"; + editor.insertInlineContent([ + { + type: "link", + href: buildDocumentPath(target.id), + content: text, + }, + " ", + ]); + return { blockId }; + }, + insertEmbedReference: (target: ReferenceTarget) => { + editor.focus(); + const cursor = editor.getTextCursorPosition(); + const referenceBlock = + cursor?.block ?? editor.topLevelBlocks[editor.topLevelBlocks.length - 1]; + const blockId = generateBlockId(); + editor.insertBlocks( + [ + { + id: blockId, + type: "pageReference", + props: { + pageId: target.id, + title: target.title ?? "无标题", + }, + }, + ], + referenceBlock, + "after", + ); + return { blockId }; + }, replaceWithSnapshot: (payload: Json) => { editor.focus(); const nextBlocks = Array.isArray(payload) @@ -1139,7 +1139,7 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [documentId, editor, openCommentsForBlock, openCommentsForPage, workspaceId]); - + useEffect(() => { if (!editor) { return undefined; @@ -1163,82 +1163,82 @@ const computeDocumentStats = (blocks: Block[]): DocumentStats if (!(activeElement instanceof HTMLElement) || !activeElement.closest(".wolai-editor")) { return; } - const items = Array.from(event.clipboardData?.files ?? []); - if (items.length === 0) { - return; - } - const imageFile = items.find((candidate) => candidate.type?.startsWith("image/")); - if (!imageFile) { - return; - } - event.preventDefault(); - void (async () => { - try { - const asset = await uploadClipboardMedia(imageFile); - insertMediaAssetBlock(asset); - } catch (error) { - console.error(error); - window.alert((error as Error).message ?? "粘贴图片失败,请稍后重试"); - } - })(); - }; + const items = Array.from(event.clipboardData?.files ?? []); + if (items.length === 0) { + return; + } + const imageFile = items.find((candidate) => candidate.type?.startsWith("image/")); + if (!imageFile) { + return; + } + event.preventDefault(); + void (async () => { + try { + const asset = await uploadClipboardMedia(imageFile); + insertMediaAssetBlock(asset); + } catch (error) { + console.error(error); + window.alert((error as Error).message ?? "粘贴图片失败,请稍后重试"); + } + })(); + }; window.addEventListener("paste", handlePaste); return () => window.removeEventListener("paste", handlePaste); }, [editor, insertMediaAssetBlock, spellCheck, uploadClipboardMedia]); - - useEffect(() => { - if (!editor) { - return; - } - const buffer = { char: "", blockId: "", timestamp: 0 }; - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key !== "[" && event.key !== "#") { - buffer.char = ""; - buffer.blockId = ""; - buffer.timestamp = 0; - return; - } - const activeElement = document.activeElement; - if (!(activeElement instanceof HTMLElement) || !activeElement.closest(".wolai-editor")) { - return; - } - const { block } = editor.getTextCursorPosition(); - if (!block) { - return; - } - const now = Date.now(); - if ( - buffer.char === event.key && - buffer.blockId === block.id && - now - buffer.timestamp < 450 - ) { - event.preventDefault(); - trimTrailingCharacter(editor, block, event.key); - openReferencePalette({ - referenceMode: event.key === "[" ? "inline" : "embed", - }); - buffer.char = ""; - buffer.blockId = ""; - buffer.timestamp = 0; - } else { - buffer.char = event.key; - buffer.blockId = block.id; - buffer.timestamp = now; - } - }; - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [editor, openReferencePalette]); - - const layoutClass = cn( - "relative mx-auto w-full", - pageOptions.wideLayout ? "max-w-none" : "max-w-[980px]", - ); - - return ( - <> -
-
+ + useEffect(() => { + if (!editor) { + return; + } + const buffer = { char: "", blockId: "", timestamp: 0 }; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key !== "[" && event.key !== "#") { + buffer.char = ""; + buffer.blockId = ""; + buffer.timestamp = 0; + return; + } + const activeElement = document.activeElement; + if (!(activeElement instanceof HTMLElement) || !activeElement.closest(".wolai-editor")) { + return; + } + const { block } = editor.getTextCursorPosition(); + if (!block) { + return; + } + const now = Date.now(); + if ( + buffer.char === event.key && + buffer.blockId === block.id && + now - buffer.timestamp < 450 + ) { + event.preventDefault(); + trimTrailingCharacter(editor, block, event.key); + openReferencePalette({ + referenceMode: event.key === "[" ? "inline" : "embed", + }); + buffer.char = ""; + buffer.blockId = ""; + buffer.timestamp = 0; + } else { + buffer.char = event.key; + buffer.blockId = block.id; + buffer.timestamp = now; + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [editor, openReferencePalette]); + + const layoutClass = cn( + "relative mx-auto w-full", + pageOptions.wideLayout ? "max-w-none" : "max-w-[980px]", + ); + + return ( + <> +
+
[]): DocumentStats )} /> )} - - -
- {isSaving ? "保存中..." : "已保存"} -
-
+ + +
+ {isSaving ? "保存中..." : "已保存"} +
+
- - - - {/* 全屏表格编辑器 Modal */} - {fullScreenTableId && ( - setFullScreenTableId(null)} - /> - )} - - ); -} + + + + {/* 全屏表格编辑器 Modal */} + {fullScreenTableId && ( + setFullScreenTableId(null)} + /> + )} + + ); +} \ No newline at end of file diff --git a/wolai-frontend/src/components/editor/document-content.tsx b/wolai-frontend/src/components/editor/document-content.tsx index 218709d1..133f2a54 100644 --- a/wolai-frontend/src/components/editor/document-content.tsx +++ b/wolai-frontend/src/components/editor/document-content.tsx @@ -232,11 +232,14 @@ export function DocumentContent({ }, CONTENT_LOADING_DELAY_MS); try { - const response = await fetch(`/api/documents/content?documentId=${encodeURIComponent(documentId)}`, { + const response = await fetch( + `/api/documents/content?documentId=${encodeURIComponent(documentId)}&workspaceId=${encodeURIComponent(workspaceId)}`, + { method: "GET", credentials: "include", signal: controller.signal, - }); + }, + ); if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload?.error ?? "加载页面内容失败"); @@ -270,7 +273,7 @@ export function DocumentContent({ contentLoadingTimerRef.current = null; } }; - }, [documentId, initialContent, contentReloadKey]); + }, [documentId, initialContent, contentReloadKey, workspaceId]); const persistTitle = useCallback( async (nextTitle: string) => { @@ -279,10 +282,10 @@ export function DocumentContent({ await fetch("/api/documents/title", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ documentId, title: payload }), + body: JSON.stringify({ documentId, workspaceId, title: payload }), }); }, - [documentId, readOnly], + [documentId, readOnly, workspaceId], ); const debouncedPersistTitle = useDebouncedCallback((value: string) => { @@ -315,7 +318,7 @@ export function DocumentContent({ const response = await fetch("/api/documents/options", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ documentId, options: patch }), + body: JSON.stringify({ documentId, workspaceId, options: patch }), }); if (!response.ok) { const payload = await response.json().catch(() => null); @@ -325,7 +328,7 @@ export function DocumentContent({ console.error(error); } }, - [documentId, readOnly], + [documentId, readOnly, workspaceId], ); const toggleOption = useCallback( @@ -600,9 +603,9 @@ export function DocumentContent({ void fetch("/api/documents/stats", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ documentId, stats: next }), + body: JSON.stringify({ documentId, workspaceId, stats: next }), }).catch((error) => console.error(error)); - }, [documentId]); + }, [documentId, workspaceId]); const persistStats = useDebouncedCallback(persistStatsRequest, 1500); diff --git a/wolai-frontend/src/components/sidebar/sidebar.tsx b/wolai-frontend/src/components/sidebar/sidebar.tsx index af1fb978..89eae517 100644 --- a/wolai-frontend/src/components/sidebar/sidebar.tsx +++ b/wolai-frontend/src/components/sidebar/sidebar.tsx @@ -1431,7 +1431,11 @@ function SidebarContent({ initialData, sidebarQuery }: SidebarContentProps) { await fetch("/api/documents/title", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ documentId, title: title.trim() }), + body: JSON.stringify({ + documentId, + workspaceId: sidebarData.activeWorkspaceId ?? null, + title: title.trim(), + }), }); await refreshTree(); }, diff --git a/wolai-frontend/src/hooks/use-convex-sidebar-data.ts b/wolai-frontend/src/hooks/use-convex-sidebar-data.ts index 29620994..3c88552d 100644 --- a/wolai-frontend/src/hooks/use-convex-sidebar-data.ts +++ b/wolai-frontend/src/hooks/use-convex-sidebar-data.ts @@ -3,7 +3,12 @@ import { useQuery, useConvexAuth } from "convex/react"; import type { SidebarInitialData } from "@/components/sidebar/types"; import type { MediaAsset } from "@/types/media"; import { api } from "@/lib/convex/api"; +import { buildSidebarInitialData } from "@/lib/sidebar-data"; import { toBrowserAccessibleUrl } from "@/lib/url/browser-file-url"; + +type CurrentUserRecord = { + _id?: string; +}; /** * Convex 模式下的侧边栏数据 hook @@ -23,9 +28,11 @@ export function useConvexSidebarData(workspaceId: string): { // 显式转为 boolean,确保类型正确 const shouldFetch = Boolean(isAuthenticated && workspaceId); const currentUser = useQuery(api.users.currentUser, shouldFetch ? {} : "skip"); + const currentUserRecord = + currentUser && typeof currentUser === "object" ? (currentUser as CurrentUserRecord) : null; const userId = - currentUser && typeof currentUser === "object" && typeof (currentUser as any)._id === "string" - ? String((currentUser as any)._id) + currentUserRecord && typeof currentUserRecord._id === "string" + ? currentUserRecord._id : ""; const shouldFetchAuthed = Boolean(shouldFetch && userId); @@ -87,133 +94,16 @@ export function useConvexSidebarData(workspaceId: string): { return null; } - const activeMindmaps = (mindmaps ?? []).filter((r) => !r.deleted_at); - const trashedMindmaps = (mindmaps ?? []).filter((r) => !!r.deleted_at); - - const mindmapDocs = Array.from(new Set(activeMindmaps.map((r) => r.document_id))); - - const mindmapAssets: MediaAsset[] = activeMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? workspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const trashedMindmapAssets: MediaAsset[] = trashedMindmaps.map((r) => { - const isLegacy = r.mindmap_id.startsWith("legacy-"); - return { - id: r.mindmap_id, - workspace_id: r.workspace_id ?? workspaceId, - document_id: r.document_id, - asset_type: "mindmap", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: r.deleted_at ?? null, - deleted_by: r.deleted_by ?? null, - purged_at: null, - signed_url: null, - created_at: r.created_at ?? "", - updated_at: r.updated_at ?? "", - }; - }); - - const tableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => !row.is_archived) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? workspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - const trashedTableAssets: MediaAsset[] = (tables ?? []) - .filter((row) => Boolean(row.is_archived)) - .map((row) => { - const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; - const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; - return { - id: row.id, - workspace_id: row.workspace_id ?? workspaceId, - document_id: row.document_id, - asset_type: "luckysheet", - file_url: null, - thumbnail_url: null, - bucket: null, - storage_path: null, - file_name: fileName, - file_size: null, - mime_type: "application/json", - ocr_payload: undefined, - ocr_strategy: null, - ocr_text: null, - ocr_status: null, - deleted_at: row.deleted_at ?? row.updated_at ?? null, - deleted_by: row.deleted_by ?? null, - purged_at: row.purged_at ?? null, - signed_url: null, - created_at: row.created_at ?? "", - updated_at: row.updated_at ?? "", - }; - }); - - return { + return buildSidebarInitialData({ activeWorkspaceId: workspacesResult.activeWorkspaceId || workspaceId, workspaces: workspacesResult.workspaces, documents, trashedDocuments, - trashedMediaAssets: ((trashedMediaAssets ?? []) as MediaAsset[]).map(normalizeAssetUrls), - trashedMindmapAssets, - trashedTableAssets, - mindmapDocs, - mindmapAssets, - mindmapAssetChildren: {}, - tableAssets, + mindmaps, mediaAssets: ((mediaAssets ?? []) as MediaAsset[]).map(normalizeAssetUrls), - }; + trashedMediaAssets: ((trashedMediaAssets ?? []) as MediaAsset[]).map(normalizeAssetUrls), + tables, + }); }, [ currentUser, documents, diff --git a/wolai-frontend/src/lib/documents/bridge-log.ts b/wolai-frontend/src/lib/documents/bridge-log.ts new file mode 100644 index 00000000..7ea124d4 --- /dev/null +++ b/wolai-frontend/src/lib/documents/bridge-log.ts @@ -0,0 +1,73 @@ +import { api } from "@/lib/convex/api"; +import type { BridgeContext, BridgeTarget, CommandEnvelope } from "@/lib/documents/bridge"; +import { getAuthedConvexClient } from "@/lib/convex/route"; + +const bridgeLogsApi = api as any; + +function buildPayloadSummary(commandName: string, context: BridgeContext): string { + return `command=${commandName};request_id=${context.requestId};trace_id=${context.traceId}`; +} + +function normalizeWorkspaceId(context: BridgeContext, target?: BridgeTarget | null): string | null { + return target?.workspaceId?.trim() || context.workspaceId?.trim() || null; +} + +export async function recordBridgeCommandArtifacts(input: { + context: BridgeContext; + envelope: CommandEnvelope; +}): Promise { + const workspaceId = normalizeWorkspaceId(input.context, input.envelope.target); + if (!workspaceId) return; + + const { client } = await getAuthedConvexClient(); + const commandLogId = `clog_${input.envelope.commandId}`; + const eventId = `evt_${input.envelope.commandId}`; + const now = new Date().toISOString(); + const payload = input.envelope.payload as Record; + + await client.mutation(bridgeLogsApi.bridgeLogs.recordCommandLog, { + workspaceId, + id: commandLogId, + requestId: input.context.requestId, + traceId: input.context.traceId, + commandId: input.envelope.commandId, + commandName: input.envelope.name, + actorId: input.context.actor.actorId, + actorType: input.context.actor.actorType, + sourceChannel: input.context.source.channel, + sourceClient: input.context.source.client, + status: "succeeded", + targetPageId: input.envelope.target?.pageId ?? null, + targetBlockId: input.envelope.target?.blockId ?? null, + payload, + payloadSummary: buildPayloadSummary(input.envelope.name, input.context), + refs: input.envelope.refs, + idempotencyKey: input.envelope.idempotencyKey, + error: null, + createdAt: now, + finishedAt: now, + }); + + await client.mutation(bridgeLogsApi.bridgeLogs.recordDomainEvent, { + workspaceId, + id: eventId, + requestId: input.context.requestId, + traceId: input.context.traceId, + commandId: input.envelope.commandId, + commandLogId, + eventType: `${input.envelope.name}.requested`, + aggregateType: input.envelope.target?.blockId ? "block" : "page", + aggregateId: + input.envelope.target?.blockId ?? input.envelope.target?.pageId ?? workspaceId, + eventVersion: 1, + status: "committed", + actorType: input.context.actor.actorType, + payload: { + request_id: input.context.requestId, + trace_id: input.context.traceId, + command_id: input.envelope.commandId, + command_name: input.envelope.name, + }, + createdAt: now, + }); +} diff --git a/wolai-frontend/src/lib/documents/bridge-server.ts b/wolai-frontend/src/lib/documents/bridge-server.ts new file mode 100644 index 00000000..26ba9b12 --- /dev/null +++ b/wolai-frontend/src/lib/documents/bridge-server.ts @@ -0,0 +1,81 @@ +import { headers } from "next/headers"; +import { ApiError } from "@/lib/api-utils"; + +type BridgeMeta = { + requestId: string; + traceId: string; + queryName: string; +}; + +type DocumentMetaResponse = { + doc: T; + meta: BridgeMeta; +}; + +function getServerRequestOrigin(headerList: Headers): string { + const forwardedProto = headerList.get("x-forwarded-proto")?.split(",")[0]?.trim(); + const forwardedHost = headerList.get("x-forwarded-host")?.split(",")[0]?.trim(); + const host = forwardedHost || headerList.get("host"); + + if (!host) { + throw new Error("缺少 host 头,无法构造 bridge 请求地址"); + } + + return `${forwardedProto || "http"}://${host}`; +} + +function copyHeaderIfPresent(target: Headers, source: Headers, name: string) { + const value = source.get(name); + if (value) { + target.set(name, value); + } +} + +export async function fetchDocumentMetaViaBridge(input: { + documentId: string; + workspaceId?: string | null; +}): Promise | null> { + const headerList = await headers(); + const requestHeaders = new Headers(); + const origin = getServerRequestOrigin(headerList); + const url = new URL("/api/documents/meta", origin); + + url.searchParams.set("documentId", input.documentId); + if (input.workspaceId?.trim()) { + url.searchParams.set("workspaceId", input.workspaceId.trim()); + } + + copyHeaderIfPresent(requestHeaders, headerList, "cookie"); + copyHeaderIfPresent(requestHeaders, headerList, "authorization"); + copyHeaderIfPresent(requestHeaders, headerList, "x-request-id"); + copyHeaderIfPresent(requestHeaders, headerList, "x-trace-id"); + copyHeaderIfPresent(requestHeaders, headerList, "x-session-id"); + copyHeaderIfPresent(requestHeaders, headerList, "x-source-channel"); + copyHeaderIfPresent(requestHeaders, headerList, "x-source-client"); + copyHeaderIfPresent(requestHeaders, headerList, "user-agent"); + + const response = await fetch(url, { + method: "GET", + headers: requestHeaders, + cache: "no-store", + }); + + if (response.status === 404) { + return null; + } + + if (!response.ok) { + let message = "加载页面元信息失败"; + try { + const payload = (await response.json()) as { error?: string }; + if (typeof payload?.error === "string" && payload.error.trim()) { + message = payload.error; + } + } catch { + // 说明:这里保留默认错误消息,避免 JSON 解析失败覆盖真实状态码。 + } + throw new ApiError(message, response.status); + } + + return (await response.json()) as DocumentMetaResponse; +} diff --git a/wolai-frontend/src/lib/documents/bridge.test.ts b/wolai-frontend/src/lib/documents/bridge.test.ts new file mode 100644 index 00000000..e2d3d163 --- /dev/null +++ b/wolai-frontend/src/lib/documents/bridge.test.ts @@ -0,0 +1,215 @@ +import { describe, expect, it, vi } from "vitest"; +import type { ConvexHttpClient } from "convex/browser"; + +vi.mock("@/lib/auth/authContext", () => ({ + HttpError: class HttpError extends Error { + status: number; + + constructor(status: number, message: string) { + super(message); + this.status = status; + } + }, + requireAuthContext: vi.fn(), +})); + +vi.mock("@/lib/api-utils", () => ({ + apiErrorResponse: vi.fn((message: string, status = 500, details?: unknown) => ({ + message, + status, + details, + })), +})); + +import { + DocumentBridgeError, + assertDocumentId, + assertBlockId, + assertNextBlock, + assertOptionsPatch, + assertStats, + assertTitle, + buildDocumentCommandEnvelope, + buildDocumentQueryEnvelope, + type BridgeContext, +} from "@/lib/documents/bridge"; +import { executeMetadataBridgeCommand } from "@/lib/documents/metadata-command-adapter"; + +vi.mock("@/lib/convex/route", () => ({ + getAuthedConvexClient: vi.fn(), +})); + +vi.mock("@/lib/documents/bridge-log", () => ({ + recordBridgeCommandArtifacts: vi.fn(), +})); + +const mockContext: BridgeContext = { + deploymentId: null, + projectId: null, + workspaceId: "ws_1", + requestId: "req_1", + traceId: "trace_1", + actor: { + actorType: "user", + actorId: "user_1", + sessionId: "sess_1", + }, + source: { + channel: "next-route", + client: "vitest", + }, + tenantId: null, + authToken: null, + idempotencyKey: "idem_1", + validateOnly: false, + dryRun: false, +}; + +describe("documents bridge helpers", () => { + it("assertDocumentId returns trimmed id", () => { + expect(assertDocumentId(" doc_1 ")).toBe("doc_1"); + }); + + it("assertDocumentId throws on empty value", () => { + expect(() => assertDocumentId(" ")).toThrow(DocumentBridgeError); + }); + + it("assertTitle normalizes empty title", () => { + expect(assertTitle(" ")).toBe("无标题"); + }); + + it("assertBlockId returns trimmed block id", () => { + expect(assertBlockId(" blk_1 ")).toBe("blk_1"); + }); + + it("assertNextBlock accepts plain object", () => { + expect(() => assertNextBlock({ id: "blk_1" })).not.toThrow(); + }); + + it("assertStats accepts finite numeric stats", () => { + expect(() => + assertStats({ + wordCount: 1, + characterCount: 2, + blockCount: 3, + todoTotal: 4, + todoDone: 5, + }), + ).not.toThrow(); + }); + + it("assertOptionsPatch accepts stable page options patch", () => { + expect(() => + assertOptionsPatch({ + showToc: true, + layoutDensity: "compact", + embedDefaultBlockId: null, + }), + ).not.toThrow(); + }); + + it("buildDocumentCommandEnvelope keeps context flags", () => { + const envelope = buildDocumentCommandEnvelope({ + name: "documents.save", + payload: { documentId: "doc_1" }, + context: mockContext, + target: { workspaceId: "ws_1", pageId: "doc_1" }, + }); + + expect(envelope.name).toBe("documents.save"); + expect(envelope.actor.actorId).toBe("user_1"); + expect(envelope.idempotencyKey).toBe("idem_1"); + expect(envelope.target?.pageId).toBe("doc_1"); + }); + + it("buildDocumentQueryEnvelope keeps payload", () => { + const envelope = buildDocumentQueryEnvelope({ + name: "documents.content.get", + payload: { documentId: "doc_1" }, + }); + + expect(envelope.payload).toEqual({ documentId: "doc_1" }); + }); + + it("executeMetadataBridgeCommand routes title update through adapter", async () => { + const mutation = vi.fn().mockResolvedValue({ ok: true }); + const { getAuthedConvexClient } = await import("@/lib/convex/route"); + vi.mocked(getAuthedConvexClient).mockResolvedValue({ + auth: { userId: "user_1" }, + client: { + mutation, + } as unknown as ConvexHttpClient, + }); + + const result = await executeMetadataBridgeCommand({ + context: mockContext, + envelope: buildDocumentCommandEnvelope({ + name: "documents.title.update", + payload: { + documentId: "doc_1", + workspaceId: "ws_1", + title: "新标题", + }, + context: mockContext, + target: { workspaceId: "ws_1", pageId: "doc_1" }, + }), + }); + + expect(mutation).toHaveBeenCalledTimes(1); + expect(mutation.mock.calls[0]?.[1]).toEqual({ + id: "doc_1", + title: "新标题", + }); + expect(result.commandName).toBe("documents.title.update"); + }); + + it("executeMetadataBridgeCommand routes options update through adapter", async () => { + const mutation = vi.fn().mockResolvedValue({ ok: true }); + const { getAuthedConvexClient } = await import("@/lib/convex/route"); + vi.mocked(getAuthedConvexClient).mockResolvedValue({ + auth: { userId: "user_1" }, + client: { + mutation, + } as unknown as ConvexHttpClient, + }); + + const result = await executeMetadataBridgeCommand({ + context: mockContext, + envelope: buildDocumentCommandEnvelope({ + name: "documents.options.update", + payload: { + documentId: "doc_1", + workspaceId: "ws_1", + options: { + showToc: true, + layoutDensity: "compact", + embedDefaultBlockId: null, + }, + }, + context: mockContext, + target: { workspaceId: "ws_1", pageId: "doc_1" }, + }), + }); + + expect(mutation).toHaveBeenCalledTimes(1); + expect(mutation.mock.calls[0]?.[1]).toEqual({ + id: "doc_1", + options: { + wideLayout: undefined, + smallText: undefined, + showHeadingNumbers: undefined, + showToc: true, + showStructure: undefined, + protectEditing: undefined, + showWordCount: undefined, + collapseBacklinks: undefined, + pageFont: undefined, + layoutDensity: "compact", + hideChildPages: undefined, + showBlockRefCount: undefined, + embedDefaultBlockId: null, + }, + }); + expect(result.commandName).toBe("documents.options.update"); + }); +}); diff --git a/wolai-frontend/src/lib/documents/bridge.ts b/wolai-frontend/src/lib/documents/bridge.ts new file mode 100644 index 00000000..4b1564d2 --- /dev/null +++ b/wolai-frontend/src/lib/documents/bridge.ts @@ -0,0 +1,345 @@ +import { randomUUID } from "crypto"; +import { HttpError, requireAuthContext } from "@/lib/auth/authContext"; +import { apiErrorResponse } from "@/lib/api-utils"; +import type { PageOptionsState } from "@/types/page-options"; + +export type BridgeActor = { + actorType: string; + actorId: string; + sessionId: string | null; +}; + +export type BridgeSource = { + channel: string; + client: string; +}; + +export type BridgeTarget = { + workspaceId?: string | null; + pageId?: string | null; + blockId?: string | null; +}; + +export type BridgeRequestMeta = { + idempotencyKey: string | null; + validateOnly: boolean; + dryRun: boolean; +}; + +export type BridgeContext = { + deploymentId: string | null; + projectId: string | null; + workspaceId: string | null; + requestId: string; + traceId: string; + actor: BridgeActor; + source: BridgeSource; + tenantId: string | null; + authToken: string | null; + idempotencyKey: string | null; + validateOnly: boolean; + dryRun: boolean; +}; + +export type BridgeErrorCode = + | "VALIDATION_ERROR" + | "UNAUTHORIZED" + | "FORBIDDEN" + | "NOT_FOUND" + | "TRANSPORT_ERROR" + | "REJECTED"; + +export class DocumentBridgeError extends Error { + constructor( + message: string, + public readonly status: number, + public readonly code: BridgeErrorCode, + public readonly details?: unknown, + ) { + super(message); + this.name = "DocumentBridgeError"; + } +} + +export type CommandEnvelope = { + name: string; + commandId: string; + idempotencyKey: string | null; + actor: BridgeActor; + source: BridgeSource; + target: BridgeTarget | null; + payload: T; + reason: string | null; + refs: string[]; + dryRun: boolean; + validateOnly: boolean; +}; + +export type QueryEnvelope = { + name: string; + payload: T; +}; + +function readHeaderValue(headerList: Headers, ...candidates: string[]): string | null { + for (const candidate of candidates) { + const value = headerList.get(candidate); + if (typeof value === "string" && value.trim()) { + return value.trim(); + } + } + return null; +} + +function toBooleanFlag(raw: string | null): boolean { + if (!raw) return false; + return raw === "1" || raw.toLowerCase() === "true"; +} + +function makeFallbackId(prefix: string): string { + return `${prefix}_${randomUUID()}`; +} + +export async function buildDocumentBridgeContext(input: { + request: Request; + workspaceId?: string | null; + idempotencyKey?: string | null; + validateOnly?: boolean; + dryRun?: boolean; +}): Promise { + let auth; + try { + auth = await requireAuthContext(); + } catch (error) { + if (error instanceof HttpError) { + throw new DocumentBridgeError(error.message || "未登录", error.status, "UNAUTHORIZED"); + } + throw new DocumentBridgeError("未登录", 401, "UNAUTHORIZED"); + } + + const headerList = input.request.headers; + const requestId = + readHeaderValue(headerList, "x-request-id", "x-mnote-request-id") ?? makeFallbackId("req"); + const traceId = + readHeaderValue(headerList, "x-trace-id", "x-mnote-trace-id", "x-request-id") ?? makeFallbackId("trace"); + const sessionId = readHeaderValue(headerList, "x-session-id", "x-mnote-session-id"); + const sourceChannel = readHeaderValue(headerList, "x-source-channel") ?? "next-route"; + const sourceClient = readHeaderValue(headerList, "x-source-client", "user-agent") ?? "wolai-frontend"; + const deploymentId = + readHeaderValue(headerList, "x-deployment-id") ?? process.env.VERCEL_DEPLOYMENT_ID ?? null; + const projectId = readHeaderValue(headerList, "x-project-id") ?? process.env.VERCEL_PROJECT_ID ?? null; + const tenantId = readHeaderValue(headerList, "x-tenant-id"); + const authToken = readHeaderValue(headerList, "authorization"); + const idempotencyKey = + input.idempotencyKey ?? readHeaderValue(headerList, "idempotency-key", "x-idempotency-key"); + const validateOnly = input.validateOnly ?? toBooleanFlag(readHeaderValue(headerList, "x-validate-only")); + const dryRun = input.dryRun ?? toBooleanFlag(readHeaderValue(headerList, "x-dry-run")); + + return { + deploymentId, + projectId, + workspaceId: input.workspaceId ?? null, + requestId, + traceId, + actor: { + actorType: "user", + actorId: auth.userId, + sessionId, + }, + source: { + channel: sourceChannel, + client: sourceClient, + }, + tenantId, + authToken, + idempotencyKey, + validateOnly, + dryRun, + }; +} + +export function buildDocumentCommandEnvelope(input: { + name: string; + payload: T; + context: BridgeContext; + target?: BridgeTarget | null; + reason?: string | null; + refs?: string[]; +}): CommandEnvelope { + return { + name: input.name, + commandId: makeFallbackId("cmd"), + idempotencyKey: input.context.idempotencyKey, + actor: input.context.actor, + source: input.context.source, + target: input.target ?? null, + payload: input.payload, + reason: input.reason ?? null, + refs: input.refs ?? [], + dryRun: input.context.dryRun, + validateOnly: input.context.validateOnly, + }; +} + +export function buildDocumentQueryEnvelope(input: { name: string; payload: T }): QueryEnvelope { + return { + name: input.name, + payload: input.payload, + }; +} + +export function assertDocumentId(documentId: string | null | undefined): string { + const normalized = typeof documentId === "string" ? documentId.trim() : ""; + if (!normalized) { + throw new DocumentBridgeError("缺少 documentId", 400, "VALIDATION_ERROR", [ + { field: "documentId", reason: "required" }, + ]); + } + return normalized; +} + +export function assertTitle(title: string | null | undefined): string { + if (typeof title !== "string") { + throw new DocumentBridgeError("缺少 title", 400, "VALIDATION_ERROR", [ + { field: "title", reason: "required" }, + ]); + } + const normalized = title.trim() || "无标题"; + return normalized; +} + +export function assertBlockId(blockId: string | null | undefined): string { + const normalized = typeof blockId === "string" ? blockId.trim() : ""; + if (!normalized) { + throw new DocumentBridgeError("缺少 blockId", 400, "VALIDATION_ERROR", [ + { field: "blockId", reason: "required" }, + ]); + } + return normalized; +} + +export function assertNextBlock(nextBlock: unknown): asserts nextBlock is Record { + if (!nextBlock || typeof nextBlock !== "object" || Array.isArray(nextBlock)) { + throw new DocumentBridgeError("缺少 nextBlock", 400, "VALIDATION_ERROR", [ + { field: "nextBlock", reason: "required object" }, + ]); + } +} + +export function assertStats(stats: unknown): asserts stats is { + wordCount: number; + characterCount: number; + blockCount: number; + todoTotal: number; + todoDone: number; +} { + if (!stats || typeof stats !== "object") { + throw new DocumentBridgeError("缺少 stats", 400, "VALIDATION_ERROR", [ + { field: "stats", reason: "required" }, + ]); + } + const record = stats as Record; + const fields = ["wordCount", "characterCount", "blockCount", "todoTotal", "todoDone"] as const; + for (const field of fields) { + if (typeof record[field] !== "number" || !Number.isFinite(record[field] as number)) { + throw new DocumentBridgeError("stats 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "must be finite number" }, + ]); + } + } +} + +const PAGE_FONT_VALUES = new Set(["default", "song", "kai"]); +const PAGE_LAYOUT_DENSITY_VALUES = new Set(["compact", "normal", "spacious"]); +const PAGE_OPTION_BOOLEAN_FIELDS = [ + "wideLayout", + "smallText", + "showHeadingNumbers", + "showToc", + "showStructure", + "protectEditing", + "showWordCount", + "collapseBacklinks", + "hideChildPages", + "showBlockRefCount", +] as const satisfies readonly (keyof PageOptionsState)[]; +const PAGE_OPTION_ALLOWED_FIELDS = new Set([ + ...PAGE_OPTION_BOOLEAN_FIELDS, + "pageFont", + "layoutDensity", + "embedDefaultBlockId", +]); + +export function assertOptionsPatch( + options: unknown, +): asserts options is Partial> { + if (!options || typeof options !== "object" || Array.isArray(options)) { + throw new DocumentBridgeError("缺少 options", 400, "VALIDATION_ERROR", [ + { field: "options", reason: "required object" }, + ]); + } + + const record = options as Record; + const entries = Object.entries(record); + if (entries.length === 0) { + throw new DocumentBridgeError("缺少 options", 400, "VALIDATION_ERROR", [ + { field: "options", reason: "must not be empty" }, + ]); + } + + for (const [field, value] of entries) { + if (!PAGE_OPTION_ALLOWED_FIELDS.has(field as keyof PageOptionsState)) { + throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "unexpected field" }, + ]); + } + + if ((PAGE_OPTION_BOOLEAN_FIELDS as readonly string[]).includes(field)) { + if (typeof value !== "boolean") { + throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "must be boolean" }, + ]); + } + continue; + } + + if (field === "pageFont") { + if (typeof value !== "string" || !PAGE_FONT_VALUES.has(value as PageOptionsState["pageFont"])) { + throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "must be one of default/song/kai" }, + ]); + } + continue; + } + + if (field === "layoutDensity") { + if ( + typeof value !== "string" || + !PAGE_LAYOUT_DENSITY_VALUES.has(value as PageOptionsState["layoutDensity"]) + ) { + throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "must be one of compact/normal/spacious" }, + ]); + } + continue; + } + + if (field === "embedDefaultBlockId" && value !== null && typeof value !== "string") { + throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [ + { field, reason: "must be string or null" }, + ]); + } + } +} + +export function documentBridgeErrorResponse(error: unknown) { + if (error instanceof DocumentBridgeError) { + return apiErrorResponse(error.message, error.status, { + code: error.code, + details: error.details, + }); + } + if (error instanceof HttpError) { + return apiErrorResponse(error.message || "未登录", error.status); + } + return apiErrorResponse(error instanceof Error ? error.message : "服务器错误", 500); +} diff --git a/wolai-frontend/src/lib/documents/document-record.test.ts b/wolai-frontend/src/lib/documents/document-record.test.ts new file mode 100644 index 00000000..33ad415e --- /dev/null +++ b/wolai-frontend/src/lib/documents/document-record.test.ts @@ -0,0 +1,178 @@ +import { describe, expect, it } from "vitest"; +import { + compareDocumentCanonicalOrder, + getCanonicalDocumentByBusinessId, + getCanonicalParentDocumentId, + pickCanonicalDocumentRecord, +} from "../../../convex/_utils/documentRecord"; + +describe("pickCanonicalDocumentRecord", () => { + it("同一 business id 出现重复记录时优先选择最新未删除记录", () => { + const selected = pickCanonicalDocumentRecord([ + { + _id: "doc_old", + deleted_at: null, + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:01.000Z", + }, + { + _id: "doc_deleted", + deleted_at: "2026-04-14T00:00:03.000Z", + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + { + _id: "doc_new", + deleted_at: null, + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:04.000Z", + }, + ]); + + expect(selected?._id).toBe("doc_new"); + }); + + it("全部已删除时仍稳定选择最新记录,避免 first() 命中漂移", () => { + const selected = pickCanonicalDocumentRecord([ + { + _id: "doc_deleted_old", + deleted_at: "2026-04-14T00:00:01.000Z", + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:01.000Z", + }, + { + _id: "doc_deleted_new", + deleted_at: "2026-04-14T00:00:03.000Z", + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + ]); + + expect(selected?._id).toBe("doc_deleted_new"); + }); + + it("时间戳相同时用 _id 稳定打破平手,避免 collect 后结果不稳定", () => { + const selected = pickCanonicalDocumentRecord([ + { + _id: "doc_b", + deleted_at: null, + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:00.000Z", + }, + { + _id: "doc_a", + deleted_at: null, + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:00.000Z", + }, + ]); + + expect(selected?._id).toBe("doc_a"); + }); +}); + +describe("compareDocumentCanonicalOrder", () => { + it("未删除记录始终排在已删除记录前面,供页面权限/内容读链复用", () => { + const records = [ + { + _id: "deleted", + deleted_at: "2026-04-14T00:00:03.000Z", + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + { + _id: "alive", + deleted_at: null, + created_at: "2026-04-14T00:00:01.000Z", + updated_at: "2026-04-14T00:00:01.000Z", + }, + ]; + + const sorted = [...records].sort(compareDocumentCanonicalOrder); + expect(sorted.map((record) => record._id)).toEqual(["alive", "deleted"]); + }); +}); + +describe("canonical document helper", () => { + const createCtx = (records: Array>) => ({ + db: { + query: () => ({ + withIndex: () => ({ + collect: async () => records, + }), + }), + }, + }); + + it("按 business id 查询时返回 canonical 记录,避免高风险读链命中旧记录", async () => { + const ctx = createCtx([ + { + _id: "doc_old", + id: "doc_1", + parent_id: "parent_old", + deleted_at: null, + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:01.000Z", + }, + { + _id: "doc_new", + id: "doc_1", + parent_id: "parent_new", + deleted_at: null, + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + ]); + + const doc = await getCanonicalDocumentByBusinessId(ctx, "doc_1"); + expect(doc?._id).toBe("doc_new"); + }); + + it("父链辅助函数返回 canonical 父页面 id,供共享/评论祖先扫描复用", async () => { + const ctx = createCtx([ + { + _id: "doc_old", + id: "doc_1", + parent_id: "parent_old", + deleted_at: null, + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:01.000Z", + }, + { + _id: "doc_new", + id: "doc_1", + parent_id: "parent_new", + deleted_at: null, + created_at: "2026-04-14T00:00:02.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + ]); + + const parentId = await getCanonicalParentDocumentId(ctx, "doc_1"); + expect(parentId).toBe("parent_new"); + }); + + it("父链辅助函数命中已删除旧记录时仍回退到最新未删除父级,供收藏祖先权限复用", async () => { + const ctx = createCtx([ + { + _id: "doc_deleted", + id: "doc_2", + parent_id: "parent_deleted", + deleted_at: "2026-04-14T00:00:04.000Z", + created_at: "2026-04-14T00:00:00.000Z", + updated_at: "2026-04-14T00:00:04.000Z", + }, + { + _id: "doc_alive", + id: "doc_2", + parent_id: "parent_alive", + deleted_at: null, + created_at: "2026-04-14T00:00:01.000Z", + updated_at: "2026-04-14T00:00:03.000Z", + }, + ]); + + const parentId = await getCanonicalParentDocumentId(ctx, "doc_2"); + expect(parentId).toBe("parent_alive"); + }); +}); diff --git a/wolai-frontend/src/lib/documents/metadata-command-adapter.ts b/wolai-frontend/src/lib/documents/metadata-command-adapter.ts new file mode 100644 index 00000000..2301e58d --- /dev/null +++ b/wolai-frontend/src/lib/documents/metadata-command-adapter.ts @@ -0,0 +1,120 @@ +import { api } from "@/lib/convex/api"; +import { getAuthedConvexClient } from "@/lib/convex/route"; +import type { CommandEnvelope, BridgeContext } from "@/lib/documents/bridge"; +import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log"; +import type { PageOptionsState } from "@/types/page-options"; + +export type DocumentTitleUpdatePayload = { + documentId: string; + workspaceId: string | null; + title: string; +}; + +export type DocumentStatsUpdatePayload = { + documentId: string; + workspaceId: string | null; + stats: { + wordCount: number; + characterCount: number; + blockCount: number; + todoTotal: number; + todoDone: number; + }; +}; + +export type DocumentOptionsUpdatePayload = { + documentId: string; + workspaceId: string | null; + options: Partial; +}; + +export type MetadataCommandExecutionResult = { + requestId: string; + traceId: string; + commandId: string; + commandName: string; +}; + +type MetadataWriteAdapter = { + convexMutation: unknown; + mapConvexArgs: (payload: TPayload) => Record; +}; + +function mapDocumentOptionsToConvexArgs(payload: DocumentOptionsUpdatePayload) { + return { + id: payload.documentId, + options: { + wideLayout: payload.options.wideLayout, + smallText: payload.options.smallText, + showHeadingNumbers: payload.options.showHeadingNumbers, + showToc: payload.options.showToc, + showStructure: payload.options.showStructure, + protectEditing: payload.options.protectEditing, + showWordCount: payload.options.showWordCount, + collapseBacklinks: payload.options.collapseBacklinks, + pageFont: payload.options.pageFont, + layoutDensity: payload.options.layoutDensity, + hideChildPages: payload.options.hideChildPages, + showBlockRefCount: payload.options.showBlockRefCount, + embedDefaultBlockId: + typeof payload.options.embedDefaultBlockId === "string" ? payload.options.embedDefaultBlockId : null, + }, + }; +} + +const metadataWriteAdapters: Record> = { + "documents.title.update": { + convexMutation: api.documents.updateTitle, + mapConvexArgs: (payload: DocumentTitleUpdatePayload) => ({ + id: payload.documentId, + title: payload.title, + }), + }, + "documents.stats.update": { + convexMutation: api.documents.updateStats, + mapConvexArgs: (payload: DocumentStatsUpdatePayload) => ({ + id: payload.documentId, + wordCount: payload.stats.wordCount, + characterCount: payload.stats.characterCount, + blockCount: payload.stats.blockCount, + todoTotal: payload.stats.todoTotal, + todoDone: payload.stats.todoDone, + }), + }, + "documents.options.update": { + convexMutation: api.documents.updateOptions, + mapConvexArgs: mapDocumentOptionsToConvexArgs, + }, +}; + +function getMetadataWriteAdapter(commandName: string): MetadataWriteAdapter { + const adapter = metadataWriteAdapters[commandName]; + if (!adapter) { + throw new Error(`未注册页面元信息命令适配器: ${commandName}`); + } + return adapter as MetadataWriteAdapter; +} + +export async function executeMetadataBridgeCommand(input: { + context: BridgeContext; + envelope: CommandEnvelope; +}): Promise { + const adapter = getMetadataWriteAdapter(input.envelope.name); + const { client } = await getAuthedConvexClient(); + + await client.mutation( + adapter.convexMutation as Parameters[0], + adapter.mapConvexArgs(input.envelope.payload) as Parameters[1], + ); + await recordBridgeCommandArtifacts({ + context: input.context, + envelope: input.envelope, + }); + + return { + requestId: input.context.requestId, + traceId: input.context.traceId, + commandId: input.envelope.commandId, + commandName: input.envelope.name, + }; +} diff --git a/wolai-frontend/src/lib/server/sidebar-data.ts b/wolai-frontend/src/lib/server/sidebar-data.ts new file mode 100644 index 00000000..41cfda3f --- /dev/null +++ b/wolai-frontend/src/lib/server/sidebar-data.ts @@ -0,0 +1,94 @@ +import { randomUUID } from "crypto"; +import type { ConvexHttpClient } from "convex/browser"; +import type { SidebarInitialData } from "@/components/sidebar/types"; +import type { DocumentRecord } from "@/lib/documents"; +import { api } from "@/lib/convex/api"; +import { buildSidebarInitialData } from "@/lib/sidebar-data"; +import type { WorkspaceSummary } from "@/lib/workspaces"; + +type LoadSidebarDataFromConvexInput = { + client: ConvexHttpClient; + userId: string; + fallbackName: string; + requestedWorkspaceId?: string | null; +}; + +type LoadSidebarDataFromConvexResult = { + workspaces: WorkspaceSummary[]; + activeWorkspaceId: string; + targetWorkspaceId: string | null; + sidebarInitialData: SidebarInitialData | null; + documents: DocumentRecord[]; +}; + +export async function loadSidebarDataFromConvex( + input: LoadSidebarDataFromConvexInput, +): Promise { + const bootstrap = await input.client.mutation(api.workspaces.ensureDefaultWorkspace, { + fallbackName: input.fallbackName, + workspaceIdIfCreate: randomUUID(), + }); + + const summaries = await input.client.query(api.workspaces.fetchWorkspaceSummaries, {}); + const workspaces = summaries.workspaces.length > 0 ? summaries.workspaces : bootstrap.workspaces; + const activeWorkspaceId = summaries.activeWorkspaceId || bootstrap.activeWorkspaceId; + const targetWorkspaceId = input.requestedWorkspaceId?.trim() || activeWorkspaceId || null; + + if (!targetWorkspaceId) { + return { + workspaces, + activeWorkspaceId, + targetWorkspaceId: null, + sidebarInitialData: null, + documents: [], + }; + } + + const [documents, trashedDocuments, mindmaps, mediaAssets, trashedMediaAssets, tables] = await Promise.all([ + input.client.query(api.documents.listByWorkspace, { + workspaceId: targetWorkspaceId, + }), + input.client.query(api.documents.listTrashedByWorkspace, { + workspaceId: targetWorkspaceId, + }), + input.client.query(api.mindmaps.listByWorkspace, { + workspaceId: targetWorkspaceId, + includeDeleted: true, + }), + input.client.query(api.mediaAssets.listByWorkspace, { + userId: input.userId, + workspaceId: targetWorkspaceId, + limit: 200, + }), + input.client.query(api.mediaAssets.listDeletedByWorkspace, { + userId: input.userId, + workspaceId: targetWorkspaceId, + limit: 2000, + }), + input.client.query(api.tables.listByWorkspaceForSearch, { + userId: input.userId, + workspaceId: targetWorkspaceId, + includeArchived: true, + limit: 3000, + }), + ]); + + const normalizedDocuments = documents as DocumentRecord[]; + + return { + workspaces, + activeWorkspaceId, + targetWorkspaceId, + sidebarInitialData: buildSidebarInitialData({ + activeWorkspaceId: targetWorkspaceId, + workspaces, + documents: normalizedDocuments, + trashedDocuments, + mindmaps: mindmaps ?? [], + mediaAssets: mediaAssets ?? [], + trashedMediaAssets: trashedMediaAssets ?? [], + tables: tables ?? [], + }), + documents: normalizedDocuments, + }; +} diff --git a/wolai-frontend/src/lib/sidebar-data.test.ts b/wolai-frontend/src/lib/sidebar-data.test.ts new file mode 100644 index 00000000..1db32c9c --- /dev/null +++ b/wolai-frontend/src/lib/sidebar-data.test.ts @@ -0,0 +1,143 @@ +import { describe, expect, it } from "vitest"; +import type { DocumentRecord } from "@/lib/documents"; +import type { WorkspaceSummary } from "@/lib/workspaces"; +import type { SidebarInitialData } from "@/components/sidebar/types"; +import { buildSidebarInitialData, extractMindmapImageAssetIdsFromData } from "@/lib/sidebar-data"; + +describe("extractMindmapImageAssetIdsFromData", () => { + it("提取导图节点里的 asset 图片引用并去重", () => { + const ids = extractMindmapImageAssetIdsFromData({ + root: { + data: { image: "asset:img_1" }, + children: [ + { image: { url: "asset:img_2" } }, + { data: { image: { url: "asset:img_1" } } }, + ], + }, + }); + + expect(ids).toEqual(["img_1", "img_2"]); + }); +}); + +describe("buildSidebarInitialData", () => { + it("统一组装 mindmap/table/media 相关侧边栏数据", () => { + const workspaces: WorkspaceSummary[] = [ + { + id: "ws_1", + name: "工作区", + type: "personal", + iconUrl: null, + memberCount: 1, + isDefault: true, + }, + ]; + + const documents: DocumentRecord[] = [ + { + id: "doc_1", + workspace_id: "ws_1", + title: "页面 1", + parent_id: null, + sort_order: 1, + is_starred: false, + access_scope: "private", + is_template: false, + created_at: "2026-04-14T00:00:00Z", + updated_at: null, + }, + ]; + + const trashedDocuments: SidebarInitialData["trashedDocuments"] = []; + + const payload = buildSidebarInitialData({ + activeWorkspaceId: "ws_1", + workspaces, + documents, + trashedDocuments, + mediaAssets: [ + { + id: "asset_file_1", + workspace_id: "ws_1", + document_id: "doc_1", + asset_type: "file", + file_url: "/file/1", + thumbnail_url: null, + bucket: null, + storage_path: null, + file_name: "a.txt", + file_size: null, + mime_type: "text/plain", + ocr_payload: undefined, + ocr_strategy: null, + ocr_text: null, + ocr_status: null, + signed_url: null, + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + }, + ], + trashedMediaAssets: [], + mindmaps: [ + { + mindmap_id: "mind_1", + workspace_id: "ws_1", + document_id: "doc_1", + data: { + root: { + data: { image: "asset:img_a" }, + children: [{ image: { url: "asset:img_b" } }], + }, + }, + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + deleted_at: null, + }, + { + mindmap_id: "mind_2", + workspace_id: "ws_1", + document_id: "doc_1", + data: null, + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + deleted_at: "2026-04-14T01:00:00Z", + deleted_by: "user_1", + }, + ], + tables: [ + { + id: "table_1", + workspace_id: "ws_1", + document_id: "doc_1", + title: "预算", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + is_archived: false, + }, + { + id: "table_2", + workspace_id: "ws_1", + document_id: "doc_1", + title: "归档表格", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + deleted_at: "2026-04-14T01:00:00Z", + deleted_by: "user_1", + purged_at: null, + is_archived: true, + }, + ], + }); + + expect(payload.activeWorkspaceId).toBe("ws_1"); + expect(payload.mindmapDocs).toEqual(["doc_1"]); + expect(payload.mindmapAssetChildren).toEqual({ + mind_1: ["img_a", "img_b"], + }); + expect(payload.mindmapAssets?.map((item) => item.id)).toEqual(["mind_1"]); + expect(payload.trashedMindmapAssets?.map((item) => item.id)).toEqual(["mind_2"]); + expect(payload.tableAssets?.map((item) => item.file_name)).toEqual(["预算.luckysheet"]); + expect(payload.trashedTableAssets?.map((item) => item.id)).toEqual(["table_2"]); + expect(payload.mediaAssets?.map((item) => item.id)).toEqual(["asset_file_1"]); + }); +}); diff --git a/wolai-frontend/src/lib/sidebar-data.ts b/wolai-frontend/src/lib/sidebar-data.ts new file mode 100644 index 00000000..5f2d7449 --- /dev/null +++ b/wolai-frontend/src/lib/sidebar-data.ts @@ -0,0 +1,189 @@ +import type { SidebarInitialData } from "@/components/sidebar/types"; +import type { DocumentRecord } from "@/lib/documents"; +import type { WorkspaceSummary } from "@/lib/workspaces"; +import type { MediaAsset } from "@/types/media"; + +type MindmapRow = { + mindmap_id: string; + workspace_id?: string | null; + document_id: string; + data?: unknown; + created_at?: string | null; + updated_at?: string | null; + deleted_at?: string | null; + deleted_by?: string | null; +}; + +type TableRow = { + id: string; + workspace_id?: string | null; + document_id: string; + title?: string | null; + created_at?: string | null; + updated_at?: string | null; + deleted_at?: string | null; + deleted_by?: string | null; + purged_at?: string | null; + is_archived?: boolean | null; +}; + +type SidebarDatasetInput = { + activeWorkspaceId: string; + workspaces: WorkspaceSummary[]; + documents: DocumentRecord[]; + trashedDocuments: SidebarInitialData["trashedDocuments"]; + mindmaps: MindmapRow[]; + mediaAssets?: MediaAsset[] | null; + trashedMediaAssets?: MediaAsset[] | null; + tables?: TableRow[] | null; +}; + +function normalizeStringArray(values: Iterable): string[] { + return Array.from(new Set(values)).filter((value) => value.trim().length > 0); +} + +export function extractMindmapImageAssetIdsFromData(input: unknown): string[] { + const root = (() => { + if (!input || typeof input !== "object") return input; + const record = input as Record; + return record && typeof record === "object" && "root" in record ? record.root : input; + })(); + + const ids: string[] = []; + const seen = new Set(); + + const push = (value: unknown) => { + if (typeof value !== "string") return; + if (!value.startsWith("asset:")) return; + const id = value.slice("asset:".length).trim(); + if (!id || seen.has(id)) return; + seen.add(id); + ids.push(id); + }; + + const get = (obj: unknown, key: string): unknown => { + if (!obj || typeof obj !== "object") return undefined; + return (obj as Record)[key]; + }; + + const walk = (node: unknown) => { + if (!node || typeof node !== "object") return; + + const data = get(node, "data"); + const image = get(node, "image"); + + push(get(data, "image")); + push(image); + push(get(image, "url")); + push(get(get(data, "image"), "url")); + + const children = get(node, "children"); + if (Array.isArray(children)) { + children.forEach(walk); + } + }; + + walk(root); + return ids; +} + +function toMindmapAsset(row: MindmapRow, workspaceId: string): MediaAsset { + const isLegacy = row.mindmap_id.startsWith("legacy-"); + return { + id: row.mindmap_id, + workspace_id: row.workspace_id ?? workspaceId, + document_id: row.document_id, + asset_type: "mindmap", + file_url: null, + thumbnail_url: null, + bucket: null, + storage_path: null, + file_name: isLegacy ? "mindmap.json" : `mindmap-${row.mindmap_id}.json`, + file_size: null, + mime_type: "application/json", + ocr_payload: undefined, + ocr_strategy: null, + ocr_text: null, + ocr_status: null, + signed_url: null, + created_at: row.created_at ?? "", + updated_at: row.updated_at ?? "", + }; +} + +function toTrashedMindmapAsset(row: MindmapRow, workspaceId: string): MediaAsset { + return { + ...toMindmapAsset(row, workspaceId), + deleted_at: row.deleted_at ?? null, + deleted_by: row.deleted_by ?? null, + purged_at: null, + }; +} + +function toTableAsset(row: TableRow, workspaceId: string): MediaAsset { + const base = String(row.title ?? "未命名表格").trim() || "未命名表格"; + const fileName = base.toLowerCase().endsWith(".luckysheet") ? base : `${base}.luckysheet`; + return { + id: row.id, + workspace_id: row.workspace_id ?? workspaceId, + document_id: row.document_id, + asset_type: "luckysheet", + file_url: null, + thumbnail_url: null, + bucket: null, + storage_path: null, + file_name: fileName, + file_size: null, + mime_type: "application/json", + ocr_payload: undefined, + ocr_strategy: null, + ocr_text: null, + ocr_status: null, + signed_url: null, + created_at: row.created_at ?? "", + updated_at: row.updated_at ?? "", + }; +} + +function toTrashedTableAsset(row: TableRow, workspaceId: string): MediaAsset { + return { + ...toTableAsset(row, workspaceId), + deleted_at: row.deleted_at ?? row.updated_at ?? null, + deleted_by: row.deleted_by ?? null, + purged_at: row.purged_at ?? null, + }; +} + +export function buildSidebarInitialData(input: SidebarDatasetInput): SidebarInitialData { + const activeMindmaps = input.mindmaps.filter((row) => !row.deleted_at); + const trashedMindmaps = input.mindmaps.filter((row) => Boolean(row.deleted_at)); + const activeTables = (input.tables ?? []).filter((row) => !row.is_archived); + const trashedTables = (input.tables ?? []).filter((row) => Boolean(row.is_archived)); + + const mindmapAssetChildren: Record = {}; + activeMindmaps.forEach((row) => { + const ids = extractMindmapImageAssetIdsFromData(row.data); + if (ids.length > 0) { + mindmapAssetChildren[row.mindmap_id] = ids; + } + }); + + return { + activeWorkspaceId: input.activeWorkspaceId, + workspaces: input.workspaces, + documents: input.documents, + trashedDocuments: input.trashedDocuments, + trashedMediaAssets: [...(input.trashedMediaAssets ?? [])], + trashedMindmapAssets: trashedMindmaps.map((row) => + toTrashedMindmapAsset(row, input.activeWorkspaceId), + ), + trashedTableAssets: trashedTables.map((row) => + toTrashedTableAsset(row, input.activeWorkspaceId), + ), + mindmapDocs: normalizeStringArray(activeMindmaps.map((row) => row.document_id)), + mindmapAssets: activeMindmaps.map((row) => toMindmapAsset(row, input.activeWorkspaceId)), + mindmapAssetChildren, + tableAssets: activeTables.map((row) => toTableAsset(row, input.activeWorkspaceId)), + mediaAssets: [...(input.mediaAssets ?? [])], + }; +}