@sanity/mutator 5.0.0-next.0-9b570ece82-202507150640 → 5.0.0-next.6
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/LICENSE +1 -1
- package/lib/index.js +31 -28
- package/lib/index.js.map +1 -1
- package/package.json +17 -15
- package/lib/index.d.mts +0 -377
- package/lib/index.mjs +0 -1820
- package/lib/index.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/mutator",
|
|
3
|
-
"version": "5.0.0-next.
|
|
3
|
+
"version": "5.0.0-next.6+1bbf0c8944",
|
|
4
4
|
"description": "A set of models to make it easier to utilize the powerful real time collaborative features of Sanity",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -22,18 +22,15 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"author": "Sanity.io <hello@sanity.io>",
|
|
24
24
|
"sideEffects": false,
|
|
25
|
-
"type": "
|
|
25
|
+
"type": "module",
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
28
28
|
"source": "./src/index.ts",
|
|
29
|
-
"import": "./lib/index.mjs",
|
|
30
|
-
"require": "./lib/index.js",
|
|
31
29
|
"default": "./lib/index.js"
|
|
32
30
|
},
|
|
33
31
|
"./package.json": "./package.json"
|
|
34
32
|
},
|
|
35
33
|
"main": "./lib/index.js",
|
|
36
|
-
"module": "./lib/index.mjs",
|
|
37
34
|
"types": "./lib/index.d.ts",
|
|
38
35
|
"files": [
|
|
39
36
|
"lib"
|
|
@@ -41,25 +38,30 @@
|
|
|
41
38
|
"dependencies": {
|
|
42
39
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
43
40
|
"@sanity/uuid": "^3.0.2",
|
|
44
|
-
"debug": "^4.3
|
|
41
|
+
"debug": "^4.4.3",
|
|
45
42
|
"lodash": "^4.17.21",
|
|
46
|
-
"@sanity/types": "5.0.0-next.
|
|
43
|
+
"@sanity/types": "5.0.0-next.6+1bbf0c8944"
|
|
47
44
|
},
|
|
48
45
|
"devDependencies": {
|
|
49
|
-
"@
|
|
50
|
-
"@types/
|
|
46
|
+
"@sanity/pkg-utils": "^10.2.1",
|
|
47
|
+
"@types/debug": "^4.1.12",
|
|
48
|
+
"@types/lodash": "^4.17.20",
|
|
49
|
+
"@types/node": "^24.3.0",
|
|
50
|
+
"@typescript/native-preview": "7.0.0-dev.20251128.1",
|
|
51
|
+
"eslint": "^9.39.1",
|
|
51
52
|
"rimraf": "^5.0.10",
|
|
52
|
-
"vitest": "^3.2.
|
|
53
|
-
"@repo/eslint-config": "5.0.0-next.
|
|
54
|
-
"@repo/package.config": "5.0.0-next.
|
|
55
|
-
"@repo/
|
|
53
|
+
"vitest": "^3.2.4",
|
|
54
|
+
"@repo/eslint-config": "5.0.0-next.6+1bbf0c8944",
|
|
55
|
+
"@repo/package.config": "5.0.0-next.6+1bbf0c8944",
|
|
56
|
+
"@repo/tsconfig": "5.0.0-next.6+1bbf0c8944",
|
|
57
|
+
"@repo/test-config": "5.0.0-next.6+1bbf0c8944"
|
|
56
58
|
},
|
|
57
59
|
"scripts": {
|
|
58
60
|
"build": "pkg-utils build --strict --check --clean",
|
|
59
|
-
"check:types": "(cd ../../.. &&
|
|
61
|
+
"check:types": "(cd ../../.. && tsgo --project packages/@sanity/mutator/tsconfig.lib.json --erasableSyntaxOnly)",
|
|
60
62
|
"clean": "rimraf lib",
|
|
61
63
|
"lint": "eslint .",
|
|
62
|
-
"perf": "node ./perf/run.
|
|
64
|
+
"perf": "node ./perf/run.cjs",
|
|
63
65
|
"test": "vitest run",
|
|
64
66
|
"watch": "pkg-utils watch"
|
|
65
67
|
}
|
package/lib/index.d.mts
DELETED
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import {CreateIfNotExistsMutation} from '@sanity/types'
|
|
2
|
-
import {CreateMutation} from '@sanity/types'
|
|
3
|
-
import {CreateOrReplaceMutation} from '@sanity/types'
|
|
4
|
-
import {DeleteMutation} from '@sanity/types'
|
|
5
|
-
import {PatchMutation} from '@sanity/types'
|
|
6
|
-
import {Path} from '@sanity/types'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Converts a path in array form to a JSONPath string
|
|
10
|
-
*
|
|
11
|
-
* @param pathArray - Array of path segments
|
|
12
|
-
* @returns String representation of the path
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
export declare function arrayToJSONMatchPath(pathArray: Path): string
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
export declare class BufferedDocument {
|
|
21
|
-
private mutations
|
|
22
|
-
/**
|
|
23
|
-
* The Document we are wrapping
|
|
24
|
-
*/
|
|
25
|
-
document: Document_2
|
|
26
|
-
/**
|
|
27
|
-
* The Document with local changes applied
|
|
28
|
-
*/
|
|
29
|
-
LOCAL: Doc | null
|
|
30
|
-
/**
|
|
31
|
-
* Commits that are waiting to be delivered to the server
|
|
32
|
-
*/
|
|
33
|
-
private commits
|
|
34
|
-
/**
|
|
35
|
-
* Local mutations that are not scheduled to be committed yet
|
|
36
|
-
*/
|
|
37
|
-
buffer: SquashingBuffer
|
|
38
|
-
/**
|
|
39
|
-
* Assignable event handler for when the buffered document applies a mutation
|
|
40
|
-
*/
|
|
41
|
-
onMutation?: (message: {mutation: Mutation; document: Doc | null; remote: boolean}) => void
|
|
42
|
-
/**
|
|
43
|
-
* Assignable event handler for when a remote mutation happened
|
|
44
|
-
*/
|
|
45
|
-
onRemoteMutation?: Document_2['onRemoteMutation']
|
|
46
|
-
/**
|
|
47
|
-
* Assignable event handler for when the buffered document rebased
|
|
48
|
-
*/
|
|
49
|
-
onRebase?: (localDoc: Doc | null, remoteMutations: Mut[], localMutations: Mut[]) => void
|
|
50
|
-
/**
|
|
51
|
-
* Assignable event handler for when the document is deleted
|
|
52
|
-
*/
|
|
53
|
-
onDelete?: (doc: Doc | null) => void
|
|
54
|
-
/**
|
|
55
|
-
* Assignable event handler for when the state of consistency changed
|
|
56
|
-
*/
|
|
57
|
-
onConsistencyChanged?: (isConsistent: boolean) => void
|
|
58
|
-
/**
|
|
59
|
-
* Assignable event handler for when the buffered document should commit changes
|
|
60
|
-
*/
|
|
61
|
-
commitHandler?: (msg: CommitHandlerMessage) => void
|
|
62
|
-
/**
|
|
63
|
-
* Whether or not we are currently commiting
|
|
64
|
-
*/
|
|
65
|
-
committerRunning: boolean
|
|
66
|
-
constructor(doc: Doc | null)
|
|
67
|
-
reset(doc: Doc | null): void
|
|
68
|
-
add(mutation: Mutation): void
|
|
69
|
-
arrive(mutation: Mutation): void
|
|
70
|
-
commit(): Promise<void>
|
|
71
|
-
performCommits(): void
|
|
72
|
-
_cycleCommitter(): void
|
|
73
|
-
handleDocRebase(edge: Doc | null, remoteMutations: Mutation[], localMutations: Mutation[]): void
|
|
74
|
-
handleDocumentDeleted(): void
|
|
75
|
-
handleDocMutation(msg: {mutation: Mutation; document: Doc | null; remote: boolean}): void
|
|
76
|
-
rebase(remoteMutations: Mutation[], localMutations: Mutation[]): void
|
|
77
|
-
handleDocConsistencyChanged(isConsistent: boolean): void
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @internal
|
|
82
|
-
*/
|
|
83
|
-
export declare interface CommitHandlerMessage {
|
|
84
|
-
mutation: Mutation
|
|
85
|
-
success: () => void
|
|
86
|
-
failure: () => void
|
|
87
|
-
cancel: (error: Error) => void
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Sanity document with a guaranteed `_id` and `_type`
|
|
92
|
-
*
|
|
93
|
-
* @internal
|
|
94
|
-
*/
|
|
95
|
-
export declare interface Doc {
|
|
96
|
-
_id: string
|
|
97
|
-
_type: string
|
|
98
|
-
_rev?: string
|
|
99
|
-
_updatedAt?: string
|
|
100
|
-
_createdAt?: string
|
|
101
|
-
[attribute: string]: unknown
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Models a document as it is changed by our own local patches and remote patches coming in from
|
|
106
|
-
* the server. Consolidates incoming patches with our own submitted patches and maintains two
|
|
107
|
-
* versions of the document. EDGE is the optimistic document that the user sees that will always
|
|
108
|
-
* immediately reflect whatever she is doing to it, and HEAD which is the confirmed version of the
|
|
109
|
-
* document consistent with the mutations we have received from the server. As long as nothing out of
|
|
110
|
-
* the ordinary happens, we can track all changes by hooking into the onMutation callback, but we
|
|
111
|
-
* must also respect onRebase events that fire when we have to backtrack because one of our optimistically
|
|
112
|
-
* applied patches were rejected, or some bastard was able to slip a mutation in between ours own.
|
|
113
|
-
*
|
|
114
|
-
* @internal
|
|
115
|
-
*/
|
|
116
|
-
declare class Document_2 {
|
|
117
|
-
/**
|
|
118
|
-
* Incoming patches from the server waiting to be applied to HEAD
|
|
119
|
-
*/
|
|
120
|
-
incoming: Mutation[]
|
|
121
|
-
/**
|
|
122
|
-
* Patches we know has been subitted to the server, but has not been seen yet in the return channel
|
|
123
|
-
* so we can't be sure about the ordering yet (someone else might have slipped something between them)
|
|
124
|
-
*/
|
|
125
|
-
submitted: Mutation[]
|
|
126
|
-
/**
|
|
127
|
-
* Pending mutations
|
|
128
|
-
*/
|
|
129
|
-
pending: Mutation[]
|
|
130
|
-
/**
|
|
131
|
-
* Our model of the document according to the incoming patches from the server
|
|
132
|
-
*/
|
|
133
|
-
HEAD: Doc | null
|
|
134
|
-
/**
|
|
135
|
-
* Our optimistic model of what the document will probably look like as soon as all our patches
|
|
136
|
-
* have been processed. Updated every time we stage a new mutation, but also might revert back
|
|
137
|
-
* to previous states if our mutations fail, or could change if unexpected mutations arrive
|
|
138
|
-
* between our own. The `onRebase` callback will be called when EDGE changes in this manner.
|
|
139
|
-
*/
|
|
140
|
-
EDGE: Doc | null
|
|
141
|
-
/**
|
|
142
|
-
* Called with the EDGE document when that document changes for a reason other than us staging
|
|
143
|
-
* a new patch or receiving a mutation from the server while our EDGE is in sync with HEAD:
|
|
144
|
-
* I.e. when EDGE changes because the order of mutations has changed in relation to our
|
|
145
|
-
* optimistic predictions.
|
|
146
|
-
*/
|
|
147
|
-
onRebase?: (edge: Doc | null, incomingMutations: Mutation[], pendingMutations: Mutation[]) => void
|
|
148
|
-
/**
|
|
149
|
-
* Called when we receive a patch in the normal order of things, but the mutation is not ours
|
|
150
|
-
*/
|
|
151
|
-
onMutation?: (msg: {mutation: Mutation; document: Doc | null; remote: boolean}) => void
|
|
152
|
-
/**
|
|
153
|
-
* Called when consistency state changes with the boolean value of the current consistency state
|
|
154
|
-
*/
|
|
155
|
-
onConsistencyChanged?: (isConsistent: boolean) => void
|
|
156
|
-
/**
|
|
157
|
-
* Called whenever a new incoming mutation comes in. These are always ordered correctly.
|
|
158
|
-
*/
|
|
159
|
-
onRemoteMutation?: (mut: Mutation) => void
|
|
160
|
-
/**
|
|
161
|
-
* We are consistent when there are no unresolved mutations of our own, and no un-applicable
|
|
162
|
-
* incoming mutations. When this has been going on for too long, and there has been a while
|
|
163
|
-
* since we staged a new mutation, it is time to reset your state.
|
|
164
|
-
*/
|
|
165
|
-
inconsistentAt: Date | null
|
|
166
|
-
/**
|
|
167
|
-
* The last time we staged a patch of our own. If we have been inconsistent for a while, but it
|
|
168
|
-
* hasn't been long since we staged a new mutation, the reason is probably just because the user
|
|
169
|
-
* is typing or something.
|
|
170
|
-
*
|
|
171
|
-
* Should be used as a guard against resetting state for inconsistency reasons.
|
|
172
|
-
*/
|
|
173
|
-
lastStagedAt: Date | null
|
|
174
|
-
constructor(doc: Doc | null)
|
|
175
|
-
reset(doc: Doc | null): void
|
|
176
|
-
arrive(mutation: Mutation): void
|
|
177
|
-
stage(mutation: Mutation, silent?: boolean): SubmissionResponder
|
|
178
|
-
isConsistent(): boolean
|
|
179
|
-
considerIncoming(): void
|
|
180
|
-
updateConsistencyFlag(): void
|
|
181
|
-
applyIncoming(mut: Mutation | undefined): boolean
|
|
182
|
-
/**
|
|
183
|
-
* Returns true if there are unresolved mutations between HEAD and EDGE, meaning we have
|
|
184
|
-
* mutations that are still waiting to be either submitted, or to be confirmed by the server.
|
|
185
|
-
*
|
|
186
|
-
* @returns true if there are unresolved mutations between HEAD and EDGE, false otherwise
|
|
187
|
-
*/
|
|
188
|
-
hasUnresolvedMutations(): boolean
|
|
189
|
-
/**
|
|
190
|
-
* When an incoming mutation is applied to HEAD, this is called to remove the mutation from
|
|
191
|
-
* the unresolved state. If the newly applied patch is the next upcoming unresolved mutation,
|
|
192
|
-
* no rebase is needed, but we might have the wrong idea about the ordering of mutations, so in
|
|
193
|
-
* that case we are given the flag `needRebase` to tell us that this mutation arrived out of
|
|
194
|
-
* order in terms of our optimistic version, so a rebase is needed.
|
|
195
|
-
*
|
|
196
|
-
* @param txnId - Transaction ID of the remote mutation
|
|
197
|
-
* @returns true if rebase is needed, false otherwise
|
|
198
|
-
*/
|
|
199
|
-
consumeUnresolved(txnId: string): boolean
|
|
200
|
-
pendingSuccessfullySubmitted(pendingTxnId: string): void
|
|
201
|
-
pendingFailed(pendingTxnId: string): void
|
|
202
|
-
rebase(incomingMutations: Mutation[]): void
|
|
203
|
-
}
|
|
204
|
-
export {Document_2 as Document}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Extracts values matching the given JsonPath
|
|
208
|
-
*
|
|
209
|
-
* @param path - Path to extract
|
|
210
|
-
* @param value - Value to extract from
|
|
211
|
-
* @returns An array of values matching the given path
|
|
212
|
-
* @public
|
|
213
|
-
*/
|
|
214
|
-
export declare function extract(path: string, value: unknown): unknown[]
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Extracts a value for the given JsonPath, and includes the specific path of where it was found
|
|
218
|
-
*
|
|
219
|
-
* @param path - Path to extract
|
|
220
|
-
* @param value - Value to extract from
|
|
221
|
-
* @returns An array of objects with `path` and `value` keys
|
|
222
|
-
* @internal
|
|
223
|
-
*/
|
|
224
|
-
export declare function extractWithPath(
|
|
225
|
-
path: string,
|
|
226
|
-
value: unknown,
|
|
227
|
-
): {
|
|
228
|
-
path: (string | number)[]
|
|
229
|
-
value: unknown
|
|
230
|
-
}[]
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Internal mutation body representation - note that theoretically a
|
|
234
|
-
* mutation can only hold one of these operations each, but for sake
|
|
235
|
-
* of simpler code it is bundled together as one here
|
|
236
|
-
*
|
|
237
|
-
* @internal
|
|
238
|
-
*/
|
|
239
|
-
export declare interface Mut {
|
|
240
|
-
create?: CreateMutation['create']
|
|
241
|
-
createIfNotExists?: CreateIfNotExistsMutation['createIfNotExists']
|
|
242
|
-
createOrReplace?: CreateOrReplaceMutation['createOrReplace']
|
|
243
|
-
delete?: DeleteMutation['delete']
|
|
244
|
-
patch?: PatchMutation['patch']
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* A mutation describing a number of operations on a single document.
|
|
249
|
-
* This should be considered an immutable structure. Mutations are compiled
|
|
250
|
-
* on first application, and any changes in properties will not effectively
|
|
251
|
-
* change its behavior after that.
|
|
252
|
-
*
|
|
253
|
-
* @internal
|
|
254
|
-
*/
|
|
255
|
-
export declare class Mutation {
|
|
256
|
-
params: MutationParams
|
|
257
|
-
compiled?: (doc: Doc | null) => Doc | null
|
|
258
|
-
_appliesToMissingDocument: boolean | undefined
|
|
259
|
-
constructor(options: MutationParams)
|
|
260
|
-
get transactionId(): string | undefined
|
|
261
|
-
get transition(): string | undefined
|
|
262
|
-
get identity(): string | undefined
|
|
263
|
-
get previousRev(): string | undefined
|
|
264
|
-
get resultRev(): string | undefined
|
|
265
|
-
get mutations(): Mut[]
|
|
266
|
-
get timestamp(): Date | undefined
|
|
267
|
-
get effects():
|
|
268
|
-
| {
|
|
269
|
-
apply: unknown
|
|
270
|
-
revert: unknown
|
|
271
|
-
}
|
|
272
|
-
| undefined
|
|
273
|
-
assignRandomTransactionId(): void
|
|
274
|
-
appliesToMissingDocument(): boolean
|
|
275
|
-
compile(): void
|
|
276
|
-
apply(document: Doc | null): Doc | null
|
|
277
|
-
static applyAll(document: Doc | null, mutations: Mutation[]): Doc | null
|
|
278
|
-
static squash(document: Doc | null, mutations: Mutation[]): Mutation
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Parameters attached to the mutation
|
|
283
|
-
*
|
|
284
|
-
* @internal
|
|
285
|
-
*/
|
|
286
|
-
export declare interface MutationParams {
|
|
287
|
-
transactionId?: string
|
|
288
|
-
transition?: string
|
|
289
|
-
identity?: string
|
|
290
|
-
previousRev?: string
|
|
291
|
-
resultRev?: string
|
|
292
|
-
mutations: Mut[]
|
|
293
|
-
timestamp?: string
|
|
294
|
-
effects?: {
|
|
295
|
-
apply: unknown
|
|
296
|
-
revert: unknown
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Implements a buffer for mutations that incrementally optimises the mutations by
|
|
302
|
-
* eliminating set-operations that overwrite earlier set-operations, and rewrite
|
|
303
|
-
* set-operations that change strings into other strings into diffMatchPatch operations.
|
|
304
|
-
*
|
|
305
|
-
* @internal
|
|
306
|
-
*/
|
|
307
|
-
export declare class SquashingBuffer {
|
|
308
|
-
/**
|
|
309
|
-
* The document forming the basis of this squash
|
|
310
|
-
*/
|
|
311
|
-
BASIS: Doc | null
|
|
312
|
-
/**
|
|
313
|
-
* The document after the out-Mutation has been applied, but before the staged
|
|
314
|
-
* operations are committed.
|
|
315
|
-
*/
|
|
316
|
-
PRESTAGE: Doc | null
|
|
317
|
-
/**
|
|
318
|
-
* setOperations contain the latest set operation by path. If the set-operations are
|
|
319
|
-
* updating strings to new strings, they are rewritten as diffMatchPatch operations,
|
|
320
|
-
* any new set operations on the same paths overwrites any older set operations.
|
|
321
|
-
* Only set-operations assigning plain values to plain values gets optimized like this.
|
|
322
|
-
*/
|
|
323
|
-
setOperations: Record<string, Mut | undefined>
|
|
324
|
-
/**
|
|
325
|
-
* `documentPresent` is true whenever we know that the document must be present due
|
|
326
|
-
* to preceeding mutations. `false` implies that it may or may not already exist.
|
|
327
|
-
*/
|
|
328
|
-
documentPresent: boolean
|
|
329
|
-
/**
|
|
330
|
-
* The operations in the out-Mutation are not able to be optimized any further
|
|
331
|
-
*/
|
|
332
|
-
out: Mut[]
|
|
333
|
-
/**
|
|
334
|
-
* Staged mutation operations
|
|
335
|
-
*/
|
|
336
|
-
staged: Mut[]
|
|
337
|
-
constructor(doc: Doc | null)
|
|
338
|
-
add(mut: Mutation): void
|
|
339
|
-
hasChanges(): boolean
|
|
340
|
-
/**
|
|
341
|
-
* Extracts the mutations in this buffer.
|
|
342
|
-
* After this is done, the buffer lifecycle is over and the client should
|
|
343
|
-
* create an new one with the new, updated BASIS.
|
|
344
|
-
*
|
|
345
|
-
* @param txnId - Transaction ID
|
|
346
|
-
* @returns A `Mutation` instance if we had outgoing mutations pending, null otherwise
|
|
347
|
-
*/
|
|
348
|
-
purge(txnId?: string): Mutation | null
|
|
349
|
-
addOperation(op: Mut): void
|
|
350
|
-
/**
|
|
351
|
-
* Attempt to perform one single set operation in an optimised manner, return value
|
|
352
|
-
* reflects whether or not the operation could be performed.
|
|
353
|
-
|
|
354
|
-
* @param path - The JSONPath to the set operation in question
|
|
355
|
-
* @param nextValue - The value to be set
|
|
356
|
-
* @returns True of optimized, false otherwise
|
|
357
|
-
*/
|
|
358
|
-
optimiseSetOperation(path: string, nextValue: unknown): boolean
|
|
359
|
-
stashStagedOperations(): void
|
|
360
|
-
/**
|
|
361
|
-
* Rebases given the new base-document
|
|
362
|
-
*
|
|
363
|
-
* @param newBasis - New base document to rebase on
|
|
364
|
-
* @returns New "edge" document with buffered changes integrated
|
|
365
|
-
*/
|
|
366
|
-
rebase(newBasis: Doc | null): Doc | null
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* @internal
|
|
371
|
-
*/
|
|
372
|
-
export declare interface SubmissionResponder {
|
|
373
|
-
success: () => void
|
|
374
|
-
failure: () => void
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
export {}
|