@fluidframework/container-runtime 2.42.0 → 2.43.0-343119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/container-runtime.test-files.tar +0 -0
- package/dist/compatUtils.d.ts +2 -0
- package/dist/compatUtils.d.ts.map +1 -1
- package/dist/compatUtils.js.map +1 -1
- package/dist/containerRuntime.d.ts +2 -2
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +28 -12
- package/dist/containerRuntime.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +40 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +57 -1
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/index.d.ts +1 -1
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js.map +1 -1
- package/lib/compatUtils.d.ts +2 -0
- package/lib/compatUtils.d.ts.map +1 -1
- package/lib/compatUtils.js.map +1 -1
- package/lib/containerRuntime.d.ts +2 -2
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +28 -12
- package/lib/containerRuntime.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +40 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +57 -1
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/index.d.ts +1 -1
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js.map +1 -1
- package/package.json +18 -18
- package/src/compatUtils.ts +2 -0
- package/src/containerRuntime.ts +33 -12
- package/src/index.ts +2 -1
- package/src/packageVersion.ts +1 -1
- package/src/summary/documentSchema.ts +125 -5
- package/src/summary/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.43.0-343119",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -119,18 +119,18 @@
|
|
|
119
119
|
"temp-directory": "nyc/.nyc_output"
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@fluid-internal/client-utils": "
|
|
123
|
-
"@fluidframework/container-definitions": "
|
|
124
|
-
"@fluidframework/container-runtime-definitions": "
|
|
125
|
-
"@fluidframework/core-interfaces": "
|
|
126
|
-
"@fluidframework/core-utils": "
|
|
127
|
-
"@fluidframework/datastore": "
|
|
128
|
-
"@fluidframework/driver-definitions": "
|
|
129
|
-
"@fluidframework/driver-utils": "
|
|
130
|
-
"@fluidframework/id-compressor": "
|
|
131
|
-
"@fluidframework/runtime-definitions": "
|
|
132
|
-
"@fluidframework/runtime-utils": "
|
|
133
|
-
"@fluidframework/telemetry-utils": "
|
|
122
|
+
"@fluid-internal/client-utils": "2.43.0-343119",
|
|
123
|
+
"@fluidframework/container-definitions": "2.43.0-343119",
|
|
124
|
+
"@fluidframework/container-runtime-definitions": "2.43.0-343119",
|
|
125
|
+
"@fluidframework/core-interfaces": "2.43.0-343119",
|
|
126
|
+
"@fluidframework/core-utils": "2.43.0-343119",
|
|
127
|
+
"@fluidframework/datastore": "2.43.0-343119",
|
|
128
|
+
"@fluidframework/driver-definitions": "2.43.0-343119",
|
|
129
|
+
"@fluidframework/driver-utils": "2.43.0-343119",
|
|
130
|
+
"@fluidframework/id-compressor": "2.43.0-343119",
|
|
131
|
+
"@fluidframework/runtime-definitions": "2.43.0-343119",
|
|
132
|
+
"@fluidframework/runtime-utils": "2.43.0-343119",
|
|
133
|
+
"@fluidframework/telemetry-utils": "2.43.0-343119",
|
|
134
134
|
"@tylerbu/sorted-btree-es6": "^1.8.0",
|
|
135
135
|
"double-ended-queue": "^2.1.0-0",
|
|
136
136
|
"lz4js": "^0.2.0",
|
|
@@ -140,16 +140,16 @@
|
|
|
140
140
|
"devDependencies": {
|
|
141
141
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
142
142
|
"@biomejs/biome": "~1.9.3",
|
|
143
|
-
"@fluid-internal/mocha-test-setup": "
|
|
144
|
-
"@fluid-private/stochastic-test-utils": "
|
|
145
|
-
"@fluid-private/test-pairwise-generator": "
|
|
143
|
+
"@fluid-internal/mocha-test-setup": "2.43.0-343119",
|
|
144
|
+
"@fluid-private/stochastic-test-utils": "2.43.0-343119",
|
|
145
|
+
"@fluid-private/test-pairwise-generator": "2.43.0-343119",
|
|
146
146
|
"@fluid-tools/benchmark": "^0.51.0",
|
|
147
147
|
"@fluid-tools/build-cli": "^0.55.0",
|
|
148
148
|
"@fluidframework/build-common": "^2.0.3",
|
|
149
149
|
"@fluidframework/build-tools": "^0.55.0",
|
|
150
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.
|
|
150
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.42.0",
|
|
151
151
|
"@fluidframework/eslint-config-fluid": "^5.7.4",
|
|
152
|
-
"@fluidframework/test-runtime-utils": "
|
|
152
|
+
"@fluidframework/test-runtime-utils": "2.43.0-343119",
|
|
153
153
|
"@microsoft/api-extractor": "7.52.8",
|
|
154
154
|
"@types/double-ended-queue": "^2.1.0",
|
|
155
155
|
"@types/lz4js": "^0.2.0",
|
package/src/compatUtils.ts
CHANGED
|
@@ -66,6 +66,8 @@ export type MinimumVersionForCollab =
|
|
|
66
66
|
* String in a valid semver format of a specific version at least specifying minor.
|
|
67
67
|
* Unlike {@link MinimumVersionForCollab}, this type allows any bigint for the major version.
|
|
68
68
|
* Used as a more generic type that allows major versions other than 1 or 2.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
69
71
|
*/
|
|
70
72
|
export type SemanticVersion =
|
|
71
73
|
| `${bigint}.${bigint}.${bigint}`
|
package/src/containerRuntime.ts
CHANGED
|
@@ -160,6 +160,7 @@ import {
|
|
|
160
160
|
tagCodeArtifacts,
|
|
161
161
|
normalizeError,
|
|
162
162
|
} from "@fluidframework/telemetry-utils/internal";
|
|
163
|
+
import { gt } from "semver-ts";
|
|
163
164
|
import { v4 as uuid } from "uuid";
|
|
164
165
|
|
|
165
166
|
import { BindBatchTracker } from "./batchTracker.js";
|
|
@@ -183,6 +184,7 @@ import {
|
|
|
183
184
|
isValidMinVersionForCollab,
|
|
184
185
|
type RuntimeOptionsAffectingDocSchema,
|
|
185
186
|
type MinimumVersionForCollab,
|
|
187
|
+
type SemanticVersion,
|
|
186
188
|
validateRuntimeOptions,
|
|
187
189
|
} from "./compatUtils.js";
|
|
188
190
|
import type { ICompressionRuntimeOptions } from "./compressionDefinitions.js";
|
|
@@ -1114,8 +1116,19 @@ export class ContainerRuntime
|
|
|
1114
1116
|
(schema) => {
|
|
1115
1117
|
runtime.onSchemaChange(schema);
|
|
1116
1118
|
},
|
|
1119
|
+
{ minVersionForCollab },
|
|
1120
|
+
logger,
|
|
1117
1121
|
);
|
|
1118
1122
|
|
|
1123
|
+
// If the minVersionForCollab for this client is greater than the existing one, we should use that one going forward.
|
|
1124
|
+
const existingMinVersionForCollab =
|
|
1125
|
+
documentSchemaController.sessionSchema.info.minVersionForCollab;
|
|
1126
|
+
const updatedMinVersionForCollab =
|
|
1127
|
+
existingMinVersionForCollab === undefined ||
|
|
1128
|
+
gt(minVersionForCollab, existingMinVersionForCollab)
|
|
1129
|
+
? minVersionForCollab
|
|
1130
|
+
: existingMinVersionForCollab;
|
|
1131
|
+
|
|
1119
1132
|
if (compressionLz4 && !enableGroupedBatching) {
|
|
1120
1133
|
throw new UsageError("If compression is enabled, op grouping must be enabled too");
|
|
1121
1134
|
}
|
|
@@ -1154,7 +1167,7 @@ export class ContainerRuntime
|
|
|
1154
1167
|
documentSchemaController,
|
|
1155
1168
|
featureGatesForTelemetry,
|
|
1156
1169
|
provideEntryPoint,
|
|
1157
|
-
|
|
1170
|
+
updatedMinVersionForCollab,
|
|
1158
1171
|
requestHandler,
|
|
1159
1172
|
undefined, // summaryConfiguration
|
|
1160
1173
|
recentBatchInfo,
|
|
@@ -1491,7 +1504,7 @@ export class ContainerRuntime
|
|
|
1491
1504
|
private readonly documentsSchemaController: DocumentsSchemaController,
|
|
1492
1505
|
featureGatesForTelemetry: Record<string, boolean | number | undefined>,
|
|
1493
1506
|
provideEntryPoint: (containerRuntime: IContainerRuntime) => Promise<FluidObject>,
|
|
1494
|
-
private readonly minVersionForCollab:
|
|
1507
|
+
private readonly minVersionForCollab: SemanticVersion,
|
|
1495
1508
|
private readonly requestHandler?: (
|
|
1496
1509
|
request: IRequest,
|
|
1497
1510
|
runtime: IContainerRuntime,
|
|
@@ -3474,22 +3487,29 @@ export class ContainerRuntime
|
|
|
3474
3487
|
throw new UsageError("cannot enter staging mode while detached");
|
|
3475
3488
|
}
|
|
3476
3489
|
|
|
3477
|
-
// Make sure
|
|
3490
|
+
// Make sure Outbox is empty before entering staging mode,
|
|
3478
3491
|
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
3479
|
-
this.
|
|
3492
|
+
this.flush();
|
|
3480
3493
|
|
|
3481
3494
|
const exitStagingMode = (discardOrCommit: () => void) => (): void => {
|
|
3482
|
-
|
|
3483
|
-
|
|
3495
|
+
try {
|
|
3496
|
+
// Final flush of any last staged changes
|
|
3497
|
+
// NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
|
|
3498
|
+
this.outbox.flush();
|
|
3484
3499
|
|
|
3485
|
-
|
|
3500
|
+
this.stageControls = undefined;
|
|
3486
3501
|
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3502
|
+
// During Staging Mode, we avoid submitting any ID Allocation ops (apart from resubmitting pre-staging ops).
|
|
3503
|
+
// Now that we've exited, we need to submit an ID Allocation op for any IDs that were generated while in Staging Mode.
|
|
3504
|
+
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
3505
|
+
discardOrCommit();
|
|
3491
3506
|
|
|
3492
|
-
|
|
3507
|
+
this.channelCollection.notifyStagingMode(false);
|
|
3508
|
+
} catch (error) {
|
|
3509
|
+
const normalizedError = normalizeError(error);
|
|
3510
|
+
this.closeFn(normalizedError);
|
|
3511
|
+
throw normalizedError;
|
|
3512
|
+
}
|
|
3493
3513
|
};
|
|
3494
3514
|
|
|
3495
3515
|
// eslint-disable-next-line import/no-deprecated
|
|
@@ -4572,6 +4592,7 @@ export class ContainerRuntime
|
|
|
4572
4592
|
newRuntimeSchema: JSON.stringify(schemaChangeMessage.runtime),
|
|
4573
4593
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
4574
4594
|
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
4595
|
+
minVersionForCollab: schemaChangeMessage.info?.minVersionForCollab,
|
|
4575
4596
|
});
|
|
4576
4597
|
const msg: OutboundContainerRuntimeDocumentSchemaMessage = {
|
|
4577
4598
|
type: ContainerMessageType.DocumentSchemaChange,
|
package/src/index.ts
CHANGED
|
@@ -31,7 +31,7 @@ export {
|
|
|
31
31
|
ChannelCollectionFactory,
|
|
32
32
|
AllowTombstoneRequestHeaderKey,
|
|
33
33
|
} from "./channelCollection.js";
|
|
34
|
-
export type { MinimumVersionForCollab } from "./compatUtils.js";
|
|
34
|
+
export type { MinimumVersionForCollab, SemanticVersion } from "./compatUtils.js";
|
|
35
35
|
export {
|
|
36
36
|
GCNodeType,
|
|
37
37
|
IGCMetadata,
|
|
@@ -91,6 +91,7 @@ export {
|
|
|
91
91
|
IRetriableFailureError,
|
|
92
92
|
IdCompressorMode,
|
|
93
93
|
IDocumentSchema,
|
|
94
|
+
IDocumentSchemaInfo,
|
|
94
95
|
DocumentSchemaValueType,
|
|
95
96
|
IDocumentSchemaCurrent,
|
|
96
97
|
currentDocumentVersionSchema,
|
package/src/packageVersion.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
+
import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
7
8
|
import { DataProcessingError } from "@fluidframework/telemetry-utils/internal";
|
|
9
|
+
import { gt, lt, parse } from "semver-ts";
|
|
8
10
|
|
|
11
|
+
import type { SemanticVersion } from "../compatUtils.js";
|
|
9
12
|
import { pkgVersion } from "../packageVersion.js";
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -80,10 +83,42 @@ export interface IDocumentSchema {
|
|
|
80
83
|
* properties to be able to open the document.
|
|
81
84
|
*/
|
|
82
85
|
runtime: Record<string, DocumentSchemaValueType>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Info about this document that can be updated via Document Schema change op, but isn't required
|
|
89
|
+
* to be understood by all clients (unlike the rest of IDocumentSchema properties). Because of this,
|
|
90
|
+
* some older documents may not have this property, so it's an optional property.
|
|
91
|
+
*/
|
|
92
|
+
info?: IDocumentSchemaInfo;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Informational properties of the document that are not subject to strict schema enforcement.
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
export interface IDocumentSchemaInfo {
|
|
101
|
+
/**
|
|
102
|
+
* The minimum version of the FF runtime that should be used to load this document.
|
|
103
|
+
* Will likely be advanced over time as applications pick up later FF versions.
|
|
104
|
+
*
|
|
105
|
+
* We use this to issue telemetry warning events if a client tries to open a document
|
|
106
|
+
* with a runtime version lower than this.
|
|
107
|
+
*
|
|
108
|
+
* See {@link @fluidframework/container-runtime#LoadContainerRuntimeParams} for additional details on `minVersionForCollab`.
|
|
109
|
+
*
|
|
110
|
+
* @remarks
|
|
111
|
+
* We use `SemanticVersion` instead of `MinimumVersionForCollab` since we may open future documents that with a
|
|
112
|
+
* minVersionForCollab version that `MinimumVersionForCollab` does not support.
|
|
113
|
+
*/
|
|
114
|
+
minVersionForCollab: SemanticVersion;
|
|
83
115
|
}
|
|
84
116
|
|
|
85
117
|
/**
|
|
86
118
|
* Content of the type=ContainerMessageType.DocumentSchemaChange ops.
|
|
119
|
+
* The meaning of refSeq field is different in such messages (compared to other usages of IDocumentSchemaCurrent)
|
|
120
|
+
* ContainerMessageType.DocumentSchemaChange messages use CAS (Compare-and-swap) semantics, and convey
|
|
121
|
+
* regSeq of last known schema change (known to a client proposing schema change).
|
|
87
122
|
* @see InboundContainerRuntimeDocumentSchemaMessage
|
|
88
123
|
* @internal
|
|
89
124
|
*/
|
|
@@ -133,6 +168,8 @@ export interface IDocumentSchemaFeatures {
|
|
|
133
168
|
* in a way that all old/new clients are required to understand.
|
|
134
169
|
* Ex: Adding a new configuration property (under IDocumentSchema.runtime) does not require changing this version since there is logic
|
|
135
170
|
* in old clients for handling new/unknown properties.
|
|
171
|
+
* Ex: Adding a new property to IDocumentSchema.info does not require changing this version, since info properties are not required to be
|
|
172
|
+
* understood by all clients.
|
|
136
173
|
* Ex: Changing the 'document schema acceptance' mechanism from convert-and-swap to one requiring consensus does require changing this version
|
|
137
174
|
* since all clients need to understand the new protocol.
|
|
138
175
|
* @internal
|
|
@@ -141,11 +178,15 @@ export const currentDocumentVersionSchema = 1;
|
|
|
141
178
|
|
|
142
179
|
/**
|
|
143
180
|
* Current document schema.
|
|
181
|
+
* This interface represents the schema that we currently understand and know the
|
|
182
|
+
* structure of (which properties will be present).
|
|
183
|
+
*
|
|
144
184
|
* @internal
|
|
145
185
|
*/
|
|
146
186
|
export interface IDocumentSchemaCurrent extends Required<IDocumentSchema> {
|
|
147
187
|
// This is the version of the schema that we currently understand.
|
|
148
188
|
version: typeof currentDocumentVersionSchema;
|
|
189
|
+
// This narrows the runtime property to only include the properties in IDocumentSchemaFeatures (all as optional)
|
|
149
190
|
runtime: {
|
|
150
191
|
[P in keyof IDocumentSchemaFeatures]?: IDocumentSchemaFeatures[P] extends boolean
|
|
151
192
|
? true
|
|
@@ -153,6 +194,18 @@ export interface IDocumentSchemaCurrent extends Required<IDocumentSchema> {
|
|
|
153
194
|
};
|
|
154
195
|
}
|
|
155
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Document schema that is incoming from another client but validated to be "current".
|
|
199
|
+
*
|
|
200
|
+
* This interface represents when we have validated that an incoming IDocumentSchema object
|
|
201
|
+
* is compatible with the current runtime (by calling `checkRuntimeCompatibility()`).
|
|
202
|
+
* However, the `info` property is optional because some older documents may not have this property, but
|
|
203
|
+
* `info` is not required to be understood by all clients to be compatible.
|
|
204
|
+
*/
|
|
205
|
+
interface IDocumentSchemaCurrentIncoming extends Omit<IDocumentSchemaCurrent, "info"> {
|
|
206
|
+
info?: IDocumentSchemaInfo;
|
|
207
|
+
}
|
|
208
|
+
|
|
156
209
|
interface IProperty<T = unknown> {
|
|
157
210
|
and: (persistedSchema: T, providedSchema: T) => T;
|
|
158
211
|
or: (persistedSchema: T, providedSchema: T) => T;
|
|
@@ -257,7 +310,7 @@ const documentSchemaSupportedConfigs = {
|
|
|
257
310
|
function checkRuntimeCompatibility(
|
|
258
311
|
documentSchema: IDocumentSchema | undefined,
|
|
259
312
|
schemaName: string,
|
|
260
|
-
): asserts documentSchema is
|
|
313
|
+
): asserts documentSchema is IDocumentSchemaCurrentIncoming {
|
|
261
314
|
// Back-compat - we can't do anything about legacy documents.
|
|
262
315
|
// There is no way to validate them, so we are taking a guess that safe deployment processes used by a given app
|
|
263
316
|
// do not run into compat problems.
|
|
@@ -315,7 +368,7 @@ function checkRuntimeCompatibility(
|
|
|
315
368
|
}
|
|
316
369
|
|
|
317
370
|
function and(
|
|
318
|
-
persistedSchema:
|
|
371
|
+
persistedSchema: IDocumentSchemaCurrentIncoming,
|
|
319
372
|
providedSchema: IDocumentSchemaCurrent,
|
|
320
373
|
): IDocumentSchemaCurrent {
|
|
321
374
|
const runtime = {};
|
|
@@ -328,15 +381,22 @@ function and(
|
|
|
328
381
|
providedSchema.runtime[key],
|
|
329
382
|
);
|
|
330
383
|
}
|
|
384
|
+
|
|
385
|
+
// We keep the persisted minVersionForCollab if present, even if the provided minVersionForCollab
|
|
386
|
+
// is higher.
|
|
387
|
+
const minVersionForCollab =
|
|
388
|
+
persistedSchema.info?.minVersionForCollab ?? providedSchema.info.minVersionForCollab;
|
|
389
|
+
|
|
331
390
|
return {
|
|
332
391
|
version: currentDocumentVersionSchema,
|
|
333
392
|
refSeq: persistedSchema.refSeq,
|
|
393
|
+
info: { minVersionForCollab },
|
|
334
394
|
runtime,
|
|
335
395
|
};
|
|
336
396
|
}
|
|
337
397
|
|
|
338
398
|
function or(
|
|
339
|
-
persistedSchema:
|
|
399
|
+
persistedSchema: IDocumentSchemaCurrentIncoming,
|
|
340
400
|
providedSchema: IDocumentSchemaCurrent,
|
|
341
401
|
): IDocumentSchemaCurrent {
|
|
342
402
|
const runtime = {};
|
|
@@ -349,17 +409,40 @@ function or(
|
|
|
349
409
|
providedSchema.runtime[key],
|
|
350
410
|
);
|
|
351
411
|
}
|
|
412
|
+
|
|
413
|
+
// We take the greater of the persisted/provided minVersionForCollab
|
|
414
|
+
const minVersionForCollab =
|
|
415
|
+
persistedSchema.info === undefined
|
|
416
|
+
? providedSchema.info.minVersionForCollab
|
|
417
|
+
: gt(persistedSchema.info.minVersionForCollab, providedSchema.info.minVersionForCollab)
|
|
418
|
+
? persistedSchema.info.minVersionForCollab
|
|
419
|
+
: providedSchema.info.minVersionForCollab;
|
|
420
|
+
|
|
352
421
|
return {
|
|
353
422
|
version: currentDocumentVersionSchema,
|
|
354
423
|
refSeq: persistedSchema.refSeq,
|
|
424
|
+
info: { minVersionForCollab },
|
|
355
425
|
runtime,
|
|
356
426
|
};
|
|
357
427
|
}
|
|
358
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Determines if two schemas are the "same".
|
|
431
|
+
* Schemas are considered **not** the same if a schema change op is required to make
|
|
432
|
+
* the properties of `persistedSchema` match to the properties of `providedSchema`.
|
|
433
|
+
*/
|
|
359
434
|
function same(
|
|
360
|
-
persistedSchema:
|
|
435
|
+
persistedSchema: IDocumentSchemaCurrentIncoming,
|
|
361
436
|
providedSchema: IDocumentSchemaCurrent,
|
|
362
437
|
): boolean {
|
|
438
|
+
if (
|
|
439
|
+
persistedSchema.info === undefined ||
|
|
440
|
+
lt(persistedSchema.info.minVersionForCollab, providedSchema.info.minVersionForCollab)
|
|
441
|
+
) {
|
|
442
|
+
// If the persisted schema's minVersionForCollab is undefined or less than the provided schema's
|
|
443
|
+
// minVersionForCollab, then we should send a schema change op to update the minVersionForCollab.
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
363
446
|
for (const key of new Set([
|
|
364
447
|
...Object.keys(persistedSchema.runtime),
|
|
365
448
|
...Object.keys(providedSchema.runtime),
|
|
@@ -483,6 +566,8 @@ export class DocumentsSchemaController {
|
|
|
483
566
|
* @param documentMetadataSchema - current document's schema, if present.
|
|
484
567
|
* @param features - features of the document schema that current session wants to see enabled.
|
|
485
568
|
* @param onSchemaChange - callback that is called whenever schema is changed (not called on creation / load, only when processing document schema change ops)
|
|
569
|
+
* @param info - Informational properties of the document that are not subject to strict schema enforcement
|
|
570
|
+
* @param logger - telemetry logger from the runtime
|
|
486
571
|
*/
|
|
487
572
|
constructor(
|
|
488
573
|
existing: boolean,
|
|
@@ -490,6 +575,8 @@ export class DocumentsSchemaController {
|
|
|
490
575
|
documentMetadataSchema: IDocumentSchema | undefined,
|
|
491
576
|
features: IDocumentSchemaFeatures,
|
|
492
577
|
private readonly onSchemaChange: (schema: IDocumentSchemaCurrent) => void,
|
|
578
|
+
info: IDocumentSchemaInfo,
|
|
579
|
+
logger: ITelemetryLoggerExt,
|
|
493
580
|
) {
|
|
494
581
|
// For simplicity, let's only support new schema features for explicit schema control mode
|
|
495
582
|
assert(
|
|
@@ -497,10 +584,34 @@ export class DocumentsSchemaController {
|
|
|
497
584
|
0x949 /* not supported */,
|
|
498
585
|
);
|
|
499
586
|
|
|
587
|
+
// We check the document's metadata to see if there is a minVersionForCollab. If it's not an existing document or
|
|
588
|
+
// if the document is older, then it won't have one. If it does have a minVersionForCollab, we check if it's greater
|
|
589
|
+
// than this client's runtime version. If so, we log a telemetry event to warn the customer that the client is outdated.
|
|
590
|
+
// Note: We only send a warning because we will confirm via `checkRuntimeCompatibility` if this client **can** understand
|
|
591
|
+
// the existing document's schema. We still want to issue a warning regardless if this client can or cannot understand the
|
|
592
|
+
// schema since it may be a sign that the customer is not properly waiting for saturation before updating their
|
|
593
|
+
// `minVersionForCollab` value, which could cause disruptions to users in the future.
|
|
594
|
+
const existingMinVersionForCollab = documentMetadataSchema?.info?.minVersionForCollab;
|
|
595
|
+
if (
|
|
596
|
+
existingMinVersionForCollab !== undefined &&
|
|
597
|
+
gt(existingMinVersionForCollab, pkgVersion) &&
|
|
598
|
+
// We also want to avoid sending the telemetry warning for dev builds, since they currently are formatted as
|
|
599
|
+
// `0.0.0-#####-test`. This will cause the telemetry warning to constantly fire.
|
|
600
|
+
// TODO: This can be removed after ADO:41351
|
|
601
|
+
!isDevBuild(pkgVersion)
|
|
602
|
+
) {
|
|
603
|
+
const warnMsg = `WARNING: The version of Fluid Framework used by this client (${pkgVersion}) is not supported by this document! Please upgrade to version ${existingMinVersionForCollab} or later to ensure compatibility.`;
|
|
604
|
+
logger.sendTelemetryEvent({
|
|
605
|
+
eventName: "MinVersionForCollabWarning",
|
|
606
|
+
message: warnMsg,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
500
610
|
// Desired schema by this session - almost all props are coming from arguments
|
|
501
611
|
this.desiredSchema = {
|
|
502
612
|
version: currentDocumentVersionSchema,
|
|
503
613
|
refSeq: documentMetadataSchema?.refSeq ?? 0,
|
|
614
|
+
info,
|
|
504
615
|
runtime: {
|
|
505
616
|
explicitSchemaControl: boolToProp(features.explicitSchemaControl),
|
|
506
617
|
compressionLz4: boolToProp(features.compressionLz4),
|
|
@@ -520,6 +631,7 @@ export class DocumentsSchemaController {
|
|
|
520
631
|
version: currentDocumentVersionSchema,
|
|
521
632
|
// see comment in summarizeDocumentSchema() on why it has to stay zero
|
|
522
633
|
refSeq: 0,
|
|
634
|
+
info,
|
|
523
635
|
// If it's existing document and it has no schema, then it was written by legacy client.
|
|
524
636
|
// If it's a new document, then we define it's legacy-related behaviors.
|
|
525
637
|
runtime: {
|
|
@@ -662,7 +774,7 @@ export class DocumentsSchemaController {
|
|
|
662
774
|
const schema = {
|
|
663
775
|
...content,
|
|
664
776
|
refSeq: sequenceNumber,
|
|
665
|
-
} satisfies
|
|
777
|
+
} satisfies IDocumentSchemaCurrentIncoming;
|
|
666
778
|
this.documentSchema = schema;
|
|
667
779
|
this.sessionSchema = and(schema, this.desiredSchema);
|
|
668
780
|
assert(this.sessionSchema.refSeq === sequenceNumber, 0x97d /* seq# */);
|
|
@@ -693,4 +805,12 @@ export class DocumentsSchemaController {
|
|
|
693
805
|
}
|
|
694
806
|
}
|
|
695
807
|
|
|
808
|
+
/**
|
|
809
|
+
* Determines if a given version is a dev-build (i.e. `0.0.0-#####-test`).
|
|
810
|
+
*/
|
|
811
|
+
function isDevBuild(version: string): boolean {
|
|
812
|
+
const parsed = parse(version);
|
|
813
|
+
return parsed !== null && parsed.prerelease.includes("test");
|
|
814
|
+
}
|
|
815
|
+
|
|
696
816
|
/* eslint-enable jsdoc/check-indentation */
|