stream_weaver 0.3.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.
- checksums.yaml +7 -0
- data/.beads/.gitignore +77 -0
- data/.beads/README.md +81 -0
- data/.beads/config.yaml +54 -0
- data/.beads/metadata.json +7 -0
- data/.rspec +3 -0
- data/AGENTS.md +91 -0
- data/CHANGELOG.md +294 -0
- data/CLAUDE.md +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +565 -0
- data/Rakefile +13 -0
- data/assets/streamweaver-hero.jpg +0 -0
- data/bench/baselines/ledger.rb +58 -0
- data/bench/baselines/warroom.rb +51 -0
- data/bench/fixtures/ledger.rb +76 -0
- data/bench/fixtures/warroom.rb +60 -0
- data/bench/results/6daf8b8.md +102 -0
- data/bench/results/c8d749b.md +102 -0
- data/bench/results/eb06ab9.md +102 -0
- data/bench/run.rb +151 -0
- data/bench/support.rb +84 -0
- data/copy_this/dto_run_sample_run_7631.html +220 -0
- data/docs/SERVICE_MODE.md +302 -0
- data/docs/architecture/how_streamweaver_works.md +772 -0
- data/docs/blog/2026-01-16-streamweaver-introduction.md +515 -0
- data/docs/bug-2026-08-31-canvas-bridge-socket-collision.md +83 -0
- data/docs/canvas-ipc-session-summary.md +127 -0
- data/docs/canvas-panel-workflow.md +172 -0
- data/docs/canvas-read.md +147 -0
- data/docs/canvas-roadmap.md +275 -0
- data/docs/case-studies/2026-09-03-didx-canvas-read-shelf.md +147 -0
- data/docs/claude-code-companion-skill-spec.md +567 -0
- data/docs/components_reference.md +1223 -0
- data/docs/crud-patterns.md +265 -0
- data/docs/endpoints.md +122 -0
- data/docs/for_llms.md +1 -0
- data/docs/form-patterns.md +165 -0
- data/docs/frontend-only.md +169 -0
- data/docs/html-artifact-audit.md +238 -0
- data/docs/ideas/2025-01-01-as-a-service.md +203 -0
- data/docs/ideas/2026-01-16-charm-tui-exploration.md +542 -0
- data/docs/ideas/2026-08-27-static-doc-shelf-export.md +153 -0
- data/docs/ideas/dhh-review-future-refactors.md +142 -0
- data/docs/opal-jamstack.md +47 -0
- data/docs/opal-npm-direction.md +184 -0
- data/docs/opal-spike-findings.md +194 -0
- data/docs/plans/2025-12-16-form-blocks-design.md +124 -0
- data/docs/plans/2025-12-30-examples-browser-design.md +152 -0
- data/docs/plans/2026-01-02-charts-design.md +288 -0
- data/docs/plans/2026-01-10-canvas-ipc-design.md +247 -0
- data/docs/plans/2026-01-19-cabinet-control-components.md +176 -0
- data/docs/plans/2026-01-26-gem-release-and-panel.md +140 -0
- data/docs/plans/canvas-claude-project-design.md +238 -0
- data/docs/plans/canvas-doc-location-and-discovery.md +32 -0
- data/docs/plans/org-doc-preview-surfaces.md +293 -0
- data/docs/plans/shadcn-polish-plan.md +417 -0
- data/docs/porting-artifacts.md +252 -0
- data/docs/reference/agent-skills-comparison.md +369 -0
- data/docs/reference/travel-state-prd.artifact.html +780 -0
- data/docs/research/2026-08-17-hotwire-alike-landscape.md +268 -0
- data/docs/research/2026-08-17-hotwire-concept-map.md +404 -0
- data/docs/research/2026-08-22-lazy-fragments-trigger-decision.md +246 -0
- data/docs/research/2026-08-22-learnhotwire-syllabus-coverage.md +239 -0
- data/docs/research/frontend-only-matrix.md +278 -0
- data/docs/research/streamweaver-way-spike-findings.md +529 -0
- data/docs/resource-dsl.md +342 -0
- data/docs/routing.md +302 -0
- data/docs/ruby-ui-comparison.md +319 -0
- data/docs/shared-dsl-fragments.md +356 -0
- data/docs/streamweaver-for-ai-agents.md +401 -0
- data/docs/streamweaver-frontend-vision.md +123 -0
- data/docs/streamweaver_canvas/glimmer_initial_final_layer.rb +77 -0
- data/docs/streamweaver_canvas/save_example.rb +9 -0
- data/docs/templates.md +213 -0
- data/docs/testing.md +155 -0
- data/docs/theming-hooks.md +281 -0
- data/docs/tutorials/the-streamweaver-way.md +882 -0
- data/docs/university/capability-inventory.md +137 -0
- data/docs/university/dependency-survey.md +114 -0
- data/docs/university/design-spec.md +217 -0
- data/docs/university/mockups/code-block-doc-theme-dark.png +0 -0
- data/docs/university/mockups/code-block-doc-theme-light.png +0 -0
- data/docs/university/mockups/course-list-dark.png +0 -0
- data/docs/university/mockups/course-list-light.png +0 -0
- data/docs/university/mockups/course_canvas_mockup.rb +741 -0
- data/docs/university/mockups/step-screen-dark.png +0 -0
- data/docs/university/mockups/step-screen-light.png +0 -0
- data/docs/university/roadmap.md +72 -0
- data/docs/university/send-to-coworker.md +77 -0
- data/docs/university/worker-session-mining.md +109 -0
- data/docs/visual/browser-rendering-spike.md +173 -0
- data/docs/visual/builder-visual-plan-analysis.md +378 -0
- data/docs/visual/sw-plan-format-exploration.md +511 -0
- data/docs/visual/sw-plan-rendering-deep-dive.md +453 -0
- data/docs/visual-skills/PROGRESS.md +60 -0
- data/docs/visual-skills/SESSION-CONTEXT.md +207 -0
- data/docs/visual-skills/analysis/components.md +317 -0
- data/docs/visual-skills/analysis/overlap.md +360 -0
- data/docs/visual-skills/analysis/pi-design-deck.md +1102 -0
- data/docs/visual-skills/analysis/streamweaver-inventory.md +93 -0
- data/docs/visual-skills/analysis/unified-specs.feature +1003 -0
- data/docs/visual-skills/analysis/visual-explainer.md +964 -0
- data/docs/visual-skills/blog/blog-series-outline.md +39 -0
- data/docs/visual-skills/blog/token-efficiency.md +73 -0
- data/docs/visual-skills/design/architecture.md +1725 -0
- data/docs/visual-skills/design/codex-review.md +340 -0
- data/docs/visual-skills/design/dhh-review.md +209 -0
- data/docs/visual-skills/design/evolution.md +100 -0
- data/docs/visual-skills/design/gemini-review.md +55 -0
- data/docs/visual-skills/design/review-synthesis.md +149 -0
- data/docs/visual-skills/implementation/STATE.md +22 -0
- data/docs/visual-skills/implementation/plan.md +347 -0
- data/docs/visual-skills/implementation/spike-findings.md +237 -0
- data/docs/visual-skills/implementation/tasks.md +702 -0
- data/docs/visual-skills/lessons-learned/process.md +145 -0
- data/examples/README.md +96 -0
- data/examples/advanced/all_components.rb +177 -0
- data/examples/advanced/examples_browser.rb +394 -0
- data/examples/advanced/teachables_browser.rb +261 -0
- data/examples/advanced/theme_tweaker.rb +366 -0
- data/examples/advanced/tutorial.rb +1528 -0
- data/examples/agentic/agentic_form.rb +30 -0
- data/examples/agentic/agentic_form_autoclose.rb +30 -0
- data/examples/agentic/cultivation_tracker.rb +69 -0
- data/examples/basic/hello_world.rb +22 -0
- data/examples/basic/opal_tabs_table.rb +44 -0
- data/examples/basic/todo_list.rb +39 -0
- data/examples/button_loading_test.rb +36 -0
- data/examples/canvas/mermaid_canvas_demo.sh +151 -0
- data/examples/charts/bar_chart_demo.rb +57 -0
- data/examples/charts/line_chart_demo.rb +73 -0
- data/examples/charts/pie_area_demo.rb +87 -0
- data/examples/charts/stacked_bar_chart_demo.rb +69 -0
- data/examples/claude_code/README.md +85 -0
- data/examples/claude_code/codebreaker/.claude/commands/infiltrate.md +632 -0
- data/examples/claude_code/codebreaker/.claude/settings.json +11 -0
- data/examples/claude_code/codebreaker/.claude/settings.local.json +17 -0
- data/examples/claude_code/codebreaker/.claude/skills/infiltrate.md +0 -0
- data/examples/claude_code/codebreaker/README.md +213 -0
- data/examples/claude_code/tutorial/.claude/commands/learn.md +453 -0
- data/examples/claude_code/tutorial/README.md +102 -0
- data/examples/claude_code/verification_flow/.claude/commands/verify.md +171 -0
- data/examples/claude_code/verification_flow/README.md +126 -0
- data/examples/components/annotated_code_demo.rb +54 -0
- data/examples/components/callout_demo.rb +33 -0
- data/examples/components/checkbox_group_demo.rb +46 -0
- data/examples/components/design_review.css +468 -0
- data/examples/components/design_review_demo.rb +44 -0
- data/examples/components/design_review_dsl.rb +300 -0
- data/examples/components/diff_block_demo.rb +112 -0
- data/examples/components/events_demo.rb +184 -0
- data/examples/components/form_demo.rb +62 -0
- data/examples/components/lesson_demo.rb +53 -0
- data/examples/components/markdown_demo.rb +110 -0
- data/examples/components/mermaid_demo.rb +166 -0
- data/examples/components/pareto_set.rb +64 -0
- data/examples/components/prd_demo.rb +21 -0
- data/examples/components/prd_dsl.rb +385 -0
- data/examples/components/quiz_demo.rb +47 -0
- data/examples/components/run_viewer_demo.rb +100 -0
- data/examples/components/score_and_collapsible_demo.rb +82 -0
- data/examples/components/select_stale_value_smoke_test.rb +39 -0
- data/examples/components/table_demo.rb +247 -0
- data/examples/components/timer_with_state_demo.rb +58 -0
- data/examples/components/todo_due_dates.rb +63 -0
- data/examples/components/uat_gaps_demo.rb +112 -0
- data/examples/dashboard/feed_simulator.rb +60 -0
- data/examples/dashboard/live_dashboard.rb +80 -0
- data/examples/dashboard_components.rb +108 -0
- data/examples/deferred_fragments_demo.rb +62 -0
- data/examples/generate_more_spike/README.md +63 -0
- data/examples/generate_more_spike/app.rb +441 -0
- data/examples/git_health.sh +401 -0
- data/examples/layout/layout_components_demo.rb +174 -0
- data/examples/layout/modal_demo.rb +215 -0
- data/examples/layout/navigation_demo.rb +227 -0
- data/examples/layout/route_tabs_demo.rb +52 -0
- data/examples/layout/routing_demo.rb +52 -0
- data/examples/layout/scroll_box_demo.rb +154 -0
- data/examples/lazy_fragments_demo.rb +151 -0
- data/examples/my_todos/README.md +29 -0
- data/examples/my_todos/my_todos.rb +312 -0
- data/examples/my_todos/store.rb +86 -0
- data/examples/opal/reactive_demo.rb +62 -0
- data/examples/opal/scenarios/s1_counter.rb +15 -0
- data/examples/opal/scenarios/s2_search_filter.rb +17 -0
- data/examples/opal/scenarios/s3_sibling_tabs.rb +25 -0
- data/examples/opal/scenarios/s4_shopping_cart.rb +34 -0
- data/examples/opal/scenarios/s5_watch.rb +39 -0
- data/examples/opal/scenarios/s6_on_start.rb +20 -0
- data/examples/opal/scenarios/s7_wizard.rb +33 -0
- data/examples/opal/scenarios/s8_loan_calculator.rb +21 -0
- data/examples/opal/scenarios/s9_dashboard.rb +27 -0
- data/examples/operations_dashboard_demo.rb +167 -0
- data/examples/panel_demo.sh +447 -0
- data/examples/parity/assets/tyrion_slice.css +86 -0
- data/examples/parity/rivet_people_slice.rb +182 -0
- data/examples/parity/tyrion_components.css +777 -0
- data/examples/parity/tyrion_warroom_components.rb +414 -0
- data/examples/parity/tyrion_warroom_slice.rb +549 -0
- data/examples/puma_dev/README.md +124 -0
- data/examples/puma_dev/config.ru +34 -0
- data/examples/puma_dev/standalone_app.rb +34 -0
- data/examples/scaffolding/blog.rb +49 -0
- data/examples/scaffolding/utf_lite.rb +105 -0
- data/examples/styling/feedback_demo.rb +229 -0
- data/examples/styling/style_showcase.rb +172 -0
- data/examples/styling/theme_demo.rb +508 -0
- data/examples/timer_health_checker.rb +111 -0
- data/examples/timer_showcase.rb +131 -0
- data/examples/tutorials/render_markdown.rb +112 -0
- data/examples/tutorials/streamweaver_way_tutorial.rb +44 -0
- data/examples/tutorials/tutorial_content.rb +1082 -0
- data/examples/visual_skills/design_deck_demo.rb +314 -0
- data/examples/visual_skills/explainer_demo.rb +499 -0
- data/exe/streamweaver +9 -0
- data/gsd/ROADMAP-1.0.md +167 -0
- data/gsd/STATE.md +13 -0
- data/gsd/research/market-positioning-research.md +139 -0
- data/gsd/research/production-patterns-research.md +270 -0
- data/gsd/research/repo-audit-1.0.md +274 -0
- data/lib/stream_weaver/action_token.rb +58 -0
- data/lib/stream_weaver/adapter/alpinejs.rb +8249 -0
- data/lib/stream_weaver/adapter/base.rb +591 -0
- data/lib/stream_weaver/adapter/opal.rb +334 -0
- data/lib/stream_weaver/adapter/static.rb +1118 -0
- data/lib/stream_weaver/admin.rb +176 -0
- data/lib/stream_weaver/app.rb +1821 -0
- data/lib/stream_weaver/assets/js/sw-copy.js +50 -0
- data/lib/stream_weaver/assets/js/sw-heredoc-rewrite.js +68 -0
- data/lib/stream_weaver/assets/js/sw-keyboard.js +165 -0
- data/lib/stream_weaver/assets/js/sw-mermaid-zoom.js +621 -0
- data/lib/stream_weaver/assets/js/sw-route-tabs.js +53 -0
- data/lib/stream_weaver/assets/js/sw-sidebar-toc.js +116 -0
- data/lib/stream_weaver/assets/js/sw-slide-nav.js +103 -0
- data/lib/stream_weaver/canvas/bridge.rb +239 -0
- data/lib/stream_weaver/canvas/bridge_server.rb +646 -0
- data/lib/stream_weaver/canvas/client.rb +298 -0
- data/lib/stream_weaver/canvas/doc_roots.rb +233 -0
- data/lib/stream_weaver/canvas/doc_store.rb +252 -0
- data/lib/stream_weaver/canvas/gist_publisher.rb +264 -0
- data/lib/stream_weaver/canvas/gist_save_handler.rb +89 -0
- data/lib/stream_weaver/canvas/gist_store.rb +135 -0
- data/lib/stream_weaver/canvas/helpers.rb +109 -0
- data/lib/stream_weaver/canvas/history.rb +90 -0
- data/lib/stream_weaver/canvas/protocol.rb +86 -0
- data/lib/stream_weaver/canvas/reader.rb +853 -0
- data/lib/stream_weaver/canvas/save_doc_widget.rb +457 -0
- data/lib/stream_weaver/canvas/scroll_top_hint.rb +21 -0
- data/lib/stream_weaver/canvas/session.rb +132 -0
- data/lib/stream_weaver/cli.rb +3235 -0
- data/lib/stream_weaver/component_assets.rb +70 -0
- data/lib/stream_weaver/component_registry.rb +67 -0
- data/lib/stream_weaver/component_renderer.rb +48 -0
- data/lib/stream_weaver/components/annotated_code.rb +53 -0
- data/lib/stream_weaver/components/api_endpoint.rb +42 -0
- data/lib/stream_weaver/components/callout.rb +59 -0
- data/lib/stream_weaver/components/chart.rb +84 -0
- data/lib/stream_weaver/components/code_block.rb +77 -0
- data/lib/stream_weaver/components/comparison.rb +39 -0
- data/lib/stream_weaver/components/decision.rb +38 -0
- data/lib/stream_weaver/components/deck/close_overlay.rb +84 -0
- data/lib/stream_weaver/components/deck/confirmation_bar.rb +60 -0
- data/lib/stream_weaver/components/deck/deck_option.rb +61 -0
- data/lib/stream_weaver/components/deck/deck_slide.rb +77 -0
- data/lib/stream_weaver/components/deck/deck_state.rb +469 -0
- data/lib/stream_weaver/components/deck/deck_summary.rb +73 -0
- data/lib/stream_weaver/components/deck/design_deck.rb +60 -0
- data/lib/stream_weaver/components/deck/generate_more_controls.rb +84 -0
- data/lib/stream_weaver/components/deck/model_selector.rb +84 -0
- data/lib/stream_weaver/components/deck/skeleton_placeholder.rb +36 -0
- data/lib/stream_weaver/components/diff_block.rb +125 -0
- data/lib/stream_weaver/components/doc_header.rb +57 -0
- data/lib/stream_weaver/components/image_block.rb +70 -0
- data/lib/stream_weaver/components/implementation_map.rb +33 -0
- data/lib/stream_weaver/components/keyboard_shortcuts.rb +94 -0
- data/lib/stream_weaver/components/kpi_dashboard.rb +78 -0
- data/lib/stream_weaver/components/mermaid.rb +79 -0
- data/lib/stream_weaver/components/pipeline.rb +63 -0
- data/lib/stream_weaver/components/sidebar_toc.rb +46 -0
- data/lib/stream_weaver/components/slide_container.rb +141 -0
- data/lib/stream_weaver/components/timeline_event.rb +58 -0
- data/lib/stream_weaver/components/wireframe.rb +29 -0
- data/lib/stream_weaver/components/wireframe_block.rb +34 -0
- data/lib/stream_weaver/components.rb +2711 -0
- data/lib/stream_weaver/css.rb +230 -0
- data/lib/stream_weaver/dev_fallback_overlay.rb +61 -0
- data/lib/stream_weaver/display_dsl.rb +1004 -0
- data/lib/stream_weaver/export/html_exporter.rb +478 -0
- data/lib/stream_weaver/feed.rb +34 -0
- data/lib/stream_weaver/feed_builder.rb +31 -0
- data/lib/stream_weaver/fonts.rb +33 -0
- data/lib/stream_weaver/interaction_runner.rb +487 -0
- data/lib/stream_weaver/iterm.rb +460 -0
- data/lib/stream_weaver/layout_registry.rb +92 -0
- data/lib/stream_weaver/opal/bridge.rb +52 -0
- data/lib/stream_weaver/opal/builder.rb +193 -0
- data/lib/stream_weaver/opal/env.rb +29 -0
- data/lib/stream_weaver/opal/reactive_state.rb +75 -0
- data/lib/stream_weaver/opal/regexp_anchor_patch.rb +62 -0
- data/lib/stream_weaver/opal/renderer.rb +77 -0
- data/lib/stream_weaver/opal/runtime.rb +250 -0
- data/lib/stream_weaver/opal/shell.rb +145 -0
- data/lib/stream_weaver/opal/string_bridge.rb +50 -0
- data/lib/stream_weaver/opal/stubs/diff.min.js +1 -0
- data/lib/stream_weaver/opal/stubs/digest.rb +15 -0
- data/lib/stream_weaver/opal/stubs/marked.umd.js +79 -0
- data/lib/stream_weaver/opal/stubs/md5.rb +3 -0
- data/lib/stream_weaver/opal/stubs/morphdom.min.js +775 -0
- data/lib/stream_weaver/opal/stubs/prism-tomorrow.min.css +1 -0
- data/lib/stream_weaver/opal/stubs/prism.min.js +1967 -0
- data/lib/stream_weaver/opal_entry.rb +136 -0
- data/lib/stream_weaver/org/inline.rb +95 -0
- data/lib/stream_weaver/org/reader.rb +561 -0
- data/lib/stream_weaver/org/recording_context.rb +86 -0
- data/lib/stream_weaver/org/source_splitter.rb +63 -0
- data/lib/stream_weaver/org/writer.rb +300 -0
- data/lib/stream_weaver/page_shell.rb +530 -0
- data/lib/stream_weaver/portfile.rb +79 -0
- data/lib/stream_weaver/pushable.rb +42 -0
- data/lib/stream_weaver/resource/default_views.rb +111 -0
- data/lib/stream_weaver/resource/field_input.rb +25 -0
- data/lib/stream_weaver/resource/state_keys.rb +16 -0
- data/lib/stream_weaver/resource/store.rb +18 -0
- data/lib/stream_weaver/resource.rb +104 -0
- data/lib/stream_weaver/server.rb +1471 -0
- data/lib/stream_weaver/service.rb +1240 -0
- data/lib/stream_weaver/service_client.rb +104 -0
- data/lib/stream_weaver/session_store.rb +186 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/SKILL.md +66 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/examples/canvas-safe-showcase.rb +98 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/actions-and-buttons.md +47 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/charts-and-diagrams.md +38 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/deck.md +32 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/inputs-and-forms.md +65 -0
- data/lib/stream_weaver/skills/streamweaver-canvas-safe/references/tabs-and-navigation.md +53 -0
- data/lib/stream_weaver/skills/streamweaver-doc-builder/SKILL.md +298 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/SKILL.md +130 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.css +468 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example.rb +39 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/design-review-example_dsl.rb +300 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example.rb +24 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/examples/doc-parity-example_dsl.rb +385 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/checkpoints-and-forms.md +28 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/cleanup-and-panel.md +44 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/example-gallery.md +8 -0
- data/lib/stream_weaver/skills/streamweaver-visual-companion/references/persistence.md +58 -0
- data/lib/stream_weaver/skills/streamweaver-way/SKILL.md +396 -0
- data/lib/stream_weaver/skills/visual-plan/SKILL.md +201 -0
- data/lib/stream_weaver/skills/visual-recap/SKILL.md +244 -0
- data/lib/stream_weaver/streamer.rb +63 -0
- data/lib/stream_weaver/templates/choices.rb +149 -0
- data/lib/stream_weaver/templates/code.rb +157 -0
- data/lib/stream_weaver/templates/confirm.rb +110 -0
- data/lib/stream_weaver/templates/diff.rb +195 -0
- data/lib/stream_weaver/templates/info.rb +142 -0
- data/lib/stream_weaver/templates/table.rb +169 -0
- data/lib/stream_weaver/templates/wizard.rb +271 -0
- data/lib/stream_weaver/theme/auto_mode.rb +116 -0
- data/lib/stream_weaver/theme/presets.rb +555 -0
- data/lib/stream_weaver/theme.rb +639 -0
- data/lib/stream_weaver/university/canvas.rb +924 -0
- data/lib/stream_weaver/university/course.rb +699 -0
- data/lib/stream_weaver/university/demos/counter.rb +33 -0
- data/lib/stream_weaver/university/demos/dashboard.rb +147 -0
- data/lib/stream_weaver/university/demos/decision_form.rb +143 -0
- data/lib/stream_weaver/university/demos.rb +50 -0
- data/lib/stream_weaver/university/listener.rb +467 -0
- data/lib/stream_weaver/university/progress.rb +201 -0
- data/lib/stream_weaver/university/runner.rb +138 -0
- data/lib/stream_weaver/university/scripts/growing_doc.rb +609 -0
- data/lib/stream_weaver/university/scripts/growing_doc_state.rb +91 -0
- data/lib/stream_weaver/utils.rb +31 -0
- data/lib/stream_weaver/version.rb +5 -0
- data/lib/stream_weaver/views/canvas/reader_layout.erb +800 -0
- data/lib/stream_weaver/views.rb +3940 -0
- data/lib/stream_weaver.rb +122 -0
- data/llms.txt +1269 -0
- data/sig/stream_weaver.rbs +4 -0
- metadata +598 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Design Deck Demo: API Gateway Architecture Review
|
|
5
|
+
# ---------------------------------------------------
|
|
6
|
+
# A realistic architectural decision deck demonstrating the full
|
|
7
|
+
# design_deck component suite: slides, options with mermaid diagrams
|
|
8
|
+
# and code blocks, recommended badges, aside text, and theme presets.
|
|
9
|
+
|
|
10
|
+
require_relative '../../lib/stream_weaver'
|
|
11
|
+
|
|
12
|
+
App = app "API Gateway Architecture Review", theme: :dark do
|
|
13
|
+
theme_preset :technical
|
|
14
|
+
theme_toggle mode: :auto
|
|
15
|
+
|
|
16
|
+
design_deck "API Gateway Architecture Review" do
|
|
17
|
+
|
|
18
|
+
# =========================================================
|
|
19
|
+
# Slide 1: Gateway Topology
|
|
20
|
+
# =========================================================
|
|
21
|
+
slide "topology", "Gateway Topology",
|
|
22
|
+
context: "How should traffic enter our platform? This decision affects latency, blast radius, and operational complexity across all downstream services." do
|
|
23
|
+
|
|
24
|
+
option "Single Gateway",
|
|
25
|
+
aside: "Lowest operational overhead. One deployment to manage, but a single point of failure.",
|
|
26
|
+
recommended: false do
|
|
27
|
+
mermaid <<~MERMAID, compact: true
|
|
28
|
+
graph TD
|
|
29
|
+
Client([Client Apps]) --> GW[API Gateway]
|
|
30
|
+
GW --> AuthSvc[Auth Service]
|
|
31
|
+
GW --> OrderSvc[Order Service]
|
|
32
|
+
GW --> InventorySvc[Inventory Service]
|
|
33
|
+
GW --> NotifySvc[Notification Service]
|
|
34
|
+
style GW fill:#3b82f6,stroke:#1e40af,color:#fff
|
|
35
|
+
MERMAID
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
option "BFF Pattern (Backend for Frontend)",
|
|
39
|
+
aside: "Each client type gets a tailored gateway. Reduces payload bloat for mobile clients by 40-60%.",
|
|
40
|
+
recommended: true do
|
|
41
|
+
mermaid <<~MERMAID, compact: true
|
|
42
|
+
graph TD
|
|
43
|
+
WebApp([Web App]) --> WebBFF[Web BFF]
|
|
44
|
+
MobileApp([Mobile App]) --> MobileBFF[Mobile BFF]
|
|
45
|
+
Partner([Partner API]) --> PartnerBFF[Partner BFF]
|
|
46
|
+
WebBFF --> Services{Microservices}
|
|
47
|
+
MobileBFF --> Services
|
|
48
|
+
PartnerBFF --> Services
|
|
49
|
+
style WebBFF fill:#22c55e,stroke:#15803d,color:#fff
|
|
50
|
+
style MobileBFF fill:#22c55e,stroke:#15803d,color:#fff
|
|
51
|
+
style PartnerBFF fill:#22c55e,stroke:#15803d,color:#fff
|
|
52
|
+
MERMAID
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
option "Service Mesh with Sidecar",
|
|
56
|
+
aside: "Maximum observability and zero-trust networking. Higher infra cost and operational complexity." do
|
|
57
|
+
mermaid <<~MERMAID, compact: true
|
|
58
|
+
graph TD
|
|
59
|
+
LB([Load Balancer]) --> Ingress[Ingress Controller]
|
|
60
|
+
Ingress --> PodA
|
|
61
|
+
Ingress --> PodB
|
|
62
|
+
Ingress --> PodC
|
|
63
|
+
subgraph PodA [Pod A]
|
|
64
|
+
ProxyA[Envoy Sidecar] --> SvcA[Auth Service]
|
|
65
|
+
end
|
|
66
|
+
subgraph PodB [Pod B]
|
|
67
|
+
ProxyB[Envoy Sidecar] --> SvcB[Order Service]
|
|
68
|
+
end
|
|
69
|
+
subgraph PodC [Pod C]
|
|
70
|
+
ProxyC[Envoy Sidecar] --> SvcC[Inventory Service]
|
|
71
|
+
end
|
|
72
|
+
style ProxyA fill:#f59e0b,stroke:#b45309,color:#000
|
|
73
|
+
style ProxyB fill:#f59e0b,stroke:#b45309,color:#000
|
|
74
|
+
style ProxyC fill:#f59e0b,stroke:#b45309,color:#000
|
|
75
|
+
MERMAID
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# =========================================================
|
|
80
|
+
# Slide 2: Authentication Strategy
|
|
81
|
+
# =========================================================
|
|
82
|
+
slide "auth", "Authentication Strategy",
|
|
83
|
+
context: "The auth layer must support both first-party SPAs and third-party integrations with distinct security profiles. Target: <5ms overhead per request." do
|
|
84
|
+
|
|
85
|
+
option "JWT with Gateway Validation",
|
|
86
|
+
aside: "Stateless tokens validated at the edge. No downstream auth calls needed. Token revocation requires a deny-list.",
|
|
87
|
+
recommended: true do
|
|
88
|
+
code_block <<~CODE, lang: "yaml", file: "gateway/auth-policy.yaml", truncate: 14
|
|
89
|
+
apiVersion: security.istio.io/v1
|
|
90
|
+
kind: AuthorizationPolicy
|
|
91
|
+
metadata:
|
|
92
|
+
name: gateway-jwt-auth
|
|
93
|
+
namespace: api-gateway
|
|
94
|
+
spec:
|
|
95
|
+
selector:
|
|
96
|
+
matchLabels:
|
|
97
|
+
app: api-gateway
|
|
98
|
+
rules:
|
|
99
|
+
- from:
|
|
100
|
+
- source:
|
|
101
|
+
requestPrincipals: ["https://auth.platform.io/*"]
|
|
102
|
+
when:
|
|
103
|
+
- key: request.auth.claims[aud]
|
|
104
|
+
values: ["api.platform.io"]
|
|
105
|
+
- key: request.auth.claims[scope]
|
|
106
|
+
notValues: ["admin:*"]
|
|
107
|
+
CODE
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
option "OAuth2 + Opaque Tokens",
|
|
111
|
+
aside: "Tokens validated via introspection endpoint. Simpler revocation but adds ~8ms latency per request." do
|
|
112
|
+
mermaid <<~MERMAID, compact: true
|
|
113
|
+
sequenceDiagram
|
|
114
|
+
participant C as Client
|
|
115
|
+
participant GW as Gateway
|
|
116
|
+
participant AS as Auth Server
|
|
117
|
+
participant API as API Service
|
|
118
|
+
C->>GW: Request + Bearer Token
|
|
119
|
+
GW->>AS: POST /introspect
|
|
120
|
+
AS-->>GW: {active: true, scope: "read"}
|
|
121
|
+
GW->>API: Forward + X-User-Claims
|
|
122
|
+
API-->>GW: Response
|
|
123
|
+
GW-->>C: Response
|
|
124
|
+
MERMAID
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
option "mTLS + Service Accounts",
|
|
128
|
+
aside: "Certificate-based identity at transport layer. Best for service-to-service; poor UX for browser clients." do
|
|
129
|
+
code_block <<~CODE, lang: "go", file: "gateway/mtls_config.go", truncate: 16
|
|
130
|
+
package gateway
|
|
131
|
+
|
|
132
|
+
import (
|
|
133
|
+
"crypto/tls"
|
|
134
|
+
"crypto/x509"
|
|
135
|
+
"os"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
func NewMTLSConfig() *tls.Config {
|
|
139
|
+
caCert, _ := os.ReadFile("/etc/certs/ca.pem")
|
|
140
|
+
caCertPool := x509.NewCertPool()
|
|
141
|
+
caCertPool.AppendCertsFromPEM(caCert)
|
|
142
|
+
|
|
143
|
+
cert, _ := tls.LoadX509KeyPair(
|
|
144
|
+
"/etc/certs/gateway.pem",
|
|
145
|
+
"/etc/certs/gateway-key.pem",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return &tls.Config{
|
|
149
|
+
ClientCAs: caCertPool,
|
|
150
|
+
ClientAuth: tls.RequireAndVerifyClientCert,
|
|
151
|
+
Certificates: []tls.Certificate{cert},
|
|
152
|
+
MinVersion: tls.VersionTLS13,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
CODE
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# =========================================================
|
|
160
|
+
# Slide 3: Rate Limiting & Resilience
|
|
161
|
+
# =========================================================
|
|
162
|
+
slide "resilience", "Rate Limiting & Resilience",
|
|
163
|
+
context: "Our API serves 12k req/s at peak. We need to protect downstream services from cascading failures while maintaining fair access for all tenants." do
|
|
164
|
+
|
|
165
|
+
option "Token Bucket per Tenant",
|
|
166
|
+
aside: "Simple, predictable quotas. Each tenant gets a fixed bucket refilled at a steady rate. Easy to reason about.",
|
|
167
|
+
recommended: true do
|
|
168
|
+
code_block <<~CODE, lang: "ruby", file: "lib/rate_limiter/token_bucket.rb"
|
|
169
|
+
class TokenBucket
|
|
170
|
+
LUA_SCRIPT = <<~LUA
|
|
171
|
+
local key = KEYS[1]
|
|
172
|
+
local capacity = tonumber(ARGV[1])
|
|
173
|
+
local refill_rate = tonumber(ARGV[2])
|
|
174
|
+
local now = tonumber(ARGV[3])
|
|
175
|
+
local requested = tonumber(ARGV[4])
|
|
176
|
+
|
|
177
|
+
local bucket = redis.call('hmget', key, 'tokens', 'last_refill')
|
|
178
|
+
local tokens = tonumber(bucket[1]) or capacity
|
|
179
|
+
local last = tonumber(bucket[2]) or now
|
|
180
|
+
|
|
181
|
+
local elapsed = math.max(0, now - last)
|
|
182
|
+
tokens = math.min(capacity, tokens + elapsed * refill_rate)
|
|
183
|
+
|
|
184
|
+
if tokens >= requested then
|
|
185
|
+
tokens = tokens - requested
|
|
186
|
+
redis.call('hmset', key, 'tokens', tokens, 'last_refill', now)
|
|
187
|
+
redis.call('expire', key, math.ceil(capacity / refill_rate) * 2)
|
|
188
|
+
return 1
|
|
189
|
+
end
|
|
190
|
+
return 0
|
|
191
|
+
LUA
|
|
192
|
+
|
|
193
|
+
def initialize(redis, capacity: 100, refill_rate: 10)
|
|
194
|
+
@redis = redis
|
|
195
|
+
@capacity = capacity
|
|
196
|
+
@refill_rate = refill_rate
|
|
197
|
+
@sha = redis.script(:load, LUA_SCRIPT)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def allow?(tenant_id, tokens: 1)
|
|
201
|
+
@redis.evalsha(@sha, keys: ["rl:\#{tenant_id}"],
|
|
202
|
+
argv: [@capacity, @refill_rate, Time.now.to_f, tokens]) == 1
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
CODE
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
option "Sliding Window Counter",
|
|
209
|
+
aside: "More accurate than fixed windows. Prevents burst-at-boundary attacks but uses more Redis memory." do
|
|
210
|
+
mermaid <<~MERMAID, compact: true
|
|
211
|
+
graph LR
|
|
212
|
+
subgraph Window ["Sliding Window (60s)"]
|
|
213
|
+
W1[T-60s<br/>Count: 45] -.->|weight: 0.3| Calc((Weighted<br/>Sum))
|
|
214
|
+
W2[T-0s<br/>Count: 30] -.->|weight: 0.7| Calc
|
|
215
|
+
end
|
|
216
|
+
Calc -->|"Est: 34.5"| Decision{Under<br/>Limit?}
|
|
217
|
+
Decision -->|Yes| Allow[Allow]
|
|
218
|
+
Decision -->|No| Reject[429 Too Many]
|
|
219
|
+
style Allow fill:#22c55e,stroke:#15803d,color:#fff
|
|
220
|
+
style Reject fill:#ef4444,stroke:#dc2626,color:#fff
|
|
221
|
+
MERMAID
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
option "Adaptive Circuit Breaker",
|
|
225
|
+
aside: "Dynamically adjusts limits based on downstream health. Self-healing but harder to set tenant expectations." do
|
|
226
|
+
mermaid <<~MERMAID, compact: true
|
|
227
|
+
stateDiagram-v2
|
|
228
|
+
[*] --> Closed
|
|
229
|
+
Closed --> Open : Failure rate > 50%
|
|
230
|
+
Open --> HalfOpen : After cooldown (30s)
|
|
231
|
+
HalfOpen --> Closed : Probe succeeds
|
|
232
|
+
HalfOpen --> Open : Probe fails
|
|
233
|
+
note right of Closed : All requests pass through
|
|
234
|
+
note right of Open : All requests rejected (503)
|
|
235
|
+
note right of HalfOpen : Single probe request allowed
|
|
236
|
+
MERMAID
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# =========================================================
|
|
241
|
+
# Slide 4: Observability Stack
|
|
242
|
+
# =========================================================
|
|
243
|
+
slide "observability", "Observability Stack",
|
|
244
|
+
context: "We need distributed tracing, metrics, and structured logging across all gateway instances. Must integrate with existing Grafana dashboards." do
|
|
245
|
+
|
|
246
|
+
option "OpenTelemetry Collector Pipeline",
|
|
247
|
+
aside: "Vendor-neutral, CNCF-backed standard. One SDK for traces, metrics, and logs. Growing ecosystem.",
|
|
248
|
+
recommended: true do
|
|
249
|
+
mermaid <<~MERMAID, compact: true
|
|
250
|
+
graph LR
|
|
251
|
+
GW[Gateway<br/>OTel SDK] -->|OTLP/gRPC| Collector[OTel Collector]
|
|
252
|
+
SvcA[Service A<br/>OTel SDK] -->|OTLP/gRPC| Collector
|
|
253
|
+
SvcB[Service B<br/>OTel SDK] -->|OTLP/gRPC| Collector
|
|
254
|
+
Collector -->|Traces| Tempo[(Tempo)]
|
|
255
|
+
Collector -->|Metrics| Prom[(Prometheus)]
|
|
256
|
+
Collector -->|Logs| Loki[(Loki)]
|
|
257
|
+
Tempo --> Grafana[Grafana]
|
|
258
|
+
Prom --> Grafana
|
|
259
|
+
Loki --> Grafana
|
|
260
|
+
style Collector fill:#6366f1,stroke:#4338ca,color:#fff
|
|
261
|
+
style Grafana fill:#f97316,stroke:#c2410c,color:#fff
|
|
262
|
+
MERMAID
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
option "Datadog APM",
|
|
266
|
+
aside: "Full-featured SaaS. Fastest time to value. $23/host/month for APM, plus log ingestion costs." do
|
|
267
|
+
code_block <<~CODE, lang: "ruby", file: "config/initializers/datadog.rb"
|
|
268
|
+
require 'datadog/tracing'
|
|
269
|
+
require 'datadog/appsec'
|
|
270
|
+
|
|
271
|
+
Datadog.configure do |c|
|
|
272
|
+
c.service = 'api-gateway'
|
|
273
|
+
c.env = ENV.fetch('RACK_ENV', 'development')
|
|
274
|
+
c.version = ENV.fetch('APP_VERSION', '0.0.0')
|
|
275
|
+
|
|
276
|
+
c.tracing.instrument :rack
|
|
277
|
+
c.tracing.instrument :http
|
|
278
|
+
c.tracing.instrument :redis
|
|
279
|
+
c.tracing.instrument :pg
|
|
280
|
+
|
|
281
|
+
c.tracing.sampling.default_rate = 0.1
|
|
282
|
+
c.tracing.sampling.rate_limit = 100
|
|
283
|
+
|
|
284
|
+
c.appsec.enabled = true
|
|
285
|
+
c.appsec.ip_passlist = ENV.fetch('TRUSTED_PROXIES', '').split(',')
|
|
286
|
+
|
|
287
|
+
c.runtime_metrics.enabled = true
|
|
288
|
+
c.runtime_metrics.statsd.host = ENV.fetch('DD_AGENT_HOST', 'localhost')
|
|
289
|
+
end
|
|
290
|
+
CODE
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
option "ELK Stack (Self-Hosted)",
|
|
294
|
+
aside: "Full control over data retention and cost. Requires dedicated SRE capacity for Elasticsearch cluster management." do
|
|
295
|
+
mermaid <<~MERMAID, compact: true
|
|
296
|
+
graph TD
|
|
297
|
+
GW[Gateway] -->|Filebeat| Logstash[Logstash]
|
|
298
|
+
SvcA[Service A] -->|Filebeat| Logstash
|
|
299
|
+
SvcB[Service B] -->|Filebeat| Logstash
|
|
300
|
+
Logstash -->|Index| ES[(Elasticsearch<br/>Cluster)]
|
|
301
|
+
ES --> Kibana[Kibana]
|
|
302
|
+
GW -->|StatsD| Telegraf[Telegraf]
|
|
303
|
+
Telegraf --> InfluxDB[(InfluxDB)]
|
|
304
|
+
InfluxDB --> GrafanaInf[Grafana]
|
|
305
|
+
style ES fill:#f59e0b,stroke:#b45309,color:#000
|
|
306
|
+
style Logstash fill:#22c55e,stroke:#15803d,color:#fff
|
|
307
|
+
MERMAID
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
App.run! if __FILE__ == $0
|