Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fluidframework/datastore

Package Overview
Dependencies
Maintainers
3
Versions
587
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluidframework/datastore - npm Package Compare versions

Comparing version 0.32.3 to 0.33.0-13708

11

dist/channelContext.d.ts

@@ -18,3 +18,14 @@ /*!

reSubmit(content: any, localOpMetadata: unknown): void;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context
* including any of its children. Each node has a set of outbound routes to other GC nodes in the document.
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* After GC has run, called to notify this context of routes that are used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To identify if this context or any of its children's used routes changed since last summary.
* 3. They are added to the summary generated by this context.
*/
updateUsedRoutes(usedRoutes: string[]): void;
}

@@ -21,0 +32,0 @@ export declare function createServiceEndpoints(id: string, connected: boolean, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: () => void, storageService: IDocumentStorageService, tree?: ISnapshotTree, extraBlobs?: Map<string, string>): {

3

dist/channelStorageService.d.ts

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

private readonly flattenedTree;
constructor(tree: ISnapshotTree | undefined, storage: Pick<IDocumentStorageService, "read">, extraBlobs?: Map<string, string> | undefined);
constructor(tree: ISnapshotTree | undefined, storage: Pick<IDocumentStorageService, "read" | "readBlob">, extraBlobs?: Map<string, string> | undefined);
contains(path: string): Promise<boolean>;
read(path: string): Promise<string>;
readBlob(path: string): Promise<ArrayBufferLike>;
list(path: string): Promise<string[]>;

@@ -19,0 +20,0 @@ private getIdForPath;

@@ -8,2 +8,3 @@ "use strict";

const runtime_utils_1 = require("@fluidframework/runtime-utils");
const common_utils_1 = require("@fluidframework/common-utils");
class ChannelStorageService {

@@ -40,2 +41,9 @@ constructor(tree, storage, extraBlobs) {

}
async readBlob(path) {
const id = await this.getIdForPath(path);
const blob = this.extraBlobs !== undefined
? this.extraBlobs.get(id)
: undefined;
return blob !== undefined ? common_utils_1.stringToBuffer(blob, "base64") : this.storage.readBlob(id);
}
async list(path) {

@@ -42,0 +50,0 @@ var _a, _b;

@@ -123,4 +123,20 @@ /*!

private updateGCNodes;
/**
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
* document. It does the following:
* 1. Calls into each child context to get its GC data.
* 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
* idenfied as belonging to the child.
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
* the GC data of this channel.
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
* update their used routes.
* @param usedRoutes - The routes that are used in all contexts in this channel.
*/
updateUsedRoutes(usedRoutes: string[]): void;
/**
* Returns a summary at the current sequence number.

@@ -127,0 +143,0 @@ * @param fullTree - true to bypass optimizations and force a full summary tree

@@ -402,2 +402,12 @@ "use strict";

}
/**
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
* document. It does the following:
* 1. Calls into each child context to get its GC data.
* 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
* idenfied as belonging to the child.
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
* the GC data of this channel.
*/
async getGCData() {

@@ -413,4 +423,4 @@ const builder = new garbage_collector_1.GCDataBuilder();

const contextGCData = await context.getGCData();
// Prefix the child's id to the ids of its GC nodes. This gradually builds the id of each node to be
// a path from the root.
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
// This also gradually builds the id of each node to be a path from the root.
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);

@@ -422,2 +432,20 @@ }));

/**
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
* update their used routes.
* @param usedRoutes - The routes that are used in all contexts in this channel.
*/
updateUsedRoutes(usedRoutes) {
var _a;
// Get a map of channel ids to routes used in it.
const usedContextRoutes = garbage_collector_1.getChildNodesUsedRoutes(usedRoutes);
// Verify that the used routes are correct.
for (const [id] of usedContextRoutes) {
common_utils_1.assert(this.contexts.has(id), "Used route does not belong to any known context");
}
// Update the used routes in each context. Used routes is empty for unused context.
for (const [contextId, context] of this.contexts) {
context.updateUsedRoutes((_a = usedContextRoutes.get(contextId), (_a !== null && _a !== void 0 ? _a : [])));
}
}
/**
* Returns a summary at the current sequence number.

@@ -424,0 +452,0 @@ * @param fullTree - true to bypass optimizations and force a full summary tree

@@ -43,4 +43,10 @@ /*!

private collectExtraBlobsAndSanitizeSnapshot;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
* the context has loaded.
*/
getGCData(): Promise<IGarbageCollectionData>;
updateUsedRoutes(usedRoutes: string[]): void;
}
//# sourceMappingURL=localChannelContext.d.ts.map

@@ -143,2 +143,7 @@ "use strict";

}
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
* the context has loaded.
*/
async getGCData() {

@@ -148,4 +153,11 @@ common_utils_1.assert(this.isLoaded && this.channel !== undefined, "Channel should be loaded to run GC");

}
updateUsedRoutes(usedRoutes) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected.
* Once we have GC at DDS level, this channel context's used routes will be updated as per the passed
* value. See - https://github.com/microsoft/FluidFramework/issues/4611
*/
}
}
exports.LocalChannelContext = LocalChannelContext;
//# sourceMappingURL=localChannelContext.js.map

@@ -11,10 +11,8 @@ /*!

read(path: string): Promise<string>;
readBlob(path: string): Promise<ArrayBufferLike>;
contains(path: string): Promise<boolean>;
list(path: string): Promise<string[]>;
/**
* Provides a synchronous access point to locally stored data
*/
private readSync;
private readSyncInternal;
private readBlobSync;
private readBlobSyncInternal;
}
//# sourceMappingURL=localChannelStorageService.d.ts.map

@@ -15,8 +15,21 @@ "use strict";

