Socket
Socket
Sign inDemoInstall

optimism

Package Overview
Dependencies
Maintainers
1
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

optimism - npm Package Compare versions

Comparing version 0.18.0-pre.1 to 0.18.0

src/tests/test-utils.ts

35

lib/bundle.cjs.native.js

@@ -94,3 +94,2 @@ 'use strict';

this.dirty = true;
this.value.length = 0;
reportDirty(this);

@@ -179,3 +178,8 @@ // We can go ahead and unsubscribe here, since any further dirty

entry.recomputing = true;
// Set entry.value as unknown.
var normalizeResult = entry.normalizeResult;
var oldValueCopy;
if (normalizeResult && entry.value.length === 1) {
oldValueCopy = valueCopy(entry.value);
}
// Make entry.value an empty array, representing an unknown value.
entry.value.length = 0;

@@ -185,5 +189,18 @@ try {

entry.value[0] = entry.fn.apply(null, args);
// If we have a viable oldValueCopy to compare with the (successfully
// recomputed) new entry.value, and they are not already === identical, give
// normalizeResult a chance to pick/choose/reuse parts of oldValueCopy[0]
// and/or entry.value[0] to determine the final cached entry.value.
if (normalizeResult && oldValueCopy && !valueIs(oldValueCopy, entry.value)) {
try {
entry.value[0] = normalizeResult(entry.value[0], oldValueCopy[0]);
}
catch (_a) {
// If normalizeResult throws, just use the newer value, rather than
// saving the exception as entry.value[1].
}
}
}
catch (e) {
// If entry.fn throws, entry.value will become exceptional.
// If entry.fn throws, entry.value will hold that exception.
entry.value[1] = e;

@@ -371,3 +388,3 @@ }

