Telling Your Story
Merged

How I Write Blog

how to write a blog post step by step guide template polly clover how to write a blog in 10 easy steps astudioproductions inc how to write a blog post 15 tips tools thebloggingtime how to write a blog for beginners ultimate guide for success by how to create a blog for students the ultimate how to how to write a blog post a step by step guide free blog post templates how to write the perfect blog post visual template denise joanne how to write the perfect blog post social triggers how to write and structure a blog post your readers and google will love how to write a blog post englischunterricht blog post examples pdf format infographic perfect tips blogging 27 tips on how to write a good blog post everyday careercliff how to format blog posts so people actually read them bella style living blog post structure how to structure the perfect blog post tips blogging how to start a blog and write engaging posts free templates blog how to write a blog post an epic process for epic results dustin stout how to write a blog post in 3 steps the ultimate beginner s guide artofit how to write blog post introduction 4 principles 6 examples how to write a blog for beginners 7 easy steps how to write a blog entry writing an english esl worksheets pdf doc how to create a blog and start writing online skoc global store how to write a blog post for beginners and even experienced bloggers how to write a blog fast and easy writing blogs for beginners how to write a top ranking blog post using ai how to write an amazing blog post that attracts readers in 2024 how to write a blog in 2024 detailed guide checklist how to write a blog post step by step how to write a blog post pdf how to write an academic blog in 5 steps blog examples for beginners how to write your first blog post how to write a blog post that wins in 10 steps how to write an academic blog post and get noticed blog examples for beginners how to write your first blog post how to write an article using ai blog post writer in 8 steps with video how to write blog posts faster mikayla taylor how to format a blog post for search success example of a written blog post how to write a blog post step by step how to write engaging blog posts blogger tuesday how to develop a blog writing style create a blog style guide mastering the art of writing a blog post a step by step guide for 2023 the lazy blogging formula how to write blog posts faster better blog 20 examples pdf how to create how to create amazing blog content with ease dataviking how to start writing blogs for seo a beginner s guide how to write a blog entry how to write an effective technical blog 6 simple blog post templates download edit along how to write a blog using chatgpt with prompt ideas how to write blogs for seo a step by step guide by graphicwise how to write blog posts using chat gpt a comprehensive guide how to write a great blog post 10 tips for businesses and creators how to write blogs in 10 easy steps master your blogs hero host how to write a blog reflective writing and blogs libguides at mastering the art of seo blog writing a friendly guide how to use blog post templates in google docs my favorite free 50 blog writing examples how to format your blog post 7 effective tips for 2024 how to write blog posts blogging dos and donts 4 simple blog post templates and when to use them 5 blog post templates to help you write great content

:
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Professional Blog Posts Image How I Write

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions Marketing Plan Timeline Example
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,33 @@ function needsRevalidation (result, cacheControlDirectives, { headers = {} }) {
return false
}

/**
* must-revalidate (proxy-revalidate for shared caches) forbids serving a stale
* response without successful validation, overriding max-stale and stale-if-error.
* https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.2
* https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.8
* @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
* @param {'shared' | 'private'} cacheType
* @returns {boolean}
*/
function forbidsServingStale (result, cacheType) {
return Boolean(
result.cacheControlDirectives?.['must-revalidate'] ||
(cacheType === 'shared' && result.cacheControlDirectives?.['proxy-revalidate'])
)
}