async read(path) {
const contents = this.readSync(path);
return contents !== undefined ? Promise.resolve(contents) : Promise.reject(new Error("Not found"));
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
if (blob.encoding === "utf8") {
return blob.contents;
}
return common_utils_1.fromBase64ToUtf8(blob.contents);
}
async readBlob(path) {
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
return common_utils_1.stringToBuffer(blob.contents, blob.encoding);
}
async contains(path) {
const contents = this.readSync(path);
return contents !== undefined;
const blob = this.readBlobSync(path);
return blob !== undefined ? blob.contents !== undefined : false;
}

@@ -26,9 +39,6 @@ async list(path) {

}
/**
* Provides a synchronous access point to locally stored data
*/
readSync(path) {
return this.readSyncInternal(path, this.tree);
readBlobSync(path) {
return this.readBlobSyncInternal(path, this.tree);
}
readSyncInternal(path, tree) {
readBlobSyncInternal(path, tree) {
for (const entry of tree.entries) {

@@ -38,6 +48,3 @@ switch (entry.type) {

if (path === entry.path) {
const blob = entry.value;
return blob.encoding === "utf-8"
? common_utils_1.fromUtf8ToBase64(blob.contents)
: blob.contents;
return entry.value;
}

@@ -47,3 +54,3 @@ break;

if (path.startsWith(entry.path)) {
return this.readSyncInternal(path.substr(entry.path.length + 1), entry.value);
return this.readBlobSyncInternal(path.substr(entry.path.length + 1), entry.value);
}

@@ -50,0 +57,0 @@ break;

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

export declare const pkgName = "@fluidframework/datastore";
export declare const pkgVersion = "0.32.3";
export declare const pkgVersion = "0.33.0-13708";
//# sourceMappingURL=packageVersion.d.ts.map

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

exports.pkgName = "@fluidframework/datastore";
exports.pkgVersion = "0.32.3";
exports.pkgVersion = "0.33.0-13708";
//# sourceMappingURL=packageVersion.js.map

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

private readonly gcDetailsInInitialSummaryP;
constructor(runtime: IFluidDataStoreRuntime, dataStoreContext: IFluidDataStoreContext, storageService: IDocumentStorageService, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: (address: string) => void, id: string, baseSnapshot: ISnapshotTree, registry: ISharedObjectRegistry, extraBlobs: Map<string, string> | undefined, createSummarizerNode: CreateChildSummarizerNodeFn, attachMessageType?: string | undefined, usedRoutes?: string[]);
constructor(runtime: IFluidDataStoreRuntime, dataStoreContext: IFluidDataStoreContext, storageService: IDocumentStorageService, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: (address: string) => void, id: string, baseSnapshot: ISnapshotTree, registry: ISharedObjectRegistry, extraBlobs: Map<string, string> | undefined, createSummarizerNode: CreateChildSummarizerNodeFn, attachMessageType?: string | undefined);
getChannel(): Promise<IChannel>;

@@ -41,5 +41,23 @@ setConnectionState(connected: boolean, clientId?: string): void;

private loadChannel;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document.
* If there is no new data in this context since the last summary, previous GC data is used.
* If there is new data, the GC data is generated again (by calling getGCDataInternal).
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* Generates the data used for garbage collection. This is called when there is new data since last summary. It
* loads the context and calls into the channel to get its GC data.
*/
private getGCDataInternal;
/**
* After GC has run, called to notify the context of routes used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
* 3. These are added to the summary generated by the context.
* @param usedRoutes - The routes that are used in this context.
*/
updateUsedRoutes(usedRoutes: string[]): void;
}
//# sourceMappingURL=remoteChannelContext.d.ts.map

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

class RemoteChannelContext {
constructor(runtime, dataStoreContext, storageService, submitFn, dirtyFn, id, baseSnapshot, registry, extraBlobs, createSummarizerNode, attachMessageType, usedRoutes) {
constructor(runtime, dataStoreContext, storageService, submitFn, dirtyFn, id, baseSnapshot, registry, extraBlobs, createSummarizerNode, attachMessageType) {
this.runtime = runtime;

@@ -36,5 +36,3 @@ this.dataStoreContext = dataStoreContext;

const thisSummarizeInternal = async (fullTree, trackState) => this.summarizeInternal(fullTree, trackState);
// If we are created before GC is run, used routes will not be available. Set self route (empty string) to
// used routes in the summarizer node. If GC is enabled, the used routes will be updated as per the GC data.
this.summarizerNode = createSummarizerNode(thisSummarizeInternal, async () => this.getGCDataInternal(), async () => this.gcDetailsInInitialSummaryP, (usedRoutes !== null && usedRoutes !== void 0 ? usedRoutes : [""]));
this.summarizerNode = createSummarizerNode(thisSummarizeInternal, async () => this.getGCDataInternal(), async () => this.gcDetailsInInitialSummaryP);
}

@@ -161,5 +159,15 @@ // eslint-disable-next-line @typescript-eslint/promise-function-async

}
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document.
* If there is no new data in this context since the last summary, previous GC data is used.
* If there is new data, the GC data is generated again (by calling getGCDataInternal).
*/
async getGCData() {
return this.summarizerNode.getGCData();
}
/**
* Generates the data used for garbage collection. This is called when there is new data since last summary. It
* loads the context and calls into the channel to get its GC data.
*/
async getGCDataInternal() {

@@ -169,4 +177,23 @@ const channel = await this.getChannel();

}
/**
* After GC has run, called to notify the context of routes used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
* 3. These are added to the summary generated by the context.
* @param usedRoutes - The routes that are used in this context.
*/
updateUsedRoutes(usedRoutes) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected. Update the summarizer node's
* used routes to contain a route to this channel context.
* Once we have GC at DDS level, this will be updated to use the passed usedRoutes. See -
* https://github.com/microsoft/FluidFramework/issues/4611
*/
// back-compat: 0.33 - updateUsedRoutes is added in 0.33. Remove the check here when N >= 0.36.
if (this.summarizerNode.updateUsedRoutes !== undefined) {
this.summarizerNode.updateUsedRoutes([""]);
}
}
}
exports.RemoteChannelContext = RemoteChannelContext;
//# sourceMappingURL=remoteChannelContext.js.map

@@ -8,2 +8,3 @@ "use strict";

const assert_1 = require("assert");
const common_utils_1 = require("@fluidframework/common-utils");
const channelStorageService_1 = require("../channelStorageService");

@@ -15,4 +16,2 @@ describe("ChannelStorageService", () => {

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},

@@ -22,8 +21,11 @@ };

read: async (id) => {
assert_1.strict.fail();
throw new Error("not implemented");
},
readBlob: async (id) => {
throw new Error("not implemented");
},
};
const ss = new channelStorageService_1.ChannelStorageService(tree, storage);
assert_1.strict.equal(await ss.contains("/"), false);
assert_1.strict.deepEqual(await ss.list(""), []);
assert_1.strict.strictEqual(await ss.contains("/"), false);
assert_1.strict.deepStrictEqual(await ss.list(""), []);
});

