diff --git a/src/engine-default-fact-decorators.js b/src/engine-default-fact-decorators.js
new file mode 100644
index 0000000..a97dd9a
--- /dev/null
+++ b/src/engine-default-fact-decorators.js
@@ -0,0 +1,64 @@
+import { JSONPath } from "jsonpath-plus";
+import FactDecorator from "./fact-decorator";
+
+export const PathDecorator = new FactDecorator("path", (params, almanac, next) => {
+    if (Object.prototype.hasOwnProperty.call(params, 'path')) {
+        const path = params.path
+        const paramCopy = Object.assign({}, params)
+        delete paramCopy.path
+        return Promise.resolve(next(paramCopy, almanac)).then(factValue => {
+          if (factValue != null && typeof factValue === 'object') {
+            const pathValue = JSONPath({ json: factValue, path, wrap: false })
+            debug('condition::evaluate extracting object', { property: path, received: pathValue })
+            return pathValue
+          } else {
+            debug('condition::evaluate could not compute object path of non-object', { path, factValue, type: typeof factValue })
+            return factValue
+          }
+        })
+
+    } else {
+        return next(params, almanac);
+    }
+})
+
+export const KeysOfDecorator = new FactDecorator("keysOf", (params, almanac, next) => {
+    const n = next(params, almanac)
+    if (n != null) {
+        if (Object.prototype.hasOwnProperty.call(n, 'keys') && typeof n.keys === 'function') {
+            return Array.from(n.keys())
+        }
+        return Object.keys(n)
+    }
+    return n;
+})
+
+export const ValuesOfDecorator = new FactDecorator("valuesOf", (params, almanac, next) => {
+    const n = next(params,almanac)
+    if (n != null) {
+        if (Object.prototype.hasOwnProperty(n, 'values') && typeof n.values === 'function') {
+            return Array.from(n.values())
+        }
+        return Object.values(n)
+    }
+    return n
+})
+
+export const SizeOfDecorator = new FactDecorator("sizeOf", (params, almanac, next) => {
+    const n = next(params, almanac)
+    if (n != null) {
+        if (Object.prototype.hasOwnProperty(n, 'length')) {
+            return n.length
+        } else if (Object.prototype.hasOwnProperty(n, 'size') && typeof n.size === 'function') {
+            return n.size()
+        }
+    }
+    return 1
+})
+
+/**
+ * Options (arg 3) are merged onto fact options and override
+ * This allows us to do things like create a noCache version of a fact
+ * noCache:name for instance would access the name fact without hitting the cache
+ */
+export const NoCacheDecorator = new FactDecorator("noCache", (params, almanac, next) => next(params, almanac), { cache: false })
\ No newline at end of file
diff --git a/src/fact-decorator.js b/src/fact-decorator.js
new file mode 100644
index 0000000..ad19b5f
--- /dev/null
+++ b/src/fact-decorator.js
@@ -0,0 +1,39 @@
+import Fact from "./fact";
+
+class FactDecorator {
+
+    /**
+     * 
+     * @param {string} id 
+     * @param {function} cb Function that computes the new fact value by invoking a 3rd argument
+     * that is a function to produce the next value
+     * @param {object} options options to override the defaults from the decorated fact
+     */
+    constructor(id, cb, options) {
+        this.id = id
+        this.cb = cb
+        if (options) {
+            this.options = options
+        } else {
+            this.options = {}
+        }
+    }
+
+    /**
+     * 
+     * @param {Fact} fact to decorate
+     * @returns {Fact} the decorated fact
+     */
+    decorate(fact) {
+        const next = fact.calculate.bind(fact);
+        return new Fact(
+            `${this.id}:${fact.id}`,
+            (params, almanac) => this.cb(params, almanac, next),
+            Object.assign({}, this.options, fact.options)
+        )
+    }
+
+}
+
+
+export default FactDecorator
\ No newline at end of file