@powerhousedao/reactor-api 6.0.0-dev.9 → 6.0.0-dev.91

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 (271) hide show
  1. package/dist/codegen.js +1 -1
  2. package/dist/codegen.js.map +1 -1
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +4 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/src/config.d.ts +1 -2
  8. package/dist/src/config.d.ts.map +1 -1
  9. package/dist/src/config.js +1 -5
  10. package/dist/src/config.js.map +1 -1
  11. package/dist/src/graphql/auth/resolvers.d.ts +17 -0
  12. package/dist/src/graphql/auth/resolvers.d.ts.map +1 -1
  13. package/dist/src/graphql/auth/resolvers.js +54 -0
  14. package/dist/src/graphql/auth/resolvers.js.map +1 -1
  15. package/dist/src/graphql/auth/schema.graphql +27 -5
  16. package/dist/src/graphql/auth/subgraph.d.ts +25 -0
  17. package/dist/src/graphql/auth/subgraph.d.ts.map +1 -1
  18. package/dist/src/graphql/auth/subgraph.js +45 -1
  19. package/dist/src/graphql/auth/subgraph.js.map +1 -1
  20. package/dist/src/graphql/base-subgraph.d.ts +12 -4
  21. package/dist/src/graphql/base-subgraph.d.ts.map +1 -1
  22. package/dist/src/graphql/base-subgraph.js +94 -2
  23. package/dist/src/graphql/base-subgraph.js.map +1 -1
  24. package/dist/src/graphql/document-model-subgraph.d.ts +16 -44
  25. package/dist/src/graphql/document-model-subgraph.d.ts.map +1 -1
  26. package/dist/src/graphql/document-model-subgraph.js +290 -88
  27. package/dist/src/graphql/document-model-subgraph.js.map +1 -1
  28. package/dist/src/graphql/graphql-manager.d.ts +28 -6
  29. package/dist/src/graphql/graphql-manager.d.ts.map +1 -1
  30. package/dist/src/graphql/graphql-manager.js +266 -161
  31. package/dist/src/graphql/graphql-manager.js.map +1 -1
  32. package/dist/src/graphql/index.d.ts +2 -1
  33. package/dist/src/graphql/index.d.ts.map +1 -1
  34. package/dist/src/graphql/index.js +2 -1
  35. package/dist/src/graphql/index.js.map +1 -1
  36. package/dist/src/graphql/packages/index.d.ts +2 -0
  37. package/dist/src/graphql/packages/index.d.ts.map +1 -0
  38. package/dist/src/graphql/packages/index.js +2 -0
  39. package/dist/src/graphql/packages/index.js.map +1 -0
  40. package/dist/src/graphql/packages/resolvers.d.ts +31 -0
  41. package/dist/src/graphql/packages/resolvers.d.ts.map +1 -0
  42. package/dist/src/graphql/packages/resolvers.js +37 -0
  43. package/dist/src/graphql/packages/resolvers.js.map +1 -0
  44. package/dist/src/graphql/packages/schema.graphql +50 -0
  45. package/dist/src/graphql/packages/subgraph.d.ts +55 -0
  46. package/dist/src/graphql/packages/subgraph.d.ts.map +1 -0
  47. package/dist/src/graphql/packages/subgraph.js +73 -0
  48. package/dist/src/graphql/packages/subgraph.js.map +1 -0
  49. package/dist/src/graphql/reactor/adapters.d.ts +10 -2
  50. package/dist/src/graphql/reactor/adapters.d.ts.map +1 -1
  51. package/dist/src/graphql/reactor/adapters.js +35 -1
  52. package/dist/src/graphql/reactor/adapters.js.map +1 -1
  53. package/dist/src/graphql/reactor/factory.d.ts +22 -1
  54. package/dist/src/graphql/reactor/factory.d.ts.map +1 -1
  55. package/dist/src/graphql/reactor/factory.js +1 -1
  56. package/dist/src/graphql/reactor/factory.js.map +1 -1
  57. package/dist/src/graphql/reactor/gen/graphql.d.ts +353 -76
  58. package/dist/src/graphql/reactor/gen/graphql.d.ts.map +1 -1
  59. package/dist/src/graphql/reactor/gen/graphql.js +245 -10
  60. package/dist/src/graphql/reactor/gen/graphql.js.map +1 -1
  61. package/dist/src/graphql/reactor/index.d.ts +1 -1
  62. package/dist/src/graphql/reactor/index.d.ts.map +1 -1
  63. package/dist/src/graphql/reactor/index.js +1 -1
  64. package/dist/src/graphql/reactor/index.js.map +1 -1
  65. package/dist/src/graphql/reactor/operations.graphql +191 -1
  66. package/dist/src/graphql/reactor/requester.with-zod.d.ts.map +1 -1
  67. package/dist/src/graphql/reactor/requester.with-zod.js +114 -38
  68. package/dist/src/graphql/reactor/requester.with-zod.js.map +1 -1
  69. package/dist/src/graphql/reactor/resolvers.d.ts +89 -26
  70. package/dist/src/graphql/reactor/resolvers.d.ts.map +1 -1
  71. package/dist/src/graphql/reactor/resolvers.js +271 -77
  72. package/dist/src/graphql/reactor/resolvers.js.map +1 -1
  73. package/dist/src/graphql/reactor/schema.graphql +75 -30
  74. package/dist/src/graphql/reactor/subgraph.d.ts +2 -31
  75. package/dist/src/graphql/reactor/subgraph.d.ts.map +1 -1
  76. package/dist/src/graphql/reactor/subgraph.js +132 -209
  77. package/dist/src/graphql/reactor/subgraph.js.map +1 -1
  78. package/dist/src/graphql/reactor/validation.d.ts +266 -20
  79. package/dist/src/graphql/reactor/validation.d.ts.map +1 -1
  80. package/dist/src/graphql/reactor/validation.js +98 -4
  81. package/dist/src/graphql/reactor/validation.js.map +1 -1
  82. package/dist/src/graphql/system/index.d.ts +0 -1
  83. package/dist/src/graphql/system/index.d.ts.map +1 -1
  84. package/dist/src/graphql/system/index.js +0 -1
  85. package/dist/src/graphql/system/index.js.map +1 -1
  86. package/dist/src/graphql/types.d.ts +6 -8
  87. package/dist/src/graphql/types.d.ts.map +1 -1
  88. package/dist/src/graphql/utils.d.ts +1 -18
  89. package/dist/src/graphql/utils.d.ts.map +1 -1
  90. package/dist/src/graphql/utils.js +7 -35
  91. package/dist/src/graphql/utils.js.map +1 -1
  92. package/dist/src/migrations/002_add_document_protection.d.ts +4 -0
  93. package/dist/src/migrations/002_add_document_protection.d.ts.map +1 -0
  94. package/dist/src/migrations/002_add_document_protection.js +18 -0
  95. package/dist/src/migrations/002_add_document_protection.js.map +1 -0
  96. package/dist/src/migrations/index.d.ts.map +1 -1
  97. package/dist/src/migrations/index.js +2 -0
  98. package/dist/src/migrations/index.js.map +1 -1
  99. package/dist/src/packages/http-loader.d.ts +68 -0
  100. package/dist/src/packages/http-loader.d.ts.map +1 -0
  101. package/dist/src/packages/http-loader.js +176 -0
  102. package/dist/src/packages/http-loader.js.map +1 -0
  103. package/dist/src/packages/https-hooks.d.mts +23 -0
  104. package/dist/src/packages/https-hooks.d.mts.map +1 -0
  105. package/dist/src/packages/https-hooks.mjs +59 -0
  106. package/dist/src/packages/https-hooks.mjs.map +1 -0
  107. package/dist/src/packages/import-loader.d.ts +5 -3
  108. package/dist/src/packages/import-loader.d.ts.map +1 -1
  109. package/dist/src/packages/import-loader.js +19 -10
  110. package/dist/src/packages/import-loader.js.map +1 -1
  111. package/dist/src/packages/package-manager.d.ts +2 -2
  112. package/dist/src/packages/package-manager.d.ts.map +1 -1
  113. package/dist/src/packages/package-manager.js.map +1 -1
  114. package/dist/src/packages/types.d.ts +9 -4
  115. package/dist/src/packages/types.d.ts.map +1 -1
  116. package/dist/src/packages/util.d.ts +3 -2
  117. package/dist/src/packages/util.d.ts.map +1 -1
  118. package/dist/src/packages/util.js +1 -1
  119. package/dist/src/packages/util.js.map +1 -1
  120. package/dist/src/packages/vite-loader.d.ts +10 -8
  121. package/dist/src/packages/vite-loader.d.ts.map +1 -1
  122. package/dist/src/packages/vite-loader.js +33 -10
  123. package/dist/src/packages/vite-loader.js.map +1 -1
  124. package/dist/src/server.d.ts +14 -11
  125. package/dist/src/server.d.ts.map +1 -1
  126. package/dist/src/server.js +153 -92
  127. package/dist/src/server.js.map +1 -1
  128. package/dist/src/services/auth.service.d.ts +0 -12
  129. package/dist/src/services/auth.service.d.ts.map +1 -1
  130. package/dist/src/services/auth.service.js +13 -40
  131. package/dist/src/services/auth.service.js.map +1 -1
  132. package/dist/src/services/authorization.service.d.ts +70 -0
  133. package/dist/src/services/authorization.service.d.ts.map +1 -0
  134. package/dist/src/services/authorization.service.js +155 -0
  135. package/dist/src/services/authorization.service.js.map +1 -0
  136. package/dist/src/services/document-permission.service.d.ts +47 -7
  137. package/dist/src/services/document-permission.service.d.ts.map +1 -1
  138. package/dist/src/services/document-permission.service.js +162 -7
  139. package/dist/src/services/document-permission.service.js.map +1 -1
  140. package/dist/src/services/package-management.service.d.ts +32 -0
  141. package/dist/src/services/package-management.service.d.ts.map +1 -0
  142. package/dist/src/services/package-management.service.js +95 -0
  143. package/dist/src/services/package-management.service.js.map +1 -0
  144. package/dist/src/services/package-storage.d.ts +23 -0
  145. package/dist/src/services/package-storage.d.ts.map +1 -0
  146. package/dist/src/services/package-storage.js +19 -0
  147. package/dist/src/services/package-storage.js.map +1 -0
  148. package/dist/src/tracing.d.ts.map +1 -1
  149. package/dist/src/tracing.js +19 -1
  150. package/dist/src/tracing.js.map +1 -1
  151. package/dist/src/types.d.ts +5 -5
  152. package/dist/src/types.d.ts.map +1 -1
  153. package/dist/src/utils/auth.d.ts +1 -1
  154. package/dist/src/utils/auth.d.ts.map +1 -1
  155. package/dist/src/utils/auth.js +5 -12
  156. package/dist/src/utils/auth.js.map +1 -1
  157. package/dist/src/utils/create-schema.d.ts +25 -6
  158. package/dist/src/utils/create-schema.d.ts.map +1 -1
  159. package/dist/src/utils/create-schema.js +458 -23
  160. package/dist/src/utils/create-schema.js.map +1 -1
  161. package/dist/src/utils/db.d.ts +8 -0
  162. package/dist/src/utils/db.d.ts.map +1 -1
  163. package/dist/src/utils/db.js.map +1 -1
  164. package/dist/src/utils/drive-url.d.ts +2 -0
  165. package/dist/src/utils/drive-url.d.ts.map +1 -0
  166. package/dist/src/utils/drive-url.js +3 -0
  167. package/dist/src/utils/drive-url.js.map +1 -0
  168. package/dist/src/utils/index.d.ts +1 -0
  169. package/dist/src/utils/index.d.ts.map +1 -1
  170. package/dist/src/utils/index.js +1 -0
  171. package/dist/src/utils/index.js.map +1 -1
  172. package/dist/test/authorization.service.test.d.ts +2 -0
  173. package/dist/test/authorization.service.test.d.ts.map +1 -0
  174. package/dist/test/authorization.service.test.js +252 -0
  175. package/dist/test/authorization.service.test.js.map +1 -0
  176. package/dist/test/connect-switchboard-reshuffle-convergence.test.d.ts +2 -0
  177. package/dist/test/connect-switchboard-reshuffle-convergence.test.d.ts.map +1 -0
  178. package/dist/test/connect-switchboard-reshuffle-convergence.test.js +203 -0
  179. package/dist/test/connect-switchboard-reshuffle-convergence.test.js.map +1 -0
  180. package/dist/test/connect-switchboard-sync.test.d.ts +2 -0
  181. package/dist/test/connect-switchboard-sync.test.d.ts.map +1 -0
  182. package/dist/test/connect-switchboard-sync.test.js +581 -0
  183. package/dist/test/connect-switchboard-sync.test.js.map +1 -0
  184. package/dist/test/document-drive-subgraph.test.d.ts +2 -0
  185. package/dist/test/document-drive-subgraph.test.d.ts.map +1 -0
  186. package/dist/test/document-drive-subgraph.test.js +186 -0
  187. package/dist/test/document-drive-subgraph.test.js.map +1 -0
  188. package/dist/test/document-model-subgraph-permissions.test.d.ts +2 -0
  189. package/dist/test/document-model-subgraph-permissions.test.d.ts.map +1 -0
  190. package/dist/test/document-model-subgraph-permissions.test.js +563 -0
  191. package/dist/test/document-model-subgraph-permissions.test.js.map +1 -0
  192. package/dist/test/drive-info-endpoint.test.d.ts +2 -0
  193. package/dist/test/drive-info-endpoint.test.d.ts.map +1 -0
  194. package/dist/test/drive-info-endpoint.test.js +123 -0
  195. package/dist/test/drive-info-endpoint.test.js.map +1 -0
  196. package/dist/test/fault-injection-sync.test.d.ts +2 -0
  197. package/dist/test/fault-injection-sync.test.d.ts.map +1 -0
  198. package/dist/test/fault-injection-sync.test.js +196 -0
  199. package/dist/test/fault-injection-sync.test.js.map +1 -0
  200. package/dist/test/index.d.ts +0 -1
  201. package/dist/test/index.d.ts.map +1 -1
  202. package/dist/test/index.js +0 -1
  203. package/dist/test/index.js.map +1 -1
  204. package/dist/test/permissions-integration.test.js +6 -20
  205. package/dist/test/permissions-integration.test.js.map +1 -1
  206. package/dist/test/push-backfill.test.d.ts +2 -0
  207. package/dist/test/push-backfill.test.d.ts.map +1 -0
  208. package/dist/test/push-backfill.test.js +298 -0
  209. package/dist/test/push-backfill.test.js.map +1 -0
  210. package/dist/test/reactor-client.test.js +172 -13
  211. package/dist/test/reactor-client.test.js.map +1 -1
  212. package/dist/test/reactor-resolvers.test.js +8 -11
  213. package/dist/test/reactor-resolvers.test.js.map +1 -1
  214. package/dist/test/reactor-subgraph-permissions.test.js +7 -36
  215. package/dist/test/reactor-subgraph-permissions.test.js.map +1 -1
  216. package/dist/test/subscriptions.test.js +2 -0
  217. package/dist/test/subscriptions.test.js.map +1 -1
  218. package/dist/test/utils/gql-resolver-bridge.d.ts +4 -1
  219. package/dist/test/utils/gql-resolver-bridge.d.ts.map +1 -1
  220. package/dist/test/utils/gql-resolver-bridge.js +36 -7
  221. package/dist/test/utils/gql-resolver-bridge.js.map +1 -1
  222. package/dist/tsconfig.tsbuildinfo +1 -1
  223. package/package.json +46 -55
  224. package/dist/src/graphql/drive-subgraph.d.ts +0 -25
  225. package/dist/src/graphql/drive-subgraph.d.ts.map +0 -1
  226. package/dist/src/graphql/drive-subgraph.js +0 -487
  227. package/dist/src/graphql/drive-subgraph.js.map +0 -1
  228. package/dist/src/graphql/system/system-subgraph.d.ts +0 -49
  229. package/dist/src/graphql/system/system-subgraph.d.ts.map +0 -1
  230. package/dist/src/graphql/system/system-subgraph.js +0 -130
  231. package/dist/src/graphql/system/system-subgraph.js.map +0 -1
  232. package/dist/src/sync/types.d.ts +0 -10
  233. package/dist/src/sync/types.d.ts.map +0 -1
  234. package/dist/src/sync/types.js +0 -2
  235. package/dist/src/sync/types.js.map +0 -1
  236. package/dist/src/sync/utils.d.ts +0 -7
  237. package/dist/src/sync/utils.d.ts.map +0 -1
  238. package/dist/src/sync/utils.js +0 -78
  239. package/dist/src/sync/utils.js.map +0 -1
  240. package/dist/test/drive-handlers.d.ts +0 -4
  241. package/dist/test/drive-handlers.d.ts.map +0 -1
  242. package/dist/test/drive-handlers.js +0 -39
  243. package/dist/test/drive-handlers.js.map +0 -1
  244. package/dist/test/drive-subgraph-permissions.test.d.ts +0 -2
  245. package/dist/test/drive-subgraph-permissions.test.d.ts.map +0 -1
  246. package/dist/test/drive-subgraph-permissions.test.js +0 -195
  247. package/dist/test/drive-subgraph-permissions.test.js.map +0 -1
  248. package/dist/test/drive.test.d.ts +0 -2
  249. package/dist/test/drive.test.d.ts.map +0 -1
  250. package/dist/test/drive.test.js +0 -142
  251. package/dist/test/drive.test.js.map +0 -1
  252. package/dist/test/pull-responder-transmitter.test.d.ts +0 -2
  253. package/dist/test/pull-responder-transmitter.test.d.ts.map +0 -1
  254. package/dist/test/pull-responder-transmitter.test.js +0 -220
  255. package/dist/test/pull-responder-transmitter.test.js.map +0 -1
  256. package/dist/test/push-transmitter.test.d.ts +0 -2
  257. package/dist/test/push-transmitter.test.d.ts.map +0 -1
  258. package/dist/test/push-transmitter.test.js +0 -179
  259. package/dist/test/push-transmitter.test.js.map +0 -1
  260. package/dist/test/system.test.d.ts +0 -2
  261. package/dist/test/system.test.d.ts.map +0 -1
  262. package/dist/test/system.test.js +0 -211
  263. package/dist/test/system.test.js.map +0 -1
  264. package/dist/test/three-reactor-gql-sync.test.d.ts +0 -2
  265. package/dist/test/three-reactor-gql-sync.test.d.ts.map +0 -1
  266. package/dist/test/three-reactor-gql-sync.test.js +0 -368
  267. package/dist/test/three-reactor-gql-sync.test.js.map +0 -1
  268. package/dist/test/two-reactor-gql-sync.test.d.ts +0 -2
  269. package/dist/test/two-reactor-gql-sync.test.d.ts.map +0 -1
  270. package/dist/test/two-reactor-gql-sync.test.js +0 -348
  271. package/dist/test/two-reactor-gql-sync.test.js.map +0 -1