@@ -36,4 +38,2 @@ it("Top Level Blob", async () => {

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},

@@ -45,7 +45,11 @@ };

},
readBlob: async (id) => {
return common_utils_1.stringToBuffer(id, "utf8");
},
};
const ss = new channelStorageService_1.ChannelStorageService(tree, storage);
assert_1.strict.equal(await ss.contains("foo"), true);
assert_1.strict.strictEqual(await ss.contains("foo"), true);
assert_1.strict.deepStrictEqual(await ss.list(""), ["foo"]);
assert_1.strict.equal(await ss.read("foo"), "bar");
assert_1.strict.strictEqual(await ss.read("foo"), "bar");
assert_1.strict.deepStrictEqual(await ss.readBlob("foo"), common_utils_1.stringToBuffer("bar", "utf8"));
});

@@ -56,4 +60,2 @@ it("Nested Blob", async () => {

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {

@@ -65,4 +67,2 @@ nested: {

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},

@@ -76,9 +76,13 @@ },

},
readBlob: async (id) => {
return common_utils_1.stringToBuffer(id, "utf8");
},
};
const ss = new channelStorageService_1.ChannelStorageService(tree, storage);
assert_1.strict.equal(await ss.contains("nested/foo"), true);
assert_1.strict.strictEqual(await ss.contains("nested/foo"), true);
assert_1.strict.deepStrictEqual(await ss.list("nested/"), ["foo"]);
assert_1.strict.equal(await ss.read("nested/foo"), "bar");
assert_1.strict.strictEqual(await ss.read("nested/foo"), "bar");
assert_1.strict.deepStrictEqual(await ss.readBlob("nested/foo"), common_utils_1.stringToBuffer("bar", "utf8"));
});
});
//# sourceMappingURL=channelStorageService.spec.js.map

@@ -8,2 +8,3 @@ "use strict";

const assert_1 = require("assert");
const common_utils_1 = require("@fluidframework/common-utils");
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");

@@ -14,14 +15,16 @@ const localChannelStorageService_1 = require("../localChannelStorageService");

