This commit is contained in:
Sonali Deshpande 2022-04-28 12:22:09 -07:00 коммит произвёл GitHub
Родитель a5e03298e6
Коммит 215ec2086e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
302 изменённых файлов: 8522 добавлений и 1779 удалений

Просмотреть файл

@ -32,6 +32,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
- login__microsoft__clientId
- login__microsoft__secret
- login__accounts
@ -44,6 +45,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
deli:
image: prague.azurecr.io/prague:12579
@ -51,6 +53,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
scriptorium:
image: prague.azurecr.io/prague:12579
@ -58,6 +61,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
broadcaster:
image: prague.azurecr.io/prague:12579
@ -65,6 +69,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
scribe:
image: prague.azurecr.io/prague:12579
@ -72,6 +77,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
riddler:
image: prague.azurecr.io/prague:12579
@ -81,6 +87,7 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
historian:
image: prague.azurecr.io/historian:5109
@ -89,12 +96,14 @@ services:
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
restart: always
gitrest:
image: prague.azurecr.io/gitrest:4048
environment:
- DEBUG=fluid:*
- NODE_ENV=development
- IS_FLUID_SERVER=true
volumes:
- git:/home/node/documents
restart: always

Просмотреть файл

@ -45,10 +45,14 @@ In order to use `IContainer.closeAndGetPendingLocalState`, pass a set of options
## 0.59 Upcoming changes
- [Remove ICodeLoader interface](#Remove-ICodeLoader-interface)
- [IFluidContainer.connect() and IFluidContainer.disconnect() will be made mandatory in future major release](#ifluidcontainer-connect-and-ifluidcontainer-disconnect-will-be-made-mandatory-in-future-major-release)
### Remove ICodeLoader interface
ICodeLoader interface was deprecated a while ago and will be removed in the next release. Please refer to [replace ICodeLoader with ICodeDetailsLoader interface](#Replace-ICodeLoader-with-ICodeDetailsLoader-interface) for more details.
### IFluidContainer.connect() and IFluidContainer.disconnect() will be made mandatory in future major release
In major release 1.0, the optional functions `IFluidContainer.connect()` and `IFluidContainer.disconnect()` will be made mandatory functions.
## 0.59 Breaking changes
- [Removing Commit from TreeEntry and commits from SnapShotTree](#Removing-Commit-from-TreeEntry-and-commits-from-SnapShotTree)
- [raiseContainerWarning removed from IContainerContext](#raiseContainerWarning-removed-from-IContainerContext)

Просмотреть файл

@ -498,6 +498,8 @@ export const ISummarizer: keyof IProvideSummarizer;
// @public (undocumented)
export interface ISummarizer extends IEventProvider<ISummarizerEvents>, IFluidLoadable, Partial<IProvideSummarizer> {
// (undocumented)
close(): void;
enqueueSummarize(options: IEnqueueSummarizeOptions): EnqueueSummarizeResult;
// (undocumented)
run(onBehalfOf: string, options?: Readonly<Partial<ISummarizerOptions>>): Promise<SummarizerStopReason>;
@ -670,6 +672,8 @@ export class Summarizer extends EventEmitter implements ISummarizer {
constructor(url: string,
runtime: ISummarizerRuntime, configurationGetter: () => ISummaryConfiguration,
internalsProvider: ISummarizerInternalsProvider, handleContext: IFluidHandleContext, summaryCollection: SummaryCollection, runCoordinatorCreateFn: (runtime: IConnectableRuntime) => Promise<ICancellableSummarizerController>);
// (undocumented)
close(): void;
static create(loader: ILoader, url: string): Promise<ISummarizer>;
dispose(): void;
// (undocumented)

Просмотреть файл

@ -80,7 +80,6 @@ export interface IDeltaConnection {
// @public
export interface IDeltaHandler {
// (undocumented)
applyStashedOp(message: any): unknown;
process: (message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) => void;
reSubmit(message: any, localOpMetadata: unknown): void;
@ -140,5 +139,4 @@ export type Jsonable<T = any, TReplaced = void> = T extends undefined | null | b
// @public
export type Serializable<T = any> = Jsonable<T, IFluidHandle>;
```

Просмотреть файл

@ -516,6 +516,12 @@ export interface InsertInternal_0_0_2 {
readonly type: typeof ChangeTypeInternal.Insert;
}
// @public
export interface InternalizedChange {
// (undocumented)
InternalChangeBrand: '2cae1045-61cf-4ef7-a6a3-8ad920cb7ab3';
}
// @public
export type InternedStringId = number & {
readonly InternedStringId: 'e221abc9-9d17-4493-8db0-70c871a1c27c';
@ -819,9 +825,9 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
constructor(runtime: IFluidDataStoreRuntime, id: string, writeFormat: WriteFormat, summarizeHistory?: false | {
uploadEditChunks: boolean;
}, expensiveValidation?: boolean);
applyEdit(...changes: Change[]): Edit<unknown>;
applyEdit(...changes: Change[]): Edit<InternalizedChange>;
// (undocumented)
applyEdit(changes: Change[]): Edit<unknown>;
applyEdit(changes: Change[]): Edit<InternalizedChange>;
// @internal
applyEditInternal(editOrChanges: Edit<ChangeInternal> | readonly ChangeInternal[]): Edit<ChangeInternal>;
protected applyStashedOp(op: unknown): void;
@ -831,9 +837,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
// (undocumented)
get currentView(): RevisionView;
// (undocumented)
get edits(): OrderedEditSet;
// @internal (undocumented)
get editsInternal(): OrderedEditSet<ChangeInternal>;
get edits(): OrderedEditSet<InternalizedChange>;
// @internal
equals(sharedTree: SharedTree): boolean;
generateNodeId(override?: string): NodeId;
@ -855,6 +859,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
// (undocumented)
protected readonly logger: ITelemetryLogger;
get logViewer(): LogViewer;
mergeEditsFrom(other: SharedTree, edits: Iterable<Edit<InternalizedChange>>, stableIdRemapper?: (id: StableNodeId) => StableNodeId): EditId[];
// (undocumented)
protected onDisconnect(): void;
// (undocumented)
@ -863,7 +868,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
protected registerCore(): void;
revert(editId: EditId): EditId | undefined;
// @internal
revertChanges(changes: readonly ChangeInternal[], before: RevisionView): ChangeInternal[] | undefined;
revertChanges(changes: readonly InternalizedChange[], before: RevisionView): ChangeInternal[] | undefined;
// @internal
saveSerializedSummary(options?: {
serializer?: IFluidSerializer;

Просмотреть файл

@ -42,9 +42,11 @@ export class FluidContainer extends TypedEventEmitter<IFluidContainerEvents> imp
constructor(container: IContainer, rootDataObject: RootDataObject);
attach(): Promise<string>;
get attachState(): AttachState;
connect(): Promise<void>;
get connected(): boolean;
get connectionState(): ConnectionState;
create<T extends IFluidLoadable>(objectClass: LoadableObjectClass<T>): Promise<T>;
disconnect(): Promise<void>;
dispose(): void;
get disposed(): boolean;
get initialObjects(): Record<string, IFluidLoadable>;
@ -61,10 +63,12 @@ export interface IConnection {
export interface IFluidContainer extends IEventProvider<IFluidContainerEvents> {
attach(): Promise<string>;
readonly attachState: AttachState;
connect?(): void;
// @deprecated
readonly connected: boolean;
readonly connectionState: ConnectionState;
create<T extends IFluidLoadable>(objectClass: LoadableObjectClass<T>): Promise<T>;
disconnect?(): void;
dispose(): void;
readonly disposed: boolean;
readonly initialObjects: LoadableObjectRecord;

Просмотреть файл

@ -43,7 +43,7 @@ export type MatrixItem<T> = Serializable<Exclude<T, null>> | undefined;
export class SharedMatrix<T = any> extends SharedObject implements IMatrixProducer<MatrixItem<T>>, IMatrixReader<MatrixItem<T>>, IMatrixWriter<MatrixItem<T>> {
constructor(runtime: IFluidDataStoreRuntime, id: string, attributes: IChannelAttributes);
// (undocumented)
protected applyStashedOp(): void;
protected applyStashedOp(content: any): unknown;
// (undocumented)
closeMatrix(consumer: IMatrixConsumer<MatrixItem<T>>): void;
// (undocumented)
@ -116,7 +116,6 @@ export class SharedMatrixFactory implements IChannelFactory {
get type(): string;
}
// (No @packageDocumentation comment for this package)
```

Просмотреть файл

@ -104,7 +104,13 @@ export class Client {
annotateMarkerNotifyConsensus(marker: Marker, props: PropertySet, consensusCallback: (m: Marker) => void): IMergeTreeAnnotateMsg | undefined;
annotateRangeLocal(start: number, end: number, props: PropertySet, combiningOp: ICombiningOp | undefined): IMergeTreeAnnotateMsg | undefined;
// (undocumented)
applyMsg(msg: ISequencedDocumentMessage): void;
applyMsg(msg: ISequencedDocumentMessage, local?: boolean): void;
// (undocumented)
applyStashedOp(op: IMergeTreeDeltaOp): SegmentGroup;
// (undocumented)
applyStashedOp(op: IMergeTreeGroupMsg): SegmentGroup[];
// (undocumented)
applyStashedOp(op: IMergeTreeOp): SegmentGroup | SegmentGroup[];
// (undocumented)
cloneFromSegments(): Client;
// (undocumented)
@ -825,7 +831,7 @@ export class LocalReference implements ReferencePosition {
toPosition(): number;
}
// @public (undocumented)
// @public
export class LocalReferenceCollection {
// (undocumented)
[Symbol.iterator](): {
@ -833,7 +839,8 @@ export class LocalReferenceCollection {
[Symbol.iterator](): any;
};
// Warning: (ae-forgotten-export) The symbol "IRefsAtOffset" needs to be exported by the entry point index.d.ts
constructor(segment: ISegment, initialRefsByfOffset?: (IRefsAtOffset | undefined)[]);
constructor(
segment: ISegment, initialRefsByfOffset?: (IRefsAtOffset | undefined)[]);
// (undocumented)
addAfterTombstones(...refs: Iterable<LocalReference>[]): void;
// (undocumented)
@ -851,7 +858,6 @@ export class LocalReferenceCollection {
hierRefCount: number;
// (undocumented)
removeLocalRef(lref: LocalReference): LocalReference | undefined;
// (undocumented)
split(offset: number, splitSeg: ISegment): void;
}
@ -1044,7 +1050,7 @@ export class MergeTree {
startCollaboration(localClientId: number, minSeq: number, currentSeq: number): void;
// (undocumented)
walkAllSegments<TClientData>(block: IMergeBlock, action: (segment: ISegment, accum?: TClientData) => boolean, accum?: TClientData): boolean;
}
}
// @public (undocumented)
export type MergeTreeDeltaCallback = (opArgs: IMergeTreeDeltaOpArgs, deltaArgs: IMergeTreeDeltaCallbackArgs) => void;
@ -1114,7 +1120,7 @@ export class MergeTreeTextHelper {
parallelText: string[];
parallelMarkers: Marker[];
};
}
}
// @public (undocumented)
export interface MinListener {
@ -1147,7 +1153,7 @@ export class PropertiesManager {
copyTo(oldProps: PropertySet, newProps: PropertySet | undefined, newManager: PropertiesManager): PropertySet | undefined;
// (undocumented)
hasPendingProperties(): boolean;
}
}
// @public (undocumented)
export interface Property<TKey, TData> {
@ -1566,7 +1572,6 @@ export const UnassignedSequenceNumber = -1;
// @public
export const UniversalSequenceNumber = 0;
// (No @packageDocumentation comment for this package)
```

59
api-report/quorum.api.md Normal file
Просмотреть файл

@ -0,0 +1,59 @@
## API Report File for "@fluid-internal/quorum"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { IChannelAttributes } from '@fluidframework/datastore-definitions';
import { IChannelFactory } from '@fluidframework/datastore-definitions';
import { IChannelStorageService } from '@fluidframework/datastore-definitions';
import { IFluidDataStoreRuntime } from '@fluidframework/datastore-definitions';
import { IFluidSerializer } from '@fluidframework/shared-object-base';
import { ISequencedDocumentMessage } from '@fluidframework/protocol-definitions';
import { ISharedObject } from '@fluidframework/shared-object-base';
import { ISharedObjectEvents } from '@fluidframework/shared-object-base';
import { ISummaryTreeWithStats } from '@fluidframework/runtime-definitions';
import { SharedObject } from '@fluidframework/shared-object-base';
// @public
export interface IQuorum extends ISharedObject<IQuorumEvents> {
delete(key: string): void;
get(key: string): any;
getPending(key: string): any;
has(key: string): boolean;
set(key: string, value: any): void;
}
// @public
export interface IQuorumEvents extends ISharedObjectEvents {
(event: "pending" | "accepted", listener: (key: string) => void): any;
}
// @public
export class Quorum extends SharedObject<IQuorumEvents> implements IQuorum {
constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes);
// (undocumented)
applyStashedOp(): void;
static create(runtime: IFluidDataStoreRuntime, id?: string): Quorum;
delete(key: string): void;
get(key: string): any;
static getFactory(): IChannelFactory;
getPending(key: string): any;
has(key: string): boolean;
// @internal (undocumented)
protected initializeLocalCore(): void;
// @internal (undocumented)
protected loadCore(storage: IChannelStorageService): Promise<void>;
// @internal (undocumented)
protected onDisconnect(): void;
// @internal
protected processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void;
set(key: string, value: any): void;
// @internal
protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats;
}
// (No @packageDocumentation comment for this package)
```

Просмотреть файл

@ -76,7 +76,7 @@ export class DocumentService implements api.IDocumentService {
// @public (undocumented)
export class DocumentStorageService extends DocumentStorageServiceProxy {
constructor(id: string, manager: GitManager, logger: ITelemetryLogger, policies?: IDocumentStorageServicePolicies, driverPolicies?: IRouterliciousDriverPolicies, blobCache?: ICache<ArrayBufferLike>, snapshotTreeCache?: ICache<ISnapshotTreeVersion>);
constructor(id: string, manager: GitManager, logger: ITelemetryLogger, policies?: IDocumentStorageServicePolicies, driverPolicies?: IRouterliciousDriverPolicies, blobCache?: ICache<ArrayBufferLike>, snapshotTreeCache?: ICache<ISnapshotTreeVersion>, noCacheGitManager?: GitManager | undefined);
// (undocumented)
getSnapshotTree(version?: IVersion): Promise<ISnapshotTree | null>;
// (undocumented)
@ -85,11 +85,14 @@ export class DocumentStorageService extends DocumentStorageServiceProxy {
get logTailSha(): string | undefined;
// (undocumented)
manager: GitManager;
// (undocumented)
noCacheGitManager?: GitManager | undefined;
}
// @public (undocumented)
export interface IRouterliciousDriverPolicies {
aggregateBlobsSmallerThanBytes: number | undefined;
enableDiscovery?: boolean;
enablePrefetch: boolean;
enableRestLess: boolean;
enableWholeSummaryUpload: boolean;
@ -141,7 +144,7 @@ export class RouterliciousDocumentServiceFactory implements IDocumentServiceFact
constructor(tokenProvider: ITokenProvider, driverPolicies?: Partial<IRouterliciousDriverPolicies>);
// (undocumented)
createContainer(createNewSummary: ISummaryTree | undefined, resolvedUrl: IResolvedUrl, logger?: ITelemetryBaseLogger, clientIsSummarizer?: boolean): Promise<IDocumentService>;
createDocumentService(resolvedUrl: IResolvedUrl, logger?: ITelemetryBaseLogger, clientIsSummarizer?: boolean): Promise<IDocumentService>;
createDocumentService(resolvedUrl: IResolvedUrl, logger?: ITelemetryBaseLogger, clientIsSummarizer?: boolean, isCreateContainer?: boolean): Promise<IDocumentService>;
// (undocumented)
readonly protocolName = "fluid:";
}

Просмотреть файл

@ -176,7 +176,7 @@ export class IntervalCollectionIterator<TInterval extends ISerializableInterval>
value: TInterval;
done: boolean;
};
}
}
// @public (undocumented)
export enum IntervalType {
@ -359,12 +359,12 @@ export abstract class SequenceEvent<TOperation extends MergeTreeDeltaOperationTy
readonly deltaArgs: IMergeTreeDeltaCallbackArgs<TOperation>;
// (undocumented)
readonly deltaOperation: TOperation;
get first(): Readonly<ISequenceDeltaRange<TOperation>> | undefined;
// (undocumented)
get first(): Readonly<ISequenceDeltaRange<TOperation>>;
// @deprecated (undocumented)
readonly isEmpty: boolean;
get last(): Readonly<ISequenceDeltaRange<TOperation>> | undefined;
get last(): Readonly<ISequenceDeltaRange<TOperation>>;
get ranges(): readonly Readonly<ISequenceDeltaRange<TOperation>>[];
}
}
// @public (undocumented)
export class SequenceInterval implements ISerializableInterval {
@ -523,7 +523,7 @@ export abstract class SharedSegmentSequence<T extends ISegment> extends SharedOb
addLocalReference(lref: LocalReference): void;
annotateRange(start: number, end: number, props: PropertySet, combiningOp?: ICombiningOp): void;
// (undocumented)
protected applyStashedOp(): void;
protected applyStashedOp(content: any): unknown;
// (undocumented)
protected client: Client;
// (undocumented)
@ -747,7 +747,6 @@ export class SubSequence<T> extends BaseSegment {
static readonly typeString: string;
}
// (No @packageDocumentation comment for this package)
```

Просмотреть файл

@ -100,7 +100,6 @@ export abstract class SharedObject<TEvent extends ISharedObjectEvents = ISharedO
// @public
export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISharedObjectEvents> extends EventEmitterWithErrorHandling<TEvent> implements ISharedObject<TEvent> {
constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes);
// (undocumented)
protected abstract applyStashedOp(content: any): unknown;
// (undocumented)
readonly attributes: IChannelAttributes;
@ -132,7 +131,7 @@ export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISha
protected runtime: IFluidDataStoreRuntime;
protected submitLocalMessage(content: any, localOpMetadata?: unknown): void;
abstract summarize(fullTree?: boolean, trackState?: boolean): Promise<ISummaryTreeWithStats>;
}
}
// @public
export class SummarySerializer extends FluidSerializer {
@ -152,7 +151,6 @@ export enum ValueType {
Shared = 0
}
// (No @packageDocumentation comment for this package)
```

Просмотреть файл

@ -32,8 +32,6 @@ a diff to review as part of a PR -- just like we do with API reports for code ch
<!-- AUTO-GENERATED-CONTENT:START (SCRIPTS) -->
| Script | Description |
|--------|-------------|
| `build` | Build the package. |
| `build:ci` | Build the package -- called as part of CI. |
| `cleanup-printed-configs` | Clean up the printed configs. Removes the `parser` property and sorts the JSON. |
| `print-config` | Print all the eslint configs. |
| `print-config:default` | Print the eslint config for regular TypeScript files (`eslint --config index.js --print-config src/file.ts`). |

6
common/build/eslint-config-fluid/package-lock.json сгенерированный
Просмотреть файл

@ -3660,9 +3660,9 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
},
"typescript": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz",
"integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"unbox-primitive": {

Просмотреть файл

@ -36,6 +36,6 @@
"eslint": "~8.6.0",
"node-jq": "^2.3.3",
"sort-json": "^2.0.1",
"typescript": "^4.5.3"
"typescript": "~4.5.5"
}
}

92
common/lib/common-definitions/package-lock.json сгенерированный
Просмотреть файл

@ -1275,6 +1275,63 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -2530,11 +2587,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -4790,6 +4860,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -5651,9 +5727,9 @@
}
},
"typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -55,11 +55,14 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"rimraf": "^2.6.2",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"typeValidation": {

122
common/lib/common-utils/package-lock.json сгенерированный
Просмотреть файл

@ -2367,9 +2367,9 @@
"dev": true
},
"@types/mocha": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
"integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz",
"integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==",
"dev": true
},
"@types/node": {
@ -2753,6 +2753,87 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -5054,11 +5135,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -11342,6 +11436,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@ -13419,9 +13519,9 @@
}
},
"typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -106,8 +106,11 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"jest": "^26.6.3",
"jest-junit": "^10.0.0",
@ -120,7 +123,7 @@
"sinon": "^7.4.2",
"ts-jest": "^26.4.4",
"ts-node": "^7.0.1",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"jest-junit": {

Просмотреть файл

@ -8,7 +8,9 @@
* impact on bundle sizes.
* @param condition - The condition that should be true, if the condition is false an error will be thrown.
* @param message - The message to include in the error when the condition does not hold.
* A number should not be specificed manually. Run policy-check to get shortcode number assigned.
* A number should not be specificed manually: use a string.
* Before a release, policy-check should be run, which will convert any asserts still using strings to
* use numbered error codes instead.
*/
export function assert(condition: boolean, message: string | number): asserts condition {
if (!condition) {

Просмотреть файл

@ -12,7 +12,7 @@ describe("Assert", () => {
for (const shortCode of ["0x000", "0x03a", "0x200", "0x4321"]) {
try {
assert(false, Number.parseInt(shortCode, 16));
} catch (e) {
} catch (e: any) {
strict(e instanceof Error, "not an error");
strict.strictEqual(
e.message,

92
common/lib/container-definitions/package-lock.json сгенерированный
Просмотреть файл

@ -1350,6 +1350,63 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -2567,11 +2624,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -4791,6 +4861,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -5581,9 +5657,9 @@
}
},
"typescript": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -62,11 +62,14 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"rimraf": "^2.6.2",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"typeValidation": {

92
common/lib/core-interfaces/package-lock.json сгенерированный
Просмотреть файл

@ -1297,6 +1297,63 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -2514,11 +2571,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -4738,6 +4808,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -5528,9 +5604,9 @@
}
},
"typescript": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -54,11 +54,14 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"rimraf": "^2.6.2",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"typeValidation": {

92
common/lib/driver-definitions/package-lock.json сгенерированный
Просмотреть файл

@ -1320,6 +1320,63 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -2537,11 +2594,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -4761,6 +4831,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -5551,9 +5627,9 @@
}
},
"typescript": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -57,11 +57,14 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"rimraf": "^2.6.2",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"typeValidation": {

92
common/lib/protocol-definitions/package-lock.json сгенерированный
Просмотреть файл

@ -1305,6 +1305,63 @@
}
}
},
"@typescript-eslint/utils": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz",
"integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.20.0",
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/typescript-estree": "5.20.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz",
"integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0"
}
},
"@typescript-eslint/types": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz",
"integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz",
"integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"@typescript-eslint/visitor-keys": "5.20.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
"semver": "^7.3.5",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz",
"integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.20.0",
"eslint-visitor-keys": "^3.0.0"
}
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz",
@ -2522,11 +2579,24 @@
}
}
},
"eslint-plugin-no-null": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz",
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
"dev": true
"eslint-plugin-jest": {
"version": "26.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.4.tgz",
"integrity": "sha512-wgqxujmqc2qpvZqMFWCh6Cniqc8lWpapvXt9j/19DmBDqeDaYhJrSRezYR1SKyemvjx+9e9kny/dgRahraHImA==",
"dev": true,
"requires": {
"@typescript-eslint/utils": "^5.10.0"
}
},
"eslint-plugin-mocha": {
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.0.4.tgz",
"integrity": "sha512-8wzAeepVY027oBHz/TmBmUr7vhVqoC1KTFeDybFLhbaWKx+aQ7fJJVuUsqcUy+L+G+XvgQBJY+cbAf7hl5DF7Q==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
"ramda": "^0.28.0"
}
},
"eslint-plugin-promise": {
"version": "6.0.0",
@ -4746,6 +4816,12 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"ramda": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz",
"integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==",
"dev": true
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -5536,9 +5612,9 @@
}
},
"typescript": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"dev": true
},
"typescript-formatter": {

Просмотреть файл

@ -55,11 +55,14 @@
"eslint-plugin-editorconfig": "~3.2.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-no-null": "~1.0.2",
"eslint-plugin-jest": "~26.1.3",
"eslint-plugin-mocha": "~10.0.3",
"eslint-plugin-promise": "~6.0.0",
"eslint-plugin-react": "~7.28.0",
"eslint-plugin-tsdoc": "~0.2.14",
"eslint-plugin-unicorn": "~40.0.0",
"rimraf": "^2.6.2",
"typescript": "~4.1.3",
"typescript": "~4.5.5",
"typescript-formatter": "7.1.0"
},
"typeValidation": {

Просмотреть файл

@ -11,13 +11,13 @@ aliases:
<!-- AUTO-GENERATED-CONTENT:START (INCLUDE:path=packages/dds/sequence/README.md&start=2&end=252) -->
The **@fluidframework/sequence** packages supports distributed data structures which are list-like. It includes
SharedString for storing storing and simultaneously editing a sequence of text. Note that SharedString is a sequence
SharedString for storing and simultaneously editing a sequence of text. Note that SharedString is a sequence
DDS but it has additional specialized features and behaviors for working with text.
Sequence DDSes share a common base class, SharedSegmentSequence. For the remainder of this document, the term
*sequence* refers to this base class.
*Item*s are the individual units that are stored within the sequence (i.e. in a SharedString the items are characters),
*Item*s are the individual units that are stored within the sequence (e.g. in a SharedString the items are characters),
but regardless of the type of data stored in the sequence, every item in a sequence is at a specific *position* starting
at 0, similar to an array. However, sequences differ from arrays in that the positions can move as local and remote
editors make modifications to the sequence.
@ -64,7 +64,7 @@ sequence, to insert at the end.
// content:
// positions:
// insert text at positions 0
// insert text at position 0
sharedString.insertText(0, "hi");
// content: hi
// positions: 01

122
docs/package-lock.json сгенерированный
Просмотреть файл

@ -28,26 +28,26 @@
}
},
"@microsoft/api-extractor-model": {
"version": "7.15.3",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.15.3.tgz",
"integrity": "sha512-NkSjolmSI7NGvbdz0Y7kjQfdpD+j9E5CwXTxEyjDqxd10MI7GXV8DnAsQ57GFJcgHKgTjf2aUnYfMJ9w3aMicw==",
"version": "7.17.2",
"resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.17.2.tgz",
"integrity": "sha512-fYfCeBeLm7jnZligC64qHiH4/vzswFLDfyPpX+uKO36OI2kIeMHrYG0zaezmuinKvE4vg1dAz38zZeDbPvBKGg==",
"requires": {
"@microsoft/tsdoc": "0.13.2",
"@microsoft/tsdoc-config": "~0.15.2",
"@rushstack/node-core-library": "3.45.0"
"@microsoft/tsdoc": "0.14.1",
"@microsoft/tsdoc-config": "~0.16.1",
"@rushstack/node-core-library": "3.45.4"
}
},
"@microsoft/tsdoc": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz",
"integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg=="
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz",
"integrity": "sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw=="
},
"@microsoft/tsdoc-config": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz",
"integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==",
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.1.tgz",
"integrity": "sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==",
"requires": {
"@microsoft/tsdoc": "0.13.2",
"@microsoft/tsdoc": "0.14.1",
"ajv": "~6.12.6",
"jju": "~1.4.0",
"resolve": "~1.19.0"
@ -97,9 +97,9 @@
}
},
"@rushstack/node-core-library": {
"version": "3.45.0",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz",
"integrity": "sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw==",
"version": "3.45.4",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.4.tgz",
"integrity": "sha512-FMoEQWjK7nWAO2uFgV1eVpVhY9ZDGOdIIomi9zTej64cKJ+8/Nvu+ny0xKaUDEjw/ALftN2D2ml7L0RDpW/Z9g==",
"requires": {
"@types/node": "12.20.24",
"colors": "~1.2.1",
@ -117,11 +117,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz",
"integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ=="
},
"colors": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
"integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg=="
},
"fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
@ -135,21 +130,14 @@
}
},
"@rushstack/ts-command-line": {
"version": "4.10.6",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.6.tgz",
"integrity": "sha512-Y3GkUag39sTIlukDg9mUp8MCHrrlJ27POrBNRQGc/uF+VVgX8M7zMzHch5zP6O1QVquWgD7Engdpn2piPYaS/g==",
"version": "4.10.10",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.10.tgz",
"integrity": "sha512-F+MH7InPDXqX40qvvcEsnvPpmg566SBpfFqj2fcCh8RjM6AyOoWlXc8zx7giBD3ZN85NVAEjZAgrcLU0z+R2yg==",
"requires": {
"@types/argparse": "1.0.38",
"argparse": "~1.0.9",
"colors": "~1.2.1",
"string-argv": "~0.3.1"
},
"dependencies": {
"colors": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
"integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg=="
}
}
},
"@szmarczak/http-timer": {
@ -207,29 +195,17 @@
}
},
"@tylerbu/custom-api-documenter": {
"version": "0.3.3-tylerbu-6-prerelease",
"resolved": "https://registry.npmjs.org/@tylerbu/custom-api-documenter/-/custom-api-documenter-0.3.3-tylerbu-6-prerelease.tgz",
"integrity": "sha512-IZ6Q0U3hUg2B3WERCU8tMYnQqZxRkO15O76+HqAHjy598E7CY4lEIvrceuozjqAwXsF2StW2YaadaaHRw8c++Q==",
"version": "0.3.4-1",
"resolved": "https://registry.npmjs.org/@tylerbu/custom-api-documenter/-/custom-api-documenter-0.3.4-1.tgz",
"integrity": "sha512-bGiiuKEUDQ20Ttxm8GT928Jb1DRbOseCZbrDUzQrGQSd6IOxEIscpkTVJoccVkcF36LcColQLw+UEoGwZOeN6w==",
"requires": {
"@microsoft/api-extractor-model": "^7.15.3",
"@microsoft/tsdoc": "0.13.2",
"@rushstack/node-core-library": "^3.45.0",
"@rushstack/ts-command-line": "^4.10.6",
"colors": "~1.4.0",
"js-yaml": "~4.1.0",
"resolve": "~1.21.0"
},
"dependencies": {
"resolve": {
"version": "1.21.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.1.tgz",
"integrity": "sha512-lfEImVbnolPuaSZuLQ52cAxPBHeI77sPwCOWRdy12UG/CNa8an7oBHH1R+Fp1/mUqSJi4c8TIP6FOIPSZAUrEQ==",
"requires": {
"is-core-module": "^2.8.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
}
"@microsoft/api-extractor-model": "~7.17.2",
"@microsoft/tsdoc": "0.14.1",
"@rushstack/node-core-library": "^3.45.4",
"@rushstack/ts-command-line": "^4.5.0",
"colors": "~1.2.1",
"js-yaml": "~3.13.1",
"resolve": "~1.17.0"
}
},
"@tylerbu/dl-cli": {
@ -1430,9 +1406,9 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz",
"integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg=="
},
"combined-stream": {
"version": "1.0.8",
@ -2299,6 +2275,11 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
@ -3592,18 +3573,12 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^2.0.1"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
}
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-parse-even-better-errors": {
@ -5779,11 +5754,6 @@
}
}
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"sync-request": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
@ -6512,11 +6482,11 @@
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"z-schema": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz",
"integrity": "sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.3.tgz",
"integrity": "sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==",
"requires": {
"commander": "^2.7.1",
"commander": "^2.20.3",
"lodash.get": "^4.4.2",
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"

Просмотреть файл

@ -32,7 +32,7 @@
"start": "npm run build:md-magic && hugo server"
},
"dependencies": {
"@tylerbu/custom-api-documenter": "0.3.3-tylerbu-6-prerelease",
"@tylerbu/custom-api-documenter": "~0.3.4-1",
"@tylerbu/dl-cli": "1.1.2-tylerbu-0",
"@tylerbu/markdown-magic": "^2.4.0-tylerbu-1",
"@vscode/codicons": "0.0.28",

Просмотреть файл

@ -30,11 +30,13 @@ module.exports = env => {
}],
exclude: /node_modules/
},
{
test: /\.js$/,
use: [require.resolve("source-map-loader")],
enforce: "pre"
},
// This example currently has missing sourcemap issues.
// Disabling source mapping allows it to be runnable with these issues.
// {
// test: /\.js$/,
// use: [require.resolve("source-map-loader")],
// enforce: "pre"
// },
{
test: /\.css$/,
use: [
@ -83,8 +85,6 @@ module.exports = env => {
},
devServer: {
host: "0.0.0.0",
onBeforeSetupMiddleware: (devServer) => fluidRoute.before(devServer.app, devServer, env),
onAfterSetupMiddleware: (devServer) => fluidRoute.after(devServer.app, devServer, __dirname, env),
},
// This impacts which files are watched by the dev server (and likely by webpack if watch is true).
// This should be configurable under devServer.static.watch

Просмотреть файл

@ -197,7 +197,7 @@ describeLoaderCompat("Table Document with Interception", (getTestObjectProvider)
let asserted: boolean = false;
try {
tableDocumentWithInterception.setCellValue(cell.row, cell.col, cell.value);
} catch (error) {
} catch (error: any) {
assert.strictEqual(error.message,
"Interception wrapper method called recursively from the interception callback",
"We should have caught an assert in setCellValue because it detects an infinite recursion");

Просмотреть файл

@ -81,13 +81,14 @@ module.exports = env => {
libraryTarget: "umd",
globalObject: 'self',
},
devServer: {
onBeforeSetupMiddleware: (devServer) => fluidRoute.before(devServer.app, devServer, env),
onAfterSetupMiddleware: (devServer) => fluidRoute.after(devServer.app, devServer, __dirname, env),
watchOptions: {
ignored: "**/node_modules/**",
}
},
// This impacts which files are watched by the dev server (and likely by webpack if watch is true).
// This should be configurable under devServer.static.watch
// (see https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md) but that does not seem to work.
// The CLI options for disabling watching don't seem to work either, so this may be a symptom of using webpack4 with the newer webpack-cli and webpack-dev-server.
watchOptions: {
ignored: "**/node_modules/**",
}
},
isProduction ? require("./webpack.prod") : require("./webpack.dev"));
isProduction ? require("./webpack.prod") : require("./webpack.dev"),
fluidRoute.devServerConfig(__dirname, env));
};

