diff --git a/.cursor/rules/always.mdc b/.cursor/rules/always.mdc new file mode 100644 index 000000000..ab868fe4a --- /dev/null +++ b/.cursor/rules/always.mdc @@ -0,0 +1,11 @@ +--- +description: +globs: +alwaysApply: true +--- +# Testing commands that are expected to stay running until cancelled +* Use the unix `timeout` (`gtimeout` on macos) command, so that they don't stay running forever +* Choose a wait time that makes it very likely a success or failure will surface before the `timeout` command cancels it +```sh +timeout 10s +``` \ No newline at end of file diff --git a/.cursor/rules/ddd-workshop-content.mdc b/.cursor/rules/ddd-workshop-content.mdc new file mode 100644 index 000000000..fcceaf33b --- /dev/null +++ b/.cursor/rules/ddd-workshop-content.mdc @@ -0,0 +1,25 @@ +--- +description: +globs: packages/website/content/blog/**/*.md +alwaysApply: false +--- +## TypeScript Code snippets +* MUST be in fenced markdown blocks like +```ts twoslash +// TypeScript code goes here +``` +* SHOULD include [twoslash notations](mdc:https:/twoslash.netlify.app/refs/notations) and [options](mdc:https:/twoslash.netlify.app/refs/options) where appropriate +* MAY include snippets with errors, as long as the "// @error " option is used + + +## Example project +* A central project that threads concepts throughout the course together is conducive to learning DDD -- particularly as "managing complexity" is a key DDD benefit +* As we teach students a concept in a chapter, we should consider applying that knowledge to the example project as we go. +* It's great to lead students right into a problem or shortcoming (e.g. we make a change that makes an architectural limitation very clear and visceral) and then proceed to teaching a concept and applying it to the example project to alleviate/fix the problem. +* The project should start out very simple, and piece by piece, new DDD concepts should be introduced, and we then should be given a "customer ask" or a business challenge that involves applying the DDD concepts +* It's ok for this project to be "backend only" in the interest of maximizing focus on teaching the DDD concepts and interaction model, rather than focusing on wiring up a bunch of different full-stack software components + wing plants from seed in a hydroponic system, there's no concept of full vs partial sun, no concept of periodic watering, no concept of "plant spacing" -- but this all becomes very important once the plants go into raised beds + +* The theme of the example project should start out as a simple Gardening App. DDD concepts should be introduced, and then + + diff --git a/.cursor/rules/teaching.mdc b/.cursor/rules/teaching.mdc new file mode 100644 index 000000000..ff2db9504 --- /dev/null +++ b/.cursor/rules/teaching.mdc @@ -0,0 +1,201 @@ +--- +description: +globs: +alwaysApply: false +--- +# TypeScript Course Style Guide + +## Teaching Approach and Structure + +1. **Progressive Complexity** + - Courses follow a logical progression from fundamental concepts to advanced topics + - Content is organized numerically (01-intro, 02-hello-typescript, etc.) creating a clear learning path + - Each concept builds on previous knowledge while introducing new material + +2. **Hands-on Learning** + - Practical examples are emphasized over theoretical discussion + - Code snippets are interactive and demonstrate real-world use cases + - Students are encouraged to follow along with provided code repositories + +3. **Mental Model Development** + - Focus on building strong foundational understanding rather than just syntax + - Clear articulation of the "why" behind TypeScript features and behaviors + - Emphasis on developing intuition for type systems + - Consistent use of types-as-sets metaphor to explain type relationships + +4. **Theme-Based Learning** + - Use of recurring themes (like "Inferring with non-intrusive specificity") to connect concepts + - Highlighting patterns that appear throughout the TypeScript ecosystem + - Building mental frameworks that can be applied to new TypeScript challenges + +5. **Challenge-Driven Learning** + - Game-like challenges that test comprehension at various levels of complexity + - Real-world coding challenges based on authentic scenarios + - Interactive exercises that require active application of concepts + - Structured challenges with clear requirements and incremental difficulty + +## Content Organization + +1. **Chapter Structure** + - Clear frontmatter with title, date, description, course name, and order + - Concise chapter goals outlined at the beginning + - Chapters focused on a single core concept with related sub-topics + - Gradual building from basic to advanced applications of each concept + +2. **Content Progression** + - Introduction to core concepts + - Progressive examples showing increasing complexity + - Practical applications and exercises + - Edge cases and common pitfalls + - Advanced usage patterns (when appropriate) + - Best practices and recommendations at the end of complex topics + +3. **Course Levels** + - Fundamentals: Core TypeScript concepts for beginners + - Intermediate: More complex type operations and patterns + - Enterprise: Production-grade TypeScript for real-world applications + - Applied: Challenge-driven courses to cement understanding + +## Presentation Style + +1. **Code Examples** + - Well-commented code with clear annotations + - Use of the "twoslash" directive for enhanced code display + - Hover-effect tooltips to demonstrate type inference + - Before/after examples to show evolution of code + - Use of line highlighting to draw attention to important code changes + +2. **Visual Learning** + - Strategic use of images, GIFs, and diagrams to illustrate complex concepts + - Code highlighting to draw attention to important changes or concepts + - Collapsible sections for additional details or large code examples + - Callout boxes for important information, warnings, and tips + +3. **Explanation Techniques** + - Clear, concise explanations avoiding unnecessary jargon + - Use of metaphors and analogies to explain abstract concepts + - Breaking down complex types step-by-step + - Comparisons between TypeScript and plain JavaScript behavior + - Error message analysis to help students understand compiler feedback + +4. **Conceptual Frameworks** + - Presenting type systems in terms of sets and subsets + - Explaining type compatibility through clear rules and principles + - Consistent terminology throughout explanations + - Contrasting different type system approaches (structural vs. nominal) + +5. **Gamification Elements** + - "Does it compile?" quizzes with hidden answers + - "Guess that type" exercises to practice type inference + - Interactive games like "Typepardy" that use familiar formats + - Competitive elements that encourage engagement + - Use of "challenges" with clear success criteria + +## Pedagogical Elements + +1. **Interactive Learning** + - Exercises integrated into course material + - Challenges that encourage applying concepts in new ways + - Clear project setup instructions to minimize environment issues + - Progressive code examples that build on previous knowledge + +2. **Conceptual Reinforcement** + - Frequent recapping of key concepts + - Real-world examples showing practical applications + - Highlighting edge cases and potential pitfalls + - Connecting new concepts to previously learned material + +3. **Addressing Different Learning Styles** + - Visual learners: Diagrams, code highlights, GIFs + - Hands-on learners: Interactive examples, exercises + - Conceptual learners: Thorough explanations of "why" + - Practical learners: Real-world use cases and implications + +4. **Motivating Examples** + - Beginning complex topics with real-world use cases that demonstrate value + - Showing problems that arise without the concept before introducing it + - Building solutions step-by-step to demonstrate thought process + +5. **Retention Strategies** + - Spacing practice over time rather than concentrated learning + - Incorporating "desirable difficulty" in exercises + - Encouraging journaling and self-assessment + - Varied presentation of similar concepts to reinforce understanding + - Balancing rule-driven and example-driven approaches + +## Technical Setup and Tools + +1. **Environment Setup** + - Quick setup guides using tools like Volta for consistent environments + - Minimal dependencies to reduce setup friction + - Clear instructions for running examples locally + +2. **Code Repository Organization** + - Well-structured monorepo approach + - Consistent package organization across courses + - Progressive example complexity within each module + - Separate challenge directories with starter code and tests + +3. **Tool Integration** + - Use of TypeScript compiler features to demonstrate concepts + - Integration with modern development tools and practices + - Demonstrations of real-world configuration patterns + - Showing compiler output and behavior in different scenarios + +## Best Practices Approach + +1. **Actionable Guidelines** + - Clear best practices included with complex topics + - Explanation of why best practices matter, not just what they are + - Examples of both good and problematic code patterns + - Focus on maintainable, team-friendly approaches + +2. **Contextual Recommendations** + - Acknowledging tradeoffs in different approaches + - Providing guidance for when to use different techniques + - Highlighting common pitfalls and how to avoid them + +## Advanced Teaching Techniques + +1. **Comparative Learning** + - Using comparison tables to highlight differences (e.g., covariance vs. contravariance) + - Demonstrating subtle distinctions between similar concepts + - Visual organization of complex type relationships + - Consistent table formatting to aid pattern recognition + +2. **Interactive Quizzes** + - Embedding quizzes with spoiler-protected answers + - Challenge questions that test understanding of core concepts + - Opportunities for self-assessment throughout the material + - Carefully constructed examples that reveal common misconceptions + +3. **Concept Building Through Refinement** + - Starting with flawed approaches and iteratively improving them + - Clearly marking anti-patterns with explicit warnings + - Showing the evolution of a solution through progressive refinement + - Demonstrating debugging thought processes + +4. **Cross-Module References** + - Linking to related concepts across different courses + - Building on previously established knowledge + - Revisiting earlier concepts with more advanced understanding + - Creating a cohesive learning journey across course boundaries + +5. **Utility Type Deconstruction** + - Breaking down complex utility types into understandable components + - Explaining built-in TypeScript utilities by rebuilding them + - Using type helpers to illustrate advanced type manipulation + - Progressive complexity in type transformations + +6. **Visual Type Modeling** + - Using directional arrows to represent type relationships + - Creating visual metaphors for complex type concepts + - Consistent visual language across advanced topics + - Diagrams to represent abstract type concepts + +7. **Deliberate Practice Design** + - Challenges that require combining multiple type system concepts + - Problems drawn from real-world TypeScript usage + - Exercises that mimic actual development scenarios + - Scaffolded challenges with hints and solution guidance + - Test-driven approach to validate understanding \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..d9f5ef73e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.gif filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci-website.yml b/.github/workflows/ci-website.yml index 889202d9d..1377472f6 100644 --- a/.github/workflows/ci-website.yml +++ b/.github/workflows/ci-website.yml @@ -52,8 +52,8 @@ jobs: - run: yarn - name: Build run: yarn build - - name: Lint - run: yarn lint + # - name: Lint + # run: yarn lint - name: Test run: yarn test - name: Cypress diff --git a/.vscode/settings.json b/.vscode/settings.json index 8af5f3b3f..42d28d8e7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -73,5 +73,25 @@ "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#65c89b", + "activityBar.background": "#65c89b", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#945bc4", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#15202b99", + "sash.hoverBorder": "#65c89b", + "statusBar.background": "#42b883", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#359268", + "statusBarItem.remoteBackground": "#42b883", + "statusBarItem.remoteForeground": "#15202b", + "titleBar.activeBackground": "#42b883", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#42b88399", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#42b883" } \ No newline at end of file diff --git a/packages/chat/assets/img/angry-cat.jpg b/packages/chat/assets/img/angry-cat.jpg index 9711d385b..df0876dcc 100644 Binary files a/packages/chat/assets/img/angry-cat.jpg and b/packages/chat/assets/img/angry-cat.jpg differ diff --git a/packages/chat/assets/img/avengers.jpg b/packages/chat/assets/img/avengers.jpg index 8c0976bfb..0387df466 100644 Binary files a/packages/chat/assets/img/avengers.jpg and b/packages/chat/assets/img/avengers.jpg differ diff --git a/packages/chat/assets/img/boss.jpg b/packages/chat/assets/img/boss.jpg index 02f774345..45665f56d 100644 Binary files a/packages/chat/assets/img/boss.jpg and b/packages/chat/assets/img/boss.jpg differ diff --git a/packages/chat/assets/img/cat.jpg b/packages/chat/assets/img/cat.jpg index cb8017f4c..1971f07ed 100644 Binary files a/packages/chat/assets/img/cat.jpg and b/packages/chat/assets/img/cat.jpg differ diff --git a/packages/chat/assets/img/clippy.png b/packages/chat/assets/img/clippy.png index e7619b7b4..25bbc16f2 100644 Binary files a/packages/chat/assets/img/clippy.png and b/packages/chat/assets/img/clippy.png differ diff --git a/packages/chat/assets/img/colonel-meow.jpg b/packages/chat/assets/img/colonel-meow.jpg index 2797b1fbc..a9d300e51 100644 Binary files a/packages/chat/assets/img/colonel-meow.jpg and b/packages/chat/assets/img/colonel-meow.jpg differ diff --git a/packages/chat/assets/img/desk_flip.jpg b/packages/chat/assets/img/desk_flip.jpg index 35ec7f301..b5d027c6d 100644 Binary files a/packages/chat/assets/img/desk_flip.jpg and b/packages/chat/assets/img/desk_flip.jpg differ diff --git a/packages/chat/assets/img/dilbert.jpg b/packages/chat/assets/img/dilbert.jpg index 5c0196356..8b4a11c5e 100644 Binary files a/packages/chat/assets/img/dilbert.jpg and b/packages/chat/assets/img/dilbert.jpg differ diff --git a/packages/chat/assets/img/drstrange.jpg b/packages/chat/assets/img/drstrange.jpg index 30b467fd6..fc2cd9915 100644 Binary files a/packages/chat/assets/img/drstrange.jpg and b/packages/chat/assets/img/drstrange.jpg differ diff --git a/packages/chat/assets/img/ironman.jpg b/packages/chat/assets/img/ironman.jpg index c2ae82bcb..36894b0b2 100644 Binary files a/packages/chat/assets/img/ironman.jpg and b/packages/chat/assets/img/ironman.jpg differ diff --git a/packages/chat/assets/img/jquery.png b/packages/chat/assets/img/jquery.png index 4a90efca7..7df51161f 100644 Binary files a/packages/chat/assets/img/jquery.png and b/packages/chat/assets/img/jquery.png differ diff --git a/packages/chat/assets/img/js.png b/packages/chat/assets/img/js.png index 21d32b05b..3d515f6eb 100644 Binary files a/packages/chat/assets/img/js.png and b/packages/chat/assets/img/js.png differ diff --git a/packages/chat/assets/img/linkedin.png b/packages/chat/assets/img/linkedin.png index d9e13c825..c8e143930 100644 Binary files a/packages/chat/assets/img/linkedin.png and b/packages/chat/assets/img/linkedin.png differ diff --git a/packages/chat/assets/img/lisa.jpeg b/packages/chat/assets/img/lisa.jpeg index c7d644a65..f380b7ed6 100644 Binary files a/packages/chat/assets/img/lisa.jpeg and b/packages/chat/assets/img/lisa.jpeg differ diff --git a/packages/chat/assets/img/maru.jpg b/packages/chat/assets/img/maru.jpg index b6d4da18d..7f3f25df4 100644 Binary files a/packages/chat/assets/img/maru.jpg and b/packages/chat/assets/img/maru.jpg differ diff --git a/packages/chat/assets/img/microsoft.png b/packages/chat/assets/img/microsoft.png index 8b78fd81c..c940e090d 100644 Binary files a/packages/chat/assets/img/microsoft.png and b/packages/chat/assets/img/microsoft.png differ diff --git a/packages/chat/assets/img/mike.jpeg b/packages/chat/assets/img/mike.jpeg index f74b91e0e..08e37120d 100644 Binary files a/packages/chat/assets/img/mike.jpeg and b/packages/chat/assets/img/mike.jpeg differ diff --git a/packages/chat/assets/img/node.png b/packages/chat/assets/img/node.png index f29598c35..a02f4b37e 100644 Binary files a/packages/chat/assets/img/node.png and b/packages/chat/assets/img/node.png differ diff --git a/packages/chat/assets/img/office97.png b/packages/chat/assets/img/office97.png index 322a0b9ab..263961356 100644 Binary files a/packages/chat/assets/img/office97.png and b/packages/chat/assets/img/office97.png differ diff --git a/packages/chat/assets/img/thor.jpg b/packages/chat/assets/img/thor.jpg index 6147afa7a..9489d75d3 100644 Binary files a/packages/chat/assets/img/thor.jpg and b/packages/chat/assets/img/thor.jpg differ diff --git a/packages/chat/assets/img/ts.png b/packages/chat/assets/img/ts.png index 20b480a29..fc53c2b0f 100644 Binary files a/packages/chat/assets/img/ts.png and b/packages/chat/assets/img/ts.png differ diff --git a/packages/notes-intermediate-ts-v2/src/05-modules-and-cjs-interop/ts-logo.png b/packages/notes-intermediate-ts-v2/src/05-modules-and-cjs-interop/ts-logo.png index 28d71eb7c..68aa5b47f 100644 Binary files a/packages/notes-intermediate-ts-v2/src/05-modules-and-cjs-interop/ts-logo.png and b/packages/notes-intermediate-ts-v2/src/05-modules-and-cjs-interop/ts-logo.png differ diff --git a/packages/website/.eslintrc b/packages/website/.eslintrc index 5d4c9ae2e..4b6395c54 100644 --- a/packages/website/.eslintrc +++ b/packages/website/.eslintrc @@ -7,7 +7,7 @@ "version": "detect" } }, - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 12 diff --git a/packages/website/cml.tmLanguage.json b/packages/website/cml.tmLanguage.json new file mode 100644 index 000000000..2b526fec8 --- /dev/null +++ b/packages/website/cml.tmLanguage.json @@ -0,0 +1,106 @@ +{ + "name": "Context Mapper DSL", + "scopeName": "source.cml", + "fileTypes": [ + "cml" + ], + "repository": { + "general": { + "patterns": [ + { + "include": "#linecomment" + }, + { + "include": "#blockcomment" + }, + { + "include": "#contextMapKeywords" + }, + { + "include": "#boundedContextKeywords" + }, + { + "include": "#domainKeywords" + }, + { + "include": "#applicationLayerKeywords" + }, + { + "include": "#userRequirementKeywords" + }, + { + "include": "#tacticDDDKeywords" + }, + { + "include": "#stakeholderKeyWords" + }, + { + "include": "#valueRegisterKeyWords" + }, + { + "include": "#stringsSingle" + }, + { + "include": "#stringsDouble" + } + ] + }, + "linecomment": { + "name": "comment.line.double-dash.cml", + "begin": "(^[ \\t]+)?(?=//)", + "end": "(?=$)" + }, + "blockcomment": { + "name": "comment.block.cml", + "begin": "/\\*(\\*)?(?!/)", + "end": "\\*/" + }, + "contextMapKeywords": { + "name": "keyword.control.cml", + "match": "\\b(import|ContextMap|type|state|contains|P|Partnership|SK|Shared-Kernel|implementationTechnology|U|D|C|S|OHS|PL|ACL|CF|Upstream-Downstream|Downstream-Upstream|Customer-Supplier|Supplier-Customer|exposedAggregates|downstreamRights|UNDEFINED|SYSTEM_LANDSCAPE|ORGANIZATIONAL|AS_IS|TO_BE|INFLUENCER|OPINION_LEADER|VETO_RIGHT|DECISION_MAKER|MONOPOLIST)\\b" + }, + "boundedContextKeywords": { + "name": "keyword.control.cml", + "match": "\\b(BoundedContext|implements|realizes|refines|domainVisionStatement|type|responsibilities|implementationTechnology|knowledgeLevel|UNDEFINED|FEATURE|APPLICATION|SYSTEM|TEAM|META|CONCRETE)\\b" + }, + "domainKeywords": { + "name": "keyword.control.cml", + "match": "\\b(Domain|domainVisionStatement|Subdomain|type|domainVisionStatement|CORE_DOMAIN|SUPPORTING_DOMAIN|GENERIC_SUBDOMAIN)\\b" + }, + "userRequirementKeywords": { + "name": "keyword.control.cml", + "match": "\\b(UseCase|UserStory|actor|secondaryActors|interactions|benefit|scope|level|As a|As an|I want to|so that|true|a|an|the|with its|with their|in|for|to|create|read|update|delete|supports|split by|and that|is|are|promoted|accepting that|reduced|harmed)\\b" + }, + "applicationLayerKeywords": { + "name": "keyword.control.cml", + "match": "\\b(Application|Flow|delegates to|emits event|event|triggers|command|operation|X|x|O|o|initiated|by|Coordination)\\b" + }, + "tacticDDDKeywords": { + "name": "keyword.control.cml", + "match": "\\b(Module|external|basePackage|hint|Aggregate|responsibilities|useCases|userRequirements|userStories|features|owner|knowledgeLevel|likelihoodForChange|structuralVolatility|contentVolatility|availabilityCriticality|consistencyCriticality|storageSimilarity|securityCriticality|securityZone|securityAccessGroup|Service|gap|nogap|webservice|Resource|scaffold|path|Consumer|unmarshall to|queueName|topicName|subscribe to|eventBus|publish|to|void|throws|hint|path|return|GET|POST|PUT|DELETE|abstract|Entity|extends|with|optimisticLocking|optimisticLocking|auditable|cache|databaseTable|discriminatorValue|discriminatorColumn|discriminatorType|discriminatorLength|inheritanceType|validate|aggregateRoot|belongsTo|ValueObject|DomainEvent|Event|CommandEvent|Command|Trait|package|def|DataTransferObject|BasicType|immutable|key|unique|changeable|required|nullable|index|digits|email|future|past|max|min|decimalMax|decimalMin|notEmpty|notBlank|pattern|range|size|length|scriptAssert|url|validate|transient|databaseColumn|databaseType|cascade|fetch|orderby|orderColumn|Repository|enum|ordinal|JOINED|SINGLE_TABLE|STRING|CHAR|INTEGER|String|int|Integer|long|Long|boolean|Boolean|Date|DateTime|Timestamp|BigDecimal|BigInteger|double|Double|float|Float|Key|PagingParameter|PagedResult|Blob|Clob|Object|Set|List|Bag|Collection|Map|public|protected|private|package|not|delegates|to|opposite|reference|aggregateLifecycle|read|-only|write)\\b" + }, + "stakeholderKeyWords": { + "name": "keyword.control.cml", + "match": "\\b(Stakeholders|of|StakeholderGroup|Stakeholder|influence|interest|description|UNDEFINED|HIGH|MEDIUM|LOW)\\b" + }, + "valueRegisterKeyWords": { + "name": "keyword.control.cml", + "match": "\\b(ValueRegister|for|ValueCluster|core|demonstrator|relatedValue|opposingValue|Value|isCore|Stakeholder|Stakeholders|priority|impact|consequences|ValueEpic|As a|I value|as demonstrated in|realization of|reduction of|ValueNarrative|When the SOI executes|stakeholders expect it to promote|protect or create|possibly degrading or prohibiting|with the following externally observable and|or internally auditable behavior|ValueWeigthing|In the context of the SOI|stakeholder|values|more than|expecting benefits such as|running the risk of harms such as|good|bad|neutral|action|ACT|MONITOR|UNDEFINED|HIGH|MEDIUM|LOW|AUTONOMY|CARE|CONTROL|FAIRNESS|INCLUSIVENESS|INNOVATION|PERFECTION|PRIVACY|RESPECT|SUSTAINABILITY|TRANSPARENCY|TRUST)\\b" + }, + "stringsSingle": { + "name": "string.quoted.single.cml", + "begin": "'", + "end": "'" + }, + "stringsDouble": { + "name": "string.quoted.double.cml", + "begin": "\"", + "end": "\"" + } + }, + "patterns": [ + { + "include": "#general" + } + ] +} \ No newline at end of file diff --git a/packages/website/content/assets/GitHub-Mark-32px.png b/packages/website/content/assets/GitHub-Mark-32px.png index 912236b38..9465cd6af 100644 Binary files a/packages/website/content/assets/GitHub-Mark-32px.png and b/packages/website/content/assets/GitHub-Mark-32px.png differ diff --git a/packages/website/content/assets/profile-pic.jpg b/packages/website/content/assets/profile-pic.jpg index f7a9d7b38..a1f6ed80b 100644 Binary files a/packages/website/content/assets/profile-pic.jpg and b/packages/website/content/assets/profile-pic.jpg differ diff --git a/packages/website/content/assets/ts-logo-512.png b/packages/website/content/assets/ts-logo-512.png index 28d71eb7c..9373696aa 100644 Binary files a/packages/website/content/assets/ts-logo-512.png and b/packages/website/content/assets/ts-logo-512.png differ diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/.gitattributes b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/.gitattributes new file mode 100644 index 000000000..7dedf4f29 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/.gitattributes @@ -0,0 +1,4 @@ +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/catalogs-on-table.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/catalogs-on-table.jpeg new file mode 100644 index 000000000..a482512b1 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/catalogs-on-table.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dce4ceda0d484667850d91957fd7a17a965cd71da43fd623d3eb5d8b4cc69b69 +size 340749 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/garden-ui.png b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/garden-ui.png new file mode 100644 index 000000000..10cde3a66 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/garden-ui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97d93896241da25c37e5bba455c0a44683e1ff7e7f2862b7dcddac9c254c8b56 +size 280491 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/hydroponics.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/hydroponics.jpeg new file mode 100644 index 000000000..62fb89ef6 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/hydroponics.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64e2891f9eaf1ca6716c46c706d9ed6d8fcc4f2c7dd7a0eb575f82d24611057b +size 28316 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1-sm.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1-sm.jpeg new file mode 100644 index 000000000..017463b45 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1-sm.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c8cb0d0aa7753ffc75a6f579d0c21921566f9a7877f2518bd912bf336f9b266 +size 57736 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1.jpeg new file mode 100644 index 000000000..6da7ce248 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-1.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5bea29c1bebf0007d449dd4d4da1ee86f2f17ed71df4e3f26fa88daa9416870 +size 311923 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2-sm.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2-sm.jpeg new file mode 100644 index 000000000..dba002374 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2-sm.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f21b11d52dc6f91247bb3187b4289dbe358c71cc4daaf9090b967ff8528f748b +size 68032 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2.jpeg new file mode 100644 index 000000000..21a3a6ab8 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-2.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be8a16e6fc3fa2ee67afec18f149db07dea430dbb7805ad7fcce2bdfa3ceed96 +size 350603 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3-sm.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3-sm.jpeg new file mode 100644 index 000000000..ac7b9f665 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3-sm.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4843712ee00a07def58d0d50a203d78e97397ba3df5fb5f4383689e1a3f2f5c +size 66449 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3.jpeg new file mode 100644 index 000000000..c13f30297 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/my-garden-3.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83e8a142a82a3ec8a790a6fb2dd0159cbdc3b892a88eed25dd0d2ac0a2771e00 +size 351019 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seed-catalog-ui.png b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seed-catalog-ui.png new file mode 100644 index 000000000..c60e28b11 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seed-catalog-ui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31de797b5fedfa0ba4c434fdb220d04c6ef228df7f65c954b0ae13918a1f8e70 +size 279053 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seeds-on-table.jpeg b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seeds-on-table.jpeg new file mode 100644 index 000000000..c85dd8529 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/img/seeds-on-table.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d3025b8f6522be4df0819318ebeb2c8b1e5209f0c4d8f09a250b8171760fbca +size 367124 diff --git a/packages/website/content/blog/domain-modeling-with-ts/01-intro/index.md b/packages/website/content/blog/domain-modeling-with-ts/01-intro/index.md new file mode 100644 index 000000000..7792dd01a --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/01-intro/index.md @@ -0,0 +1,69 @@ +--- +title: "Introduction" +order: 1 +date: "2025-06-10T09:00:00.000Z" +description: "Workshop overview, DDD motivation, and real-world context." +course: domain-modeling-with-ts +--- + + + +## What is Domain Modeling? + +- Creating a **conceptual representation of a business problem and its rules** +- Involves identifying key entities, their relationships and constraints +- Translating this understanding into a structured model that can guide software design + +An example from Stripe + +- Key entities: Payment, Dispute +- Relationship: Disputes must be associated with a payment +- Constraints: A dispute can only be opened if a payment has been successfully processed + +We'll be talking a bit about _Domain Driven Design_ in this course. + +- An approach to building software such that it aligns well with business needs +- Emphasizes collaboration between technical and domain experts +- Involves defining a common language that developers and stakeholders can have common understanding and clear discussions + +Modeling a domain involves coming up with an accurate understanding of how a problem space is structured, and how users or business stakeholders engage with it. + +## Why I'm here to talk about it + +- Agentic coding is changing the way we write software. Having well-defined software that clearly groups related concepts together allows LLMs to participate in the "shared language" +- At Stripe, I'm the Product Architect for Stripe's developer platform. An important part of my job is making sure, across the company, that our products fit or compose together in ways that solve real problems for Stripe's users +- Learning how to practice domain modeling well, and the collaborative skills that DDD encourages, is a big part of what's fueled the latest phase of my career growth + +## Why domain modeling in TypeScript? + +TypeScript is particularly well-suited to articulating a wide range of contracts and software shapes. + +## What are we building today? + +We need a complex set of problems to dive into, and I have a real set of problems that I wish I could solve with software. I have a big vegetable garden +![Garden downhill](./img/my-garden-2.jpeg) +We have a lot of raised beds +![Raised beds](./img/my-garden-1.jpeg) + +There are so many things to manage here. We order seeds from catalogs +![Seed catalogs on a table](./img/catalogs-on-table.jpeg) +and now have a substantial collection of them +![Seed catalogs on a table](./img/seeds-on-table.jpeg) +and then start the seedlings indoors +![Hydroponic bins](./img/hydroponics.jpeg) +And then when the time is right, I transplant them outside into the raised beds. The "right time" has to do with the size of the plant, the temperature, whether it's been hardened (gradually exposed to the outdoor environment, a few hours at a time at first and ramping up). +![Plants in a raised bed](./img/my-garden-3.jpeg) + +