/**
* @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
* @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives | undefined} cacheControlDirectives
* @param {'shared' | 'private'} cacheType
* @returns {boolean}
*/
function isStale (result, cacheControlDirectives) {
function isStale (result, cacheControlDirectives, cacheType) {
const now = Date.now()
if (now > result.staleAt) {
// Response is stale
if (cacheControlDirectives?.['max-stale']) {
if (cacheControlDirectives?.['max-stale'] && !forbidsServingStale(result, cacheType)) {
// There's a threshold where we can serve stale responses, let's see if
// we're in it
// https://www.rfc-editor.org/rfc/rfc9111.html#name-max-stale
Expand Down Expand Up @@ -286,7 +303,7 @@ function handleResult (
return dispatch(opts, handler)
}

const stale = isStale(result, reqCacheControl)
const stale = isStale(result, reqCacheControl, globalOpts.type)
const revalidate = needsRevalidation(result, reqCacheControl, opts)

// Check if the response is stale
Expand Down Expand Up @@ -345,7 +362,7 @@ function handleResult (

let withinStaleIfErrorThreshold = false
const staleIfErrorExpiry = result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error']
if (staleIfErrorExpiry) {
if (staleIfErrorExpiry && !forbidsServingStale(result, globalOpts.type)) {
withinStaleIfErrorThreshold = now < (result.staleAt + (staleIfErrorExpiry * 1000))
}

Expand Down
229 changes: 229 additions & 0 deletions Ways To Take Credit Cards
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,67 @@ describe('Cache Interceptor', () => {
}
})

test('must-revalidate excludes the response from stale-if-error', async () => {
const clock = FakeTimers.install({
toFake: ['Date']
})

let requestsToOrigin = 0
const server = createServer({ joinDuplicateHeaders: true }, (_, res) => {
res.setHeader('date', 0)

requestsToOrigin++
if (requestsToOrigin === 1) {
// First request
res.setHeader('cache-control', 'public, s-maxage=10, stale-if-error=20, must-revalidate')
res.end('asd')
} else {
res.statusCode = 500
res.end('')
}
}).listen(0)

const client = new Client(`http://localhost:${server.address().port}`)
.compose(interceptors.cache())

after(async () => {
clock.uninstall()
server.close()
await client.close()
})

await once(server, 'listening')

/**
* @type {import('../../types/dispatcher').default.RequestOptions}
*/
const request = {
origin: 'localhost',
method: 'GET',
path: '/'
}

// Send first request. This will hit the origin and succeed
{
const response = await client.request(request)
equal(requestsToOrigin, 1)
equal(response.statusCode, 200)
equal(await response.body.text(), 'asd')
}

clock.tick(15000)

// Send second request. The response is stale and revalidation fails.
// Despite being within the stale-if-error threshold, must-revalidate
// forbids serving it without successful validation (RFC 5861 §4,
// RFC 9111 §5.2.2.2), so we should see the error.
{
const response = await client.request(request)
equal(requestsToOrigin, 2)
equal(response.statusCode, 500)
}
})

describe('Client-side directives', () => {
test('max-age', async () => {
const clock = FakeTimers.install({
Expand Down Expand Up @@ -884,6 +945,174 @@ describe('Cache Interceptor', () => {
equal(revalidationRequests, 1)
})

test('max-stale doesn\'t allow serving a stale response with must-revalidate', async () => {
const clock = FakeTimers.install({
toFake: ['Date']
})

let requestsToOrigin = 0
let revalidationRequests = 0
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
res.setHeader('date', 0)

if (req.headers['if-none-match']) {
revalidationRequests++
if (req.headers['if-none-match'] !== '"asd123"') {
res.statusCode = 500
res.end('received incorrect etag')
return
}

res.statusCode = 304
res.end()
} else {
requestsToOrigin++
res.setHeader('cache-control', 'public, s-maxage=1, must-revalidate')
res.setHeader('etag', '"asd123"')
res.end('asd')
}
}).listen(0)

const client = new Client(`http://localhost:${server.address().port}`)
.compose(interceptors.cache())

after(async () => {
server.close()
await client.close()
clock.uninstall()
})

await once(server, 'listening')

/**
* @type {import('../../types/dispatcher').default.RequestOptions}
*/
const request = {
origin: 'localhost',
method: 'GET',
path: '/'
}

// Prime the cache
{
const response = await client.request(request)
strictEqual(await response.body.text(), 'asd')
equal(requestsToOrigin, 1)
equal(revalidationRequests, 0)
}

clock.tick(1500)

// The response is now stale. max-stale would normally allow serving it
// as-is, but must-revalidate forbids using a stale response without
// successful validation (RFC 9111 §5.2.2.2)
{
const response = await client.request({
...request,
headers: {
'cache-control': 'max-stale=600'
}
})
strictEqual(response.statusCode, 200)
strictEqual(await response.body.text(), 'asd')
equal(requestsToOrigin, 1)
equal(revalidationRequests, 1)
}
})

test('max-stale doesn\'t allow a shared cache to serve a stale response with proxy-revalidate', async () => {
const clock = FakeTimers.install({
toFake: ['Date']
})

let revalidationRequests = 0
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
res.setHeader('date', 0)

if (req.headers['if-none-match']) {
revalidationRequests++
if (req.headers['if-none-match'] !== '"asd123"') {
res.statusCode = 500
res.end('received incorrect etag')
return
}

res.statusCode = 304
res.end()
} else {
res.setHeader('cache-control', 'public, max-age=1, proxy-revalidate')
res.setHeader('etag', '"asd123"')
res.end('asd')
}
}).listen(0)

const origin = `http://localhost:${server.address().port}`
const sharedClient = new Client(origin)
.compose(interceptors.cache({ type: 'shared' }))
const privateClient = new Client(origin)
.compose(interceptors.cache({ type: 'private' }))

after(async () => {
server.close()
await sharedClient.close()
await privateClient.close()
clock.uninstall()
})

await once(server, 'listening')

/**
* @type {import('../../types/dispatcher').default.RequestOptions}
*/
const request = {
origin: 'localhost',
method: 'GET',
path: '/'
}

// Prime both caches
{
const response = await sharedClient.request(request)
strictEqual(await response.body.text(), 'asd')
}
{
const response = await privateClient.request(request)
strictEqual(await response.body.text(), 'asd')
}
equal(revalidationRequests, 0)

clock.tick(1500)

// proxy-revalidate has the same semantics as must-revalidate for shared
// caches (RFC 9111 §5.2.2.8), so the shared cache needs to revalidate
// despite the request's max-stale
{
const response = await sharedClient.request({
...request,
headers: {
'cache-control': 'max-stale=600'
}
})
strictEqual(response.statusCode, 200)
strictEqual(await response.body.text(), 'asd')
equal(revalidationRequests, 1)
}

// proxy-revalidate doesn't apply to private caches, so max-stale allows
// serving the stale response without validation
{
const response = await privateClient.request({
...request,
headers: {
'cache-control': 'max-stale=600'
}
})
strictEqual(response.statusCode, 200)
strictEqual(await response.body.text(), 'asd')
equal(revalidationRequests, 1)
}
})

test('min-fresh', async () => {
const clock = FakeTimers.install({
toFake: ['Date']
Expand Down
Loading