@powerhousedao/reactor-api 1.10.9 → 1.11.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.
@@ -2,13 +2,14 @@ import { buildSubgraphSchema } from "@apollo/subgraph";
2
2
  import { IDocumentDriveServer } from "document-drive";
3
3
  import { GraphQLResolverMap } from "@apollo/subgraph/dist/schema-helper";
4
4
  import { typeDefs as scalarsTypeDefs } from "@powerhousedao/scalars";
5
- import { parse } from "graphql";
6
- import { Context } from "src/types";
5
+ import { DocumentNode, parse } from "graphql";
6
+ import gql from "graphql-tag";
7
+ import { Context } from "src/subgraphs";
7
8
 
8
9
  export const createSchema = (
9
10
  documentDriveServer: IDocumentDriveServer,
10
11
  resolvers: GraphQLResolverMap<Context>,
11
- typeDefs: string,
12
+ typeDefs: DocumentNode,
12
13
  ) =>
13
14
  buildSubgraphSchema([
14
15
  {
@@ -19,7 +20,7 @@ export const createSchema = (
19
20
 
20
21
  export const getDocumentModelTypeDefs = (
21
22
  documentDriveServer: IDocumentDriveServer,
22
- typeDefs: string,
23
+ typeDefs: DocumentNode,
23
24
  ) => {
24
25
  const documentModels = documentDriveServer.getDocumentModels();
25
26
  let dmSchema = "";
@@ -63,29 +64,27 @@ export const getDocumentModelTypeDefs = (
63
64
  });
64
65
 
65
66
  // add the mutation and query types
66
- const schema = `
67
- ${scalarsTypeDefs.join("\n")}
68
-
67
+ const schema = gql`
68
+ ${scalarsTypeDefs.join("\n").replaceAll(";", "")}
69
69
 
70
+ type Operation {
71
+ type: String!
72
+ index: Int!
73
+ timestamp: DateTime!
74
+ hash: String!
75
+ }
76
+ interface IDocument {
77
+ name: String!
78
+ documentType: String!
79
+ revision: Int!
80
+ created: DateTime!
81
+ lastModified: DateTime!
82
+ operations: [Operation!]!
83
+ }
84
+ ${dmSchema.replaceAll(";", "")}
70
85
 
71
- type Operation {
72
- type: String!
73
- index: Int!
74
- timestamp: DateTime!
75
- hash: String!
76
- }
77
- interface IDocument {
78
- name: String!
79
- documentType: String!
80
- revision: Int!
81
- created: DateTime!
82
- lastModified: DateTime!
83
- operations: [Operation!]!
84
- }
85
- ${dmSchema}
86
-
87
- ${typeDefs}
88
- `;
86
+ ${typeDefs}
87
+ `;
89
88
 
90
- return parse(schema.replaceAll(";", ""));
89
+ return schema;
91
90
  };
@@ -1,7 +1,8 @@
1
1
  import { PGlite } from "@electric-sql/pglite";
2
- import pkg from "knex";
2
+ import knex, { type Knex } from "knex";
3
3
  import ClientPgLite from "knex-pglite";
4
- const knex = pkg;
4
+
5
+ export type Db = Knex;
5
6
 
6
7
  function isPG(connectionString: string) {
7
8
  if (connectionString.startsWith("postgres://")) {
@@ -10,7 +11,9 @@ function isPG(connectionString: string) {
10
11
  return false;
11
12
  }
12
13
 
13
- export function getKnexClient(connectionString: string | undefined) {
14
+ export function getDbClient(
15
+ connectionString: string | undefined = undefined,
16
+ ): Db {
14
17
  const isPg = connectionString && isPG(connectionString);
15
18
  const client = isPg ? "pg" : (ClientPgLite as typeof knex.Client);
16
19
 
@@ -0,0 +1,2 @@
1
+ export * from "./create-schema";
2
+ export * from "./get-db-client";
@@ -1,15 +1,11 @@
1
- import express from "express";
2
- import { describe, expect, it } from "vitest";
3
- import { buildSubgraphSchema } from "@apollo/subgraph";
1
+ import { DocumentDriveServer } from "document-drive";
4
2
  import * as DocumentModelsLibs from "document-model-libs/document-models";
5
3
  import { DocumentModel } from "document-model/document";
6
4
  import { module as DocumentModelLib } from "document-model/document-model";
7
- import {
8
- IDocumentDriveServer,
9
- DocumentDriveServer,
10
- } from "../../document-drive/src/server";
11
- import { ReactorRouterManager } from "../src/router";
12
- import { getDocumentModelTypeDefs } from "../src/utils/create-schema";
5
+ import express from "express";
6
+ import { SubgraphManager } from "src";
7
+ import { getDbClient } from "src/utils/get-db-client";
8
+ import { describe, expect, it } from "vitest";
13
9
 
14
10
  const documentModels = [
15
11
  DocumentModelLib,
@@ -19,8 +15,9 @@ const documentModels = [
19
15
  describe("Reactor Router", () => {
20
16
  it("should be initialized", async () => {
21
17
  const app = express();
18
+ const knex = getDbClient();
22
19
  const reactor = new DocumentDriveServer(documentModels);
23
- const reactorRouter = new ReactorRouterManager("/", app, reactor);
20
+ const reactorRouter = new SubgraphManager("/", app, reactor, knex);
24
21
  await expect(reactorRouter.init()).resolves.toBeUndefined();
25
22
  });
26
23
 
@@ -1,58 +0,0 @@
1
- import { Knex } from "knex";
2
-
3
- export async function createSchema(knex: Knex) {
4
- if (!(await knex.schema.hasTable("AnalyticsDimension"))) {
5
- await knex.schema.createTable("AnalyticsDimension", (table) => {
6
- table.increments("id").primary();
7
- table
8
- .string("dimension")
9
- .notNullable()
10
- .index("analyticsdimension_dimension_index");
11
- table.string("path").notNullable().index("analyticsdimension_path_index");
12
- table.string("label").nullable();
13
- table.string("icon").nullable();
14
- table.text("description").nullable();
15
- });
16
- }
17
-
18
- if (!(await knex.schema.hasTable("AnalyticsSeries"))) {
19
- await knex.schema.createTable("AnalyticsSeries", (table) => {
20
- table.increments("id").primary();
21
- table
22
- .string("source")
23
- .notNullable()
24
- .index("analyticsseries_source_index");
25
- table
26
- .timestamp("start")
27
- .notNullable()
28
- .index("analyticsseries_start_index");
29
- table.timestamp("end").nullable().index("analyticsseries_end_index");
30
- table
31
- .string("metric")
32
- .notNullable()
33
- .index("analyticsseries_metric_index");
34
- table.float("value").notNullable().index("analyticsseries_value_index");
35
- table.string("unit").nullable().index("analyticsseries_unit_index");
36
- table.string("fn").notNullable().index("analyticsseries_fn_index");
37
- table.json("params").nullable();
38
- });
39
- }
40
-
41
- if (!(await knex.schema.hasTable("AnalyticsSeries_AnalyticsDimension"))) {
42
- await knex.schema.createTable(
43
- "AnalyticsSeries_AnalyticsDimension",
44
- (table) => {
45
- table
46
- .integer("seriesId")
47
- .references("AnalyticsSeries.id")
48
- .onDelete("CASCADE")
49
- .index("analyticsseries_analyticsdimension_seriesid_index");
50
- table
51
- .integer("dimensionId")
52
- .references("AnalyticsDimension.id")
53
- .onDelete("CASCADE")
54
- .index("analyticsseries_analyticsdimension_dimensionid_index");
55
- },
56
- );
57
- }
58
- }
@@ -1,234 +0,0 @@
1
- import { GraphQLResolverMap } from "@apollo/subgraph/dist/schema-helper";
2
- import {
3
- generateUUID,
4
- ListenerRevision,
5
- PullResponderTransmitter,
6
- StrandUpdateGraphQL,
7
- } from "document-drive";
8
- import {
9
- actions,
10
- DocumentDriveAction,
11
- FileNode,
12
- Listener,
13
- ListenerFilter,
14
- TransmitterType,
15
- } from "document-model-libs/document-drive";
16
- import { Asset } from "document-model-libs/real-world-assets";
17
- import { BaseAction, Operation } from "document-model/document";
18
- import {
19
- DocumentModelInput,
20
- DocumentModelState,
21
- } from "document-model/document-model";
22
- import { Context } from "../types";
23
-
24
- export const resolvers: GraphQLResolverMap<Context> = {
25
- Asset: {
26
- __resolveType: (obj: Asset) => {
27
- return obj.type;
28
- },
29
- },
30
- Node: {
31
- __resolveType: (obj: FileNode) => {
32
- return obj.documentType ? "FileNode" : "FolderNode";
33
- },
34
- },
35
- Query: {
36
- drive: async (_: unknown, args: unknown, ctx: Context) => {
37
- if (!ctx.driveId) throw new Error("Drive ID is required");
38
- const drive = await ctx.driveServer.getDrive(ctx.driveId);
39
- return drive.state.global;
40
- },
41
- documents: async (_: unknown, args: unknown, ctx: Context) => {
42
- if (!ctx.driveId) throw new Error("Drive ID is required");
43
- const documents = await ctx.driveServer.getDocuments(ctx.driveId);
44
- return documents;
45
- },
46
- document: async (_: unknown, { id }: { id: string }, ctx: Context) => {
47
- if (!ctx.driveId) throw new Error("Drive ID is required");
48
- const document = await ctx.driveServer.getDocument(ctx.driveId, id);
49
-
50
- const dms = ctx.driveServer.getDocumentModels();
51
- const dm = dms.find(
52
- ({ documentModel }: { documentModel: DocumentModelState }) =>
53
- documentModel.id === document.documentType,
54
- );
55
- const globalState = document.state.global;
56
- if (!globalState) throw new Error("Document not found");
57
- const response = {
58
- ...document,
59
- id,
60
- revision: document.revision.global,
61
- state: document.state.global,
62
- operations: document.operations.global.map((op: Operation) => ({
63
- ...op,
64
- inputText:
65
- typeof op.input === "string" ? op.input : JSON.stringify(op.input),
66
- })),
67
- initialState: document.initialState.state.global,
68
- __typename: dm?.documentModel.name,
69
- };
70
- return response;
71
- },
72
- system: () => ({ sync: {} }),
73
- },
74
- Mutation: {
75
- registerPullResponderListener: async (
76
- _: unknown,
77
- { filter }: { filter: ListenerFilter },
78
- ctx: Context,
79
- ) => {
80
- if (!ctx.driveId) throw new Error("Drive ID is required");
81
- const uuid = generateUUID();
82
- const listener: Listener = {
83
- block: false,
84
- callInfo: {
85
- data: "",
86
- name: "PullResponder",
87
- transmitterType: "PullResponder" as TransmitterType,
88
- },
89
- filter: {
90
- branch: filter.branch ?? [],
91
- documentId: filter.documentId ?? [],
92
- documentType: filter.documentType ?? [],
93
- scope: filter.scope ?? [],
94
- },
95
- label: `Pullresponder #${uuid}`,
96
- listenerId: uuid,
97
- system: false,
98
- };
99
-
100
- const result = await ctx.driveServer.queueDriveAction(
101
- ctx.driveId,
102
- actions.addListener({ listener }),
103
- );
104
-
105
- if (result.status !== "SUCCESS" && result.error) {
106
- throw new Error(
107
- `Listener couldn't be registered: ${result.error.message}`,
108
- );
109
- }
110
-
111
- return listener;
112
- },
113
- pushUpdates: async (
114
- _: unknown,
115
- { strands }: { strands: StrandUpdateGraphQL[] },
116
- ctx: Context,
117
- ) => {
118
- if (!ctx.driveId) throw new Error("Drive ID is required");
119
- const listenerRevisions: ListenerRevision[] = await Promise.all(
120
- strands.map(async (s) => {
121
- const operations =
122
- s.operations.map((o) => ({
123
- ...o,
124
- input: JSON.parse(o.input) as DocumentModelInput,
125
- skip: o.skip ?? 0,
126
- scope: s.scope,
127
- branch: "main",
128
- })) ?? [];
129
-
130
- const result = await (s.documentId !== undefined
131
- ? ctx.driveServer.queueOperations(
132
- s.driveId,
133
- s.documentId,
134
- operations,
135
- )
136
- : ctx.driveServer.queueDriveOperations(
137
- s.driveId,
138
- operations as Operation<DocumentDriveAction | BaseAction>[],
139
- ));
140
-
141
- const scopeOperations = result.document?.operations[s.scope] ?? [];
142
- if (scopeOperations.length === 0) {
143
- return {
144
- revision: -1,
145
- branch: s.branch,
146
- documentId: s.documentId ?? "",
147
- driveId: s.driveId,
148
- scope: s.scope,
149
- status: result.status,
150
- };
151
- }
152
-
153
- const revision = scopeOperations.slice().pop()?.index ?? -1;
154
- return {
155
- revision,
156
- branch: s.branch,
157
- documentId: s.documentId ?? "",
158
- driveId: s.driveId,
159
- scope: s.scope,
160
- status: result.status,
161
- error: result.error?.message || undefined,
162
- };
163
- }),
164
- );
165
-
166
- return listenerRevisions;
167
- },
168
- acknowledge: async (
169
- _: unknown,
170
- {
171
- listenerId,
172
- revisions,
173
- }: { listenerId: string; revisions: ListenerRevision[] },
174
- ctx: Context,
175
- ) => {
176
- if (!listenerId || !revisions) return false;
177
- if (!ctx.driveId) throw new Error("Drive ID is required");
178
- const validEntries = revisions
179
- .filter((r) => r !== null)
180
- .map((e) => ({
181
- driveId: e.driveId,
182
- documentId: e.documentId,
183
- scope: e.scope,
184
- branch: e.branch,
185
- revision: e.revision,
186
- status: e.status,
187
- }));
188
-
189
- const transmitter = (await ctx.driveServer.getTransmitter(
190
- ctx.driveId,
191
- listenerId,
192
- )) as PullResponderTransmitter;
193
- const result = await transmitter.processAcknowledge(
194
- ctx.driveId ?? "1",
195
- listenerId,
196
- validEntries,
197
- );
198
-
199
- return result;
200
- },
201
- },
202
- System: {},
203
- Sync: {
204
- strands: async (
205
- _: unknown,
206
- { listenerId, since }: { listenerId: string; since: string | undefined },
207
- ctx: Context,
208
- ) => {
209
- if (!ctx.driveId) throw new Error("Drive ID is required");
210
- const listener = (await ctx.driveServer.getTransmitter(
211
- ctx.driveId,
212
- listenerId,
213
- )) as PullResponderTransmitter;
214
- const strands = await listener.getStrands({ since });
215
- return strands.map((e) => ({
216
- driveId: e.driveId,
217
- documentId: e.documentId,
218
- scope: e.scope,
219
- branch: e.branch,
220
- operations: e.operations.map((o) => ({
221
- index: o.index,
222
- skip: o.skip,
223
- name: o.type,
224
- input: JSON.stringify(o.input),
225
- hash: o.hash,
226
- timestamp: o.timestamp,
227
- type: o.type,
228
- context: o.context,
229
- id: o.id,
230
- })),
231
- }));
232
- },
233
- },
234
- };
@@ -1,136 +0,0 @@
1
- export const typeDefs = `type Query {
2
- system: System
3
- drive: DocumentDriveState
4
- document(id: ID!): IDocument
5
- documents: [String!]!
6
- }
7
-
8
- type Mutation {
9
- registerPullResponderListener(filter: InputListenerFilter!): Listener
10
- pushUpdates(strands: [InputStrandUpdate!]): [ListenerRevision!]!
11
- acknowledge(listenerId: String!, revisions: [ListenerRevisionInput]): Boolean
12
- }
13
-
14
- input InputOperationSignerUser {
15
- address: String!
16
- networkId: String!
17
- chainId: Int!
18
- }
19
-
20
- type OperationSignerUser {
21
- address: String!
22
- networkId: String!
23
- chainId: Int!
24
- }
25
-
26
- input InputOperationSignerApp {
27
- name: String!
28
- key: String!
29
- }
30
-
31
- type OperationSignerApp {
32
- name: String!
33
- key: String!
34
- }
35
-
36
- type OperationSigner {
37
- app: OperationSignerApp
38
- user: OperationSignerUser
39
- signatures: [[String!]]!
40
- }
41
-
42
- input InputOperationSigner {
43
- app: InputOperationSignerApp
44
- user: InputOperationSignerUser
45
- signatures: [[String!]]!
46
- }
47
-
48
- type OperationContext {
49
- signer: OperationSigner
50
- }
51
-
52
- input InputOperationContext {
53
- signer: InputOperationSigner
54
- }
55
-
56
- input InputOperationUpdate {
57
- index: Int!
58
- skip: Int
59
- type: String!
60
- id: String!
61
- input: String!
62
- hash: String!
63
- timestamp: String!
64
- error: String
65
- context: InputOperationContext
66
- }
67
-
68
- type OperationUpdate {
69
- index: Int!
70
- skip: Int
71
- type: String!
72
- id: String!
73
- input: String!
74
- hash: String!
75
- timestamp: String!
76
- error: String
77
- context: OperationContext
78
- }
79
-
80
- type StrandUpdate {
81
- driveId: String!
82
- documentId: String!
83
- scope: String!
84
- branch: String!
85
- operations: [OperationUpdate!]!
86
- }
87
-
88
- input InputStrandUpdate {
89
- driveId: String!
90
- documentId: String!
91
- scope: String!
92
- branch: String!
93
- operations: [InputOperationUpdate!]!
94
- }
95
-
96
- input InputListenerFilter {
97
- documentType: [String!]
98
- documentId: [String!]
99
- scope: [String!]
100
- branch: [String!]
101
- }
102
-
103
- enum UpdateStatus {
104
- SUCCESS
105
- MISSING
106
- CONFLICT
107
- ERROR
108
- }
109
-
110
- input ListenerRevisionInput {
111
- driveId: String!
112
- documentId: String!
113
- scope: String!
114
- branch: String!
115
- status: UpdateStatus!
116
- revision: Int!
117
- }
118
-
119
- type ListenerRevision {
120
- driveId: String!
121
- documentId: String!
122
- scope: String!
123
- branch: String!
124
- status: UpdateStatus!
125
- revision: Int!
126
- error: String
127
- }
128
-
129
- type System {
130
- sync: Sync
131
- }
132
-
133
- type Sync {
134
- strands(listenerId: ID!, since: String): [StrandUpdate!]!
135
- }
136
- `;
@@ -1,23 +0,0 @@
1
- import { DriveInput } from "document-drive";
2
- import { Context } from "../types";
3
- import { GraphQLResolverMap } from "@apollo/subgraph/dist/schema-helper";
4
-
5
- export const resolvers: GraphQLResolverMap<Context> = {
6
- Query: {
7
- drives: async (parent: unknown, args: unknown, ctx: Context) => {
8
- const drives = await ctx.driveServer.getDrives();
9
- return drives;
10
- },
11
- },
12
- Mutation: {
13
- addDrive: async (parent: unknown, args: DriveInput, ctx: Context) => {
14
- try {
15
- const drive = await ctx.driveServer.addDrive(args);
16
- return drive.state.global;
17
- } catch (e) {
18
- console.error(e);
19
- throw new Error(e as string);
20
- }
21
- },
22
- },
23
- };
@@ -1,18 +0,0 @@
1
- export const typeDefs = `type Query {
2
- drives: [String!]!
3
- driveIdBySlug(slug: String!): String
4
- }
5
-
6
- type Mutation {
7
- addDrive(global: DocumentDriveStateInput!): DocumentDriveState
8
- deleteDrive(id: ID!): Boolean
9
- setDriveIcon(id: String!, icon: String!): Boolean
10
- setDriveName(id: String!, name: String!): Boolean
11
- }
12
-
13
- input DocumentDriveStateInput {
14
- name: String
15
- id: String
16
- slug: String
17
- icon: String
18
- }`;