Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@fluidframework/container-loader
Advanced tools
Topics covered below:
Related topics covered elsewhere:
When taking a dependency on a Fluid Framework library's public APIs, we recommend using a ^
(caret) version range, such as ^1.3.4
.
While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
library consumers should always prefer ^
.
If using any of Fluid Framework's unstable APIs (for example, its beta
APIs), we recommend using a more constrained version range, such as ~
.
To get started, install the package by running the following command:
npm i @fluidframework/container-loader
This package leverages package.json exports to separate its APIs by support level. For more information on the related support guarantees, see API Support Levels.
To access the public
(SemVer) APIs, import via @fluidframework/container-loader
like normal.
To access the legacy
APIs, import via @fluidframework/container-loader/legacy
.
API documentation for @fluidframework/container-loader is available at https://fluidframework.com/docs/apis/container-loader.
The loader makes up the minimal kernel of the Fluid runtime. This kernel is responsible for providing access to Fluid storage as well as consensus over a quorum of clients.
Storage includes snapshots as well as the live and persisted operation stream.
The consensus system allows clients within the collaboration window to agree on container's properties. One example of this is the npm package that should be loaded to process operations applied to the container.
It's expected that host will listen to various events described in other sections of this document and conveys correctly (in some form) information to the user to ensure that user is aware of various situations and is not going to lose data. Please see specific sections for more details on these states and events - this section only serves as a summary and does not go into details.
Container.forceReadonly(true)
IContainer.dispose(...)
API to free up resources. If you intend on using the container after closure, or need to pass some critical error to the container, use the IContainer.close(...)
API.Container is returned as result of Loader.resolve() call. Loader can cache containers, so if same URI is requested from same loader instance, earlier created container might be returned. This is important, as some of the headers (like pause
) might be ignored because of Container reuse.
ILoaderHeader
in loader.ts describes properties controlling container loading.
Usually container is returned when state of container (and data stores) is rehydrated from snapshot. Unless IRequest.headers.pause
is specified, connection to ordering service will be established at some point (asynchronously) and latest Ops would be processed, allowing local changes to flow form client to server. Container.connectionState
indicates whether connection to ordering service is established, and Connectivity events are notifying about connectivity changes. While it's highly recommended for listeners to check initial state at the moment they register for connectivity events, new listeners are called on registration to propagate current state. That is, if a container is disconnected when both "connected" and "disconnected" listeners are installed, newly installed listeners for "disconnected" event will be called on registration.
Container can be closed directly by host by calling Container.close()
and/or Container.dispose()
. If the container is expected to be used upon closure, or you need to pass your own critical error to the container, use the close()
API. Otherwise, use the dispose()
API. The differences between these methods are detailed in the sections below.
Container.close()
Once closed, container terminates connection to ordering service, and any local changes (former or future) do not propagate to storage. This method is to be used when the container IS still expected to be used and the container needs to be switched to a "safe" state for viewing. For example, allowing a user to copy the content out of a container.
The "closed" state effectively means the container is disconnected forever and cannot be reconnected.
If after some time a closed container is no longer needed, calling Container.dispose()
will dispose the runtime resources.
Container can also be closed and/or disposed by runtime itself as result of some critical error. Critical errors can be internal (like violation in op ordering invariants), or external (file was deleted). Please see Error Handling for more details.
When container is closed, the following is true (in no particular order):
"closed"
event is available on Container for hosts.
Container.dispose()
Once disposed, container terminates connection to ordering service, and any local changes (former or future) do not propagate to storage. This method is to be used when the container is NOT expected to be used anymore.
When container is disposed, the following is true (in no particular order):
"disposed"
event is available on Container for hosts. "dispose"
event is delivered to container runtime when container is disposed, but container runtime can be also disposed when new code proposal is made and new version of the code (and container runtime) is loaded in accordance with it.
Container.audience
exposes an object that tracks all connected clients to same container.
getMembers()
can be used to retrieve current set of usersgetMember()
can be used to get IClient information about particular client (returns undefined if such client is not connected)"addMember"
event is raised when new member joins"removeMember"
event is raised when an earlier connected member leaves (disconnects from container)getMembers()
and "addMember"
event provide IClient interface that describes type of connection, permissions and user information:
Please note that if this client losses connection to ordering server, then audience information is not reset at that moment. It will become stale while client is disconnected, and will refresh the moment client connects back to container. For more details, please see Connectivity events section
Container.clientId
exposes ID of a client. Ordering service assigns unique random IDs to all connected clients. Please note that if same user opened same container on 3 different machines, then there would be 3 clientIDs tracking 3 sessions for the same user.
A single user connecting to a container may result in multiple sessions for the container (and thus multiple clientID). This is due to various agents (including summarizing agents) working along humans. You can leverage IClient.details.capabilities.interactive
to differentiate humans vs. agents. This property should be used to filter out bots when exposing user presence (like in coauth gallery)
IClient.user represents user ID (in storage) and can be used to identify sessions from same user (from same or different machines).
There are two ways errors are exposed:
"closed"
event on container, when container is closed due to critical error."warning"
event on container.Critical errors can show up in #1 & #2 workflows. For example, data store URI may point to a deleted file, which will result in errors on container open. But file can also be deleted while container is opened, resulting in same error type being raised through "error" handler.
Errors are of ICriticalContainerError type, and warnings are of ContainerWarning type. Both have errorType
property, describing type of an error (and appropriate interface of error object):
readonly errorType: string;
There are 4 sources of errors:
"summarizingError"
, "dataCorruptionError"
. This class of errors is not pre-determined and depends on type of container loaded.ICriticalContainerError.errorType
is a string, which represents a union of 4 error types described above. Hosting application may package different drivers and open different types of containers, and only hosting application may have enough information to enumerate all possible error codes in such scenarios.
Hosts must listen to "closed"
event. If error object is present there, container was closed due to error and this information needs to be communicated to user in some way. If there is no error object, it was closed due to host application calling Container.close() (without specifying error).
When container is closed, it is no longer connected to ordering service. It is also in read-only state, communicating to data stores not to allow user to make changes to container.
Container raises two events to notify hosting application about connectivity issues and connectivity status.
"connected"
event is raised when container is connected and is up-to-date, i.e. changes are flowing between client and server."disconnected"
event is raised when container lost connectivity (for any reason).Container also exposes Container.connectionState
property to indicate current state.
In normal circumstances, container will attempt to reconnect back to ordering service as quickly as possible. But it will scale down retries if computer is offline. That said, if IThrottlingWarning is raised through "warning"
handler, then container is following storage throttling policy and will attempt to reconnect after some amount of time (IThrottlingWarning.retryAfterSeconds
).
Container will also not attempt to reconnect on lost connection if Container.disconnect()
was called prior to loss of connection. This can be useful if the hosting application implements "user away" type of experience to reduce cost on both client and server of maintaining connection while user is away. Calling Container.connect()
will reenable automatic reconnections, but the host might need to allow extra time for reconnection as it likely involves token fetch and processing of a lot of Ops generated by other clients while it was not connected.
Data stores should almost never listen to these events (see more on Readonly states), and should use consensus DDSes if they need to synchronize activity across clients. DDSes listen for these events to know when to resubmit pending Ops.
Hosting application can use these events in order to indicate to user when user changes are not propagating through the system, and thus can be lost (on browser tab being closed). It's advised to use some delay (like 5 seconds) before showing such UI, as network connectivity might be intermittent. Also if container was offline for very long period of time due to Container.disconnect()
being called, it might take a while to get connected and current.
Please note that hosts can implement various strategies on how to handle disconnections. Some may decide to show some UX letting user know about potential loss of data if container is closed while disconnected. Others can force container to disallow user edits while offline (see Readonly states).
It's worth pointing out that being connected does not mean all user edits are preserved on container closure. There is latency in the system, and loader layer does not provide any guarantees here. Not every implementation needs a solution here (games likely do not care), and thus solving this problem is pushed to framework level (i.e. having a data store that can expose 'dirtyDocument'
signal from ContainerRuntime and request route that can return such data store).
flowchart TD;
A(Disconnected)-->B{Reconnect on error if \n AutoReconnect Enabled?};
B--Yes-->C(Establishing Connection);
B--No-->D[Connection during Container \n connect call];
D-->C
C-->E{Connection Success \n including any Retry?};
E--No-->F[Error or container.close or container.disconnect];
A-->F;
F-->A;
E--Yes-->G(Catching Up);
G-->F;
G-->H{Which Connection Mode?};
H--Read-->I(Connected);
H--Write-->J[Wait for Join Op];
J-->I;
I-->F;
User permissions can change over lifetime of Container. They can't change during single connection session (in other words, change in permissions causes disconnect and reconnect). Hosts are advised to recheck this property on every reconnect.
DeltaManager will emit a "readonly"
event when transitioning to a read-only state. Readonly events are accessible by data stores and DDSes (through ContainerRuntime.deltaManager). It's expected that data stores adhere to requirements and expose read-only (or rather 'no edit') experiences.
Container.readOnlyInfo
(and DeltaManager.readOnlyInfo
) indicates to the host if the file is writable or not.
It contains the following properties:
readonly
One of the following:
true
: Container is read-only. One or more of the additional properties listed below will be true
.undefined
: Runtime does not know yet if file is writable or not. Currently we get a signal here only when websocket connection is made to the server.false
: Container.forceReadonly() was never called or last call was with false, plus it's known that user has write permissions to a file.permissions
There are two cases when it's true
:
forced
true
if the Container is in read-only mode due to the host calling Container.forceReadonly(true)
.
This can be useful in scenarios like:
storageOnly
Storage-only mode is a read-only mode in which the container does not connect to the delta stream and is unable to submit or receive ops. This is useful for viewing a specific version of a document.
The Container runtime can communicate with the container to get the container's current state. In response, the container will raise two events - "dirty"
and "saved"
events. Transitions between these two events signify presence (or lack of) user changes that were not saved to storage. In other words, if container is dirty, closing it at that moment will result in data loss from user perspective, because not all user changes made it to storage.
This information can be used by a host to build appropriate UX that allows user to be confident in the platform. For example, a host may chose to show a dialog asking the user if they want to save their changes before closing. Instead of, or in addition to this, the host may choose to show "Saving..." and "Saved" text somewhere in UX. Coupled with lack of connectivity to ordering service (and appropriate notification to the user) that may create enough continuous notification to user not to require a blocking dialog on closing.
Note that when an active connection is in place, it's just a matter of time before changes will be flushed to storage unless there is some source of continuous local changes being generated that prevents container from ever being fully saved. But if there is no active connection, because the user is offline, for example, then a document may stay in a dirty state for very long time.
Container.isDirty
can be used to get current state of container.
These are the platform requirements for the current version of Fluid Framework Client Packages. These requirements err on the side of being too strict since within a major version they can be relaxed over time, but not made stricter. For Long Term Support (LTS) versions this can require supporting these platforms for several years.
It is likely that other configurations will work, but they are not supported: if they stop working, we do not consider that a bug. If you would benefit from support for something not listed here, file an issue and the product team will evaluate your request. When making such a request please include if the configuration already works (and thus the request is just that it becomes officially supported), or if changes are required to get it working.
strict
options are supported.strictNullChecks
is required.exactOptionalPropertyTypes
is currently not fully supported.
If used, narrowing members of Fluid Framework types types using in
, Reflect.has
, Object.hasOwn
or Object.prototype.hasOwnProperty
should be avoided as they may incorrectly exclude undefined
from the possible values in some cases.Node16
, NodeNext
, or Bundler
resolution should be used with TypeScript compilerOptions to follow the Node.js v12+ ESM Resolution and Loading algorithm.
Node10 resolution is not supported as it does not support Fluid Framework's API structuring pattern that is used to distinguish stable APIs from those that are in development.
ES Modules: ES Modules are the preferred way to consume our client packages (including in NodeJs) and consuming our client packages from ES Modules is fully supported.
CommonJs: Consuming our client packages as CommonJs is supported only in NodeJS and only for the cases listed below. This is done to accommodate some workflows without good ES Module support. If you have a workflow you would like included in this list, file an issue. Once this list of workflows motivating CommonJS support is empty, we may drop support for CommonJS one year after notice of the change is posted here.
There are many ways to contribute to Fluid.
Detailed instructions for working in the repo can be found in the Wiki.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
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. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Not finding what you're looking for in this README? Check out fluidframework.com.
Still not finding what you're looking for? Please file an issue.
Thank you!
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.
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
FAQs
Fluid container loader
We found that @fluidframework/container-loader demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.