@powerhousedao/reactor-api 1.7.0 → 1.8.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/index.js CHANGED
@@ -1,23 +1,21 @@
1
1
  import express2, { Router } from 'express';
2
+ import pg from 'pg';
2
3
  import { ApolloServer } from '@apollo/server';
3
4
  import { expressMiddleware } from '@apollo/server/express4';
4
5
  import { ApolloServerPluginInlineTraceDisabled } from '@apollo/server/plugin/disabled';
6
+ import { PGlite } from '@electric-sql/pglite';
5
7
  import bodyParser from 'body-parser';
6
8
  import cors from 'cors';
9
+ import { drizzle } from 'drizzle-orm/node-postgres';
10
+ import { drizzle as drizzle$1 } from 'drizzle-orm/pglite';
7
11
  import { actions } from 'document-model-libs/document-drive';
8
12
  import { utils } from 'document-model/document';
9
13
  import 'graphql-request';
10
14
  import 'nanoevents';
11
15
  import { v4 } from 'uuid';
12
16
  import { parse } from 'graphql';
13
- import { readFileSync } from 'fs';
14
- import { dirname, resolve } from 'path';
15
- import { fileURLToPath } from 'url';
16
17
  import { buildSubgraphSchema } from '@apollo/subgraph';
17
18
  import { typeDefs as typeDefs$1 } from '@powerhousedao/scalars';
18
- import { readFileSync as readFileSync$1 } from 'node:fs';
19
- import { dirname as dirname$1, resolve as resolve$1 } from 'node:path';
20
- import { fileURLToPath as fileURLToPath$1 } from 'node:url';
21
19
 
22
20
  var __create = Object.create;
23
21
  var __defProp = Object.defineProperty;
@@ -28,6 +26,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
28
26
  var __commonJS = (cb, mod) => function __require() {
29
27
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
30
28
  };
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, { get: all[name], enumerable: true });
32
+ };
31
33
  var __copyProps = (to, from, except, desc) => {
32
34
  if (from && typeof from === "object" || typeof from === "function") {
33
35
  for (let key of __getOwnPropNames(from))
@@ -265,9 +267,9 @@ var RunAsap;
265
267
  cause: queueMethod
266
268
  });
267
269
  }
268
- return new Promise((resolve3, reject) => {
270
+ return new Promise((resolve, reject) => {
269
271
  queueMethod(() => {
270
- task().then(resolve3).catch(reject);
272
+ task().then(resolve).catch(reject);
271
273
  });
272
274
  });
273
275
  }
@@ -469,53 +471,33 @@ var InternalListenerManager = class {
469
471
  await this.init();
470
472
  }
471
473
  };