@@ -6,14 +6,14 @@ import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin
6
6
  import { expressMiddleware } from "@as-integrations/express4";
7
7
  import bodyParser from "body-parser";
8
8
  import cors from "cors";
9
- import { childLogger, debounce } from "document-drive";
9
+ import { childLogger, debounce, responseForDrive } from "document-drive";
10
10
  import { Router } from "express";
11
11
  import path from "node:path";
12
12
  import { setTimeout } from "node:timers/promises";
13
+ import { match } from "path-to-regexp";
13
14
  import { AuthService } from "../services/auth.service.js";
14
15
  import { buildSubgraphSchemaModule, createSchema, } from "../utils/create-schema.js";
15
- import { DocumentModelSubgraphLegacy } from "./document-model-subgraph.js";
16
- import { DriveSubgraph } from "./drive-subgraph.js";
16
+ import { DocumentModelSubgraph } from "./document-model-subgraph.js";
17
17
  import { useServer } from "./websocket.js";
18
18
  class AuthenticatedDataSource extends RemoteGraphQLDataSource {
19
19
  willSendRequest(options) {
@@ -24,10 +24,41 @@ class AuthenticatedDataSource extends RemoteGraphQLDataSource {
24
24
  }
25
25
  }
26
26
  }
27
- const DOCUMENT_MODELS_TO_EXCLUDE = [
28
- "powerhouse/document-model",
29
- "powerhouse/document-drive",
30
- ];
27
+ const DOCUMENT_MODELS_TO_EXCLUDE = [];
28
+ /**
29
+ * Check if a document model has any operations with valid schemas.
30
+ * Document models without valid operation schemas cannot generate valid subgraph schemas.
31
+ */
32
+ function hasOperationSchemas(documentModel) {
33
+ const specification = documentModel.documentModel.global.specifications.at(-1);
34
+ if (!specification)
35
+ return false;
36
+ // Check if any operation has a schema with actual GraphQL type definitions
37
+ const hasValidSchema = (schema) => schema && /\b(input|type|enum|union|interface)\s+\w+/.test(schema);
38
+ return specification.modules.some((module) => module.operations.some((op) => hasValidSchema(op.schema)));
39
+ }
40
+ /**
41
+ * Filter document models to keep only the latest version of each unique document model.
42
+ * When multiple versions exist with the same name, the one with the most recent specification is kept.
43
+ */
44
+ function filterLatestDocumentModelVersions(documentModels) {
45
+ const latestByName = new Map();
46
+ for (const documentModel of documentModels) {
47
+ const name = documentModel.documentModel.global.name;
48
+ const existing = latestByName.get(name);
49
+ if (!existing) {
50
+ latestByName.set(name, documentModel);
51
+ continue;
52
+ }
53
+ // Compare version numbers from the latest specification
54
+ const currentVersion = documentModel.documentModel.global.specifications.at(-1)?.version ?? 0;
55
+ const existingVersion = existing.documentModel.global.specifications.at(-1)?.version ?? 0;
56
+ if (currentVersion > existingVersion) {
57
+ latestByName.set(name, documentModel);
58
+ }
59
+ }
60
+ return Array.from(latestByName.values());
61
+ }
31
62
  const DefaultFeatureFlags = {
32
63
  enableDocumentModelSubgraphs: true,
33
64
  };