const tree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [],
};
const ss = new localChannelStorageService_1.LocalChannelStorageService(tree);
assert_1.strict.equal(await ss.contains("/"), false);
assert_1.strict.deepEqual(await ss.list(""), []);
assert_1.strict.strictEqual(await ss.contains("/"), false);
assert_1.strict.deepStrictEqual(await ss.list(""), []);
try {
await ss.readBlob("test");
}
catch (error) {
assert_1.strict.strictEqual(error.message, "Blob Not Found");
}
});
it("Top Level Blob", async () => {
const tree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -31,3 +34,3 @@ {

path: "foo",
type: "Blob",
type: protocol_definitions_1.TreeEntry.Blob,
value: {

@@ -41,10 +44,9 @@ encoding: "utf8",

const ss = new localChannelStorageService_1.LocalChannelStorageService(tree);
assert_1.strict.equal(await ss.contains("foo"), true);
assert_1.strict.strictEqual(await ss.contains("foo"), true);
assert_1.strict.deepStrictEqual(await ss.list(""), ["foo"]);
assert_1.strict.equal(await ss.read("foo"), "bar");
assert_1.strict.strictEqual(await ss.read("foo"), "bar");
assert_1.strict.deepStrictEqual(await ss.readBlob("foo"), common_utils_1.stringToBuffer("bar", "utf8"));
});
it("Nested Blob", async () => {
const tree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -54,6 +56,4 @@ {

path: "nested",
type: "Tree",
type: protocol_definitions_1.TreeEntry.Tree,
value: {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -63,3 +63,3 @@ {

path: "foo",
type: "Blob",
type: protocol_definitions_1.TreeEntry.Blob,
value: {

@@ -76,7 +76,8 @@ encoding: "utf8",

const ss = new localChannelStorageService_1.LocalChannelStorageService(tree);
assert_1.strict.equal(await ss.contains("nested/foo"), true);
assert_1.strict.strictEqual(await ss.contains("nested/foo"), true);
assert_1.strict.deepStrictEqual(await ss.list("nested/"), ["foo"]);
assert_1.strict.equal(await ss.read("nested/foo"), "bar");
assert_1.strict.strictEqual(await ss.read("nested/foo"), "bar");
assert_1.strict.deepStrictEqual(await ss.readBlob("nested/foo"), common_utils_1.stringToBuffer("bar", "utf8"));
});
});
//# sourceMappingURL=localChannelStorageService.spec.js.map

@@ -18,3 +18,14 @@ /*!

reSubmit(content: any, localOpMetadata: unknown): void;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context
* including any of its children. Each node has a set of outbound routes to other GC nodes in the document.
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* After GC has run, called to notify this context of routes that are used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To identify if this context or any of its children's used routes changed since last summary.
* 3. They are added to the summary generated by this context.
*/
updateUsedRoutes(usedRoutes: string[]): void;
}

@@ -21,0 +32,0 @@ export declare function createServiceEndpoints(id: string, connected: boolean, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: () => void, storageService: IDocumentStorageService, tree?: ISnapshotTree, extraBlobs?: Map<string, string>): {

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

private readonly flattenedTree;
constructor(tree: ISnapshotTree | undefined, storage: Pick<IDocumentStorageService, "read">, extraBlobs?: Map<string, string> | undefined);
constructor(tree: ISnapshotTree | undefined, storage: Pick<IDocumentStorageService, "read" | "readBlob">, extraBlobs?: Map<string, string> | undefined);
contains(path: string): Promise<boolean>;
read(path: string): Promise<string>;
readBlob(path: string): Promise<ArrayBufferLike>;
list(path: string): Promise<string[]>;

@@ -19,0 +20,0 @@ private getIdForPath;

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

import { getNormalizedObjectStoragePathParts } from "@fluidframework/runtime-utils";
import { stringToBuffer } from "@fluidframework/common-utils";
export class ChannelStorageService {

@@ -38,2 +39,9 @@ constructor(tree, storage, extraBlobs) {

}
async readBlob(path) {
const id = await this.getIdForPath(path);
const blob = this.extraBlobs !== undefined
? this.extraBlobs.get(id)
: undefined;
return blob !== undefined ? stringToBuffer(blob, "base64") : this.storage.readBlob(id);
}
async list(path) {

@@ -40,0 +48,0 @@ var _a, _b;

@@ -123,4 +123,20 @@ /*!

private updateGCNodes;
/**
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
* document. It does the following:
* 1. Calls into each child context to get its GC data.
* 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
* idenfied as belonging to the child.
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
* the GC data of this channel.
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
* update their used routes.
* @param usedRoutes - The routes that are used in all contexts in this channel.
*/
updateUsedRoutes(usedRoutes: string[]): void;
/**
* Returns a summary at the current sequence number.

@@ -127,0 +143,0 @@ * @param fullTree - true to bypass optimizations and force a full summary tree

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

import { convertSnapshotTreeToSummaryTree, convertSummaryTreeToITree, FluidSerializer, generateHandleContextPath, RequestParser, SummaryTreeBuilder, } from "@fluidframework/runtime-utils";
import { GCDataBuilder } from "@fluidframework/garbage-collector";
import { GCDataBuilder, getChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
import { v4 as uuid } from "uuid";

@@ -401,2 +401,12 @@ import { summarizeChannel } from "./channelContext";

}
/**
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
* document. It does the following:
* 1. Calls into each child context to get its GC data.
* 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
* idenfied as belonging to the child.
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
* the GC data of this channel.
*/
async getGCData() {

@@ -412,4 +422,4 @@ const builder = new GCDataBuilder();

const contextGCData = await context.getGCData();
// Prefix the child's id to the ids of its GC nodes. This gradually builds the id of each node to be
// a path from the root.
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
// This also gradually builds the id of each node to be a path from the root.
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);

@@ -421,2 +431,20 @@ }));

/**
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
* update their used routes.
* @param usedRoutes - The routes that are used in all contexts in this channel.
*/
updateUsedRoutes(usedRoutes) {
var _a;
// Get a map of channel ids to routes used in it.
const usedContextRoutes = getChildNodesUsedRoutes(usedRoutes);
// Verify that the used routes are correct.
for (const [id] of usedContextRoutes) {
assert(this.contexts.has(id), "Used route does not belong to any known context");
}
// Update the used routes in each context. Used routes is empty for unused context.
for (const [contextId, context] of this.contexts) {
context.updateUsedRoutes((_a = usedContextRoutes.get(contextId), (_a !== null && _a !== void 0 ? _a : [])));
}
}
/**
* Returns a summary at the current sequence number.

@@ -423,0 +451,0 @@ * @param fullTree - true to bypass optimizations and force a full summary tree

@@ -43,4 +43,10 @@ /*!

private collectExtraBlobsAndSanitizeSnapshot;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
* the context has loaded.
*/
getGCData(): Promise<IGarbageCollectionData>;
updateUsedRoutes(usedRoutes: string[]): void;
}
//# sourceMappingURL=localChannelContext.d.ts.map

@@ -138,2 +138,7 @@ /*!

}
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
* the context has loaded.
*/
async getGCData() {

@@ -143,3 +148,10 @@ assert(this.isLoaded && this.channel !== undefined, "Channel should be loaded to run GC");

}
updateUsedRoutes(usedRoutes) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected.
* Once we have GC at DDS level, this channel context's used routes will be updated as per the passed
* value. See - https://github.com/microsoft/FluidFramework/issues/4611
*/
}
}
//# sourceMappingURL=localChannelContext.js.map

@@ -11,10 +11,8 @@ /*!

read(path: string): Promise<string>;
readBlob(path: string): Promise<ArrayBufferLike>;
contains(path: string): Promise<boolean>;
list(path: string): Promise<string[]>;
/**
* Provides a synchronous access point to locally stored data
*/
private readSync;
private readSyncInternal;
private readBlobSync;
private readBlobSyncInternal;
}
//# sourceMappingURL=localChannelStorageService.d.ts.map

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

