@fluidframework/local-driver 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/api-report/local-driver.alpha.api.md +21 -7
- package/dist/alpha.d.ts +4 -1
- package/dist/ephemeralService.d.ts +126 -74
- package/dist/ephemeralService.d.ts.map +1 -1
- package/dist/ephemeralService.js +125 -19
- package/dist/ephemeralService.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/localSessionStorageDb.d.ts.map +1 -1
- package/dist/localSessionStorageDb.js +19 -8
- package/dist/localSessionStorageDb.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/alpha.d.ts +4 -1
- package/lib/ephemeralService.d.ts +126 -74
- package/lib/ephemeralService.d.ts.map +1 -1
- package/lib/ephemeralService.js +123 -19
- package/lib/ephemeralService.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/localSessionStorageDb.d.ts.map +1 -1
- package/lib/localSessionStorageDb.js +19 -8
- package/lib/localSessionStorageDb.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +16 -17
- package/src/ephemeralService.ts +278 -109
- package/src/index.ts +5 -1
- package/src/localSessionStorageDb.ts +23 -7
- package/src/packageVersion.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @fluidframework/local-driver
|
|
2
2
|
|
|
3
|
+
## 3.3.0
|
|
4
|
+
|
|
5
|
+
Dependency updates only.
|
|
6
|
+
|
|
7
|
+
## 3.2.0
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- Collect container telemetry through ServiceClient ([#28259](https://github.com/microsoft/FluidFramework/pull/28259)) [11261004291](https://github.com/microsoft/FluidFramework/commit/11261004291599575a23483fbaf8b20f4ff1afc1)
|
|
12
|
+
|
|
13
|
+
The alpha [ServiceOptions](https://fluidframework.com/docs/api/driver-definitions/serviceoptions-interface) interface now accepts an optional `logger`.
|
|
14
|
+
Session, ephemeral, and Tinylicious clients forward telemetry from containers they create or load to this logger.
|
|
15
|
+
Existing callers can omit the option without changing their behavior.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { startEphemeralService } from "@fluidframework/local-driver/alpha";
|
|
19
|
+
|
|
20
|
+
const service = startEphemeralService();
|
|
21
|
+
const client = service.newClient({
|
|
22
|
+
oldestSupportedClient: "2.100.0",
|
|
23
|
+
logger: {
|
|
24
|
+
send(event) {
|
|
25
|
+
console.log(event);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The same `logger` option is supported by `getSessionService().newClient(...)` and `createTinyliciousServiceClient(...)`.
|
|
32
|
+
|
|
33
|
+
- Add session-storage-backed local services ([#27902](https://github.com/microsoft/FluidFramework/pull/27902)) [da0dd40c087](https://github.com/microsoft/FluidFramework/commit/da0dd40c087b075822f0a9be723e1879f25d23b5)
|
|
34
|
+
|
|
35
|
+
The new alpha [`getSessionService`](https://fluidframework.com/docs/api/local-driver/getsessionservice-function) API provides a [`ServiceClient`](https://fluidframework.com/docs/api/driver-definitions/serviceclient-interface) compatible way to use the browser-local Fluid service that retains attached documents across page reloads in the same browser tab. Calls within one JavaScript realm share a lazily created service for the lifetime of that realm. Local services also expose APIs to list and delete their stored documents.
|
|
36
|
+
|
|
37
|
+
Session storage can be shared by separate same-origin JavaScript realms or applications loading separate copies of the package. Such instances run independent local servers, so concurrently editing the same stored document across them is unsupported.
|
|
38
|
+
|
|
39
|
+
The alpha `EphemeralServiceClient` type has been replaced by the more general [`LocalServiceClient`](https://fluidframework.com/docs/api/local-driver/localserviceclient-interface) type. Update type imports and annotations to use [`LocalServiceClient`](https://fluidframework.com/docs/api/local-driver/localserviceclient-interface)<[`EphemeralService`](https://fluidframework.com/docs/api/local-driver/ephemeralservice-interface)>.
|
|
40
|
+
|
|
3
41
|
## 3.1.0
|
|
4
42
|
|
|
5
43
|
Dependency updates only.
|
|
@@ -8,20 +8,34 @@
|
|
|
8
8
|
export function cleanupEphemeralService(service?: EphemeralService): Promise<void>;
|
|
9
9
|
|
|
10
10
|
// @alpha @sealed
|
|
11
|
-
export interface EphemeralService extends
|
|
11
|
+
export interface EphemeralService extends LocalService<LocalServiceClient<EphemeralService>> {
|
|
12
12
|
close(): Promise<void>;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// @alpha
|
|
16
|
+
export function getDefaultEphemeralService(): EphemeralService;
|
|
17
|
+
|
|
18
|
+
// @alpha
|
|
19
|
+
export function getSessionService(): SessionService;
|
|
20
|
+
|
|
21
|
+
// @alpha @sealed
|
|
22
|
+
export interface LocalService<out TClient extends ServiceClient = LocalServiceClient> extends ErasedBaseType<readonly ["LocalService", TClient]> {
|
|
23
|
+
readonly defaultClient: TClient;
|
|
24
|
+
deleteAllDocuments(): Promise<void>;
|
|
25
|
+
deleteDocument(id: string): Promise<void>;
|
|
26
|
+
listDocumentIds(): Promise<readonly string[]>;
|
|
27
|
+
newClient(options: ServiceOptions): TClient;
|
|
15
28
|
synchronize(timeoutMilliseconds?: number): Promise<void>;
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
// @alpha @sealed
|
|
19
|
-
export interface
|
|
20
|
-
readonly service:
|
|
32
|
+
export interface LocalServiceClient<out TService extends LocalService<ServiceClient> = LocalService<ServiceClient>> extends ServiceClient {
|
|
33
|
+
readonly service: TService;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
|
-
// @alpha
|
|
24
|
-
export
|
|
36
|
+
// @alpha @sealed
|
|
37
|
+
export interface SessionService extends LocalService<LocalServiceClient<SessionService>> {
|
|
38
|
+
}
|
|
25
39
|
|
|
26
40
|
// @alpha
|
|
27
41
|
export function startEphemeralService(isDefault?: boolean): EphemeralService;
|
package/dist/alpha.d.ts
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
export {
|
|
12
12
|
// #region @alpha APIs
|
|
13
13
|
EphemeralService,
|
|
14
|
-
|
|
14
|
+
LocalService,
|
|
15
|
+
LocalServiceClient,
|
|
16
|
+
SessionService,
|
|
15
17
|
cleanupEphemeralService,
|
|
16
18
|
getDefaultEphemeralService,
|
|
19
|
+
getSessionService,
|
|
17
20
|
startEphemeralService
|
|
18
21
|
// #endregion
|
|
19
22
|
} from "./index.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { IRequest } from "@fluidframework/core-interfaces";
|
|
6
6
|
import { type ErasedBaseType } from "@fluidframework/core-interfaces/internal";
|
|
7
|
-
import { type DataStoreKind, type DataStoreRegistry, type FluidContainerAttached, type FluidContainerWithService, type
|
|
7
|
+
import { type DataStoreKind, type DataStoreRegistry, type FluidContainerAttached, type FluidContainerWithService, type ServiceClient, type ServiceOptions } from "@fluidframework/driver-definitions/internal";
|
|
8
8
|
import { ServiceContainerBase } from "@fluidframework/runtime-utils/internal";
|
|
9
9
|
/**
|
|
10
10
|
* Starts and returns a new {@link EphemeralService}.
|
|
@@ -18,6 +18,20 @@ import { ServiceContainerBase } from "@fluidframework/runtime-utils/internal";
|
|
|
18
18
|
* @alpha
|
|
19
19
|
*/
|
|
20
20
|
export declare function startEphemeralService(isDefault?: boolean): EphemeralService;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the session-storage-backed local Fluid service for the current JavaScript realm, creating it on first use.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Attached documents remain available after a page reload within the same browser tab. Repeated calls return
|
|
26
|
+
* the same service instance within the current JavaScript realm.
|
|
27
|
+
*
|
|
28
|
+
* This service is only available in browser environments that provide `sessionStorage`.
|
|
29
|
+
* @returns A local service backed by browser session storage.
|
|
30
|
+
* @alpha
|
|
31
|
+
*/
|
|
32
|
+
export declare function getSessionService(): SessionService;
|
|
33
|
+
/** Closes and clears the current session service for test isolation. */
|
|
34
|
+
export declare function resetSessionServiceForTesting(): Promise<void>;
|
|
21
35
|
/**
|
|
22
36
|
* Cleans up the service passed in {@link startEphemeralService}, or the {@link getDefaultEphemeralService|default} if none is passed.
|
|
23
37
|
* @remarks
|
|
@@ -34,23 +48,91 @@ export declare function cleanupEphemeralService(service?: EphemeralService): Pro
|
|
|
34
48
|
*/
|
|
35
49
|
export declare function getDefaultEphemeralService(): EphemeralService;
|
|
36
50
|
/**
|
|
37
|
-
* Internal
|
|
38
|
-
*
|
|
51
|
+
* Internal options for creating a local service client, extending
|
|
52
|
+
* {@link @fluidframework/driver-definitions#ServiceOptions} with the service the client should connect to.
|
|
39
53
|
* @input
|
|
40
54
|
* @internal
|
|
41
55
|
*/
|
|
42
|
-
export interface
|
|
56
|
+
export interface LocalServiceOptions<TService extends LocalService = LocalService> extends ServiceOptions {
|
|
43
57
|
/**
|
|
44
|
-
*
|
|
58
|
+
* The service instance to connect to.
|
|
45
59
|
*/
|
|
46
|
-
readonly
|
|
60
|
+
readonly service: TService;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Internal options for creating a {@link LocalServiceClient} connected to an {@link EphemeralService}.
|
|
64
|
+
* @input
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export interface EphemeralServiceOptions extends LocalServiceOptions<EphemeralService> {
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A local Fluid service with an explicitly managed lifecycle.
|
|
71
|
+
* @remarks
|
|
72
|
+
* There are two implementations of this interface with different document lifetimes:
|
|
73
|
+
* {@link EphemeralService} and {@link SessionService}.
|
|
74
|
+
*
|
|
75
|
+
* @typeParam TClient - The type of client this service creates.
|
|
76
|
+
* @alpha @sealed
|
|
77
|
+
*/
|
|
78
|
+
export interface LocalService<out TClient extends ServiceClient = LocalServiceClient> extends ErasedBaseType<readonly ["LocalService", TClient]> {
|
|
47
79
|
/**
|
|
48
|
-
*
|
|
80
|
+
* Lists the IDs of documents currently stored by this service.
|
|
81
|
+
*/
|
|
82
|
+
listDocumentIds(): Promise<readonly string[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Deletes a stored document.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* Deletion is only allowed when this service has no open containers because resetting the local
|
|
88
|
+
* server invalidates all of its active connections. Content-addressed summary data shared with other
|
|
89
|
+
* documents may be retained until {@link LocalService.deleteAllDocuments} is called.
|
|
90
|
+
* Only one call to {@link LocalService.deleteDocument} or {@link LocalService.deleteAllDocuments}
|
|
91
|
+
* may be in progress at a time.
|
|
92
|
+
*
|
|
93
|
+
* @param id - The ID of the document to delete.
|
|
94
|
+
* @throws A `UsageError` if the service is closed, another document deletion is in progress, or the
|
|
95
|
+
* service has open containers.
|
|
96
|
+
*/
|
|
97
|
+
deleteDocument(id: string): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Deletes all documents stored by this service.
|
|
100
|
+
*
|
|
101
|
+
* @remarks
|
|
102
|
+
* Deletion is only allowed when this service has no open containers because resetting the local
|
|
103
|
+
* server invalidates all of its active connections.
|
|
104
|
+
* Only one call to {@link LocalService.deleteDocument} or {@link LocalService.deleteAllDocuments}
|
|
105
|
+
* may be in progress at a time.
|
|
106
|
+
* @throws A `UsageError` if the service is closed, another document deletion is in progress, or the
|
|
107
|
+
* service has open containers.
|
|
108
|
+
*/
|
|
109
|
+
deleteAllDocuments(): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Drives all containers connected to this service toward convergence, processing pending operations and
|
|
112
|
+
* waiting for all dirty containers to save.
|
|
113
|
+
*
|
|
114
|
+
* @param timeoutMilliseconds - The maximum time to wait for containers to quiesce, in milliseconds. Defaults to 30_000.
|
|
115
|
+
* @throws A `UsageError` if the containers do not quiesce before the timeout expires.
|
|
116
|
+
*
|
|
117
|
+
* @privateRemarks
|
|
118
|
+
* This is a best-effort implementation simplified from `LoaderContainerTracker.ensureSynchronized`.
|
|
119
|
+
* Currently it does not perform receiver-side sequence-number quiescence or wait for join/leave (audience) ops.
|
|
120
|
+
* See `LoaderContainerTracker.ensureSynchronized` for the fuller version this is based on.
|
|
121
|
+
*/
|
|
122
|
+
synchronize(timeoutMilliseconds?: number): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Creates a client connected to this service.
|
|
125
|
+
*
|
|
126
|
+
* @param options - Collaboration options for the client.
|
|
127
|
+
*/
|
|
128
|
+
newClient(options: ServiceOptions): TClient;
|
|
129
|
+
/**
|
|
130
|
+
* A client connected to this service using the default options.
|
|
49
131
|
*/
|
|
50
|
-
readonly
|
|
132
|
+
readonly defaultClient: TClient;
|
|
51
133
|
}
|
|
52
134
|
/**
|
|
53
|
-
* An in-memory Fluid service that can produce connected {@link
|
|
135
|
+
* An in-memory Fluid service that can produce connected {@link LocalServiceClient}s.
|
|
54
136
|
* @remarks
|
|
55
137
|
* All documents created through clients connected to a given `EphemeralService` are held in-memory by that service.
|
|
56
138
|
* Closing the service (via {@link EphemeralService.close} or {@link cleanupEphemeralService}) closes the connections
|
|
@@ -68,20 +150,20 @@ export interface EphemeralServiceOptions extends ServiceOptions {
|
|
|
68
150
|
* document with different `oldestSupportedClient` values.
|
|
69
151
|
* This also exposes a place to put APIs for preloading and exporting document contents in the future.
|
|
70
152
|
*
|
|
71
|
-
* This is an erased type: its only implementation is the module-private
|
|
153
|
+
* This is an erased type: its only implementation is the module-private `LocalServiceImplementation`, which holds
|
|
72
154
|
* the mutable server and container state so it does not appear on this public type.
|
|
73
155
|
*
|
|
74
156
|
* TODO: formalize this lifecycle with an interface which documents these stages.
|
|
75
157
|
* Lifecycle:
|
|
76
158
|
* The intended lifecycle of an {@link EphemeralService} follows roughly the same pattern as containers:
|
|
77
159
|
*
|
|
78
|
-
* 1. Open: accepts connections from {@link
|
|
160
|
+
* 1. Open: accepts connections from {@link LocalServiceClient}s, which can create and load containers.
|
|
79
161
|
* Might have timers and event registrations which can trigger asynchronous work, and retain the object in memory.
|
|
80
162
|
*
|
|
81
163
|
* 2. Closing: asynchronous transition from open to closed. New use should behave as it closed, but may be cleaning up or saving resources asynchronously.
|
|
82
164
|
* Timers and event registrations may still be active, but should be cleaned up by the time the transition to closed completes.
|
|
83
165
|
*
|
|
84
|
-
* 3. Closed: no longer accepts connections from {@link
|
|
166
|
+
* 3. Closed: no longer accepts connections from {@link LocalServiceClient}s, and all containers connected to it are closed.
|
|
85
167
|
* Should have no subscriptions to events or timers which could retain it in memory or trigger asynchronous work.
|
|
86
168
|
* The object can still be used in a limited capacity (typically just to inspect its status (e.g. `isClosed`), and to view (but not edit) the final state of any containers which were connected to it before it closed.)
|
|
87
169
|
*
|
|
@@ -90,75 +172,45 @@ export interface EphemeralServiceOptions extends ServiceOptions {
|
|
|
90
172
|
*
|
|
91
173
|
* @alpha @sealed
|
|
92
174
|
*/
|
|
93
|
-
export interface EphemeralService extends
|
|
94
|
-
/**
|
|
95
|
-
* Close this service, which closes all containers connected to it and releases its resources.
|
|
96
|
-
* @remarks
|
|
97
|
-
* All documents held by this service are discarded, and any timers it (or its containers) were keeping alive
|
|
98
|
-
* are cleaned up.
|
|
99
|
-
* The returned promise resolves once all asynchronous cleanup (including shutting down the in-memory server)
|
|
100
|
-
* has completed.
|
|
101
|
-
* Closing is idempotent: calling it again after the service is closed resolves without doing anything.
|
|
102
|
-
*/
|
|
103
|
-
close(): Promise<void>;
|
|
104
|
-
/**
|
|
105
|
-
* Drives all containers connected to this service toward convergence, processing pending operations and
|
|
106
|
-
* waiting for all dirty containers to save.
|
|
107
|
-
*
|
|
108
|
-
* @param timeoutMilliseconds - The maximum time to wait for containers to quiesce, in milliseconds. Defaults to 30_000.
|
|
109
|
-
*
|
|
110
|
-
* @privateRemarks
|
|
111
|
-
* This is a best-effort implementation simplified from `LoaderContainerTracker.ensureSynchronized`.
|
|
112
|
-
* Currently it does not perform receiver-side sequence-number quiescence or wait for join/leave (audience) ops.
|
|
113
|
-
* See `LoaderContainerTracker.ensureSynchronized` for the fuller version this is based on.
|
|
114
|
-
* For the currently exposed API surface, this should be sufficient,
|
|
115
|
-
* but users down casting to internal types might run into some limitations.
|
|
116
|
-
*/
|
|
117
|
-
synchronize(timeoutMilliseconds?: number): Promise<void>;
|
|
175
|
+
export interface EphemeralService extends LocalService<LocalServiceClient<EphemeralService>> {
|
|
118
176
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* @param options - Options for the client. `oldestSupportedClient` may be omitted because all
|
|
122
|
-
* clients are in the same process, so it defaults to the current version. `service` may be omitted to allocate a new
|
|
123
|
-
* {@link EphemeralService} dedicated to this client, or provided to connect the client to an existing service instance.
|
|
177
|
+
* Closes all containers connected to this service and releases its active resources.
|
|
124
178
|
*
|
|
125
179
|
* @remarks
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* A document created and attached (obtaining an `id`) can be loaded by `id` for as long as its service remains open,
|
|
129
|
-
* even after every container for it has been closed.
|
|
130
|
-
* Closing the service (via {@link EphemeralService.close} or {@link cleanupEphemeralService}) discards all of its
|
|
131
|
-
* documents and releases its resources; afterwards those `id`s can no longer be loaded.
|
|
132
|
-
*
|
|
133
|
-
* When no `service` is provided, a new one is allocated for this client (accessible via {@link EphemeralServiceClient.service}).
|
|
134
|
-
* Provide the same {@link EphemeralService} to multiple clients (via `options.service`) to have them collaborate on the
|
|
135
|
-
* same documents, and control that service's lifetime explicitly.
|
|
136
|
-
*
|
|
137
|
-
* Since a service holds timers while open, tests should close the services they use (e.g. via
|
|
138
|
-
* {@link cleanupEphemeralService} in an `afterEach`) to avoid lingering timers that can hang test runners.
|
|
139
|
-
*
|
|
140
|
-
* @privateRemarks
|
|
141
|
-
* TODO: We should provide a way to extract (for potential serialization as test data) and load documents into a service.
|
|
142
|
-
* This is needed to use this API surface for testing reference documents.
|
|
143
|
-
* Ideally we would provide a service agnostic way to do the export, but likely only support loading them into the local service.
|
|
144
|
-
* This can be done via an API on FluidContainer (or a free function taking one) to do the export, then adding a
|
|
145
|
-
* service specific API (on {@link EphemeralService}) to load from the export format and return the ID of the loaded document.
|
|
146
|
-
*/
|
|
147
|
-
newClient(options: ServiceOptions): EphemeralServiceClient;
|
|
148
|
-
/**
|
|
149
|
-
* A client connected to this service using the default options.
|
|
180
|
+
* Closing is idempotent. Closing an ephemeral service permanently discards all documents it holds,
|
|
181
|
+
* so their IDs can no longer be loaded.
|
|
150
182
|
*/
|
|
151
|
-
|
|
183
|
+
close(): Promise<void>;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* A browser-local Fluid service that persists documents in session storage.
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* Its attached documents remain available after a page reload within the same browser tab. The service is
|
|
190
|
+
* shared within the current JavaScript realm and intentionally has no close operation: its active resources
|
|
191
|
+
* live until the realm is unloaded.
|
|
192
|
+
*
|
|
193
|
+
* Session storage is shared more broadly than JavaScript module state. Separate same-origin realms, such as
|
|
194
|
+
* same-origin frames, or applications that load separate copies of this package can access the same stored
|
|
195
|
+
* documents while running independent local servers. Concurrently editing the same document from such realms
|
|
196
|
+
* is unsupported and may produce inconsistent stored state.
|
|
197
|
+
*
|
|
198
|
+
* Create one with {@link getSessionService}.
|
|
199
|
+
* @alpha @sealed
|
|
200
|
+
*/
|
|
201
|
+
export interface SessionService extends LocalService<LocalServiceClient<SessionService>> {
|
|
152
202
|
}
|
|
153
203
|
/**
|
|
154
|
-
* A {@link @fluidframework/driver-definitions#ServiceClient} connected to a specific {@link
|
|
204
|
+
* A {@link @fluidframework/driver-definitions#ServiceClient} connected to a specific {@link LocalService}.
|
|
205
|
+
*
|
|
206
|
+
* @typeParam TService - The type of local service this client is connected to.
|
|
155
207
|
* @alpha @sealed
|
|
156
208
|
*/
|
|
157
|
-
export interface
|
|
209
|
+
export interface LocalServiceClient<out TService extends LocalService<ServiceClient> = LocalService<ServiceClient>> extends ServiceClient {
|
|
158
210
|
/**
|
|
159
211
|
* The service instance this client is connected to.
|
|
160
212
|
*/
|
|
161
|
-
readonly service:
|
|
213
|
+
readonly service: TService;
|
|
162
214
|
}
|
|
163
215
|
/**
|
|
164
216
|
* A Fluid container backed by an ephemeral (in-memory) local service, implementing
|
|
@@ -170,10 +222,10 @@ export interface EphemeralServiceClient extends ServiceClient {
|
|
|
170
222
|
*
|
|
171
223
|
* @internal
|
|
172
224
|
*/
|
|
173
|
-
export declare class EphemeralServiceContainer<TData> extends ServiceContainerBase<TData,
|
|
174
|
-
readonly service:
|
|
175
|
-
static createDetached<T>(registry: DataStoreRegistry<T>, options:
|
|
176
|
-
static load<T>(registry: DataStoreRegistry<T>, options:
|
|
225
|
+
export declare class EphemeralServiceContainer<TData> extends ServiceContainerBase<TData, LocalServiceOptions> implements FluidContainerWithService<TData> {
|
|
226
|
+
readonly service: LocalService;
|
|
227
|
+
static createDetached<T>(registry: DataStoreRegistry<T>, options: LocalServiceOptions, root: DataStoreKind<T>): Promise<EphemeralServiceContainer<T>>;
|
|
228
|
+
static load<T>(registry: DataStoreRegistry<T>, options: LocalServiceOptions, id: string): Promise<EphemeralServiceContainer<T> & FluidContainerAttached<T>>;
|
|
177
229
|
private constructor();
|
|
178
230
|
close(): void;
|
|
179
231
|
protected createAttachRequest(): IRequest;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ephemeralService.d.ts","sourceRoot":"","sources":["../src/ephemeralService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAEN,KAAK,cAAc,EACnB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAEN,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,
|
|
1
|
+
{"version":3,"file":"ephemeralService.d.ts","sourceRoot":"","sources":["../src/ephemeralService.ts"],"names":[],"mappings":"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"}
|
package/dist/ephemeralService.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.EphemeralServiceContainer = void 0;
|
|
8
8
|
exports.startEphemeralService = startEphemeralService;
|
|
9
|
+
exports.getSessionService = getSessionService;
|
|
10
|
+
exports.resetSessionServiceForTesting = resetSessionServiceForTesting;
|
|
9
11
|
exports.cleanupEphemeralService = cleanupEphemeralService;
|
|
10
12
|
exports.getDefaultEphemeralService = getDefaultEphemeralService;
|
|
11
13
|
const internal_1 = require("@fluidframework/container-definitions/internal");
|
|
@@ -17,8 +19,10 @@ const internal_6 = require("@fluidframework/driver-definitions/internal");
|
|
|
17
19
|
const internal_7 = require("@fluidframework/runtime-utils/internal");
|
|
18
20
|
const server_local_server_1 = require("@fluidframework/server-local-server");
|
|
19
21
|
const internal_8 = require("@fluidframework/driver-utils/internal");
|
|
22
|
+
const uuid_1 = require("uuid");
|
|
20
23
|
const localDocumentServiceFactory_js_1 = require("./localDocumentServiceFactory.js");
|
|
21
24
|
const localResolver_js_1 = require("./localResolver.js");
|
|
25
|
+
const localSessionStorageDb_js_1 = require("./localSessionStorageDb.js");
|
|
22
26
|
const packageVersion_js_1 = require("./packageVersion.js");
|
|
23
27
|
/**
|
|
24
28
|
* Starts and returns a new {@link EphemeralService}.
|
|
@@ -35,12 +39,34 @@ function startEphemeralService(isDefault = true) {
|
|
|
35
39
|
if (isDefault && defaultEphemeralService) {
|
|
36
40
|
throw new internal_8.UsageError("A default EphemeralService is already running");
|
|
37
41
|
}
|
|
38
|
-
const service = new
|
|
42
|
+
const service = new LocalServiceImplementation();
|
|
39
43
|
if (isDefault) {
|
|
40
44
|
defaultEphemeralService = service;
|
|
41
45
|
}
|
|
42
46
|
return service;
|
|
43
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets the session-storage-backed local Fluid service for the current JavaScript realm, creating it on first use.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* Attached documents remain available after a page reload within the same browser tab. Repeated calls return
|
|
53
|
+
* the same service instance within the current JavaScript realm.
|
|
54
|
+
*
|
|
55
|
+
* This service is only available in browser environments that provide `sessionStorage`.
|
|
56
|
+
* @returns A local service backed by browser session storage.
|
|
57
|
+
* @alpha
|
|
58
|
+
*/
|
|
59
|
+
function getSessionService() {
|
|
60
|
+
if (typeof sessionStorage === "undefined") {
|
|
61
|
+
throw new internal_8.UsageError("SessionService requires browser session storage");
|
|
62
|
+
}
|
|
63
|
+
return (sessionService ??= new LocalServiceImplementation(new localSessionStorageDb_js_1.LocalSessionStorageDbFactory()));
|
|
64
|
+
}
|
|
65
|
+
/** Closes and clears the current session service for test isolation. */
|
|
66
|
+
async function resetSessionServiceForTesting() {
|
|
67
|
+
await sessionService?.close();
|
|
68
|
+
sessionService = undefined;
|
|
69
|
+
}
|
|
44
70
|
/**
|
|
45
71
|
* Cleans up the service passed in {@link startEphemeralService}, or the {@link getDefaultEphemeralService|default} if none is passed.
|
|
46
72
|
* @remarks
|
|
@@ -75,31 +101,59 @@ function getDefaultEphemeralService() {
|
|
|
75
101
|
*/
|
|
76
102
|
let defaultEphemeralService;
|
|
77
103
|
/**
|
|
78
|
-
* The
|
|
104
|
+
* The lazily created session service for this JavaScript realm.
|
|
105
|
+
*/
|
|
106
|
+
let sessionService;
|
|
107
|
+
/**
|
|
108
|
+
* The concrete implementation of local services.
|
|
79
109
|
* @remarks
|
|
80
110
|
* Kept module-private so its mutable state and internal helpers are not part of the public API.
|
|
81
|
-
* Narrow
|
|
111
|
+
* Narrow a {@link LocalService} to it with `LocalServiceImplementation.narrow`.
|
|
82
112
|
*/
|
|
83
|
-
class
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
class LocalServiceImplementation extends internal_4.ErasedTypeImplementation {
|
|
114
|
+
/**
|
|
115
|
+
* The active in-memory server shared by this service's containers so they can communicate.
|
|
116
|
+
* Replaced after document maintenance resets the server.
|
|
117
|
+
*/
|
|
118
|
+
server;
|
|
119
|
+
/** The document service factory bound to the active {@link LocalServiceImplementation.server}. */
|
|
120
|
+
documentServiceFactory;
|
|
121
|
+
/** The persistent database factory retained when the active server is replaced. */
|
|
122
|
+
databaseFactory;
|
|
123
|
+
/** The open containers connected to this service. */
|
|
89
124
|
containers = new Set();
|
|
125
|
+
/** Whether this service has been permanently closed. */
|
|
90
126
|
closed = false;
|
|
91
|
-
|
|
127
|
+
/** Whether document deletion is temporarily preventing other service operations. */
|
|
128
|
+
maintenanceInProgress = false;
|
|
129
|
+
constructor(databaseFactory) {
|
|
92
130
|
super();
|
|
131
|
+
this.server = server_local_server_1.LocalDeltaConnectionServer.create(databaseFactory);
|
|
132
|
+
this.databaseFactory = this.server.testDbFactory;
|
|
133
|
+
this.documentServiceFactory = new localDocumentServiceFactory_js_1.LocalDocumentServiceFactory(this.server);
|
|
93
134
|
this.defaultClient = this.newClient();
|
|
94
135
|
}
|
|
95
136
|
newClient(options) {
|
|
96
137
|
const finalOptions = {
|
|
138
|
+
...options,
|
|
97
139
|
oldestSupportedClient: options?.oldestSupportedClient ?? (0, internal_6.featureVersion)(packageVersion_js_1.pkgVersion),
|
|
98
140
|
service: this,
|
|
99
141
|
};
|
|
100
|
-
return new
|
|
142
|
+
return new LocalServiceClientImplementation(finalOptions);
|
|
101
143
|
}
|
|
102
144
|
defaultClient;
|
|
145
|
+
async listDocumentIds() {
|
|
146
|
+
this.ensureAvailable();
|
|
147
|
+
const documentCollection = await this.server.databaseManager.getDocumentCollection();
|
|
148
|
+
const documents = await documentCollection.findAll();
|
|
149
|
+
return documents.map((document) => document.documentId);
|
|
150
|
+
}
|
|
151
|
+
async deleteDocument(id) {
|
|
152
|
+
await this.deleteDocuments(id);
|
|
153
|
+
}
|
|
154
|
+
async deleteAllDocuments() {
|
|
155
|
+
await this.deleteDocuments();
|
|
156
|
+
}
|
|
103
157
|
async close() {
|
|
104
158
|
if (this.closed) {
|
|
105
159
|
return;
|
|
@@ -116,6 +170,57 @@ class EphemeralServiceImplementation extends internal_4.ErasedTypeImplementation
|
|
|
116
170
|
// server rather than any container, so closing containers alone would leave them running.
|
|
117
171
|
await this.server.close();
|
|
118
172
|
}
|
|
173
|
+
ensureAvailable() {
|
|
174
|
+
if (this.closed) {
|
|
175
|
+
throw new internal_8.UsageError("Local service is closed");
|
|
176
|
+
}
|
|
177
|
+
if (this.maintenanceInProgress) {
|
|
178
|
+
throw new internal_8.UsageError("Local service document maintenance is already in progress");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Delete all documents unless `id` is specified, in which case only that document is deleted.
|
|
183
|
+
*/
|
|
184
|
+
async deleteDocuments(id) {
|
|
185
|
+
this.ensureAvailable();
|
|
186
|
+
if (this.containers.size > 0) {
|
|
187
|
+
throw new internal_8.UsageError("Close all containers before deleting local service documents");
|
|
188
|
+
}
|
|
189
|
+
this.maintenanceInProgress = true;
|
|
190
|
+
let serverClosed = false;
|
|
191
|
+
try {
|
|
192
|
+
await this.server.close();
|
|
193
|
+
serverClosed = true;
|
|
194
|
+
const databaseManager = this.server.databaseManager;
|
|
195
|
+
const filter = id === undefined ? {} : { documentId: id };
|
|
196
|
+
const historianDatabase = this.databaseFactory.testDatabase;
|
|
197
|
+
const documentCollection = await databaseManager.getDocumentCollection();
|
|
198
|
+
const checkpointCollection = await databaseManager.getCheckpointCollection();
|
|
199
|
+
const deltaCollection = await databaseManager.getDeltaCollection(undefined, id);
|
|
200
|
+
const scribeDeltaCollection = await databaseManager.getScribeDeltaCollection(undefined, id);
|
|
201
|
+
const deletions = [
|
|
202
|
+
documentCollection.deleteMany(filter),
|
|
203
|
+
checkpointCollection.deleteMany(filter),
|
|
204
|
+
deltaCollection.deleteMany(filter),
|
|
205
|
+
scribeDeltaCollection.deleteMany(filter),
|
|
206
|
+
historianDatabase
|
|
207
|
+
.collection("refs")
|
|
208
|
+
.deleteMany(id === undefined ? {} : { _id: `heads/${id}` }),
|
|
209
|
+
];
|
|
210
|
+
if (id === undefined) {
|
|
211
|
+
const nodeCollection = await databaseManager.getNodeCollection();
|
|
212
|
+
deletions.push(nodeCollection.deleteMany({}), historianDatabase.collection("blobs").deleteMany({}), historianDatabase.collection("commits").deleteMany({}), historianDatabase.collection("trees").deleteMany({}));
|
|
213
|
+
}
|
|
214
|
+
await Promise.all(deletions);
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
if (serverClosed) {
|
|
218
|
+
this.server = server_local_server_1.LocalDeltaConnectionServer.create(this.databaseFactory);
|
|
219
|
+
this.documentServiceFactory = new localDocumentServiceFactory_js_1.LocalDocumentServiceFactory(this.server);
|
|
220
|
+
}
|
|
221
|
+
this.maintenanceInProgress = false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
119
224
|
async synchronize(timeoutMilliseconds = 30_000) {
|
|
120
225
|
// Timeout to allow for better errors in the case of hangs.
|
|
121
226
|
let timedOut = false;
|
|
@@ -188,6 +293,7 @@ class EphemeralServiceImplementation extends internal_4.ErasedTypeImplementation
|
|
|
188
293
|
* @remarks Internal helper for {@link EphemeralServiceContainer}; not part of the public {@link EphemeralService} API.
|
|
189
294
|
*/
|
|
190
295
|
getDocumentServiceFactory() {
|
|
296
|
+
this.ensureAvailable();
|
|
191
297
|
(0, internal_5.assert)(!this.closed, 0xd11 /* Cannot create or load containers on a closed EphemeralService */);
|
|
192
298
|
return this.documentServiceFactory;
|
|
193
299
|
}
|
|
@@ -206,7 +312,7 @@ class EphemeralServiceImplementation extends internal_4.ErasedTypeImplementation
|
|
|
206
312
|
this.containers.delete(container);
|
|
207
313
|
}
|
|
208
314
|
}
|
|
209
|
-
class
|
|
315
|
+
class LocalServiceClientImplementation extends internal_7.ServiceClientImplementation {
|
|
210
316
|
service;
|
|
211
317
|
constructor(options) {
|
|
212
318
|
super(options, EphemeralServiceContainer);
|
|
@@ -242,7 +348,6 @@ const urlResolver = new localResolver_js_1.LocalResolver();
|
|
|
242
348
|
const createLoadExistingRequest = (documentId) => {
|
|
243
349
|
return { url: `http://localhost:3000/${documentId}` };
|
|
244
350
|
};
|
|
245
|
-
let documentIdCounter = 0;
|
|
246
351
|
/**
|
|
247
352
|
* A Fluid container backed by an ephemeral (in-memory) local service, implementing
|
|
248
353
|
* {@link @fluidframework/driver-definitions#FluidContainerWithService}.
|
|
@@ -256,9 +361,10 @@ let documentIdCounter = 0;
|
|
|
256
361
|
class EphemeralServiceContainer extends internal_7.ServiceContainerBase {
|
|
257
362
|
service;
|
|
258
363
|
static async createDetached(registry, options, root) {
|
|
259
|
-
|
|
364
|
+
LocalServiceImplementation.narrow(options.service);
|
|
260
365
|
const container = await (0, internal_2.createDetachedContainer)({
|
|
261
366
|
codeDetails: { package: "1.0" },
|
|
367
|
+
logger: options.logger,
|
|
262
368
|
urlResolver,
|
|
263
369
|
documentServiceFactory: options.service.getDocumentServiceFactory(),
|
|
264
370
|
codeLoader: (0, internal_7.makeCodeLoader)(registry, options.oldestSupportedClient, containerRuntimeLoader, root),
|
|
@@ -266,9 +372,10 @@ class EphemeralServiceContainer extends internal_7.ServiceContainerBase {
|
|
|
266
372
|
return new EphemeralServiceContainer(registry, options, container, (await container.getEntryPoint()), undefined);
|
|
267
373
|
}
|
|
268
374
|
static async load(registry, options, id) {
|
|
269
|
-
|
|
375
|
+
LocalServiceImplementation.narrow(options.service);
|
|
270
376
|
const containerInner = await (0, internal_2.loadExistingContainer)({
|
|
271
377
|
request: createLoadExistingRequest(id),
|
|
378
|
+
logger: options.logger,
|
|
272
379
|
urlResolver,
|
|
273
380
|
documentServiceFactory: options.service.getDocumentServiceFactory(),
|
|
274
381
|
codeLoader: (0, internal_7.makeCodeLoader)(registry, options.oldestSupportedClient, containerRuntimeLoader),
|
|
@@ -280,18 +387,17 @@ class EphemeralServiceContainer extends internal_7.ServiceContainerBase {
|
|
|
280
387
|
constructor(registry, options, container, data, id) {
|
|
281
388
|
super(registry, options, container, data, id);
|
|
282
389
|
this.service = options.service;
|
|
283
|
-
|
|
390
|
+
LocalServiceImplementation.narrow(this.service);
|
|
284
391
|
this.service.addContainer(this);
|
|
285
392
|
}
|
|
286
393
|
close() {
|
|
287
394
|
super.close();
|
|
288
395
|
// Remove this now-closed container from its service's set of open containers.
|
|
289
|
-
|
|
396
|
+
LocalServiceImplementation.narrow(this.service);
|
|
290
397
|
this.service.removeContainer(this);
|
|
291
398
|
}
|
|
292
399
|
createAttachRequest() {
|
|
293
|
-
|
|
294
|
-
return (0, localResolver_js_1.createLocalResolverCreateNewRequest)(documentId);
|
|
400
|
+
return (0, localResolver_js_1.createLocalResolverCreateNewRequest)((0, uuid_1.v4)());
|
|
295
401
|
}
|
|
296
402
|
}
|
|
297
403
|
exports.EphemeralServiceContainer = EphemeralServiceContainer;
|