@@ -36,40 +67,49 @@ export class GraphQLManager {
36
67
  app;
37
68
  httpServer;
38
69
  wsServer;
39
- reactor;
40
70
  reactorClient;
41
71
  relationalDb;
42
72
  analyticsStore;
43
73
  syncManager;
74
+ logger;
44
75
  authConfig;
45
76
  documentPermissionService;
46
77
  featureFlags;
78
+ port;
79
+ authorizationService;
47
80
  initialized = false;
48
- coreRouter = Router();
81
+ router = Router();
49
82
  coreSubgraphsMap = new Map();
50
- reactorRouter = Router();
51
83
  contextFields = {};
52
84
  subgraphs = new Map();
53
85
  authService = null;
54
- logger = childLogger(["reactor-api", "graphql-manager"]);
86
+ coreApolloServer = null;
87
+ subgraphServers = new Map();
88
+ subgraphHandlers = new Map();
89
+ subgraphWsDisposers = new Map();
90
+ gatewayOptions = null;
91
+ /** Cached document models for schema generation - updated on init and regenerate */
92
+ cachedDocumentModels = [];
55
93
  apolloLogger = childLogger([
56
94
  "reactor-api",
57
95
  "graphql-manager",
58
96
  "apollo",
59
97
  ]);
60
- constructor(path, app, httpServer, wsServer, reactor, reactorClient, relationalDb, analyticsStore, syncManager, authConfig, documentPermissionService, featureFlags = DefaultFeatureFlags) {
98
+ constructor(path, app, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, authConfig, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService) {
61
99
  this.path = path;
62
100
  this.app = app;
63
101
  this.httpServer = httpServer;
64
102
  this.wsServer = wsServer;
65
- this.reactor = reactor;
66
103
  this.reactorClient = reactorClient;
67
104
  this.relationalDb = relationalDb;
68
105
  this.analyticsStore = analyticsStore;
69
106
  this.syncManager = syncManager;
107
+ this.logger = logger;
70
108
  this.authConfig = authConfig;
71
109
  this.documentPermissionService = documentPermissionService;
72
110
  this.featureFlags = featureFlags;
111
+ this.port = port;
112
+ this.authorizationService = authorizationService;
73
113
  if (this.authConfig) {
74
114
  this.authService = new AuthService(this.authConfig);
75
115
  this.setAdditionalContextFields(this.authService.getAdditionalContextFields());
@@ -78,34 +118,25 @@ export class GraphQLManager {
78
118
  async init(coreSubgraphs) {
79
119
  this.logger.debug(`Initializing Subgraph Manager...`);
80
120
  // check if Document Drive model is available
81
- const models = this.reactor.getDocumentModelModules();
121
+ const modulesResult = await this.reactorClient.getDocumentModelModules();
122
+ const models = modulesResult.results;
123
+ // Cache models for schema generation
124
+ this.cachedDocumentModels = models;
82
125
  const driveModel = models.find((it) => it.documentModel.global.name === "DocumentDrive");
83
126
  if (!driveModel) {
84
127
  throw new Error("DocumentDrive model required");
85
128
  }
86
- this.coreRouter.use(cors());
87
- this.coreRouter.use(bodyParser.json({ limit: "50mb" }));
88
- this.coreRouter.use(bodyParser.urlencoded({ extended: true, limit: "50mb" }));
89
- this.app.use("/", (req, res, next) => {
90
- this.setAdditionalContextFields({
91
- user: req.user,
92
- isAdmin: (address) => !req.auth_enabled
93
- ? true
94
- : (req.admins
95
- ?.map((a) => a.toLowerCase())
96
- .includes(address.toLowerCase() ?? "") ?? false),
97
- isUser: (address) => !req.auth_enabled
98
- ? true
99
- : (req.users
100
- ?.map((a) => a.toLowerCase())
101
- .includes(address.toLowerCase() ?? "") ?? false),
102
- isGuest: (address) => !req.auth_enabled
103
- ? true
104
- : (req.guests
105
- ?.map((a) => a.toLowerCase())
106
- .includes(address.toLowerCase() ?? "") ?? false),
129
+ this.router.use(cors());
130
+ this.router.use(bodyParser.json({ limit: "50mb" }));
131
+ this.router.use(bodyParser.urlencoded({ extended: true, limit: "50mb" }));
132
+ this.router.use("/graphql", (req, res, next) => {
133
+ const result = this.subgraphHandlers.values().find(({ matcher }) => {
134
+ return matcher("/graphql" + req.path);
107
135
  });
108
- this.coreRouter(req, res, next);
136
+ if (!result) {
137
+ return res.status(404).send(`${req.path} subgraph not found`);
138
+ }
139
+ return result.handler(req, res, next);
109
140
  });
110
141
  this.app.use("/", (req, res, next) => {
111
142
  this.setAdditionalContextFields({
@@ -115,35 +146,38 @@ export class GraphQLManager {
115
146
  : (req.admins
116
147
  ?.map((a) => a.toLowerCase())
117
148
  .includes(address.toLowerCase() ?? "") ?? false),
118
- isUser: (address) => !req.auth_enabled
119
- ? true
120
- : (req.users
121
- ?.map((a) => a.toLowerCase())
122
- .includes(address.toLowerCase() ?? "") ?? false),
123
- isGuest: (address) => !req.auth_enabled
124
- ? true
125
- : (req.guests
126
- ?.map((a) => a.toLowerCase())
127
- .includes(address.toLowerCase() ?? "") ?? false),
128
149
  });
129
- this.reactorRouter(req, res, next);
150
+ this.router(req, res, next);
130
151
  });
131
152
  await this.#setupCoreSubgraphs("graphql", coreSubgraphs);
132
153
  if (this.featureFlags.enableDocumentModelSubgraphs) {
133
- await this.#setupDocumentModelSubgraphs("graphql", this.reactor.getDocumentModelModules());
154
+ await this.#setupDocumentModelSubgraphs("graphql", models);
134
155
  }
135
- this.reactor.on("documentModelModules", (documentModels) => {
136
- if (this.featureFlags.enableDocumentModelSubgraphs) {
137
- this.#setupDocumentModelSubgraphs("graphql", documentModels)
138
- .then(() => this.updateRouter())
139
- .catch((error) => this.logger.error(error));
140
- }
141
- else {
142
- this.updateRouter().catch((error) => this.logger.error(error));
143
- }
144
- });
156
+ await this.#createApolloGateway();
145
157
  return this.updateRouter();
146
158
  }
159
+ /**
160
+ * Regenerate document model subgraphs when models are dynamically loaded.
161
+ * Fetches current modules from reactor client (source of truth).
162
+ */
163
+ async regenerateDocumentModelSubgraphs() {
164
+ if (!this.featureFlags.enableDocumentModelSubgraphs) {
165
+ return;
166
+ }
167
+ try {
168
+ const modulesResult = await this.reactorClient.getDocumentModelModules();
169
+ const models = modulesResult.results;
170
+ // Update cached models for schema generation
171
+ this.cachedDocumentModels = models;
172
+ await this.#setupDocumentModelSubgraphs("graphql", models);
173
+ await this.updateRouter();
174
+ this.logger.info("Regenerated document model subgraphs with @count models", models.length);
175
+ }
176
+ catch (error) {
177
+ this.logger.error("Failed to regenerate document model subgraphs", error);
178
+ throw error;
179
+ }
180
+ }
147
181
  async #setupCoreSubgraphs(supergraph, coreSubgraphs) {
148
182
  for (const subgraph of coreSubgraphs) {
149
183
  try {
@@ -153,84 +187,103 @@ export class GraphQLManager {
153
187
  this.logger.error(`Failed to setup core subgraph ${subgraph.name}`, error);
154
188
  }
155
189
  }
156
- // special case for drive
157
- await this.registerSubgraph(DriveSubgraph, undefined, true);
158
- return this.#setupSubgraphs(this.coreSubgraphsMap, this.coreRouter);
190
+ // REST endpoint for drive info at /d/:drive
191
+ this.#setupDriveInfoRestEndpoint(this.router);
192
+ return this.#setupSubgraphs(this.coreSubgraphsMap);
159
193
  }
160
194
  async #setupDocumentModelSubgraphs(supergraph, documentModels) {
161
- for (const documentModel of documentModels) {
195
+ // Filter to keep only the latest version of each document model
196
+ const latestDocumentModels = filterLatestDocumentModelVersions(documentModels);
197
+ for (const documentModel of latestDocumentModels) {
162
198
  if (DOCUMENT_MODELS_TO_EXCLUDE.includes(documentModel.documentModel.global.id)) {
163
199
  continue; // Skip the legacy document model
164
200
  }
201
+ if (!hasOperationSchemas(documentModel)) {
202
+ continue; // Skip document models without operation schemas
203
+ }
165
204
  try {
166
- const subgraphInstance = new DocumentModelSubgraphLegacy(documentModel, {
205
+ const subgraphInstance = new DocumentModelSubgraph(documentModel, {
167
206
  relationalDb: this.relationalDb,
168
207
  analyticsStore: this.analyticsStore,
169
- reactor: this.reactor,
170
208
  reactorClient: this.reactorClient,
171
209
  graphqlManager: this,
172
210
  syncManager: this.syncManager,
173
211
  path: this.path,
174
212
  documentPermissionService: this.documentPermissionService,
213
+ authorizationService: this.authorizationService,
175
214
  });
176
215
  await this.#addSubgraphInstance(subgraphInstance, supergraph, false);
177
216
  }
178
217
  catch (error) {
179
218
  this.logger.error(`Failed to setup document model subgraph for ${documentModel.documentModel.global.id}`, error instanceof Error ? error.message : error);
180
- this.logger.debug(error);
219
+ this.logger.debug("@error", error);
181
220
  }
182
221
  }
183
- // special case for drive
184
- await this.registerSubgraph(DriveSubgraph, undefined, true);
185
- return this.#setupSubgraphs(this.coreSubgraphsMap, this.coreRouter);
222
+ return this.#setupSubgraphs(this.coreSubgraphsMap);
186
223
  }
187
224
  async #addSubgraphInstance(subgraphInstance, supergraph = "", core = false) {
188
- await subgraphInstance.onSetup?.();
189
225
  const subgraphsMap = core ? this.coreSubgraphsMap : this.subgraphs;
190
226
  const subgraphs = subgraphsMap.get(supergraph) ?? [];
191
227
  const existingSubgraph = subgraphs.find((it) => it.name === subgraphInstance.name);
228
+ if (existingSubgraph) {
229
+ this.logger.debug(`Skipping duplicate subgraph: ${subgraphInstance.name}`);
230
+ return existingSubgraph;
231
+ }
232
+ await subgraphInstance.onSetup?.();
192
233
  subgraphs.push(subgraphInstance);
193
234
  subgraphsMap.set(supergraph, subgraphs);
194
235
  // also add to global graphql supergraph
195
236
  if (supergraph !== "" && supergraph !== "graphql") {
196
237
  subgraphsMap.get("graphql")?.push(subgraphInstance);
197
238
  }
198
- const logMessage = `Registered ${this.path.endsWith("/") ? this.path : this.path + "/"}${supergraph ? supergraph + "/" : ""}${subgraphInstance.name} subgraph.`;
199
- if (!existingSubgraph) {
200
- this.logger.info(logMessage);
201
- }
202
- else {
203
- this.logger.debug(logMessage);
204
- }
239
+ this.logger.info(`Registered ${this.path.endsWith("/") ? this.path : this.path + "/"}${supergraph ? supergraph + "/" : ""}${subgraphInstance.name} subgraph.`);
205
240
  return subgraphInstance;
206
241
  }
242
+ /**
243
+ * Register a pre-constructed subgraph instance.
244
+ * Use this when you need to pass custom dependencies to a subgraph.
245
+ */
246
+ async registerSubgraphInstance(subgraphInstance, supergraph = "", core = false) {
247
+ return this.#addSubgraphInstance(subgraphInstance, supergraph, core);
248
+ }
249
+ /**
250
+ * Get the base path used for subgraph registration.
251
+ */
252
+ getBasePath() {
253
+ return this.path;
254
+ }
207
255
  async registerSubgraph(subgraph, supergraph = "", core = false) {
208
256
  const subgraphInstance = new subgraph({
209
257
  relationalDb: this.relationalDb,
210
258
  analyticsStore: this.analyticsStore,
211
- reactor: this.reactor,
212
259
  reactorClient: this.reactorClient,
213
260
  graphqlManager: this,
214
261
  syncManager: this.syncManager,
215
262
  path: this.path,
216
263
  documentPermissionService: this.documentPermissionService,
264
+ authorizationService: this.authorizationService,
217
265
  });
218
266
  return this.#addSubgraphInstance(subgraphInstance, supergraph, core);
219
267
  }
220
268
  updateRouter = debounce(this._updateRouter.bind(this), 1000);
221
269
  async _updateRouter() {
222
270
  this.logger.debug("Updating router");
223
- const newRouter = Router();
224
- newRouter.use(cors());
225
- newRouter.use(bodyParser.json());
226
271
  // @todo:
227
272
  // if auth enabled, subgraphs are only available to guests, users and admins
228
273
  // if auth enabled, set req user to the graphql context
229
274
  // if auth disabled, subgraphs are available to all
230
- await this.#setupSubgraphs(this.subgraphs, newRouter);
231
- this.reactorRouter = newRouter;
232
- await this.#createApolloGateway();
233
- return;
275
+ await this.#setupSubgraphs(this.subgraphs);
276
+ // Update Apollo Gateway's supergraph when subgraphs change
277
+ if (this.gatewayOptions) {
278
+ try {
279
+ const { supergraphSdl } = await this.#buildSupergrahSdl();
280
+ this.gatewayOptions.update(supergraphSdl);
281
+ this.logger.debug("Updated Apollo Gateway supergraph");
282
+ }
283
+ catch (error) {
284
+ this.logger.error("Failed to update Apollo Gateway supergraph", error);
285
+ }
286
+ }
234
287
  }
235
288
  getAdditionalContextFields = () => {
236
289
  return this.contextFields;
@@ -247,7 +300,6 @@ export class GraphQLManager {
247
300
  const context = {
248
301
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
249
302
  headers: connectionParams,
250
- driveServer: this.reactor,
251
303
  db: this.relationalDb,
252
304
  ...this.getAdditionalContextFields(),
253
305
  };
@@ -276,8 +328,8 @@ export class GraphQLManager {
276
328
  });
277
329
  });
278
330
  }
279
- #createApolloServer(schema) {
280
- return new ApolloServer({
331
+ async #createApolloServer(schema) {
332
+ const server = new ApolloServer({
281
333
  schema,
282
334
  logger: this.apolloLogger,
283
335
  introspection: true,
@@ -286,6 +338,9 @@ export class GraphQLManager {
286
338
  ApolloServerPluginLandingPageLocalDefault(),
287
339
  ],
288
340
  });
341
+ server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();
342
+ await this.#waitForServer(server);
343
+ return server;
289
344
  }
290
345
  async #waitForServer(server) {
291
346
  try {
@@ -300,34 +355,50 @@ export class GraphQLManager {
300
355
  #getSubgraphPath(subgraph, supergraph) {
301
356
  return path.join(subgraph.path ?? "", supergraph, subgraph.name);
302
357
  }
303
- async #setupSubgraphs(subgraphsMap, router) {
358
+ async #setupSubgraphs(subgraphsMap) {
304
359
  for (const [supergraph, subgraphs] of subgraphsMap.entries()) {
305
360
  for (const subgraph of subgraphs) {
306
361
  this.logger.debug(`Setting up subgraph ${subgraph.name}`);
362
+ const subgraphPath = this.#getSubgraphPath(subgraph, supergraph);
307
363
  try {
364
+ // dispose existing websocket server before starting new one
365
+ const existingWsDisposer = this.subgraphWsDisposers.get(subgraphPath);
366
+ if (existingWsDisposer) {
367
+ try {
368
+ await existingWsDisposer.dispose();
369
+ }
370
+ catch {
371
+ // ignore error when disposing websocket server
372
+ }
373
+ this.subgraphWsDisposers.delete(subgraphPath);
374
+ }
308
375
  // create subgraph schema
309
- const schema = createSchema(this.reactor, subgraph.resolvers, subgraph.typeDefs);
376
+ const schema = createSchema(this.cachedDocumentModels, subgraph.resolvers, subgraph.typeDefs);
310
377
  // create and start apollo server
311
- const server = this.#createApolloServer(schema);
312
- server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests();
313
- await this.#waitForServer(server);
378
+ const existingServer = this.subgraphServers.get(subgraphPath);
379
+ const server = existingServer || (await this.#createApolloServer(schema));
380
+ this.subgraphServers.set(subgraphPath, server);
314
381
  if (subgraph.hasSubscriptions) {
315
- useServer({
316
- schema,
317
- context: async (ctx) => {
318
- const connectionParams = (ctx.connectionParams ??
319
- {});
320
- return this.#createWebSocketContext(connectionParams);
321
- },
322
- }, this.wsServer);
323
- this.logger.info(`WebSocket subscriptions enabled for ${subgraph.name}`);
382
+ try {
383
+ const wsDisposer = useServer({
384
+ schema,
385
+ context: async (ctx) => {
386
+ const connectionParams = (ctx.connectionParams ??
387
+ {});
388
+ return this.#createWebSocketContext(connectionParams);
389
+ },
390
+ }, this.wsServer);
391
+ this.subgraphWsDisposers.set(subgraphPath, wsDisposer);
392
+ this.logger.debug(`WebSocket subscriptions enabled for ${subgraph.name}`);
393
+ }
394
+ catch (error) {
395
+ this.logger.error("Failed to setup websocket for subgraph @name at path @path: @error", subgraph.name, subgraphPath, error);
396
+ }
324
397
  }
325
- const path = this.#getSubgraphPath(subgraph, supergraph);
326
- this.#setupApolloExpressMiddleware(server, router, path);
398
+ this.#setupApolloExpressMiddleware(server, subgraphPath);
327
399
  }
328
400
  catch (error) {
329
- this.logger.error(`Failed to setup subgraph ${subgraph.name} at path ${this.#getSubgraphPath(subgraph, supergraph)}`);
330
- this.logger.error(error);
401
+ this.logger.error("Failed to setup subgraph @name at path @path: @error", subgraph.name, subgraphPath, error);
331
402
  }
332
403
  }
333
404
  }
@@ -348,69 +419,103 @@ export class GraphQLManager {
348
419
  }
349
420
  return subgraphsMap;
350
421
  }
422
+ /**
423
+ * Setup REST GET endpoint for drive info at /d/:drive
424
+ * Accepts both drive slug (e.g., "powerhouse") and UUID
425
+ * Returns DriveInfo JSON: { id, name, slug, icon, meta, graphqlEndpoint }
426
+ */
427
+ #setupDriveInfoRestEndpoint(router) {
428
+ const routePath = path.join(this.path, "d/:drive");
429
+ router.get(routePath, (req, res) => {
430
+ const driveIdOrSlug = req.params.drive;
431
+ if (!driveIdOrSlug) {
432
+ res.status(400).json({ error: "Drive ID or slug is required" });
433
+ return;
434
+ }
435
+ (async () => {
436
+ const driveDoc = await this.reactorClient.get(driveIdOrSlug);
437
+ // Construct the graphqlEndpoint from the request
438
+ // Use X-Forwarded-Proto header when behind a reverse proxy (Heroku, Traefik, etc.)
439
+ const forwardedProto = req.get("x-forwarded-proto");
440
+ const protocol = (forwardedProto ?? req.protocol) + ":";
441
+ const host = req.get("host") ?? "";
442
+ const basePath = this.path === "/" ? "" : this.path;
443
+ const graphqlEndpoint = `${protocol}//${host}${basePath}/graphql/r`;
444
+ const driveInfo = responseForDrive(driveDoc, graphqlEndpoint);
445
+ res.json(driveInfo);
446
+ })().catch((error) => {
447
+ this.logger.debug(`Drive not found: ${driveIdOrSlug}`, error);
448
+ res.status(404).json({ error: "Drive not found" });
449
+ });
450
+ });
451
+ this.logger.info(`Registered REST endpoint: GET ${routePath}`);
452
+ }
351
453
  #buildSubgraphSchemaModule(subgraph) {
352
- return buildSubgraphSchemaModule(this.reactor, subgraph.resolvers, subgraph.typeDefs);
454
+ return buildSubgraphSchemaModule(this.cachedDocumentModels, subgraph.resolvers, subgraph.typeDefs);
353
455
  }
354
- async #createApolloGateway() {
456
+ async #buildSupergrahSdl() {
457
+ if (!this.gatewayOptions) {
458
+ throw new Error("Gateway is not ready");
459
+ }
355
460
  const subgraphs = this.#getAllSubgraphs();
356
- try {
357
- const herokuOrLocal = process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME
358
- ? `https://${process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME}`
359
- : `http://localhost:${process.env.PORT ?? 4001}`;
360
- const serviceList = Array.from(subgraphs.entries()).map(([path, subgraph]) => ({
361
- name: path.replace("/", ":"),
362
- typeDefs: this.#buildSubgraphSchemaModule(subgraph).typeDefs,
363
- url: `${herokuOrLocal}${path}`,
364
- }));
365
- const gateway = new ApolloGateway({
366
- supergraphSdl: new LocalCompose({
367
- localServiceList: serviceList,
368
- }),
369
- buildService: (serviceConfig) => {
370
- return new AuthenticatedDataSource(serviceConfig);
371
- },
372
- });
373
- const server = new ApolloServer({
374
- gateway,
375
- logger: this.apolloLogger,
376
- introspection: true,
377
- plugins: [
378
- ApolloServerPluginDrainHttpServer({
379
- httpServer: this.httpServer,
380
- }),
381
- ApolloServerPluginInlineTraceDisabled(),
382
- ApolloServerPluginLandingPageLocalDefault(),
383
- ],
384
- });
385
- await server.start();
386
- await this.#waitForServer(server);
387
- const superGraphPath = path.join(this.path, "graphql");
388
- this.#setupApolloExpressMiddleware(server, this.reactorRouter, superGraphPath);
389
- if (!this.initialized) {
390
- this.logger.info(`Registered ${superGraphPath} supergraph `);
391
- this.initialized = true;
392
- }
393
- return server;
461
+ const herokuOrLocal = process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME
462
+ ? `https://${process.env.HEROKU_APP_DEFAULT_DOMAIN_NAME}`
463
+ : `http://localhost:${this.port}`;
464
+ const serviceList = Array.from(subgraphs.entries()).map(([path, subgraph]) => ({
465
+ name: path.replace("/", ":"),
466
+ typeDefs: this.#buildSubgraphSchemaModule(subgraph).typeDefs,
467
+ url: `${herokuOrLocal}${path}`,
468
+ }));
469
+ const localCompose = new LocalCompose({
470
+ localServiceList: serviceList,
471
+ });
472
+ return await localCompose.initialize(this.gatewayOptions);
473
+ }
474
+ async #createApolloGateway() {
475
+ const gateway = new ApolloGateway({
476
+ supergraphSdl: async (options) => {
477
+ this.gatewayOptions = options;
478
+ return await this.#buildSupergrahSdl();
479
+ },
480
+ buildService: (serviceConfig) => {
481
+ return new AuthenticatedDataSource(serviceConfig);
482
+ },
483
+ });
484
+ if (this.coreApolloServer) {
485
+ throw new Error("Supergrah server is already running");
394
486
  }
395
- catch (e) {
396
- if (e instanceof Error) {
397
- this.logger.error(e.message);
398
- }
399
- else {
400
- this.logger.error("Could not create Apollo Gateway", e);
401
- }
487
+ this.coreApolloServer = new ApolloServer({
488
+ gateway,
489
+ logger: this.apolloLogger,
490
+ introspection: true,
491
+ plugins: [
492
+ ApolloServerPluginDrainHttpServer({ httpServer: this.httpServer }),
493
+ ApolloServerPluginInlineTraceDisabled(),
494
+ ApolloServerPluginLandingPageLocalDefault(),
495
+ ],
496
+ });
497
+ await this.coreApolloServer.start();
498
+ await this.#waitForServer(this.coreApolloServer);
499
+ const superGraphPath = path.join(this.path, "graphql");
500
+ this.#setupApolloExpressMiddleware(this.coreApolloServer, superGraphPath);
501
+ if (!this.initialized) {
502
+ this.logger.info(`Registered ${superGraphPath} supergraph `);
503
+ this.initialized = true;
402
504
  }
505
+ return;
403
506
  }
404
- #setupApolloExpressMiddleware(server, router, path) {
405
- router.use(path, expressMiddleware(server, {
406
- context: ({ req }) => Promise.resolve({
407
- headers: req.headers,
408
- driveId: req.params.drive ?? undefined,
409
- driveServer: this.reactor,
410
- db: this.relationalDb,
411
- ...this.getAdditionalContextFields(),
507
+ #setupApolloExpressMiddleware(server, path) {
508
+ this.subgraphHandlers.set(path, {
509
+ handler: expressMiddleware(server, {
510
+ context: ({ req }) => Promise.resolve({
511
+ headers: req.headers,
512
+ driveId: req.params.drive ?? undefined,
513
+ db: this.relationalDb,
514
+ ...this.getAdditionalContextFields(),
515
+ }),
412
516
  }),
413
- }));
517
+ matcher: match(path),
518
+ });
414
519
  }
415
520
  }
416
521
  //# sourceMappingURL=graphql-manager.js.map