472
- var createSchema = (documentDriveServer, resolvers3, typeDefs3) => buildSubgraphSchema([
473
- {
474
- typeDefs: getDocumentModelTypeDefs(documentDriveServer, typeDefs3),
475
- resolvers: resolvers3
474
+
475
+ // src/subgraphs/system/index.ts
476
+ var system_exports = {};
477
+ __export(system_exports, {
478
+ resolvers: () => resolvers,
479
+ typeDefs: () => typeDefs
480
+ });
481
+
482
+ // src/subgraphs/system/type-defs.ts
483
+ var typeDefs = `type Query {
484
+ drives: [String!]!
485
+ driveIdBySlug(slug: String!): String
476
486
  }
477
- ]);
478
- var getDocumentModelTypeDefs = (documentDriveServer, typeDefs3) => {
479
- const documentModels = documentDriveServer.getDocumentModels();
480
- let dmSchema = "";
481
- documentModels.forEach(({ documentModel: documentModel2 }) => {
482
- dmSchema += `
483
- ${documentModel2.specifications.map(
484
- (specification) => specification.state.global.schema.replaceAll(" Account ", ` ${documentModel2.name}Account `).replaceAll(`: Account`, `: ${documentModel2.name}Account`).replaceAll(`[Account!]!`, `[${documentModel2.name}Account!]!`).replaceAll("scalar DateTime", "").replaceAll(/input (.*?) {[\s\S]*?}/g, "")
485
- ).join("\n")};
486
487
 
487
- ${documentModel2.specifications.map(
488
- (specification) => specification.state.local.schema.replaceAll(" Account ", ` ${documentModel2.name}Account `).replaceAll(`: Account`, `: ${documentModel2.name}Account`).replaceAll(`[Account!]!`, `[${documentModel2.name}Account!]!`).replaceAll("scalar DateTime", "").replaceAll(/input (.*?) {[\s\S]*?}/g, "").replaceAll("type AccountSnapshotLocalState", "").replaceAll("type BudgetStatementLocalState", "").replaceAll("type ScopeFrameworkLocalState", "")
489
- ).join("\n")};
490
-
491
- type ${documentModel2.name} implements IDocument {
492
- id: ID!
493
- name: String!
494
- documentType: String!
495
- revision: Int!
496
- created: DateTime!
497
- lastModified: DateTime!
498
- ${documentModel2.name !== "DocumentModel" ? `state: ${documentModel2.name}State!` : ""}
499
- }
500
- `;
501
- });
502
- const schema = `
503
- ${typeDefs$1.join("\n")}
504
-
505
- interface IDocument {
506
- name: String!
507
- documentType: String!
508
- revision: Int!
509
- created: DateTime!
510
- lastModified: DateTime!
511
-
512
- }
513
- ${dmSchema}
488
+ type Mutation {
489
+ addDrive(global: DocumentDriveStateInput!): DocumentDriveState
490
+ deleteDrive(id: ID!): Boolean
491
+ setDriveIcon(id: String!, icon: String!): Boolean
492
+ setDriveName(id: String!, name: String!): Boolean
493
+ }
514
494
 
515
- ${typeDefs3}
516
- `;
517
- return parse(schema.replaceAll(";", ""));
518
- };
495
+ input DocumentDriveStateInput {
496
+ name: String
497
+ id: String
498
+ slug: String
499
+ icon: String
500
+ }`;
519
501
 
520
502
  // src/subgraphs/system/resolvers.ts
521
503
  var resolvers = {
@@ -538,13 +520,150 @@ var resolvers = {
538
520
  }
539
521
  };
540
522
 
541
- // src/subgraphs/system/schema.graphql
542
- var schema_default = "./schema-2TGHGOBQ.graphql";
523
+ // src/subgraphs/drive/index.ts
524
+ var drive_exports = {};
525
+ __export(drive_exports, {
526
+ resolvers: () => resolvers2,
527
+ typeDefs: () => typeDefs2
528
+ });
529
+
530
+ // src/subgraphs/drive/type-defs.ts
531
+ var typeDefs2 = `type Query {
532
+ system: System
533
+ drive: DocumentDriveState
534
+ document(id: ID!): IDocument
535
+ documents: [String!]!
536
+ }
537
+
538
+ type Mutation {
539
+ registerPullResponderListener(filter: InputListenerFilter!): Listener
540
+ pushUpdates(strands: [InputStrandUpdate!]): [ListenerRevision!]!
541
+ acknowledge(listenerId: String!, revisions: [ListenerRevisionInput]): Boolean
542
+ }
543
+
544
+ input InputOperationSignerUser {
545
+ address: String!
546
+ networkId: String!
547
+ chainId: Int!
548
+ }
549
+
550
+ type OperationSignerUser {
551
+ address: String!
552
+ networkId: String!
553
+ chainId: Int!
554
+ }
555
+
556
+ input InputOperationSignerApp {
557
+ name: String!
558
+ key: String!
559
+ }
560
+
561
+ type OperationSignerApp {
562
+ name: String!
563
+ key: String!
564
+ }
565
+
566
+ type OperationSigner {
567
+ app: OperationSignerApp
568
+ user: OperationSignerUser
569
+ signatures: [[String!]]!
570
+ }
571
+
572
+ input InputOperationSigner {
573
+ app: InputOperationSignerApp
574
+ user: InputOperationSignerUser
575
+ signatures: [[String!]]!
576
+ }
577
+
578
+ type OperationContext {
579
+ signer: OperationSigner
580
+ }
581
+
582
+ input InputOperationContext {
583
+ signer: InputOperationSigner
584
+ }
585
+
586
+ input InputOperationUpdate {
587
+ index: Int!
588
+ skip: Int
589
+ type: String!
590
+ id: String!
591
+ input: String!
592
+ hash: String!
593
+ timestamp: String!
594
+ error: String
595
+ context: InputOperationContext
596
+ }
597
+
598
+ type OperationUpdate {
599
+ index: Int!
600
+ skip: Int
601
+ type: String!
602
+ id: String!
603
+ input: String!
604
+ hash: String!
605
+ timestamp: String!
606
+ error: String
607
+ context: OperationContext
608
+ }
609
+
610
+ type StrandUpdate {
611
+ driveId: String!
612
+ documentId: String!
613
+ scope: String!
614
+ branch: String!
615
+ operations: [OperationUpdate!]!
616
+ }
617
+
618
+ input InputStrandUpdate {
619
+ driveId: String!
620
+ documentId: String!
621
+ scope: String!
622
+ branch: String!
623
+ operations: [InputOperationUpdate!]!
624
+ }
625
+
626
+ input InputListenerFilter {
627
+ documentType: [String!]
628
+ documentId: [String!]
629
+ scope: [String!]
630
+ branch: [String!]
631
+ }
632
+
633
+ enum UpdateStatus {
634
+ SUCCESS
635
+ MISSING
636
+ CONFLICT
637
+ ERROR
638
+ }
639
+
640
+ input ListenerRevisionInput {
641
+ driveId: String!
642
+ documentId: String!
643
+ scope: String!
644
+ branch: String!
645
+ status: UpdateStatus!
646
+ revision: Int!
647
+ }
648
+
649
+ type ListenerRevision {
650
+ driveId: String!
651
+ documentId: String!
652
+ scope: String!
653
+ branch: String!
654
+ status: UpdateStatus!
655
+ revision: Int!
656
+ error: String
657
+ }
543
658
 
544
- // src/subgraphs/system/subgraph.ts
545
- var __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url));
546
- var typeDefs = readFileSync(resolve(__dirname, schema_default), "utf8");
547
- var getSchema = (driveServer) => createSchema(driveServer, resolvers, typeDefs);
659
+ type System {
660
+ sync: Sync
661
+ }
662
+
663
+ type Sync {
664
+ strands(listenerId: ID!, since: String): [StrandUpdate!]!
665
+ }
666
+ `;
548
667
  var resolvers2 = {
549
668
  Query: {
550
669
  drive: async (_, args, ctx) => {
@@ -713,115 +832,196 @@ var resolvers2 = {
713
832
  }
714
833
  }
715
834
  };
716
-
717
- // src/subgraphs/drive/schema.graphql
718
- var schema_default2 = "./schema-2U4EFPTT.graphql";
719
-
720
- // src/subgraphs/drive/subgraph.ts
721
- var __dirname2 = import.meta.dirname ?? dirname$1(fileURLToPath$1(import.meta.url));
722
- var typeDefs2 = readFileSync$1(resolve$1(__dirname2, schema_default2), "utf8");
723
- var getSchema2 = (driveServer) => createSchema(driveServer, resolvers2, typeDefs2);
724
-
725
- // src/subgraphs/index.ts
726
- var SUBGRAPH_REGISTRY = [
727
- {
728
- name: "system",
729
- getSchema
730
- },
835
+ var createSchema = (documentDriveServer, resolvers3, typeDefs3) => buildSubgraphSchema([
731
836
  {
732
- name: "d/:drive",
733
- getSchema: getSchema2
837
+ typeDefs: getDocumentModelTypeDefs(documentDriveServer, typeDefs3),
838
+ resolvers: resolvers3
734
839
  }
735
- ];
840
+ ]);
841
+ var getDocumentModelTypeDefs = (documentDriveServer, typeDefs3) => {
842
+ const documentModels = documentDriveServer.getDocumentModels();
843
+ let dmSchema = "";
844
+ documentModels.forEach(({ documentModel: documentModel2 }) => {
845
+ dmSchema += `
846
+ ${documentModel2.specifications.map(
847
+ (specification) => specification.state.global.schema.replaceAll(" Account ", ` ${documentModel2.name}Account `).replaceAll(`: Account`, `: ${documentModel2.name}Account`).replaceAll(`[Account!]!`, `[${documentModel2.name}Account!]!`).replaceAll("scalar DateTime", "").replaceAll(/input (.*?) {[\s\S]*?}/g, "")
848
+ ).join("\n")};
849
+
850
+ ${documentModel2.specifications.map(
851
+ (specification) => specification.state.local.schema.replaceAll(" Account ", ` ${documentModel2.name}Account `).replaceAll(`: Account`, `: ${documentModel2.name}Account`).replaceAll(`[Account!]!`, `[${documentModel2.name}Account!]!`).replaceAll("scalar DateTime", "").replaceAll(/input (.*?) {[\s\S]*?}/g, "").replaceAll("type AccountSnapshotLocalState", "").replaceAll("type BudgetStatementLocalState", "").replaceAll("type ScopeFrameworkLocalState", "")
852
+ ).join("\n")};
853
+
854
+ type ${documentModel2.name} implements IDocument {
855
+ id: ID!
856
+ name: String!
857
+ documentType: String!
858
+ revision: Int!
859
+ created: DateTime!
860
+ lastModified: DateTime!
861
+ ${documentModel2.name !== "DocumentModel" ? `state: ${documentModel2.name}State!` : ""}
862
+ }
863
+ `;
864
+ });
865
+ const schema = `
866
+ ${typeDefs$1.join("\n")}
867
+
868
+ interface IDocument {
869
+ name: String!
870
+ documentType: String!
871
+ revision: Int!
872
+ created: DateTime!
873
+ lastModified: DateTime!
874
+
875
+ }
876
+ ${dmSchema}
877
+
878
+ ${typeDefs3}
879
+ `;
880
+ return parse(schema.replaceAll(";", ""));
881
+ };
736
882
 
737
883
  // src/router.ts
738
- var reactorRouter = Router();
739
- var getLocalSubgraphConfig = (subgraphName) => SUBGRAPH_REGISTRY.find((it) => it.name === subgraphName);
740
- var listenerManager;
741
- var getListenerManager = async (driveServer) => {
742
- if (!listenerManager) {
743
- listenerManager = new InternalListenerManager(driveServer);
744
- await listenerManager.init();
745
- }
746
- return listenerManager;
747
- };
748
- var updateRouter = async (driveServer) => {
749
- const newRouter = Router();
750
- newRouter.use(cors());
751
- newRouter.use(bodyParser.json());
752
- for (const subgraph of SUBGRAPH_REGISTRY) {
753
- const subgraphConfig = getLocalSubgraphConfig(subgraph.name);
754
- if (!subgraphConfig) continue;
755
- const schema = subgraphConfig.getSchema(driveServer);
756
- const server = new ApolloServer({
757
- schema,
758
- introspection: true,
759
- plugins: [ApolloServerPluginInlineTraceDisabled()]
884
+ var { Pool } = pg;
885
+ var ReactorRouterManager = class {
886
+ constructor(path, app, driveServer, client = new PGlite(), listenerManager = new InternalListenerManager(
887
+ driveServer
888
+ )) {
889
+ this.path = path;
890
+ this.app = app;
891
+ this.driveServer = driveServer;
892
+ this.client = client;
893
+ this.listenerManager = listenerManager;
894
+ }
895
+ database;
896
+ reactorRouter = Router();
897
+ contextFields = {};
898
+ // @todo: need to persist somewhere
899
+ registry = [
900
+ {
901
+ name: "system",
902
+ resolvers: system_exports.resolvers,
903
+ typeDefs: system_exports.typeDefs
904
+ },
905
+ {
906
+ name: "d/:drive",
907
+ resolvers: drive_exports.resolvers,
908
+ typeDefs: drive_exports.typeDefs
909
+ }
910
+ ];
911
+ async init() {
912
+ if (this.client instanceof Pool) {
913
+ this.database = drizzle(this.client);
914
+ } else {
915
+ this.database = drizzle$1(this.client);
916
+ }
917
+ await this.listenerManager.init();
918
+ const models = this.driveServer.getDocumentModels();
919
+ const driveModel = models.find(
920
+ (it) => it.documentModel.name === "DocumentDrive"
921
+ );
922
+ if (!driveModel) {
923
+ throw new Error("DocumentDrive model required");
924
+ }
925
+ this.driveServer.on("documentModels", () => {
926
+ this.updateRouter().catch((error) => console.error(error));
760
927
  });
761
- await server.start();
762
- const path = `/${subgraphConfig.name}`;
763
- newRouter.use(
764
- path,
765
- // @ts-ignore
766
- expressMiddleware(server, {
767
- context: ({ req }) => Promise.resolve({
768
- headers: req.headers,
769
- driveId: req.params.drive ?? void 0,
770
- driveServer,
771
- ...getAdditionalContextFields()
928
+ this.app.use(
929
+ this.path,
930
+ (req, res, next) => this.reactorRouter(req, res, next)
931
+ );
932
+ await this.updateRouter();
933
+ }
934
+ async updateRouter() {
935
+ if (!this.database) {
936
+ await this.init();
937
+ }
938
+ const newRouter = Router();
939
+ newRouter.use(cors());
940
+ newRouter.use(bodyParser.json());
941
+ for (const subgraph of this.registry) {
942
+ if (subgraph.options && subgraph.transmit) {
943
+ await this.#registerInternalListener({
944
+ name: subgraph.name,
945
+ options: subgraph.options,
946
+ transmit: subgraph.transmit
947
+ });
948
+ }
949
+ const subgraphConfig = this.#getLocalSubgraphConfig(subgraph.name);
950
+ if (!subgraphConfig) continue;
951
+ console.log(`Setting up subgraph ${subgraphConfig.name}`);
952
+ const schema = createSchema(
953
+ this.driveServer,
954
+ subgraphConfig.resolvers,
955
+ subgraphConfig.typeDefs
956
+ );
957
+ const server = new ApolloServer({
958
+ schema,
959
+ introspection: true,
960
+ plugins: [ApolloServerPluginInlineTraceDisabled()]
961
+ });
962
+ await server.start();
963
+ const path = `/${subgraphConfig.name}`;
964
+ newRouter.use(
965
+ path,
966
+ // @ts-ignore
967
+ expressMiddleware(server, {
968
+ context: ({ req }) => ({
969
+ headers: req.headers,
970
+ driveId: req.params.drive ?? void 0,
971
+ driveServer: this.driveServer,
972
+ db: this.database,
973
+ // analyticStore: undefined, // TODO: add analytic store
974
+ ...this.getAdditionalContextFields()
975
+ })
772
976
  })
773
- })
977
+ );
978
+ }
979
+ this.reactorRouter = newRouter;
980
+ console.log("Router updated.");
981
+ }
982
+ async registerProcessor(processor) {
983
+ createSchema(
984
+ this.driveServer,
985
+ processor.resolvers,
986
+ processor.typeDefs
774
987
  );
775
- console.log(`Setting up [${subgraphConfig.name}] subgraph at ${path}`);
988
+ this.registry.unshift({
989
+ ...processor
990
+ });
991
+ console.log(`Registering [${processor.name}] processor.`);
992
+ await this.updateRouter();
776
993
  }
777
- listenerManager = await getListenerManager(driveServer);
778
- reactorRouter = newRouter;
779
- console.log("All subgraphs started.");
780
- };
781
- var docDriveServer;
782
- var initReactorRouter = async (path, app, driveServer) => {
783
- docDriveServer = driveServer;
784
- const models = driveServer.getDocumentModels();
785
- const driveModel = models.find(
786
- (it) => it.documentModel.name === "DocumentDrive"
787
- );
788
- if (!driveModel) {
789
- throw new Error("DocumentDrive model required");
994
+ #getLocalSubgraphConfig(subgraphName) {
995
+ return this.registry.find((it) => it.name === subgraphName);
790
996
  }
791
- await updateRouter(driveServer);
792
- driveServer.on("documentModels", () => {
793
- updateRouter(driveServer).catch((error) => console.error(error));
794
- });
795
- app.use(path, (req, res, next) => reactorRouter(req, res, next));
796
- };
797
- var addSubgraph = async (subgraph) => {
798
- SUBGRAPH_REGISTRY.unshift(subgraph);
799
- await updateRouter(docDriveServer);
800
- };
801
- var registerInternalListener = async (module) => {
802
- if (!listenerManager) {
803
- throw new Error("Listener manager not initialized");
997
+ async #registerInternalListener(module) {
998
+ if (!this.listenerManager) {
999
+ throw new Error("Listener manager not initialized");
1000
+ }
1001
+ await this.listenerManager.registerInternalListener(module);
1002
+ }
1003
+ getAdditionalContextFields = () => {
1004
+ return this.contextFields;
1005
+ };
1006
+ setAdditionalContextFields(fields) {
1007
+ this.contextFields = { ...this.contextFields, ...fields };
804
1008
  }
805
- await listenerManager.registerInternalListener(module);
806
- };
807
- var contextFields = {};
808
- var getAdditionalContextFields = () => {
809
- return contextFields;
810
- };
811
- var setAdditionalContextFields = (fields) => {
812
- contextFields = { ...contextFields, ...fields };
813
1009
  };
814
-
815
- // src/server.ts
816
1010
  var DEFAULT_PORT = 4e3;
817
1011
  async function startAPI(reactor, options) {
818
1012
  const port = options.port ?? DEFAULT_PORT;
819
1013
  const app = options.express ?? express2();
820
- await initReactorRouter("/", app, reactor);
1014
+ const reactorRouterManager = new ReactorRouterManager(
1015
+ "/",
1016
+ app,
1017
+ reactor,
1018
+ options.client
1019
+ );
1020
+ await reactorRouterManager.init();
821
1021
  app.listen(port);
822
- return app;
1022
+ return { app, reactorRouterManager };
823
1023
  }
824
1024
 
825
- export { addSubgraph, createSchema, getAdditionalContextFields, getDocumentModelTypeDefs, getListenerManager, initReactorRouter, reactorRouter, registerInternalListener, setAdditionalContextFields, startAPI, updateRouter };
1025
+ export { ReactorRouterManager, createSchema, getDocumentModelTypeDefs, startAPI };
826
1026
  //# sourceMappingURL=index.js.map
827
1027
  //# sourceMappingURL=index.js.map