Просмотреть файл

@ -30,7 +30,6 @@ import {
SequenceDeltaEvent,
} from "@fluidframework/sequence";
import { ISharedDirectory, SharedDirectory } from "@fluidframework/map";
import { IFluidHTMLOptions } from "@fluidframework/view-interfaces";
import { IEvent } from "@fluidframework/common-definitions";
import { clamp, emptyArray, randomId, TagName, TokenList } from "../util";
import { IHTMLAttributes } from "../util/attr";
@ -43,14 +42,13 @@ export const enum DocSegmentKind {
paragraph = "<p>",
lineBreak = "<br>",
beginTags = "<t>",
inclusion = "<?>",
endTags = "</>",
// Special case for LocalReference to end of document. (See comments on 'endOfTextSegment').
endOfText = "eot",
}
const tilesAndRanges = new Set([DocSegmentKind.paragraph, DocSegmentKind.lineBreak, DocSegmentKind.beginTags, DocSegmentKind.inclusion]);
const tilesAndRanges = new Set([DocSegmentKind.paragraph, DocSegmentKind.lineBreak, DocSegmentKind.beginTags]);
const enum Workaround { checkpoint = "*" }
@ -95,9 +93,6 @@ const empty = Object.freeze({});
export const getCss = (segment: ISegment): Readonly<{ style?: string, classList?: string }> => segment.properties || empty;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
export const getComponentOptions = (segment: ISegment): IFluidHTMLOptions | undefined => (segment.properties && segment.properties.componentOptions) || empty;
type LeafAction = (position: number, segment: ISegment, startOffset: number, endOffset: number) => boolean;
/**
@ -153,9 +148,8 @@ export class FlowDocument extends LazyLoadedDataObject<ISharedDirectory, IFlowDo
private static readonly paragraphProperties = Object.freeze({ [reservedTileLabelsKey]: [DocSegmentKind.paragraph, DocTile.checkpoint], tag: TagName.p });
private static readonly lineBreakProperties = Object.freeze({ [reservedTileLabelsKey]: [DocSegmentKind.lineBreak, DocTile.checkpoint] });
private static readonly inclusionProperties = Object.freeze({ [reservedTileLabelsKey]: [DocSegmentKind.inclusion, DocTile.checkpoint] });
private static readonly tagsProperties = Object.freeze({
[reservedTileLabelsKey]: [DocSegmentKind.inclusion, DocTile.checkpoint],
[reservedTileLabelsKey]: [DocTile.checkpoint],
[reservedRangeLabelsKey]: [DocSegmentKind.beginTags],
});
@ -318,14 +312,6 @@ export class FlowDocument extends LazyLoadedDataObject<ISharedDirectory, IFlowDo
this.sharedString.insertMarker(position, ReferenceType.Tile, FlowDocument.lineBreakProperties);
}
// eslint-disable-next-line @typescript-eslint/ban-types
public insertComponent(position: number, handle: IFluidHandle, view: string, componentOptions: object, style?: string, classList?: string[]) {
this.sharedString.insertMarker(position, ReferenceType.Tile, Object.freeze({
...FlowDocument.inclusionProperties,
componentOptions, handle, style, classList: classList && classList.join(" "), view,
}));
}
public setFormat(position: number, tag: TagName) {
const { start } = this.findParagraph(position);

Просмотреть файл

@ -4,10 +4,9 @@
*/
import { FluidObject } from "@fluidframework/core-interfaces";
import { IFluidHTMLView } from "@fluidframework/view-interfaces";
import { paste } from "../clipboard/paste";
import { DocSegmentKind, FlowDocument, getDocSegmentKind } from "../document";
import { caretEnter, Direction, getDeltaX, getDeltaY, KeyCode } from "../util";
import { FlowDocument } from "../document";
import { Direction, getDeltaX, KeyCode } from "../util";
import { ownsNode } from "../util/event";
import { IFormatterState, RootFormatter } from "../view/formatter";
import { Layout } from "../view/layout";
@ -15,22 +14,14 @@ import { Caret } from "./caret";
import { debug } from "./debug";
import * as styles from "./index.css";
/**
* The Host provides the Editor with a registry of view factories which will be used to render components that have
* been inserted into the document.
*/
export interface IFluidHTMLViewFactory {
createView(model: FluidObject, scope?: FluidObject): IFluidHTMLView;
}
export class Editor {
private readonly layout: Layout;
private readonly caret: Caret;
private readonly caretSync: () => void;
private get doc() { return this.layout.doc; }
constructor(doc: FlowDocument, private readonly root: HTMLElement, formatter: Readonly<RootFormatter<IFormatterState>>, viewFactoryRegistry?: Map<string, IFluidHTMLViewFactory>, scope?: FluidObject) {
this.layout = new Layout(doc, root, formatter, viewFactoryRegistry, scope);
constructor(doc: FlowDocument, private readonly root: HTMLElement, formatter: Readonly<RootFormatter<IFormatterState>>, scope?: FluidObject) {
this.layout = new Layout(doc, root, formatter);
this.caret = new Caret(this.layout);
let scheduled = false;
@ -122,14 +113,6 @@ export class Editor {
break;
}
case KeyCode.arrowLeft:
this.enterIfInclusion(e, this.caret.position - 1, Direction.left);
break;
case KeyCode.arrowRight:
this.enterIfInclusion(e, this.caret.position, Direction.right);
break;
// Note: Chrome 69 delivers backspace on 'keydown' only (i.e., 'keypress' is not fired.)
case KeyCode.backspace: {
this.delete(e, Direction.left);
@ -190,17 +173,4 @@ export class Editor {
e.preventDefault();
e.stopPropagation();
}
private enterIfInclusion(e: Event, position: number, direction: Direction) {
const { segment } = this.doc.getSegmentAndOffset(position);
const kind = getDocSegmentKind(segment);
if (kind === DocSegmentKind.inclusion) {
const { node } = this.layout.segmentAndOffsetToNodeAndOffset(segment, 0);
const bounds = this.caret.bounds;
debug("Entering inclusion: (dx=%d,dy=%d,bounds=%o)", getDeltaX(direction), getDeltaY(direction), bounds);
if (caretEnter(node as Element, direction, bounds)) {
this.consume(e);
}
}
}
}

Просмотреть файл

@ -3,4 +3,4 @@
* Licensed under the MIT License.
*/
export { Editor, IFluidHTMLViewFactory } from "./editor";
export { Editor } from "./editor";

Просмотреть файл

@ -4,12 +4,9 @@
*/
import { assert } from "@fluidframework/common-utils";
import { FluidObject } from "@fluidframework/core-interfaces";
import { Marker, TextSegment } from "@fluidframework/merge-tree";
import { IFluidHTMLView } from "@fluidframework/view-interfaces";
import { DocSegmentKind, getComponentOptions, getCss, getDocSegmentKind } from "../document";
import * as styles from "../editor/index.css";
import { caretEnter, Direction, emptyObject, Rect, TagName } from "../util";
import { DocSegmentKind, getCss, getDocSegmentKind } from "../document";
import { emptyObject, TagName } from "../util";
import { getAttrs, syncAttrs } from "../util/attr";
import { Formatter, IFormatterState, RootFormatter } from "../view/formatter";
@ -42,11 +39,6 @@ class HtmlFormatter extends RootFormatter<IFormatterState> {
return { state, consumed: true };
}
case DocSegmentKind.inclusion: {
layout.pushFormat(inclusionFormatter, emptyObject);
return { state, consumed: false };
}
case DocSegmentKind.beginTags: {
layout.pushFormat(tagsFormatter, emptyObject);
return { state, consumed: true };
@ -67,59 +59,6 @@ class HtmlFormatter extends RootFormatter<IFormatterState> {
public onChange() { }
}
interface IInclusionState {
root?: HTMLElement;
slot?: HTMLElement;
view?: Promise<IFluidHTMLView>;
}
export class InclusionFormatter extends Formatter<IInclusionState> {
public begin(layout: Layout, init: Readonly<Partial<IInclusionState>>, prevState?: Readonly<IInclusionState>) {
const segment = layout.segment;
const state: IInclusionState = prevState || {};
if (!state.root) {
const marker = segment as Marker;
state.root = document.createElement(TagName.span);
state.root.contentEditable = "false";
state.slot = document.createElement(
getComponentOptions(segment).display === "block"
? TagName.div
: TagName.span);
const viewFactory = layout.viewFactoryRegistry.get(marker.properties.view);
state.view = layout.doc.getComponentFromMarker(marker).then((component: FluidObject) => {
if (viewFactory) {
// We found a view class registered for this marker's view type
const view = viewFactory.createView(component, layout.scope);
view.render(state.slot);
caretEnter(state.slot, Direction.right, Rect.empty);
state.slot.focus();
return view;
}
});
}
syncCss(state.root, getCss(segment), styles.inclusion);
layout.pushNode(state.root);
layout.emitNode(state.slot);
return state;
}
public visit(layout: Layout, state: Readonly<IInclusionState>) {
assert(getDocSegmentKind(layout.segment) === DocSegmentKind.inclusion, "Layout segment has unexpected kind!");
layout.popFormat();
return { state, consumed: true };
}
public end(layout: Layout) {
layout.popNode();
}
}
interface ITagsState extends IFormatterState { root?: HTMLElement; pTag: TagName; popCount: number; }
interface ITagsProps { tags?: TagName[]; }
@ -163,11 +102,6 @@ class TagsFormatter extends Formatter<ITagsState> {
return { state, consumed: true };
}
case DocSegmentKind.inclusion: {
layout.pushFormat(inclusionFormatter, emptyObject);
return { state, consumed: false };
}
case DocSegmentKind.beginTags: {
layout.pushFormat(tagsFormatter, emptyObject);
return { state, consumed: true };
@ -231,16 +165,6 @@ class ParagraphFormatter extends Formatter<IParagraphState> {
return { state, consumed: true };
}
case DocSegmentKind.inclusion: {
// If the inclusion is a block, it implicitly terminates the current paragraph.
if (getComponentOptions(segment).display === "block") {
layout.popFormat();
}
layout.pushFormat(inclusionFormatter, emptyObject);
return { state, consumed: false };
}
default:
debug("%s@%d: Unhanded DocSegmentKind '%s'.", this, layout.position, kind);
layout.popFormat();
@ -294,7 +218,6 @@ class TextFormatter extends Formatter<ITextState> {
}
export const htmlFormatter = Object.freeze(new HtmlFormatter());
const inclusionFormatter = Object.freeze(new InclusionFormatter());
const paragraphFormatter = Object.freeze(new ParagraphFormatter(TagName.p));
const tagsFormatter = Object.freeze(new TagsFormatter());
const textFormatter = Object.freeze(new TextFormatter());

Просмотреть файл

@ -6,7 +6,7 @@
import { ContainerViewRuntimeFactory } from "@fluid-example/example-utils";
export { FlowDocument } from "./document";
export { Editor, IFluidHTMLViewFactory } from "./editor";
export { Editor } from "./editor";
import { WebFlow, WebflowView } from "./host";
export { htmlFormatter } from "./html/formatters";

Просмотреть файл

@ -7,8 +7,8 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
require("jsdom-global")("", { url: "http://localhost" });
window.performance.mark = window.performance.mark || (() => { });
window.performance.measure = window.performance.measure || (() => { });
window.performance.mark ??= (() => undefined as PerformanceMark);
window.performance.measure ??= (() => undefined as PerformanceMeasure);
import { strict as assert } from "assert";
import { requestFluidObject } from "@fluidframework/runtime-utils";

Просмотреть файл

@ -5,11 +5,9 @@
import assert from "assert";
import { EventEmitter } from "events";
import { FluidObject } from "@fluidframework/core-interfaces";
import { ISegment, LocalReference, MergeTreeMaintenanceType } from "@fluidframework/merge-tree";
import { SequenceEvent } from "@fluidframework/sequence";
import { FlowDocument } from "../document";
import { IFluidHTMLViewFactory } from "../editor";
import { clamp, Dom, done, emptyObject, getSegmentRange, hasTagName, isTextNode, TagName } from "../util";
import { extractRef, updateRef } from "../util/localref";
import { debug } from "./debug";
@ -92,7 +90,7 @@ export class Layout extends EventEmitter {
private renderPromise = done;
private renderResolver: () => void;
constructor(public readonly doc: FlowDocument, public readonly root: Element, formatter: Readonly<RootFormatter<IFormatterState>>, public readonly viewFactoryRegistry: Map<string, IFluidHTMLViewFactory> = new Map(), public readonly scope?: FluidObject) {
constructor(public readonly doc: FlowDocument, public readonly root: Element, formatter: Readonly<RootFormatter<IFormatterState>>) {
super();
let scheduled = false;

Просмотреть файл

@ -81,13 +81,14 @@ module.exports = env => {
libraryTarget: "umd",
globalObject: 'self',
},
devServer: {
onBeforeSetupMiddleware: (devServer) => fluidRoute.before(devServer.app, devServer, env),
onAfterSetupMiddleware: (devServer) => fluidRoute.after(devServer.app, devServer, __dirname, env),
watchOptions: {
ignored: "**/node_modules/**",
}
},
// This impacts which files are watched by the dev server (and likely by webpack if watch is true).
// This should be configurable under devServer.static.watch
// (see https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md) but that does not seem to work.
// The CLI options for disabling watching don't seem to work either, so this may be a symptom of using webpack4 with the newer webpack-cli and webpack-dev-server.
watchOptions: {
ignored: "**/node_modules/**",
}
},
isProduction ? require("./webpack.prod") : require("./webpack.dev"));
isProduction ? require("./webpack.prod") : require("./webpack.dev"),
fluidRoute.devServerConfig(__dirname, env));
};

Просмотреть файл

@ -48,6 +48,7 @@
"@fluidframework/container-runtime-definitions": "^0.60.1000",
"@fluidframework/core-interfaces": "^0.43.1000",
"@fluidframework/driver-definitions": "^0.47.1000-0",
"@fluidframework/driver-utils": "^0.60.1000",
"@fluidframework/register-collection": "^0.60.1000",
"@fluidframework/request-handler": "^0.60.1000",
"@fluidframework/routerlicious-driver": "^0.60.1000",

Просмотреть файл

@ -5,32 +5,19 @@
import { IContainer, IFluidModuleWithDetails } from "@fluidframework/container-definitions";
import { Loader } from "@fluidframework/container-loader";
import { ensureFluidResolvedUrl } from "@fluidframework/driver-utils";
import { requestFluidObject } from "@fluidframework/runtime-utils";
import { createTinyliciousCreateNewRequest } from "@fluidframework/tinylicious-driver";
import React from "react";
import ReactDOM from "react-dom";
import { TinyliciousService } from "./tinyliciousService";
import { AppView } from "./appView";
import { containerKillBitId, InventoryListContainerRuntimeFactory } from "./containerCode";
import { IContainerKillBit } from "./containerKillBit";
import { extractStringData, fetchData, applyStringData, writeData } from "./dataHelpers";
import { IInventoryList } from "./inventoryList";
// In interacting with the service, we need to be explicit about whether we're creating a new document vs. loading
// an existing one. We also need to provide the unique ID for the document we are creating or loading from.
// In this app, we'll choose to create a new document when navigating directly to http://localhost:8080. For the ID,
// we'll choose to use the current timestamp. We'll also choose to interpret the URL hash as an existing document's
// ID to load from, so the URL for a document load will look something like http://localhost:8080/#1596520748752.
// These policy choices are arbitrary for demo purposes, and can be changed however you'd like.
let createNew = false;
if (location.hash.length === 0) {
createNew = true;
location.hash = Date.now().toString();
}
const documentId = location.hash.substring(1);
document.title = documentId;
import { TinyliciousService } from "./tinyliciousService";
async function getInventoryListFromContainer(container: IContainer): Promise<IInventoryList> {
// Since we're using a ContainerRuntimeFactoryWithDefaultDataStore, our inventory list is available at the URL "/".
@ -61,21 +48,42 @@ async function start(): Promise<void> {
let fetchedData: string | undefined;
let container: IContainer;
let containerId: string;
let inventoryList: IInventoryList;
let containerKillBit: IContainerKillBit;
if (createNew) {
// In interacting with the service, we need to be explicit about whether we're creating a new container vs.
// loading an existing one. If loading, we also need to provide the unique ID for the container we are
// loading from.
// In this app, we'll choose to create a new container when navigating directly to http://localhost:8080.
// A newly created container will generate its own ID, which we'll place in the URL hash.
// If navigating to http://localhost:8080#containerId, we'll load from the ID in the hash.
// These policy choices are arbitrary for demo purposes, and can be changed however you'd like.
if (location.hash.length === 0) {
fetchedData = await fetchData();
container = await loader.createDetachedContainer({ package: "no-dynamic-package", config: {} });
inventoryList = await getInventoryListFromContainer(container);
containerKillBit = await getContainerKillBitFromContainer(container);
await applyStringData(inventoryList, fetchedData);
await container.attach({ url: documentId });
await container.attach(createTinyliciousCreateNewRequest());
// Discover the container ID after attaching
const resolved = container.resolvedUrl;
ensureFluidResolvedUrl(resolved);
containerId = resolved.id;
// Update the URL with the actual container ID
location.hash = containerId;
} else {
container = await loader.resolve({ url: documentId });
containerId = location.hash.substring(1);
container = await loader.resolve({ url: containerId });
containerKillBit = await getContainerKillBitFromContainer(container);
inventoryList = await getInventoryListFromContainer(container);
}
// Put the container ID in the tab title
document.title = containerId;
const writeToExternalStorage = async () => {
// CONSIDER: it's perhaps more-correct to spawn a new client to extract with (to avoid local changes).

Просмотреть файл

@ -23,7 +23,7 @@ module.exports = env => {
]
},
output: {
filename: "[name].[contenthash].js",
filename: "[name].[hash].js",
},
plugins: [
new HtmlWebpackPlugin({

Просмотреть файл

@ -14,6 +14,13 @@ module.exports = env => {
devServer: {
port: 9000
},
// This impacts which files are watched by the dev server (and likely by webpack if watch is true).
// This should be configurable under devServer.static.watch
// (see https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md) but that does not seem to work.
// The CLI options for disabling watching don't seem to work either, so this may be a symptom of using webpack4 with the newer webpack-cli and webpack-dev-server.
watchOptions: {
ignored: "**/node_modules/**",
},
module: {
rules: [{
test: /\.s?css$/,
@ -30,7 +37,7 @@ module.exports = env => {
]
},
output: {
filename: "[name].[contenthash].js",
filename: "[name].[hash].js",
},
plugins: [
new HtmlWebpackPlugin({

Просмотреть файл

@ -59,7 +59,7 @@
"@fluidframework/local-driver": "^0.60.1000",
"@fluidframework/mocha-test-setup": "^0.60.1000",
"@fluidframework/sequence": "^0.60.1000",
"@fluidframework/server-local-server": "^0.1036.1000",
"@fluidframework/server-local-server": "^0.1036.2000-0",
"@fluidframework/test-runtime-utils": "^0.60.1000",
"@fluidframework/test-utils": "^0.60.1000",
"@microsoft/api-extractor": "^7.22.2",

Просмотреть файл

@ -34,7 +34,7 @@
"test:coverage": "jest --coverage --ci --reporters=default --reporters=jest-junit",
"test:jest": "jest",
"tsc": "tsc",
"webpack": "webpack --config webpack.prod.js --env production"
"webpack": "webpack --config webpack.prod.js --env production --color --no-stats"
},
"dependencies": {
"@hig/fonts": "^1.0.2",
@ -104,8 +104,6 @@
"webpack": "^4.46.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "4.0.0",
"webpack-karma-die-hard": "^1.0.4",
"webpack-license-plugin": "^4.2.2",
"webpack-merge": "^5.8.0"
},
"peerDependencies": {

Просмотреть файл

@ -67,7 +67,6 @@ interface IEditReferencePathProps {
}
export const EditReferencePath: React.FunctionComponent<IEditReferencePathProps
// eslint-disable-next-line react/prop-types
& React.HTMLAttributes<HTMLDivElement>> = ({onCancel, onEdit, name, path, className, ...restProps}) => {
const classes = useStyles();
const [newPath, setNewPath] = React.useState(path);

Просмотреть файл

@ -54,7 +54,7 @@ export async function ErrorPopup<
} else {
return Promise.resolve(result);
}
} catch (err) {
} catch (err: any) {
processError(err);
if (catchErr) {
return Promise.resolve();

Просмотреть файл

@ -240,7 +240,7 @@ export class HashCalculator {
// Compute the hash for the remaining bytes in the buffer
const buffer = this.hashBuffer.Uint8HashArray;
/* eslint-disable no-fallthrough, no-multi-spaces */ // Allows cases to fallthrough without complaints.
/* eslint-disable no-multi-spaces */
switch (this.hashBuffer.pos) {
case 15: k4 = (k4 ^ (buffer[14] << 16)) >>> 0;
case 14: k4 = (k4 ^ (buffer[13] << 8)) >>> 0;
@ -284,7 +284,7 @@ export class HashCalculator {
default:
throw new Error("_finalizeHash: We should never get into the default case.");
}
/* eslint-enable no-fallthrough, no-multi-spaces */
/* eslint-enable no-multi-spaces */
// Perform the finalization
const len = this.length + this.hashBuffer.pos;

Просмотреть файл

@ -349,7 +349,6 @@ class InspectorTable extends React.Component<WithStyles<typeof styles> & IInspec
// We need to update the table rows directly, because they might be used in the search call below.
// Treating table rows as a mutable state property is fine, since it is purely derived from props anyway, and
// we also update it directly in other places already.
// eslint-disable-next-line react/no-direct-mutation-state
this.state.tableRows = updatedTableRows;
// We still need to add it to the new state to trigger a re-render of the table.
newState.tableRows = updatedTableRows;
@ -646,7 +645,7 @@ class InspectorTable extends React.Component<WithStyles<typeof styles> & IInspec
parentProp.setValues({[rowData.name]: newPath});
try {
(parentProp as unknown as ReferenceMapProperty).isReferenceValid(rowData.name);
} catch (e) {
} catch (e: any) {
// if maximum call stack size is exceeded, user probably created cyclic reference
// we can't delete cyclic references so we need set reference path to some other value
if (e.message.includes("Maximum call stack size exceeded")) {
@ -660,7 +659,7 @@ class InspectorTable extends React.Component<WithStyles<typeof styles> & IInspec
unresolvedProperty.setValue(newPath);
try {
unresolvedProperty.isReferenceValid();
} catch (e) {
} catch (e: any) {
if (e.message.includes("Maximum call stack size exceeded")) {
unresolvedProperty.setValue("Could not resolve the reference");
}

Просмотреть файл

@ -2244,7 +2244,7 @@ declare module "@fluid-experimental/property-properties" {
/**
* Returns the full property type identifier for the ChangeSet including the enum type id
* @param in_hideCollection if true the collection type (if applicable) will be omitted
* since that is not aplicable here, this param is ignored. Default to false
* since that is not applicable here, this param is ignored. Default to false
* @return The typeid
*/
getFullTypeid(in_hideCollection?: boolean): string;
@ -2267,7 +2267,7 @@ declare module "@fluid-experimental/property-properties" {
*/
getParent(): BaseProperty | undefined;
/**
* checks whether the property is dynamic (only properties inherting from NodeProperty are)
* checks whether the property is dynamic (only properties inheriting from NodeProperty are)
*/
isDynamic(): boolean;
/**
@ -2547,7 +2547,7 @@ declare module "@fluid-experimental/property-properties" {
/**
* Returns the full property type identifier for the ChangeSet including the enum type id
* @param in_hideCollection - if true the collection type (if applicable) will be omitted
* since that is not aplicable here, this param is ignored. Default to false
* since that is not applicable here, this param is ignored. Default to false
* @return The typeid
*/
getFullTypeid(in_hideCollection: boolean): string;

Просмотреть файл

@ -136,7 +136,7 @@ export class AbstractStaticCollectionProperty extends BaseProperty {
/**
* returns the value of a sub-property
* This is a shortcut for .get(in_ids, in_options).getValue()
* @param {string|number|array<string|number>} in_ids the ID or IDs of the property or an array of IDs
* @param {string|number|Array<string|number>} in_ids the ID or IDs of the property or an array of IDs
* if an array is passed, the .get function will be performed on each id in sequence
* for example .getValue(['position','x']) is equivalent to .get('position').get('x').getValue().
* If at any point .get resolves to a ReferenceProperty, it will, by default, return the property that the
@ -326,8 +326,8 @@ export class AbstractStaticCollectionProperty extends BaseProperty {
* found in that object.
* @see {setValues}
* @param {object} in_values The object containing the nested values to assign
* @param {Bool} in_typed Whether the values are typed/polymorphic.
* @param {Bool} in_initial - Whether we are setting default/initial values
* @param {boolean} in_typed Whether the values are typed/polymorphic.
* @param {boolean} in_initial - Whether we are setting default/initial values
or if the function is called directly with the values to set.
*/
_setValues(in_values, in_typed, in_initial) {

Просмотреть файл

@ -147,7 +147,7 @@ export class Integer64Property extends ValueProperty {
/**
* @param {number} in_high set the higher 32 bit integer part
* @throws if in_high is not a number
* @return {boolen} true if the value was actually changed
* @return {boolean} true if the value was actually changed
*/
setValueHigh(in_high) {
ConsoleUtils.assert(_.isNumber(in_high), MSG.IN_HIGH_MUST_BE_NUMBER + in_high);
@ -164,7 +164,7 @@ export class Integer64Property extends ValueProperty {
/**
* @param {number} in_low set the lower 32 bit integer part
* @throws if in_low is not a number
* @return {boolen} true if the value was actually changed
* @return {boolean} true if the value was actually changed
*/
setValueLow(in_low) {
ConsoleUtils.assert(_.isNumber(in_low), MSG.IN_LOW_MUST_BE_NUMBER + in_low);

Просмотреть файл

@ -8,7 +8,7 @@ export class PropertyUtils {
* Gather all properties that pass an arbitrary predicate function
* @param {property-properties.NodeProperty} in_rootProperty The root property to traverse from
* @param {function} in_predicate The predicate function
* @return {Array.<property-properties.BasePropertyy>} The list of properties that passed the predicate
* @return {Array.<property-properties.BaseProperty>} The list of properties that passed the predicate
* function
*/
static gatherProperties = function(in_rootProperty, in_predicate) {

Просмотреть файл

@ -7,8 +7,6 @@
"strict": false,
"allowJs": true,
"checkJs": false,
"declarationDir": "./dist",
"rootDir": "./src",
"outDir": "./dist",
"composite": true

Просмотреть файл

@ -14,5 +14,5 @@ module.exports = {
tsconfig: 'src/test/tsconfig.json'
}
},
testPathIgnorePatterns: ['/node_modules/']
testPathIgnorePatterns: ['/node_modules/', 'dist'],
};

Просмотреть файл

@ -68,7 +68,7 @@ const setTrapSpecialCases = getTrapSpecialCases.concat(["fill", "sort"]);
* must be used in conjunction with this class.
* @hidden
*/
export const arrayProxyHandler = {
export const arrayProxyHandler: ProxyHandler<ComponentArray> = {
/**
* The get trap that handles access to properties and functions.
* @param target The {@link ComponentArray} the Proxy handles.
@ -150,14 +150,28 @@ export const arrayProxyHandler = {
/**
* The trap for the in operator.
* Forwards the query to the has() method of the {@link external:ArrayProperty ArrayProperty}.
* @param target The {@link ComponentArray} the Proxy handles.
* @param key The name of the property/function that is to be accessed.
* @return if the key is part of the {@link external:ArrayProperty ArrayProperty}, otherwise false.
*/
has: (target: ComponentArray, key: string | number | typeof proxySymbol) =>
key === "swap" || key in [] || key === proxySymbol ||
(key >= 0 && key < target.getProperty().getLength()),
has: (target: ComponentArray, key: string | symbol) => {
switch (typeof key) {
case "string":
{
if (key === "swap" || key in []) {
return true;
}
// Act like an array.
// This means that "0" can be present, but not "0.0", "0.1", "-0", " 0" etc.
// Simplest way to check for this is to round trip:
const numeric = Number(key);
return String(Number(key)) === key &&
Number.isInteger(numeric) && numeric >= 0 && numeric < target.getProperty().getLength();
}
default:
return key === proxySymbol;
}
},
/**
* Trap for the Object.keys().
@ -168,70 +182,78 @@ export const arrayProxyHandler = {
ownKeys: (target: ComponentArray) => Reflect.ownKeys(Array.from(target.getProperty().getIds())),
/**
* The set trap handles setting of properties. If key is a number >= 0 it sets the
* property at that index in the {@link external:ArrayProperty ArrayProperty}.
* The set trap handles setting of properties.
*
* If key is a numeric string it sets the property at that index in the
* {@link external:ArrayProperty ArrayProperty}.
* In this case the key must be a normalized positive integer.
*
* If the key is 'length' it sets a new length for the {@link external:ArrayProperty ArrayProperty}.
*
* Otherwise, it just sets it on the associated {@link ComponentArray}.
*
* @param target The {@link ComponentArray} the Proxy handles.
* @param key The name of the property/function that is to be accessed.
* @param value The value to be set.
* @return Returns a boolean.
*/
set(target: ComponentArray, key: string | number, value: any) {
set(target: ComponentArray, key: string | symbol, value: any) {
// process key for cases like "*1" 1 "string_key", "*string_key"
let processed_key = key;
let processed_key: string | symbol = key;
const asteriskFound = Utilities.containsAsterisk(processed_key);
if (asteriskFound) {
// if we found an * we can safely treat it as string
processed_key = (processed_key as string).slice(0, -1);
}
// convert strings of numbers to numbers
processed_key = isNaN(Number(processed_key)) ? processed_key : Number(processed_key);
// handle special cases for strigns and numbers
if (typeof processed_key === "string") {
const numeric_key = Number(processed_key);
// handle special cases for strings and numbers
if (isNaN(numeric_key)) {
if (processed_key === "length") {
return setLength(target, Number(value));
}
// If we land here there was no special case: simply assign.
target[processed_key] = value;
return true;
} else {
if (!isNaN(processed_key) && processed_key >= 0) {
const property = target.getProperty();
const isReferenceArray = PropertyFactory.instanceOf(property, "Reference", "array");
let insert = false;
if (processed_key >= property.getLength()) {
setLength(target, processed_key + 1);
// Trying to set something that was currently not in the array,
// means a new reference path is inserted
insert = true;
}
const specialCases = setTrapSpecialCases.includes(target.lastCalledMethod);
if (isReferenceArray && forceType<ReferenceArrayProperty>(property)
&& !specialCases && !asteriskFound && !insert) {
Utilities.setValueOfReferencedProperty(property, processed_key, value);
} else {
if (asteriskFound && !isReferenceArray) {
throw new Error(PropertyProxyErrors.NON_REFERENCE_ASSIGN);
}
if (property.isPrimitiveType() || property.get(processed_key)!.getContext() === "single") {
Utilities.throwOnIterableForSingleProperty(value);
property.set(processed_key,
Utilities.prepareElementForInsertion(property, value, target.lastCalledMethod));
} else {
const child = property.get(processed_key);
if (child) {
Utilities.assign(child, value);
} else {
throw new Error(PropertyProxyErrors.INVALID_PROPERTY);
}
}
}
return true;
// Validate numeric property: must be a normalized numeric string for an non-negative integer.
if (numeric_key < 0 || !Number.isInteger(numeric_key) || String(numeric_key) !== processed_key) {
throw new Error(PropertyProxyErrors.INVALID_PROPERTY);
}
const property = target.getProperty();
const isReferenceArray = PropertyFactory.instanceOf(property, "Reference", "array");
let insert = false;
if (numeric_key >= property.getLength()) {
setLength(target, numeric_key + 1);
// Trying to set something that was currently not in the array,
// means a new reference path is inserted
insert = true;
}
const specialCases = setTrapSpecialCases.includes(target.lastCalledMethod);
if (isReferenceArray && forceType<ReferenceArrayProperty>(property)
&& !specialCases && !asteriskFound && !insert) {
Utilities.setValueOfReferencedProperty(property, numeric_key, value);
} else {
if (asteriskFound && !isReferenceArray) {
throw new Error(PropertyProxyErrors.NON_REFERENCE_ASSIGN);
}
if (property.isPrimitiveType() || property.get(numeric_key)!.getContext() === "single") {
Utilities.throwOnIterableForSingleProperty(value);
property.set(numeric_key,
Utilities.prepareElementForInsertion(property, value, target.lastCalledMethod));
} else {
const child = property.get(numeric_key);
if (child) {
Utilities.assign(child, value);
} else {
throw new Error(PropertyProxyErrors.INVALID_PROPERTY);
}
}
}
return true;
}
// if we land here there was no special case
// simply assign simply assign
target[processed_key] = value;
return true;
},
};

Просмотреть файл

@ -74,7 +74,7 @@
"@fluidframework/local-driver": "^0.60.1000",
"@fluidframework/mocha-test-setup": "^0.60.1000",
"@fluidframework/runtime-utils": "^0.60.1000",
"@fluidframework/server-local-server": "^0.1036.1000",
"@fluidframework/server-local-server": "^0.1036.2000-0",
"@fluidframework/test-utils": "^0.60.1000",
"@rushstack/eslint-config": "^2.5.1",
"aws-sdk": "2.547.0",

Просмотреть файл

@ -15,7 +15,7 @@
"module": "lib/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "concurrently npm:build:compile npm:lint",
"build": "concurrently npm:build:compile npm:lint && npm run build:docs",
"build:compile": "concurrently npm:tsc npm:build:esnext",
"build:docs": "api-extractor run --local --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/doc-models/* ../../../_api-extractor-temp/",
"build:esnext": "tsc --project ./tsconfig.esnext.json",

Просмотреть файл

@ -323,7 +323,7 @@ export abstract class Checkout extends EventEmitterWithErrorHandling<ICheckoutEv
public revert(editId: EditId): void {
assert(this.currentEdit !== undefined);
const index = this.tree.edits.getIndexOfId(editId);
const edit = this.tree.editsInternal.getEditInSessionAtIndex(index);
const edit = this.tree.edits.getEditInSessionAtIndex(index);
const before = this.tree.logViewer.getRevisionViewInSession(index);
const changes = this.tree.revertChanges(edit.changes, before);
if (changes !== undefined) {

Просмотреть файл

@ -66,6 +66,7 @@ import {
ghostSessionId,
WriteFormat,
TreeNodeSequence,
InternalizedChange,
} from './persisted-types';
import { serialize, SummaryContents } from './Summary';
import {
@ -558,16 +559,8 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
* @returns the edit history of the tree.
* @public
*/
public get edits(): OrderedEditSet {
return this.editLog;
}
/**
* @returns the edit history of the tree. The format of the contents of edits are subject to change and should not be relied upon.
* @internal
*/
public get editsInternal(): OrderedEditSet<ChangeInternal> {
return this.editLog;
public get edits(): OrderedEditSet<InternalizedChange> {
return this.editLog as unknown as OrderedEditSet<InternalizedChange>;
}
private deserializeHandle(serializedHandle: string): IFluidHandle<ArrayBufferLike> {
@ -1133,7 +1126,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
const unifyHistoricalIds = (context: NodeIdContext): void => {
for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
const edit = this.editsInternal.getEditInSessionAtIndex(i);
const edit = this.editLog.getEditInSessionAtIndex(i);
convertEditIds(edit, (id) => context.generateNodeId(this.convertToStableNodeId(id)));
}
};
@ -1146,7 +1139,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
unifyHistoricalIds(ghostContext);
// The same logic applies to string interning, so intern all the strings in the history (superset of those in the current view)
for (let i = 0; i < this.editLog.numberOfSequencedEdits; i++) {
this.internStringsFromEdit(this.editsInternal.getEditInSessionAtIndex(i));
this.internStringsFromEdit(this.editLog.getEditInSessionAtIndex(i));
}
} else {
// Clients do not have the full history, but all share the same current view (sequenced). They can all finalize the same final
@ -1174,9 +1167,9 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
* should be used instead.
* @public
*/
public applyEdit(...changes: Change[]): Edit<unknown>;
public applyEdit(changes: Change[]): Edit<unknown>;
public applyEdit(headOrChanges: Change | Change[], ...tail: Change[]): Edit<unknown> {
public applyEdit(...changes: Change[]): Edit<InternalizedChange>;
public applyEdit(changes: Change[]): Edit<InternalizedChange>;
public applyEdit(headOrChanges: Change | Change[], ...tail: Change[]): Edit<InternalizedChange> {
const changes = Array.isArray(headOrChanges) ? headOrChanges : [headOrChanges, ...tail];
const id = newEditId();
const internalEdit: Edit<ChangeInternal> = {
@ -1185,7 +1178,34 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
};
this.submitEditOp(internalEdit);
this.applyEditLocally(internalEdit, undefined);
return internalEdit;
return internalEdit as unknown as Edit<InternalizedChange>;
}
/**
* Merges `edits` from `other` into this SharedTree.
* @param other - Tree containing the edits that should be applied to this one.
* @param edits - Iterable of edits from `other` to apply.
* @param stableIdRemapper - Optional remapper to translate stable identities from `other` into stable identities on this tree.
* Any references that `other` contains to a stable id `foo` will be replaced with references to the id `stableIdRemapper(foo)`.
*
* Payloads on the edits are left intact.
* @returns a list containing `EditId`s for all applied edits.
*/
public mergeEditsFrom(
other: SharedTree,
edits: Iterable<Edit<InternalizedChange>>,
stableIdRemapper?: (id: StableNodeId) => StableNodeId
): EditId[] {
const idConverter = (id: NodeId) => {
const stableId = other.convertToStableNodeId(id);
const convertedStableId = stableIdRemapper?.(stableId) ?? stableId;
return this.generateNodeId(convertedStableId);
};
return Array.from(
edits as unknown as Iterable<Edit<ChangeInternal>>,
(edit) => this.applyEditInternal(convertEditIds(edit, (id) => idConverter(id))).id
);
}
/**
@ -1305,7 +1325,7 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
*/
public revert(editId: EditId): EditId | undefined {
const index = this.edits.getIndexOfId(editId);
const edit = this.editLog.getEditInSessionAtIndex(index);
const edit = this.edits.getEditInSessionAtIndex(index);
const before = this.logViewer.getRevisionViewInSession(index);
const changes = this.revertChanges(edit.changes, before);
if (changes === undefined) {
@ -1322,8 +1342,8 @@ export class SharedTree extends SharedObject<ISharedTreeEvents> implements NodeI
* @returns the inverse of `changes` or undefined if the changes could not be inverted for the given tree state.
* @internal
*/
public revertChanges(changes: readonly ChangeInternal[], before: RevisionView): ChangeInternal[] | undefined {
return revert(changes, before);
public revertChanges(changes: readonly InternalizedChange[], before: RevisionView): ChangeInternal[] | undefined {
return revert(changes as unknown as readonly ChangeInternal[], before);
}
/**

Просмотреть файл

@ -29,7 +29,7 @@ export interface UploadedEditChunkContents {
*/
export async function getUploadedEditChunkContents(sharedTree: SharedTree): Promise<UploadedEditChunkContents[]> {
const editChunks: UploadedEditChunkContents[] = [];
const { editChunks: editsOrHandles } = (sharedTree.edits as EditLog<ChangeInternal>).getEditLogSummary();
const { editChunks: editsOrHandles } = (sharedTree.edits as unknown as EditLog<ChangeInternal>).getEditLogSummary();
for (const { chunk } of editsOrHandles) {
if (!Array.isArray(chunk)) {
const handle = chunk as FluidEditHandle;

Просмотреть файл

@ -111,8 +111,8 @@ export class Transaction extends TypedEventEmitter<TransactionEvents> {
if (this.transaction.changes.length > 0) {
const result = this.transaction.close();
const edit: Edit<ChangeInternal> = { id: newEditId(), changes: result.changes };
if (this.tree.editsInternal instanceof CachingLogViewer) {
this.tree.editsInternal.setKnownEditingResult(edit, result);
if (this.tree.edits instanceof CachingLogViewer) {
this.tree.edits.setKnownEditingResult(edit, result);
}
this.tree.applyEditInternal(edit);
}

Просмотреть файл

@ -56,6 +56,7 @@ export {
ConstraintEffect,
Edit,
ChangeInternal,
InternalizedChange,
ChangeNode,
ChangeNode_0_0_2,
EditLogSummary,

Просмотреть файл

@ -204,6 +204,17 @@ export interface CompressedBuildInternal<TId extends OpSpaceNodeId> {
*/
export type CompressedBuildNode<TId extends OpSpaceNodeId> = CompressedPlaceholderTree<TId, DetachedSequenceId>;
// TODO: `ChangeInternal`s should be assignable to this type without casting; this will require some test refactoring.
/**
* This type should be used as an opaque handle in the public API for `ChangeInternal` objects.
* This is useful for supporting public APIs which involve working with a tree's edit history,
* which will involve changes that have already been internalized.
* @public
*/
export interface InternalizedChange {
InternalChangeBrand: '2cae1045-61cf-4ef7-a6a3-8ad920cb7ab3';
}
/**
* {@inheritdoc (Change:type)}
* @public

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License.
*/
import { benchmark, BenchmarkType } from '@fluid-tools/benchmark';
import { benchmark, BenchmarkType, isInPerformanceTestingMode } from '@fluid-tools/benchmark';
import { v4 } from 'uuid';
import { assert } from '../Common';
@ -17,8 +17,10 @@ import { refreshTestTree } from './utilities/TestUtilities';
describe('Forest Perf', () => {
const testTree = refreshTestTree();
// Larger sizes can slow down correctness test runs, or even time out, so only run smaller sizes as correctness tests.
const sizes = isInPerformanceTestingMode ? [100, 1_000, 10_000, 100_000] : [100, 1_000];
for (const count of [100, 1_000, 10_000, 100_000]) {
for (const count of sizes) {
// Pick a single representative size for the 'Measurement' suite to keep it small.
const type = count === 10_000 ? BenchmarkType.Measurement : BenchmarkType.Perspective;
@ -71,7 +73,7 @@ describe('Forest Perf', () => {
});
let otherForest: Forest | undefined;
for (const otherCount of [100, 1_000, 10_000, 100_000]) {
for (const otherCount of sizes) {
benchmark({
type,
title: `invoke delta on Forest with ${count} nodes against Forest with ${otherCount} nodes`,

Просмотреть файл

@ -1325,7 +1325,7 @@ function createNetworkTestFunction(validateAfter: boolean): NetworkTestFunction
network.deliverOperations(DestinationClient.All);
network.assertNetworkState();
}
});
}).timeout(10000);
};
}

Просмотреть файл

@ -30,6 +30,7 @@ import { sequencedIdNormalizer } from '../NodeIdUtilities';
import { expectDefined } from './utilities/TestCommon';
import { TestFluidSerializer } from './utilities/TestSerializer';
import {
getEditLogInternal,
getIdNormalizerFromSharedTree,
makeNodeIdContext,
setUpLocalServerTestSharedTree,
@ -421,12 +422,12 @@ async function expectSharedTreesEqual(
const roundTrip = <T>(obj: T): T => JSON.parse(JSON.stringify(obj)) as T;
const editA = roundTrip(
convertEditIds(await sharedTreeA.editsInternal.getEditAtIndex(i), (id) =>
convertEditIds(await getEditLogInternal(sharedTreeA).getEditAtIndex(i), (id) =>
sharedTreeA.convertToStableNodeId(id)
)
);
const editB = roundTrip(
convertEditIds(await sharedTreeB.editsInternal.getEditAtIndex(i), (id) =>
convertEditIds(await getEditLogInternal(sharedTreeB).getEditAtIndex(i), (id) =>
sharedTreeB.convertToStableNodeId(id)
)
);

Просмотреть файл

@ -109,8 +109,8 @@ export async function performFuzzActions(
const first = trees[0].tree;
for (let i = 1; i < trees.length; i++) {
const tree = trees[i].tree;
const editLogA = first.editsInternal as EditLog<ChangeInternal>;
const editLogB = tree.editsInternal as EditLog<ChangeInternal>;
const editLogA = first.edits;
const editLogB = tree.edits;
const minEdits = Math.min(editLogA.length, editLogB.length);
for (let j = 0; j < minEdits - 1; j++) {
const editA = await editLogA.getEditAtIndex(editLogA.length - j - 1);

Просмотреть файл

@ -13,6 +13,7 @@ import type { EditLog } from '../../EditLog';
import { SharedTree } from '../../SharedTree';
import { Change, StablePlace } from '../../ChangeTypes';
import {
getEditLogInternal,
LocalServerSharedTreeTestingComponents,
LocalServerSharedTreeTestingOptions,
setUpTestTree,
@ -67,9 +68,7 @@ export function runPendingLocalStateTests(
testObjectProvider,
container,
() =>
tree.applyEdit(
...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left))
) as Edit<ChangeInternal>
tree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)))
);
await testObjectProvider.ensureSynchronized();
const leftTraitAfterOfflineClose = tree2.currentView.getTrait(
@ -82,7 +81,7 @@ export function runPendingLocalStateTests(
const container3 = await loader.resolve({ url }, pendingLocalState);
const dataObject3 = await requestFluidObject<ITestFluidObject>(container3, '/');
const tree3 = await dataObject3.getSharedObject<SharedTree>(documentId);
expect((tree3.editsInternal as EditLog<ChangeInternal>).isLocalEdit(edit.id)).to.be.true; // Kludge
expect((tree3.edits as unknown as EditLog<ChangeInternal>).isLocalEdit(edit.id)).to.be.true; // Kludge
await testObjectProvider.ensureSynchronized();
@ -99,12 +98,12 @@ export function runPendingLocalStateTests(
'Tree collaborating with a client that applies stashed pending edits should see them.'
);
const stableEdit = stabilizeEdit(tree, edit);
const stableEdit = stabilizeEdit(tree, edit as unknown as Edit<ChangeInternal>);
expect(
stabilizeEdit(tree2, (await tree2.editsInternal.tryGetEdit(edit.id)) ?? fail())
stabilizeEdit(tree2, (await getEditLogInternal(tree2).tryGetEdit(edit.id)) ?? fail())
).to.deep.equal(stableEdit);
expect(
stabilizeEdit(tree3, (await tree3.editsInternal.tryGetEdit(edit.id)) ?? fail())
stabilizeEdit(tree3, (await getEditLogInternal(tree3).tryGetEdit(edit.id)) ?? fail())
).to.deep.equal(stableEdit);
expect(tree2.edits.length).to.equal(initialEditLogLength + 1);
expect(tree3.edits.length).to.equal(initialEditLogLength + 1);

Просмотреть файл

@ -14,7 +14,7 @@ import {
MockFluidDataStoreRuntime,
} from '@fluidframework/test-runtime-utils';
import { assertArrayOfOne, assertNotUndefined, fail, isSharedTreeEvent } from '../../Common';
import { EditId, OpSpaceNodeId, TraitLabel } from '../../Identifiers';
import { EditId, NodeId, OpSpaceNodeId, TraitLabel } from '../../Identifiers';
import { CachingLogViewer } from '../../LogViewer';
import { EditLog, OrderedEditSet } from '../../EditLog';
import { initialTree } from '../../InitialTree';
@ -69,6 +69,7 @@ import {
applyNoop,
getIdNormalizerFromSharedTree,
waitForSummary,
getEditLogInternal,
} from './TestUtilities';
function revertEditInTree(tree: SharedTree, edit: EditId): EditId | undefined {
@ -1294,7 +1295,7 @@ export function runSharedTreeOperationsTests(
});
// Verify we loaded a no-history summary.
expect(tree3.editsInternal.length).to.equal(1);
expect(tree3.edits.length).to.equal(1);
let unexpectedHistoryChunkCount = 0;
tree3.on(SharedTreeDiagnosticEvent.UnexpectedHistoryChunk, () => unexpectedHistoryChunkCount++);
@ -1361,10 +1362,12 @@ export function runSharedTreeOperationsTests(
containerRuntimeFactory.processAllMessages();
const { internedStrings } = tree.saveSummary() as SharedTreeSummary;
const insertEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(1));
const moveEdit = normalizeEdit(tree, tree.editsInternal.getEditInSessionAtIndex(2));
const insertEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(1));
const moveEdit2 = normalizeEdit(secondTree, secondTree.editsInternal.getEditInSessionAtIndex(2));
const log = getEditLogInternal(tree);
const log2 = getEditLogInternal(secondTree);
const insertEdit = normalizeEdit(tree, log.getEditInSessionAtIndex(1));
const moveEdit = normalizeEdit(tree, log.getEditInSessionAtIndex(2));
const insertEdit2 = normalizeEdit(secondTree, log2.getEditInSessionAtIndex(1));
const moveEdit2 = normalizeEdit(secondTree, log2.getEditInSessionAtIndex(2));
expect(insertEdit).to.deep.equal(insertEdit2);
expect(moveEdit).to.deep.equal(moveEdit2);
expect(tree.equals(secondTree)).to.be.true;
@ -1413,7 +1416,7 @@ export function runSharedTreeOperationsTests(
const uncompressedEdits: EditWithoutId<ChangeInternal>[] = [
{
changes: tree.edits.getEditInSessionAtIndex(0).changes as ChangeInternal[],
changes: getEditLogInternal(tree).getEditInSessionAtIndex(0).changes,
},
];
@ -1466,5 +1469,67 @@ export function runSharedTreeOperationsTests(
});
});
}
describe('mergeEditsFrom', () => {
const getTestTreeRootHandle = (tree: SharedTree, testTree: TestTree): TreeNodeHandle => {
const view = tree.currentView;
const handle = new TreeNodeHandle(view, view.root);
return handle.traits[testTree.traitLabel][0];
};
it('can be used with simple edits', () => {
const { sharedTree, testTree } = createSimpleTestTree();
const { sharedTree: sharedTree2, testTree: testTree2 } = createSimpleTestTree();
sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
sharedTree.applyEdit(
Change.delete(StableRange.all({ parent: testTree.identifier, label: testTree.right.traitLabel }))
);
const preEditRootHandle = getTestTreeRootHandle(sharedTree2, testTree2);
const edits = [0, 1, 2].map((i) => sharedTree.edits.getEditInSessionAtIndex(i));
// Since the TestTree setup edit is a `setTrait`, this should wipe `testTree2` state.
sharedTree2.mergeEditsFrom(sharedTree, edits);
expect(sharedTree2.edits.length).to.equal(4);
const rootHandle = getTestTreeRootHandle(sharedTree2, testTree2);
expect(preEditRootHandle.identifier).to.not.equal(rootHandle.identifier);
expect(rootHandle.traits[testTree2.left.traitLabel].length).to.equal(2);
});
it('can be used with a translation map', () => {
const { sharedTree, testTree } = createSimpleTestTree();
const { sharedTree: sharedTree2, testTree: testTree2 } = createSimpleTestTree();
// For each of the identities in the simple test tree...
const nodeIdGetters: ((tree: TestTree) => NodeId)[] = [
(tree) => tree.identifier,
(tree) => tree.left.identifier,
(tree) => tree.right.identifier,
];
// Make a map translating that identifier from `testTree` to `testTree2`
const translationMap = new Map(
nodeIdGetters.map((getter) => [
sharedTree.convertToStableNodeId(getter(testTree)),
sharedTree2.convertToStableNodeId(getter(testTree2)),
])
);
sharedTree.applyEdit(...Change.insertTree(testTree.buildLeaf(), StablePlace.after(testTree.left)));
sharedTree.applyEdit(
Change.delete(StableRange.all({ parent: testTree.identifier, label: testTree.right.traitLabel }))
);
const edits = [1, 2].map((i) => sharedTree.edits.getEditInSessionAtIndex(i));
sharedTree2.mergeEditsFrom(sharedTree, edits, (id) => translationMap.get(id) ?? id);
const root = getTestTreeRootHandle(sharedTree, testTree);
const root2 = getTestTreeRootHandle(sharedTree2, testTree2);
const leftTrait = root.traits[testTree.left.traitLabel];
const leftTrait2 = root2.traits[testTree2.left.traitLabel];
// Inserted leaves should be equivalent.
expect(leftTrait2.length).to.equal(2);
expect(leftTrait2[1]).to.deep.equal(leftTrait[1]);
// Right subtree should have been deleted.
expect(Object.entries(root2.traits).length).to.equal(1);
expect(root2.traits[testTree2.right.traitLabel]).to.equal(undefined);
});
});
});
}

Просмотреть файл

@ -507,7 +507,7 @@ export function runSharedTreeVersioningTests(
version: newVersion,
};
containerRuntimeFactory.pushMessage({ contents: op });
(tree.editsInternal as EditLog).getLocalEdits = () => {
(tree.edits as EditLog).getLocalEdits = () => {
throw new Error('Simulated issue in update');
};
const matchesFailedVersionUpdate = (event: ITelemetryBaseEvent) =>

Просмотреть файл

@ -142,7 +142,7 @@ export function runSummarySizeTests(
if (revertEdits) {
for (let i = changes.length - 1; i >= 0; i--) {
const editIndex = tree.edits.getIndexOfId(edits[i].id);
const edit = tree.edits.getEditInSessionAtIndex(editIndex) as Edit<ChangeInternal>;
const edit = tree.edits.getEditInSessionAtIndex(editIndex) as unknown as Edit<ChangeInternal>;
const reverted = revert(edit.changes, tree.logViewer.getRevisionViewInSession(editIndex));
if (reverted !== undefined) {
tree.applyEditInternal(reverted);

Просмотреть файл

@ -53,6 +53,7 @@ import { newEdit, setTrait } from '../../EditUtilities';
import { SharedTree } from '../../SharedTree';
import { BuildNode, Change, StablePlace } from '../../ChangeTypes';
import { convertEditIds } from '../../IdConversion';
import { OrderedEditSet } from '../../EditLog';
import { buildLeaf, RefreshingTestTree, SimpleTestTree, TestTree } from './TestNode';
/** Objects returned by setUpTestSharedTree */
@ -283,8 +284,13 @@ export async function setUpLocalServerTestSharedTree(
TestDataStoreType,
new TestFluidObjectFactory(registry),
{
summaryOptions: { initialSummarizerDelayMs: 0 },
enableOfflineLoad: true,
summaryOptions: {
summaryConfigOverrides: {
idleTime: 1000, // Current default idleTime is 15000 which will cause some SharedTree tests to timeout.
},
initialSummarizerDelayMs: 0,
},
},
[innerRequestHandler]
);
@ -586,6 +592,10 @@ export function stabilizeEdit(
return convertEditIds(edit, (id) => tree.convertToStableNodeId(id));
}
export function getEditLogInternal(tree: SharedTree): OrderedEditSet<ChangeInternal> {
return tree.edits as unknown as OrderedEditSet<ChangeInternal>;
}
/**
* Spies on all future ops submitted to `containerRuntimeFactory`. When ops are submitted, they will be `push`ed into the
* returned array.

Просмотреть файл

@ -62,7 +62,7 @@
"@fluidframework/common-utils": "^0.32.1",
"@fluidframework/core-interfaces": "^0.43.1000",
"@fluidframework/datastore-definitions": "^0.60.1000",
"@fluidframework/protocol-base": "^0.1036.1000",
"@fluidframework/protocol-base": "^0.1036.2000-0",
"@fluidframework/protocol-definitions": "^0.1028.1000",
"@fluidframework/runtime-definitions": "^0.60.1000",
"@fluidframework/runtime-utils": "^0.60.1000",

Просмотреть файл

@ -55,33 +55,36 @@ const createObjectProxy = (
consumer: Consumer,
parent?: IProxy,
parentKey?: json1.Key,
) => new Proxy(subject, {
get: (target, key, receiver) => {
if (key === contextSym) {
return { parent, parentKey };
}
) => {
const handler: ProxyHandler<IProxy> = {
get: (target, key, receiver) => {
if (key === contextSym) {
return { parent, parentKey };
}
const value = target[key];
const value = target[key];
/* eslint-disable @typescript-eslint/no-unsafe-return */
return value !== null && typeof value === "object"
? getProxy(/* target: */ value, consumer, /* parent: */ receiver, key as string)
: value;
/* eslint-enable @typescript-eslint/no-unsafe-return */
},
set: (target, key, value, receiver) => {
const path = getPath(receiver, key as json1.Key);
/* eslint-disable @typescript-eslint/no-unsafe-return */
return value !== null && typeof value === "object"
? getProxy(/* target: */ value, consumer, /* parent: */ receiver, key as string)
: value;
/* eslint-enable @typescript-eslint/no-unsafe-return */
},
set: (target, key, value, receiver) => {
const path = getPath(receiver, key as json1.Key);
if (Object.prototype.hasOwnProperty.call(target, key)) {
consumer(json1.replaceOp(path, /* oldVal: */ target[key], /* newVal: */ value));
} else {
consumer(json1.insertOp(path, value));
}
if (Object.prototype.hasOwnProperty.call(target, key)) {
consumer(json1.replaceOp(path, /* oldVal: */ target[key], /* newVal: */ value));
} else {
consumer(json1.insertOp(path, value));
}
target[key] = value;
return true;
},
});
target[key] = value;
return true;
},
};
return new Proxy(subject, handler);
};
// If given key is a string containing an integer then convert it to an integer,
// otherwise return the key unmodified.

Просмотреть файл

@ -72,13 +72,13 @@ export const TreeObjectProxy = <T extends Object>(
// },
});
export class TreeArrayProxy<T> implements IArrayish<T> {
export class TreeArrayProxy<T> implements IArrayish<Serializable<T>> {
constructor(
private readonly tree: SharedTree,
private readonly nodeId: NodeId,
private readonly update: (...change: Change[]) => void,
) {
return new Proxy(this, {
const handler: ProxyHandler<TreeArrayProxy<T>> = {
get(target, key) {
if (typeof key !== "symbol" && !isNaN(key as unknown as number)) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
@ -107,10 +107,11 @@ export class TreeArrayProxy<T> implements IArrayish<T> {
return false;
},
});
};
return new Proxy(this, handler);
}
[n: number]: T;
[n: number]: Serializable<T>;
private get itemIds(): readonly NodeId[] {
const view = this.tree.currentView;
@ -118,13 +119,13 @@ export class TreeArrayProxy<T> implements IArrayish<T> {
}
private idsToItems(itemIds: readonly NodeId[]) {
return itemIds.map((itemId) => getChild(this.tree, itemId, this.update)) as T[];
return itemIds.map((itemId) => getChild(this.tree, itemId, this.update)) as Serializable<T>[];
}
private get items(): T[] { return this.idsToItems(this.itemIds); }
private get items(): Serializable<T>[] { return this.idsToItems(this.itemIds); }
get length(): number { return this.items.length; }
[Symbol.iterator](): IterableIterator<T> { return this.items[Symbol.iterator](); }
[Symbol.iterator](): IterableIterator<Serializable<T>> { return this.items[Symbol.iterator](); }
toString(): string { return this.items.toString(); }
toLocaleString(): string { return this.items.toLocaleString(); }
@ -159,7 +160,7 @@ export class TreeArrayProxy<T> implements IArrayish<T> {
label: "items" as TraitLabel })));
}
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] {
map<U>(callbackfn: (value: Serializable<T>, index: number, array: Serializable<T>[]) => U, thisArg?: any): U[] {
return this.items.map<U>(callbackfn, thisArg);
}
}

Просмотреть файл

@ -38,7 +38,7 @@
"@fluidframework/local-driver": "^0.60.1000",
"@fluidframework/protocol-definitions": "^0.1028.1000",
"@fluidframework/routerlicious-driver": "^0.60.1000",
"@fluidframework/server-local-server": "^0.1036.1000",
"@fluidframework/server-local-server": "^0.1036.2000-0",
"@fluidframework/test-runtime-utils": "^0.60.1000",
"@fluidframework/tinylicious-driver": "^0.60.1000",
"jsonwebtoken": "^8.4.0"

1787
lerna-package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -349,6 +349,7 @@ export class SharedMap extends SharedObject<ISharedMapEvents> implements IShared
}
/**
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
* @internal
*/
protected applyStashedOp(content: any): unknown {

Просмотреть файл

@ -179,7 +179,7 @@ describe("Directory", () => {
try {
subDirectory.set("throw", "error");
assert.fail("Should throw usage error");
} catch (error) {
} catch (error: any) {
assert.strictEqual(error.errorType, "usageError", "Should throw usage error");
}

Просмотреть файл

@ -64,7 +64,7 @@
"@fluidframework/core-interfaces": "^0.43.1000",
"@fluidframework/datastore-definitions": "^0.60.1000",
"@fluidframework/merge-tree": "^0.60.1000",
"@fluidframework/protocol-base": "^0.1036.1000",
"@fluidframework/protocol-base": "^0.1036.2000-0",
"@fluidframework/protocol-definitions": "^0.1028.1000",
"@fluidframework/runtime-definitions": "^0.60.1000",
"@fluidframework/runtime-utils": "^0.60.1000",

Просмотреть файл

@ -507,7 +507,7 @@ export class SharedMatrix<T = any>
default: {
assert(content.type === MatrixOp.set, 0x020 /* "Unknown SharedMatrix 'op' type." */);
const setOp = content as ISetOp<Serializable<T>>;
const setOp = content as ISetOp<T>;
const { rowHandle, colHandle, localSeq } = localOpMetadata as ISetOpMetadata;
// If there are more pending local writes to the same row/col handle, it is important
@ -564,10 +564,10 @@ export class SharedMatrix<T = any>
switch (contents.target) {
case SnapshotPath.cols:
this.cols.applyMsg(msg);
this.cols.applyMsg(msg, local);
break;
case SnapshotPath.rows:
this.rows.applyMsg(msg);
this.rows.applyMsg(msg, local);
break;
default: {
assert(contents.type === MatrixOp.set,
@ -687,7 +687,52 @@ export class SharedMatrix<T = any>
return `${s}\n`;
}
protected applyStashedOp() {
throw new Error("not implemented");
/**
* {@inheritDoc @fluidframework/shared-object-base#SharedObjectCore.applyStashedOp}
*/
protected applyStashedOp(content: any): unknown {
if (content.target === SnapshotPath.cols || content.target === SnapshotPath.rows) {
const op = content as IMergeTreeOp;
const currentVector = content.target === SnapshotPath.cols ? this.cols : this.rows;
const oppositeVector = content.target === SnapshotPath.cols ? this.rows : this.cols;
const metadata = currentVector.applyStashedOp(op);
const localSeq = currentVector.getCollabWindow().localSeq;
const oppositeWindow = oppositeVector.getCollabWindow();
assert(localSeq > oppositeWindow.localSeq,
"The 'localSeq' of the vector applying stashed op must > the 'localSeq' of the other vector.");
oppositeWindow.localSeq = localSeq;
return metadata;
} else {
assert(content.type === MatrixOp.set, "Unknown SharedMatrix 'op' type.");
const setOp = content as ISetOp<T>;
const rowHandle = this.rows.getAllocatedHandle(setOp.row);
const colHandle = this.cols.getAllocatedHandle(setOp.col);
if (this.undo !== undefined) {
let oldValue = this.cells.getCell(rowHandle, colHandle);
if (oldValue === null) {
oldValue = undefined;
}
this.undo.cellSet(
rowHandle,
colHandle,
oldValue);
}
this.cells.setCell(rowHandle, colHandle, setOp.value);
const localSeq = this.nextLocalSeq();
const metadata: ISetOpMetadata = {
rowHandle,
colHandle,
localSeq,
};
this.pending.setCell(rowHandle, colHandle, localSeq);
return metadata;
}
}
}

Просмотреть файл

@ -11,7 +11,7 @@ import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol
import { IFluidDataStoreRuntime, IChannelStorageService } from "@fluidframework/datastore-definitions";
import { ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
import { ITelemetryLogger } from "@fluidframework/common-definitions";
import { assert, Trace } from "@fluidframework/common-utils";
import { assert, Trace, unreachableCase } from "@fluidframework/common-utils";
import { LoggingError } from "@fluidframework/telemetry-utils";
import { IIntegerRange } from "./base";
import { RedBlackTree } from "./collections";
@ -778,7 +778,34 @@ export class Client {
}
}
public applyMsg(msg: ISequencedDocumentMessage) {
public applyStashedOp(op: IMergeTreeDeltaOp): SegmentGroup;
public applyStashedOp(op: IMergeTreeGroupMsg): SegmentGroup[];
public applyStashedOp(op: IMergeTreeOp): SegmentGroup | SegmentGroup[];
public applyStashedOp(op: IMergeTreeOp): SegmentGroup | SegmentGroup[] {
let metadata: SegmentGroup | SegmentGroup[] | undefined;
switch (op.type) {
case MergeTreeDeltaType.INSERT:
this.applyInsertOp({ op });
metadata = this.peekPendingSegmentGroups();
break;
case MergeTreeDeltaType.REMOVE:
this.applyRemoveRangeOp({ op });
metadata = this.peekPendingSegmentGroups();
break;
case MergeTreeDeltaType.ANNOTATE:
this.applyAnnotateRangeOp({ op });
metadata = this.peekPendingSegmentGroups();
break;
case MergeTreeDeltaType.GROUP:
return op.ops.map((o) => this.applyStashedOp(o));
default:
unreachableCase(op, "unrecognized op type");
}
assert(!!metadata, "Applying op must generate a pending segment");
return metadata;
}
public applyMsg(msg: ISequencedDocumentMessage, local: boolean = false) {
// Ensure client ID is registered
this.getOrAddShortClientId(msg.clientId);
// Apply if an operation message
@ -787,7 +814,7 @@ export class Client {
op: msg.contents as IMergeTreeOp,
sequencedMessage: msg,
};
if (opArgs.sequencedMessage?.clientId === this.longClientId) {
if (opArgs.sequencedMessage?.clientId === this.longClientId || local) {
this.ackPendingSegment(opArgs);
}
else {

Просмотреть файл

@ -128,6 +128,9 @@ interface IRefsAtOffset {
after?: LocalReference[];
}
/**
* Represents a collection of {@link LocalReference}s associated with one segment in a merge-tree.
*/
export class LocalReferenceCollection {
public static append(seg1: ISegment, seg2: ISegment) {
if (seg2.localRefs && !seg2.localRefs.empty) {
@ -150,6 +153,7 @@ export class LocalReferenceCollection {
private refCount: number = 0;
constructor(
/** Segment this `LocalReferenceCollection` is associated to. */
private readonly segment: ISegment,
initialRefsByfOffset = new Array<IRefsAtOffset | undefined>(segment.cachedLength)) {
// Since javascript arrays are sparse the above won't populate any of the
@ -296,6 +300,15 @@ export class LocalReferenceCollection {
this.refsByOffset.push(...other.refsByOffset);
}
/**
* Splits this `LocalReferenceCollection` into the intervals [0, offset) and [offset, originalLength).
* Local references in the former half of this split will remain associated with the segment used on construction.
* Local references in the latter half of this split will be transferred to `splitSeg`,
* and its `localRefs` field will be set.
* @param offset - Offset into the original segment at which the collection should be split
* @param splitSeg - Split segment which originally corresponded to the indices [offset, originalLength)
* before splitting.
*/
public split(offset: number, splitSeg: ISegment) {
if (!this.empty) {
const localRefs =
@ -314,6 +327,9 @@ export class LocalReferenceCollection {
this.refCount--;
localRefs.refCount++;
}
} else {
// shrink the offset array when empty and splitting
this.refsByOffset.length = offset;
}
}

Просмотреть файл

@ -2331,7 +2331,7 @@ export class MergeTree {
this.mapRange({ leaf: annotateSegment }, refSeq, clientId, undefined, start, end);
// OpArgs == undefined => test code
if (this.mergeTreeDeltaCallback) {
if (this.mergeTreeDeltaCallback && deltaSegments.length > 0) {
this.mergeTreeDeltaCallback(
opArgs,
{
@ -2445,7 +2445,7 @@ export class MergeTree {
}
// opArgs == undefined => test code
if (this.mergeTreeDeltaCallback) {
if (this.mergeTreeDeltaCallback && removedSegments.length > 0) {
this.mergeTreeDeltaCallback(
opArgs,
{

Просмотреть файл

@ -0,0 +1,182 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/
import random from "random-js";
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
import { IMergeTreeOp } from "../ops";
import { SegmentGroup } from "../mergeTree";
import {
generateClientNames,
doOverRange,
runMergeTreeOperationRunner,
annotateRange,
removeRange,
IMergeTreeOperationRunnerConfig,
IConfigRange,
} from "./mergeTreeOperationRunner";
import { TestClient } from "./testClient";
import { TestClientLogger } from "./testClientLogger";
function applyMessagesWithReconnect(
startingSeq: number,
messageDatas: [ISequencedDocumentMessage, SegmentGroup | SegmentGroup[]][],
clients: readonly TestClient[],
stashClients: readonly TestClient[],
) {
let seq = startingSeq;
const reconnectClientMsgs: [IMergeTreeOp, SegmentGroup | SegmentGroup[]][] = [];
let minSeq = 0;
// apply ops as stashed ops except for client #1
const stashedOps: [IMergeTreeOp, SegmentGroup | SegmentGroup[], number][] = [];
for (const messageData of messageDatas) {
if (messageData[0].clientId !== clients[1].longClientId) {
const index = clients.map((c) => c.longClientId).indexOf(messageData[0].clientId);
const localMetadata = stashClients[index].applyStashedOp(messageData[0].contents);
stashedOps.push([messageData[0].contents, localMetadata, index]);
}
}
// this should put all stash clients (except #1) in the same state as the
// respective normal clients, having local changes only.
for (let i = 0; i < clients.length; ++i) {
if (i !== 1) {
TestClientLogger.validate([clients[i], stashClients[i]]);
}
}
TestClientLogger.validate([clients[0], stashClients[1]]);
// apply the ops to the normal clients. they will all be the same now,
// except #1 which has local changes other clients haven't seen yet
for (const [message, sg] of messageDatas) {
if (message.clientId === clients[1].longClientId) {
reconnectClientMsgs.push([message.contents as IMergeTreeOp, sg]);
} else {
message.sequenceNumber = ++seq;
clients.forEach((c) => c.applyMsg(message));
minSeq = message.minimumSequenceNumber;
}
}
// regenerate the ops that were applied as stashed ops. this simulates resubmit()
const regeneratedStashedOps = stashedOps.map((op) =>
stashClients[op[2]].makeOpMessage(
stashClients[op[2]].regeneratePendingOp(
op[0],
op[1],
)));
// apply the regenerated stashed ops
let stashedOpSeq = startingSeq;
for (const msg of regeneratedStashedOps) {
msg.sequenceNumber = ++stashedOpSeq;
stashClients.forEach((c) => c.applyMsg(msg));
}
// all stash and normal clients should now be in the same state,
// except #1 (normal) which still has local changes
TestClientLogger.validate([...clients.filter((_, i) => i !== 1), ...stashClients]);
// regenerate ops for client #1
const reconnectMsgs: ISequencedDocumentMessage[] = [];
reconnectClientMsgs.forEach((opData) => {
const newMsg = clients[1].makeOpMessage(
clients[1].regeneratePendingOp(
opData[0],
opData[1],
));
newMsg.minimumSequenceNumber = minSeq;
reconnectMsgs.push(newMsg);
});
// apply regenerated ops as stashed ops for client #1
const stashedRegeneratedOps: [IMergeTreeOp, SegmentGroup | SegmentGroup[]][] = reconnectMsgs.map((message) => {
const localMetadata = stashClients[1].applyStashedOp(message.contents);
return [message.contents, localMetadata];
});
// now both clients at index 1 should be the same
TestClientLogger.validate([clients[1], stashClients[1]]);
// apply the regenerated ops from client #1
for (const message of reconnectMsgs) {
message.sequenceNumber = ++seq;
clients.forEach((c) => c.applyMsg(message));
}
// resubmit regenerated stashed ops
const reRegeneratedStashedMessages = stashedRegeneratedOps.map((stashedOp) =>
stashClients[1].makeOpMessage(
stashClients[1].regeneratePendingOp(
stashedOp[0],
stashedOp[1]),
));
for (const reRegeneratedStashedOp of reRegeneratedStashedMessages) {
reRegeneratedStashedOp.sequenceNumber = ++stashedOpSeq;
stashClients.forEach((c) => c.applyMsg(reRegeneratedStashedOp));
}
// all clients should now be the same
TestClientLogger.validate([...clients, ...stashClients]);
return seq;
}
export const defaultOptions: IMergeTreeOperationRunnerConfig & { minLength: number, clients: IConfigRange } = {
minLength: 16,
clients: { min: 3, max: 12 },
opsPerRoundRange: { min: 40, max: 120 },
rounds: 3,
operations: [annotateRange, removeRange],
growthFunc: (input: number) => input * 2,
};
describe("MergeTree.Client", () => {
const opts = defaultOptions;
// Generate a list of single character client names, support up to 69 clients
const clientNames = generateClientNames();
doOverRange(opts.clients, opts.growthFunc.bind(opts), (clientCount) => {
it(`applyStashedOpFarm_${clientCount}`, async () => {
const mt = random.engines.mt19937();
mt.seedWithArray([0xDEADBEEF, 0xFEEDBED, clientCount]);
const clients: TestClient[] = [new TestClient()];
// This test is based on reconnectFarm, but we keep a second set of clients. For
// these clients, we apply the generated ops as stashed ops, then regenerate
// them to simulate resubmit(), then apply them. In the end, they should arrive
// at the same state as the "normal" set of clients
let stashClients: TestClient[] = [];
clients.forEach(
(c, i) => c.startOrUpdateCollaboration(clientNames[i]));
stashClients = [new TestClient()];
stashClients.forEach(
(c, i) => c.startOrUpdateCollaboration(clientNames[i]));
let seq = 0;
clients.forEach((c) => c.updateMinSeq(seq));
stashClients.forEach((c) => c.updateMinSeq(seq));
// Add double the number of clients each iteration
const targetClients = Math.max(opts.clients.min, clientCount);
for (let cc = clients.length; cc < targetClients; cc++) {
const newClient = await TestClient.createFromClientSnapshot(clients[0], clientNames[cc]);
clients.push(newClient);
// add 1 stash client per normal client
const anotherNewClient = await TestClient.createFromClientSnapshot(clients[0], clientNames[cc]);
stashClients.push(anotherNewClient);
}
seq = runMergeTreeOperationRunner(
mt,
seq,
clients,
opts.minLength,
opts,
(s, m, c) => applyMessagesWithReconnect(s, m, c, stashClients));
})
.timeout(30 * 1000);
});
});

Просмотреть файл

@ -5,8 +5,11 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { strict as assert } from "assert";
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
import { LocalReference } from "../localReference";
import { ReferenceType } from "../ops";
import { TextSegment } from "../textSegment";
import { createClientsAtInitialState } from "./testClientLogger";
import { TestClient } from "./";
describe("MergeTree.Client", () => {
@ -171,4 +174,44 @@ describe("MergeTree.Client", () => {
assert.equal(c1LocalRef.toPosition(), -1);
});
it("Split segment with no references and append to segment with references", () => {
const clients = createClientsAtInitialState("","A", "B");
const messages: ISequencedDocumentMessage[] = [];
let seq = 0;
messages.push(clients.A.makeOpMessage(clients.A.insertTextLocal(0, "0123456789"),++seq));
// initialize the local reference collection on the segment, but keep it empty
{
const segInfo = clients.A.getContainingSegment(9);
const segment = segInfo.segment;
assert(TextSegment.is(segment!));
assert.strictEqual(segment.text[segInfo.offset!], "9");
const localRef =
new LocalReference(clients.A, segment, segInfo.offset, ReferenceType.Simple);
clients.A.addLocalReference(localRef);
clients.A.removeLocalReference(localRef);
}
// split the segment
messages.push(clients.A.makeOpMessage(clients.A.insertTextLocal(5, "ABCD"),++seq));
// add a local reference to the newly inserted segment that caused the split
{
const segInfo = clients.A.getContainingSegment(6);
const segment = segInfo.segment;
assert(TextSegment.is(segment!));
assert.strictEqual(segment.text[segInfo.offset!], "B");
const localRef =
new LocalReference(clients.A, segment, segInfo.offset, ReferenceType.SlideOnRemove);
clients.A.addLocalReference(localRef);
}
// apply all the ops
while (messages.length > 0) {
const msg = messages.shift()!;
clients.all.forEach((c)=>c.applyMsg(msg));
}
// regression: would fire 0x2be on zamboni during segment append
clients.all.forEach((c)=>c.updateMinSeq(seq));
});
});

Просмотреть файл

@ -54,12 +54,31 @@ describe("MergeTree", () => {
});
});
it("No event on annotation of empty range", () => {
const count = countOperations(mergeTree);
mergeTree.annotateRange(
3,
3,
{
foo: "bar",
},
undefined,
currentSequenceNumber,
localClientId,
++currentSequenceNumber,
undefined as any);
assert.deepStrictEqual(count, {
[MergeTreeMaintenanceType.SPLIT]: 1,
});
});
it("Annotate over local insertion", () => {
insertText(
mergeTree,
4,
localClientId,
currentSequenceNumber,
localClientId,
UnassignedSequenceNumber,
"a",
undefined,
@ -92,8 +111,8 @@ describe("MergeTree", () => {
insertText(
mergeTree,
4,
remoteClientId,
remoteSequenceNumber,
remoteClientId,
++remoteSequenceNumber,
"a",
undefined,
@ -126,8 +145,8 @@ describe("MergeTree", () => {
mergeTree.markRangeRemoved(
4,
6,
remoteClientId,
remoteSequenceNumber,
remoteClientId,
++remoteSequenceNumber,
false,
undefined as any);
@ -151,5 +170,38 @@ describe("MergeTree", () => {
[MergeTreeMaintenanceType.SPLIT]: 2,
});
});
it("Remote annotate within local deletion", () => {
const remoteClientId: number = 35;
let remoteSequenceNumber = currentSequenceNumber;
mergeTree.markRangeRemoved(
3,
8,
currentSequenceNumber,
localClientId,
UnassignedSequenceNumber,
false,
undefined as any);
const count = countOperations(mergeTree);
mergeTree.annotateRange(
4,
6,
{
foo: "bar",
},
undefined,
remoteSequenceNumber,
remoteClientId,
++remoteSequenceNumber,
undefined as any);
assert.deepStrictEqual(count, {
[MergeTreeDeltaType.ANNOTATE]: 1,
[MergeTreeMaintenanceType.SPLIT]: 2,
});
});
});
});

Просмотреть файл

@ -152,5 +152,35 @@ describe("MergeTree", () => {
[MergeTreeMaintenanceType.SPLIT]: 2,
});
});
it("Local delete shadows remote", () => {
const remoteClientId: number = 35;
let remoteSequenceNumber = currentSequenceNumber;
mergeTree.markRangeRemoved(
3,
6,
currentSequenceNumber,
localClientId,
UnassignedSequenceNumber,
false,
undefined as any);
const count = countOperations(mergeTree);
mergeTree.markRangeRemoved(
4,
5,
remoteSequenceNumber,
remoteClientId,
++remoteSequenceNumber,
false,
undefined as any);
assert.deepStrictEqual(count, {
/* MergeTreeDeltaType.REMOVE is absent as it should not be fired. */
[MergeTreeMaintenanceType.SPLIT]: 2,
});
});
});
});

Просмотреть файл

@ -10,7 +10,7 @@ import * as fs from "fs";
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
import random from "random-js";
import { LocalReference } from "../localReference";
import { IMergeTreeOp, MergeTreeDeltaType } from "../ops";
import { IMergeTreeOp, MergeTreeDeltaType, ReferenceType } from "../ops";
import { TextSegment } from "../textSegment";
import { ISegment, SegmentGroup } from "../mergeTree";
import { TestClient } from "./testClient";
@ -44,9 +44,13 @@ export const insertAtRefPos: TestOperation =
if(segs.length > 0) {
const text = client.longClientId!.repeat(random.integer(1, 3)(mt));
const seg = random.pick(mt,segs);
return client.insertAtReferencePositionLocal(
new LocalReference(client, seg, random.integer(0, seg.cachedLength - 1)(mt)),
TextSegment.make(text));
const lref = new LocalReference(
client, seg, random.integer(0, seg.cachedLength - 1)(mt),
random.pick(mt,[ReferenceType.Simple, ReferenceType.SlideOnRemove, ReferenceType.Transient]));
if(lref.refType !== ReferenceType.Transient) {
client.addLocalReference(lref);
}
return client.insertAtReferencePositionLocal(lref,TextSegment.make(text));
}
};

Просмотреть файл

@ -168,6 +168,10 @@ export class TestClientLogger {
return baseText;
}
static validate(clients: readonly TestClient[], title?: string) {
return new TestClientLogger(clients, title).validate();
}
public toString(excludeHeader: boolean = false) {
let str = "";
if(!excludeHeader) {

Просмотреть файл

@ -0,0 +1,13 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/
module.exports = {
"extends": [
"@fluidframework/eslint-config-fluid/strict"
],
"parserOptions": {
"project": ["./tsconfig.json", "./src/test/tsconfig.json"]
},
}

52
packages/dds/quorum/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
# Compiled TypeScript and CSS
dist
lib
# Babel
public/scripts/es5
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
.cache-loader
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
# Typings
typings
# Debug log from npm
npm-debug.log
# Code coverage
nyc
.nyc_output/
# Chart dependencies
**/charts/*.tgz
# Generated modules
intel_modules/
temp_modules/

Просмотреть файл

@ -0,0 +1,6 @@
nyc
*.log
**/*.tsbuildinfo
src/test
dist/test
**/_api-extractor-temp/**

Просмотреть файл

@ -0,0 +1,21 @@
Copyright (c) Microsoft Corporation and contributors. All rights reserved.
MIT License
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.

Просмотреть файл

@ -0,0 +1,15 @@
# @fluid-internal/quorum
<!-- AUTO-GENERATED-CONTENT:START (TEMPLATE:src=readme-simple.md) -->
<!-- This section is automatically generated. To update it, edit docs/md-magic.config.js then run
'npm run build:md-magic' in the docs folder. -->
Documentation available at <https://fluidframework.com/docs/apis/quorum>.
## Trademark
This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services. Use of these trademarks
or logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
<!-- AUTO-GENERATED-CONTENT:END -->

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше