UNPKG

@azure-utils/durable-functions

Version:
264 lines (260 loc) 10.2 kB
"use strict"; //#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion const durable_functions = __toESM(require("durable-functions")); //#region ../../node_modules/deepmerge/dist/cjs.js var require_cjs = __commonJS({ "../../node_modules/deepmerge/dist/cjs.js"(exports, module) { var isMergeableObject = function isMergeableObject$1(value) { return isNonNullObject(value) && !isSpecial(value); }; function isNonNullObject(value) { return !!value && typeof value === "object"; } function isSpecial(value) { var stringValue = Object.prototype.toString.call(value); return stringValue === "[object RegExp]" || stringValue === "[object Date]" || isReactElement(value); } var canUseSymbol = typeof Symbol === "function" && Symbol.for; var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for("react.element") : 60103; function isReactElement(value) { return value.$$typeof === REACT_ELEMENT_TYPE; } function emptyTarget(val) { return Array.isArray(val) ? [] : {}; } function cloneUnlessOtherwiseSpecified(value, options) { return options.clone !== false && options.isMergeableObject(value) ? deepmerge(emptyTarget(value), value, options) : value; } function defaultArrayMerge(target, source, options) { return target.concat(source).map(function(element) { return cloneUnlessOtherwiseSpecified(element, options); }); } function getMergeFunction(key, options) { if (!options.customMerge) return deepmerge; var customMerge = options.customMerge(key); return typeof customMerge === "function" ? customMerge : deepmerge; } function getEnumerableOwnPropertySymbols(target) { return Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(target).filter(function(symbol) { return Object.propertyIsEnumerable.call(target, symbol); }) : []; } function getKeys(target) { return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target)); } function propertyIsOnObject(object, property) { try { return property in object; } catch (_) { return false; } } function propertyIsUnsafe(target, key) { return propertyIsOnObject(target, key) && !(Object.hasOwnProperty.call(target, key) && Object.propertyIsEnumerable.call(target, key)); } function mergeObject(target, source, options) { var destination = {}; if (options.isMergeableObject(target)) getKeys(target).forEach(function(key) { destination[key] = cloneUnlessOtherwiseSpecified(target[key], options); }); getKeys(source).forEach(function(key) { if (propertyIsUnsafe(target, key)) return; if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) destination[key] = getMergeFunction(key, options)(target[key], source[key], options); else destination[key] = cloneUnlessOtherwiseSpecified(source[key], options); }); return destination; } function deepmerge(target, source, options) { options = options || {}; options.arrayMerge = options.arrayMerge || defaultArrayMerge; options.isMergeableObject = options.isMergeableObject || isMergeableObject; options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified; var sourceIsArray = Array.isArray(source); var targetIsArray = Array.isArray(target); var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray; if (!sourceAndTargetTypesMatch) return cloneUnlessOtherwiseSpecified(source, options); else if (sourceIsArray) return options.arrayMerge(target, source, options); else return mergeObject(target, source, options); } deepmerge.all = function deepmergeAll(array, options) { if (!Array.isArray(array)) throw new Error("first argument should be an array"); return array.reduce(function(prev, next) { return deepmerge(prev, next, options); }, {}); }; var deepmerge_1 = deepmerge; module.exports = deepmerge_1; } }); var import_cjs = __toESM(require_cjs(), 1); //#endregion //#region src/DurableEntity.ts /** * DurableEntity is a wrapper around Durable Functions entities. * * It provides a simple way to define an entity with a default state and operations. * It also provides methods to get, reset, and update the state of the entity. * The entity is registered with the Durable framework when the class is instantiated. * The entity is identified by a name and a key. * * Note: keep Entity state small, limit to 16kB for reliable read operations. * * @example * ```ts * type MyEntityState = { foo: string; bar: number }; * const defaultState: MyEntityState = { foo: "bar", bar: 0 }; * const myEntity = new DurableEntity("my-entity", defaultState, { * updateFoo: (input: { foo: string }) => ({ foo: input.foo }), * updateBar: (input: { bar: number }) => ({ bar: input.bar }), * }) * ``` */ var DurableEntity = class DurableEntity { name; #defaultState; #operations; constructor(entityName, defaultState, operations) { this.name = entityName; this.#defaultState = defaultState; this.#operations = operations; durable_functions.app.entity(entityName, (context) => { const state = context.df.getState(() => this.#defaultState); const operationName = context.df.operationName; context.info(`[${context.functionName}] Operation: ${operationName}`); switch (operationName) { case "get": return context.df.return(state); case "reset": return context.df.setState(this.#defaultState); case "update": { const operationInput = context.df.getInput(); const newState = (0, import_cjs.default)(state, operationInput ?? {}); return context.df.setState(newState); } default: { context.error(`[${context.functionName}] Unknown operation: ${operationName}`); return; } } }); } /** * Converts a string key to an EntityId. * If the key is already an EntityId, it returns it as is. */ entityId(key) { if (typeof key === "string") return new durable_functions.EntityId(this.name, key.includes("@") ? this.getKey(key) : key); else return key; } getState(context, keyOrEntityId) { const client = getDfClientFromContext(context); const entityId = this.entityId(keyOrEntityId); if ("callEntity" in client) return client.callEntity(entityId, "get"); return client.readEntityState(entityId).then((result) => result.entityState ?? this.#defaultState); } /** * Resets the state of the entity to the default state. */ async resetState(context, keyOrEntityId) { const client = getDfClientFromContext(context); const entityId = this.entityId(keyOrEntityId); client.signalEntity(entityId, "reset"); return this.#defaultState; } /** * Updates the state of the entity with the new state. * The new state is deeply-merged with the current state. */ async updateState(context, keyOrEntityId, newState) { const client = getDfClientFromContext(context); const entityId = this.entityId(keyOrEntityId); client.signalEntity(entityId, "update", newState); } /** * Returns the key of the entity. * If the entityId is a string, it extracts the key from it. * If the entityId is an EntityId, it returns the key property. */ getKey(entityId) { if (typeof entityId === "string") return entityId.split("@").pop() ?? entityId; else return entityId.key; } /** * Returns a map of operations that can be used to update the state of the entity. * The operations are defined in the constructor and are deeply-merged with the current state. */ operations(context, keyOrEntityId) { const updateOperations = {}; Object.keys(this.#operations).forEach((operationName) => { context.debug("Update Entity state", { caller: context.functionName, entity: this.entityId(keyOrEntityId), operation: operationName }); updateOperations[operationName] = (input) => this.updateState(context, keyOrEntityId, this.#operations[operationName](input)); }); return updateOperations; } /** * Lists all instances of the entity. */ async listInstances(context, filter) { return await DurableEntity.listInstances(context, this.name, filter); } static async listInstances(context, entityName, filter) { try { const client = getDfClientFromContext(context); const instances = await client.getStatusBy({ ...filter, runtimeStatus: filter?.runtimeStatus ?? [durable_functions.OrchestrationRuntimeStatus.Running] }); const filteredInstances = instances.filter((instance) => instance.instanceId.startsWith(entityName ? `@${entityName}@` : "@")); return filteredInstances.map((instance) => { const input = instance.input; return { name: instance.name, instanceId: instance.instanceId, createdTime: instance.createdTime, lastUpdatedTime: instance.lastUpdatedTime, key: instance.instanceId.split("@").pop() ?? instance.instanceId, state: input.exists && input.state && typeof input.state === "string" ? JSON.parse(String(input.state)) : null }; }); } catch (error) { context.error("Error listing instances", { error, entityName, filter }); return []; } } }; function getDfClientFromContext(context) { if ("df" in context) return context.df; else return durable_functions.getClient(context); } //#endregion exports.DurableEntity = DurableEntity //# sourceMappingURL=DurableEntity.cjs.map