@adonis-agora/collaboration-client 0.6.0 → 0.7.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/dist/src/components/excalidraw_board.d.ts +2 -3
- package/dist/src/components/excalidraw_board.d.ts.map +1 -1
- package/dist/src/components/excalidraw_board.js +12 -8
- package/dist/src/components/excalidraw_board.js.map +1 -1
- package/dist/src/context.d.ts +15 -3
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +22 -8
- package/dist/src/context.js.map +1 -1
- package/dist/src/hooks/use_automerge_doc.d.ts +32 -0
- package/dist/src/hooks/use_automerge_doc.d.ts.map +1 -1
- package/dist/src/hooks/use_automerge_doc.js +137 -40
- package/dist/src/hooks/use_automerge_doc.js.map +1 -1
- package/dist/src/hooks/use_awareness.d.ts +30 -0
- package/dist/src/hooks/use_awareness.d.ts.map +1 -1
- package/dist/src/hooks/use_awareness.js +55 -14
- package/dist/src/hooks/use_awareness.js.map +1 -1
- package/dist/src/hooks/use_collab_doc.d.ts +17 -1
- package/dist/src/hooks/use_collab_doc.d.ts.map +1 -1
- package/dist/src/hooks/use_collab_doc.js +44 -19
- package/dist/src/hooks/use_collab_doc.js.map +1 -1
- package/dist/src/hooks/use_collab_editor.d.ts +5 -0
- package/dist/src/hooks/use_collab_editor.d.ts.map +1 -1
- package/dist/src/hooks/use_collab_editor.js +26 -17
- package/dist/src/hooks/use_collab_editor.js.map +1 -1
- package/dist/src/hooks/use_comments.d.ts +6 -1
- package/dist/src/hooks/use_comments.d.ts.map +1 -1
- package/dist/src/hooks/use_comments.js +4 -4
- package/dist/src/hooks/use_comments.js.map +1 -1
- package/dist/src/hooks/use_excalidraw_sync.d.ts +27 -6
- package/dist/src/hooks/use_excalidraw_sync.d.ts.map +1 -1
- package/dist/src/hooks/use_excalidraw_sync.js +56 -22
- package/dist/src/hooks/use_excalidraw_sync.js.map +1 -1
- package/dist/src/hooks/use_versions.js +2 -2
- package/dist/src/hooks/use_versions.js.map +1 -1
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/rest_client.d.ts +11 -2
- package/dist/src/rest_client.d.ts.map +1 -1
- package/dist/src/rest_client.js +11 -2
- package/dist/src/rest_client.js.map +1 -1
- package/dist/src/session.d.ts +112 -4
- package/dist/src/session.d.ts.map +1 -1
- package/dist/src/session.js +198 -16
- package/dist/src/session.js.map +1 -1
- package/dist/src/transports/hocuspocus.d.ts +3 -0
- package/dist/src/transports/hocuspocus.d.ts.map +1 -1
- package/dist/src/transports/hocuspocus.js +23 -2
- package/dist/src/transports/hocuspocus.js.map +1 -1
- package/dist/src/types.d.ts +13 -2
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import '@excalidraw/excalidraw/index.css';
|
|
2
1
|
export interface ExcalidrawBoardProps {
|
|
3
2
|
/** Nome do doc Yjs (ex.: `whiteboards/:id/canvas`). */
|
|
4
3
|
docName: string;
|
|
@@ -6,8 +5,8 @@ export interface ExcalidrawBoardProps {
|
|
|
6
5
|
readOnly?: boolean;
|
|
7
6
|
/** Código do idioma (default `'pt-BR'`). */
|
|
8
7
|
langCode?: string;
|
|
9
|
-
/** Base URL do servidor (ex.: `https://api.app`). */
|
|
10
|
-
baseUrl
|
|
8
|
+
/** Base URL do servidor (ex.: `https://api.app`). Default: `location.origin`. */
|
|
9
|
+
baseUrl?: string | undefined;
|
|
11
10
|
/** Retorna cabeçalhos de auth (ex.: `() => ({ cookie })`). */
|
|
12
11
|
getHeaders: () => Record<string, string>;
|
|
13
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"excalidraw_board.d.ts","sourceRoot":"","sources":["../../../src/components/excalidraw_board.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"excalidraw_board.d.ts","sourceRoot":"","sources":["../../../src/components/excalidraw_board.tsx"],"names":[],"mappings":"AA6DA,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,8DAA8D;IAC9D,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,QAAgB,EAChB,QAAkB,EAClB,OAAO,EACP,UAAU,GACX,EAAE,oBAAoB,2CAWtB"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { CollaborationProvider, useCollabDoc } from '../index.js';
|
|
2
|
+
import { Suspense, lazy, useEffect, useState } from 'react';
|
|
4
3
|
import { useExcalidrawSync } from '../hooks/use_excalidraw_sync.js';
|
|
5
|
-
import {
|
|
6
|
-
// CSS do Excalidraw
|
|
7
|
-
|
|
4
|
+
import { CollaborationProvider, useCollabDoc } from '../index.js';
|
|
5
|
+
// O CSS do Excalidraw precisa ser importado pelo APP HOST, não por esta
|
|
6
|
+
// lib — bundlers (Vite, rolldown) não conseguem resolver o subpath
|
|
7
|
+
// './index.css' no campo `exports` do `@excalidraw/excalidraw` quando
|
|
8
|
+
// o import vem de um `node_modules` aninhado (peer dep), e forçar
|
|
9
|
+
// o import aqui quebra a build com `Package subpath './index.css' is
|
|
10
|
+
// not defined`. O app que usa o `ExcalidrawBoard` deve importar
|
|
11
|
+
// `@excalidraw/excalidraw/index.css` direto no entrypoint (e.g. na
|
|
12
|
+
// `whiteboard_canvas.tsx` ou na página de lousa).
|
|
8
13
|
const ExcalidrawLazy = lazy(async () => {
|
|
9
14
|
const mod = await import('@excalidraw/excalidraw');
|
|
10
15
|
return { default: mod.Excalidraw };
|
|
@@ -13,13 +18,12 @@ function BoardLoading() {
|
|
|
13
18
|
return (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-stone-50", children: _jsx("p", { className: "text-sm text-muted-foreground", children: "Carregando lousa\u2026" }) }));
|
|
14
19
|
}
|
|
15
20
|
function ExcalidrawBoardInner({ docName, readOnly = false, langCode = 'pt-BR', }) {
|
|
16
|
-
const {
|
|
17
|
-
const collabDoc = useMemo(() => (doc ? new YjsCollabDoc(doc) : null), [doc]);
|
|
21
|
+
const { collabDoc } = useCollabDoc({ docName });
|
|
18
22
|
const [api, setApi] = useState(null);
|
|
19
23
|
const [mounted, setMounted] = useState(false);
|
|
20
24
|
useEffect(() => setMounted(true), []);
|
|
21
25
|
useExcalidrawSync({ doc: collabDoc, excalidrawAPI: api, editorId: docName });
|
|
22
|
-
if (!mounted
|
|
26
|
+
if (!mounted)
|
|
23
27
|
return _jsx(BoardLoading, {});
|
|
24
28
|
return (_jsx("div", { className: "relative h-full w-full overflow-hidden bg-stone-50", children: _jsx(Suspense, { fallback: _jsx(BoardLoading, {}), children: _jsx(ExcalidrawLazy, { excalidrawAPI: setApi, langCode: langCode, viewModeEnabled: readOnly, initialData: { appState: { viewBackgroundColor: '#fafaf9' } } }) }) }));
|
|
25
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"excalidraw_board.js","sourceRoot":"","sources":["../../../src/components/excalidraw_board.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"excalidraw_board.js","sourceRoot":"","sources":["../../../src/components/excalidraw_board.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAElE,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,kEAAkE;AAClE,qEAAqE;AACrE,gEAAgE;AAChE,mEAAmE;AACnE,kDAAkD;AAElD,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE;IACrC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACnD,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;AACrC,CAAC,CAAC,CAAC;AAEH,SAAS,YAAY;IACnB,OAAO,CACL,cAAK,SAAS,EAAC,4DAA4D,YACzE,YAAG,SAAS,EAAC,+BAA+B,uCAAsB,GAC9D,CACP,CAAC;AACJ,CAAC;AAQD,SAAS,oBAAoB,CAAC,EAC5B,OAAO,EACP,QAAQ,GAAG,KAAK,EAChB,QAAQ,GAAG,OAAO,GACQ;IAC1B,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAU,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtC,iBAAiB,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,GAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAEtF,IAAI,CAAC,OAAO;QAAE,OAAO,KAAC,YAAY,KAAG,CAAC;IAEtC,OAAO,CACL,cAAK,SAAS,EAAC,oDAAoD,YACjE,KAAC,QAAQ,IAAC,QAAQ,EAAE,KAAC,YAAY,KAAG,YAClC,KAAC,cAAc,IACb,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,QAAQ,EAClB,eAAe,EAAE,QAAQ,EACzB,WAAW,EAAE,EAAE,QAAQ,EAAE,EAAE,mBAAmB,EAAE,SAAS,EAAE,EAAE,GAC7D,GACO,GACP,CACP,CAAC;AACJ,CAAC;AAeD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,QAAQ,GAAG,KAAK,EAChB,QAAQ,GAAG,OAAO,EAClB,OAAO,EACP,UAAU,GACW;IACrB,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtC,IAAI,CAAC,OAAO;QAAE,OAAO,KAAC,YAAY,KAAG,CAAC;IAEtC,OAAO,CACL,KAAC,qBAAqB,IAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,YAC7D,KAAC,oBAAoB,IAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,GAC5D,CACzB,CAAC;AACJ,CAAC"}
|
package/dist/src/context.d.ts
CHANGED
|
@@ -10,8 +10,13 @@ export interface CollaborationProviderProps extends CollaborationClientConfig {
|
|
|
10
10
|
children: ReactNode;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Provider raiz. Uma sessão (Y.Doc + transporte) por docName
|
|
14
|
-
*
|
|
13
|
+
* Provider raiz. Uma sessão (Y.Doc + transporte) por docName.
|
|
14
|
+
*
|
|
15
|
+
* As sessões são **contadas por referência**: o socket abre no primeiro hook
|
|
16
|
+
* que monta e fecha quando o último desmonta, mas o `Y.Doc` continua vivo no
|
|
17
|
+
* mapa do provider — voltar para a página reconecta sobre o mesmo documento
|
|
18
|
+
* em vez de recomeçar do zero. O mapa (e com ele os documentos) morre junto
|
|
19
|
+
* com o provider.
|
|
15
20
|
*
|
|
16
21
|
* ```tsx
|
|
17
22
|
* <CollaborationProvider baseUrl="https://api.app" getHeaders={() => ({ cookie: ... })}>
|
|
@@ -22,7 +27,14 @@ export interface CollaborationProviderProps extends CollaborationClientConfig {
|
|
|
22
27
|
export declare function CollaborationProvider({ children, ...config }: CollaborationProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
23
28
|
/** Contexto interno — lança se usado fora do provider. */
|
|
24
29
|
export declare function useCollaborationContext(): CollaborationContextValue;
|
|
25
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Pega ou cria a sessão do documento (1 Y.Doc por docName).
|
|
32
|
+
*
|
|
33
|
+
* Uma sessão já destruída nunca é devolvida: `destroy()` se auto-remove do
|
|
34
|
+
* mapa e, se por algum caminho sobrar um cadáver aqui, ele é substituído por
|
|
35
|
+
* uma sessão nova — senão o consumidor recebe um editor morto que não
|
|
36
|
+
* reconecta (o `start()` curto-circuita no `#startPromise` antigo).
|
|
37
|
+
*/
|
|
26
38
|
export declare function getOrCreateSession(context: CollaborationContextValue, docName: string): DocSession;
|
|
27
39
|
export {};
|
|
28
40
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA8C,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,UAAU,yBAAyB;IACjC,4DAA4D;IAC5D,SAAS,IAAI,yBAAyB,CAAC;IACvC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC;AAID,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA8C,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,UAAU,yBAAyB;IACjC,4DAA4D;IAC5D,SAAS,IAAI,yBAAyB,CAAC;IACvC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC;AAID,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,0BAA0B,2CAkBxF;AAED,0DAA0D;AAC1D,wBAAgB,uBAAuB,IAAI,yBAAyB,CAMnE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,MAAM,GACd,UAAU,CASZ"}
|
package/dist/src/context.js
CHANGED
|
@@ -3,8 +3,13 @@ import { createContext, useContext, useMemo, useRef } from 'react';
|
|
|
3
3
|
import { DocSession } from './session.js';
|
|
4
4
|
const CollaborationContext = createContext(null);
|
|
5
5
|
/**
|
|
6
|
-
* Provider raiz. Uma sessão (Y.Doc + transporte) por docName
|
|
7
|
-
*
|
|
6
|
+
* Provider raiz. Uma sessão (Y.Doc + transporte) por docName.
|
|
7
|
+
*
|
|
8
|
+
* As sessões são **contadas por referência**: o socket abre no primeiro hook
|
|
9
|
+
* que monta e fecha quando o último desmonta, mas o `Y.Doc` continua vivo no
|
|
10
|
+
* mapa do provider — voltar para a página reconecta sobre o mesmo documento
|
|
11
|
+
* em vez de recomeçar do zero. O mapa (e com ele os documentos) morre junto
|
|
12
|
+
* com o provider.
|
|
8
13
|
*
|
|
9
14
|
* ```tsx
|
|
10
15
|
* <CollaborationProvider baseUrl="https://api.app" getHeaders={() => ({ cookie: ... })}>
|
|
@@ -33,13 +38,22 @@ export function useCollaborationContext() {
|
|
|
33
38
|
}
|
|
34
39
|
return context;
|
|
35
40
|
}
|
|
36
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Pega ou cria a sessão do documento (1 Y.Doc por docName).
|
|
43
|
+
*
|
|
44
|
+
* Uma sessão já destruída nunca é devolvida: `destroy()` se auto-remove do
|
|
45
|
+
* mapa e, se por algum caminho sobrar um cadáver aqui, ele é substituído por
|
|
46
|
+
* uma sessão nova — senão o consumidor recebe um editor morto que não
|
|
47
|
+
* reconecta (o `start()` curto-circuita no `#startPromise` antigo).
|
|
48
|
+
*/
|
|
37
49
|
export function getOrCreateSession(context, docName) {
|
|
38
|
-
|
|
39
|
-
if (!
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
const existing = context.sessions.get(docName);
|
|
51
|
+
if (existing && !existing.isDestroyed)
|
|
52
|
+
return existing;
|
|
53
|
+
const session = new DocSession(docName, context.getConfig(), {
|
|
54
|
+
registry: context.sessions,
|
|
55
|
+
});
|
|
56
|
+
context.sessions.set(docName, session);
|
|
43
57
|
return session;
|
|
44
58
|
}
|
|
45
59
|
//# sourceMappingURL=context.js.map
|
package/dist/src/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAkB,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAS1C,MAAM,oBAAoB,GAAG,aAAa,CAAmC,IAAI,CAAC,CAAC;AAMnF
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAkB,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAS1C,MAAM,oBAAoB,GAAG,aAAa,CAAmC,IAAI,CAAC,CAAC;AAMnF;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAE,QAAQ,EAAE,GAAG,MAAM,EAA8B;IACvF,MAAM,WAAW,GAAG,MAAM,CAAiC,IAAI,CAAC,CAAC;IACjE,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACjC,WAAW,CAAC,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtD,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAA4B,MAAM,CAAC,CAAC;IAC5D,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAE3B,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO;QAClC,QAAQ,EAAE,WAAW,CAAC,OAAkC;KACzD,CAAC,EACF,EAAE,CACH,CAAC;IAEF,OAAO,KAAC,oBAAoB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAiC,CAAC;AACjG,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,uBAAuB;IACrC,MAAM,OAAO,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAkC,EAClC,OAAe;IAEf,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,WAAW;QAAE,OAAO,QAAQ,CAAC;IAEvD,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE;QAC3D,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAC;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -12,12 +12,44 @@ import type { CollabStatus } from '../types.js';
|
|
|
12
12
|
* 3. client sends its full saved doc after local changes (debounced)
|
|
13
13
|
* 4. incoming binaries are merged into the local doc and re-broadcast by
|
|
14
14
|
* the server to the other peers
|
|
15
|
+
*
|
|
16
|
+
* ## Reconnection
|
|
17
|
+
*
|
|
18
|
+
* The socket is rebuilt on a drop, with a fresh token and the same backoff
|
|
19
|
+
* budget as {@link DocSession} — the two paths share
|
|
20
|
+
* {@link MAX_RECONNECT_ATTEMPTS}, {@link isPermanentFailure} and
|
|
21
|
+
* {@link reconnectExhausted} rather than growing two dialects of the same
|
|
22
|
+
* reasoning. A permanent failure (a `4003` close, an engine that is not
|
|
23
|
+
* Automerge) is NOT retried: retrying it cannot succeed, and the retry would
|
|
24
|
+
* end by replacing the message that named the problem with "exceeded N
|
|
25
|
+
* attempts", which names nothing.
|
|
26
|
+
*
|
|
27
|
+
* Local changes made while the socket is down are **not lost**. Automerge's
|
|
28
|
+
* full-document exchange makes the local doc its own outbox: the change is
|
|
29
|
+
* applied locally, {@link UseAutomergeDocResult.pendingChanges} counts it, and
|
|
30
|
+
* the whole saved document is written the moment a socket is open again. The
|
|
31
|
+
* count is exposed so a UI can say "3 unsaved changes" instead of pretending
|
|
32
|
+
* everything is fine.
|
|
15
33
|
*/
|
|
16
34
|
export interface UseAutomergeDocResult<T extends Record<string, unknown>> {
|
|
17
35
|
/** Merged Automerge doc; null until the first server payload arrives. */
|
|
18
36
|
doc: A.Doc<T> | null;
|
|
19
37
|
status: CollabStatus;
|
|
20
38
|
error: Error | null;
|
|
39
|
+
/**
|
|
40
|
+
* True once the server's state has been merged on the CURRENT connection —
|
|
41
|
+
* the Automerge counterpart of `DocSessionSnapshot.synced`. `status ===
|
|
42
|
+
* 'connected'` only means the socket is open; this means the two sides have
|
|
43
|
+
* actually exchanged the document. Goes back to false on a drop.
|
|
44
|
+
*/
|
|
45
|
+
synced: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Local changes applied but not yet written to the socket. Non-zero while
|
|
48
|
+
* disconnected, and back to zero once the reconnected socket has been sent
|
|
49
|
+
* the document. Surface it — a user typing into a document that stopped
|
|
50
|
+
* syncing deserves to know.
|
|
51
|
+
*/
|
|
52
|
+
pendingChanges: number;
|
|
21
53
|
/** Applies a local change and schedules the debounced send. */
|
|
22
54
|
change(cb: A.ChangeFn<T>): void;
|
|
23
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_automerge_doc.d.ts","sourceRoot":"","sources":["../../../src/hooks/use_automerge_doc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"use_automerge_doc.d.ts","sourceRoot":"","sources":["../../../src/hooks/use_automerge_doc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,sBAAsB,CAAC;AAU1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtE,yEAAyE;IACzE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;CAClC;AAeD,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzF,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAAC,CAAC,CAAC,CAqL1B"}
|
|
@@ -1,76 +1,172 @@
|
|
|
1
1
|
import * as A from '@automerge/automerge';
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { useCollaborationContext } from '../context.js';
|
|
4
4
|
import { fetchToken, resolveBaseUrl } from '../rest_client.js';
|
|
5
|
+
import { MAX_RECONNECT_ATTEMPTS, MAX_RECONNECT_DELAY_MS, isPermanentFailure, reconnectExhausted, } from '../session.js';
|
|
5
6
|
const SEND_DEBOUNCE_MS = 200;
|
|
7
|
+
/**
|
|
8
|
+
* Close codes the AutomergeDriver uses for an answer, not an outage.
|
|
9
|
+
*
|
|
10
|
+
* `4000` (no doc) and `4001` (no token, or one that does not parse) are config problems — the token is
|
|
11
|
+
* minted fresh on every attempt, so a socket that refuses it will refuse the next one
|
|
12
|
+
* too. `4003` is `authorize` saying no. `4503` ("authorization unavailable") and `4500`
|
|
13
|
+
* ("could not open the document") are deliberately NOT here: the server distinguishes
|
|
14
|
+
* "you may not" from "we could not tell", and so does this client.
|
|
15
|
+
*/
|
|
16
|
+
const PERMANENT_CLOSE_CODES = new Set([4000, 4001, 4003]);
|
|
6
17
|
export function useAutomergeDoc(options) {
|
|
7
18
|
const { docName, WebSocketImpl } = options;
|
|
8
19
|
const context = useCollaborationContext();
|
|
9
20
|
const config = context.getConfig();
|
|
10
|
-
//
|
|
11
|
-
|
|
21
|
+
// No DocSession here: the Automerge path owns its own socket, and creating
|
|
22
|
+
// a Yjs session in the render body only leaked one Y.Doc per server render.
|
|
12
23
|
const [doc, setDoc] = useState(null);
|
|
13
24
|
const [status, setStatus] = useState('connecting');
|
|
14
25
|
const [error, setError] = useState(null);
|
|
15
|
-
const
|
|
26
|
+
const [synced, setSynced] = useState(false);
|
|
27
|
+
const [pendingChanges, setPendingChanges] = useState(0);
|
|
16
28
|
const localDocRef = useRef(null);
|
|
17
29
|
const sendTimerRef = useRef(undefined);
|
|
18
|
-
const
|
|
30
|
+
const pendingRef = useRef(0);
|
|
31
|
+
/** Installed by the connection effect, which owns the socket. */
|
|
32
|
+
const flushRef = useRef(null);
|
|
19
33
|
useEffect(() => {
|
|
20
|
-
|
|
21
|
-
let
|
|
22
|
-
|
|
34
|
+
const Impl = WebSocketImpl ?? globalThis.WebSocket;
|
|
35
|
+
let socket = null;
|
|
36
|
+
let reconnectTimer;
|
|
37
|
+
let attempt = 0;
|
|
38
|
+
let disposed = false;
|
|
39
|
+
/**
|
|
40
|
+
* Bumped on every connect. A token request still in flight when the socket
|
|
41
|
+
* is rebuilt (or the component unmounts) comes back to a dead generation
|
|
42
|
+
* and is discarded, instead of opening a second socket on the same doc.
|
|
43
|
+
*/
|
|
44
|
+
let generation = 0;
|
|
45
|
+
/** The last real failure, kept so an exhausted budget can name it. */
|
|
46
|
+
let lastFailure = null;
|
|
47
|
+
const fail = (failure) => {
|
|
48
|
+
lastFailure = failure;
|
|
49
|
+
setStatus('error');
|
|
50
|
+
setError(failure);
|
|
51
|
+
};
|
|
52
|
+
const flush = () => {
|
|
53
|
+
if (socket?.readyState !== Impl.OPEN || !localDocRef.current)
|
|
54
|
+
return;
|
|
55
|
+
socket.send(A.save(localDocRef.current));
|
|
56
|
+
pendingRef.current = 0;
|
|
57
|
+
setPendingChanges(0);
|
|
58
|
+
};
|
|
59
|
+
flushRef.current = flush;
|
|
60
|
+
const scheduleReconnect = () => {
|
|
61
|
+
if (reconnectTimer || disposed)
|
|
62
|
+
return;
|
|
63
|
+
if (attempt >= MAX_RECONNECT_ATTEMPTS) {
|
|
64
|
+
setStatus('error');
|
|
65
|
+
setError(reconnectExhausted(lastFailure));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const delay = Math.min(1000 * 2 ** attempt, MAX_RECONNECT_DELAY_MS);
|
|
69
|
+
attempt += 1;
|
|
70
|
+
reconnectTimer = setTimeout(() => {
|
|
71
|
+
reconnectTimer = undefined;
|
|
72
|
+
if (disposed)
|
|
73
|
+
return;
|
|
74
|
+
setStatus('connecting');
|
|
75
|
+
void connect();
|
|
76
|
+
}, delay);
|
|
77
|
+
};
|
|
78
|
+
const connect = async () => {
|
|
79
|
+
generation += 1;
|
|
80
|
+
const mine = generation;
|
|
81
|
+
const stale = () => disposed || mine !== generation;
|
|
23
82
|
try {
|
|
24
83
|
const info = await fetchToken(config, docName);
|
|
25
|
-
if (
|
|
26
|
-
if (!cancelled && info.engine !== 'automerge') {
|
|
27
|
-
throw new Error(`useAutomergeDoc requires engine=automerge (got ${info.engine})`);
|
|
28
|
-
}
|
|
84
|
+
if (stale())
|
|
29
85
|
return;
|
|
86
|
+
if (info.engine !== 'automerge') {
|
|
87
|
+
throw new Error(`useAutomergeDoc requires engine=automerge (got ${info.engine})`);
|
|
30
88
|
}
|
|
31
89
|
const base = resolveBaseUrl(config);
|
|
90
|
+
const query = `?doc=${encodeURIComponent(docName)}&token=${encodeURIComponent(info.token)}`;
|
|
32
91
|
const wsUrl = info.wsUrl.startsWith('ws')
|
|
33
|
-
? `${info.wsUrl}
|
|
34
|
-
: `${base.replace(/^http/, 'ws')}${info.wsUrl}
|
|
35
|
-
const ws = new (
|
|
92
|
+
? `${info.wsUrl}${query}`
|
|
93
|
+
: `${base.replace(/^http/, 'ws')}${info.wsUrl}${query}`;
|
|
94
|
+
const ws = new Impl(wsUrl);
|
|
36
95
|
ws.binaryType = 'arraybuffer';
|
|
37
|
-
|
|
96
|
+
socket = ws;
|
|
38
97
|
ws.onopen = () => {
|
|
39
|
-
if (
|
|
40
|
-
|
|
98
|
+
if (stale())
|
|
99
|
+
return;
|
|
100
|
+
// A connection that opened resets the backoff window, exactly as
|
|
101
|
+
// `DocSession#applyStatus` does on 'connected'.
|
|
102
|
+
attempt = 0;
|
|
103
|
+
lastFailure = null;
|
|
104
|
+
setStatus('connected');
|
|
105
|
+
setError(null);
|
|
106
|
+
// Everything typed while the socket was down goes out now. The local
|
|
107
|
+
// doc IS the outbox: this protocol sends whole documents.
|
|
108
|
+
if (pendingRef.current > 0)
|
|
109
|
+
flush();
|
|
41
110
|
};
|
|
42
111
|
ws.onmessage = (event) => {
|
|
43
|
-
if (
|
|
112
|
+
if (stale())
|
|
44
113
|
return;
|
|
45
114
|
const bytes = new Uint8Array(event.data);
|
|
46
115
|
const incoming = A.load(bytes);
|
|
47
116
|
const next = localDocRef.current ? A.merge(localDocRef.current, incoming) : incoming;
|
|
48
117
|
localDocRef.current = next;
|
|
49
118
|
setDoc(next);
|
|
119
|
+
setSynced(true);
|
|
50
120
|
};
|
|
51
121
|
ws.onerror = () => {
|
|
52
|
-
if (
|
|
53
|
-
|
|
122
|
+
if (stale())
|
|
123
|
+
return;
|
|
124
|
+
// A browser socket fires error and then close; `onclose` is what
|
|
125
|
+
// decides between retrying and giving up. This only records the
|
|
126
|
+
// reason, so an exhausted budget has something to name.
|
|
127
|
+
lastFailure = new Error('websocket error');
|
|
54
128
|
};
|
|
55
|
-
ws.onclose = () => {
|
|
56
|
-
if (
|
|
57
|
-
|
|
129
|
+
ws.onclose = (event) => {
|
|
130
|
+
if (stale())
|
|
131
|
+
return;
|
|
132
|
+
socket = null;
|
|
133
|
+
setSynced(false);
|
|
134
|
+
const code = event?.code;
|
|
135
|
+
const reason = event?.reason;
|
|
136
|
+
const failure = new Error(`websocket closed${code ? ` (${code}${reason ? `: ${reason}` : ''})` : ''}`);
|
|
137
|
+
if (code !== undefined && PERMANENT_CLOSE_CODES.has(code)) {
|
|
138
|
+
fail(failure);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
lastFailure = failure;
|
|
142
|
+
setStatus('disconnected');
|
|
143
|
+
scheduleReconnect();
|
|
58
144
|
};
|
|
59
145
|
}
|
|
60
146
|
catch (cause) {
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
147
|
+
if (stale())
|
|
148
|
+
return;
|
|
149
|
+
const failure = cause instanceof Error ? cause : new Error(String(cause));
|
|
150
|
+
fail(failure);
|
|
151
|
+
// A PERMANENT failure is not retried: an unsupported engine or a rejected
|
|
152
|
+
// document fails the same way five times, and the retry then buries the
|
|
153
|
+
// message that explained it under "exceeded N attempts".
|
|
154
|
+
if (isPermanentFailure(failure))
|
|
155
|
+
return;
|
|
156
|
+
scheduleReconnect();
|
|
65
157
|
}
|
|
66
|
-
}
|
|
158
|
+
};
|
|
159
|
+
void connect();
|
|
67
160
|
return () => {
|
|
68
|
-
|
|
69
|
-
|
|
161
|
+
disposed = true;
|
|
162
|
+
generation += 1;
|
|
163
|
+
flushRef.current = null;
|
|
164
|
+
if (reconnectTimer)
|
|
165
|
+
clearTimeout(reconnectTimer);
|
|
70
166
|
if (sendTimerRef.current)
|
|
71
167
|
clearTimeout(sendTimerRef.current);
|
|
72
|
-
|
|
73
|
-
|
|
168
|
+
socket?.close();
|
|
169
|
+
socket = null;
|
|
74
170
|
};
|
|
75
171
|
}, [docName, config, WebSocketImpl]);
|
|
76
172
|
const change = useCallback((cb) => {
|
|
@@ -78,16 +174,17 @@ export function useAutomergeDoc(options) {
|
|
|
78
174
|
return;
|
|
79
175
|
localDocRef.current = A.change(localDocRef.current, cb);
|
|
80
176
|
setDoc(localDocRef.current);
|
|
177
|
+
pendingRef.current += 1;
|
|
178
|
+
setPendingChanges(pendingRef.current);
|
|
81
179
|
if (sendTimerRef.current)
|
|
82
180
|
clearTimeout(sendTimerRef.current);
|
|
83
181
|
sendTimerRef.current = setTimeout(() => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
182
|
+
sendTimerRef.current = undefined;
|
|
183
|
+
// A no-op while the socket is down — the change stays counted in
|
|
184
|
+
// `pendingChanges` and goes out on the next `onopen`.
|
|
185
|
+
flushRef.current?.();
|
|
89
186
|
}, SEND_DEBOUNCE_MS);
|
|
90
|
-
}, [
|
|
91
|
-
return { doc, status, error, change };
|
|
187
|
+
}, []);
|
|
188
|
+
return { doc, status, error, synced, pendingChanges, change };
|
|
92
189
|
}
|
|
93
190
|
//# sourceMappingURL=use_automerge_doc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_automerge_doc.js","sourceRoot":"","sources":["../../../src/hooks/use_automerge_doc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"use_automerge_doc.js","sourceRoot":"","sources":["../../../src/hooks/use_automerge_doc.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import type * as AwarenessProtocol from 'y-protocols/awareness.js';
|
|
2
2
|
import type { CollabPeer, CollabStatus } from '../types.js';
|
|
3
|
+
/** Identity published into the awareness `user` field, which peers read. */
|
|
4
|
+
export interface CollabLocalUser {
|
|
5
|
+
userId?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
avatarUrl?: string | null;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
3
10
|
export interface UseAwarenessOptions {
|
|
4
11
|
docName: string;
|
|
12
|
+
/**
|
|
13
|
+
* Sua identidade, publicada no campo `user` do awareness local — é
|
|
14
|
+
* exatamente o campo que `peers` lê nos outros clientes. Sem isso ninguém
|
|
15
|
+
* vê seu nome/avatar (o snapshot de todo mundo vem com `user: undefined`).
|
|
16
|
+
*/
|
|
17
|
+
user?: CollabLocalUser | null;
|
|
5
18
|
}
|
|
6
19
|
export interface UseAwarenessResult {
|
|
7
20
|
/** Peers conectados (sem o usuário local). */
|
|
@@ -17,10 +30,27 @@ export interface UseAwarenessResult {
|
|
|
17
30
|
* so it needs the object rather than a snapshot.
|
|
18
31
|
*/
|
|
19
32
|
awareness: AwarenessProtocol.Awareness | null;
|
|
33
|
+
/**
|
|
34
|
+
* Publishes the local awareness state (cursor, selection, identity).
|
|
35
|
+
*
|
|
36
|
+
* The value is remembered and re-published on every transport rebuild, so
|
|
37
|
+
* a reconnect does not silently drop you from everyone else's presence
|
|
38
|
+
* list. Pass `null` to go invisible.
|
|
39
|
+
*/
|
|
40
|
+
setLocalState(state: Record<string, unknown> | null): void;
|
|
41
|
+
/** Publishes/overwrites a single field of the local awareness state. */
|
|
42
|
+
setLocalStateField(field: string, value: unknown): void;
|
|
20
43
|
}
|
|
21
44
|
/**
|
|
22
45
|
* Presença via awareness: quem está online no documento agora.
|
|
23
46
|
* Atualiza em mudança de awareness (entrar/sair/atualizar cursor).
|
|
47
|
+
*
|
|
48
|
+
* ```tsx
|
|
49
|
+
* const { peers, setLocalState } = useAwareness({
|
|
50
|
+
* docName: 'researches/42/writing',
|
|
51
|
+
* user: { userId: me.id, name: me.name, avatarUrl: me.avatarUrl },
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
24
54
|
*/
|
|
25
55
|
export declare function useAwareness(options: UseAwarenessOptions): UseAwarenessResult;
|
|
26
56
|
//# sourceMappingURL=use_awareness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_awareness.d.ts","sourceRoot":"","sources":["../../../src/hooks/use_awareness.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,iBAAiB,MAAM,0BAA0B,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"use_awareness.d.ts","sourceRoot":"","sources":["../../../src/hooks/use_awareness.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,iBAAiB,MAAM,0BAA0B,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;;;OAQG;IACH,SAAS,EAAE,iBAAiB,CAAC,SAAS,GAAG,IAAI,CAAC;IAC9C;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3D,wEAAwE;IACxE,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACzD;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA+E7E"}
|