-
Notifications
You must be signed in to change notification settings - Fork 12
Add Functions gRPC core helpers #282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
57b48be
c272a55
fb3a55b
fe7c594
f54138d
93f5122
9ca4fce
ff3ab8f
4d26c6b
91ba76b
11a042d
b98583a
4ca9a62
8d863de
708a950
00d705e
073bf25
cd2b6ff
bfc4e3b
701cd13
834d347
c7114a9
e4fd451
f935bce
8f62a35
900064f
0dcc47c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Changelog | ||
|
|
||
| ## 0.4.0 | ||
|
|
||
| Initial gRPC-consolidated Azure Functions Durable provider, built on `@microsoft/durabletask-js`. | ||
|
|
||
| ### Requirements | ||
|
|
||
| - Node.js >= 22 (drops the Node 18/20 that `durable-functions` v3 supported). | ||
|
|
||
| ### Breaking changes vs `durable-functions` v3 | ||
|
|
||
| - Classic orchestration and entity contexts no longer extend `InvocationContext`; they expose only | ||
| `df` plus replay-safe log helpers (no `invocationId` / `functionName` / `extraInputs`), and the | ||
| classic entity context exposes only `{ df }`. Rationale: reading `InvocationContext` members such as | ||
| `invocationId` / `extraInputs` inside an orchestrator is replay-nondeterministic and was never | ||
| recommended, so they are intentionally not surfaced. | ||
| - Task result shape follows the core SDK: use `isComplete` / `isFailed` / `getResult()` (v3 used | ||
| `isCompleted` / `isFaulted` / `result`). `context.df.createTimer(...)` returns a cancelable | ||
| `TimerTask`, so the timeout-race pattern (`Task.any` then `timer.cancel()`) keeps working. | ||
| - `client.getStatus()` matches the v3 signature: it returns a non-optional `DurableOrchestrationStatus` | ||
| and throws when the instance does not exist. `showInput` suppresses only the top-level input (output | ||
| and custom status are always returned, as in v3); `showHistory` populates `history`, and | ||
| `showHistoryOutput` toggles whether those entries keep their input/result payloads. One | ||
| consolidated-path note: `history` entries are core `HistoryEvent`s (v3 types `history` as | ||
| `Array<unknown>`), not the classic .NET extension's history serialization. | ||
| - `client.startNew()` supports the v3 `version` option (forwarded to the core scheduler). | ||
| - Removed top-level exports: `DummyOrchestrationContext`, `DummyEntityContext`, `DurableError`, | ||
| `AggregatedError`, `ManagedIdentityTokenSource`, `TokenSource`. `TaskFailedError` is re-exported | ||
| from the core SDK and aggregate failures surface as JS-native `AggregateError`. For orchestration | ||
| unit tests, use the core `TestOrchestrationWorker` / `TestOrchestrationClient` in place of the v3 | ||
| dummy contexts. | ||
|
|
||
| ### Release order | ||
|
|
||
| - Publish `@microsoft/durabletask-js` (0.3.0) before this package. | ||
| - Confirm the target `@azure/functions` (^4.16.1) build includes the Durable extension changes | ||
| (`DurableRequiresGrpc`) required by the consolidated gRPC path. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) Microsoft Corporation. | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| # durable-functions | ||
|
|
||
| Write [Azure Durable Functions](https://learn.microsoft.com/azure/azure-functions/durable/) orchestrations, activities, and entities in JavaScript and TypeScript, running on the [Durable Task JavaScript SDK](https://github.com/microsoft/durabletask-js). | ||
|
|
||
| ## What this package is | ||
|
|
||
| `durable-functions` is the Azure Functions Durable provider for JavaScript, built on `@microsoft/durabletask-js`. You author Durable Functions apps with the familiar `app.orchestration` / `app.activity` / `app.entity` model, and the provider talks to the Durable Task backend over the Functions host's gRPC channel. | ||
|
|
||
| This package supersedes the legacy [`durable-functions`](https://github.com/Azure/azure-functions-durable-js) package. New and existing (classic v3) orchestration, activity, and entity code all run on the same gRPC engine. | ||
|
|
||
| ## Why it is needed | ||
|
|
||
| - **One gRPC protocol.** Durable work items flow between the Functions host and your app over a single gRPC channel instead of the legacy out-of-proc HTTP protocol, keeping the JavaScript provider aligned with the .NET, Python, and Java Durable providers. | ||
| - **Shared core engine.** Orchestration replay, activities, entities, retries, and instance-management APIs all come from `@microsoft/durabletask-js`, so behavior matches the other Durable Task SDKs. | ||
| - **Backward compatible.** Existing v3 Durable Functions orchestrators and entities keep working through a compatibility layer, so you can move to this provider without rewriting your functions. | ||
|
|
||
| ## What it supports | ||
|
|
||
| - **Authoring** — `app.orchestration`, `app.activity`, and `app.entity` register durable functions (each trigger opts into the host's gRPC protocol automatically). | ||
| - **Client** — `getClient(context)` returns a `DurableFunctionsClient` for scheduling, querying, signaling, and managing instances, plus HTTP management-payload helpers (`createCheckStatusResponse`, `createHttpManagementPayload`) for durable HTTP starters. | ||
| - **Classic (v3) compatibility** — orchestrators and entities written in the legacy `context.df.*` style, `RetryOptions`, `EntityId`, and the deprecated client aliases are adapted onto the core engine. | ||
|
|
||
| ## Migrating from durable-functions v3 | ||
|
|
||
| This provider keeps classic `context.df.*` orchestrators and entities working, but a few surfaces | ||
| changed. See [`CHANGELOG.md`](./CHANGELOG.md) for the full list; the highlights: | ||
|
|
||
| - **Node.js >= 22** is required (v3 supported Node 18/20). | ||
| - **Classic contexts no longer extend `InvocationContext`** — only `df` plus replay-safe log helpers | ||
| are available (no `invocationId` / `functionName` / `extraInputs`; the classic entity context is | ||
| just `{ df }`). Reading those `InvocationContext` members inside an orchestrator is | ||
| replay-nondeterministic and was never recommended. | ||
| - **Task result shape follows the core SDK** — use `isComplete` / `isFailed` / `getResult()` instead | ||
| of v3's `isCompleted` / `isFaulted` / `result`. `context.df.createTimer(...)` still returns a | ||
| cancelable `TimerTask` for the timeout-race pattern. | ||
| - **`client.getStatus()` keeps the v3 shape** — it returns a non-optional `DurableOrchestrationStatus` | ||
| and throws when the instance is missing. `showInput` suppresses only the top-level input, | ||
| `showHistory` populates `history`, and `showHistoryOutput` toggles the per-entry input/result | ||
| payloads; `history` entries are core `HistoryEvent`s (v3 types `history` as `Array<unknown>`). | ||
| **`client.startNew()` supports the `version` option.** | ||
| - **Some v3 top-level exports were removed** — `DummyOrchestrationContext` / `DummyEntityContext`, | ||
| `DurableError` / `AggregatedError`, and `ManagedIdentityTokenSource` / `TokenSource`. | ||
| `TaskFailedError` is re-exported from the core SDK; use the core `TestOrchestrationWorker` / | ||
| `TestOrchestrationClient` for orchestration unit tests. | ||
|
|
||
| ## Getting started | ||
|
|
||
| ```typescript | ||
| import * as df from "durable-functions"; | ||
| import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions"; | ||
|
|
||
| df.app.orchestration("helloOrchestrator", async function* (ctx, input) { | ||
| return yield ctx.callActivity("sayHello", input); | ||
| }); | ||
|
|
||
| df.app.activity("sayHello", { | ||
| handler: (name: string) => `Hello, ${name}!`, | ||
| }); | ||
|
|
||
| app.http("startHello", { | ||
| route: "orchestrators/helloOrchestrator", | ||
| extraInputs: [df.input.durableClient()], | ||
| handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => { | ||
| const client = df.getClient(context); | ||
| const instanceId = await client.scheduleNewOrchestration("helloOrchestrator", "Durable"); | ||
| return client.createCheckStatusResponse(request, instanceId); | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| ## Status | ||
|
|
||
| This package is an early preview (`0.4.0`); APIs may change before the stable release. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| module.exports = { | ||
| preset: "ts-jest", | ||
| testEnvironment: "node", | ||
| testMatch: ["**/test/**/*.spec.ts"], | ||
| moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], | ||
| transform: { | ||
| "^.+\\.ts$": "ts-jest", | ||
| }, | ||
| moduleNameMapper: { | ||
| "^@microsoft/durabletask-js$": "<rootDir>/../durabletask-js/src/index.ts", | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "name": "durable-functions", | ||
| "version": "0.4.0", | ||
| "description": "Azure Functions Durable provider for the Durable Task JavaScript SDK", | ||
| "main": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "require": "./dist/index.js", | ||
| "import": "./dist/index.js" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "dist", | ||
| "LICENSE", | ||
| "README.md", | ||
| "CHANGELOG.md" | ||
| ], | ||
| "scripts": { | ||
| "clean": "node -e \"require('fs').rmSync('dist', {recursive:true, force:true})\"", | ||
| "prebuild": "node -p \"'// Auto-generated by prebuild\\nexport const SDK_VERSION = ' + JSON.stringify(require('./package.json').version) + ';\\nexport const SDK_PACKAGE_NAME = ' + JSON.stringify(require('./package.json').name) + ';'\" > src/version.ts", | ||
| "build:core": "npm run build -w @microsoft/durabletask-js", | ||
| "build": "npm run prebuild && npm run clean && npm run build:core && tsc -p tsconfig.build.json", | ||
| "test": "jest --runInBand --detectOpenHandles", | ||
| "test:unit": "jest test/unit --runInBand --detectOpenHandles", | ||
| "prepublishOnly": "npm run build && npm run test" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/microsoft/durabletask-js.git", | ||
| "directory": "packages/azure-functions-durable" | ||
| }, | ||
| "keywords": [ | ||
| "azure-functions", | ||
| "durable-functions", | ||
| "durabletask", | ||
| "orchestration", | ||
| "workflow", | ||
| "grpc" | ||
| ], | ||
| "author": "Microsoft", | ||
| "license": "MIT", | ||
| "bugs": { | ||
| "url": "https://github.com/microsoft/durabletask-js/issues" | ||
| }, | ||
| "homepage": "https://github.com/microsoft/durabletask-js/tree/main/packages/azure-functions-durable#readme", | ||
| "engines": { | ||
| "node": ">=22.0.0" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed in f935bce |
||
| }, | ||
| "dependencies": { | ||
| "@azure/functions": "^4.16.1", | ||
| "@grpc/grpc-js": "^1.14.4", | ||
| "@microsoft/durabletask-js": "0.3.0" | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: remember to bump this to 0.4.0, and we need to release @microsoft/durabletask-js first also confirm whether @azure/functions 4.16.1 has the extension changes required,
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed in f935bce |
||
| }, | ||
| "devDependencies": { | ||
| "@types/jest": "^29.5.1", | ||
| "@types/node": "^22.0.0", | ||
| "jest": "^29.5.0", | ||
| "ts-jest": "^29.1.0", | ||
| "typescript": "^5.0.4" | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.