function wrap(originalFunction, _a) {
var _b = _a === void 0 ? Object.create(null) : _a, _c = _b.max, max = _c === void 0 ? Math.pow(2, 16) : _c, _d = _b.makeCacheKey, makeCacheKey = _d === void 0 ? defaultMakeCacheKey : _d, keyArgs = _b.keyArgs, subscribe = _b.subscribe, _e = _b.cache, cacheOption = _e === void 0 ? caches$1.StrongCache : _e;
var _b = _a === void 0 ? Object.create(null) : _a, _c = _b.max, max = _c === void 0 ? Math.pow(2, 16) : _c, keyArgs = _b.keyArgs, _d = _b.makeCacheKey, makeCacheKey = _d === void 0 ? defaultMakeCacheKey : _d, normalizeResult = _b.normalizeResult, subscribe = _b.subscribe, _e = _b.cache, cacheOption = _e === void 0 ? caches$1.StrongCache : _e;
var cache = typeof cacheOption === "function"

@@ -384,2 +401,3 @@ ? new cacheOption(max, function (entry) { return entry.dispose(); })

cache.set(key, entry = new Entry(originalFunction));
entry.normalizeResult = normalizeResult;
entry.subscribe = subscribe;

@@ -411,4 +429,5 @@ // Give the Entry the ability to trigger cache.delete(key), even though

max: max,
keyArgs: keyArgs,
makeCacheKey: makeCacheKey,
keyArgs: keyArgs,
normalizeResult: normalizeResult,
subscribe: subscribe,

@@ -418,3 +437,3 @@ cache: cache,

function dirtyKey(key) {
var entry = cache.get(key);
var entry = key && cache.get(key);
if (entry) {

@@ -429,3 +448,3 @@ entry.setDirty();

function peekKey(key) {
var entry = cache.get(key);
var entry = key && cache.get(key);
if (entry) {

@@ -440,3 +459,3 @@ return entry.peek();

function forgetKey(key) {
return cache.delete(key);
return key ? cache.delete(key) : false;
}

@@ -443,0 +462,0 @@ optimistic.forgetKey = forgetKey;

@@ -9,2 +9,3 @@ import { OptimisticWrapOptions } from "./index.js";

static count: number;
normalizeResult: OptimisticWrapOptions<TArgs, any, any, TValue>["normalizeResult"];
subscribe: OptimisticWrapOptions<TArgs>["subscribe"];

@@ -11,0 +12,0 @@ unsubscribe: Unsubscribable["unsubscribe"];

@@ -70,3 +70,2 @@ import { parentEntrySlot } from "./context.js";

this.dirty = true;
this.value.length = 0;
reportDirty(this);

@@ -152,3 +151,8 @@ // We can go ahead and unsubscribe here, since any further dirty

entry.recomputing = true;
// Set entry.value as unknown.
const { normalizeResult } = entry;
let oldValueCopy;
if (normalizeResult && entry.value.length === 1) {
oldValueCopy = valueCopy(entry.value);
}
// Make entry.value an empty array, representing an unknown value.
entry.value.length = 0;

@@ -158,5 +162,18 @@ try {

entry.value[0] = entry.fn.apply(null, args);
// If we have a viable oldValueCopy to compare with the (successfully
// recomputed) new entry.value, and they are not already === identical, give
// normalizeResult a chance to pick/choose/reuse parts of oldValueCopy[0]
// and/or entry.value[0] to determine the final cached entry.value.
if (normalizeResult && oldValueCopy && !valueIs(oldValueCopy, entry.value)) {
try {
entry.value[0] = normalizeResult(entry.value[0], oldValueCopy[0]);
}
catch (_a) {
// If normalizeResult throws, just use the newer value, rather than
// saving the exception as entry.value[1].
}
}
}
catch (e) {
// If entry.fn throws, entry.value will become exceptional.
// If entry.fn throws, entry.value will hold that exception.
entry.value[1] = e;

@@ -163,0 +180,0 @@ }

@@ -13,9 +13,9 @@ import { Trie } from "@wry/trie";

dirty: (...args: TKeyArgs) => void;
dirtyKey: (key: TCacheKey) => void;
dirtyKey: (key: TCacheKey | undefined) => void;
peek: (...args: TKeyArgs) => TResult | undefined;
peekKey: (key: TCacheKey) => TResult | undefined;
peekKey: (key: TCacheKey | undefined) => TResult | undefined;
forget: (...args: TKeyArgs) => boolean;
forgetKey: (key: TCacheKey) => boolean;
getKey: (...args: TArgs) => TCacheKey;
makeCacheKey: (...args: TKeyArgs) => TCacheKey;
forgetKey: (key: TCacheKey | undefined) => boolean;
getKey: (...args: TArgs) => TCacheKey | undefined;
makeCacheKey: (...args: TKeyArgs) => TCacheKey | undefined;
};

@@ -29,3 +29,4 @@ export { CommonCache };

keyArgs?: (...args: TArgs) => TKeyArgs;
makeCacheKey?: (...args: NoInfer<TKeyArgs>) => TCacheKey;
makeCacheKey?: (...args: NoInfer<TKeyArgs>) => TCacheKey | undefined;
normalizeResult?: (newer: TResult, older: TResult) => TResult;
subscribe?: (...args: TArgs) => void | (() => any);

@@ -37,2 +38,2 @@ cache?: CommonCache<NoInfer<TCacheKey>, Entry<NoInfer<TArgs>, NoInfer<TResult>>> | CommonCacheConstructor<NoInfer<TCacheKey>, NoInfer<TResult>, NoInfer<TArgs>>;

}
export declare function wrap<TArgs extends any[], TResult, TKeyArgs extends any[] = TArgs, TCacheKey = any>(originalFunction: (...args: TArgs) => TResult, { max, makeCacheKey, keyArgs, subscribe, cache: cacheOption, }?: OptimisticWrapOptions<TArgs, TKeyArgs, TCacheKey, TResult>): OptimisticWrapperFunction<TArgs, TResult, TKeyArgs, TCacheKey>;
export declare function wrap<TArgs extends any[], TResult, TKeyArgs extends any[] = TArgs, TCacheKey = any>(originalFunction: (...args: TArgs) => TResult, { max, keyArgs, makeCacheKey, normalizeResult, subscribe, cache: cacheOption, }?: OptimisticWrapOptions<TArgs, TKeyArgs, TCacheKey, TResult>): OptimisticWrapperFunction<TArgs, TResult, TKeyArgs, TCacheKey>;

@@ -35,3 +35,3 @@ import { Trie } from "@wry/trie";

const caches = new Set();
export function wrap(originalFunction, { max = Math.pow(2, 16), makeCacheKey = defaultMakeCacheKey, keyArgs, subscribe, cache: cacheOption = StrongCache, } = Object.create(null)) {
export function wrap(originalFunction, { max = Math.pow(2, 16), keyArgs, makeCacheKey = defaultMakeCacheKey, normalizeResult, subscribe, cache: cacheOption = StrongCache, } = Object.create(null)) {
const cache = typeof cacheOption === "function"

@@ -48,2 +48,3 @@ ? new cacheOption(max, entry => entry.dispose())

cache.set(key, entry = new Entry(originalFunction));
entry.normalizeResult = normalizeResult;
entry.subscribe = subscribe;

@@ -75,4 +76,5 @@ // Give the Entry the ability to trigger cache.delete(key), even though

max,
keyArgs,
makeCacheKey,
keyArgs,
normalizeResult,
subscribe,

@@ -82,3 +84,3 @@ cache,

function dirtyKey(key) {
const entry = cache.get(key);
const entry = key && cache.get(key);
if (entry) {

@@ -93,3 +95,3 @@ entry.setDirty();

function peekKey(key) {
const entry = cache.get(key);
const entry = key && cache.get(key);
if (entry) {

@@ -104,3 +106,3 @@ return entry.peek();

function forgetKey(key) {
return cache.delete(key);
return key ? cache.delete(key) : false;
}

@@ -107,0 +109,0 @@ optimistic.forgetKey = forgetKey;

{
"name": "optimism",
"version": "0.18.0-pre.1",
"version": "0.18.0",
"author": "Ben Newman <ben@benjamn.com>",

@@ -43,2 +43,3 @@ "description": "Composable reactive caching with efficient invalidation.",

"@types/node": "^20.2.5",
"@wry/equality": "^0.5.7",
"mocha": "^10.2.0",

@@ -45,0 +46,0 @@ "rimraf": "^5.0.0",

@@ -53,2 +53,3 @@ import { parentEntrySlot } from "./context.js";

public normalizeResult: OptimisticWrapOptions<TArgs, any, any, TValue>["normalizeResult"];
public subscribe: OptimisticWrapOptions<TArgs>["subscribe"];

@@ -99,3 +100,2 @@ public unsubscribe: Unsubscribable["unsubscribe"];

this.dirty = true;
this.value.length = 0;
reportDirty(this);

@@ -196,11 +196,34 @@ // We can go ahead and unsubscribe here, since any further dirty

entry.recomputing = true;
// Set entry.value as unknown.
const { normalizeResult } = entry;
let oldValueCopy: Value<any> | undefined;
if (normalizeResult && entry.value.length === 1) {
oldValueCopy = valueCopy(entry.value);
}
// Make entry.value an empty array, representing an unknown value.
entry.value.length = 0;
try {
// If entry.fn succeeds, entry.value will become a normal Value.
entry.value[0] = entry.fn.apply(null, args);
// If we have a viable oldValueCopy to compare with the (successfully
// recomputed) new entry.value, and they are not already === identical, give
// normalizeResult a chance to pick/choose/reuse parts of oldValueCopy[0]
// and/or entry.value[0] to determine the final cached entry.value.
if (normalizeResult && oldValueCopy && !valueIs(oldValueCopy, entry.value)) {
try {
entry.value[0] = normalizeResult(entry.value[0], oldValueCopy[0]);
} catch {
// If normalizeResult throws, just use the newer value, rather than
// saving the exception as entry.value[1].
}
}
} catch (e) {
// If entry.fn throws, entry.value will become exceptional.
// If entry.fn throws, entry.value will hold that exception.
entry.value[1] = e;
}
// Either way, this line is always reached.

@@ -207,0 +230,0 @@ entry.recomputing = false;

@@ -68,3 +68,3 @@ import { Trie } from "@wry/trie";

// A version of .dirty that accepts a key returned by .getKey.
dirtyKey: (key: TCacheKey) => void;
dirtyKey: (key: TCacheKey | undefined) => void;

@@ -74,3 +74,3 @@ // Examine the current value without recomputing it.

// A version of .peek that accepts a key returned by .getKey.
peekKey: (key: TCacheKey) => TResult | undefined;
peekKey: (key: TCacheKey | undefined) => TResult | undefined;

@@ -80,3 +80,3 @@ // Completely remove the entry from the cache, dirtying any parent entries.

// A version of .forget that accepts a key returned by .getKey.
forgetKey: (key: TCacheKey) => boolean;
forgetKey: (key: TCacheKey | undefined) => boolean;

@@ -88,8 +88,9 @@ // In order to use the -Key version of the above functions, you need a key

// getKey and makeCacheKey will be synonymous.
getKey: (...args: TArgs) => TCacheKey;
getKey: (...args: TArgs) => TCacheKey | undefined;
// This property is equivalent to the makeCacheKey function provided in the
// OptimisticWrapOptions, or (if no options.makeCacheKey function is provided)
// a default implementation of makeCacheKey.
makeCacheKey: (...args: TKeyArgs) => TCacheKey;
// a default implementation of makeCacheKey. This function is also exposed as
// optimistic.options.makeCacheKey, somewhat redundantly.
makeCacheKey: (...args: TKeyArgs) => TCacheKey | undefined;
};

@@ -117,3 +118,6 @@

// in a Map to identify the cached result.
makeCacheKey?: (...args: NoInfer<TKeyArgs>) => TCacheKey;
makeCacheKey?: (...args: NoInfer<TKeyArgs>) => TCacheKey | undefined;
// Called when a new value is computed to allow efficient normalization of
// results over time, for example by returning older if equal(newer, older).
normalizeResult?: (newer: TResult, older: TResult) => TResult;
// If provided, the subscribe function should either return an unsubscribe

@@ -144,4 +148,5 @@ // function or return nothing.

max = Math.pow(2, 16),
keyArgs,
makeCacheKey = (defaultMakeCacheKey as () => TCacheKey),
keyArgs,
normalizeResult,
subscribe,

@@ -168,2 +173,3 @@ cache: cacheOption = StrongCache,

cache.set(key, entry = new Entry(originalFunction));
entry.normalizeResult = normalizeResult;
entry.subscribe = subscribe;

@@ -204,4 +210,5 @@ // Give the Entry the ability to trigger cache.delete(key), even though

max,
keyArgs,
makeCacheKey,
keyArgs,
normalizeResult,
subscribe,

@@ -211,4 +218,4 @@ cache,

function dirtyKey(key: TCacheKey) {
const entry = cache.get(key);
function dirtyKey(key: TCacheKey | undefined) {
const entry = key && cache.get(key);
if (entry) {

@@ -223,4 +230,4 @@ entry.setDirty();

function peekKey(key: TCacheKey) {
const entry = cache.get(key);
function peekKey(key: TCacheKey | undefined) {
const entry = key && cache.get(key);
if (entry) {

@@ -235,4 +242,4 @@ return entry.peek();

function forgetKey(key: TCacheKey) {
return cache.delete(key);
function forgetKey(key: TCacheKey | undefined) {
return key ? cache.delete(key) : false;
}

@@ -247,5 +254,5 @@ optimistic.forgetKey = forgetKey;

return makeCacheKey.apply(null, keyArgs.apply(null, arguments as any));
} : makeCacheKey as (...args: any[]) => TCacheKey;
} : makeCacheKey as (...args: any[]) => TCacheKey | undefined;
return Object.freeze(optimistic);
}

@@ -9,4 +9,6 @@ import * as assert from "assert";

} from "../index";
import { equal } from '@wry/equality';
import { wrapYieldingFiberMethods } from '@wry/context';
import { dep } from "../dep";
import { permutations } from "./test-utils";

@@ -573,2 +575,7 @@ type NumThunk = OptimisticWrapperFunction<[], number>;

function subscribe() {}
let normalizeCalls: [number, number][] = [];
function normalizeResult(newer: number, older: number) {
normalizeCalls.push([newer, older]);
return newer;
}

@@ -580,2 +587,3 @@ let counter1 = 0;

makeCacheKey,
normalizeResult,
subscribe,

@@ -586,4 +594,22 @@ });

assert.strictEqual(wrapped.options.makeCacheKey, makeCacheKey);
assert.strictEqual(wrapped.options.normalizeResult, normalizeResult);
assert.strictEqual(wrapped.options.subscribe, subscribe);
assert.deepEqual(normalizeCalls, []);
assert.strictEqual(wrapped(), 1);
assert.deepEqual(normalizeCalls, []);
assert.strictEqual(wrapped(), 1);
assert.deepEqual(normalizeCalls, []);
wrapped.dirty();
assert.deepEqual(normalizeCalls, []);
assert.strictEqual(wrapped(), 2);
assert.deepEqual(normalizeCalls, [[2, 1]]);
assert.strictEqual(wrapped(), 2);
wrapped.dirty();
assert.strictEqual(wrapped(), 3);
assert.deepEqual(normalizeCalls, [[2, 1], [3, 2]]);
assert.strictEqual(wrapped(), 3);
assert.deepEqual(normalizeCalls, [[2, 1], [3, 2]]);
assert.strictEqual(wrapped(), 3);
let counter2 = 0;

@@ -594,2 +620,3 @@ const wrappedWithDefaults = wrap(() => ++counter2);

assert.strictEqual(typeof wrappedWithDefaults.options.makeCacheKey, "function");
assert.strictEqual(wrappedWithDefaults.options.normalizeResult, void 0);
assert.strictEqual(wrappedWithDefaults.options.subscribe, void 0);

@@ -809,2 +836,139 @@ });

});
describe("wrapOptions.normalizeResult", function () {
it("can normalize array results", function () {
const normalizeArgs: [number[], number[]][] = [];
const range = wrap((n: number) => {
let result = [];
for (let i = 0; i < n; ++i) {
result[i] = i;
}
return result;
}, {
normalizeResult(newer, older) {
normalizeArgs.push([newer, older]);
return equal(newer, older) ? older : newer;
},
});
const r3a = range(3);
assert.deepStrictEqual(r3a, [0, 1, 2]);
// Nothing surprising, just regular caching.
assert.strictEqual(r3a, range(3));
// Force range(3) to be recomputed below.
range.dirty(3);
const r3b = range(3);
assert.deepStrictEqual(r3b, [0, 1, 2]);
assert.strictEqual(r3a, r3b);
assert.deepStrictEqual(normalizeArgs, [
[r3b, r3a],
]);
// Though r3a and r3b ended up ===, the normalizeResult callback should
// have been called with two !== arrays.
assert.notStrictEqual(
normalizeArgs[0][0],
normalizeArgs[0][1],
);
});
it("can normalize recursive array results", function () {
const range = wrap((n: number): number[] => {
if (n <= 0) return [];
return range(n - 1).concat(n - 1);
}, {
normalizeResult: (newer, older) => equal(newer, older) ? older : newer,
});
const ranges = [
range(0),
range(1),
range(2),
range(3),
range(4),
];
assert.deepStrictEqual(ranges[0], []);
assert.deepStrictEqual(ranges[1], [0]);
assert.deepStrictEqual(ranges[2], [0, 1]);
assert.deepStrictEqual(ranges[3], [0, 1, 2]);
assert.deepStrictEqual(ranges[4], [0, 1, 2, 3]);
const perms = permutations(ranges[4]);
assert.strictEqual(perms.length, 4 * 3 * 2 * 1);
// For each permutation of the range sizes, check that strict equality
// holds for r[i] and range(i) for all i after dirtying each number.
let count = 0;
perms.forEach(perm => {
perm.forEach(toDirty => {
range.dirty(toDirty);
perm.forEach(i => {
assert.strictEqual(ranges[i], range(i));
++count;
});
})
});
assert.strictEqual(count, perms.length * 4 * 4);
});
it("exceptions thrown by normalizeResult are ignored", function () {
const normalizeCalls: [string | number, string | number][] = [];
const maybeThrow = wrap((value: string | number, shouldThrow: boolean) => {
if (shouldThrow) throw value;
return value;
}, {
makeCacheKey(value, shouldThrow) {
return JSON.stringify({
// Coerce the value to a string so we can trigger normalizeResult
// using either 2 or "2" below.
value: String(value),
shouldThrow,
});
},
normalizeResult(a, b) {
normalizeCalls.push([a, b]);
throw new Error("from normalizeResult (expected)");
},
});
assert.strictEqual(maybeThrow(1, false), 1);
assert.strictEqual(maybeThrow(2, false), 2);
maybeThrow.dirty(2, false);
assert.strictEqual(maybeThrow("2", false), "2");
assert.strictEqual(maybeThrow(2, false), "2");
maybeThrow.dirty(2, false);
assert.strictEqual(maybeThrow(2, false), 2);
assert.strictEqual(maybeThrow("2", false), 2);
assert.throws(
() => maybeThrow(3, true),
error => error === 3,
);
assert.throws(
() => maybeThrow("3", true),
// Still 3 because the previous maybeThrow(3, true) exception is cached.
error => error === 3,
);
maybeThrow.dirty(3, true);
assert.throws(
() => maybeThrow("3", true),
error => error === "3",
);
// Even though the exception thrown by normalizeResult was ignored, check
// that it was in fact called (twice).
assert.deepStrictEqual(normalizeCalls, [
["2", 2],
[2, "2"],
]);
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc