@adonis-agora/collaboration 0.1.1

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/configure.d.ts +10 -0
  3. package/dist/configure.d.ts.map +1 -0
  4. package/dist/configure.js +16 -0
  5. package/dist/configure.js.map +1 -0
  6. package/dist/providers/collaboration_provider.d.ts +18 -0
  7. package/dist/providers/collaboration_provider.d.ts.map +1 -0
  8. package/dist/providers/collaboration_provider.js +65 -0
  9. package/dist/providers/collaboration_provider.js.map +1 -0
  10. package/dist/src/collaboration_manager.d.ts +34 -0
  11. package/dist/src/collaboration_manager.d.ts.map +1 -0
  12. package/dist/src/collaboration_manager.js +59 -0
  13. package/dist/src/collaboration_manager.js.map +1 -0
  14. package/dist/src/define_config.d.ts +20 -0
  15. package/dist/src/define_config.d.ts.map +1 -0
  16. package/dist/src/define_config.js +8 -0
  17. package/dist/src/define_config.js.map +1 -0
  18. package/dist/src/driver.d.ts +49 -0
  19. package/dist/src/driver.d.ts.map +1 -0
  20. package/dist/src/driver.js +2 -0
  21. package/dist/src/driver.js.map +1 -0
  22. package/dist/src/drivers/automerge/automerge_driver.d.ts +30 -0
  23. package/dist/src/drivers/automerge/automerge_driver.d.ts.map +1 -0
  24. package/dist/src/drivers/automerge/automerge_driver.js +209 -0
  25. package/dist/src/drivers/automerge/automerge_driver.js.map +1 -0
  26. package/dist/src/drivers/yjs/yjs_driver.d.ts +30 -0
  27. package/dist/src/drivers/yjs/yjs_driver.d.ts.map +1 -0
  28. package/dist/src/drivers/yjs/yjs_driver.js +232 -0
  29. package/dist/src/drivers/yjs/yjs_driver.js.map +1 -0
  30. package/dist/src/index.d.ts +9 -0
  31. package/dist/src/index.d.ts.map +1 -0
  32. package/dist/src/index.js +8 -0
  33. package/dist/src/index.js.map +1 -0
  34. package/dist/src/services/booted_app.d.ts +10 -0
  35. package/dist/src/services/booted_app.d.ts.map +1 -0
  36. package/dist/src/services/booted_app.js +42 -0
  37. package/dist/src/services/booted_app.js.map +1 -0
  38. package/dist/src/services/comment_service.d.ts +17 -0
  39. package/dist/src/services/comment_service.d.ts.map +1 -0
  40. package/dist/src/services/comment_service.js +53 -0
  41. package/dist/src/services/comment_service.js.map +1 -0
  42. package/dist/src/services/main.d.ts +6 -0
  43. package/dist/src/services/main.d.ts.map +1 -0
  44. package/dist/src/services/main.js +29 -0
  45. package/dist/src/services/main.js.map +1 -0
  46. package/dist/src/services/presence_service.d.ts +44 -0
  47. package/dist/src/services/presence_service.d.ts.map +1 -0
  48. package/dist/src/services/presence_service.js +103 -0
  49. package/dist/src/services/presence_service.js.map +1 -0
  50. package/dist/src/storage/file_system_storage.d.ts +24 -0
  51. package/dist/src/storage/file_system_storage.d.ts.map +1 -0
  52. package/dist/src/storage/file_system_storage.js +83 -0
  53. package/dist/src/storage/file_system_storage.js.map +1 -0
  54. package/dist/src/storage/in_memory_storage.d.ts +22 -0
  55. package/dist/src/storage/in_memory_storage.d.ts.map +1 -0
  56. package/dist/src/storage/in_memory_storage.js +50 -0
  57. package/dist/src/storage/in_memory_storage.js.map +1 -0
  58. package/dist/src/storage/lucid_storage.d.ts +41 -0
  59. package/dist/src/storage/lucid_storage.d.ts.map +1 -0
  60. package/dist/src/storage/lucid_storage.js +176 -0
  61. package/dist/src/storage/lucid_storage.js.map +1 -0
  62. package/dist/src/types.d.ts +121 -0
  63. package/dist/src/types.d.ts.map +1 -0
  64. package/dist/src/types.js +11 -0
  65. package/dist/src/types.js.map +1 -0
  66. package/dist/src/versioning.d.ts +9 -0
  67. package/dist/src/versioning.d.ts.map +1 -0
  68. package/dist/src/versioning.js +19 -0
  69. package/dist/src/versioning.js.map +1 -0
  70. package/dist/stubs/config/collaboration.stub +39 -0
  71. package/dist/stubs/main.d.ts +3 -0
  72. package/dist/stubs/main.d.ts.map +1 -0
  73. package/dist/stubs/main.js +4 -0
  74. package/dist/stubs/main.js.map +1 -0
  75. package/package.json +124 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @adonis-agora/collaboration
2
+
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 30b5ec1: Initial release — CRDT collaboration for AdonisJS (Yjs/Hocuspocus + Automerge drivers), LucidStorage, generic anchored comments (CommentAnchor), Redis presence.
@@ -0,0 +1,10 @@
1
+ import type Configure from '@adonisjs/core/commands/configure';
2
+ /**
3
+ * `node ace configure @adonis-agora/collaboration` — auto-wires the package:
4
+ *
5
+ * 1. registers the collaboration service provider in `adonisrc.ts`;
6
+ * 2. publishes `config/collaboration.ts` (engine, path, redis, authorize,
7
+ * storage).
8
+ */
9
+ export declare function configure(command: Configure): Promise<void>;
10
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAQjD"}
@@ -0,0 +1,16 @@
1
+ import { stubsRoot } from './stubs/main.js';
2
+ /**
3
+ * `node ace configure @adonis-agora/collaboration` — auto-wires the package:
4
+ *
5
+ * 1. registers the collaboration service provider in `adonisrc.ts`;
6
+ * 2. publishes `config/collaboration.ts` (engine, path, redis, authorize,
7
+ * storage).
8
+ */
9
+ export async function configure(command) {
10
+ const codemods = await command.createCodemods();
11
+ await codemods.updateRcFile((rcFile) => {
12
+ rcFile.addProvider('@adonis-agora/collaboration/collaboration_provider');
13
+ });
14
+ await codemods.makeUsingStub(stubsRoot, 'config/collaboration.stub', {});
15
+ }
16
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,2BAA2B,EAAE,EAAE,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ /**
3
+ * Wires `@adonis-agora/collaboration` into the AdonisJS application:
4
+ *
5
+ * - `register()` hands the booted app to `services/main` and binds the
6
+ * `CollaborationManager` singleton from `config/collaboration.ts`;
7
+ * - `boot()` attaches the collaboration WebSocket upgrade to the Adonis HTTP
8
+ * server (path from config, default `/collaboration`);
9
+ * - `shutdown()` flushes pending stores and closes all connections.
10
+ */
11
+ export default class CollaborationServiceProvider {
12
+ protected app: ApplicationService;
13
+ constructor(app: ApplicationService);
14
+ register(): void;
15
+ ready(): Promise<void>;
16
+ shutdown(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=collaboration_provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaboration_provider.d.ts","sourceRoot":"","sources":["../../providers/collaboration_provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAU/D;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IACnC,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAiCF,KAAK;IA4BL,QAAQ;CAIf"}
@@ -0,0 +1,65 @@
1
+ import { CollaborationManager } from '../src/collaboration_manager.js';
2
+ import { setBootedApp } from '../src/services/booted_app.js';
3
+ /**
4
+ * Wires `@adonis-agora/collaboration` into the AdonisJS application:
5
+ *
6
+ * - `register()` hands the booted app to `services/main` and binds the
7
+ * `CollaborationManager` singleton from `config/collaboration.ts`;
8
+ * - `boot()` attaches the collaboration WebSocket upgrade to the Adonis HTTP
9
+ * server (path from config, default `/collaboration`);
10
+ * - `shutdown()` flushes pending stores and closes all connections.
11
+ */
12
+ export default class CollaborationServiceProvider {
13
+ app;
14
+ constructor(app) {
15
+ this.app = app;
16
+ }
17
+ register() {
18
+ // Hand the booted app to `services/main` so its eager population reads the SAME @adonisjs/core
19
+ // copy bin/server booted, never a pnpm-split non-booted one (see services/booted_app.ts).
20
+ setBootedApp(this.app);
21
+ const config = this.app.config.get('collaboration');
22
+ this.app.container.singleton(CollaborationManager, () => {
23
+ if (!config?.authorize) {
24
+ throw new Error('Configure authorize() em config/collaboration.ts — a lib nunca autoriza por padrão');
25
+ }
26
+ const managerConfig = {
27
+ engine: config.engine ?? 'yjs',
28
+ authorize: (ctx, docName) => config.authorize(ctx, docName),
29
+ path: config.path ?? '/collaboration',
30
+ debounce: config.debounce ?? 2000,
31
+ };
32
+ if (config.storage) {
33
+ managerConfig.storage = config.storage;
34
+ }
35
+ if (config.redisUrl) {
36
+ managerConfig.redis = { url: config.redisUrl };
37
+ }
38
+ return new CollaborationManager(managerConfig);
39
+ });
40
+ }
41
+ async ready() {
42
+ // O attach fica no `ready` (fase 4 do ciclo de vida): o Ignitor já criou
43
+ // o node HTTP server em `start()` (fase 3), então getNodeServer() existe.
44
+ // Em comandos ace não há HTTP server — a colaboração simplesmente não
45
+ // sobe (o manager segue disponível pra uso programático).
46
+ // Sem HTTP server fora do processo web (ace/console, test, repl) — a
47
+ // colaboração não sobe; o manager segue disponível pra uso programático.
48
+ const env = this.app.getEnvironment();
49
+ if (env !== 'web') {
50
+ return;
51
+ }
52
+ const manager = await this.app.container.make(CollaborationManager);
53
+ const adonisServer = (await this.app.container.make('server'));
54
+ const nodeServer = adonisServer.getNodeServer?.();
55
+ if (!nodeServer) {
56
+ throw new Error('[@adonis-agora/collaboration] getNodeServer() indisponível no ready() — verifique a versão do @adonisjs/http-server');
57
+ }
58
+ await manager.attach(nodeServer);
59
+ }
60
+ async shutdown() {
61
+ const manager = await this.app.container.make(CollaborationManager);
62
+ await manager.close();
63
+ }
64
+ }
65
+ //# sourceMappingURL=collaboration_provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaboration_provider.js","sourceRoot":"","sources":["../../providers/collaboration_provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAO7D;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IACzB;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,QAAQ;QACN,+FAA+F;QAC/F,0FAA0F;QAC1F,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAyB,eAAe,CAAC,CAAC;QAE5E,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,oBAAoB,EAAE,GAAG,EAAE;YACtD,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;YACJ,CAAC;YAED,MAAM,aAAa,GAAwB;gBACzC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,KAAK;gBAC9B,SAAS,EAAE,CAAC,GAA4B,EAAE,OAAe,EAA6B,EAAE,CACtF,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC;gBAChC,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,gBAAgB;gBACrC,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI;aAClC,CAAC;YAEF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,aAAa,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YACzC,CAAC;YACD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACpB,aAAa,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;YACjD,CAAC;YAED,OAAO,IAAI,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACT,yEAAyE;QACzE,0EAA0E;QAC1E,sEAAsE;QACtE,0DAA0D;QAC1D,qEAAqE;QACrE,yEAAyE;QACzE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAY,CAAC;QAChD,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAEpE,MAAM,YAAY,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAE5D,CAAC;QAEF,MAAM,UAAU,GAAG,YAAY,CAAC,aAAa,EAAE,EAAE,CAAC;QAClD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,qHAAqH,CACtH,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACpE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ import type { CollaborationDriver } from './driver.js';
2
+ import { AutomergeDriver } from './drivers/automerge/automerge_driver.js';
3
+ import { YjsDriver } from './drivers/yjs/yjs_driver.js';
4
+ import { CommentService } from './services/comment_service.js';
5
+ import { type PresenceMember, PresenceService } from './services/presence_service.js';
6
+ import type { CollabDiffSummary, CollabVersion, CollaborationConfig } from './types.js';
7
+ /**
8
+ * Fachada da lib: escolhe o driver pela config e expõe a API unificada
9
+ * (documento + versões + presença + comentários) pro app hospedeiro.
10
+ */
11
+ export declare class CollaborationManager {
12
+ private config;
13
+ private driver;
14
+ presence: PresenceService;
15
+ comments: CommentService;
16
+ constructor(config: CollaborationConfig);
17
+ get engine(): string;
18
+ /** Anexa o WebSocket do driver no server HTTP do Adonis. */
19
+ attach(server: import('node:http').Server): Promise<void>;
20
+ /** Estado binário atual (pra push externo — Google Drive, export…). */
21
+ getDocumentState(docName: string): Promise<Uint8Array>;
22
+ /** Texto plano (LanguageTool, RAG, índices de comentários). */
23
+ getDocumentText(docName: string): Promise<string>;
24
+ createVersion(docName: string, createdBy: string | null, label?: string | null): Promise<CollabVersion>;
25
+ listVersions(docName: string): Promise<CollabVersion[]>;
26
+ restoreVersion(docName: string, versionId: string, restoredBy: string | null): Promise<void>;
27
+ diffVersions(docName: string, aId: string, bId: string): Promise<CollabDiffSummary>;
28
+ /** Membros online num documento (todas as instâncias). */
29
+ listPresence(docName: string): PresenceMember[];
30
+ close(): Promise<void>;
31
+ }
32
+ export { YjsDriver, AutomergeDriver };
33
+ export type { CollaborationDriver, CollaborationConfig, PresenceMember };
34
+ //# sourceMappingURL=collaboration_manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaboration_manager.d.ts","sourceRoot":"","sources":["../../src/collaboration_manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtF,OAAO,KAAK,EAAiB,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEvG;;;GAGG;AACH,qBAAa,oBAAoB;IAKnB,OAAO,CAAC,MAAM;IAJ1B,OAAO,CAAC,MAAM,CAAsB;IACpC,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,cAAc,CAAC;gBAEL,MAAM,EAAE,mBAAmB;IAQ/C,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,4DAA4D;IACtD,MAAM,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/D,uEAAuE;IACvE,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAItD,+DAA+D;IAC/D,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjD,aAAa,CACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,aAAa,CAAC;IAIzB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIvD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5F,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAInF,0DAA0D;IAC1D,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE;IAIzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAED,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;AACtC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { AutomergeDriver } from './drivers/automerge/automerge_driver.js';
2
+ import { YjsDriver } from './drivers/yjs/yjs_driver.js';
3
+ import { CommentService } from './services/comment_service.js';
4
+ import { PresenceService } from './services/presence_service.js';
5
+ import { InMemoryCollaborationStorage } from './storage/in_memory_storage.js';
6
+ /**
7
+ * Fachada da lib: escolhe o driver pela config e expõe a API unificada
8
+ * (documento + versões + presença + comentários) pro app hospedeiro.
9
+ */
10
+ export class CollaborationManager {
11
+ config;
12
+ driver;
13
+ presence;
14
+ comments;
15
+ constructor(config) {
16
+ this.config = config;
17
+ this.driver =
18
+ config.engine === 'automerge' ? new AutomergeDriver(config) : new YjsDriver(config);
19
+ this.presence = new PresenceService(config.redis?.url);
20
+ this.comments = new CommentService(config.storage ?? new InMemoryCollaborationStorage());
21
+ }
22
+ get engine() {
23
+ return this.driver.engine;
24
+ }
25
+ /** Anexa o WebSocket do driver no server HTTP do Adonis. */
26
+ async attach(server) {
27
+ await this.driver.attach(server);
28
+ }
29
+ /** Estado binário atual (pra push externo — Google Drive, export…). */
30
+ getDocumentState(docName) {
31
+ return this.driver.getDocumentState(docName);
32
+ }
33
+ /** Texto plano (LanguageTool, RAG, índices de comentários). */
34
+ getDocumentText(docName) {
35
+ return this.driver.getDocumentText(docName);
36
+ }
37
+ createVersion(docName, createdBy, label) {
38
+ return this.driver.createVersion(docName, createdBy, label ?? null);
39
+ }
40
+ listVersions(docName) {
41
+ return this.driver.listVersions(docName);
42
+ }
43
+ restoreVersion(docName, versionId, restoredBy) {
44
+ return this.driver.restoreVersion(docName, versionId, restoredBy);
45
+ }
46
+ diffVersions(docName, aId, bId) {
47
+ return this.driver.diffVersions(docName, aId, bId);
48
+ }
49
+ /** Membros online num documento (todas as instâncias). */
50
+ listPresence(docName) {
51
+ return this.presence.list(docName);
52
+ }
53
+ async close() {
54
+ await this.driver.close();
55
+ await this.presence.close();
56
+ }
57
+ }
58
+ export { YjsDriver, AutomergeDriver };
59
+ //# sourceMappingURL=collaboration_manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collaboration_manager.js","sourceRoot":"","sources":["../../src/collaboration_manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAuB,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAG9E;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IAKX;IAJZ,MAAM,CAAsB;IACpC,QAAQ,CAAkB;IAC1B,QAAQ,CAAiB;IAEzB,YAAoB,MAA2B;QAA3B,WAAM,GAAN,MAAM,CAAqB;QAC7C,IAAI,CAAC,MAAM;YACT,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;QAEtF,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,4BAA4B,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,MAAM,CAAC,MAAkC;QAC7C,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,uEAAuE;IACvE,gBAAgB,CAAC,OAAe;QAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,+DAA+D;IAC/D,eAAe,CAAC,OAAe;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,aAAa,CACX,OAAe,EACf,SAAwB,EACxB,KAAqB;QAErB,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,YAAY,CAAC,OAAe;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,cAAc,CAAC,OAAe,EAAE,SAAiB,EAAE,UAAyB;QAC1E,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACpE,CAAC;IAED,YAAY,CAAC,OAAe,EAAE,GAAW,EAAE,GAAW;QACpD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,0DAA0D;IAC1D,YAAY,CAAC,OAAe;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACF;AAED,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { CollaborationConfig } from './types.js';
2
+ /**
3
+ * Shape do `config/collaboration.ts` publicado pelo stub — versão "app-friendly"
4
+ * do {@link CollaborationConfig} (redisUrl como string, storage opcional).
5
+ */
6
+ export type CollaborationAppConfig = {
7
+ engine?: CollaborationConfig['engine'];
8
+ path?: string;
9
+ redisUrl?: string;
10
+ debounce?: number;
11
+ authorize: CollaborationConfig['authorize'];
12
+ /** Omitido = in-memory (só dev). Produção passa um storage persistente. */
13
+ storage?: CollaborationConfig['storage'];
14
+ };
15
+ /**
16
+ * Config helper (type-safe) usado no `config/collaboration.ts` do app —
17
+ * mesmo padrão do `defineConfig` do @adonisjs/*.
18
+ */
19
+ export declare function defineConfig(config: CollaborationAppConfig): CollaborationAppConfig;
20
+ //# sourceMappingURL=define_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,OAAO,CAAC,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC;CAC1C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,sBAAsB,GAAG,sBAAsB,CAEnF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Config helper (type-safe) usado no `config/collaboration.ts` do app —
3
+ * mesmo padrão do `defineConfig` do @adonisjs/*.
4
+ */
5
+ export function defineConfig(config) {
6
+ return config;
7
+ }
8
+ //# sourceMappingURL=define_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAA8B;IACzD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { CollabComment, CollabConnectionContext, CollabDiffSummary, CollabPermission, CollabVersion, CollaborationConfig } from './types.js';
2
+ /**
3
+ * Contrato de um driver CRDT. O driver é dono do motor (Yjs/Hocuspocus ou
4
+ * Automerge): recebe o server HTTP do Adonis pra anexar o WebSocket, fala o
5
+ * protocolo binário do motor com os clientes e implementa version control
6
+ * (nativo no Automerge, via snapshots no Yjs).
7
+ */
8
+ export interface CollaborationDriver {
9
+ readonly engine: string;
10
+ /** Anexa o transporte WebSocket ao server HTTP (upgrade no path configurado). */
11
+ attach(server: import('node:http').Server): Promise<void>;
12
+ /** Estado binário atual do documento (pra export/push externo, ex.: Drive). */
13
+ getDocumentState(docName: string): Promise<Uint8Array>;
14
+ /** Texto plano do documento (pra LanguageTool, RAG, índices de comentário). */
15
+ getDocumentText(docName: string): Promise<string>;
16
+ /** Cria uma versão nomeada no histórico. */
17
+ createVersion(docName: string, createdBy: string | null, label: string | null): Promise<CollabVersion>;
18
+ /** Lista as versões do histórico. */
19
+ listVersions(docName: string): Promise<CollabVersion[]>;
20
+ /** Restaura o documento pra uma versão (cria nova versão "restaurado de X"). */
21
+ restoreVersion(docName: string, versionId: string, restoredBy: string | null): Promise<void>;
22
+ /** Resumo de diff entre duas versões. */
23
+ diffVersions(docName: string, aId: string, bId: string): Promise<CollabDiffSummary>;
24
+ /** Comentários ancorados do documento (por espaço opcional). */
25
+ listComments(docName: string, space?: string): Promise<CollabComment[]>;
26
+ /** Cria um comentário ancorado num espaço do documento. */
27
+ createComment(docName: string, comment: Omit<CollabComment, 'id' | 'createdAt' | 'updatedAt' | 'resolvedAt'>): Promise<CollabComment>;
28
+ /** Resolve/reabre um comentário. Retorna null se não existir. */
29
+ resolveComment(docName: string, commentId: string, resolved: boolean): Promise<CollabComment | null>;
30
+ /** Remove um comentário. Retorna false se não existir. */
31
+ deleteComment(docName: string, commentId: string): Promise<boolean>;
32
+ /** Encerra graciosamente (flush + fecha conexões). */
33
+ close(): Promise<void>;
34
+ }
35
+ /**
36
+ * Camada de comentários ancorados — genérica por tipo de conteúdo.
37
+ * A lib persiste âncoras + lifecycle; o app registra o resolver do seu
38
+ * formato (texto, canvas/tldraw, mídia).
39
+ */
40
+ export interface CommentService {
41
+ list(docName: string, space?: string): Promise<CollabComment[]>;
42
+ create(docName: string, comment: Omit<CollabComment, 'id' | 'createdAt' | 'updatedAt' | 'resolvedAt'>): Promise<CollabComment>;
43
+ resolve(docName: string, commentId: string, resolved: boolean): Promise<CollabComment | null>;
44
+ remove(docName: string, commentId: string): Promise<boolean>;
45
+ }
46
+ /** Helpers compartilhados pelos drivers. */
47
+ export type { CollabComment, CollabConnectionContext, CollabPermission, CollabVersion, CollaborationConfig, };
48
+ export type { CollabDiffSummary };
49
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/driver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,iFAAiF;IACjF,MAAM,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D,+EAA+E;IAC/E,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvD,+EAA+E;IAC/E,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElD,4CAA4C;IAC5C,aAAa,CACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1B,qCAAqC;IACrC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAExD,gFAAgF;IAChF,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7F,yCAAyC;IACzC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEpF,gEAAgE;IAChE,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAExE,2DAA2D;IAC3D,aAAa,CACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC,GAC5E,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1B,iEAAiE;IACjE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAErG,0DAA0D;IAC1D,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpE,sDAAsD;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAChE,MAAM,CACJ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC,GAC5E,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC9F,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED,4CAA4C;AAC5C,YAAY,EACV,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,GACpB,CAAC;AACF,YAAY,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/driver.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import type { CollaborationDriver } from '../../driver.js';
2
+ import type { CollabComment, CollabDiffSummary, CollabVersion, CollaborationConfig } from '../../types.js';
3
+ export declare class AutomergeDriver implements CollaborationDriver {
4
+ #private;
5
+ private config;
6
+ readonly engine = "automerge";
7
+ private rooms;
8
+ private wss?;
9
+ private commentsService;
10
+ constructor(config: CollaborationConfig);
11
+ private loadRoom;
12
+ private broadcast;
13
+ attach(server: import('node:http').Server): Promise<void>;
14
+ private handleClientMessage;
15
+ private persistTimers;
16
+ private persistDebounced;
17
+ getDocumentState(docName: string): Promise<Uint8Array>;
18
+ getDocumentText(docName: string): Promise<string>;
19
+ /** Versões = marcadores sobre o history nativo (heads hash). */
20
+ createVersion(docName: string, createdBy: string | null, label: string | null): Promise<CollabVersion>;
21
+ listVersions(docName: string): Promise<CollabVersion[]>;
22
+ restoreVersion(docName: string, versionId: string, _restoredBy: string | null): Promise<void>;
23
+ diffVersions(docName: string, aId: string, bId: string): Promise<CollabDiffSummary>;
24
+ listComments(docName: string, space?: string): Promise<CollabComment[]>;
25
+ createComment(docName: string, comment: Omit<CollabComment, 'id' | 'createdAt' | 'updatedAt' | 'resolvedAt'>): Promise<CollabComment>;
26
+ resolveComment(docName: string, commentId: string, resolved: boolean): Promise<CollabComment | null>;
27
+ deleteComment(docName: string, commentId: string): Promise<boolean>;
28
+ close(): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=automerge_driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automerge_driver.d.ts","sourceRoot":"","sources":["../../../../src/drivers/automerge/automerge_driver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EAEpB,MAAM,gBAAgB,CAAC;AAwBxB,qBAAa,eAAgB,YAAW,mBAAmB;;IAO7C,OAAO,CAAC,MAAM;IAN1B,QAAQ,CAAC,MAAM,eAAe;IAE9B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,GAAG,CAAC,CAAkB;IAC9B,OAAO,CAAC,eAAe,CAAiB;gBAEpB,MAAM,EAAE,mBAAmB;YAcjC,QAAQ;IActB,OAAO,CAAC,SAAS;IAQX,MAAM,CAAC,MAAM,EAAE,OAAO,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAsDjD,mBAAmB;IAkBjC,OAAO,CAAC,aAAa,CAAoD;IAEzE,OAAO,CAAC,gBAAgB;IAgBlB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKtD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMvD,gEAAgE;IAC1D,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,aAAa,CAAC;IAgBnB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIvD,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,IAAI,CAAC;IAoBV,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiBnF,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIvE,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC,GAC5E,OAAO,CAAC,aAAa,CAAC;IAInB,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI1B,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B"}
@@ -0,0 +1,209 @@
1
+ import * as A from '@automerge/automerge';
2
+ import { next as ANext } from '@automerge/automerge';
3
+ import { WebSocketServer } from 'ws';
4
+ import { CommentService } from '../../services/comment_service.js';
5
+ import { InMemoryCollaborationStorage } from '../../storage/in_memory_storage.js';
6
+ import { createVersionMetadata, seqVersions } from '../../versioning.js';
7
+ export class AutomergeDriver {
8
+ config;
9
+ engine = 'automerge';
10
+ rooms = new Map();
11
+ wss;
12
+ commentsService;
13
+ constructor(config) {
14
+ this.config = config;
15
+ this.commentsService = new CommentService(config.storage ?? new InMemoryCollaborationStorage());
16
+ }
17
+ /** Storage configurado ou in-memory (dev). */
18
+ #storage() {
19
+ if (this.config.storage) {
20
+ return this.config.storage;
21
+ }
22
+ this.#memoryStorage ??= new InMemoryCollaborationStorage();
23
+ return this.#memoryStorage;
24
+ }
25
+ #memoryStorage;
26
+ async loadRoom(docName) {
27
+ let room = this.rooms.get(docName);
28
+ if (room)
29
+ return room;
30
+ const stored = await this.#storage().loadDocument(docName);
31
+ const doc = stored
32
+ ? A.load(stored.state)
33
+ : A.from({ content: '' });
34
+ room = { text: doc, clients: new Set() };
35
+ this.rooms.set(docName, room);
36
+ return room;
37
+ }
38
+ broadcast(room, payload, sender) {
39
+ for (const client of room.clients) {
40
+ if (client !== sender && client.readyState === 1) {
41
+ client.send(payload);
42
+ }
43
+ }
44
+ }
45
+ async attach(server) {
46
+ const path = this.config.path ?? '/collaboration';
47
+ const wss = new WebSocketServer({ noServer: true });
48
+ this.wss = wss;
49
+ server.on('upgrade', (request, socket, head) => {
50
+ const url = new URL(request.url ?? '/', 'http://localhost');
51
+ if (url.pathname !== path)
52
+ return;
53
+ wss.handleUpgrade(request, socket, head, async (ws) => {
54
+ const docName = decodeURIComponent(url.searchParams.get('doc') ?? '');
55
+ if (!docName) {
56
+ ws.close(4000, 'doc obrigatório');
57
+ return;
58
+ }
59
+ // Autenticação: token base64 na query (?token=...&doc=...).
60
+ const token = url.searchParams.get('token');
61
+ if (!token) {
62
+ ws.close(4001, 'não autenticado');
63
+ return;
64
+ }
65
+ try {
66
+ const ctx = JSON.parse(Buffer.from(token, 'base64').toString());
67
+ const permission = await this.config.authorize(ctx, docName);
68
+ if (!permission.canRead) {
69
+ ws.close(4003, 'sem acesso');
70
+ return;
71
+ }
72
+ const room = await this.loadRoom(docName);
73
+ room.clients.add(ws);
74
+ // Envia o estado completo pro cliente novo.
75
+ ws.send(A.save(room.text));
76
+ ws.on('message', (data) => {
77
+ void this.handleClientMessage(docName, data, ws);
78
+ });
79
+ ws.on('close', () => {
80
+ room.clients.delete(ws);
81
+ });
82
+ }
83
+ catch {
84
+ ws.close(4001, 'autenticação falhou');
85
+ }
86
+ });
87
+ });
88
+ }
89
+ // biome-ignore lint/suspicious/noExplicitAny: tipagem do automerge é instável entre minors
90
+ async handleClientMessage(docName, data, sender) {
91
+ const room = await this.loadRoom(docName);
92
+ try {
93
+ // Cliente manda um doc binário incremental; merge no doc do server.
94
+ const incoming = A.load(data);
95
+ const merged = A.merge(room.text, incoming);
96
+ room.text = merged;
97
+ this.broadcast(room, data, sender);
98
+ // Persistência com debounce simples por sala.
99
+ await this.persistDebounced(docName, room);
100
+ }
101
+ catch {
102
+ // Payload inválido: ignora (cliente mal comportado não derruba a sala).
103
+ }
104
+ }
105
+ persistTimers = new Map();
106
+ persistDebounced(docName, room) {
107
+ return new Promise((resolve) => {
108
+ const existing = this.persistTimers.get(docName);
109
+ if (existing)
110
+ clearTimeout(existing);
111
+ this.persistTimers.set(docName, setTimeout(() => {
112
+ void this.#storage()
113
+ .saveDocument(docName, A.save(room.text))
114
+ .finally(() => resolve());
115
+ }, this.config.debounce ?? 2000));
116
+ });
117
+ }
118
+ async getDocumentState(docName) {
119
+ const room = await this.loadRoom(docName);
120
+ return A.save(room.text);
121
+ }
122
+ async getDocumentText(docName) {
123
+ const room = await this.loadRoom(docName);
124
+ const content = room.text.content;
125
+ return content ? content.toString() : '';
126
+ }
127
+ /** Versões = marcadores sobre o history nativo (heads hash). */
128
+ async createVersion(docName, createdBy, label) {
129
+ const room = await this.loadRoom(docName);
130
+ const heads = ANext.getHeads(room.text);
131
+ const existing = await this.#storage().listVersions(docName);
132
+ const version = {
133
+ ...createVersionMetadata(createdBy, label, seqVersions(existing)),
134
+ id: heads.join(','),
135
+ };
136
+ // Snapshot = save binário do doc até esses heads (o Automerge comprime
137
+ // bem; guardar o doc inteiro como "snapshot" mantém restore O(1)).
138
+ await this.#storage().saveVersion(docName, version, A.save(room.text));
139
+ return version;
140
+ }
141
+ async listVersions(docName) {
142
+ return this.#storage().listVersions(docName);
143
+ }
144
+ async restoreVersion(docName, versionId, _restoredBy) {
145
+ const bytes = await this.#storage().loadVersionSnapshot(docName, versionId);
146
+ if (!bytes)
147
+ throw new Error(`Versão ${versionId} não encontrada`);
148
+ const snapshotDoc = A.load(bytes);
149
+ const room = await this.loadRoom(docName);
150
+ const merged = A.merge(room.text, snapshotDoc);
151
+ // Automerge merge converge pro estado mais recente; pra "restaurar",
152
+ // sobrescrevemos o campo de conteúdo com o valor da versão.
153
+ const restoredContent = snapshotDoc
154
+ .content;
155
+ const finalDoc = A.change(merged, (doc) => {
156
+ doc.content = restoredContent?.toString() ?? '';
157
+ });
158
+ room.text = finalDoc;
159
+ this.broadcast(room, A.save(finalDoc));
160
+ await this.#storage().saveDocument(docName, A.save(finalDoc));
161
+ }
162
+ async diffVersions(docName, aId, bId) {
163
+ const [aBytes, bBytes] = await Promise.all([
164
+ this.#storage().loadVersionSnapshot(docName, aId),
165
+ this.#storage().loadVersionSnapshot(docName, bId),
166
+ ]);
167
+ if (!aBytes || !bBytes)
168
+ throw new Error('Versão não encontrada');
169
+ const textOf = (bytes) => {
170
+ const doc = A.load(bytes);
171
+ return doc.content?.toString() ?? '';
172
+ };
173
+ return diffByLine(textOf(aBytes), textOf(bBytes));
174
+ }
175
+ /* ───────────────────────── comentários ancorados ───────────────────────── */
176
+ async listComments(docName, space) {
177
+ return this.commentsService.list(docName, space);
178
+ }
179
+ async createComment(docName, comment) {
180
+ return this.commentsService.create(docName, comment);
181
+ }
182
+ async resolveComment(docName, commentId, resolved) {
183
+ return this.commentsService.resolve(docName, commentId, resolved);
184
+ }
185
+ async deleteComment(docName, commentId) {
186
+ return this.commentsService.remove(docName, commentId);
187
+ }
188
+ async close() {
189
+ for (const timer of this.persistTimers.values())
190
+ clearTimeout(timer);
191
+ return new Promise((resolve) => {
192
+ this.wss?.close(() => resolve());
193
+ });
194
+ }
195
+ }
196
+ function diffByLine(a, b) {
197
+ const linesA = new Set(a.split('\n'));
198
+ const linesB = new Set(b.split('\n'));
199
+ let added = 0;
200
+ for (const line of linesB)
201
+ if (!linesA.has(line))
202
+ added++;
203
+ let removed = 0;
204
+ for (const line of linesA)
205
+ if (!linesB.has(line))
206
+ removed++;
207
+ return { added, removed };
208
+ }
209
+ //# sourceMappingURL=automerge_driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automerge_driver.js","sourceRoot":"","sources":["../../../../src/drivers/automerge/automerge_driver.ts"],"names":[],"mappings":"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"}