Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PACKAGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ The dependencies between layers are enforced by the layer-check command._

| Packages | Layer Dependencies |
| --- | --- |
| - [@fluid-example/blobs](/examples/apps/blobs) (private)</br>- [@fluid-example/collaborative-textarea](/examples/apps/collaborative-textarea) (private)</br>- [@fluid-example/contact-collection](/examples/apps/contact-collection) (private)</br>- [@fluid-example/data-object-grid](/examples/apps/data-object-grid) (private)</br>- [@fluid-example/diceroller](/examples/apps/diceroller) (private)</br>- [@fluid-example/presence-tracker](/examples/apps/presence-tracker) (private)</br>- [@fluid-example/staging](/examples/apps/staging) (private)</br>- [@fluid-example/task-selection](/examples/apps/task-selection) (private)</br>- [@fluid-example/tree-cli-app](/examples/apps/tree-cli-app) (private)</br>- [@fluid-example/tree-comparison](/examples/apps/tree-comparison) (private)</br>- [@fluid-example/bubblebench-baseline](/examples/benchmarks/bubblebench/baseline) (private)</br>- [@fluid-example/bubblebench-common](/examples/benchmarks/bubblebench/common) (private)</br>- [@fluid-example/bubblebench-experimental-tree](/examples/benchmarks/bubblebench/experimental-tree) (private)</br>- [@fluid-example/bubblebench-ot](/examples/benchmarks/bubblebench/ot) (private)</br>- [@fluid-example/bubblebench-shared-tree](/examples/benchmarks/bubblebench/shared-tree) (private)</br>- [@fluid-internal/tablebench](/examples/benchmarks/tablebench) (private)</br>- [@fluid-example/app-insights-logger](/examples/client-logger/app-insights-logger) (private)</br>- [@fluid-example/canvas](/examples/data-objects/canvas) (private)</br>- [@fluid-example/clicker](/examples/data-objects/clicker) (private)</br>- [@fluid-example/codemirror](/examples/data-objects/codemirror) (private)</br>- [@fluid-example/inventory-app](/examples/data-objects/inventory-app) (private)</br>- [@fluid-example/monaco](/examples/data-objects/monaco) (private)</br>- [@fluid-example/multiview-constellation-model](/examples/data-objects/multiview/constellation-model) (private)</br>- [@fluid-example/multiview-constellation-view](/examples/data-objects/multiview/constellation-view) (private)</br>- [@fluid-example/multiview-container](/examples/data-objects/multiview/container) (private)</br>- [@fluid-example/multiview-coordinate-model](/examples/data-objects/multiview/coordinate-model) (private)</br>- [@fluid-example/multiview-coordinate-interface](/examples/data-objects/multiview/interface) (private)</br>- [@fluid-example/multiview-plot-coordinate-view](/examples/data-objects/multiview/plot-coordinate-view) (private)</br>- [@fluid-example/multiview-slider-coordinate-view](/examples/data-objects/multiview/slider-coordinate-view) (private)</br>- [@fluid-example/multiview-triangle-view](/examples/data-objects/multiview/triangle-view) (private)</br>- [@fluid-example/prosemirror](/examples/data-objects/prosemirror) (private)</br>- [@fluid-example/smde](/examples/data-objects/smde) (private)</br>- [@fluid-example/table-document](/examples/data-objects/table-document)</br>- [@fluid-example/table-tree](/examples/data-objects/table-tree) (private)</br>- [@fluid-example/text-editor](/examples/data-objects/text-editor) (private)</br>- [@fluid-example/todo](/examples/data-objects/todo) (private)</br>- [@fluid-example/webflow](/examples/data-objects/webflow) (private)</br>- [@fluid-example/app-integration-external-data](/examples/external-data) (private)</br>- [@fluid-example/azure-client-todo-list](/examples/service-clients/azure-client/todo-list) (private)</br>- [@fluid-example/shared-tree-demo](/examples/service-clients/odsp-client/shared-tree-demo) (private)</br>- [@fluid-example/bundle-size-tests](/examples/utils/bundle-size-tests) (private)</br>- [@fluid-example/example-driver](/examples/utils/example-driver) (private)</br>- [@fluid-example/example-utils](/examples/utils/example-utils) (private)</br>- [@fluid-example/example-webpack-integration](/examples/utils/example-webpack-integration) (private)</br>- [@fluid-example/import-testing](/examples/utils/import-testing) (private)</br>- [@fluid-example/migration-tools](/examples/utils/migration-tools) (private)</br>- [@fluid-example/typescript-versions-host](/examples/utils/typescript-versions-host) (private)</br>- [@fluid-example/webpack-fluid-loader](/examples/utils/webpack-fluid-loader) (private)</br>- [@fluid-example/app-integration-live-schema-upgrade](/examples/version-migration/live-schema-upgrade) (private)</br>- [@fluid-example/version-migration-same-container](/examples/version-migration/same-container) (private)</br>- [@fluid-example/version-migration-separate-container](/examples/version-migration/separate-container) (private)</br>- [@fluid-example/tree-shim](/examples/version-migration/tree-shim) (private)</br>- [@fluid-example/app-integration-container-views](/examples/view-integration/container-views) (private)</br>- [@fluid-example/app-integration-external-views](/examples/view-integration/external-views) (private)</br>- [@fluid-example/view-framework-sampler](/examples/view-integration/view-framework-sampler) (private) | - [Core-Interfaces](#Core-Interfaces)</br>- [Driver-Definitions](#Driver-Definitions)</br>- [Container-Definitions](#Container-Definitions)</br>- [Core-Utils](#Core-Utils)</br>- [Client-Utils](#Client-Utils)</br>- [Telemetry-Utils](#Telemetry-Utils)</br>- [Driver-Utils](#Driver-Utils)</br>- [Other-Utils](#Other-Utils)</br>- [Tool-Utils](#Tool-Utils)</br>- [Driver](#Driver)</br>- [Loader](#Loader)</br>- [Runtime](#Runtime)</br>- [Framework](#Framework)</br>- [UberPackage](#UberPackage)</br>- [Server-Libs](#Server-Libs)</br>- [Routerlicious-Driver](#Routerlicious-Driver)</br>- [Test-Utils](#Test-Utils)</br>- [ServiceClients](#ServiceClients)</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp; |
| - [@fluid-example/blobs](/examples/apps/blobs) (private)</br>- [@fluid-example/claims-example](/examples/apps/claims-example) (private)</br>- [@fluid-example/collaborative-textarea](/examples/apps/collaborative-textarea) (private)</br>- [@fluid-example/contact-collection](/examples/apps/contact-collection) (private)</br>- [@fluid-example/data-object-grid](/examples/apps/data-object-grid) (private)</br>- [@fluid-example/diceroller](/examples/apps/diceroller) (private)</br>- [@fluid-example/presence-tracker](/examples/apps/presence-tracker) (private)</br>- [@fluid-example/staging](/examples/apps/staging) (private)</br>- [@fluid-example/task-selection](/examples/apps/task-selection) (private)</br>- [@fluid-example/tree-cli-app](/examples/apps/tree-cli-app) (private)</br>- [@fluid-example/tree-comparison](/examples/apps/tree-comparison) (private)</br>- [@fluid-example/bubblebench-baseline](/examples/benchmarks/bubblebench/baseline) (private)</br>- [@fluid-example/bubblebench-common](/examples/benchmarks/bubblebench/common) (private)</br>- [@fluid-example/bubblebench-experimental-tree](/examples/benchmarks/bubblebench/experimental-tree) (private)</br>- [@fluid-example/bubblebench-ot](/examples/benchmarks/bubblebench/ot) (private)</br>- [@fluid-example/bubblebench-shared-tree](/examples/benchmarks/bubblebench/shared-tree) (private)</br>- [@fluid-internal/tablebench](/examples/benchmarks/tablebench) (private)</br>- [@fluid-example/app-insights-logger](/examples/client-logger/app-insights-logger) (private)</br>- [@fluid-example/canvas](/examples/data-objects/canvas) (private)</br>- [@fluid-example/clicker](/examples/data-objects/clicker) (private)</br>- [@fluid-example/codemirror](/examples/data-objects/codemirror) (private)</br>- [@fluid-example/inventory-app](/examples/data-objects/inventory-app) (private)</br>- [@fluid-example/monaco](/examples/data-objects/monaco) (private)</br>- [@fluid-example/multiview-constellation-model](/examples/data-objects/multiview/constellation-model) (private)</br>- [@fluid-example/multiview-constellation-view](/examples/data-objects/multiview/constellation-view) (private)</br>- [@fluid-example/multiview-container](/examples/data-objects/multiview/container) (private)</br>- [@fluid-example/multiview-coordinate-model](/examples/data-objects/multiview/coordinate-model) (private)</br>- [@fluid-example/multiview-coordinate-interface](/examples/data-objects/multiview/interface) (private)</br>- [@fluid-example/multiview-plot-coordinate-view](/examples/data-objects/multiview/plot-coordinate-view) (private)</br>- [@fluid-example/multiview-slider-coordinate-view](/examples/data-objects/multiview/slider-coordinate-view) (private)</br>- [@fluid-example/multiview-triangle-view](/examples/data-objects/multiview/triangle-view) (private)</br>- [@fluid-example/prosemirror](/examples/data-objects/prosemirror) (private)</br>- [@fluid-example/smde](/examples/data-objects/smde) (private)</br>- [@fluid-example/table-document](/examples/data-objects/table-document)</br>- [@fluid-example/table-tree](/examples/data-objects/table-tree) (private)</br>- [@fluid-example/text-editor](/examples/data-objects/text-editor) (private)</br>- [@fluid-example/todo](/examples/data-objects/todo) (private)</br>- [@fluid-example/webflow](/examples/data-objects/webflow) (private)</br>- [@fluid-example/app-integration-external-data](/examples/external-data) (private)</br>- [@fluid-example/azure-client-todo-list](/examples/service-clients/azure-client/todo-list) (private)</br>- [@fluid-example/shared-tree-demo](/examples/service-clients/odsp-client/shared-tree-demo) (private)</br>- [@fluid-example/bundle-size-tests](/examples/utils/bundle-size-tests) (private)</br>- [@fluid-example/example-driver](/examples/utils/example-driver) (private)</br>- [@fluid-example/example-utils](/examples/utils/example-utils) (private)</br>- [@fluid-example/example-webpack-integration](/examples/utils/example-webpack-integration) (private)</br>- [@fluid-example/import-testing](/examples/utils/import-testing) (private)</br>- [@fluid-example/migration-tools](/examples/utils/migration-tools) (private)</br>- [@fluid-example/typescript-versions-host](/examples/utils/typescript-versions-host) (private)</br>- [@fluid-example/webpack-fluid-loader](/examples/utils/webpack-fluid-loader) (private)</br>- [@fluid-example/app-integration-live-schema-upgrade](/examples/version-migration/live-schema-upgrade) (private)</br>- [@fluid-example/version-migration-same-container](/examples/version-migration/same-container) (private)</br>- [@fluid-example/version-migration-separate-container](/examples/version-migration/separate-container) (private)</br>- [@fluid-example/tree-shim](/examples/version-migration/tree-shim) (private)</br>- [@fluid-example/app-integration-container-views](/examples/view-integration/container-views) (private)</br>- [@fluid-example/app-integration-external-views](/examples/view-integration/external-views) (private)</br>- [@fluid-example/view-framework-sampler](/examples/view-integration/view-framework-sampler) (private) | - [Core-Interfaces](#Core-Interfaces)</br>- [Driver-Definitions](#Driver-Definitions)</br>- [Container-Definitions](#Container-Definitions)</br>- [Core-Utils](#Core-Utils)</br>- [Client-Utils](#Client-Utils)</br>- [Telemetry-Utils](#Telemetry-Utils)</br>- [Driver-Utils](#Driver-Utils)</br>- [Other-Utils](#Other-Utils)</br>- [Tool-Utils](#Tool-Utils)</br>- [Driver](#Driver)</br>- [Loader](#Loader)</br>- [Runtime](#Runtime)</br>- [Framework](#Framework)</br>- [UberPackage](#UberPackage)</br>- [Server-Libs](#Server-Libs)</br>- [Routerlicious-Driver](#Routerlicious-Driver)</br>- [Test-Utils](#Test-Utils)</br>- [ServiceClients](#ServiceClients)</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp;</br>&nbsp; |

### Tools

Expand Down
72 changes: 72 additions & 0 deletions examples/apps/claims-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# @fluid-example/claims-example

This example demonstrates the **Claims DDS** running inside a **real Fluid container**. It claims ownership of resources by binding claim keys to the **`IFluidHandle`** of other DDSes.

> **Why this shape?** The Claims DDS is currently an internal building block. The intent is for it to eventually live inside every `PureDataObject`, reachable through an API on the data object itself. That API does not exist yet, so this example wires the Claims DDS up by hand inside a custom `ResourceManager` data object to show the underlying mechanics.

## What it shows

The example runs two independent clients against a single in-memory ordering service (`LocalDeltaConnectionServer`). Everything other than the service is real: a real container runtime, real ops that roundtrip through the service, and real handle serialization across clients.

A custom `ResourceManager` data object hosts one Claims DDS. To claim a resource, the client creates a brand-new `SharedMap` (the "resource") and stores **its handle** as the claim value. Other clients read the claim and resolve the handle to access the same underlying DDS.

## ClaimResult overview

`ClaimResult<T>` is a discriminated union returned by `trySetClaim` / `compareAndSetClaim`:

| Status | Meaning | Available fields |
|--------|---------|-----------------|
| `"Accepted"` | Claim accepted synchronously (only in detached mode) | `currentValue: T` |
| `"AlreadyClaimed"` | Another client already claimed this key | `currentValue: T \| undefined` |
| `"Pending"` | Op is in-flight awaiting server confirmation | `promise: Promise<ClaimConfirmation<T>>` |

In a connected container, `trySetClaim` returns `"Pending"`; awaiting the promise yields a `ClaimConfirmation<T>` whose status is `"Accepted"`, `"AlreadyClaimed"`, or `"Aborted"`.

## Scenarios demonstrated

1. **Claim a resource** — client A creates a new `SharedMap` and claims its handle under `"database"`.
2. **Cross-client read** — client B loads the same document and resolves the claimed handle to the same resource.
3. **First-writer-wins** — client B's competing claim for `"database"` is rejected and returns the winner's handle.
4. **Race** — both clients claim a fresh key concurrently; the ordering service picks the winner.
5. **Compare-and-swap** — client A atomically reassigns `"config"` to a new handle.

## Running

```bash
# From the repo root
cd examples/apps/claims-example
npm run build:esnext
npm start
```

## Key pattern

Consuming `ClaimResult` with handles in a connected container:

```typescript
const resourceHandle: IFluidHandle<ISharedMap> = resourceManager.createResource("my-resource");
const result = resourceManager.claims.trySetClaim("resource-key", resourceHandle);

switch (result.status) {
case "Accepted": {
// Detached-only: you won immediately.
const mine = await result.currentValue.get();
break;
}
case "AlreadyClaimed": {
// Someone else won — resolve the winner's handle.
const theirs = await result.currentValue?.get();
break;
}
case "Pending": {
// Connected: await server confirmation.
const confirmation = await result.promise;
if (confirmation.status === "Accepted") {
const mine = await confirmation.currentValue.get();
} else if (confirmation.status === "AlreadyClaimed") {
const theirs = await confirmation.currentValue?.get();
}
break;
}
}
```
28 changes: 28 additions & 0 deletions examples/apps/claims-example/eslint.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import type { Linter } from "eslint";
import { strict } from "@fluidframework/eslint-config-fluid/flat.mts";
import sharedConfig, { importInternalModulesAllowed } from "../../eslint.config.data.mts";

const config: Linter.Config[] = [
...strict,
...sharedConfig,
{
rules: {
"import-x/no-internal-modules": [
"error",
{
// This example consumes the Claims DDS and the container/runtime plumbing
// through their `/internal` entry points, since no public consumption path
// for Claims exists yet.
allow: [...importInternalModulesAllowed, "@fluidframework/*/internal"],
},
],
},
},
];

export default config;
55 changes: 55 additions & 0 deletions examples/apps/claims-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@fluid-example/claims-example",
"version": "2.110.0",
"private": true,
"description": "Example demonstrating handle-based claiming patterns with the Claims DDS",
"homepage": "https://fluidframework.com",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/FluidFramework.git",
"directory": "examples/apps/claims-example"
},
"license": "MIT",
"author": "Microsoft and contributors",
"type": "module",
"scripts": {
"build": "fluid-build . --task build",
"build:compile": "fluid-build . --task compile",
"build:esnext": "tsc --project ./tsconfig.json",
"check:biome": "biome check .",
"check:format": "npm run check:biome",
"clean": "rimraf --glob dist lib \"**/*.tsbuildinfo\" \"**/*.build.log\" nyc",
"eslint": "eslint --quiet --format stylish src",
"eslint:fix": "eslint --quiet --format stylish src --fix --fix-type problem,suggestion,layout",
"format": "npm run format:biome",
"format:biome": "biome check . --write",
"lint": "fluid-build . --task lint",
"lint:fix": "fluid-build . --task eslint:fix --task format",
"start": "node ./lib/index.js"
},
"dependencies": {
"@fluid-internal/claims": "workspace:~",
"@fluidframework/aqueduct": "workspace:~",
"@fluidframework/container-definitions": "workspace:~",
"@fluidframework/container-loader": "workspace:~",
"@fluidframework/core-interfaces": "workspace:~",
"@fluidframework/local-driver": "workspace:~",
"@fluidframework/map": "workspace:~",
"@fluidframework/runtime-definitions": "workspace:~",
"@fluidframework/server-local-server": "^7.0.1"
},
"devDependencies": {
"@biomejs/biome": "~2.4.5",
"@fluidframework/build-tools": "catalog:buildTools",
"@fluidframework/eslint-config-fluid": "catalog:eslint",
"@fluidframework/test-utils": "workspace:~",
"@types/node": "catalog:types",
"eslint": "catalog:eslint",
"jiti": "^2.6.1",
"rimraf": "^6.1.3",
"typescript": "~5.4.5"
},
"typeValidation": {
"disabled": true
}
}
Loading
Loading