Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/content/posts/003-getting-started-with-inject.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ The package defines four types of lifecycle:

## ~~Extension methods~~

[(We have already said goodbye to extension methods)](/008-byebye-extension-methods/)
[(We have already said goodbye to extension methods)](/posts/008-byebye-extension-methods/)

A simple injector can be easily extended by 3rd party packages with extension methods, just like the FuryStack packages. These extension methods usually provides a _shortcut_ of an instance or sets up a preconfigured explicit instance of a service. You can build clean and nice fluent API-s in that way - you can get the idea from one of the [FuryStack Injector Extensions](https://github.com/furystack/furystack/blob/develop/packages/rest-service/src/injector-extensions.ts)

You find more inject-related articles [here](/tags/inject) or check out the package at NPM
You find more inject-related articles [here](/tags/inject/) or check out the package at NPM

[![npm](https://img.shields.io/npm/v/@furystack/inject.svg?maxAge=3600)](https://www.npmjs.com/package/@furystack/inject)
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ excerpt: We have a strongly typed REST API interface with build-time type checki

### Prerequisites

Read the [previous article](/006-getting-started-with-rest) about type-safe REST APIs, if you haven't yet. It will give a huge boost on productivity. Seriously... go go go...
Read the [previous article](/posts/006-getting-started-with-rest/) about type-safe REST APIs, if you haven't yet. It will give a huge boost on productivity. Seriously... go go go...
So, glad you've back. Now let's get down to business...

### Some sad facts about Typescript interfaces - and a possible solution
Expand Down
2 changes: 1 addition & 1 deletion src/content/posts/009-inject-refactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ excerpt: Emitting decorator type data is doomed :(

## Why

As you can see, in FuryStack I've tried to take some steps to use only standardized APIs to maintain the supportability. As I started to work with new tools and framework, I've found some bottlenecks. The first big bad was the hacky [extension method support](/008-byebye-extension-methods/) that I've introduced in the beginning of the project, but I've found an another black sheep in the heart of the Typescript ecosystem - Decorator support.
As you can see, in FuryStack I've tried to take some steps to use only standardized APIs to maintain the supportability. As I started to work with new tools and framework, I've found some bottlenecks. The first big bad was the hacky [extension method support](/posts/008-byebye-extension-methods/) that I've introduced in the beginning of the project, but I've found an another black sheep in the heart of the Typescript ecosystem - Decorator support.

In short: "[The emitDecoratorMetadata flag is intentionally not supported.](https://github.com/evanw/esbuild/issues/257#issuecomment-658053616)"

Expand Down
472 changes: 472 additions & 0 deletions src/content/posts/018-entity-sync.md

Large diffs are not rendered by default.

Binary file added src/content/posts/img/018-entity-sync.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/data/tags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,9 @@
description: The Shades Showcase App demonstrating UI components and patterns
- id: cache
description: '@furystack/cache — observable async cache with state machine, deduplication, and LRU eviction'
- id: entity-sync
description: '@furystack/entity-sync — shared protocol types for real-time entity synchronization over WebSocket'
- id: entity-sync-client
description: '@furystack/entity-sync-client — client-side reactive entity sync with auto-reconnect and local caching'
- id: entity-sync-service
description: '@furystack/entity-sync-service — server-side change tracking and push notifications for entity sync'
104 changes: 75 additions & 29 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
</div>
</section>

<main id="main-content" class="main-content">
<div class="inner">
{
featured && (
<section class="featured-section reveal">
<span class="section-label">Latest</span>
<PostCard post={featured} large eager />
</section>
)
}
</div>
</main>

<section class="features">
<div class="inner">
<div class="features-grid">
Expand Down Expand Up @@ -153,17 +166,8 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
</div>
</section>

<main id="main-content" class="main-content">
<section class="posts-content">
<div class="inner">
{
featured && (
<section class="featured-section reveal">
<span class="section-label">Latest</span>
<PostCard post={featured} large eager />
</section>
)
}

{
displayedPosts.length > 0 && (
<section class="posts-section">
Expand All @@ -177,7 +181,7 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
</div>
{rest.length > 6 && (
<div class="view-all-wrapper">
<a href="/tags/" class="btn btn-outline">
<a href="/posts/" class="btn btn-outline">
View all posts
</a>
</div>
Expand All @@ -186,7 +190,7 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
)
}
</div>
</main>
</section>
</BaseLayout>

<script>
Expand Down Expand Up @@ -216,6 +220,11 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
text-align: center;
overflow: hidden;
}
@media (min-width: 1080px) {
.hero {
padding: calc(64px + 2.5vw) 5vw 2.5vw;
}
}

.hero-aurora {
position: absolute;
Expand Down Expand Up @@ -281,6 +290,12 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
gap: 24px;
margin: 0 auto 36px;
}
@media (min-width: 1080px) {
.hero-brands {
gap: 20px;
margin-bottom: 24px;
}
}
@media (max-width: 600px) {
.hero-brands {
flex-direction: column;
Expand Down Expand Up @@ -308,6 +323,13 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
border-color: rgba(255, 255, 255, 0.14);
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.15);
}
@media (min-width: 1080px) {
.hero-brand-card {
flex-direction: row;
gap: 16px;
padding: 20px 28px;
}
}
@media (max-width: 600px) {
.hero-brand-card {
flex-direction: row;
Expand Down Expand Up @@ -354,6 +376,12 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
margin-bottom: 32px;
font-weight: 400;
}
@media (min-width: 1080px) {
.hero-description {
font-size: 1.7rem;
margin-bottom: 20px;
}
}
@media (max-width: 600px) {
.hero-description {
font-size: 1.6rem;
Expand Down Expand Up @@ -436,6 +464,11 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
}
@media (min-width: 1080px) {
.quick-start {
margin-top: 16px;
}
}
.quick-start code {
font-family: var(--font-mono);
font-size: 1.4rem;
Expand All @@ -457,19 +490,7 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
}
}
.features::before {
content: '';
position: absolute;
top: -60px;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(to bottom, var(--color-nav-bg), var(--color-whitegrey));
pointer-events: none;
}
@media (prefers-color-scheme: dark) {
.features::before {
background: linear-gradient(to bottom, var(--color-nav-bg), var(--color-darkmode));
}
display: none;
}

.features-grid {
Expand Down Expand Up @@ -545,17 +566,42 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
}
}

/* ── Main content ── */
/* ── Main content (featured post) ── */
.main-content {
position: relative;
flex-grow: 1;
padding: 64px 5vw 80px;
padding: 48px 5vw 0;
background: var(--color-whitegrey);
}
.main-content::before {
content: '';
position: absolute;
top: -60px;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(to bottom, var(--color-nav-bg), var(--color-whitegrey));
pointer-events: none;
}
@media (prefers-color-scheme: dark) {
.main-content {
background: var(--color-darkmode);
}
.main-content::before {
background: linear-gradient(to bottom, var(--color-nav-bg), var(--color-darkmode));
}
}

/* ── Posts content (remaining posts) ── */
.posts-content {
position: relative;
flex-grow: 1;
padding: 48px 5vw 80px;
background: var(--color-whitegrey);
}
@media (prefers-color-scheme: dark) {
.posts-content {
background: var(--color-darkmode);
}
}

.section-label {
Expand All @@ -569,7 +615,7 @@ const ogImage = `${SITE_URL}/images/blog-cover.png`;
}

.featured-section {
margin-bottom: 64px;
margin-bottom: 0;
}

.posts-section {
Expand Down
73 changes: 73 additions & 0 deletions src/pages/posts/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import PostCard from '../../components/PostCard.astro';
import { getCollection } from 'astro:content';
import { SITE_TITLE } from '../../consts';

const allPosts = await getCollection('posts', ({ data }) => !data.draft);
const posts = allPosts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());

const title = `All Posts - ${SITE_TITLE}`;
const description = `Browse all ${posts.length} posts on ${SITE_TITLE}`;
---

<BaseLayout title={title} description={description}>
<header class="posts-header">
<div class="posts-header-inner">
<h1>All Posts</h1>
<p>A collection of {posts.length} {posts.length === 1 ? 'post' : 'posts'}</p>
</div>
</header>

<main id="main-content" class="posts-main">
<div class="inner">
<div class="posts-grid">
{posts.map((post, i) => <PostCard post={post} eager={i === 0} />)}
</div>
</div>
</main>
</BaseLayout>

<style>
.posts-header {
padding: calc(64px + 48px) 5vw 40px;
background: var(--color-nav-bg);
color: #fff;
}

.posts-header-inner {
max-width: 1120px;
margin: 0 auto;
}

.posts-header h1 {
font-size: 3.6rem;
font-weight: 800;
color: #fff;
letter-spacing: -0.03em;
margin-bottom: 8px;
}
@media (max-width: 600px) {
.posts-header h1 {
font-size: 2.6rem;
}
}

.posts-header p {
font-size: 1.6rem;
color: rgba(255, 255, 255, 0.55);
margin: 0;
max-width: 500px;
}

.posts-main {
flex-grow: 1;
padding: 40px 5vw 80px;
background: var(--color-whitegrey);
}
@media (prefers-color-scheme: dark) {
.posts-main {
background: var(--color-darkmode);
}
}
</style>
Loading