Repository: Azure/azure-functions-durable-js
Pull request #447 (2023-01-31 22:34:11)

Update orchestration and entity types
This PR ticks some boxes off #416. Specifically:

1. Replaces `IOrchestrationFunctionContext` with `OrchestrationContext`, and exports it from the root of the package
1. Replaces `IEntityFunctionContext<T>` with `EntityContext<T>` and exports it from the root of the package.
1. Exports `Task` and `TimerTask` (user-facing types) from the root of the package


Patch:
diff --git a/src/classes.ts b/src/classes.ts
index 848fb9c..e689be4 100644
--- a/src/classes.ts
+++ b/src/classes.ts
@@ -5,8 +5,6 @@ export { Utils } from "./utils";
 export { Orchestrator } from "./orchestrator";
 export { Entity } from "./entity";
 
-export { IEntityFunctionContext } from "./ientityfunctioncontext";
-export { IOrchestrationFunctionContext } from "./iorchestrationfunctioncontext";
 export { DurableEntityBindingInfo } from "./durableentitybindinginfo";
 export { DurableEntityContext } from "./durableentitycontext";
 export { DurableOrchestrationBindingInfo } from "./durableorchestrationbindinginfo";
diff --git a/src/durableorchestrationcontext.ts b/src/durableorchestrationcontext.ts
index 2ee589d..125cda4 100644
--- a/src/durableorchestrationcontext.ts
+++ b/src/durableorchestrationcontext.ts
@@ -24,15 +24,13 @@ import {
     AtomicTask,
     RetryableTask,
     DFTimerTask,
-    Task,
-    TimerTask,
     DFTask,
     LongTimerTask,
     CallHttpWithPollingTask,
 } from "./task";
 import moment = require("moment");
 import { ReplaySchema } from "./replaySchema";
