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
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ if (result.error) {
}
```

### Callback return value

What your callback does determines the response `cache.go` returns:

- **Return raw data** — it is wrapped in a `Hit` and stored.
- **Return a `Hit`** — it is used as-is (lets you set a custom `etag`, for example).
- **Return a `Miss`** — it is used as-is: an _authoritative_ miss. It is stored as a
real `Miss` (preserving its `consecutiveErrors`) and is **not** treated as a thrown
error, so it does **not** trigger the `cacheOnFail`/`preferCache` fallback to a stale
cached `Hit`. When constructing the `Miss`, use the cache name handed to your callback
so it persists under the right key: `return new Cacheism.Miss(existing.cacheName, message, n)`.
- **Throw** — `cache.go` decides the response based on the status (for example,
`cacheOnFail` falls back to a cached `Hit` if one exists).

In short: a returned value is authoritative, while a throw delegates to the status policy.

## Statuses

### Only Fresh
Expand All @@ -89,7 +105,9 @@ want to fetch the fresh data and store it in the cache for other requests.
### Cache on Fail

The cacheOnFail status is for times where we want to try to fetch fresh data,
but if an error is thrown, use the cache if present.
but if an error is thrown, use the cache if present. Note this fallback only
applies when the callback _throws_ — explicitly returning a `Miss` is honored
as-is (see [Callback return value](#callback-return-value)).

### Prefer Cache

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export class Cacheism {
const result = await callback(existing);
if (result instanceof Hit) {
response = result;
} else if (result instanceof Miss) {
response = result;
} else {
response = new Hit(name, result);
}
Expand Down
52 changes: 52 additions & 0 deletions test/memory.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,58 @@ describe('memory', function() {

});

describe('when callback returns a Miss instance', function() {

it('should use the returned Miss directly', async function() {
const customMiss = new Cacheism.Miss('-internal/cache', 'custom error', 2);

const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => {
return customMiss;
});

helpers.expectCacheMiss(c, false, null);
helpers.expectCacheErrors(c, 'custom error', 2);
});

it('should persist the returned Miss and preserve consecutiveErrors', async function() {
const customMiss = new Cacheism.Miss('-internal/cache', 'custom error', 2);

await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => {
return customMiss;
});

assert.strictEqual(await cache.store.isset('-internal/cache'), true);

const d = await cache.store.get('-internal/cache');
helpers.expectDataMiss(d, null, null);
helpers.expectDataErrors(d, 'custom error', 2);

const back = d.response();
assert.ok(back instanceof Cacheism.Miss);
assert.strictEqual(back.consecutiveErrors, 2);
});

it('should honor a returned Miss over a cached Hit under cacheOnFail', async function() {
mockdate.set('2000-11-22');
await cache.store.set(Cacheism.Data.fromResponse(new Cacheism.Hit('-internal/cache', 'cached')));
mockdate.reset();

const customMiss = new Cacheism.Miss('-internal/cache', 'deliberate miss', 1);

const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => {
return customMiss;
});

helpers.expectCacheMiss(c, false, null);
helpers.expectCacheErrors(c, 'deliberate miss', 1);

const d = await cache.store.get('-internal/cache');
helpers.expectDataMiss(d, null, null);
helpers.expectDataErrors(d, 'deliberate miss', 1);
});

});

describe('when status=onlyFresh', async function () {

describe('and no existing cache', async function () {
Expand Down
Loading