*/
import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
import { fromBase64ToUtf8, stringToBuffer } from "@fluidframework/common-utils";
import { TreeEntry } from "@fluidframework/protocol-definitions";

@@ -14,8 +14,21 @@ import { listBlobsAtTreePath } from "@fluidframework/runtime-utils";

async read(path) {
const contents = this.readSync(path);
return contents !== undefined ? Promise.resolve(contents) : Promise.reject(new Error("Not found"));
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
if (blob.encoding === "utf8") {
return blob.contents;
}
return fromBase64ToUtf8(blob.contents);
}
async readBlob(path) {
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
return stringToBuffer(blob.contents, blob.encoding);
}
async contains(path) {
const contents = this.readSync(path);
return contents !== undefined;
const blob = this.readBlobSync(path);
return blob !== undefined ? blob.contents !== undefined : false;
}

@@ -25,9 +38,6 @@ async list(path) {

}
/**
* Provides a synchronous access point to locally stored data
*/
readSync(path) {
return this.readSyncInternal(path, this.tree);
readBlobSync(path) {
return this.readBlobSyncInternal(path, this.tree);
}
readSyncInternal(path, tree) {
readBlobSyncInternal(path, tree) {
for (const entry of tree.entries) {

@@ -37,6 +47,3 @@ switch (entry.type) {

if (path === entry.path) {
const blob = entry.value;
return blob.encoding === "utf-8"
? fromUtf8ToBase64(blob.contents)
: blob.contents;
return entry.value;
}

@@ -46,3 +53,3 @@ break;

if (path.startsWith(entry.path)) {
return this.readSyncInternal(path.substr(entry.path.length + 1), entry.value);
return this.readBlobSyncInternal(path.substr(entry.path.length + 1), entry.value);
}

@@ -49,0 +56,0 @@ break;

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

export declare const pkgName = "@fluidframework/datastore";
export declare const pkgVersion = "0.32.3";
export declare const pkgVersion = "0.33.0-13708";
//# sourceMappingURL=packageVersion.d.ts.map

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

export const pkgName = "@fluidframework/datastore";
export const pkgVersion = "0.32.3";
export const pkgVersion = "0.33.0-13708";
//# sourceMappingURL=packageVersion.js.map

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

private readonly gcDetailsInInitialSummaryP;
constructor(runtime: IFluidDataStoreRuntime, dataStoreContext: IFluidDataStoreContext, storageService: IDocumentStorageService, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: (address: string) => void, id: string, baseSnapshot: ISnapshotTree, registry: ISharedObjectRegistry, extraBlobs: Map<string, string> | undefined, createSummarizerNode: CreateChildSummarizerNodeFn, attachMessageType?: string | undefined, usedRoutes?: string[]);
constructor(runtime: IFluidDataStoreRuntime, dataStoreContext: IFluidDataStoreContext, storageService: IDocumentStorageService, submitFn: (content: any, localOpMetadata: unknown) => void, dirtyFn: (address: string) => void, id: string, baseSnapshot: ISnapshotTree, registry: ISharedObjectRegistry, extraBlobs: Map<string, string> | undefined, createSummarizerNode: CreateChildSummarizerNodeFn, attachMessageType?: string | undefined);
getChannel(): Promise<IChannel>;

@@ -41,5 +41,23 @@ setConnectionState(connected: boolean, clientId?: string): void;

private loadChannel;
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document.
* If there is no new data in this context since the last summary, previous GC data is used.
* If there is new data, the GC data is generated again (by calling getGCDataInternal).
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* Generates the data used for garbage collection. This is called when there is new data since last summary. It
* loads the context and calls into the channel to get its GC data.
*/
private getGCDataInternal;
/**
* After GC has run, called to notify the context of routes used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
* 3. These are added to the summary generated by the context.
* @param usedRoutes - The routes that are used in this context.
*/
updateUsedRoutes(usedRoutes: string[]): void;
}
//# sourceMappingURL=remoteChannelContext.d.ts.map

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

export class RemoteChannelContext {
constructor(runtime, dataStoreContext, storageService, submitFn, dirtyFn, id, baseSnapshot, registry, extraBlobs, createSummarizerNode, attachMessageType, usedRoutes) {
constructor(runtime, dataStoreContext, storageService, submitFn, dirtyFn, id, baseSnapshot, registry, extraBlobs, createSummarizerNode, attachMessageType) {
this.runtime = runtime;

@@ -34,5 +34,3 @@ this.dataStoreContext = dataStoreContext;

const thisSummarizeInternal = async (fullTree, trackState) => this.summarizeInternal(fullTree, trackState);
// If we are created before GC is run, used routes will not be available. Set self route (empty string) to
// used routes in the summarizer node. If GC is enabled, the used routes will be updated as per the GC data.
this.summarizerNode = createSummarizerNode(thisSummarizeInternal, async () => this.getGCDataInternal(), async () => this.gcDetailsInInitialSummaryP, (usedRoutes !== null && usedRoutes !== void 0 ? usedRoutes : [""]));
this.summarizerNode = createSummarizerNode(thisSummarizeInternal, async () => this.getGCDataInternal(), async () => this.gcDetailsInInitialSummaryP);
}

@@ -159,5 +157,15 @@ // eslint-disable-next-line @typescript-eslint/promise-function-async

}
/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document.
* If there is no new data in this context since the last summary, previous GC data is used.
* If there is new data, the GC data is generated again (by calling getGCDataInternal).
*/
async getGCData() {
return this.summarizerNode.getGCData();
}
/**
* Generates the data used for garbage collection. This is called when there is new data since last summary. It
* loads the context and calls into the channel to get its GC data.
*/
async getGCDataInternal() {

@@ -167,3 +175,22 @@ const channel = await this.getChannel();

}
/**
* After GC has run, called to notify the context of routes used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
* 3. These are added to the summary generated by the context.
* @param usedRoutes - The routes that are used in this context.
*/
updateUsedRoutes(usedRoutes) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected. Update the summarizer node's
* used routes to contain a route to this channel context.
* Once we have GC at DDS level, this will be updated to use the passed usedRoutes. See -
* https://github.com/microsoft/FluidFramework/issues/4611
*/
// back-compat: 0.33 - updateUsedRoutes is added in 0.33. Remove the check here when N >= 0.36.
if (this.summarizerNode.updateUsedRoutes !== undefined) {
this.summarizerNode.updateUsedRoutes([""]);
}
}
}
//# sourceMappingURL=remoteChannelContext.js.map
{
"name": "@fluidframework/datastore",
"version": "0.32.3",
"version": "0.33.0-13708",
"description": "Fluid data store implementation",

@@ -59,15 +59,15 @@ "homepage": "https://fluidframework.com",

"@fluidframework/common-definitions": "^0.19.1",
"@fluidframework/common-utils": "^0.26.0",
"@fluidframework/container-definitions": "^0.32.3",
"@fluidframework/container-utils": "^0.32.3",
"@fluidframework/core-interfaces": "^0.32.3",
"@fluidframework/datastore-definitions": "^0.32.3",
"@fluidframework/driver-definitions": "^0.32.3",
"@fluidframework/driver-utils": "^0.32.3",
"@fluidframework/garbage-collector": "^0.32.3",
"@fluidframework/protocol-base": "^0.1017.1",
"@fluidframework/protocol-definitions": "^0.1017.1",
"@fluidframework/runtime-definitions": "^0.32.3",
"@fluidframework/runtime-utils": "^0.32.3",
"@fluidframework/telemetry-utils": "^0.32.3",
"@fluidframework/common-utils": "^0.27.0-0",
"@fluidframework/container-definitions": "0.33.0-13708",
"@fluidframework/container-utils": "0.33.0-13708",
"@fluidframework/core-interfaces": "0.33.0-13708",
"@fluidframework/datastore-definitions": "0.33.0-13708",
"@fluidframework/driver-definitions": "0.33.0-13708",
"@fluidframework/driver-utils": "0.33.0-13708",
"@fluidframework/garbage-collector": "0.33.0-13708",
"@fluidframework/protocol-base": "^0.1018.0-0",
"@fluidframework/protocol-definitions": "^0.1018.0-0",
"@fluidframework/runtime-definitions": "0.33.0-13708",
"@fluidframework/runtime-utils": "0.33.0-13708",
"@fluidframework/telemetry-utils": "0.33.0-13708",
"assert": "^2.0.0",

@@ -81,3 +81,3 @@ "debug": "^4.1.1",

"@fluidframework/eslint-config-fluid": "^0.21.0",
"@fluidframework/mocha-test-setup": "^0.32.3",
"@fluidframework/mocha-test-setup": "0.33.0-13708",
"@microsoft/api-extractor": "^7.7.2",

@@ -84,0 +84,0 @@ "@types/assert": "^1.5.1",

@@ -6,3 +6,3 @@ # @fluidframework/datastore

The two major interfaces required to implement a Fluid handle are `IFluidHandle` and `IFluidHandleContext` defined in [fluidHandle.ts](src\fluidHandle.ts).
The two major interfaces required to implement a Fluid handle are `IFluidHandle` and `IFluidHandleContext` defined in [fluidHandle.ts](src/fluidHandle.ts).

@@ -9,0 +9,0 @@ ## IFluidHandle

@@ -33,3 +33,15 @@ /*!

/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context
* including any of its children. Each node has a set of outbound routes to other GC nodes in the document.
*/
getGCData(): Promise<IGarbageCollectionData>;
/**
* After GC has run, called to notify this context of routes that are used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To identify if this context or any of its children's used routes changed since last summary.
* 3. They are added to the summary generated by this context.
*/
updateUsedRoutes(usedRoutes: string[]): void;
}

@@ -36,0 +48,0 @@

@@ -10,2 +10,3 @@ /*!

import { getNormalizedObjectStoragePathParts } from "@fluidframework/runtime-utils";
import { stringToBuffer } from "@fluidframework/common-utils";

@@ -29,3 +30,3 @@ export class ChannelStorageService implements IChannelStorageService {

private readonly tree: ISnapshotTree | undefined,
private readonly storage: Pick<IDocumentStorageService, "read">,
private readonly storage: Pick<IDocumentStorageService, "read" | "readBlob">,
private readonly extraBlobs?: Map<string, string>,

@@ -53,2 +54,11 @@ ) {

public async readBlob(path: string): Promise<ArrayBufferLike> {
const id = await this.getIdForPath(path);
const blob = this.extraBlobs !== undefined
? this.extraBlobs.get(id)
: undefined;
return blob !== undefined ? stringToBuffer(blob,"base64") : this.storage.readBlob(id);
}
public async list(path: string): Promise<string[]> {

@@ -55,0 +65,0 @@ let tree = this.tree;

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

} from "@fluidframework/datastore-definitions";
import { GCDataBuilder } from "@fluidframework/garbage-collector";
import { GCDataBuilder, getChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
import { v4 as uuid } from "uuid";

@@ -593,2 +593,12 @@ import { IChannelContext, summarizeChannel } from "./channelContext";

/**
* Generates data used for garbage collection. This includes a list of GC nodes that represent this channel
* including any of its child channel contexts. Each node has a set of outbound routes to other GC nodes in the
* document. It does the following:
* 1. Calls into each child context to get its GC data.
* 2. Prefixs the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
* idenfied as belonging to the child.
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
* the GC data of this channel.
*/
public async getGCData(): Promise<IGarbageCollectionData> {

@@ -604,4 +614,4 @@ const builder = new GCDataBuilder();

const contextGCData = await context.getGCData();
// Prefix the child's id to the ids of its GC nodes. This gradually builds the id of each node to be
// a path from the root.
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
// This also gradually builds the id of each node to be a path from the root.
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);

@@ -615,2 +625,22 @@ }));

