@polderlabs/bizar 3.23.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -16
- package/bizar-dash/.bizar/activity.log +3 -0
- package/bizar-dash/.bizar/graph/.graphify_analysis.json +1540 -0
- package/bizar-dash/.bizar/graph/.graphify_labels.json +1 -0
- package/bizar-dash/.bizar/graph/GRAPH_REPORT.md +404 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/0661b816358db84dfdb7f10443db8a61152fd20b3fee950caf8fbf32920a1790.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/16da06d774142a6e5d74829ad982531286a6193ef798de8f593db029e123ec32.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/27e5697c01254c69c64c5a33896669073b40afb5ba88375102ae77fa5404136d.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/532997898027254aa4c0932243dc71d2f55888181232b65974961934997dc85b.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/84e3451a64c7f99f0cd6a88b0748d7426780486f53ab4935b7476b28c08f8293.json +1 -0
- package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/88e5ea5f3e78fa69353fe9e6a8d1a05391d54605e25600cb125531eb5a6432ff.json +1 -0
- package/bizar-dash/.bizar/graph/cache/stat-index.json +1 -0
- package/bizar-dash/.bizar/graph/graph.html +307 -0
- package/bizar-dash/.bizar/graph/graph.json +40267 -0
- package/bizar-dash/.bizar/graph/manifest.json +762 -0
- package/bizar-dash/.graphifyignore +40 -0
- package/bizar-dash/.obsidian/README.md +14 -0
- package/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
- package/bizar-dash/.obsidian/vault.json +7 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
- package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
- package/bizar-dash/CHANGELOG.md +474 -0
- package/bizar-dash/DESIGN.md +136 -0
- package/bizar-dash/dist/assets/main-B3RgW6FS.js +331 -0
- package/bizar-dash/dist/assets/main-B3RgW6FS.js.map +1 -0
- package/bizar-dash/dist/assets/main-DlJ8wgLR.css +1 -0
- package/bizar-dash/dist/assets/mobile-CsZQAswA.css +1 -0
- package/bizar-dash/dist/assets/mobile-DbxIw8BG.js +2 -0
- package/bizar-dash/dist/assets/mobile-DbxIw8BG.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js +354 -0
- package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js.map +1 -0
- package/bizar-dash/dist/index.html +40 -0
- package/bizar-dash/dist/mobile.html +22 -0
- package/bizar-dash/plans/debug-plan/comments.json +1 -0
- package/bizar-dash/plans/debug-plan/meta.json +10 -0
- package/bizar-dash/plans/debug-plan/plan.json +174 -0
- package/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
- package/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
- package/bizar-dash/src/cli.mjs +522 -0
- package/bizar-dash/src/server/activity-log.mjs +174 -0
- package/bizar-dash/src/server/agents-store.mjs +465 -0
- package/bizar-dash/src/server/api.mjs +138 -0
- package/bizar-dash/src/server/artifact-template.html +810 -0
- package/bizar-dash/src/server/artifacts-store.mjs +733 -0
- package/bizar-dash/src/server/auth.mjs +381 -0
- package/bizar-dash/src/server/background-store.mjs +447 -0
- package/bizar-dash/src/server/bg-poller.mjs +305 -0
- package/bizar-dash/src/server/bg-retry.mjs +574 -0
- package/bizar-dash/src/server/browser.mjs +40 -0
- package/bizar-dash/src/server/diagnostics-store.mjs +148 -0
- package/bizar-dash/src/server/dialog-poller.mjs +105 -0
- package/bizar-dash/src/server/dialog-store.mjs +177 -0
- package/bizar-dash/src/server/glyphs/mdx-compiler.mjs +583 -0
- package/bizar-dash/src/server/lib/path-safe.mjs +283 -0
- package/bizar-dash/src/server/memory-git.mjs +329 -0
- package/bizar-dash/src/server/memory-lightrag.mjs +767 -0
- package/bizar-dash/src/server/memory-schema.mjs +145 -0
- package/bizar-dash/src/server/memory-secrets.mjs +84 -0
- package/bizar-dash/src/server/memory-store.mjs +471 -0
- package/bizar-dash/src/server/mod-security.mjs +398 -0
- package/bizar-dash/src/server/mods-loader.mjs +1067 -0
- package/bizar-dash/src/server/notifications-store.mjs +243 -0
- package/bizar-dash/src/server/obsidian-store.mjs +318 -0
- package/bizar-dash/src/server/opencode-runner.mjs +279 -0
- package/bizar-dash/src/server/pair-store.mjs +138 -0
- package/bizar-dash/src/server/projects-store.mjs +364 -0
- package/bizar-dash/src/server/providers-store.mjs +789 -0
- package/bizar-dash/src/server/routes/_shared.mjs +415 -0
- package/bizar-dash/src/server/routes/activity.mjs +110 -0
- package/bizar-dash/src/server/routes/agents.mjs +134 -0
- package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
- package/bizar-dash/src/server/routes/auth.mjs +69 -0
- package/bizar-dash/src/server/routes/background.mjs +165 -0
- package/bizar-dash/src/server/routes/chat.mjs +485 -0
- package/bizar-dash/src/server/routes/config.mjs +129 -0
- package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
- package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
- package/bizar-dash/src/server/routes/fs.mjs +429 -0
- package/bizar-dash/src/server/routes/history.mjs +78 -0
- package/bizar-dash/src/server/routes/memory.mjs +658 -0
- package/bizar-dash/src/server/routes/misc.mjs +89 -0
- package/bizar-dash/src/server/routes/mods.mjs +359 -0
- package/bizar-dash/src/server/routes/notifications.mjs +54 -0
- package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
- package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
- package/bizar-dash/src/server/routes/overview.mjs +91 -0
- package/bizar-dash/src/server/routes/pair.mjs +67 -0
- package/bizar-dash/src/server/routes/projects.mjs +175 -0
- package/bizar-dash/src/server/routes/providers.mjs +47 -0
- package/bizar-dash/src/server/routes/schedules.mjs +104 -0
- package/bizar-dash/src/server/routes/settings.mjs +51 -0
- package/bizar-dash/src/server/routes/skills.mjs +76 -0
- package/bizar-dash/src/server/routes/tasks.mjs +468 -0
- package/bizar-dash/src/server/routes/themes.mjs +51 -0
- package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
- package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
- package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
- package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
- package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
- package/bizar-dash/src/server/schedules-runner.mjs +369 -0
- package/bizar-dash/src/server/schedules-store.mjs +268 -0
- package/bizar-dash/src/server/search-store.mjs +220 -0
- package/bizar-dash/src/server/serve-info.mjs +684 -0
- package/bizar-dash/src/server/server.mjs +796 -0
- package/bizar-dash/src/server/settings-store.mjs +45 -0
- package/bizar-dash/src/server/skills-store.mjs +300 -0
- package/bizar-dash/src/server/state.mjs +437 -0
- package/bizar-dash/src/server/tailscale-store.mjs +113 -0
- package/bizar-dash/src/server/task-delegator.mjs +824 -0
- package/bizar-dash/src/server/tasks-store.mjs +508 -0
- package/bizar-dash/src/server/tui.mjs +923 -0
- package/bizar-dash/src/server/update-store.mjs +168 -0
- package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
- package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
- package/bizar-dash/src/server/watcher.mjs +81 -0
- package/bizar-dash/src/server/yaml.mjs +238 -0
- package/bizar-dash/src/web/App.tsx +718 -0
- package/bizar-dash/src/web/MobileApp.tsx +408 -0
- package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
- package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
- package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
- package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
- package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
- package/bizar-dash/src/web/components/Button.tsx +56 -0
- package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
- package/bizar-dash/src/web/components/Card.tsx +40 -0
- package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
- package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
- package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
- package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
- package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
- package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
- package/bizar-dash/src/web/components/Modal.tsx +215 -0
- package/bizar-dash/src/web/components/Notifications.tsx +241 -0
- package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
- package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
- package/bizar-dash/src/web/components/Spinner.tsx +19 -0
- package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
- package/bizar-dash/src/web/components/Tag.tsx +28 -0
- package/bizar-dash/src/web/components/Toast.tsx +150 -0
- package/bizar-dash/src/web/components/Topbar.tsx +368 -0
- package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
- package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
- package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
- package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
- package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
- package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
- package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
- package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
- package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
- package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
- package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
- package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
- package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
- package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
- package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
- package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
- package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
- package/bizar-dash/src/web/components/chat/index.ts +20 -0
- package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
- package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
- package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
- package/bizar-dash/src/web/index.html +37 -0
- package/bizar-dash/src/web/lib/api.ts +193 -0
- package/bizar-dash/src/web/lib/markdown.tsx +59 -0
- package/bizar-dash/src/web/lib/types.ts +668 -0
- package/bizar-dash/src/web/lib/utils.ts +114 -0
- package/bizar-dash/src/web/lib/ws.ts +176 -0
- package/bizar-dash/src/web/main.tsx +34 -0
- package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
- package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
- package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
- package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
- package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
- package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
- package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
- package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
- package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
- package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
- package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
- package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
- package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
- package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
- package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
- package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
- package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
- package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
- package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
- package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
- package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
- package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
- package/bizar-dash/src/web/mobile.html +20 -0
- package/bizar-dash/src/web/mobile.tsx +13 -0
- package/bizar-dash/src/web/styles/chat.css +198 -0
- package/bizar-dash/src/web/styles/glyphs.css +960 -0
- package/bizar-dash/src/web/styles/main.css +8855 -0
- package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
- package/bizar-dash/src/web/styles/mobile.css +1960 -0
- package/bizar-dash/src/web/views/Activity.tsx +1203 -0
- package/bizar-dash/src/web/views/Agents.tsx +701 -0
- package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
- package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
- package/bizar-dash/src/web/views/Chat.tsx +190 -0
- package/bizar-dash/src/web/views/Config.tsx +2063 -0
- package/bizar-dash/src/web/views/History.tsx +335 -0
- package/bizar-dash/src/web/views/ModView.tsx +207 -0
- package/bizar-dash/src/web/views/Mods.tsx +693 -0
- package/bizar-dash/src/web/views/Overview.tsx +812 -0
- package/bizar-dash/src/web/views/Providers.tsx +302 -0
- package/bizar-dash/src/web/views/Schedules.tsx +802 -0
- package/bizar-dash/src/web/views/Settings.tsx +1787 -0
- package/bizar-dash/src/web/views/Skills.tsx +431 -0
- package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
- package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
- package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
- package/bizar-dash/templates/mod/FORMAT.md +76 -0
- package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
- package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
- package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
- package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
- package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
- package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
- package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
- package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
- package/bizar-dash/tests/memory-cli.test.mjs +542 -0
- package/bizar-dash/tests/memory-config.test.mjs +422 -0
- package/bizar-dash/tests/memory-git.test.mjs +246 -0
- package/bizar-dash/tests/memory-lightrag.test.mjs +153 -0
- package/bizar-dash/tests/memory-protocol-drift.test.mjs +45 -0
- package/bizar-dash/tests/memory-schema.test.mjs +198 -0
- package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
- package/bizar-dash/tests/memory-store.test.mjs +390 -0
- package/bizar-dash/tests/memory-sync.test.mjs +141 -0
- package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
- package/bizar-dash/tests/mod-security.test.mjs +273 -0
- package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
- package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
- package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
- package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
- package/bizar-dash/tests/path-safe.test.mjs +108 -0
- package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
- package/bizar-dash/tests/smoke-v2.mjs +212 -0
- package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
- package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
- package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
- package/bizar-dash/tests/yaml.test.mjs +128 -0
- package/bizar-dash/tsconfig.json +23 -0
- package/bizar-dash/vite.config.ts +27 -0
- package/cli/atomic.mjs +73 -0
- package/cli/banner.mjs +1 -1
- package/cli/bin.mjs +44 -47
- package/cli/bootstrap.mjs +1 -1
- package/cli/copy.mjs +25 -16
- package/cli/dev-link.mjs +17 -1
- package/cli/doctor.mjs +4 -4
- package/cli/doctor.test.mjs +2 -2
- package/cli/init.mjs +135 -1
- package/cli/install.mjs +31 -39
- package/cli/memory-constants.mjs +59 -0
- package/cli/memory.mjs +1436 -0
- package/cli/service.mjs +5 -6
- package/cli/utils.mjs +6 -3
- package/config/AGENTS.md +7 -7
- package/config/agents/_shared/AGENT_BASELINE.md +59 -61
- package/config/opencode.json +13 -38
- package/config/skills/memory-protocol/SKILL.md +105 -0
- package/config/skills/obsidian/SKILL.md +270 -65
- package/install.sh +59 -19
- package/package.json +49 -14
- package/packages/sdk/LICENSE +21 -0
- package/packages/sdk/README.md +80 -0
- package/packages/sdk/src/client.ts +183 -0
- package/packages/sdk/src/errors.ts +120 -0
- package/packages/sdk/src/events.ts +153 -0
- package/packages/sdk/src/index.ts +63 -0
- package/packages/sdk/src/types.ts +176 -0
- package/packages/sdk/src/version.ts +4 -0
- package/packages/sdk/tests/client.test.ts +217 -0
- package/packages/sdk/tests/errors.test.ts +108 -0
- package/packages/sdk/tests/events.test.ts +139 -0
- package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
- package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
- package/packages/sdk/tsconfig.json +25 -0
- package/packages/sdk/vitest.config.ts +9 -0
- package/plugins/bizar/LICENSE +21 -0
- package/plugins/bizar/README.md +448 -0
- package/plugins/bizar/dist/index.js +29901 -0
- package/plugins/bizar/index.ts +1491 -0
- package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
- package/plugins/bizar/src/background-state.ts +551 -0
- package/plugins/bizar/src/background.ts +1250 -0
- package/plugins/bizar/src/commands-impl.ts +464 -0
- package/plugins/bizar/src/commands.ts +1168 -0
- package/plugins/bizar/src/dashboard-client.ts +233 -0
- package/plugins/bizar/src/event-stream.ts +606 -0
- package/plugins/bizar/src/fingerprint.ts +120 -0
- package/plugins/bizar/src/handoff.ts +79 -0
- package/plugins/bizar/src/http-client.ts +467 -0
- package/plugins/bizar/src/key-rotation.ts +218 -0
- package/plugins/bizar/src/logger.ts +144 -0
- package/plugins/bizar/src/loop.ts +176 -0
- package/plugins/bizar/src/opencode-runner.ts +390 -0
- package/plugins/bizar/src/options.ts +421 -0
- package/plugins/bizar/src/plan-fs.ts +323 -0
- package/plugins/bizar/src/reasoning-clean.ts +454 -0
- package/plugins/bizar/src/report.ts +178 -0
- package/plugins/bizar/src/research-prompt.ts +35 -0
- package/plugins/bizar/src/serve-info.ts +228 -0
- package/plugins/bizar/src/serve.ts +496 -0
- package/plugins/bizar/src/settings.ts +349 -0
- package/plugins/bizar/src/state.ts +298 -0
- package/plugins/bizar/src/tools/bg-collect.ts +104 -0
- package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
- package/plugins/bizar/src/tools/bg-kill.ts +87 -0
- package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
- package/plugins/bizar/src/tools/bg-status.ts +99 -0
- package/plugins/bizar/src/tools/open-kb.ts +191 -0
- package/plugins/bizar/src/tools/plan-action.ts +767 -0
- package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
- package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
- package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
- package/plugins/bizar/tests/background-state.test.ts +277 -0
- package/plugins/bizar/tests/background.test.ts +402 -0
- package/plugins/bizar/tests/block.test.ts +195 -0
- package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
- package/plugins/bizar/tests/commands-impl.test.ts +442 -0
- package/plugins/bizar/tests/commands.test.ts +584 -0
- package/plugins/bizar/tests/config.test.ts +122 -0
- package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
- package/plugins/bizar/tests/dispose.test.ts +336 -0
- package/plugins/bizar/tests/event-stream.test.ts +409 -0
- package/plugins/bizar/tests/event.test.ts +262 -0
- package/plugins/bizar/tests/fingerprint.test.ts +162 -0
- package/plugins/bizar/tests/http-client.test.ts +404 -0
- package/plugins/bizar/tests/init-helpers.test.ts +203 -0
- package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
- package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
- package/plugins/bizar/tests/key-rotation.test.ts +396 -0
- package/plugins/bizar/tests/loop.test.ts +397 -0
- package/plugins/bizar/tests/options.test.ts +276 -0
- package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
- package/plugins/bizar/tests/serve.test.ts +339 -0
- package/plugins/bizar/tests/settings.test.ts +351 -0
- package/plugins/bizar/tests/stall-think.test.ts +750 -0
- package/plugins/bizar/tests/state.test.ts +276 -0
- package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
- package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
- package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
- package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
- package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
- package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
- package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
- package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
- package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
- package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
- package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
- package/plugins/bizar/tsconfig.json +29 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/server/glyphs/mdx-compiler.mjs
|
|
3
|
+
*
|
|
4
|
+
* v3.21.0 — Server-side MDX compile for Glyphs (the visual artifact /
|
|
5
|
+
* plan / recap system). Takes an MDX source string and returns a
|
|
6
|
+
* JSON-serializable "compiled glyph" object that the browser can
|
|
7
|
+
* render with React components per block type.
|
|
8
|
+
*
|
|
9
|
+
* Why this approach instead of @mdx-js/mdx compile() + evaluate()?
|
|
10
|
+
* 1. evaluate() returns a React component, not JSON — can't serialize
|
|
11
|
+
* across HTTP or for caching.
|
|
12
|
+
* 2. Blocks have JSON-serializable props (no functions, no React
|
|
13
|
+
* elements), so we can emit a clean data structure.
|
|
14
|
+
* 3. The browser renders blocks from that JSON, with a per-type React
|
|
15
|
+
* component map on the client.
|
|
16
|
+
*
|
|
17
|
+
* Pipeline:
|
|
18
|
+
* 1. Validate with @mdx-js/mdx compile() so syntax errors surface
|
|
19
|
+
* early (unclosed tags, bad import statements, broken JSX).
|
|
20
|
+
* 2. Extract frontmatter (--- delimited YAML-ish).
|
|
21
|
+
* 3. Walk the body and find <BlockName id="..." attr1="..." attr2={...}>
|
|
22
|
+
* children </BlockName> patterns. Emit a flat list of typed blocks.
|
|
23
|
+
* 4. Parse JSX expression values via a tiny recursive-descent parser
|
|
24
|
+
* that supports objects, arrays, strings, numbers, booleans, null.
|
|
25
|
+
* Identifiers are treated as `undefined` (literals only — actual
|
|
26
|
+
* runtime values must come from frontmatter if needed).
|
|
27
|
+
* 5. Treat the markdown text OUTSIDE blocks as a fallback "RichText"
|
|
28
|
+
* section with id `__prologue__`, so legacy plans that have no
|
|
29
|
+
* blocks still render something useful.
|
|
30
|
+
*
|
|
31
|
+
* The block vocabulary (matches agent-native.com /visual-plan and the
|
|
32
|
+
* glyphs-research.md spec):
|
|
33
|
+
* <RichText id="...">markdown</RichText>
|
|
34
|
+
* <Callout id="..." tone="info|warn|success|danger">markdown</Callout>
|
|
35
|
+
* <Checklist id="..." items={[{id, label, checked}]} />
|
|
36
|
+
* <Table id="..." columns={["a", "b"]} rows={[["x", "y"]]} />
|
|
37
|
+
* <CodeTabs id="..." tabs={[{id, label, language, code, caption?}]} />
|
|
38
|
+
* <Decision id="..." title question options={[{id, label, detail, recommended?}]} />
|
|
39
|
+
* <OpenQuestions id="..." questions={[{id, label, kind, options?}]} />
|
|
40
|
+
* <FileTree id="..." title entries={[{path, change, note?}]} />
|
|
41
|
+
* <Diff id="..." filename language mode="split|unified" before after />
|
|
42
|
+
* <Stat id="..." label value trend? />
|
|
43
|
+
* <Workflow id="..." steps={[{id, label, type}]} connections={[{from, to, label?}]} />
|
|
44
|
+
* <Mockup id="..." title x y w h html />
|
|
45
|
+
* <CommentPin id="..." x y commentId? />
|
|
46
|
+
* <Diagram id="..." title dataHtml dataCss />
|
|
47
|
+
*
|
|
48
|
+
* Returned shape:
|
|
49
|
+
* {
|
|
50
|
+
* frontmatter: { title?, status?, author?, tags?, ... },
|
|
51
|
+
* blocks: [{ id, type, data, childrenMarkdown? }],
|
|
52
|
+
* errors: [{ line, message }],
|
|
53
|
+
* compiledAt: ISO timestamp,
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
import { compile } from '@mdx-js/mdx';
|
|
58
|
+
|
|
59
|
+
const BLOCK_TYPES = new Set([
|
|
60
|
+
'RichText',
|
|
61
|
+
'Callout',
|
|
62
|
+
'Checklist',
|
|
63
|
+
'Table',
|
|
64
|
+
'CodeTabs',
|
|
65
|
+
'Decision',
|
|
66
|
+
'OpenQuestions',
|
|
67
|
+
'FileTree',
|
|
68
|
+
'Diff',
|
|
69
|
+
'Stat',
|
|
70
|
+
'Workflow',
|
|
71
|
+
'Mockup',
|
|
72
|
+
'CommentPin',
|
|
73
|
+
'Diagram',
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const PROLOGUE_ID = '__prologue__';
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parse a YAML-ish frontmatter block. Scalar values only; numbers and
|
|
80
|
+
* booleans are coerced, anything else stays as a string.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} raw
|
|
83
|
+
* @returns {{ frontmatter: Record<string, unknown>, body: string }}
|
|
84
|
+
*/
|
|
85
|
+
function parseFrontmatter(raw) {
|
|
86
|
+
if (!raw || !raw.startsWith('---')) {
|
|
87
|
+
return { frontmatter: {}, body: raw || '' };
|
|
88
|
+
}
|
|
89
|
+
// First "---" closes after a line that begins with "---"
|
|
90
|
+
const lines = raw.split(/\r?\n/);
|
|
91
|
+
if (lines[0].trim() !== '---') {
|
|
92
|
+
return { frontmatter: {}, body: raw };
|
|
93
|
+
}
|
|
94
|
+
let endIdx = -1;
|
|
95
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
96
|
+
if (lines[i].trim() === '---') {
|
|
97
|
+
endIdx = i;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (endIdx === -1) {
|
|
102
|
+
return { frontmatter: {}, body: raw };
|
|
103
|
+
}
|
|
104
|
+
const fmBlock = lines.slice(1, endIdx).join('\n');
|
|
105
|
+
const body = lines.slice(endIdx + 1).join('\n').replace(/^\s+/, '');
|
|
106
|
+
const frontmatter = {};
|
|
107
|
+
for (const line of fmBlock.split(/\r?\n/)) {
|
|
108
|
+
const m = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line);
|
|
109
|
+
if (!m) continue;
|
|
110
|
+
const key = m[1];
|
|
111
|
+
let val = m[2].trim();
|
|
112
|
+
if (
|
|
113
|
+
(val.startsWith('"') && val.endsWith('"')) ||
|
|
114
|
+
(val.startsWith("'") && val.endsWith("'"))
|
|
115
|
+
) {
|
|
116
|
+
val = val.slice(1, -1);
|
|
117
|
+
} else if (val === 'true') {
|
|
118
|
+
val = true;
|
|
119
|
+
} else if (val === 'false') {
|
|
120
|
+
val = false;
|
|
121
|
+
} else if (val !== '' && !Number.isNaN(Number(val))) {
|
|
122
|
+
val = Number(val);
|
|
123
|
+
} else if (val === 'null') {
|
|
124
|
+
val = null;
|
|
125
|
+
}
|
|
126
|
+
frontmatter[key] = val;
|
|
127
|
+
}
|
|
128
|
+
return { frontmatter, body };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Recursive-descent parser for JSX expression values. Supports the
|
|
133
|
+
* subset that the glyph block vocabulary uses:
|
|
134
|
+
* - Objects: { key: value, key2: value2 }
|
|
135
|
+
* - Arrays: [value, value2]
|
|
136
|
+
* - Strings: "value" or 'value'
|
|
137
|
+
* - Numbers: 123 or 1.5 or -1.5
|
|
138
|
+
* - Booleans: true / false
|
|
139
|
+
* - null
|
|
140
|
+
* - Identifiers are treated as `undefined` (we don't import runtime
|
|
141
|
+
* values; if you need one, put it in frontmatter and reference it).
|
|
142
|
+
*
|
|
143
|
+
* @param {string} src
|
|
144
|
+
* @returns {{ value: unknown, rest: string }}
|
|
145
|
+
*/
|
|
146
|
+
function parseJsxValue(src) {
|
|
147
|
+
let i = 0;
|
|
148
|
+
const len = src.length;
|
|
149
|
+
const skipWs = () => {
|
|
150
|
+
while (i < len && /\s/.test(src[i])) i += 1;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const readString = (quote) => {
|
|
154
|
+
let out = '';
|
|
155
|
+
i += 1; // opening quote
|
|
156
|
+
while (i < len) {
|
|
157
|
+
const ch = src[i];
|
|
158
|
+
if (ch === '\\' && i + 1 < len) {
|
|
159
|
+
// Standard JS escape sequences. We support the common ones;
|
|
160
|
+
// unknown escapes pass through the character verbatim.
|
|
161
|
+
const next = src[i + 1];
|
|
162
|
+
const esc =
|
|
163
|
+
next === 'n' ? '\n' :
|
|
164
|
+
next === 't' ? '\t' :
|
|
165
|
+
next === 'r' ? '\r' :
|
|
166
|
+
next === 'b' ? '\b' :
|
|
167
|
+
next === 'f' ? '\f' :
|
|
168
|
+
next === 'v' ? '\v' :
|
|
169
|
+
next === '0' ? '\0' :
|
|
170
|
+
next;
|
|
171
|
+
out += esc;
|
|
172
|
+
i += 2;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (ch === quote) {
|
|
176
|
+
i += 1;
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
out += ch;
|
|
180
|
+
i += 1;
|
|
181
|
+
}
|
|
182
|
+
throw new Error(`unterminated string starting at offset ${i}`);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const parseValue = () => {
|
|
186
|
+
skipWs();
|
|
187
|
+
if (i >= len) throw new Error('unexpected end of expression');
|
|
188
|
+
const ch = src[i];
|
|
189
|
+
if (ch === '{' || ch === '[') {
|
|
190
|
+
const closer = ch === '{' ? '}' : ']';
|
|
191
|
+
const open = ch;
|
|
192
|
+
i += 1;
|
|
193
|
+
const arr = ch === '[';
|
|
194
|
+
const items = arr ? [] : {};
|
|
195
|
+
skipWs();
|
|
196
|
+
if (src[i] === closer) {
|
|
197
|
+
i += 1;
|
|
198
|
+
return arr ? items : items;
|
|
199
|
+
}
|
|
200
|
+
while (i < len) {
|
|
201
|
+
skipWs();
|
|
202
|
+
if (arr) {
|
|
203
|
+
items.push(parseValue());
|
|
204
|
+
} else {
|
|
205
|
+
// key (bareword or quoted string)
|
|
206
|
+
let key;
|
|
207
|
+
if (src[i] === '"' || src[i] === "'") {
|
|
208
|
+
key = readString(src[i]);
|
|
209
|
+
} else {
|
|
210
|
+
const start = i;
|
|
211
|
+
while (i < len && /[A-Za-z0-9_-]/.test(src[i])) i += 1;
|
|
212
|
+
key = src.slice(start, i);
|
|
213
|
+
if (!key) throw new Error(`expected object key at offset ${start}`);
|
|
214
|
+
}
|
|
215
|
+
skipWs();
|
|
216
|
+
if (src[i] !== ':') throw new Error(`expected ':' at offset ${i}`);
|
|
217
|
+
i += 1;
|
|
218
|
+
items[key] = parseValue();
|
|
219
|
+
}
|
|
220
|
+
skipWs();
|
|
221
|
+
if (src[i] === ',') {
|
|
222
|
+
i += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (src[i] === closer) {
|
|
226
|
+
i += 1;
|
|
227
|
+
return items;
|
|
228
|
+
}
|
|
229
|
+
throw new Error(`expected ',' or '${closer}' at offset ${i}`);
|
|
230
|
+
}
|
|
231
|
+
throw new Error(`unterminated ${open === '{' ? 'object' : 'array'}`);
|
|
232
|
+
}
|
|
233
|
+
if (ch === '"' || ch === "'") return readString(ch);
|
|
234
|
+
if (ch === 't' && src.slice(i, i + 4) === 'true') {
|
|
235
|
+
i += 4;
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
if (ch === 'f' && src.slice(i, i + 5) === 'false') {
|
|
239
|
+
i += 5;
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
if (ch === 'n' && src.slice(i, i + 4) === 'null') {
|
|
243
|
+
i += 4;
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
if (ch === '-' || (ch >= '0' && ch <= '9')) {
|
|
247
|
+
const start = i;
|
|
248
|
+
if (ch === '-') i += 1;
|
|
249
|
+
while (i < len && /[0-9.]/.test(src[i])) i += 1;
|
|
250
|
+
const numStr = src.slice(start, i);
|
|
251
|
+
const num = Number(numStr);
|
|
252
|
+
if (Number.isNaN(num)) throw new Error(`bad number '${numStr}' at offset ${start}`);
|
|
253
|
+
return num;
|
|
254
|
+
}
|
|
255
|
+
// bareword identifier — treat as undefined marker
|
|
256
|
+
const start = i;
|
|
257
|
+
while (i < len && /[A-Za-z0-9_$.]/.test(src[i])) i += 1;
|
|
258
|
+
const ident = src.slice(start, i);
|
|
259
|
+
return { __ident: ident };
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const value = parseValue();
|
|
263
|
+
skipWs();
|
|
264
|
+
return { value, rest: src.slice(i) };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Parse attributes on a JSX element opening tag. Returns an object
|
|
269
|
+
* keyed by attribute name, plus an array of validation errors.
|
|
270
|
+
*
|
|
271
|
+
* Handles:
|
|
272
|
+
* attr="value" → string
|
|
273
|
+
* attr='value' → string
|
|
274
|
+
* attr={jsExpression} → parsed JSON-ish value
|
|
275
|
+
* attr → boolean true
|
|
276
|
+
*
|
|
277
|
+
* @param {string} inside
|
|
278
|
+
* @returns {{ attrs: Record<string, unknown>, errors: Array<{ message: string }> }}
|
|
279
|
+
*/
|
|
280
|
+
function parseAttrs(inside) {
|
|
281
|
+
const attrs = {};
|
|
282
|
+
const errors = [];
|
|
283
|
+
let i = 0;
|
|
284
|
+
const len = inside.length;
|
|
285
|
+
const skipWs = () => {
|
|
286
|
+
while (i < len && /\s/.test(inside[i])) i += 1;
|
|
287
|
+
};
|
|
288
|
+
while (i < len) {
|
|
289
|
+
skipWs();
|
|
290
|
+
if (i >= len) break;
|
|
291
|
+
// Read attribute name
|
|
292
|
+
const nameStart = i;
|
|
293
|
+
while (i < len && /[A-Za-z0-9_:-]/.test(inside[i])) i += 1;
|
|
294
|
+
const name = inside.slice(nameStart, i);
|
|
295
|
+
if (!name) break;
|
|
296
|
+
skipWs();
|
|
297
|
+
if (inside[i] === '=') {
|
|
298
|
+
i += 1;
|
|
299
|
+
skipWs();
|
|
300
|
+
if (inside[i] === '"' || inside[i] === "'") {
|
|
301
|
+
const q = inside[i];
|
|
302
|
+
i += 1;
|
|
303
|
+
const valStart = i;
|
|
304
|
+
while (i < len && inside[i] !== q) i += 1;
|
|
305
|
+
attrs[name] = inside.slice(valStart, i);
|
|
306
|
+
if (i < len) i += 1; // closing quote
|
|
307
|
+
} else if (inside[i] === '{') {
|
|
308
|
+
// Try parse; on failure, capture the raw source for debug.
|
|
309
|
+
// We need to find the matching closing brace (best-effort depth walk
|
|
310
|
+
// over the whole value) and pass the slice WITHOUT the trailing `}`,
|
|
311
|
+
// so parseJsxValue doesn't see an unterminated string at end of input.
|
|
312
|
+
let depth = 1;
|
|
313
|
+
let j = i + 1;
|
|
314
|
+
while (j < len && depth > 0) {
|
|
315
|
+
if (inside[j] === '{') depth += 1;
|
|
316
|
+
else if (inside[j] === '}') depth -= 1;
|
|
317
|
+
if (depth > 0) j += 1;
|
|
318
|
+
}
|
|
319
|
+
const valueSrc = inside.slice(i + 1, j);
|
|
320
|
+
try {
|
|
321
|
+
const { value } = parseJsxValue(valueSrc);
|
|
322
|
+
attrs[name] = value && typeof value === 'object' && value.__ident
|
|
323
|
+
? undefined
|
|
324
|
+
: value;
|
|
325
|
+
} catch (err) {
|
|
326
|
+
errors.push({
|
|
327
|
+
message: `failed to parse attr "${name}": ${err.message}`,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
// Skip past the matching closing brace
|
|
331
|
+
i = j + 1;
|
|
332
|
+
} else {
|
|
333
|
+
// Bare value (rare)
|
|
334
|
+
const valStart = i;
|
|
335
|
+
while (i < len && !/\s/.test(inside[i])) i += 1;
|
|
336
|
+
attrs[name] = inside.slice(valStart, i);
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
attrs[name] = true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { attrs, errors };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Walk the MDX body string and extract blocks. We look for `<BlockName`
|
|
347
|
+
* tags whose name is in BLOCK_TYPES, parse attributes, and capture
|
|
348
|
+
* children (markdown text or self-closing form).
|
|
349
|
+
*
|
|
350
|
+
* Returns:
|
|
351
|
+
* { blocks: [...], errors: [...] }
|
|
352
|
+
*
|
|
353
|
+
* The line numbers reported are 1-indexed against the body (after
|
|
354
|
+
* frontmatter stripping); the caller is responsible for offset if it
|
|
355
|
+
* wants file-line numbers.
|
|
356
|
+
*
|
|
357
|
+
* @param {string} body
|
|
358
|
+
* @returns {{ blocks: Array, errors: Array }}
|
|
359
|
+
*/
|
|
360
|
+
function extractBlocks(body) {
|
|
361
|
+
const blocks = [];
|
|
362
|
+
const errors = [];
|
|
363
|
+
// Track trailing free-form markdown BEFORE the first block as the
|
|
364
|
+
// "__prologue__" RichText — this lets legacy plans without blocks
|
|
365
|
+
// still render their content.
|
|
366
|
+
let cursor = 0;
|
|
367
|
+
let prologueCaptured = false;
|
|
368
|
+
let blockCounter = 0;
|
|
369
|
+
|
|
370
|
+
const lineOf = (offset) => {
|
|
371
|
+
let line = 1;
|
|
372
|
+
for (let k = 0; k < offset && k < body.length; k += 1) {
|
|
373
|
+
if (body[k] === '\n') line += 1;
|
|
374
|
+
}
|
|
375
|
+
return line;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// Find the next opening tag for a known block. We use a custom walker
|
|
379
|
+
// instead of a regex because MDX attribute values can contain `>` (in
|
|
380
|
+
// strings or arrow functions) — a regex would stop too early and
|
|
381
|
+
// truncate the attribute list. The walker respects:
|
|
382
|
+
// - string literals (single + double quotes, with escape sequences)
|
|
383
|
+
// - JSX expression braces `{...}` (depth-balanced)
|
|
384
|
+
// - HTML comments `<!-- ... -->`
|
|
385
|
+
const findNextOpenTag = (startFrom) => {
|
|
386
|
+
let i = startFrom;
|
|
387
|
+
while (i < body.length) {
|
|
388
|
+
const ch = body[i];
|
|
389
|
+
if (ch !== '<') { i += 1; continue; }
|
|
390
|
+
// Skip comments `<!-- ... -->`
|
|
391
|
+
if (body.slice(i, i + 4) === '<!--') {
|
|
392
|
+
const end = body.indexOf('-->', i + 4);
|
|
393
|
+
if (end === -1) return null;
|
|
394
|
+
i = end + 3;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
// Must be `<Word` (capital letter followed by alpha) — a known block opening tag.
|
|
398
|
+
if (!/[A-Z]/.test(body[i + 1] || '')) { i += 1; continue; }
|
|
399
|
+
let j = i + 1;
|
|
400
|
+
while (j < body.length && /[A-Za-z0-9]/.test(body[j])) j += 1;
|
|
401
|
+
const name = body.slice(i + 1, j);
|
|
402
|
+
if (!BLOCK_TYPES.has(name)) { i = j; continue; }
|
|
403
|
+
// Walk past the rest of the opening tag, respecting strings + braces.
|
|
404
|
+
while (j < body.length && body[j] !== '>') {
|
|
405
|
+
const c = body[j];
|
|
406
|
+
if (c === '"' || c === "'") {
|
|
407
|
+
// Skip string literal
|
|
408
|
+
j += 1;
|
|
409
|
+
while (j < body.length && body[j] !== c) {
|
|
410
|
+
if (body[j] === '\\') j += 2;
|
|
411
|
+
else j += 1;
|
|
412
|
+
}
|
|
413
|
+
j += 1;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
j += 1;
|
|
417
|
+
}
|
|
418
|
+
if (j >= body.length) return null;
|
|
419
|
+
const selfClose = body[j - 1] === '/' ? '/' : '';
|
|
420
|
+
const inside = body.slice(i + 1 + name.length, j - (selfClose ? 1 : 0));
|
|
421
|
+
return { name, inside, selfClose, start: i, end: j + 1 };
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
while (cursor < body.length) {
|
|
427
|
+
const found = findNextOpenTag(cursor);
|
|
428
|
+
if (!found) break;
|
|
429
|
+
const { name, inside, selfClose, start, end } = found;
|
|
430
|
+
const { attrs, errors: attrErrors } = parseAttrs(inside);
|
|
431
|
+
for (const e of attrErrors) errors.push({ line: lineOf(start), ...e });
|
|
432
|
+
blockCounter += 1;
|
|
433
|
+
const id = typeof attrs.id === 'string' && attrs.id
|
|
434
|
+
? attrs.id
|
|
435
|
+
: `${name.toLowerCase()}_${blockCounter}`;
|
|
436
|
+
// Capture anything between this tag and its closer as children
|
|
437
|
+
let childrenMarkdown = '';
|
|
438
|
+
let closeOffset = -1;
|
|
439
|
+
if (selfClose === '/') {
|
|
440
|
+
closeOffset = end;
|
|
441
|
+
cursor = closeOffset;
|
|
442
|
+
} else {
|
|
443
|
+
const closeStr = `</${name}>`;
|
|
444
|
+
const ci = body.indexOf(closeStr, end);
|
|
445
|
+
if (ci === -1) {
|
|
446
|
+
errors.push({
|
|
447
|
+
line: lineOf(start),
|
|
448
|
+
message: `<${name}> opened at line ${lineOf(start)} has no closing tag`,
|
|
449
|
+
});
|
|
450
|
+
closeOffset = end;
|
|
451
|
+
cursor = closeOffset;
|
|
452
|
+
} else {
|
|
453
|
+
closeOffset = ci;
|
|
454
|
+
childrenMarkdown = body.slice(end, ci);
|
|
455
|
+
cursor = ci + closeStr.length;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Capture free-form markdown BEFORE this block as the prologue.
|
|
459
|
+
if (!prologueCaptured) {
|
|
460
|
+
const pre = body.slice(cursor, start).trim();
|
|
461
|
+
if (pre) {
|
|
462
|
+
blocks.push({
|
|
463
|
+
id: PROLOGUE_ID,
|
|
464
|
+
type: 'RichText',
|
|
465
|
+
data: {},
|
|
466
|
+
childrenMarkdown: pre,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
prologueCaptured = true;
|
|
470
|
+
}
|
|
471
|
+
const data = { ...attrs };
|
|
472
|
+
delete data.id;
|
|
473
|
+
blocks.push({
|
|
474
|
+
id,
|
|
475
|
+
type: name,
|
|
476
|
+
data,
|
|
477
|
+
childrenMarkdown: childrenMarkdown.trim(),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Trailing free-form markdown after the last block
|
|
482
|
+
if (!prologueCaptured) {
|
|
483
|
+
const rest = body.slice(cursor).trim();
|
|
484
|
+
if (rest) {
|
|
485
|
+
blocks.push({
|
|
486
|
+
id: PROLOGUE_ID,
|
|
487
|
+
type: 'RichText',
|
|
488
|
+
data: {},
|
|
489
|
+
childrenMarkdown: rest,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
const rest = body.slice(cursor).trim();
|
|
494
|
+
if (rest) {
|
|
495
|
+
blocks.push({
|
|
496
|
+
id: `${PROLOGUE_ID}_tail`,
|
|
497
|
+
type: 'RichText',
|
|
498
|
+
data: {},
|
|
499
|
+
childrenMarkdown: rest,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return { blocks, errors };
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Compile MDX source into a JSON-serializable glyph object.
|
|
509
|
+
*
|
|
510
|
+
* Validates the source with `@mdx-js/mdx` so MDX syntax errors surface
|
|
511
|
+
* as structured errors, then walks the body to extract blocks using
|
|
512
|
+
* the recursive parser above.
|
|
513
|
+
*
|
|
514
|
+
* @param {string} source Raw MDX text (frontmatter optional).
|
|
515
|
+
* @param {object} [options]
|
|
516
|
+
* @param {boolean} [options.skipValidate] Skip the @mdx-js/mdx pass (faster, for trusted input).
|
|
517
|
+
* @returns {Promise<{
|
|
518
|
+
* frontmatter: Record<string, unknown>,
|
|
519
|
+
* blocks: Array<{ id: string, type: string, data: object, childrenMarkdown?: string }>,
|
|
520
|
+
* errors: Array<{ line?: number, message: string }>,
|
|
521
|
+
* compiledAt: string,
|
|
522
|
+
* }>}
|
|
523
|
+
*/
|
|
524
|
+
export async function compileGlyphMdx(source, options = {}) {
|
|
525
|
+
const { skipValidate = false } = options;
|
|
526
|
+
const errors = [];
|
|
527
|
+
const { frontmatter, body } = parseFrontmatter(source || '');
|
|
528
|
+
|
|
529
|
+
if (!skipValidate) {
|
|
530
|
+
try {
|
|
531
|
+
await compile(source || '', { jsx: false, development: false });
|
|
532
|
+
} catch (err) {
|
|
533
|
+
errors.push({
|
|
534
|
+
line: err.line ?? null,
|
|
535
|
+
message: err.message || String(err),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const { blocks, errors: extractErrors } = extractBlocks(body);
|
|
541
|
+
for (const e of extractErrors) errors.push(e);
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
frontmatter,
|
|
545
|
+
blocks,
|
|
546
|
+
errors,
|
|
547
|
+
compiledAt: new Date().toISOString(),
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Synchronous variant of `compileGlyphMdx`. Skips MDX validation (so
|
|
553
|
+
* it doesn't need `compile()` from @mdx-js/mdx, which is async-only).
|
|
554
|
+
* Use this only for trusted input where you want zero-overhead
|
|
555
|
+
* extraction.
|
|
556
|
+
*
|
|
557
|
+
* @param {string} source
|
|
558
|
+
* @returns {{
|
|
559
|
+
* frontmatter: Record<string, unknown>,
|
|
560
|
+
* blocks: Array<{ id: string, type: string, data: object, childrenMarkdown?: string }>,
|
|
561
|
+
* errors: Array<{ line?: number, message: string }>,
|
|
562
|
+
* compiledAt: string,
|
|
563
|
+
* }}
|
|
564
|
+
*/
|
|
565
|
+
export function compileGlyphMdxSync(source) {
|
|
566
|
+
const { frontmatter, body } = parseFrontmatter(source || '');
|
|
567
|
+
const { blocks, errors } = extractBlocks(body);
|
|
568
|
+
return {
|
|
569
|
+
frontmatter,
|
|
570
|
+
blocks,
|
|
571
|
+
errors,
|
|
572
|
+
compiledAt: new Date().toISOString(),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export const _internal = {
|
|
577
|
+
BLOCK_TYPES,
|
|
578
|
+
PROLOGUE_ID,
|
|
579
|
+
parseFrontmatter,
|
|
580
|
+
parseJsxValue,
|
|
581
|
+
parseAttrs,
|
|
582
|
+
extractBlocks,
|
|
583
|
+
};
|