-import { CallHttpOptions } from "./types";
+import { CallHttpOptions, Task, TimerTask } from "./types";
 
 /**
  * Parameter data for orchestration bindings that can be used to schedule
diff --git a/src/entity.ts b/src/entity.ts
index 78c2ba0..35c6c85 100644
--- a/src/entity.ts
+++ b/src/entity.ts
@@ -4,13 +4,13 @@ import {
     DurableEntityContext,
     EntityId,
     EntityState,
-    IEntityFunctionContext,
     OperationResult,
     RequestMessage,
     Signal,
     Utils,
 } from "./classes";
 import { DurableEntityBindingInfoReqFields } from "./durableentitybindinginfo";
+import { EntityContext } from "./types";
 
 /** @hidden */
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -18,17 +18,17 @@ const log = debug("orchestrator");
 
 /** @hidden */
 export class Entity<T> {
-    constructor(public fn: (context: IEntityFunctionContext<T>) => void) {}
+    constructor(public fn: (context: EntityContext<T>) => void) {}
 
     public listen(): (
-        context: IEntityFunctionContext<T>,
+        context: EntityContext<T>,
         entityTrigger: DurableEntityBindingInfo
     ) => Promise<EntityState> {
         return this.handle.bind(this);
     }
 
     private async handle(
-        context: IEntityFunctionContext<T>,
+        context: EntityContext<T>,
         entityTrigger: DurableEntityBindingInfo
     ): Promise<EntityState> {
         const entityBinding = Utils.getInstancesOf<DurableEntityBindingInfo>(
diff --git a/src/ientityfunctioncontext.ts b/src/ientityfunctioncontext.ts
deleted file mode 100644
index b9098f0..0000000
--- a/src/ientityfunctioncontext.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { InvocationContext } from "@azure/functions";
-import { DurableEntityContext } from "./classes";
-
-export interface IEntityFunctionContext<T> extends InvocationContext {
-    df: DurableEntityContext<T>;
-}
diff --git a/src/iorchestrationfunctioncontext.ts b/src/iorchestrationfunctioncontext.ts
deleted file mode 100644
index f773a94..0000000
--- a/src/iorchestrationfunctioncontext.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { InvocationContext } from "@azure/functions";
-import { DurableOrchestrationContext } from "./classes";
-
-export interface IOrchestrationFunctionContext extends InvocationContext {
-    df: DurableOrchestrationContext;
-}
diff --git a/src/orchestrator.ts b/src/orchestrator.ts
index 98829db..d2c5aac 100644
--- a/src/orchestrator.ts
+++ b/src/orchestrator.ts
@@ -2,7 +2,6 @@ import {
     DurableOrchestrationBindingInfo,
     HistoryEvent,
     HistoryEventType,
-    IOrchestrationFunctionContext,
     OrchestratorState,
     Utils,
 } from "./classes";
@@ -10,6 +9,7 @@ import { DurableOrchestrationContext } from "./durableorchestrationcontext";
 import { TaskOrchestrationExecutor } from "./taskorchestrationexecutor";
 import { LatestReplaySchema, ReplaySchema } from "./replaySchema";
 import { DurableOrchestrationBindingInfoReqFields } from "./durableorchestrationbindinginfo";
+import { OrchestrationContext } from "./types";
 
 /** @hidden */
 export class Orchestrator {
@@ -20,17 +20,17 @@ export class Orchestrator {
     // nicely with Orchestrator data being initialized in the constructor: state may preserved
     // across unit test runs.
     // As a result, we are currently constrained to initialize all of our data in the `handle` method.
-    constructor(public fn: (context: IOrchestrationFunctionContext) => IterableIterator<unknown>) {}
+    constructor(public fn: (context: OrchestrationContext) => IterableIterator<unknown>) {}
 
     public listen(): (
-        context: IOrchestrationFunctionContext,
+        context: OrchestrationContext,
         orchestrationTrigger: DurableOrchestrationBindingInfo
     ) => Promise<OrchestratorState> {
         return this.handle.bind(this);
     }
 
     private async handle(
-        context: IOrchestrationFunctionContext,
+        context: OrchestrationContext,
         orchestrationTrigger: DurableOrchestrationBindingInfo
     ): Promise<OrchestratorState> {
         this.taskOrchestrationExecutor = new TaskOrchestrationExecutor();
diff --git a/src/shim.ts b/src/shim.ts
index de5c7f9..d1481d7 100644
--- a/src/shim.ts
+++ b/src/shim.ts
@@ -14,28 +14,21 @@ import {
     EntityTrigger,
     OrchestrationOptions,
     EntityOptions,
+    OrchestrationContext,
+    EntityContext,
 } from "./types";
-import {
-    Entity,
-    EntityState,
-    IEntityFunctionContext,
-    IOrchestrationFunctionContext,
-    Orchestrator,
-} from "./classes";
+import { Entity, EntityState, Orchestrator } from "./classes";
 import { DurableEntityBindingInfo } from "./durableentitybindinginfo";
 import { OrchestratorState } from "./orchestratorstate";
 import { DurableOrchestrationInput } from "./testingUtils";
 
 type EntityFunction<T> = FunctionHandler &
-    ((
-        entityTrigger: DurableEntityBindingInfo,
-        context: IEntityFunctionContext<T>
-    ) => Promise<EntityState>);
+    ((entityTrigger: DurableEntityBindingInfo, context: EntityContext<T>) => Promise<EntityState>);
 
 type OrchestrationFunction = FunctionHandler &
     ((
         orchestrationTrigger: DurableOrchestrationInput,
-        context: IOrchestrationFunctionContext
+        context: OrchestrationContext
     ) => Promise<OrchestratorState>);
 
 /**
@@ -48,7 +41,7 @@ export function createOrchestrator(fn: OrchestrationHandler): OrchestrationFunct
 
     return async (
         orchestrationTrigger: DurableOrchestrationInput,
-        context: IOrchestrationFunctionContext
+        context: OrchestrationContext
     ): Promise<OrchestratorState> => {
         return await listener(context, orchestrationTrigger);
     };
@@ -64,7 +57,7 @@ export function createEntityFunction<T = unknown>(fn: EntityHandler<T>): EntityF
 
     return async (
         entityTrigger: DurableEntityBindingInfo,
-        context: IEntityFunctionContext<T>
+        context: EntityContext<T>
     ): Promise<EntityState> => {
         return await listener(context, entityTrigger);
     };
diff --git a/src/task.ts b/src/task.ts
index b261e5d..70c7e3d 100644
--- a/src/task.ts
+++ b/src/task.ts
@@ -3,6 +3,7 @@ import { IAction, CreateTimerAction, CallHttpAction } from "./classes";
 import { TaskOrchestrationExecutor } from "./taskorchestrationexecutor";
 import moment = require("moment");
 import { DurableOrchestrationContext } from "./durableorchestrationcontext";
+import { Task, TimerTask } from "./types";
 
 /**
  * @hidden
@@ -28,82 +29,6 @@ export type TaskID = number | string | false;
  */
 export type BackingAction = IAction | "noOp";
 
-/**
- * A Durable Functions Task.
- */
-export interface Task {
-    /**
-     * Whether the task has completed. Note that completion is not
-     * equivalent to success.
-     */
-    isCompleted: boolean;
-    /**
-     * Whether the task faulted in some way due to error.
-     */
-    isFaulted: boolean;
-    /**
-     * The result of the task, if completed. Otherwise `undefined`.
-     */
-    result?: unknown;
-}
-
-/**
- * Returned from [[DurableOrchestrationClient]].[[createTimer]] if the call is
- * not `yield`-ed. Represents a pending timer. See documentation on [[Task]]
- * for more information.
- *
- * All pending timers must be completed or canceled for an orchestration to
- * complete.
- *
- * @example Cancel a timer
- * ```javascript
- * // calculate expiration date
- * const timeoutTask = context.df.createTimer(expirationDate);
- *
- * // do some work
- *
- * if (!timeoutTask.isCompleted) {
- *     // An orchestration won't get marked as completed until all its scheduled
- *     // tasks have returned, or been cancelled. Therefore, it is important
- *     // to cancel timers when they're no longer needed
- *     timeoutTask.cancel();
- * }
- * ```
- *
- * @example Create a timeout
- * ```javascript
- * const now = Date.now();
- * const expiration = new Date(now.valueOf()).setMinutes(now.getMinutes() + 30);
- *
- * const timeoutTask = context.df.createTimer(expirationDate);
- * const otherTask = context.df.callActivity("DoWork");
- *
- * const winner = yield context.df.Task.any([timeoutTask, otherTask]);
- *
- * if (winner === otherTask) {
- *     // do some more work
- * }
- *
- * if (!timeoutTask.isCompleted) {
- *     // An orchestration won't get marked as completed until all its scheduled
- *     // tasks have returned, or been cancelled. Therefore, it is important
- *     // to cancel timers when they're no longer needed
- *     timeoutTask.cancel();
- * }
- * ```
- */
-export interface TimerTask extends Task {
-    /**
-     * @returns Whether or not the timer has been canceled.
-     */
-    isCanceled: boolean;
-    /**
-     * Indicates the timer should be canceled. This request will execute on the
-     * next `yield` or `return` statement.
-     */
-    cancel: () => void;
-}
-
 /**
  * @hidden
  * Base class for all Tasks, defines the basic state transitions for all tasks.
diff --git a/src/taskorchestrationexecutor.ts b/src/taskorchestrationexecutor.ts
index 91a0e3e..786ca80 100644
--- a/src/taskorchestrationexecutor.ts
+++ b/src/taskorchestrationexecutor.ts
@@ -6,7 +6,6 @@ import {
     HistoryEvent,
     HistoryEventType,
     IAction,
-    IOrchestrationFunctionContext,
     RequestMessage,
     ResponseMessage,
     SubOrchestrationInstanceCompletedEvent,
@@ -18,6 +17,7 @@ import { OrchestratorState } from "./orchestratorstate";
 import { TaskBase, NoOpTask, DFTask, CompoundTask, TaskState } from "./task";
 import { ReplaySchema } from "./replaySchema";
 import { Utils } from "./utils";
+import { OrchestrationContext } from "./types";
 
 /**
  * @hidden
@@ -95,10 +95,10 @@ export class TaskOrchestrationExecutor {
      *  Returns void but communicates the resulting orchestrator state via the context object's handler
      */
     public async execute(
-        context: IOrchestrationFunctionContext,
+        context: OrchestrationContext,
         history: HistoryEvent[],
         schemaVersion: ReplaySchema,
-        fn: (context: IOrchestrationFunctionContext) => IterableIterator<unknown>
+        fn: (context: OrchestrationContext) => IterableIterator<unknown>
     ): Promise<OrchestratorState> {
         this.schemaVersion = schemaVersion;
         this.context = context.df;
diff --git a/src/types/entityTypes.ts b/src/types/entityTypes.ts
index c616bf3..c2f4bf4 100644
--- a/src/types/entityTypes.ts
+++ b/src/types/entityTypes.ts
@@ -1,7 +1,7 @@
-import { FunctionOptions, FunctionTrigger } from "@azure/functions";
-import { IEntityFunctionContext } from "../ientityfunctioncontext";
+import { FunctionOptions, FunctionTrigger, InvocationContext } from "@azure/functions";
+import { DurableEntityContext } from "../durableentitycontext";
 
-export type EntityHandler<T> = (context: IEntityFunctionContext<T>) => void;
+export type EntityHandler<T> = (context: EntityContext<T>) => void;
 
 export interface EntityOptions<T> extends Partial<FunctionOptions> {
     handler: EntityHandler<T>;
@@ -10,3 +10,13 @@ export interface EntityOptions<T> extends Partial<FunctionOptions> {
 export interface EntityTrigger extends FunctionTrigger {
     type: "entityTrigger";
 }
+
+/**
+ * Context object passed to entity Functions.
+ */
+export interface EntityContext<T> extends InvocationContext {
+    /**
+     * Object containing all DF entity APIs and properties
+     */
+    df: DurableEntityContext<T>;
+}
diff --git a/src/types/index.ts b/src/types/index.ts
index a46d1d5..e75a58a 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -4,6 +4,7 @@ export * from "./activityTypes";
 export * from "./durableClientTypes";
 export * from "./entityTypes";
 export * from "./orchestrationTypes";
+export * from "./taskTypes";
 
 // Over time we will likely add more implementations
 export type TokenSource = ManagedIdentityTokenSource;
diff --git a/src/types/orchestrationTypes.ts b/src/types/orchestrationTypes.ts
index a70e03c..efd1501 100644
--- a/src/types/orchestrationTypes.ts
+++ b/src/types/orchestrationTypes.ts
@@ -1,11 +1,24 @@
-import { FunctionOptions, FunctionTrigger } from "@azure/functions";
+import { FunctionOptions, FunctionTrigger, InvocationContext } from "@azure/functions";
 import { TokenSource } from ".";
-import { IOrchestrationFunctionContext } from "../iorchestrationfunctioncontext";
+import { DurableOrchestrationContext } from "../classes";
 
+/**
+ * Type of a Generator that can be registered as an orchestration
+ */
 export type OrchestrationHandler = (
-    context: IOrchestrationFunctionContext
+    context: OrchestrationContext
 ) => Generator<unknown, unknown, any>;
 
+/**
+ * Context object passed to orchestration Functions.
+ */
+export interface OrchestrationContext extends InvocationContext {
+    /**
+     * Object containing all DF orchestration APIs and properties
+     */
+    df: DurableOrchestrationContext;
+}
+
 export interface OrchestrationOptions extends Partial<FunctionOptions> {
     handler: OrchestrationHandler;
 }
diff --git a/src/types/taskTypes.ts b/src/types/taskTypes.ts
new file mode 100644
index 0000000..c84e548
--- /dev/null
+++ b/src/types/taskTypes.ts
@@ -0,0 +1,75 @@
+/**
+ * A Durable Functions Task.
+ */
+export interface Task {
+    /**
+     * Whether the task has completed. Note that completion is not
+     * equivalent to success.
+     */
+    isCompleted: boolean;
+    /**
+     * Whether the task faulted in some way due to error.
+     */
+    isFaulted: boolean;
+    /**
+     * The result of the task, if completed. Otherwise `undefined`.
+     */
+    result?: unknown;
+}
+
+/**
+ * Returned from [[DurableOrchestrationClient]].[[createTimer]] if the call is
+ * not `yield`-ed. Represents a pending timer. See documentation on [[Task]]
+ * for more information.
+ *
+ * All pending timers must be completed or canceled for an orchestration to
+ * complete.
+ *
+ * @example Cancel a timer
+ * ```javascript
+ * // calculate expiration date
+ * const timeoutTask = context.df.createTimer(expirationDate);
+ *
+ * // do some work
+ *
+ * if (!timeoutTask.isCompleted) {
+ *     // An orchestration won't get marked as completed until all its scheduled
+ *     // tasks have returned, or been cancelled. Therefore, it is important
+ *     // to cancel timers when they're no longer needed
+ *     timeoutTask.cancel();
+ * }
+ * ```
+ *
+ * @example Create a timeout
+ * ```javascript
+ * const now = Date.now();
+ * const expiration = new Date(now.valueOf()).setMinutes(now.getMinutes() + 30);
+ *
+ * const timeoutTask = context.df.createTimer(expirationDate);
+ * const otherTask = context.df.callActivity("DoWork");
+ *
+ * const winner = yield context.df.Task.any([timeoutTask, otherTask]);
+ *
+ * if (winner === otherTask) {
+ *     // do some more work
+ * }
+ *
+ * if (!timeoutTask.isCompleted) {
+ *     // An orchestration won't get marked as completed until all its scheduled
+ *     // tasks have returned, or been cancelled. Therefore, it is important
+ *     // to cancel timers when they're no longer needed
+ *     timeoutTask.cancel();
+ * }
+ * ```
+ */
+export interface TimerTask extends Task {
+    /**
+     * @returns Whether or not the timer has been canceled.
+     */
+    isCanceled: boolean;
+    /**
+     * Indicates the timer should be canceled. This request will execute on the
+     * next `yield` or `return` statement.
+     */
+    cancel: () => void;
+}


Test patch:
diff --git a/src/testingUtils.ts b/src/testingUtils.ts
index 2a0df2e..3f626df 100644
--- a/src/testingUtils.ts
+++ b/src/testingUtils.ts
@@ -6,18 +6,16 @@ import {
     HistoryEventOptions,
     OrchestratorStartedEvent,
 } from "./classes";
-import { IOrchestrationFunctionContext } from "./iorchestrationfunctioncontext";
 import { ReplaySchema } from "./replaySchema";
 import * as uuidv1 from "uuid/v1";
-import { IEntityFunctionContext } from "./ientityfunctioncontext";
 import { DurableEntityContext } from "./durableentitycontext";
+import { EntityContext, OrchestrationContext } from "./types";
 
 /**
  * An orchestration context with dummy default values to facilitate mocking/stubbing the
  * Durable Functions API.
  */
-export class DummyOrchestrationContext extends InvocationContext
-    implements IOrchestrationFunctionContext {
+export class DummyOrchestrationContext extends InvocationContext implements OrchestrationContext {
     /**
      * Creates a new instance of a dummy orchestration context.
      * All parameters are optional but are exposed to enable flexibility
@@ -94,7 +92,7 @@ export class DurableOrchestrationInput extends DurableOrchestrationBindingInfo {
     }
 }
 
-export class DummyEntityContext<T> extends InvocationContext implements IEntityFunctionContext<T> {
+export class DummyEntityContext<T> extends InvocationContext implements EntityContext<T> {
     /**
      * Creates a new instance of a dummy entity context.
      * All parameters are optional but are exposed to enable flexibility