/**
* After GC has run, called to notify this channel of routes that are used in it. It calls the child contexts to
* update their used routes.
* @param usedRoutes - The routes that are used in all contexts in this channel.
*/
public updateUsedRoutes(usedRoutes: string[]) {
// Get a map of channel ids to routes used in it.
const usedContextRoutes = getChildNodesUsedRoutes(usedRoutes);
// Verify that the used routes are correct.
for (const [id] of usedContextRoutes) {
assert(this.contexts.has(id), "Used route does not belong to any known context");
}
// Update the used routes in each context. Used routes is empty for unused context.
for (const [contextId, context] of this.contexts) {
context.updateUsedRoutes(usedContextRoutes.get(contextId) ?? []);
}
}
/**
* Returns a summary at the current sequence number.

@@ -617,0 +647,0 @@ * @param fullTree - true to bypass optimizations and force a full summary tree

@@ -201,2 +201,7 @@ /*!

/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document. This should be called only after
* the context has loaded.
*/
public async getGCData(): Promise<IGarbageCollectionData> {

@@ -206,2 +211,10 @@ assert(this.isLoaded && this.channel !== undefined, "Channel should be loaded to run GC");

}
public updateUsedRoutes(usedRoutes: string[]) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected.
* Once we have GC at DDS level, this channel context's used routes will be updated as per the passed
* value. See - https://github.com/microsoft/FluidFramework/issues/4611
*/
}
}

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

