You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@fluidframework/runtime-utils

Package Overview
Dependencies
Maintainers
3
Versions
662
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluidframework/runtime-utils - npm Package Compare versions

Comparing version
0.40.0-25719
to
0.40.0-25851
+1
-1
dist/packageVersion.d.ts

@@ -8,3 +8,3 @@ /*!

export declare const pkgName = "@fluidframework/runtime-utils";
export declare const pkgVersion = "0.40.0-25719";
export declare const pkgVersion = "0.40.0-25851";
//# sourceMappingURL=packageVersion.d.ts.map

@@ -11,3 +11,3 @@ "use strict";

exports.pkgName = "@fluidframework/runtime-utils";
exports.pkgVersion = "0.40.0-25719";
exports.pkgVersion = "0.40.0-25851";
//# sourceMappingURL=packageVersion.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,+BAA+B,CAAC;AAC1C,QAAA,UAAU,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/runtime-utils\";\nexport const pkgVersion = \"0.40.0-25719\";\n"]}
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,+BAA+B,CAAC;AAC1C,QAAA,UAAU,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/runtime-utils\";\nexport const pkgVersion = \"0.40.0-25851\";\n"]}

@@ -14,6 +14,27 @@ /*!

get IFluidSerializer(): this;
/**
* Given a mostly-jsonable object tree that may have handle objects embedded within, will return a
* fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* Any unbound handles encountered are bound to the provided IFluidHandle.
*/
replaceHandles(input: any, bind: IFluidHandle): any;
/**
* Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an
* equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* The decoded handles are implicitly bound to the handle context of this serializer.
*/
decode(input: any): any;
stringify(input: any, bind: IFluidHandle): string;
parse(input: string): any;
private recursivelyReplaceHandles;
private readonly encodeValue;
private readonly decodeValue;
private recursivelyReplace;
protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle): {

@@ -20,0 +41,0 @@ type: string;

@@ -1,1 +0,1 @@

{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EACnB,MAAM,iCAAiC,CAAC;AAKzC;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAGjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAF3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEP,OAAO,EAAE,mBAAmB;IAOhE,IAAW,gBAAgB,SAAmB;IAEvC,cAAc,CACjB,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,YAAY;IAUf,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY;IAgBxC,KAAK,CAAC,KAAK,EAAE,MAAM;IAqB1B,OAAO,CAAC,yBAAyB;IA2CjC,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY;;;;CAOrE"}
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACH,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EACnB,MAAM,iCAAiC,CAAC;AAKzC;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAGjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAF3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEP,OAAO,EAAE,mBAAmB;IAOhE,IAAW,gBAAgB,SAAmB;IAE9C;;;;;;;;OAQG;IACK,cAAc,CAClB,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,YAAY;IAUtB;;;;;;;;OAQG;IACK,MAAM,CAAC,KAAK,EAAE,GAAG;IASlB,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY;IAKxC,KAAK,CAAC,KAAK,EAAE,MAAM;IAM1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAQ1B;IAIF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAa1B;IAKF,OAAO,CAAC,kBAAkB;IAgD1B,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY;;;;CAOrE"}

@@ -17,2 +17,28 @@ "use strict";

this.context = context;
// If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'replaceHandles()' and 'stringify()'.
this.encodeValue = (value, bind) => {
// Detect if 'value' is an IFluidHandle.
const handle = value === null || value === void 0 ? void 0 : value.IFluidHandle;
// If 'value' is an IFluidHandle return its encoded form.
return handle !== undefined
? this.serializeHandle(handle, bind)
: value;
};
// If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.
this.decodeValue = (value) => {
// If 'value' is a serialized IFluidHandle return the deserialized result.
if (utils_1.isSerializedHandle(value)) {
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: dataStoreHandleContextUtils_1.generateHandleContextPath(value.url, this.context);
return new remoteObjectHandle_1.RemoteFluidObjectHandle(absolutePath, this.root);
}
else {
return value;
}
};
this.root = this.context;

@@ -24,50 +50,58 @@ while (this.root.routeContext !== undefined) {

get IFluidSerializer() { return this; }
/**
* Given a mostly-jsonable object tree that may have handle objects embedded within, will return a
* fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* Any unbound handles encountered are bound to the provided IFluidHandle.
*/
replaceHandles(input, bind) {
// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplaceHandles()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplaceHandles(input, bind)
? this.recursivelyReplace(input, this.encodeValue, bind)
: input;
}
/**
* Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an
* equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* The decoded handles are implicitly bound to the handle context of this serializer.
*/
decode(input) {
// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplace(input, this.decodeValue)
: input;
}
stringify(input, bind) {
return JSON.stringify(input, (key, value) => {
// If the current 'value' is not a handle, return it unmodified. Otherwise,
// return the result of 'serializeHandle'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const handle = !!value && value.IFluidHandle;
// TODO - understand why handle === false in some of our tests
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
return handle
? this.serializeHandle(handle, bind)
: value;
});
return JSON.stringify(input, (key, value) => this.encodeValue(value, bind));
}
// Parses the serialized data - context must match the context with which the JSON was stringified
parse(input) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(input, (key, value) => {
if (!utils_1.isSerializedHandle(value)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value;
}
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: dataStoreHandleContextUtils_1.generateHandleContextPath(value.url, this.context);
return new remoteObjectHandle_1.RemoteFluidObjectHandle(absolutePath, this.root);
});
return JSON.parse(input, (key, value) => this.decodeValue(value));
}
// Invoked by `replaceHandles()` for non-null objects to recursively replace IFluidHandle references
// with serialized handles (cloning as-needed to avoid mutating the original `input` object.)
recursivelyReplaceHandles(input, bind) {
// If the current input is an IFluidHandle instance, replace this leaf in the object graph with
// the handle's serialized from.
// Note: Caller is responsible for ensuring that `input` is a non-null object.
const handle = input.IFluidHandle;
if (handle !== undefined) {
return this.serializeHandle(handle, bind);
// Invoked for non-null objects to recursively replace references to IFluidHandles.
// Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present,
// returns the original `input`.
recursivelyReplace(input, replacer, context) {
// Note: Caller is responsible for ensuring that `input` is defined / non-null.
// (Required for Object.keys() below.)
// Execute the `replace` on the current input. Note that Caller is responsible for ensuring that `input`
// is a non-null object.
const maybeReplaced = replacer(input, context);
// If the replacer made a substitution there is no need to decscend further. IFluidHandles are always
// leaves in the object graph.
if (maybeReplaced !== input) {
return maybeReplaced;
}
// Otherwise descend into the object graph looking for IFluidHandle instances.
// eslint-disable-next-line @typescript-eslint/ban-types

@@ -82,3 +116,3 @@ let clone;

// lead to a later error when attempting to stringify().
const replaced = this.recursivelyReplaceHandles(value, bind);
const replaced = this.recursivelyReplace(value, replacer, context);
// If the `replaced` object is different than the original `value` then the subgraph contained one

@@ -98,3 +132,2 @@ // or more handles. If this happens, we need to return a clone of the `input` object where the

}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return clone !== null && clone !== void 0 ? clone : input;

@@ -101,0 +134,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n IFluidHandle,\n IFluidHandleContext,\n IFluidSerializer,\n} from \"@fluidframework/core-interfaces\";\nimport { RemoteFluidObjectHandle } from \"./remoteObjectHandle\";\nimport { generateHandleContextPath } from \"./dataStoreHandleContextUtils\";\nimport { isSerializedHandle } from \"./utils\";\n\n/**\n * Data Store serializer implementation\n */\nexport class FluidSerializer implements IFluidSerializer {\n private readonly root: IFluidHandleContext;\n\n public constructor(private readonly context: IFluidHandleContext) {\n this.root = this.context;\n while (this.root.routeContext !== undefined) {\n this.root = this.root.routeContext;\n }\n }\n\n public get IFluidSerializer() { return this; }\n\n public replaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplaceHandles()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplaceHandles(input, bind)\n : input;\n }\n\n public stringify(input: any, bind: IFluidHandle) {\n return JSON.stringify(input, (key, value) => {\n // If the current 'value' is not a handle, return it unmodified. Otherwise,\n // return the result of 'serializeHandle'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n const handle = !!value && value.IFluidHandle;\n // TODO - understand why handle === false in some of our tests\n // eslint-disable-next-line max-len\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return\n return handle\n ? this.serializeHandle(handle, bind)\n : value;\n });\n }\n\n // Parses the serialized data - context must match the context with which the JSON was stringified\n public parse(input: string) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(\n input,\n (key, value) => {\n if (!isSerializedHandle(value)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value;\n }\n\n // Old documents may have handles with relative path in their summaries. Convert these to absolute\n // paths. This will ensure that future summaries will have absolute paths for these handles.\n const absolutePath = value.url.startsWith(\"/\")\n ? value.url\n : generateHandleContextPath(value.url, this.context);\n return new RemoteFluidObjectHandle(absolutePath, this.root);\n });\n }\n\n // Invoked by `replaceHandles()` for non-null objects to recursively replace IFluidHandle references\n // with serialized handles (cloning as-needed to avoid mutating the original `input` object.)\n private recursivelyReplaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the current input is an IFluidHandle instance, replace this leaf in the object graph with\n // the handle's serialized from.\n\n // Note: Caller is responsible for ensuring that `input` is a non-null object.\n const handle = input.IFluidHandle;\n if (handle !== undefined) {\n return this.serializeHandle(handle, bind);\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-types\n let clone: object | undefined;\n for (const key of Object.keys(input)) {\n const value = input[key];\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n if (!!value && typeof value === \"object\") {\n // Note: Except for IFluidHandle, `input` must not contain circular references (as object must\n // be JSON serializable.) Therefore, guarding against infinite recursion here would only\n // lead to a later error when attempting to stringify().\n const replaced = this.recursivelyReplaceHandles(value, bind);\n\n // If the `replaced` object is different than the original `value` then the subgraph contained one\n // or more handles. If this happens, we need to return a clone of the `input` object where the\n // current property is replaced by the `replaced` value.\n if (replaced !== value) {\n // Lazily create a shallow clone of the `input` object if we haven't done so already.\n clone = clone ?? (Array.isArray(input)\n ? [...input]\n : { ...input });\n\n // Overwrite the current property `key` in the clone with the `replaced` value.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n clone![key] = replaced;\n }\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return clone ?? input;\n }\n\n protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle) {\n bind.bind(handle);\n return {\n type: \"__fluid_handle__\",\n url: handle.absolutePath,\n };\n }\n}\n"]}
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// RATIONALE: Many methods consume and return 'any' by necessity.\n/* eslint-disable @typescript-eslint/no-unsafe-return */\n\nimport {\n IFluidHandle,\n IFluidHandleContext,\n IFluidSerializer,\n} from \"@fluidframework/core-interfaces\";\nimport { RemoteFluidObjectHandle } from \"./remoteObjectHandle\";\nimport { generateHandleContextPath } from \"./dataStoreHandleContextUtils\";\nimport { isSerializedHandle } from \"./utils\";\n\n/**\n * Data Store serializer implementation\n */\nexport class FluidSerializer implements IFluidSerializer {\n private readonly root: IFluidHandleContext;\n\n public constructor(private readonly context: IFluidHandleContext) {\n this.root = this.context;\n while (this.root.routeContext !== undefined) {\n this.root = this.root.routeContext;\n }\n }\n\n public get IFluidSerializer() { return this; }\n\n /**\n * Given a mostly-jsonable object tree that may have handle objects embedded within, will return a\n * fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * Any unbound handles encountered are bound to the provided IFluidHandle.\n */\n public replaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplace()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplace(input, this.encodeValue, bind)\n : input;\n }\n\n /**\n * Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an\n * equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * The decoded handles are implicitly bound to the handle context of this serializer.\n */\n public decode(input: any) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplace()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplace(input, this.decodeValue)\n : input;\n }\n\n public stringify(input: any, bind: IFluidHandle) {\n return JSON.stringify(input, (key, value) => this.encodeValue(value, bind));\n }\n\n // Parses the serialized data - context must match the context with which the JSON was stringified\n public parse(input: string) {\n return JSON.parse(input, (key, value) => this.decodeValue(value));\n }\n\n // If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.\n // Otherwise returns the original 'value'. Used by 'replaceHandles()' and 'stringify()'.\n private readonly encodeValue = (value: any, bind: IFluidHandle) => {\n // Detect if 'value' is an IFluidHandle.\n const handle = value?.IFluidHandle;\n\n // If 'value' is an IFluidHandle return its encoded form.\n return handle !== undefined\n ? this.serializeHandle(handle, bind)\n : value;\n };\n\n // If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.\n // Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.\n private readonly decodeValue = (value: any) => {\n // If 'value' is a serialized IFluidHandle return the deserialized result.\n if (isSerializedHandle(value)) {\n // Old documents may have handles with relative path in their summaries. Convert these to absolute\n // paths. This will ensure that future summaries will have absolute paths for these handles.\n const absolutePath = value.url.startsWith(\"/\")\n ? value.url\n : generateHandleContextPath(value.url, this.context);\n\n return new RemoteFluidObjectHandle(absolutePath, this.root);\n } else {\n return value;\n }\n };\n\n // Invoked for non-null objects to recursively replace references to IFluidHandles.\n // Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present,\n // returns the original `input`.\n private recursivelyReplace(\n input: any,\n replacer: (input: any, context: any) => any,\n context?: any,\n ) {\n // Note: Caller is responsible for ensuring that `input` is defined / non-null.\n // (Required for Object.keys() below.)\n\n // Execute the `replace` on the current input. Note that Caller is responsible for ensuring that `input`\n // is a non-null object.\n const maybeReplaced = replacer(input, context);\n\n // If the replacer made a substitution there is no need to decscend further. IFluidHandles are always\n // leaves in the object graph.\n if (maybeReplaced !== input) {\n return maybeReplaced;\n }\n\n // Otherwise descend into the object graph looking for IFluidHandle instances.\n // eslint-disable-next-line @typescript-eslint/ban-types\n let clone: object | undefined;\n for (const key of Object.keys(input)) {\n const value = input[key];\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n if (!!value && typeof value === \"object\") {\n // Note: Except for IFluidHandle, `input` must not contain circular references (as object must\n // be JSON serializable.) Therefore, guarding against infinite recursion here would only\n // lead to a later error when attempting to stringify().\n const replaced = this.recursivelyReplace(value, replacer, context);\n\n // If the `replaced` object is different than the original `value` then the subgraph contained one\n // or more handles. If this happens, we need to return a clone of the `input` object where the\n // current property is replaced by the `replaced` value.\n if (replaced !== value) {\n // Lazily create a shallow clone of the `input` object if we haven't done so already.\n clone = clone ?? (Array.isArray(input)\n ? [...input]\n : { ...input });\n\n // Overwrite the current property `key` in the clone with the `replaced` value.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n clone![key] = replaced;\n }\n }\n }\n return clone ?? input;\n }\n\n protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle) {\n bind.bind(handle);\n return {\n type: \"__fluid_handle__\",\n url: handle.absolutePath,\n };\n }\n}\n"]}

@@ -8,3 +8,3 @@ /*!

export declare const pkgName = "@fluidframework/runtime-utils";
export declare const pkgVersion = "0.40.0-25719";
export declare const pkgVersion = "0.40.0-25851";
//# sourceMappingURL=packageVersion.d.ts.map

@@ -8,3 +8,3 @@ /*!

export const pkgName = "@fluidframework/runtime-utils";
export const pkgVersion = "0.40.0-25719";
export const pkgVersion = "0.40.0-25851";
//# sourceMappingURL=packageVersion.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,+BAA+B,CAAC;AACvD,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/runtime-utils\";\nexport const pkgVersion = \"0.40.0-25719\";\n"]}
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,+BAA+B,CAAC;AACvD,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/runtime-utils\";\nexport const pkgVersion = \"0.40.0-25851\";\n"]}

@@ -14,6 +14,27 @@ /*!

get IFluidSerializer(): this;
/**
* Given a mostly-jsonable object tree that may have handle objects embedded within, will return a
* fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* Any unbound handles encountered are bound to the provided IFluidHandle.
*/
replaceHandles(input: any, bind: IFluidHandle): any;
/**
* Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an
* equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* The decoded handles are implicitly bound to the handle context of this serializer.
*/
decode(input: any): any;
stringify(input: any, bind: IFluidHandle): string;
parse(input: string): any;
private recursivelyReplaceHandles;
private readonly encodeValue;
private readonly decodeValue;
private recursivelyReplace;
protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle): {

@@ -20,0 +41,0 @@ type: string;

@@ -1,1 +0,1 @@

{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EACnB,MAAM,iCAAiC,CAAC;AAKzC;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAGjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAF3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEP,OAAO,EAAE,mBAAmB;IAOhE,IAAW,gBAAgB,SAAmB;IAEvC,cAAc,CACjB,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,YAAY;IAUf,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY;IAgBxC,KAAK,CAAC,KAAK,EAAE,MAAM;IAqB1B,OAAO,CAAC,yBAAyB;IA2CjC,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY;;;;CAOrE"}
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACH,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EACnB,MAAM,iCAAiC,CAAC;AAKzC;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAGjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAF3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;gBAEP,OAAO,EAAE,mBAAmB;IAOhE,IAAW,gBAAgB,SAAmB;IAE9C;;;;;;;;OAQG;IACK,cAAc,CAClB,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,YAAY;IAUtB;;;;;;;;OAQG;IACK,MAAM,CAAC,KAAK,EAAE,GAAG;IASlB,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY;IAKxC,KAAK,CAAC,KAAK,EAAE,MAAM;IAM1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAQ1B;IAIF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAa1B;IAKF,OAAO,CAAC,kBAAkB;IAgD1B,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY;;;;CAOrE"}

@@ -14,2 +14,28 @@ /*!

this.context = context;
// If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'replaceHandles()' and 'stringify()'.
this.encodeValue = (value, bind) => {
// Detect if 'value' is an IFluidHandle.
const handle = value === null || value === void 0 ? void 0 : value.IFluidHandle;
// If 'value' is an IFluidHandle return its encoded form.
return handle !== undefined
? this.serializeHandle(handle, bind)
: value;
};
// If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.
this.decodeValue = (value) => {
// If 'value' is a serialized IFluidHandle return the deserialized result.
if (isSerializedHandle(value)) {
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: generateHandleContextPath(value.url, this.context);
return new RemoteFluidObjectHandle(absolutePath, this.root);
}
else {
return value;
}
};
this.root = this.context;

@@ -21,50 +47,58 @@ while (this.root.routeContext !== undefined) {

get IFluidSerializer() { return this; }
/**
* Given a mostly-jsonable object tree that may have handle objects embedded within, will return a
* fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* Any unbound handles encountered are bound to the provided IFluidHandle.
*/
replaceHandles(input, bind) {
// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplaceHandles()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplaceHandles(input, bind)
? this.recursivelyReplace(input, this.encodeValue, bind)
: input;
}
/**
* Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an
* equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* The decoded handles are implicitly bound to the handle context of this serializer.
*/
decode(input) {
// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplace(input, this.decodeValue)
: input;
}
stringify(input, bind) {
return JSON.stringify(input, (key, value) => {
// If the current 'value' is not a handle, return it unmodified. Otherwise,
// return the result of 'serializeHandle'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const handle = !!value && value.IFluidHandle;
// TODO - understand why handle === false in some of our tests
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
return handle
? this.serializeHandle(handle, bind)
: value;
});
return JSON.stringify(input, (key, value) => this.encodeValue(value, bind));
}
// Parses the serialized data - context must match the context with which the JSON was stringified
parse(input) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(input, (key, value) => {
if (!isSerializedHandle(value)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value;
}
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: generateHandleContextPath(value.url, this.context);
return new RemoteFluidObjectHandle(absolutePath, this.root);
});
return JSON.parse(input, (key, value) => this.decodeValue(value));
}
// Invoked by `replaceHandles()` for non-null objects to recursively replace IFluidHandle references
// with serialized handles (cloning as-needed to avoid mutating the original `input` object.)
recursivelyReplaceHandles(input, bind) {
// If the current input is an IFluidHandle instance, replace this leaf in the object graph with
// the handle's serialized from.
// Note: Caller is responsible for ensuring that `input` is a non-null object.
const handle = input.IFluidHandle;
if (handle !== undefined) {
return this.serializeHandle(handle, bind);
// Invoked for non-null objects to recursively replace references to IFluidHandles.
// Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present,
// returns the original `input`.
recursivelyReplace(input, replacer, context) {
// Note: Caller is responsible for ensuring that `input` is defined / non-null.
// (Required for Object.keys() below.)
// Execute the `replace` on the current input. Note that Caller is responsible for ensuring that `input`
// is a non-null object.
const maybeReplaced = replacer(input, context);
// If the replacer made a substitution there is no need to decscend further. IFluidHandles are always
// leaves in the object graph.
if (maybeReplaced !== input) {
return maybeReplaced;
}
// Otherwise descend into the object graph looking for IFluidHandle instances.
// eslint-disable-next-line @typescript-eslint/ban-types

@@ -79,3 +113,3 @@ let clone;

// lead to a later error when attempting to stringify().
const replaced = this.recursivelyReplaceHandles(value, bind);
const replaced = this.recursivelyReplace(value, replacer, context);
// If the `replaced` object is different than the original `value` then the subgraph contained one

@@ -95,3 +129,2 @@ // or more handles. If this happens, we need to return a clone of the `input` object where the

}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return clone !== null && clone !== void 0 ? clone : input;

@@ -98,0 +131,0 @@ }

@@ -1,1 +0,1 @@

{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n IFluidHandle,\n IFluidHandleContext,\n IFluidSerializer,\n} from \"@fluidframework/core-interfaces\";\nimport { RemoteFluidObjectHandle } from \"./remoteObjectHandle\";\nimport { generateHandleContextPath } from \"./dataStoreHandleContextUtils\";\nimport { isSerializedHandle } from \"./utils\";\n\n/**\n * Data Store serializer implementation\n */\nexport class FluidSerializer implements IFluidSerializer {\n private readonly root: IFluidHandleContext;\n\n public constructor(private readonly context: IFluidHandleContext) {\n this.root = this.context;\n while (this.root.routeContext !== undefined) {\n this.root = this.root.routeContext;\n }\n }\n\n public get IFluidSerializer() { return this; }\n\n public replaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplaceHandles()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplaceHandles(input, bind)\n : input;\n }\n\n public stringify(input: any, bind: IFluidHandle) {\n return JSON.stringify(input, (key, value) => {\n // If the current 'value' is not a handle, return it unmodified. Otherwise,\n // return the result of 'serializeHandle'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n const handle = !!value && value.IFluidHandle;\n // TODO - understand why handle === false in some of our tests\n // eslint-disable-next-line max-len\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return\n return handle\n ? this.serializeHandle(handle, bind)\n : value;\n });\n }\n\n // Parses the serialized data - context must match the context with which the JSON was stringified\n public parse(input: string) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return JSON.parse(\n input,\n (key, value) => {\n if (!isSerializedHandle(value)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value;\n }\n\n // Old documents may have handles with relative path in their summaries. Convert these to absolute\n // paths. This will ensure that future summaries will have absolute paths for these handles.\n const absolutePath = value.url.startsWith(\"/\")\n ? value.url\n : generateHandleContextPath(value.url, this.context);\n return new RemoteFluidObjectHandle(absolutePath, this.root);\n });\n }\n\n // Invoked by `replaceHandles()` for non-null objects to recursively replace IFluidHandle references\n // with serialized handles (cloning as-needed to avoid mutating the original `input` object.)\n private recursivelyReplaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the current input is an IFluidHandle instance, replace this leaf in the object graph with\n // the handle's serialized from.\n\n // Note: Caller is responsible for ensuring that `input` is a non-null object.\n const handle = input.IFluidHandle;\n if (handle !== undefined) {\n return this.serializeHandle(handle, bind);\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-types\n let clone: object | undefined;\n for (const key of Object.keys(input)) {\n const value = input[key];\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n if (!!value && typeof value === \"object\") {\n // Note: Except for IFluidHandle, `input` must not contain circular references (as object must\n // be JSON serializable.) Therefore, guarding against infinite recursion here would only\n // lead to a later error when attempting to stringify().\n const replaced = this.recursivelyReplaceHandles(value, bind);\n\n // If the `replaced` object is different than the original `value` then the subgraph contained one\n // or more handles. If this happens, we need to return a clone of the `input` object where the\n // current property is replaced by the `replaced` value.\n if (replaced !== value) {\n // Lazily create a shallow clone of the `input` object if we haven't done so already.\n clone = clone ?? (Array.isArray(input)\n ? [...input]\n : { ...input });\n\n // Overwrite the current property `key` in the clone with the `replaced` value.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n clone![key] = replaced;\n }\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return clone ?? input;\n }\n\n protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle) {\n bind.bind(handle);\n return {\n type: \"__fluid_handle__\",\n url: handle.absolutePath,\n };\n }\n}\n"]}
{"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// RATIONALE: Many methods consume and return 'any' by necessity.\n/* eslint-disable @typescript-eslint/no-unsafe-return */\n\nimport {\n IFluidHandle,\n IFluidHandleContext,\n IFluidSerializer,\n} from \"@fluidframework/core-interfaces\";\nimport { RemoteFluidObjectHandle } from \"./remoteObjectHandle\";\nimport { generateHandleContextPath } from \"./dataStoreHandleContextUtils\";\nimport { isSerializedHandle } from \"./utils\";\n\n/**\n * Data Store serializer implementation\n */\nexport class FluidSerializer implements IFluidSerializer {\n private readonly root: IFluidHandleContext;\n\n public constructor(private readonly context: IFluidHandleContext) {\n this.root = this.context;\n while (this.root.routeContext !== undefined) {\n this.root = this.root.routeContext;\n }\n }\n\n public get IFluidSerializer() { return this; }\n\n /**\n * Given a mostly-jsonable object tree that may have handle objects embedded within, will return a\n * fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * Any unbound handles encountered are bound to the provided IFluidHandle.\n */\n public replaceHandles(\n input: any,\n bind: IFluidHandle,\n ) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplace()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplace(input, this.encodeValue, bind)\n : input;\n }\n\n /**\n * Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an\n * equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.\n *\n * The original `input` object is not mutated. This method will shallowly clones all objects in the path from\n * the root to any replaced handles. (If no handles are found, returns the original object.)\n *\n * The decoded handles are implicitly bound to the handle context of this serializer.\n */\n public decode(input: any) {\n // If the given 'input' cannot contain handles, return it immediately. Otherwise,\n // return the result of 'recursivelyReplace()'.\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n return !!input && typeof input === \"object\"\n ? this.recursivelyReplace(input, this.decodeValue)\n : input;\n }\n\n public stringify(input: any, bind: IFluidHandle) {\n return JSON.stringify(input, (key, value) => this.encodeValue(value, bind));\n }\n\n // Parses the serialized data - context must match the context with which the JSON was stringified\n public parse(input: string) {\n return JSON.parse(input, (key, value) => this.decodeValue(value));\n }\n\n // If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.\n // Otherwise returns the original 'value'. Used by 'replaceHandles()' and 'stringify()'.\n private readonly encodeValue = (value: any, bind: IFluidHandle) => {\n // Detect if 'value' is an IFluidHandle.\n const handle = value?.IFluidHandle;\n\n // If 'value' is an IFluidHandle return its encoded form.\n return handle !== undefined\n ? this.serializeHandle(handle, bind)\n : value;\n };\n\n // If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.\n // Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.\n private readonly decodeValue = (value: any) => {\n // If 'value' is a serialized IFluidHandle return the deserialized result.\n if (isSerializedHandle(value)) {\n // Old documents may have handles with relative path in their summaries. Convert these to absolute\n // paths. This will ensure that future summaries will have absolute paths for these handles.\n const absolutePath = value.url.startsWith(\"/\")\n ? value.url\n : generateHandleContextPath(value.url, this.context);\n\n return new RemoteFluidObjectHandle(absolutePath, this.root);\n } else {\n return value;\n }\n };\n\n // Invoked for non-null objects to recursively replace references to IFluidHandles.\n // Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present,\n // returns the original `input`.\n private recursivelyReplace(\n input: any,\n replacer: (input: any, context: any) => any,\n context?: any,\n ) {\n // Note: Caller is responsible for ensuring that `input` is defined / non-null.\n // (Required for Object.keys() below.)\n\n // Execute the `replace` on the current input. Note that Caller is responsible for ensuring that `input`\n // is a non-null object.\n const maybeReplaced = replacer(input, context);\n\n // If the replacer made a substitution there is no need to decscend further. IFluidHandles are always\n // leaves in the object graph.\n if (maybeReplaced !== input) {\n return maybeReplaced;\n }\n\n // Otherwise descend into the object graph looking for IFluidHandle instances.\n // eslint-disable-next-line @typescript-eslint/ban-types\n let clone: object | undefined;\n for (const key of Object.keys(input)) {\n const value = input[key];\n // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions\n if (!!value && typeof value === \"object\") {\n // Note: Except for IFluidHandle, `input` must not contain circular references (as object must\n // be JSON serializable.) Therefore, guarding against infinite recursion here would only\n // lead to a later error when attempting to stringify().\n const replaced = this.recursivelyReplace(value, replacer, context);\n\n // If the `replaced` object is different than the original `value` then the subgraph contained one\n // or more handles. If this happens, we need to return a clone of the `input` object where the\n // current property is replaced by the `replaced` value.\n if (replaced !== value) {\n // Lazily create a shallow clone of the `input` object if we haven't done so already.\n clone = clone ?? (Array.isArray(input)\n ? [...input]\n : { ...input });\n\n // Overwrite the current property `key` in the clone with the `replaced` value.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n clone![key] = replaced;\n }\n }\n }\n return clone ?? input;\n }\n\n protected serializeHandle(handle: IFluidHandle, bind: IFluidHandle) {\n bind.bind(handle);\n return {\n type: \"__fluid_handle__\",\n url: handle.absolutePath,\n };\n }\n}\n"]}
{
"name": "@fluidframework/runtime-utils",
"version": "0.40.0-25719",
"version": "0.40.0-25851",
"description": "Collection of utility functions for Fluid Runtime",

@@ -62,7 +62,7 @@ "homepage": "https://fluidframework.com",

"@fluidframework/core-interfaces": "^0.39.0",
"@fluidframework/datastore-definitions": "0.40.0-25719",
"@fluidframework/garbage-collector": "0.40.0-25719",
"@fluidframework/datastore-definitions": "0.40.0-25851",
"@fluidframework/garbage-collector": "0.40.0-25851",
"@fluidframework/protocol-base": "^0.1025.0-0",
"@fluidframework/protocol-definitions": "^0.1024.0",
"@fluidframework/runtime-definitions": "0.40.0-25719",
"@fluidframework/runtime-definitions": "0.40.0-25851",
"assert": "^2.0.0"

@@ -73,3 +73,3 @@ },

"@fluidframework/eslint-config-fluid": "^0.23.0",
"@fluidframework/mocha-test-setup": "0.40.0-25719",
"@fluidframework/mocha-test-setup": "0.40.0-25851",
"@microsoft/api-extractor": "^7.13.1",

@@ -76,0 +76,0 @@ "@types/assert": "^1.5.2",

@@ -9,2 +9,2 @@ /*!

export const pkgName = "@fluidframework/runtime-utils";
export const pkgVersion = "0.40.0-25719";
export const pkgVersion = "0.40.0-25851";

@@ -6,2 +6,5 @@ /*!

// RATIONALE: Many methods consume and return 'any' by necessity.
/* eslint-disable @typescript-eslint/no-unsafe-return */
import {

@@ -31,3 +34,12 @@ IFluidHandle,

public replaceHandles(
/**
* Given a mostly-jsonable object tree that may have handle objects embedded within, will return a
* fully-jsonable object tree where any embedded IFluidHandles have been replaced with a serializable form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* Any unbound handles encountered are bound to the provided IFluidHandle.
*/
public replaceHandles(
input: any,

@@ -37,22 +49,29 @@ bind: IFluidHandle,

// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplaceHandles()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplaceHandles(input, bind)
? this.recursivelyReplace(input, this.encodeValue, bind)
: input;
}
/**
* Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an
* equivalent object tree where any encoded IFluidHandles have been replaced with thier decoded form.
*
* The original `input` object is not mutated. This method will shallowly clones all objects in the path from
* the root to any replaced handles. (If no handles are found, returns the original object.)
*
* The decoded handles are implicitly bound to the handle context of this serializer.
*/
public decode(input: any) {
// If the given 'input' cannot contain handles, return it immediately. Otherwise,
// return the result of 'recursivelyReplace()'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
return !!input && typeof input === "object"
? this.recursivelyReplace(input, this.decodeValue)
: input;
}
public stringify(input: any, bind: IFluidHandle) {
return JSON.stringify(input, (key, value) => {
// If the current 'value' is not a handle, return it unmodified. Otherwise,
// return the result of 'serializeHandle'.
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const handle = !!value && value.IFluidHandle;
// TODO - understand why handle === false in some of our tests
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unsafe-return
return handle
? this.serializeHandle(handle, bind)
: value;
});
return JSON.stringify(input, (key, value) => this.encodeValue(value, bind));
}

@@ -62,35 +81,56 @@

public parse(input: string) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(
input,
(key, value) => {
if (!isSerializedHandle(value)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value;
}
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: generateHandleContextPath(value.url, this.context);
return new RemoteFluidObjectHandle(absolutePath, this.root);
});
return JSON.parse(input, (key, value) => this.decodeValue(value));
}
// Invoked by `replaceHandles()` for non-null objects to recursively replace IFluidHandle references
// with serialized handles (cloning as-needed to avoid mutating the original `input` object.)
private recursivelyReplaceHandles(
// If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'replaceHandles()' and 'stringify()'.
private readonly encodeValue = (value: any, bind: IFluidHandle) => {
// Detect if 'value' is an IFluidHandle.
const handle = value?.IFluidHandle;
// If 'value' is an IFluidHandle return its encoded form.
return handle !== undefined
? this.serializeHandle(handle, bind)
: value;
};
// If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle.
// Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'.
private readonly decodeValue = (value: any) => {
// If 'value' is a serialized IFluidHandle return the deserialized result.
if (isSerializedHandle(value)) {
// Old documents may have handles with relative path in their summaries. Convert these to absolute
// paths. This will ensure that future summaries will have absolute paths for these handles.
const absolutePath = value.url.startsWith("/")
? value.url
: generateHandleContextPath(value.url, this.context);
return new RemoteFluidObjectHandle(absolutePath, this.root);
} else {
return value;
}
};
// Invoked for non-null objects to recursively replace references to IFluidHandles.
// Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present,
// returns the original `input`.
private recursivelyReplace(
input: any,
bind: IFluidHandle,
replacer: (input: any, context: any) => any,
context?: any,
) {
// If the current input is an IFluidHandle instance, replace this leaf in the object graph with
// the handle's serialized from.
// Note: Caller is responsible for ensuring that `input` is defined / non-null.
// (Required for Object.keys() below.)
// Note: Caller is responsible for ensuring that `input` is a non-null object.
const handle = input.IFluidHandle;
if (handle !== undefined) {
return this.serializeHandle(handle, bind);
// Execute the `replace` on the current input. Note that Caller is responsible for ensuring that `input`
// is a non-null object.
const maybeReplaced = replacer(input, context);
// If the replacer made a substitution there is no need to decscend further. IFluidHandles are always
// leaves in the object graph.
if (maybeReplaced !== input) {
return maybeReplaced;
}
// Otherwise descend into the object graph looking for IFluidHandle instances.
// eslint-disable-next-line @typescript-eslint/ban-types

@@ -105,3 +145,3 @@ let clone: object | undefined;

// lead to a later error when attempting to stringify().
const replaced = this.recursivelyReplaceHandles(value, bind);
const replaced = this.recursivelyReplace(value, replacer, context);

@@ -123,3 +163,2 @@ // If the `replaced` object is different than the original `value` then the subgraph contained one

}
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return clone ?? input;

@@ -126,0 +165,0 @@ }