Today, we're going to work on something to make this easier!

+ +## Workshop Overview & Goals + +We'll build a seed collection so we can keep track of what we have in the collection + +![seed catalog](./img/seed-catalog-ui.png) + +And build an amazing drag and drop UI so that we can plan what we want to grow each year! + +![garden ui](./img/garden-ui.png) + +I'm going to be both your instructor and your Gardening Expert as we work on this app together. At the end of the course, you'll also be able to take your learning further as a "final project". This is a real app that will actually get used! diff --git a/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/img/calculator.png b/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/img/calculator.png new file mode 100644 index 000000000..7e56b9ec5 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/img/calculator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67fbc0920104ec9250404ef6c02e2c7193403dabf0cdfcd7a661a8a6421ccae7 +size 14309 diff --git a/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/index.md b/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/index.md new file mode 100644 index 000000000..af8fa1d9f --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/02-value-objects-and-entities/index.md @@ -0,0 +1,107 @@ +--- +title: "Value Objects & Entities" +order: 2 +date: "2025-06-10T09:00:00.000Z" +description: "In this chapter, we'll explore root-level persisted entities and the rich data types they are comprised of" +course: domain-modeling-with-ts +--- + +## Value Objects? + +Value Objects represent **descriptive aspects of the domain that do not possess a unique identity**. Instead, they are defined entirely by their attributes. If two value objects have the same attribute values, they are considered equal. + +Several examples already exist the workshop codebase. For example + +```ts +// packages/server/src/values/rgb-color.ts +import { IRGBColor } from "@peashoot/types" +import { Column } from "typeorm" + +export class RGBColor implements IRGBColor { + @Column() + red!: number + + @Column() + green!: number + + @Column() + blue!: number + + @Column({ default: 1 }) + alpha!: number +} +``` + +There are a couple of things going on here. + +### TypeORM + +We're using [TypeORM](https://typeorm.io/) as our Object Relational Mapping (ORM) tool. These `@Column` decorators indicate that each of these class fields should be persisted in a database (in this case a SQLite DB at `packages/server/peashoot.sqlite`) + +### The `@peashoot/types` package + +This app has a client-server architecture, and `@peashoot/types` is where we're storing a very specific set of things + +- Common types for request/response shapes - `packages/types/src/resources/*` +- Common types that are included in those request/response shapes - `packages/types/src/entities/*` +- Some type guards and utility functions that go along with those types + +In this package we use [Zod](https://zod.dev/) schemas which look like this + +```ts +export const RGBColorSchema = z.object({ + red: z.number(), + green: z.number(), + blue: z.number(), + alpha: z.number().optional(), +}) +``` + +## Entities + +In DDD, Entities as persisted, mutable objects. In our app, this means they'll have IDs and will each relate to a table in our database. There's a relevant example in our app already + +```ts +import { Entity, Column } from 'typeorm' +import { PeashootEntity } from './peashoot-entity' + + +@Entity() +export class Location extends PeashootEntity<'loc'> { + constructor() { + super('loc') + } + + @Column('text') + name!: string + + @Column('text') + region!: string + + @Column('text') + country!: string +``` + +the base class `PeashootEntity<'loc'>` takes care of the id field for us, and prefixes it with `loc_` so that they're easy to identify if we ever see them logged. + +The `@Entity` decorator is really important here. This is what means these are persisted in a table (as opposed to the value-objects which are _embedded_ in the tables of entities they belong to) + +## Our task + +Our app contains a calculator UI +![](./img/calculator.png) +where a gardener can enter a location and temperature, and they need to be able to get an estimate of the _date_ when the weather will be _no colder_ than that temperature. + +Here's how the data flow works + +- The UI component is `packages/client/src/pages/CalculatorsPage.svelte` +- The request initiates from the client at `packages/client/src/lib/repositories/location-temperature-data.repository.ts` in the `calculateDate` method +- The request/response shapes are described here `packages/types/src/resources/locations-calculate-date.ts` +- And ultimately the location service is `packages/server/src/services/location.ts` which makes the calculation and returns a date, which bubbles back up to the UI. You may notice that there's a data file being read and we're not doing anything with it yet (perhaps we should?) + +We have a very basic model for `Location` here +`packages/server/src/entities/location.ts` -- but we'll need to do more in order to complete this task. + +### Before you begin + +Think about value objects and entities? Can we draw a simple diagram of what we plant to build? diff --git a/packages/website/content/blog/domain-modeling-with-ts/03-ubiquitous-language/index.md b/packages/website/content/blog/domain-modeling-with-ts/03-ubiquitous-language/index.md new file mode 100644 index 000000000..a90e98275 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/03-ubiquitous-language/index.md @@ -0,0 +1,42 @@ +--- +title: "Conversations with Domain Experts" +order: 3 +date: "2025-06-10T09:00:00.000Z" +description: "Tips and key points for having conversations with domain experts, developing a shared language, and developing a shared understanding of a domain model" +course: domain-modeling-with-ts +--- + +## The Collaboration Model + +A key part of Domain-Driven Design is getting in the same room with Subject-Matter Experts (SMEs) to discuss the problem space and any current ways the problem is solved today. + +## Developing a shared vocabulary + +When your SMEs and the people who write the software that they'll use are speaking in different terms, it's very likely that something will be lost in the conversation. **You want your future user/stakeholder to be able to correct any misconceptions you may have** (or verify that you're on the right track) and they must be able to understand what you're saying in order to do that. **It's actually important that you don't _adapt_ between different languages -- it's the actual application of the shared language that promotes strong alignment.** + +#### Tips + +- Clarify and confirm terminology to ensure shared meanings. +- Use visual tools to make abstract concepts tangible. +- Minimize technical language and speak in business terms. + +## Stay curious and open + +Don't immediately jump in to suggest solutions too early, or your user might say "sure, build that" before a shared understanding is discussed. + +#### Tips + +- Focus on business problems before proposing solutions. +- Ask open-ended questions to understand motivations, not just surface details. +- Document nuances and confirm with experts rather than oversimplifying. +- Request concrete examples for complex scenarios. +- Listen actively and show genuine interest in their challenges. + +## Iterate and validate regularly + +As you build, you're going to run into more questions. You'll demonstrate what you've built and how you ended up organizing things, and it's important that you confirm with your user that you're on the right track. Keep pursuing a distillation of the _core domain_ -- the part of the software that's at the heart of its reason for existence + +#### Tips + +- Summarize and reflect concepts back to experts for verification. +- Identify and focus on critical business domain aspects that create core value. diff --git a/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-catalog-clip.png b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-catalog-clip.png new file mode 100644 index 000000000..879108379 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-catalog-clip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6678fc71a5f41885f05e1b4d31c6e94a5803109a869a0a626237f4195ea37e4 +size 569228 diff --git a/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-back.png b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-back.png new file mode 100644 index 000000000..456c06ba9 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-back.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:328df1470e10f7b2cbaa5433e97a67291f79be3545925e80a8bca9a9c3897e83 +size 1045099 diff --git a/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-front.png b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-front.png new file mode 100644 index 000000000..d98c08194 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/img/seed-packet-front.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9541a643572f32eaa14dd17228772f979b74e082d1b7608a46b407f7d26161e +size 746853 diff --git a/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/index.md b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/index.md new file mode 100644 index 000000000..2010b22c4 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/04-seed-collection-discussion/index.md @@ -0,0 +1,248 @@ +--- +title: "Seed Collection (discussion)" +order: 4 +date: "2025-06-10T09:00:00.000Z" +description: "Let's attempt our first discussion with a domain expert and an AI assitant to discover more about the challenges associated with managing a large seed collection" +course: domain-modeling-with-ts +--- + +## Your task + +I'll serve as your the "domain expert" in _maintaining a seed collection_ as we work toward a solution for keeping a large one organized. + +Take a few minutes to learn, and come up with some good questions + +Here's an example of seeds for sale in a catalog + +
+Click to see image + +![tomato seeds for sale](./img/seed-catalog-clip.png) + +
+ +and here are the front and back of a seed packet + +
+Click to see images + +![seed packet front](./img/seed-packet-front.png) +![seed packet back](./img/seed-packet-back.png) + +
+ +What kind of rich information do you notice? What subset of it matters in terms of solving a real problem for your user (me or the LLM)? You have a _ton_ of data to start with (see `packages/server/data/seeds.yml`) -- **modeling every detail of the real world is not the objective.** + +
+Click to see an example RAW seed data item + +```yaml +id: burpee-big-boy-tomato + commonName: Big Boy Tomato + latinName: Solanum lycopersicum + plantFamily: tomatoes + description: >- + Classic indeterminate beefsteak tomato producing large, meaty fruits up to + 1 pound. Excellent for slicing and sandwiches. + propagationMethod: seed + spacing: + minimum: + value: 18 + unit: inches + optimal: + value: 24 + unit: inches + rowSpacing: + value: 36 + unit: inches + canIntercrop: false + environmental: + light: full-sun + water: consistent + hardiness: + frostHardy: false + heatTolerant: true + coolSeasonHardy: false + usdaZone: + min: 5 + max: 8 + soil: + texture: loamy + drainage: well-draining + ph: neutral + organicMatter: high + temperatureRanges: + transplant: + min: + value: 60 + unit: fahrenheit + max: + value: 85 + unit: fahrenheit + ideal: + min: + value: 70 + unit: fahrenheit + max: + value: 80 + unit: fahrenheit + soilMin: + value: 60 + unit: fahrenheit + growth: + lifespan: annual + habit: vine + rate: fast + matureSize: + height: + min: + value: 6 + unit: feet + max: + value: 8 + unit: feet + spread: + min: + value: 2 + unit: feet + max: + value: 3 + unit: feet + rootDepth: deep + supportNeeds: cage + resources: + nutrients: heavy + competitiveNature: moderate + planting: + method: transplant + timing: + seasons: + - late-spring + weeksAfterLastFrost: 2 + seedDepth: + value: 0.25 + unit: inches + daysToGermination: + min: + value: 7 + unit: days + max: + value: 14 + unit: days + functions: + primary: food + beneficialInsects: false + pollinatorValue: false + production: + producesFruit: true + edibleParts: + - fruit + harvestDuration: + value: 80 + unit: days + harvestMethod: continuous + harvestWindow: + value: 60 + unit: days + companions: + goodCompanions: + - basil + - carrots + - onions + - parsley + - marigolds + - nasturtiums + badCompanions: + - fennel + - corn + - brassicas + - walnut trees + allelopathicEffects: + - attracts beneficial insects when planted with basil + - improved flavor when grown near basil + seedSource: Burpee + seedPacketInfo: + seedCount: 30 + germinationRate: 85 + viabilityYears: 4 + presentation: + accentColor: + red: 204 + green: 32 + blue: 39 + iconPath: tomatoes-burpee-big-boy-tomato.png + + +``` + +
+ +## If you're watching the recorded course, how can you practice collaboration? + +If you have access to an LLM, here's a prompt to seed a conversation with + +
+ +Click here to reveal prompt + +
+You are a "domain expert" (in the Domain Driven Design sense) in planning, caring for and harvesting vegetable gardens planted in raised garden beds, to be used in a domain driven design workshop for software engineers to practice conversing with a "domain expert". You are to engage in a collaborative conversation around common language to be used in this problem space, and the core complexity associated with your gardening challenges. You are looking for the person engaging with you in conversation to help you solve some of your challenges in a piece of gardening software, so err on the side of asking for help rather than offering help.
+
+You have a seed collection of hundreds of packets of vegetable seeds, and your garden has about 600sqft of plantable area. Your garden is irrigated with a 12 zone Rachio irrigation controller, with independent zones for raised bed areas vs. ground-level areas (e.g. for trees, shrubs). Your garden is in Kirkland, WA USA. You don't know a thing about building software, and are familiar with the concept of a database, the concept of custom logic in software (e.g. spreadsheet formulas, simple "if this, then that" rules), etc... 
+
+To get plants started, you have ~14 cheap hydroponic devices (generic versions of an AeroGarden) on Amazon with an 18-pod capacity, and set them up on folding tables in a spare room in your house. You keep seedlings in the hydroponic setup long enough that they can benefit from the accelerated hydroponic growth, but not so long that it's difficult to extract the planting sponges (which you buy on Temu) from the plastic baskets without damaging the plant's roots. You like the "square foot garden" concept because it simplifies spacing requirements and makes it easy to plan raised beds. Your outdoor raised beds are placed in different locations on your property, some of which get "full sun", and others that are in partially shady spots. The beds vary in shape -- 6 are 14x2 feet, 14 are 8x2, 5 are 3x6, 1 is 4x4, 3 are 4x6, one is 2x3, 3 are 2x2
+
+Some of your biggest challenges (what you're looking for software to help you with) are
+- Keeping track of which seeds you own, when they expire (really this is just a reduction of germination rate)
+- Planning your garden so you get good yields of the vegetables you want
+- Starting seeds indoors at the right time, so that things can be transplanted outside when each respective plant is ready (e.g. its temperature requirements are met) such that you can balance getting a long growing season while minimizing risk of transplanting too early and getting hit with a frost (or stunted growth)
+- Benefiting from companion planting, and avoiding putting antagonist plants too close together
+- Managing reminders/schedules for periodic tasks like succession planting, soil additions (e.g. fertilizer), pest control (e.g. slug traps), special care for certain plants (e.g. nipping buds on tomato plants to encourage vegetative growth early in the season)
+
+Currently you use a bunch of complicated spreadsheets to keep track of all of this, and it's incredibly manual.
+
+You have several fruit trees in your garden
+- Desert king fig
+- Puget gold apricot
+- 2x Honeycrisp Apple
+- 1x Fuji Apple
+- 1x Granny Smith Apple
+- 2x Comice Pear
+- 2x Asian Pear
+- 4x Ranier Cherry
+- 1x Paradise Apple
+
+
+And a few perennials 
+- Anna Hardy Kiwi
+- Thornless red raspberry
+- Several highbush blueberry plants
+- Alexandria Alpine Strawberry
+- Maui Berry
+- Thimbleberry
+- Salmonberry
+- Honeyberry
+
+For annuals, you like to grow
+- Tomatoes (Black from Tula, Cherokee Purple, Sweet 100s, Alice's dream, Napa Chardonnay ,Black Strawberry, Roma)
+- Peppers (Jalapeno, Nadapeno, Sweet bell peppers, Lemon drop, Carolina reaper, Seranno, Habanero, Poblano)
+- Cucumbers (Dragon's egg, other varieties)
+- Various greens (Swiss chard, Spinach, Watercress, Arugula, Red leaf lettuce, Bibb lettuce)
+- Fava beans
+- Snap peas and snow peas
+- Herbs: peppermint, spearmint, thyme, sage, cilantro, chives, parsley, oregano, bay leaves from a bay laurel
+
+Our local pests are
+- Deer and rabbits that eat our young plants -- particularly young pea vines and pepers
+- Cherry aphids on our cherry trees
+- Slugs
+
+Our local weeds and invasive species
+- Japanese knotweed
+- Himalayan Blackberry
+- Foxglove
+
+Begin by giving the user a simple greeting, and wait for them to begin the conversation
+
+ +
diff --git a/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/img/seed-collection-ui.png b/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/img/seed-collection-ui.png new file mode 100644 index 000000000..dbe94d60c --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/img/seed-collection-ui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37623ff366752229644a9340156534e19a196ca673ec5c2025dea3a90326bb28 +size 202446 diff --git a/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/index.md b/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/index.md new file mode 100644 index 000000000..267fe5b3a --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/05-seed-collection-implementation/index.md @@ -0,0 +1,25 @@ +--- +title: "Seed Collection (implementation)" +order: 5 +date: "2025-06-10T09:00:00.000Z" +description: "Let's attempt our first discussion with a domain expert and an AI assitant to discover more about the challenges associated with managing a large seed collection" +course: domain-modeling-with-ts +--- + +## Let's implement it! + +![](./img/seed-collection-ui.png) + +Once you've decided on your domain model, implement it in the workshop project! + +You'll need to do some work that may touch the following places + +- `packages/server/src/services/seed-packets-service.ts` - has two important methods + - `parseSeedPacket` - to take raw information from the yaml file and create a `SeedPacket` entity + - `getAllSeedPackets` - returns the list that powers the seed collection in the UI +- `packages/types/src/resources/packets-list.ts` - the request/response shapes +- `packages/server/src/application/seed-packets-router.ts` - is the API route that's hit. **any conversion between the `SeedPacket` entity and the HTTP response shape should happen here** + - **Important** - You're going to be kind of "piggy backing" off of the UI's generic concept of a `Packet`, by stuffing anything interesting beyond the basic definition of `Packet` into a well-typed `metadata` field. + - `packages/types/src/entities/seed-packet-metadata.type.ts` is that metadata type, which is incorporated into the API response shape. Remember, your server-side model doesn't have to match your API contract! +- `packages/client/src/components/SeedPacketBack.svelte` - the UI component of the back of the seed packet (click on them to flip them over). Thread some data through that's interesting for your user! +- `packages/client/src/components/SeedPacket.svelte` - the UI component for the front of the seed packet. This is a bit more complicated and has some gnarly SVG, but you should be able to find places where data can be surfaced diff --git a/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/dnd-ui.png b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/dnd-ui.png new file mode 100644 index 000000000..ca13357d6 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/dnd-ui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edb2fcf78662fccc1ac86db7ea48d9161611661db75c390df75b024e6c5f2a23 +size 280478 diff --git a/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/ui-bounded-context.png b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/ui-bounded-context.png new file mode 100644 index 000000000..98358749f --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/img/ui-bounded-context.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ab3e977575388f5a4114a6870769c54fd6012e9228d51108f0bb4a76431fe3e +size 16849 diff --git a/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/index.md b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/index.md new file mode 100644 index 000000000..89115dbec --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/06-bounded-contexts/index.md @@ -0,0 +1,34 @@ +--- +title: "Bounded contexts" +order: 6 +date: "2025-06-10T09:00:00.000Z" +description: "Create clearly defined boundaries within which a specific domain model and language apply." +course: domain-modeling-with-ts +--- + +## What's a bounded context + +Bounded contexts encapsulate specific concepts, terminology, and rules relevant to a subset of the business domain, ensuring _internal consistency_. Different bounded contexts communicate through well-defined interfaces, minimizing confusion and ambiguity across the system. + +We have a need for this in our workshop project! There's a fancy UI for dragging and dropping plants into raised beds, just waiting for us to hook some data up to +![Drag and drop raised bed planning UI](./img/dnd-ui.png) + +We have an existing domain model that lives in our UI +![](./img/ui-bounded-context.png) + +This has nothing to do with gardening! Our UI entirely deals in a world of + +- A `Workspace` which consists of multiple `Zone`s +- `Zone`s have a grid where `ItemPlacement`s are arranged +- Each `ItemPlacement` relates to an `Item` + +We're about to create a gardening-related bounded context of our own, and there's going to be some messy code we'll have to write to adapt between the two + +## Anti-corruption layers + +Anti-corruption layers in DDD are effectively **well-contained adapters a bounded contexts and other models or external systems**. We have the beginnings of one in our server's routing layer. For example, + +- `packages/server/src/application/plants-router.ts` adapts between our `Plant` entity and the HTTP response then endpoint needs to return +- `packages/server/src/application/seed-packets-router.ts` adapts between our `SeedPacket` entity and a different HTTP response. + +Anti-corruption layers help keep our domain services (e.g. `packages/server/src/services/plants-service.ts`) clean and simple. They'll be easy to unit test in isolation, and they deal exclusively in the entities that they're associated with. diff --git a/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/img/dnd-ui.png b/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/img/dnd-ui.png new file mode 100644 index 000000000..ca13357d6 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/img/dnd-ui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edb2fcf78662fccc1ac86db7ea48d9161611661db75c390df75b024e6c5f2a23 +size 280478 diff --git a/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/index.md b/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/index.md new file mode 100644 index 000000000..ae2b2a70e --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/07-raised-beds-domain-discussion/index.md @@ -0,0 +1,21 @@ +--- +title: "Domain Modeling: Garden (discussion)" +order: 7 +date: "2025-06-10T09:00:00.000Z" +description: "We'll develop our domain model for a raised bed garden" +course: domain-modeling-with-ts +--- + +Time to dig into our garden bed domain model. Let's start with the following user needs to anchor on to + +> I need to be able to plan out where I want things to go in my raised beds, well in advance of the plants actually going into the ground + +> I'd like to embrace the square-foot garden concept + +> I'd like to ensure I put my plants in the place that's best for them + +These are intentionally vague statements. Dometimes your domain experts will use terminology that you don't understand, or that's insufficiently precise to build software around. Part of your job is to ask clarifying questions and help drive a discussion that leads toward deeper understanding and **refinement of knowledge**. + +## Your task + +Discuss the related statements above with your domain expert. If you'd like, use the same LLM as before as your expert! diff --git a/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/img/items-in-bed.png b/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/img/items-in-bed.png new file mode 100644 index 000000000..aefd4e018 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/img/items-in-bed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60630a70c7c9d25d41088b76733af8094e0ebc68dfd53ab6608410a5a191232e +size 13513 diff --git a/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/index.md b/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/index.md new file mode 100644 index 000000000..2482287c7 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/08-raised-beds-domain-implementation/index.md @@ -0,0 +1,26 @@ +--- +title: "Domain Modeling: Garden (implementation)" +order: 8 +date: "2025-06-10T09:00:00.000Z" +description: "We'll implement our raised bed entities, value objects and relationships" +course: domain-modeling-with-ts +--- + +Now let's implement our domain model! Our goal is to get some sort of diagram showing up on the http://localhost:5173/garden page. + +- The plants must show up as the appropriate size (e.g. tomatoes must be 2x2 tiles) +- The plant tiles (`ItemPlacement` in the UI domain model) should have a working icon (it's ok if a few don't work). Icons are in `packages/client/public/plant-icons/*` and if you just provide an `Item.iconPath` that's set to a filename in that folder, it should show up on the tile + +![items in raied bed](./img/items-in-bed.png) + +You might find that it appears you can drag tiles around. Let's not get ahead of ourselves -- we'll get to that! + +## Context + +- `packages/server/src/entities/garden.ts` will certainly need to be altered and you will probably need to create some more entities in that folder +- `packages/server/src/application/gardens-router.ts` will need to be altered so that it's not hardcoding an empty `zones: []` field +- `packages/server/src/services/gardens-service.ts` will need an update to `createExampleGarden` -- the easiest way to play with a garden setup (including whatever sub-objects you assemble underneath it). This will re-run every time you save, and give you a totally fresh database + +### Bonus + +`packages/client/src/lib/ItemTooltipContent.svelte` is the UI component where your `Item.metadata` will eventually end up as `plantMetadata`. Add some info that your user cares about to the tooltip! diff --git a/packages/website/content/blog/domain-modeling-with-ts/08-semantic-actions/index.md b/packages/website/content/blog/domain-modeling-with-ts/08-semantic-actions/index.md new file mode 100644 index 000000000..b03fb03d9 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/08-semantic-actions/index.md @@ -0,0 +1,23 @@ +--- +title: "Semantic Actions" +order: 9 +date: "2025-06-10T09:00:00.000Z" +description: "Build highly-specific operations as the verbs of your domain" +course: domain-modeling-with-ts +--- + +You may have dealt with CRUD APIs before -- that's not what we're going to be doing today. Yes, we have a couple of list endpoints, but when it comes to mutations, we want to have some very specific operations that give us an opportunity for some real business logic! + +## Task 1 + +We have some client-side validation in `packages/client/src/lib/controllers/workspace-controller.ts` to help ensure we don't place a plant out-of-bounds (ideally this would be a shared piece of validation logic that could _also_ run on the back end). You may notice there's a fairly modular system that allows for validation rules to be flexibly defined. + +> Have a conversation with your domain expert (or LLM) about the kinds of things that should prevent a plant from being placed in a given position within a bed (or within the bed at all) + +## Task 2 + +In `packages/client/src/lib/repositories/workspace.repository.ts` you may notice that we're immediately throwing errors `addItemToWorkspace`. This method is called when a plant is dragged from the toolbar into one of the `Zone`s. Build an API endpoint on our server (handled by `packages/server/src/application/gardens-router.ts`) that places the plant in the raised bed (using whatever domain modeling you previously decided on) and returns the latest representation of a `Workspace`. You'll need to design new request/response types in `packages/types/src/resources/*` + +## Bonus task + +There's a "clone" operation as well that's called when you alt/command + drag an existing tile that's in a raised bed to create a copy. Wire this up similarly diff --git a/packages/website/content/blog/domain-modeling-with-ts/09-aggregates/index.md b/packages/website/content/blog/domain-modeling-with-ts/09-aggregates/index.md new file mode 100644 index 000000000..8451e8f07 --- /dev/null +++ b/packages/website/content/blog/domain-modeling-with-ts/09-aggregates/index.md @@ -0,0 +1,15 @@ +--- +title: "Aggregates" +order: 10 +date: "2025-06-10T09:00:00.000Z" +description: "Let's think about transactionality and how it may lead us to design our semantic actions and data models" +course: domain-modeling-with-ts +--- + +In `packages/client/src/lib/repositories/workspace.repository.ts` there's a `moveItemWithinZone` to move a plant from one place to another within the same zone, and a `moveItemBetweenZones` to move _across_ beds. + +Let's say that we've scaled this sytem way up, and moving plants is mainly something that happens to a `Zone`, not a `Workspace`. This might be a choice that we could embrace, and it works out fine for moving plants within a single zone. + +Hoever, when moving plants across zones, we need to think carefully about _transaction boundaries_. Let's imagine that `Zone` is powered by a fairly reliable service -- it's up 99.999% of the time (a little over 8 hours of downtime per year). If we model a move of a plant across beds as _two atomic operations_, timetimes one of them will succeed and the other will fail. We could end up with cloned plants, or plants that disappear entirely from both beds! + +This illustrates why it's so important to choose _where we put_ a transaction boundary. So far, there are absolutely no operations that involve more than one `Workspace`, so it may be that we already have the split in the right location! diff --git a/packages/website/content/blog/enterprise-v2/01-intro/img/project_screenshot.png b/packages/website/content/blog/enterprise-v2/01-intro/img/project_screenshot.png index 18d8b49b3..358e0ee93 100644 Binary files a/packages/website/content/blog/enterprise-v2/01-intro/img/project_screenshot.png and b/packages/website/content/blog/enterprise-v2/01-intro/img/project_screenshot.png differ diff --git a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-018.png b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-018.png index d6fbdf384..88859703e 100644 Binary files a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-018.png and b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-018.png differ diff --git a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-019.png b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-019.png index f41493204..600435c72 100644 Binary files a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-019.png and b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-019.png differ diff --git a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-020.png b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-020.png index 9ffa1f774..72657af3e 100644 Binary files a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-020.png and b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-020.png differ diff --git a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-021.png b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-021.png index 246d422f1..6ba75ef49 100644 Binary files a/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-021.png and b/packages/website/content/blog/enterprise-v2/05-converting-to-ts/img/slide-021.png differ diff --git a/packages/website/content/blog/enterprise-v2/06-steps-1-2-3/img/app-home.png b/packages/website/content/blog/enterprise-v2/06-steps-1-2-3/img/app-home.png index f09a4c998..4fdb05fe3 100644 Binary files a/packages/website/content/blog/enterprise-v2/06-steps-1-2-3/img/app-home.png and b/packages/website/content/blog/enterprise-v2/06-steps-1-2-3/img/app-home.png differ diff --git a/packages/website/content/blog/enterprise-v2/img/eslint-error.png b/packages/website/content/blog/enterprise-v2/img/eslint-error.png index 1013bcf5e..94691db98 100644 Binary files a/packages/website/content/blog/enterprise-v2/img/eslint-error.png and b/packages/website/content/blog/enterprise-v2/img/eslint-error.png differ diff --git a/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.001.png b/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.001.png index 42a2e02f2..3c5fb7924 100644 Binary files a/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.001.png and b/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.001.png differ diff --git a/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.002.png b/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.002.png index faea66869..536b7df8c 100644 Binary files a/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.002.png and b/packages/website/content/blog/full-stack-typescript/01-intro/fullstack-ts.002.png differ diff --git a/packages/website/content/blog/full-stack-typescript/02-graphql-intro/doctor-ui.png b/packages/website/content/blog/full-stack-typescript/02-graphql-intro/doctor-ui.png index e4abf2cc8..62f70d874 100644 Binary files a/packages/website/content/blog/full-stack-typescript/02-graphql-intro/doctor-ui.png and b/packages/website/content/blog/full-stack-typescript/02-graphql-intro/doctor-ui.png differ diff --git a/packages/website/content/blog/full-stack-typescript/07-imported-resolver/restart-ts-server.png b/packages/website/content/blog/full-stack-typescript/07-imported-resolver/restart-ts-server.png index 679d9e956..a0392d351 100644 Binary files a/packages/website/content/blog/full-stack-typescript/07-imported-resolver/restart-ts-server.png and b/packages/website/content/blog/full-stack-typescript/07-imported-resolver/restart-ts-server.png differ diff --git a/packages/website/content/blog/full-stack-typescript/08-ui-consumes-data/suggestions.png b/packages/website/content/blog/full-stack-typescript/08-ui-consumes-data/suggestions.png index d32dc119c..e2dc73f9d 100644 Binary files a/packages/website/content/blog/full-stack-typescript/08-ui-consumes-data/suggestions.png and b/packages/website/content/blog/full-stack-typescript/08-ui-consumes-data/suggestions.png differ diff --git a/packages/website/content/blog/full-stack-typescript/09-ui-consumes-data/4-suggestions.png b/packages/website/content/blog/full-stack-typescript/09-ui-consumes-data/4-suggestions.png index 996e28f9d..ac8d20396 100644 Binary files a/packages/website/content/blog/full-stack-typescript/09-ui-consumes-data/4-suggestions.png and b/packages/website/content/blog/full-stack-typescript/09-ui-consumes-data/4-suggestions.png differ diff --git a/packages/website/content/blog/full-stack-typescript/10-nested-data/follow-count.png b/packages/website/content/blog/full-stack-typescript/10-nested-data/follow-count.png index 6c84ea468..7e3453f1a 100644 Binary files a/packages/website/content/blog/full-stack-typescript/10-nested-data/follow-count.png and b/packages/website/content/blog/full-stack-typescript/10-nested-data/follow-count.png differ diff --git a/packages/website/content/blog/full-stack-typescript/11-nested-data copy/create-tweet.png b/packages/website/content/blog/full-stack-typescript/11-nested-data copy/create-tweet.png index 5cfaac4e0..c52bceb1d 100644 Binary files a/packages/website/content/blog/full-stack-typescript/11-nested-data copy/create-tweet.png and b/packages/website/content/blog/full-stack-typescript/11-nested-data copy/create-tweet.png differ diff --git a/packages/website/content/blog/full-stack-typescript/12-favorites/liked.png b/packages/website/content/blog/full-stack-typescript/12-favorites/liked.png index 6d10f7449..0c0d94488 100644 Binary files a/packages/website/content/blog/full-stack-typescript/12-favorites/liked.png and b/packages/website/content/blog/full-stack-typescript/12-favorites/liked.png differ diff --git a/packages/website/content/blog/full-stack-typescript/14-trends/trends.png b/packages/website/content/blog/full-stack-typescript/14-trends/trends.png index cdd5f475c..3ed66ccfa 100644 Binary files a/packages/website/content/blog/full-stack-typescript/14-trends/trends.png and b/packages/website/content/blog/full-stack-typescript/14-trends/trends.png differ diff --git a/packages/website/content/blog/fundamentals-v3/01-intro/graph.png b/packages/website/content/blog/fundamentals-v3/01-intro/graph.png index 32714a135..a79c920c0 100644 Binary files a/packages/website/content/blog/fundamentals-v3/01-intro/graph.png and b/packages/website/content/blog/fundamentals-v3/01-intro/graph.png differ diff --git a/packages/website/content/blog/fundamentals-v3/02-hello-typescript/cursor-tooltip-ts.gif b/packages/website/content/blog/fundamentals-v3/02-hello-typescript/cursor-tooltip-ts.gif index c38434f46..1c6b5c227 100644 Binary files a/packages/website/content/blog/fundamentals-v3/02-hello-typescript/cursor-tooltip-ts.gif and b/packages/website/content/blog/fundamentals-v3/02-hello-typescript/cursor-tooltip-ts.gif differ diff --git a/packages/website/content/blog/fundamentals-v3/06-union-and-intersection-types/venn.png b/packages/website/content/blog/fundamentals-v3/06-union-and-intersection-types/venn.png index 939fb32a7..1c7e2e363 100644 Binary files a/packages/website/content/blog/fundamentals-v3/06-union-and-intersection-types/venn.png and b/packages/website/content/blog/fundamentals-v3/06-union-and-intersection-types/venn.png differ diff --git a/packages/website/content/blog/fundamentals-v4/01-intro/graph.png b/packages/website/content/blog/fundamentals-v4/01-intro/graph.png index 2a28896cd..166e41545 100644 Binary files a/packages/website/content/blog/fundamentals-v4/01-intro/graph.png and b/packages/website/content/blog/fundamentals-v4/01-intro/graph.png differ diff --git a/packages/website/content/blog/fundamentals-v4/02-hello-typescript/cursor-tooltip-ts.gif b/packages/website/content/blog/fundamentals-v4/02-hello-typescript/cursor-tooltip-ts.gif index c38434f46..1c6b5c227 100644 Binary files a/packages/website/content/blog/fundamentals-v4/02-hello-typescript/cursor-tooltip-ts.gif and b/packages/website/content/blog/fundamentals-v4/02-hello-typescript/cursor-tooltip-ts.gif differ diff --git a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection-preview.png b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection-preview.png index 996b52183..a8729912b 100644 Binary files a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection-preview.png and b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection-preview.png differ diff --git a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection.png b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection.png index 91ea3587f..368106ca9 100644 Binary files a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection.png and b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/union-intersection.png differ diff --git a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/venn.png b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/venn.png index 396dd7880..1cd3973d3 100644 Binary files a/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/venn.png and b/packages/website/content/blog/fundamentals-v4/06-union-and-intersection-types/venn.png differ diff --git a/packages/website/content/blog/intermediate-v2/09-mapped-types/male-to-male.jpg b/packages/website/content/blog/intermediate-v2/09-mapped-types/male-to-male.jpg index 20e5a0c35..8036b86b9 100644 Binary files a/packages/website/content/blog/intermediate-v2/09-mapped-types/male-to-male.jpg and b/packages/website/content/blog/intermediate-v2/09-mapped-types/male-to-male.jpg differ diff --git a/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/union-intersection.png b/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/union-intersection.png index 91ea3587f..368106ca9 100644 Binary files a/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/union-intersection.png and b/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/union-intersection.png differ diff --git a/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/venn.png b/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/venn.png index 396dd7880..1cd3973d3 100644 Binary files a/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/venn.png and b/packages/website/content/blog/intermediate-v2/11-covariance-contravariance/venn.png differ diff --git a/packages/website/content/blog/making-typescript-stick/01-intro/Making-TypeScript-Stick.001.jpeg b/packages/website/content/blog/making-typescript-stick/01-intro/Making-TypeScript-Stick.001.jpeg index c36eb8d52..ca7a78e4d 100644 Binary files a/packages/website/content/blog/making-typescript-stick/01-intro/Making-TypeScript-Stick.001.jpeg and b/packages/website/content/blog/making-typescript-stick/01-intro/Making-TypeScript-Stick.001.jpeg differ diff --git a/packages/website/content/blog/making-typescript-stick/01-intro/making-it-stick.jpeg b/packages/website/content/blog/making-typescript-stick/01-intro/making-it-stick.jpeg index ea8c11700..ae46d7ae4 100644 Binary files a/packages/website/content/blog/making-typescript-stick/01-intro/making-it-stick.jpeg and b/packages/website/content/blog/making-typescript-stick/01-intro/making-it-stick.jpeg differ diff --git a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-error.png b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-error.png index 7a5e04cef..8fd1d2a1b 100644 Binary files a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-error.png and b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-error.png differ diff --git a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-string.png b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-string.png index 16a2fbb51..d07f91b36 100644 Binary files a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-string.png and b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/throw-string.png differ diff --git a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/variadic-tuple-generic-TS3.png b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/variadic-tuple-generic-TS3.png index bbccea0af..418341bc7 100644 Binary files a/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/variadic-tuple-generic-TS3.png and b/packages/website/content/blog/making-typescript-stick/03-recent-updates-to-typescript/variadic-tuple-generic-TS3.png differ diff --git a/packages/website/content/blog/monorepos-v2/01-intro/index.md b/packages/website/content/blog/monorepos-v2/01-intro/index.md new file mode 100644 index 000000000..deabac50c --- /dev/null +++ b/packages/website/content/blog/monorepos-v2/01-intro/index.md @@ -0,0 +1,11 @@ +--- +title: Intro & Project Setup +date: "2025-06-12T09:00:00.000Z" +description: | + This course tackles challenges in large-scale enterprise software, + emphasizing productivity, cohesion, longevity, and complexity for TypeScript developers. +course: monorepos-v2 +order: 1 +--- + +# Hello Monorepos diff --git a/packages/website/content/courses.yml b/packages/website/content/courses.yml index c092471de..d07092b8e 100644 --- a/packages/website/content/courses.yml +++ b/packages/website/content/courses.yml @@ -1,4 +1,26 @@ courses: + - name: "TypeScript Monorepos v2" + id: monorepos-v2 + visibleInTopNav: false + visibleInCourseIndex: true + visibleInCoursePage: true + squareImage: /monorepos-v2.png + facebookImage: /fb-monorepos-v2.png + twitterImage: /tw-monorepos-v2.png + - name: "Domain modeling for humans and AI" + id: domain-modeling-with-ts + visibleInTopNav: true + visibleInCourseIndex: true + visibleInCoursePage: true + squareImage: /ddd-typescript.png + facebookImage: /fb-domain-modeling-with-ts.png + twitterImage: /tw-domain-modeling-with-ts.png + femCourseUrl: https://frontendmasters.com/courses/domain-modeling-typescript/ + femCoursePublished: false + femWorkshopUrl: https://frontendmasters.com/workshops/domain-modeling-typescript/ + femWorkshopPublished: true + summary: | + Learn how to model a domain in TypeScript, and how to collaborate with domain experts and agentic AI coding tools to build a software system that solves real problems. - name: "TypeScript Fundamentals v3" id: fundamentals-v3 visibleInTopNav: false @@ -100,6 +122,12 @@ courses: type-checked code bases that are shockingly easy to refactor and maintain. course_groups: + - name: "Domain Modeling with TypeScript" + id: domain-modeling-typescript + topNavOrder: 0 + courseIndexOrder: 0 + currentCourse: domain-modeling-with-ts + courses: [domain-modeling-with-ts] - name: "TypeScript Fundamentals" id: typescript-fundamentals topNavOrder: 1 diff --git a/packages/website/cypress/e2e/course-list/course-list.cy.ts b/packages/website/cypress/e2e/course-list/course-list.cy.ts index 8ea838fcc..744869e23 100644 --- a/packages/website/cypress/e2e/course-list/course-list.cy.ts +++ b/packages/website/cypress/e2e/course-list/course-list.cy.ts @@ -6,7 +6,7 @@ describe('course list page', () => { }); it('displays multiple courses', () => { - cy.get('.course-summary').should('have.length', 5); + cy.get('.course-summary').should('have.length', 6); }); it('clicking TS-fundamentals v4 course link visits the course page', () => { @@ -87,6 +87,6 @@ describe('course list page', () => { it('course top nav has the correct courses', () => { cy.get('.course-summary:nth-child(2) header h3 a').click(); cy.get('li.course-tab a').should("contain.text", "TypeScript Fundamentals v4Intermediate TypeScript v2Making TypeScript StickEnterprise-Scale TypeScript v2Full Stack TypeScript"); - cy.get('li.course-tab a').should("have.length", 5); + cy.get('li.course-tab a').should("have.length", 6); }); }); diff --git a/packages/website/gatsby-config.js b/packages/website/gatsby-config.js index 5349cd490..46da56c80 100644 --- a/packages/website/gatsby-config.js +++ b/packages/website/gatsby-config.js @@ -1,5 +1,14 @@ const fs = require(`fs`); const path = require(`path`); +// const { bundledLanguages, bundledThemes } = require('shiki') +const cmlGrammar = require('./cml.tmLanguage.json'); // make sure this is imported correctly +const cmlLanguage = { + id: 'cml', + scopeName: 'source.cml', + grammar: cmlGrammar, + aliases: ['context-mapper', 'cml'], +}; + const PACKAGE_JSON_PATH = require('pkg-up').sync(); const PROJECT_ROOT_PATH = path.join( @@ -8,6 +17,7 @@ const PROJECT_ROOT_PATH = path.join( ); const yaml = require('js-yaml'); +// const { languages } = require('prismjs'); const { courses, course_groups: courseGroups } = yaml.load( fs.readFileSync( @@ -95,7 +105,8 @@ module.exports = { resolve: 'gatsby-remark-shiki-twoslash', options: { theme: 'github-light', - addTryButton: true + addTryButton: true, + languages: [cmlLanguage] }, }, `gatsby-remark-copy-linked-files`, diff --git a/packages/website/gatsby-node.js b/packages/website/gatsby-node.js index a89287cde..c4873a8a5 100644 --- a/packages/website/gatsby-node.js +++ b/packages/website/gatsby-node.js @@ -67,7 +67,7 @@ exports.createPages = async ({ graphql, actions }) => { ` { allMarkdownRemark( - sort: { fields: [frontmatter___order], order: ASC } + sort: { fields: [frontmatter___date], order: DESC } limit: 1000 ) { edges { @@ -77,7 +77,7 @@ exports.createPages = async ({ graphql, actions }) => { } frontmatter { title - order + date course isExercise } @@ -104,10 +104,10 @@ exports.createPages = async ({ graphql, actions }) => { slug: post.node.fields.slug, title: post.node.frontmatter.title, course: post.node.frontmatter.course, - order: post.node.frontmatter.order, + date: post.node.frontmatter.date, }, }) - ), ['context.course', 'context.order']), + ), ['context.course', 'context.date']), 'context.course', ) diff --git a/packages/website/package.json b/packages/website/package.json index 8893d125e..405ebc7a9 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -74,6 +74,8 @@ "prettier": "^3.0.3", "react-test-renderer": "^17.0.2", "start-server-and-test": "^2.0.1", + "twoslash-eslint": "^0.3.1", + "typeorm": "^0.3.23", "typescript": "~5.8.0" }, "homepage": "https://github.com/mike-north/typescript-courses#readme", diff --git a/packages/website/src/components/__tests__/__snapshots__/bio.tsx.snap b/packages/website/src/components/__tests__/__snapshots__/bio.tsx.snap index 29af8523b..fe8689a91 100644 --- a/packages/website/src/components/__tests__/__snapshots__/bio.tsx.snap +++ b/packages/website/src/components/__tests__/__snapshots__/bio.tsx.snap @@ -28,7 +28,7 @@ exports[`Bio renders correctly 1`] = ` Mike\\"Mike\\"", + "__html": "\\"\\"", } } /> diff --git a/packages/website/src/components/bio.tsx b/packages/website/src/components/bio.tsx index b024abc80..857a64897 100644 --- a/packages/website/src/components/bio.tsx +++ b/packages/website/src/components/bio.tsx @@ -20,6 +20,11 @@ interface IPureBioProps { }; } +interface BioProps { + author?: IPureBioProps['author']; + social?: IPureBioProps['social']; +} + const PureBio: React.FunctionComponent = ({ author, social, @@ -34,7 +39,6 @@ const PureBio: React.FunctionComponent = ({ > {author.name} = ({ ); }; -const Bio = (): JSX.Element => { +const Bio = ({ + author: authorProp, + social: socialProp, +}: BioProps): JSX.Element => { const data = useStaticQuery(graphql` query BioQuery { avatar: file( @@ -97,8 +104,8 @@ const Bio = (): JSX.Element => { const { author, social } = data.site.siteMetadata; return ( ); diff --git a/packages/website/src/pages/index.tsx b/packages/website/src/pages/index.tsx index d4f8d366e..b793ebe6e 100644 --- a/packages/website/src/pages/index.tsx +++ b/packages/website/src/pages/index.tsx @@ -44,6 +44,14 @@ interface IBlogIndexProps { site: { siteMetadata: { title: string; + author: { + name: string; + summary: string; + }; + social: { + twitter: string; + linkedin: string; + }; courseGroups: IAbbrevCourseGroup[]; courses: IAbbrevCourse[]; }; @@ -58,7 +66,12 @@ const BlogIndex: React.FunctionComponent< IBlogIndexProps > = ({ data, location }) => { const siteTitle = data.site.siteMetadata.title; - const { courseGroups, courses } = data.site.siteMetadata; + const { + courseGroups, + courses, + author, + social, + } = data.site.siteMetadata; const courseGroupMap: { [key: string]: { courses: IAbbrevCourse[]; @@ -84,7 +97,7 @@ const BlogIndex: React.FunctionComponent< return ( - + {courseGroups .filter( (c) => courseGroupMap[c.id].courses.length > 0, @@ -133,6 +146,14 @@ export const pageQuery = graphql` site { siteMetadata { title + author { + name + summary + } + social { + twitter + linkedin + } courseGroups { id name @@ -154,7 +175,7 @@ export const pageQuery = graphql` } } allMarkdownRemark( - sort: { fields: [frontmatter___order], order: ASC } + sort: { fields: [frontmatter___date], order: DESC } ) { edges { node { @@ -166,7 +187,6 @@ export const pageQuery = graphql` date(formatString: "MMMM DD, YYYY") title description - order } } } diff --git a/packages/website/static/ddd-typescript.png b/packages/website/static/ddd-typescript.png new file mode 100644 index 000000000..24101c9c4 --- /dev/null +++ b/packages/website/static/ddd-typescript.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37568ba15e3b9b6699d260403d8f14d183ca96b1386c5c2854ee765151b05476 +size 151581 diff --git a/packages/website/static/enterprise-ts-v2.png b/packages/website/static/enterprise-ts-v2.png index b9db6d385..65dadc115 100644 Binary files a/packages/website/static/enterprise-ts-v2.png and b/packages/website/static/enterprise-ts-v2.png differ diff --git a/packages/website/static/favicon.png b/packages/website/static/favicon.png index ffed70399..6ae6b1a74 100644 Binary files a/packages/website/static/favicon.png and b/packages/website/static/favicon.png differ diff --git a/packages/website/static/fb-full-stack-ts.png b/packages/website/static/fb-full-stack-ts.png index 780681f5c..c150fe13c 100644 Binary files a/packages/website/static/fb-full-stack-ts.png and b/packages/website/static/fb-full-stack-ts.png differ diff --git a/packages/website/static/fb-intermediate-ts.png b/packages/website/static/fb-intermediate-ts.png index 3935c6dac..06dfbcdec 100644 Binary files a/packages/website/static/fb-intermediate-ts.png and b/packages/website/static/fb-intermediate-ts.png differ diff --git a/packages/website/static/fb-making-typescript-stick.png b/packages/website/static/fb-making-typescript-stick.png index 08037fcb1..82400a20a 100644 Binary files a/packages/website/static/fb-making-typescript-stick.png and b/packages/website/static/fb-making-typescript-stick.png differ diff --git a/packages/website/static/fb-ts-fundamentals-v3.png b/packages/website/static/fb-ts-fundamentals-v3.png index 3d78bd602..479fb3ccb 100644 Binary files a/packages/website/static/fb-ts-fundamentals-v3.png and b/packages/website/static/fb-ts-fundamentals-v3.png differ diff --git a/packages/website/static/fem-logo.png b/packages/website/static/fem-logo.png index 66fd73bab..f20704067 100644 Binary files a/packages/website/static/fem-logo.png and b/packages/website/static/fem-logo.png differ diff --git a/packages/website/static/full-stack-ts.png b/packages/website/static/full-stack-ts.png index c519a7634..535d6a0d4 100644 Binary files a/packages/website/static/full-stack-ts.png and b/packages/website/static/full-stack-ts.png differ diff --git a/packages/website/static/intermediate-ts-v2.png b/packages/website/static/intermediate-ts-v2.png index ed55ece7b..ee1089552 100644 Binary files a/packages/website/static/intermediate-ts-v2.png and b/packages/website/static/intermediate-ts-v2.png differ diff --git a/packages/website/static/intermediate-ts.png b/packages/website/static/intermediate-ts.png index da39a60bc..d27b39e08 100644 Binary files a/packages/website/static/intermediate-ts.png and b/packages/website/static/intermediate-ts.png differ diff --git a/packages/website/static/making-typescript-stick.png b/packages/website/static/making-typescript-stick.png index 8dc8d0e31..ece6b1479 100644 Binary files a/packages/website/static/making-typescript-stick.png and b/packages/website/static/making-typescript-stick.png differ diff --git a/packages/website/static/ts-fundamentals-v3.png b/packages/website/static/ts-fundamentals-v3.png index c442fe9bf..dab3dfd1d 100644 Binary files a/packages/website/static/ts-fundamentals-v3.png and b/packages/website/static/ts-fundamentals-v3.png differ diff --git a/packages/website/static/ts-fundamentals-v4.png b/packages/website/static/ts-fundamentals-v4.png index cbb6623d5..113bc4175 100644 Binary files a/packages/website/static/ts-fundamentals-v4.png and b/packages/website/static/ts-fundamentals-v4.png differ diff --git a/packages/website/static/tw-full-stack-ts.png b/packages/website/static/tw-full-stack-ts.png index b695e07db..482cbef48 100644 Binary files a/packages/website/static/tw-full-stack-ts.png and b/packages/website/static/tw-full-stack-ts.png differ diff --git a/packages/website/static/tw-intermediate-ts.png b/packages/website/static/tw-intermediate-ts.png index 8ffcc17fa..f59694d75 100644 Binary files a/packages/website/static/tw-intermediate-ts.png and b/packages/website/static/tw-intermediate-ts.png differ diff --git a/packages/website/static/tw-making-typescript-stick.png b/packages/website/static/tw-making-typescript-stick.png index 691c9f945..a6f3bacdf 100644 Binary files a/packages/website/static/tw-making-typescript-stick.png and b/packages/website/static/tw-making-typescript-stick.png differ diff --git a/packages/website/static/tw-ts-fundamentals-v3.png b/packages/website/static/tw-ts-fundamentals-v3.png index 1581a365e..488bd4ab9 100644 Binary files a/packages/website/static/tw-ts-fundamentals-v3.png and b/packages/website/static/tw-ts-fundamentals-v3.png differ diff --git a/yarn.lock b/yarn.lock index 9187bd51b..04a1f071b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4744,6 +4744,13 @@ __metadata: languageName: node linkType: hard +"@sqltools/formatter@npm:^1.2.5": + version: 1.2.5 + resolution: "@sqltools/formatter@npm:1.2.5" + checksum: 9b8354e715467d660daa5afe044860b5686bbb1a5cb67a60866b932effafbf5e8b429f19a8ae67cd412065a4f067161f227e182f3664a0245339d5eb1e26e355 + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.3.92": version: 1.3.92 resolution: "@swc/core-darwin-arm64@npm:1.3.92" @@ -6483,6 +6490,13 @@ __metadata: languageName: node linkType: hard +"ansis@npm:^3.17.0": + version: 3.17.0 + resolution: "ansis@npm:3.17.0" + checksum: 6fd6bc4d1187b894d9706f4c141c81b788e90766426617385486dae38f8b2f5a1726d8cc754939e44265f92a9db4647d5136cb1425435c39ac42b35e3acf4f3d + languageName: node + linkType: hard + "any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" @@ -6510,6 +6524,13 @@ __metadata: languageName: node linkType: hard +"app-root-path@npm:^3.1.0": + version: 3.1.0 + resolution: "app-root-path@npm:3.1.0" + checksum: e3db3957aee197143a0f6c75e39fe89b19e7244f28b4f2944f7276a9c526d2a7ab2d115b4b2d70a51a65a9a3ca17506690e5b36f75a068a7e5a13f8c092389ba + languageName: node + linkType: hard + "append-field@npm:^1.0.0": version: 1.0.0 resolution: "append-field@npm:1.0.0" @@ -7606,6 +7627,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.2.1 + checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 + languageName: node + linkType: hard + "builtin-modules@npm:^1.1.1": version: 1.1.1 resolution: "builtin-modules@npm:1.1.1" @@ -9285,6 +9316,13 @@ __metadata: languageName: node linkType: hard +"dayjs@npm:^1.11.13": + version: 1.11.13 + resolution: "dayjs@npm:1.11.13" + checksum: f388db88a6aa93956c1f6121644e783391c7b738b73dbc54485578736565c8931bdfba4bb94e9b1535c6e509c97d5deb918bbe1ae6b34358d994de735055cca9 + languageName: node + linkType: hard + "debug@npm:*, debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.1": version: 4.3.4 resolution: "debug@npm:4.3.4" @@ -9324,6 +9362,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" + dependencies: + ms: ^2.1.3 + peerDependenciesMeta: + supports-color: + optional: true + checksum: fb42df878dd0e22816fc56e1fdca9da73caa85212fbe40c868b1295a6878f9101ae684f4eeef516c13acfc700f5ea07f1136954f43d4cd2d477a811144136479 + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -9912,6 +9962,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.4.7": + version: 16.5.0 + resolution: "dotenv@npm:16.5.0" + checksum: 6543fe87b5ddf2d60dd42df6616eec99148a5fc150cb4530fef5bda655db5204a3afa0e6f25f7cd64b20657ace4d79c0ef974bec32fdb462cad18754191e7a90 + languageName: node + linkType: hard + "dotenv@npm:^7.0.0": version: 7.0.0 resolution: "dotenv@npm:7.0.0" @@ -12900,6 +12957,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.4.5": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 + bin: + glob: dist/esm/bin.mjs + checksum: 0bc725de5e4862f9f387fd0f2b274baf16850dcd2714502ccf471ee401803997983e2c05590cb65f9675a3c6f2a58e7a53f9e365704108c6ad3cbf1d60934c4a + languageName: node + linkType: hard + "glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -15039,6 +15112,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: be31027fc72e7cc726206b9f560395604b82e0fddb46c4cbf9f97d049bcef607491a5afc0699612eaa4213ca5be8fd3e1e7cd187b3040988b65c9489838a7c00 + languageName: node + linkType: hard + "jest-changed-files@npm:^26.6.2": version: 26.6.2 resolution: "jest-changed-files@npm:26.6.2" @@ -16957,6 +17043,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 6476138d2125387a6d20f100608c2583d415a4f64a0fecf30c9e2dda976614f09cad4baa0842447bd37dd459a7bd27f57d9d8f8ce558805abd487c583f3d774a + languageName: node + linkType: hard + "lru-cache@npm:^4.0.0": version: 4.1.5 resolution: "lru-cache@npm:4.1.5" @@ -18055,6 +18148,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2c035575eda1e50623c731ec6c14f65a85296268f749b9337005210bb2b34e2705f8ef1a358b188f69892286ab99dc42c8fb98a57bde55c8d81b3023c19cea28 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -18147,6 +18249,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 2bfd325b95c555f2b4d2814d49325691c7bee937d753814861b0b49d5edcda55cbbf22b6b6a60bb91eddac8668771f03c5ff647dcd9d0f798e9548b9cdc46ee3 + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -18251,7 +18360,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -19221,6 +19330,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 58ee9538f2f762988433da00e26acc788036914d57c71c246bf0be1b60cdbd77dd60b6a3e1a30465f0b248aeb80079e0b34cb6050b1dfa18c06953bb1cbc7602 + languageName: node + linkType: hard + "package-json@npm:^6.3.0": version: 6.5.0 resolution: "package-json@npm:6.5.0" @@ -19544,6 +19660,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: ^10.2.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: 890d5abcd593a7912dcce7cf7c6bf7a0b5648e3dee6caf0712c126ca0a65c7f3d7b9d769072a4d1baf370f61ce493ab5b038d59988688e0c5f3f646ee3c69023 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -22510,6 +22636,18 @@ __metadata: languageName: node linkType: hard +"sha.js@npm:^2.4.11": + version: 2.4.11 + resolution: "sha.js@npm:2.4.11" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + bin: + sha.js: ./bin.js + checksum: ebd3f59d4b799000699097dadb831c8e3da3eb579144fd7eb7a19484cbcbb7aca3c68ba2bb362242eb09e33217de3b4ea56e4678184c334323eca24a58e3ad07 + languageName: node + linkType: hard + "shallow-clone@npm:^3.0.0": version: 3.0.1 resolution: "shallow-clone@npm:3.0.1" @@ -23001,6 +23139,13 @@ __metadata: languageName: node linkType: hard +"sql-highlight@npm:^6.0.0": + version: 6.0.0 + resolution: "sql-highlight@npm:6.0.0" + checksum: 34bfba3ada8e8f1ff9843f1dfa1386db4cf62be212676c300c9f455a6be202fced784098b16488c780198e109024ecb3efafef6ad5527fca46ebbf363080f31d + languageName: node + linkType: hard + "srcset@npm:4": version: 4.0.0 resolution: "srcset@npm:4.0.0" @@ -24216,6 +24361,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a + languageName: node + linkType: hard + "tslib@npm:~2.0.1": version: 2.0.3 resolution: "tslib@npm:2.0.3" @@ -24300,6 +24452,24 @@ __metadata: languageName: node linkType: hard +"twoslash-eslint@npm:^0.3.1": + version: 0.3.1 + resolution: "twoslash-eslint@npm:0.3.1" + dependencies: + twoslash-protocol: 0.3.1 + peerDependencies: + eslint: ">=8.50.0" + checksum: c5577b1ac9c127213bb5c9624ce510c1c8ce29c3f6ad7984002311aeb0426a5017dadef17344e2ff84fe516aa0c3b16fbafbf6760a4b2b90c590fffe2619ab63 + languageName: node + linkType: hard + +"twoslash-protocol@npm:0.3.1": + version: 0.3.1 + resolution: "twoslash-protocol@npm:0.3.1" + checksum: 22191a5248d86b381eab1d04489076fe898d41b49e4c5c08d39387fc98e38500bd561c614063439ad438b09f1c0e3a6edc033d2881f17bef620c2085b4188c64 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -24466,6 +24636,85 @@ __metadata: languageName: node linkType: hard +"typeorm@npm:^0.3.23": + version: 0.3.23 + resolution: "typeorm@npm:0.3.23" + dependencies: + "@sqltools/formatter": ^1.2.5 + ansis: ^3.17.0 + app-root-path: ^3.1.0 + buffer: ^6.0.3 + dayjs: ^1.11.13 + debug: ^4.4.0 + dotenv: ^16.4.7 + glob: ^10.4.5 + sha.js: ^2.4.11 + sql-highlight: ^6.0.0 + tslib: ^2.8.1 + uuid: ^11.1.0 + yargs: ^17.7.2 + peerDependencies: + "@google-cloud/spanner": ^5.18.0 || ^6.0.0 || ^7.0.0 + "@sap/hana-client": ^2.12.25 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.1 || ^11.0.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + reflect-metadata: ^0.1.14 || ^0.2.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + "@google-cloud/spanner": + optional: true + "@sap/hana-client": + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + bin: + typeorm: cli.js + typeorm-ts-node-commonjs: cli-ts-node-commonjs.js + typeorm-ts-node-esm: cli-ts-node-esm.js + checksum: f42770241d784354b0b868de8f2bf2761eff21e76650f6634f1a9fa5e6788dcef26c7d2b95b1a150f205c4f934431291cdac0cb5a4320c1a3fa52450f51813e6 + languageName: node + linkType: hard + "typescript@npm:>3, typescript@npm:~5.8.0": version: 5.8.3 resolution: "typescript@npm:5.8.3" @@ -25115,6 +25364,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^11.1.0": + version: 11.1.0 + resolution: "uuid@npm:11.1.0" + bin: + uuid: dist/esm/bin/uuid + checksum: 840f19758543c4631e58a29439e51b5b669d5f34b4dd2700b6a1d15c5708c7a6e0c3e2c8c4a2eae761a3a7caa7e9884d00c86c02622ba91137bd3deade6b4b4a + languageName: node + linkType: hard + "uuid@npm:^8.3.0, uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -25591,8 +25849,10 @@ __metadata: replace-in-file: ^7.0.1 sass: ^1.69.3 start-server-and-test: ^2.0.1 + twoslash-eslint: ^0.3.1 typeface-merriweather: 1.1.13 typeface-montserrat: 1.1.13 + typeorm: ^0.3.23 typescript: ~5.8.0 typography: ^0.16.24 typography-theme-wordpress-2016: ^0.16.19