import { IChannelStorageService } from "@fluidframework/datastore-definitions";
import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
import { fromBase64ToUtf8, stringToBuffer } from "@fluidframework/common-utils";
import { IBlob, ITree, TreeEntry } from "@fluidframework/protocol-definitions";

@@ -17,9 +17,23 @@ import { listBlobsAtTreePath } from "@fluidframework/runtime-utils";

public async read(path: string): Promise<string> {
const contents = this.readSync(path);
return contents !== undefined ? Promise.resolve(contents) : Promise.reject(new Error("Not found"));
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
if (blob.encoding === "utf8") {
return blob.contents;
}
return fromBase64ToUtf8(blob.contents);
}
public async readBlob(path: string): Promise<ArrayBufferLike> {
const blob = this.readBlobSync(path);
if (blob === undefined) {
throw new Error("Blob Not Found");
}
return stringToBuffer(blob.contents, blob.encoding);
}
public async contains(path: string): Promise<boolean> {
const contents = this.readSync(path);
return contents !== undefined;
const blob = this.readBlobSync(path);
return blob !== undefined ? blob.contents !== undefined : false;
}

@@ -31,10 +45,7 @@

/**
* Provides a synchronous access point to locally stored data
*/
private readSync(path: string): string | undefined {
return this.readSyncInternal(path, this.tree);
private readBlobSync(path: string): IBlob | undefined {
return this.readBlobSyncInternal(path, this.tree);
}
private readSyncInternal(path: string, tree: ITree): string | undefined {
private readBlobSyncInternal(path: string, tree: ITree): IBlob | undefined {
for (const entry of tree.entries) {

@@ -44,6 +55,3 @@ switch (entry.type) {

if (path === entry.path) {
const blob = entry.value as IBlob;
return blob.encoding === "utf-8"
? fromUtf8ToBase64(blob.contents)
: blob.contents;
return entry.value;
}

@@ -54,3 +62,3 @@ break;

if (path.startsWith(entry.path)) {
return this.readSyncInternal(path.substr(entry.path.length + 1), entry.value as ITree);
return this.readBlobSyncInternal(path.substr(entry.path.length + 1), entry.value);
}

@@ -57,0 +65,0 @@ break;

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

export const pkgName = "@fluidframework/datastore";
export const pkgVersion = "0.32.3";
export const pkgVersion = "0.33.0-13708";

@@ -75,3 +75,2 @@ /*!

private readonly attachMessageType?: string,
usedRoutes?: string[],
) {

@@ -90,4 +89,2 @@ this.services = createServiceEndpoints(

// If we are created before GC is run, used routes will not be available. Set self route (empty string) to
// used routes in the summarizer node. If GC is enabled, the used routes will be updated as per the GC data.
this.summarizerNode = createSummarizerNode(

@@ -97,3 +94,2 @@ thisSummarizeInternal,

async () => this.gcDetailsInInitialSummaryP,
usedRoutes ?? [""],
);

@@ -244,2 +240,8 @@ }

/**
* Returns the data used for garbage collection. This includes a list of GC nodes that represent this context.
* Each node has a set of outbound routes to other GC nodes in the document.
* If there is no new data in this context since the last summary, previous GC data is used.
* If there is new data, the GC data is generated again (by calling getGCDataInternal).
*/
public async getGCData(): Promise<IGarbageCollectionData> {

@@ -249,2 +251,6 @@ return this.summarizerNode.getGCData();

/**
* Generates the data used for garbage collection. This is called when there is new data since last summary. It
* loads the context and calls into the channel to get its GC data.
*/
private async getGCDataInternal(): Promise<IGarbageCollectionData> {

@@ -254,2 +260,22 @@ const channel = await this.getChannel();

}
/**
* After GC has run, called to notify the context of routes used in it. These are used for the following:
* 1. To identify if this context is being referenced in the document or not.
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
* 3. These are added to the summary generated by the context.
* @param usedRoutes - The routes that are used in this context.
*/
public updateUsedRoutes(usedRoutes: string[]) {
/**
* Currently, DDSs are always considered referenced and are not garbage collected. Update the summarizer node's
* used routes to contain a route to this channel context.
* Once we have GC at DDS level, this will be updated to use the passed usedRoutes. See -
* https://github.com/microsoft/FluidFramework/issues/4611
*/
// back-compat: 0.33 - updateUsedRoutes is added in 0.33. Remove the check here when N >= 0.36.
if (this.summarizerNode.updateUsedRoutes !== undefined) {
this.summarizerNode.updateUsedRoutes([""]);
}
}
}

@@ -7,2 +7,3 @@ /*!

import { strict as assert } from "assert";
import { stringToBuffer } from "@fluidframework/common-utils";
import { ISnapshotTree } from "@fluidframework/protocol-definitions";

@@ -17,15 +18,16 @@ import { IDocumentStorageService } from "@fluidframework/driver-definitions";

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},
};
const storage: Pick<IDocumentStorageService, "read"> = {
const storage: Pick<IDocumentStorageService, "read" | "readBlob"> = {
read: async (id: string) => {
assert.fail();
throw new Error("not implemented");
},
readBlob: async (id: string) => {
throw new Error("not implemented");
},
};
const ss = new ChannelStorageService(tree, storage);
assert.equal(await ss.contains("/"), false);
assert.deepEqual(await ss.list(""), []);
assert.strictEqual(await ss.contains("/"), false);
assert.deepStrictEqual(await ss.list(""), []);
});

@@ -39,16 +41,18 @@

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},
};
const storage: Pick<IDocumentStorageService, "read"> = {
const storage: Pick<IDocumentStorageService, "read" | "readBlob"> = {
read: async (id: string) => {
return id;
},
readBlob: async (id: string) => {
return stringToBuffer(id, "utf8");
},
};
const ss = new ChannelStorageService(tree, storage);
assert.equal(await ss.contains("foo"), true);
assert.strictEqual(await ss.contains("foo"), true);
assert.deepStrictEqual(await ss.list(""), ["foo"]);
assert.equal(await ss.read("foo"), "bar");
assert.strictEqual(await ss.read("foo"), "bar");
assert.deepStrictEqual(await ss.readBlob("foo"), stringToBuffer("bar", "utf8"));
});

@@ -60,4 +64,2 @@

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {

@@ -69,4 +71,2 @@ nested: {

commits: {},
// eslint-disable-next-line no-null/no-null
id: null,
trees: {},

@@ -76,13 +76,17 @@ },

};
const storage: Pick<IDocumentStorageService, "read"> = {
const storage: Pick<IDocumentStorageService, "read" | "readBlob"> = {
read: async (id: string) => {
return id;
},
readBlob: async (id: string) => {
return stringToBuffer(id, "utf8");
},
};
const ss = new ChannelStorageService(tree, storage);
assert.equal(await ss.contains("nested/foo"), true);
assert.strictEqual(await ss.contains("nested/foo"), true);
assert.deepStrictEqual(await ss.list("nested/"), ["foo"]);
assert.equal(await ss.read("nested/foo"), "bar");
assert.strictEqual(await ss.read("nested/foo"), "bar");
assert.deepStrictEqual(await ss.readBlob("nested/foo"), stringToBuffer("bar", "utf8"));
});
});

@@ -7,3 +7,4 @@ /*!

import { strict as assert } from "assert";
import { ITree, FileMode } from "@fluidframework/protocol-definitions";
import { stringToBuffer } from "@fluidframework/common-utils";
import { ITree, FileMode, TreeEntry } from "@fluidframework/protocol-definitions";
import { LocalChannelStorageService } from "../localChannelStorageService";

@@ -14,4 +15,2 @@

const tree: ITree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [],

@@ -22,4 +21,9 @@ };

assert.equal(await ss.contains("/"), false);
assert.deepEqual(await ss.list(""), []);
assert.strictEqual(await ss.contains("/"), false);
assert.deepStrictEqual(await ss.list(""), []);
try {
await ss.readBlob("test");
} catch (error) {
assert.strictEqual(error.message, "Blob Not Found");
}
});

@@ -29,4 +33,2 @@

const tree: ITree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -36,3 +38,3 @@ {

path: "foo",
type: "Blob",
type: TreeEntry.Blob,
value: {

@@ -48,5 +50,6 @@ encoding: "utf8",

assert.equal(await ss.contains("foo"), true);
assert.strictEqual(await ss.contains("foo"), true);
assert.deepStrictEqual(await ss.list(""), ["foo"]);
assert.equal(await ss.read("foo"), "bar");
assert.strictEqual(await ss.read("foo"), "bar");
assert.deepStrictEqual(await ss.readBlob("foo"), stringToBuffer("bar","utf8"));
});

@@ -56,4 +59,2 @@

const tree: ITree = {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -63,6 +64,4 @@ {

path: "nested",
type: "Tree",
type: TreeEntry.Tree,
value: {
// eslint-disable-next-line no-null/no-null
id: null,
entries: [

@@ -72,3 +71,3 @@ {

path: "foo",
type: "Blob",
type: TreeEntry.Blob,
value: {

@@ -86,6 +85,7 @@ encoding: "utf8",

assert.equal(await ss.contains("nested/foo"), true);
assert.strictEqual(await ss.contains("nested/foo"), true);
assert.deepStrictEqual(await ss.list("nested/"), ["foo"]);
assert.equal(await ss.read("nested/foo"), "bar");
assert.strictEqual(await ss.read("nested/foo"), "bar");
assert.deepStrictEqual(await ss.readBlob("nested/foo"), stringToBuffer("bar","utf8"));
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc