@powerhousedao/reactor-api 6.2.0-dev.4 → 6.2.0-dev.41

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.mjs CHANGED
@@ -1,20 +1,19 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5769db0f-d60a-5870-9d50-dc4f5aca8086")}catch(e){}}();
3
- import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, i as buildGraphqlOperations, l as loadProcessors, n as buildGraphQlDriveDocument, o as debounce, r as buildGraphqlOperation, t as buildGraphQlDocument, u as loadSubgraphs } from "./utils-BFkbSO_H.mjs";
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6300f5e6-16a2-5335-a19f-9064a271f52d")}catch(e){}}();
3
+ import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, f as AuthorizationPolicy, i as buildGraphqlOperations, l as loadProcessors, m as createAuthorizationService, n as buildGraphQlDriveDocument, o as debounce, p as AuthorizedDocumentHandle, r as buildGraphqlOperation, t as buildGraphQlDocument, u as loadSubgraphs } from "./utils-iibOQ50e.mjs";
4
4
  import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
5
5
  import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
6
6
  import { gql } from "graphql-tag";
7
7
  import { GraphQLError, Kind, parse, print } from "graphql";
8
+ import { DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl } from "@powerhousedao/reactor";
8
9
  import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
9
10
  import path from "node:path";
10
11
  import { match } from "path-to-regexp";
11
- import { verifyAuthBearerToken } from "@renown/sdk";
12
12
  import { buildSubgraphSchema } from "@apollo/subgraph";
13
13
  import { typeDefs } from "@powerhousedao/document-engineering/graphql";
14
14
  import { camelCase, kebabCase, pascalCase } from "change-case";
15
15
  import { GraphQLJSONObject } from "graphql-type-json";
16
16
  import { setName } from "@powerhousedao/shared/document-model";
17
- import { DEFAULT_DRIVE_CONTAINER_TYPES, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl } from "@powerhousedao/reactor";
18
17
  import * as z$1 from "zod";
19
18
  import { z } from "zod";
20
19
  import { createHandler } from "graphql-sse/lib/use/fetch";
@@ -35,6 +34,7 @@ import { tmpdir } from "node:os";
35
34
  import { WebSocketServer } from "ws";
36
35
  import { createRelationalDb } from "@powerhousedao/shared/processors";
37
36
  import { Kysely, Migrator, sql } from "kysely";
37
+ import { verifyAuthBearerToken } from "@renown/sdk";
38
38
  import { PGlite } from "@electric-sql/pglite";
39
39
  import { AtomicNodeFs } from "@powerhousedao/pglite-fs";
40
40
  import knex from "knex";
@@ -70,141 +70,70 @@ var AnalyticsSubgraph = class extends BaseSubgraph {
70
70
  };
71
71
  //#endregion
72
72
  //#region src/graphql/auth/schema.graphql
73
- var schema_default$2 = "# Auth Subgraph Schema\n# Contains all document permission and authorization related types\n\nscalar DateTime\n\n# Permission levels for documents\nenum DocumentPermissionLevel {\n # Can fetch and read the document\n READ\n # Can push updates and modify the document\n WRITE\n # Can manage document permissions and settings\n ADMIN\n}\n\n# Document permission entry\ntype DocumentPermissionEntry {\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n grantedBy: String!\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\n# Group type\ntype Group {\n id: Int!\n name: String!\n description: String\n createdAt: DateTime!\n updatedAt: DateTime!\n members: [String!]!\n}\n\n# Document group permission entry\ntype DocumentGroupPermission {\n documentId: String!\n groupId: Int!\n group: Group!\n permission: DocumentPermissionLevel!\n grantedBy: String!\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\n# Operation user permission entry\ntype OperationUserPermission {\n documentId: String!\n operationType: String!\n userAddress: String!\n grantedBy: String!\n createdAt: DateTime!\n}\n\n# Operation group permission entry\ntype OperationGroupPermission {\n documentId: String!\n operationType: String!\n groupId: Int!\n group: Group!\n grantedBy: String!\n createdAt: DateTime!\n}\n\n# Combined document access info\ntype DocumentAccessInfo {\n documentId: String!\n permissions: [DocumentPermissionEntry!]!\n groupPermissions: [DocumentGroupPermission!]!\n}\n\n# Operation permissions info\ntype OperationPermissionsInfo {\n documentId: String!\n operationType: String!\n userPermissions: [OperationUserPermission!]!\n groupPermissions: [OperationGroupPermission!]!\n}\n\n# Document protection info\ntype DocumentProtectionInfo {\n documentId: String!\n protected: Boolean!\n ownerAddress: String\n}\n\ntype Query {\n # Get permissions for a document\n documentAccess(documentId: String!): DocumentAccessInfo!\n\n # Get protection status for a document\n documentProtection(documentId: String!): DocumentProtectionInfo!\n\n # Get all documents the current user has explicit access to\n userDocumentPermissions: [DocumentPermissionEntry!]!\n\n # Get all groups\n groups: [Group!]!\n\n # Get a specific group by ID\n group(id: Int!): Group\n\n # Get all groups a user belongs to\n userGroups(userAddress: String!): [Group!]!\n\n # Get operation permissions for a document and operation type\n operationPermissions(\n documentId: String!\n operationType: String!\n ): OperationPermissionsInfo!\n\n # Check if the current user can execute a specific operation on a document\n canExecuteOperation(documentId: String!, operationType: String!): Boolean!\n}\n\ntype Mutation {\n # Set document protection status (requires ADMIN permission or global admin)\n setDocumentProtection(\n documentId: String!\n protected: Boolean!\n ): DocumentProtectionInfo!\n\n # Transfer document ownership (requires ADMIN permission or global admin)\n transferDocumentOwnership(\n documentId: String!\n newOwnerAddress: String!\n ): DocumentProtectionInfo!\n\n # Grant a user permission on a document (requires ADMIN permission or global admin)\n grantDocumentPermission(\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n ): DocumentPermissionEntry!\n\n # Revoke a user's permission on a document (requires ADMIN permission or global admin)\n revokeDocumentPermission(documentId: String!, userAddress: String!): Boolean!\n\n # --- Group Management ---\n\n # Create a new group\n createGroup(name: String!, description: String): Group!\n\n # Delete a group and all its associations\n deleteGroup(id: Int!): Boolean!\n\n # Add a user to a group\n addUserToGroup(userAddress: String!, groupId: Int!): Boolean!\n\n # Remove a user from a group\n removeUserFromGroup(userAddress: String!, groupId: Int!): Boolean!\n\n # --- Group Document Permissions ---\n\n # Grant a group permission on a document\n grantGroupPermission(\n documentId: String!\n groupId: Int!\n permission: DocumentPermissionLevel!\n ): DocumentGroupPermission!\n\n # Revoke a group's permission on a document\n revokeGroupPermission(documentId: String!, groupId: Int!): Boolean!\n\n # --- Operation Permissions ---\n\n # Grant a user permission to execute an operation on a document\n grantOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): OperationUserPermission!\n\n # Revoke a user's permission to execute an operation\n revokeOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): Boolean!\n\n # Grant a group permission to execute an operation on a document\n grantGroupOperationPermission(\n documentId: String!\n operationType: String!\n groupId: Int!\n ): OperationGroupPermission!\n\n # Revoke a group's permission to execute an operation\n revokeGroupOperationPermission(\n documentId: String!\n operationType: String!\n groupId: Int!\n ): Boolean!\n}\n";
73
+ var schema_default$2 = "# Auth Subgraph Schema\n# Contains all document permission and authorization related types\n\nscalar DateTime\n\n# Permission levels for documents\nenum DocumentPermissionLevel {\n # Can fetch and read the document\n READ\n # Can push updates and modify the document\n WRITE\n # Can manage document permissions and settings\n ADMIN\n}\n\n# Document permission entry\ntype DocumentPermissionEntry {\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n grantedBy: String!\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\n# Operation user permission entry\ntype OperationUserPermission {\n documentId: String!\n operationType: String!\n userAddress: String!\n grantedBy: String!\n createdAt: DateTime!\n}\n\n# Combined document access info\ntype DocumentAccessInfo {\n documentId: String!\n permissions: [DocumentPermissionEntry!]!\n}\n\n# Operation permissions info\ntype OperationPermissionsInfo {\n documentId: String!\n operationType: String!\n userPermissions: [OperationUserPermission!]!\n}\n\n# Document protection info\ntype DocumentProtectionInfo {\n documentId: String!\n protected: Boolean!\n ownerAddress: String\n}\n\ntype Query {\n # Get permissions for a document (requires ADMIN permission or global admin)\n documentAccess(documentId: String!): DocumentAccessInfo!\n\n # Get protection status for a document (requires ADMIN permission or global admin)\n documentProtection(documentId: String!): DocumentProtectionInfo!\n\n # Get all documents the current user has explicit access to\n userDocumentPermissions: [DocumentPermissionEntry!]!\n\n # Get operation permissions for a document and operation type\n # (requires ADMIN permission or global admin)\n operationPermissions(\n documentId: String!\n operationType: String!\n ): OperationPermissionsInfo!\n\n # Check if the current user can execute a specific operation on a document\n canExecuteOperation(documentId: String!, operationType: String!): Boolean!\n}\n\ntype Mutation {\n # Set document protection status (requires ADMIN permission or global admin)\n setDocumentProtection(\n documentId: String!\n protected: Boolean!\n ): DocumentProtectionInfo!\n\n # Transfer document ownership (requires ADMIN permission or global admin)\n transferDocumentOwnership(\n documentId: String!\n newOwnerAddress: String!\n ): DocumentProtectionInfo!\n\n # Grant a user permission on a document (requires ADMIN permission or global admin)\n grantDocumentPermission(\n documentId: String!\n userAddress: String!\n permission: DocumentPermissionLevel!\n ): DocumentPermissionEntry!\n\n # Revoke a user's permission on a document (requires ADMIN permission or global admin)\n revokeDocumentPermission(documentId: String!, userAddress: String!): Boolean!\n\n # --- Operation Permissions ---\n\n # Grant a user permission to execute an operation on a document\n grantOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): OperationUserPermission!\n\n # Revoke a user's permission to execute an operation\n revokeOperationPermission(\n documentId: String!\n operationType: String!\n userAddress: String!\n ): Boolean!\n}\n";
74
74
  //#endregion
75
75
  //#region src/graphql/auth/resolvers.ts
76
- async function documentAccess(service, args) {
76
+ async function documentAccess(service, authorizationService, args, userAddress) {
77
+ if (!userAddress) throw new GraphQLError("Authentication required");
78
+ if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its permissions");
77
79
  const permissions = await service.getDocumentPermissions(args.documentId);
78
- const groupPermissions = await service.getDocumentGroupPermissions(args.documentId);
79
80
  return {
80
81
  documentId: args.documentId,
81
- permissions,
82
- groupPermissions
82
+ permissions
83
83
  };
84
84
  }
85
85
  async function userDocumentPermissions(service, userAddress) {
86
86
  return service.getUserDocuments(userAddress);
87
87
  }
88
- async function grantDocumentPermission(service, args, grantedByAddress, isGlobalAdmin) {
88
+ async function grantDocumentPermission(service, authorizationService, args, grantedByAddress) {
89
89
  if (!grantedByAddress) throw new GraphQLError("Authentication required");
90
- if (!isGlobalAdmin) {
91
- if (!await service.canManageDocument(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant permissions");
92
- }
90
+ if (!await authorizationService.canManage(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant permissions");
93
91
  return service.grantPermission(args.documentId, args.userAddress, args.permission, grantedByAddress);
94
92
  }
95
- async function revokeDocumentPermission(service, args, revokedByAddress, isGlobalAdmin) {
93
+ async function revokeDocumentPermission(service, authorizationService, args, revokedByAddress) {
96
94
  if (!revokedByAddress) throw new GraphQLError("Authentication required");
97
- if (!isGlobalAdmin) {
98
- if (!await service.canManageDocument(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke permissions");
99
- }
95
+ if (!await authorizationService.canManage(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke permissions");
100
96
  await service.revokePermission(args.documentId, args.userAddress);
101
97
  return true;
102
98
  }
103
- async function groups(service) {
104
- return service.listGroups();
105
- }
106
- async function group(service, args) {
107
- return service.getGroup(args.id);
108
- }
109
- async function userGroups(service, args) {
110
- return service.getUserGroups(args.userAddress);
111
- }
112
- async function createGroup(service, args) {
113
- return service.createGroup(args.name, args.description ?? void 0);
114
- }
115
- async function deleteGroup(service, args) {
116
- await service.deleteGroup(args.id);
117
- return true;
118
- }
119
- async function addUserToGroup(service, args) {
120
- await service.addUserToGroup(args.userAddress, args.groupId);
121
- return true;
122
- }
123
- async function removeUserFromGroup(service, args) {
124
- await service.removeUserFromGroup(args.userAddress, args.groupId);
125
- return true;
126
- }
127
- async function getGroupMembers(service, groupId) {
128
- return service.getGroupMembers(groupId);
129
- }
130
- async function grantGroupPermission(service, args, grantedByAddress, isGlobalAdmin) {
131
- if (!grantedByAddress) throw new GraphQLError("Authentication required");
132
- if (!isGlobalAdmin) {
133
- if (!await service.canManageDocument(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant permissions");
134
- }
135
- return service.grantGroupPermission(args.documentId, args.groupId, args.permission, grantedByAddress);
136
- }
137
- async function revokeGroupPermission(service, args, revokedByAddress, isGlobalAdmin) {
138
- if (!revokedByAddress) throw new GraphQLError("Authentication required");
139
- if (!isGlobalAdmin) {
140
- if (!await service.canManageDocument(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke permissions");
141
- }
142
- await service.revokeGroupPermission(args.documentId, args.groupId);
143
- return true;
144
- }
145
- async function operationPermissions(service, args) {
99
+ async function operationPermissions(service, authorizationService, args, userAddress) {
100
+ if (!userAddress) throw new GraphQLError("Authentication required");
101
+ if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its operation permissions");
146
102
  const userPermissions = await service.getOperationUserPermissions(args.documentId, args.operationType);
147
- const groupPermissions = await service.getOperationGroupPermissions(args.documentId, args.operationType);
148
103
  return {
149
104
  documentId: args.documentId,
150
105
  operationType: args.operationType,
151
- userPermissions,
152
- groupPermissions
106
+ userPermissions
153
107
  };
154
108
  }
155
- async function canExecuteOperation(service, args, userAddress) {
156
- return service.canExecuteOperation(args.documentId, args.operationType, userAddress);
109
+ async function canExecuteOperation(authorizationService, args, userAddress) {
110
+ return authorizationService.canMutate(args.documentId, args.operationType, userAddress);
157
111
  }
158
- async function grantOperationPermission(service, args, grantedByAddress, isGlobalAdmin) {
112
+ async function grantOperationPermission(service, authorizationService, args, grantedByAddress) {
159
113
  if (!grantedByAddress) throw new GraphQLError("Authentication required");
160
- if (!isGlobalAdmin) {
161
- if (!await service.canManageDocument(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant operation permissions");
162
- }
114
+ if (!await authorizationService.canManage(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant operation permissions");
163
115
  return service.grantOperationPermission(args.documentId, args.operationType, args.userAddress, grantedByAddress);
164
116
  }
165
- async function revokeOperationPermission(service, args, revokedByAddress, isGlobalAdmin) {
117
+ async function revokeOperationPermission(service, authorizationService, args, revokedByAddress) {
166
118
  if (!revokedByAddress) throw new GraphQLError("Authentication required");
167
- if (!isGlobalAdmin) {
168
- if (!await service.canManageDocument(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke operation permissions");
169
- }
119
+ if (!await authorizationService.canManage(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke operation permissions");
170
120
  await service.revokeOperationPermission(args.documentId, args.operationType, args.userAddress);
171
121
  return true;
172
122
  }
173
- async function grantGroupOperationPermission(service, args, grantedByAddress, isGlobalAdmin) {
174
- if (!grantedByAddress) throw new GraphQLError("Authentication required");
175
- if (!isGlobalAdmin) {
176
- if (!await service.canManageDocument(args.documentId, grantedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to grant operation permissions");
177
- }
178
- return service.grantGroupOperationPermission(args.documentId, args.operationType, args.groupId, grantedByAddress);
179
- }
180
- async function revokeGroupOperationPermission(service, args, revokedByAddress, isGlobalAdmin) {
181
- if (!revokedByAddress) throw new GraphQLError("Authentication required");
182
- if (!isGlobalAdmin) {
183
- if (!await service.canManageDocument(args.documentId, revokedByAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to revoke operation permissions");
184
- }
185
- await service.revokeGroupOperationPermission(args.documentId, args.operationType, args.groupId);
186
- return true;
187
- }
188
- async function documentProtection(service, args) {
123
+ async function documentProtection(service, authorizationService, args, userAddress) {
124
+ if (!userAddress) throw new GraphQLError("Authentication required");
125
+ if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to view its protection info");
189
126
  return service.getDocumentProtection(args.documentId);
190
127
  }
191
- async function setDocumentProtection(service, authorizationService, args, userAddress, isGlobalAdmin) {
128
+ async function setDocumentProtection(service, authorizationService, args, userAddress) {
192
129
  if (!userAddress) throw new GraphQLError("Authentication required");
193
- if (!isGlobalAdmin) {
194
- if (authorizationService) {
195
- if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to change protection");
196
- } else if (!await service.canManageDocument(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to change protection");
197
- }
130
+ if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to change protection");
198
131
  await service.setDocumentProtection(args.documentId, args.protected);
199
132
  return service.getDocumentProtection(args.documentId);
200
133
  }
201
- async function transferDocumentOwnership(service, authorizationService, args, userAddress, isGlobalAdmin) {
134
+ async function transferDocumentOwnership(service, authorizationService, args, userAddress) {
202
135
  if (!userAddress) throw new GraphQLError("Authentication required");
203
- if (!isGlobalAdmin) {
204
- if (authorizationService) {
205
- if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to transfer ownership");
206
- } else if (!await service.canManageDocument(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to transfer ownership");
207
- }
136
+ if (!await authorizationService.canManage(args.documentId, userAddress)) throw new GraphQLError("Forbidden: You must be an admin of this document to transfer ownership");
208
137
  const previousOwner = await service.getDocumentOwner(args.documentId);
209
138
  if (previousOwner) await service.revokePermission(args.documentId, previousOwner);
210
139
  await service.setDocumentOwner(args.documentId, args.newOwnerAddress);
@@ -219,8 +148,7 @@ async function transferDocumentOwnership(service, authorizationService, args, us
219
148
  * This subgraph is conditionally registered based on the DOCUMENT_PERMISSIONS_ENABLED
220
149
  * feature flag. When enabled, it provides GraphQL operations for:
221
150
  * - Document permissions (grant/revoke user access)
222
- * - Group management (create/delete groups, manage membership)
223
- * - Group document permissions (grant/revoke group access)
151
+ * - Document protection and ownership
224
152
  * - Operation-level permissions (fine-grained operation control)
225
153
  */
226
154
  var AuthSubgraph = class extends BaseSubgraph {
@@ -234,11 +162,11 @@ var AuthSubgraph = class extends BaseSubgraph {
234
162
  typeDefs = gql(schema_default$2);
235
163
  resolvers = {
236
164
  Query: {
237
- documentAccess: async (_parent, args) => {
165
+ documentAccess: async (_parent, args, ctx) => {
238
166
  this.logger.debug("documentAccess(@args)", args);
239
167
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
240
168
  try {
241
- return await documentAccess(this.documentPermissionService, args);
169
+ return await documentAccess(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
242
170
  } catch (error) {
243
171
  this.logger.error("Error in documentAccess: @error", error);
244
172
  throw error;
@@ -255,41 +183,11 @@ var AuthSubgraph = class extends BaseSubgraph {
255
183
  throw error;
256
184
  }
257
185
  },
258
- groups: async () => {
259
- this.logger.debug("groups");
260
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
261
- try {
262
- return await groups(this.documentPermissionService);
263
- } catch (error) {
264
- this.logger.error("Error in groups: @error", error);
265
- throw error;
266
- }
267
- },
268
- group: async (_parent, args) => {
269
- this.logger.debug("group(@args)", args);
270
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
271
- try {
272
- return await group(this.documentPermissionService, args);
273
- } catch (error) {
274
- this.logger.error("Error in group: @error", error);
275
- throw error;
276
- }
277
- },
278
- userGroups: async (_parent, args) => {
279
- this.logger.debug("userGroups(@args)", args);
280
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
281
- try {
282
- return await userGroups(this.documentPermissionService, args);
283
- } catch (error) {
284
- this.logger.error("Error in userGroups: @error", error);
285
- throw error;
286
- }
287
- },
288
- operationPermissions: async (_parent, args) => {
186
+ operationPermissions: async (_parent, args, ctx) => {
289
187
  this.logger.debug("operationPermissions(@args)", args);
290
188
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
291
189
  try {
292
- return await operationPermissions(this.documentPermissionService, args);
190
+ return await operationPermissions(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
293
191
  } catch (error) {
294
192
  this.logger.error("Error in operationPermissions: @error", error);
295
193
  throw error;
@@ -297,9 +195,8 @@ var AuthSubgraph = class extends BaseSubgraph {
297
195
  },
298
196
  canExecuteOperation: async (_parent, args, ctx) => {
299
197
  this.logger.debug("canExecuteOperation(@args)", args);
300
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
301
198
  try {
302
- return await canExecuteOperation(this.documentPermissionService, args, ctx.user?.address);
199
+ return await canExecuteOperation(this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
303
200
  } catch (error) {
304
201
  this.logger.error("Error in canExecuteOperation: @error", error);
305
202
  throw error;
@@ -308,9 +205,8 @@ var AuthSubgraph = class extends BaseSubgraph {
308
205
  documentProtection: async (_parent, args, ctx) => {
309
206
  this.logger.debug("documentProtection(@args)", args);
310
207
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
311
- if (!ctx.user?.address) throw new GraphQLError("Authentication required to view document protection info");
312
208
  try {
313
- return await documentProtection(this.documentPermissionService, args);
209
+ return await documentProtection(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
314
210
  } catch (error) {
315
211
  this.logger.error("Error in documentProtection: @error", error);
316
212
  throw error;
@@ -322,8 +218,7 @@ var AuthSubgraph = class extends BaseSubgraph {
322
218
  this.logger.debug("setDocumentProtection(@args)", args);
323
219
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
324
220
  try {
325
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
326
- return await setDocumentProtection(this.documentPermissionService, this.authorizationService, args, ctx.user?.address, isGlobalAdmin);
221
+ return await setDocumentProtection(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
327
222
  } catch (error) {
328
223
  this.logger.error("Error in setDocumentProtection: @error", error);
329
224
  throw error;
@@ -333,8 +228,7 @@ var AuthSubgraph = class extends BaseSubgraph {
333
228
  this.logger.debug("transferDocumentOwnership(@args)", args);
334
229
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
335
230
  try {
336
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
337
- return await transferDocumentOwnership(this.documentPermissionService, this.authorizationService, args, ctx.user?.address, isGlobalAdmin);
231
+ return await transferDocumentOwnership(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
338
232
  } catch (error) {
339
233
  this.logger.error("Error in transferDocumentOwnership: @error", error);
340
234
  throw error;
@@ -344,8 +238,11 @@ var AuthSubgraph = class extends BaseSubgraph {
344
238
  this.logger.debug("grantDocumentPermission(@args)", args);
345
239
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
346
240
  try {
347
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
348
- return await grantDocumentPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
241
+ const resolved = await this.withCanonicalDocumentId(args, ctx);
242
+ return await grantDocumentPermission(this.documentPermissionService, this.authorizationService, {
243
+ ...resolved,
244
+ permission: resolved.permission
245
+ }, ctx.user?.address);
349
246
  } catch (error) {
350
247
  this.logger.error("Error in grantDocumentPermission: @error", error);
351
248
  throw error;
@@ -355,81 +252,17 @@ var AuthSubgraph = class extends BaseSubgraph {
355
252
  this.logger.debug("revokeDocumentPermission(@args)", args);
356
253
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
357
254
  try {
358
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
359
- return await revokeDocumentPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
255
+ return await revokeDocumentPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
360
256
  } catch (error) {
361
257
  this.logger.error("Error in revokeDocumentPermission: @error", error);
362
258
  throw error;
363
259
  }
364
260
  },
365
- createGroup: async (_parent, args) => {
366
- this.logger.debug("createGroup(@args)", args);
367
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
368
- try {
369
- return await createGroup(this.documentPermissionService, args);
370
- } catch (error) {
371
- this.logger.error("Error in createGroup: @error", error);
372
- throw error;
373
- }
374
- },
375
- deleteGroup: async (_parent, args) => {
376
- this.logger.debug("deleteGroup(@args)", args);
377
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
378
- try {
379
- return await deleteGroup(this.documentPermissionService, args);
380
- } catch (error) {
381
- this.logger.error("Error in deleteGroup: @error", error);
382
- throw error;
383
- }
384
- },
385
- addUserToGroup: async (_parent, args) => {
386
- this.logger.debug("addUserToGroup(@args)", args);
387
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
388
- try {
389
- return await addUserToGroup(this.documentPermissionService, args);
390
- } catch (error) {
391
- this.logger.error("Error in addUserToGroup: @error", error);
392
- throw error;
393
- }
394
- },
395
- removeUserFromGroup: async (_parent, args) => {
396
- this.logger.debug("removeUserFromGroup(@args)", args);
397
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
398
- try {
399
- return await removeUserFromGroup(this.documentPermissionService, args);
400
- } catch (error) {
401
- this.logger.error("Error in removeUserFromGroup: @error", error);
402
- throw error;
403
- }
404
- },
405
- grantGroupPermission: async (_parent, args, ctx) => {
406
- this.logger.debug("grantGroupPermission(@args)", args);
407
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
408
- try {
409
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
410
- return await grantGroupPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
411
- } catch (error) {
412
- this.logger.error("Error in grantGroupPermission: @error", error);
413
- throw error;
414
- }
415
- },
416
- revokeGroupPermission: async (_parent, args, ctx) => {
417
- this.logger.debug("revokeGroupPermission(@args)", args);
418
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
419
- try {
420
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
421
- return await revokeGroupPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
422
- } catch (error) {
423
- this.logger.error("Error in revokeGroupPermission: @error", error);
424
- throw error;
425
- }
426
- },
427
261
  grantOperationPermission: async (_parent, args, ctx) => {
428
262
  this.logger.debug("grantOperationPermission(@args)", args);
429
263
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
430
264
  try {
431
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
432
- return await grantOperationPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
265
+ return await grantOperationPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
433
266
  } catch (error) {
434
267
  this.logger.error("Error in grantOperationPermission: @error", error);
435
268
  throw error;
@@ -439,52 +272,13 @@ var AuthSubgraph = class extends BaseSubgraph {
439
272
  this.logger.debug("revokeOperationPermission(@args)", args);
440
273
  if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
441
274
  try {
442
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
443
- return await revokeOperationPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
275
+ return await revokeOperationPermission(this.documentPermissionService, this.authorizationService, await this.withCanonicalDocumentId(args, ctx), ctx.user?.address);
444
276
  } catch (error) {
445
277
  this.logger.error("Error in revokeOperationPermission: @error", error);
446
278
  throw error;
447
279
  }
448
- },
449
- grantGroupOperationPermission: async (_parent, args, ctx) => {
450
- this.logger.debug("grantGroupOperationPermission(@args)", args);
451
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
452
- try {
453
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
454
- return await grantGroupOperationPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
455
- } catch (error) {
456
- this.logger.error("Error in grantGroupOperationPermission: @error", error);
457
- throw error;
458
- }
459
- },
460
- revokeGroupOperationPermission: async (_parent, args, ctx) => {
461
- this.logger.debug("revokeGroupOperationPermission(@args)", args);
462
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
463
- try {
464
- const isGlobalAdmin = ctx.isAdmin?.(ctx.user?.address ?? "") ?? false;
465
- return await revokeGroupOperationPermission(this.documentPermissionService, args, ctx.user?.address, isGlobalAdmin);
466
- } catch (error) {
467
- this.logger.error("Error in revokeGroupOperationPermission: @error", error);
468
- throw error;
469
- }
470
280
  }
471
- },
472
- Group: { members: async (parent) => {
473
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
474
- return getGroupMembers(this.documentPermissionService, parent.id);
475
- } },
476
- DocumentGroupPermission: { group: async (parent) => {
477
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
478
- const grp = await group(this.documentPermissionService, { id: parent.groupId });
479
- if (!grp) throw new GraphQLError(`Group not found: ${parent.groupId}`);
480
- return grp;
481
- } },
482
- OperationGroupPermission: { group: async (parent) => {
483
- if (!this.documentPermissionService) throw new GraphQLError("DocumentPermissionService not available");
484
- const grp = await group(this.documentPermissionService, { id: parent.groupId });
485
- if (!grp) throw new GraphQLError(`Group not found: ${parent.groupId}`);
486
- return grp;
487
- } }
281
+ }
488
282
  };
489
283
  onSetup() {
490
284
  this.logger.debug("Setting up AuthSubgraph");
@@ -511,7 +305,7 @@ function createAuthFetchMiddleware(authService) {
511
305
  async function createGatewayAdapter(type, logger) {
512
306
  switch (type) {
513
307
  case "apollo": {
514
- const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-IZuGzFaY.mjs");
308
+ const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-CxV_hMTv.mjs");
515
309
  return new ApolloGatewayAdapter(logger);
516
310
  }
517
311
  case "mercurius": {
@@ -533,133 +327,6 @@ async function createHttpAdapter(type) {
533
327
  }
534
328
  }
535
329
  //#endregion
536
- //#region src/services/auth.service.ts
537
- var AuthService = class {
538
- config;
539
- constructor(config) {
540
- this.config = config;
541
- }
542
- async authenticateRequest(request) {
543
- if (!this.config.enabled) return {
544
- user: void 0,
545
- admins: [],
546
- auth_enabled: false
547
- };
548
- const method = request.method;
549
- if (method === "OPTIONS" || method === "GET") return {
550
- user: void 0,
551
- admins: this.config.admins,
552
- auth_enabled: true
553
- };
554
- return this.verifyBearer(request.headers.get("authorization") ?? void 0);
555
- }
556
- /**
557
- * Verify a Bearer token regardless of HTTP method. Use this from non-GraphQL
558
- * middleware that must enforce authentication on every request.
559
- */
560
- async verifyBearer(authorization) {
561
- if (!this.config.enabled) return {
562
- user: void 0,
563
- admins: [],
564
- auth_enabled: false
565
- };
566
- const token = authorization?.split(" ")[1];
567
- if (!token) return {
568
- user: void 0,
569
- admins: this.config.admins,
570
- auth_enabled: true
571
- };
572
- try {
573
- const verified = await this.verifyToken(token);
574
- if (!verified) return new Response(JSON.stringify({ error: "Verification failed" }), { status: 401 });
575
- const user = this.extractUserFromVerification(verified);
576
- if (!user) return new Response(JSON.stringify({ error: "Missing credentials" }), { status: 401 });
577
- if (!this.config.skipCredentialVerification) {
578
- if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) return new Response(JSON.stringify({ error: "Credentials no longer valid" }), { status: 401 });
579
- }
580
- return {
581
- user,
582
- admins: this.config.admins,
583
- auth_enabled: true
584
- };
585
- } catch {
586
- return new Response(JSON.stringify({ error: "Authentication failed" }), { status: 401 });
587
- }
588
- }
589
- async authenticateWebSocketConnection(connectionParams) {
590
- if (!this.config.enabled) return null;
591
- const authHeader = connectionParams.authorization;
592
- if (!authHeader) throw new Error("Missing authorization in connection parameters");
593
- const token = authHeader.split(" ")[1];
594
- if (!token) throw new Error("Invalid authorization format");
595
- const verified = await this.verifyToken(token);
596
- if (!verified) throw new Error("Token verification failed");
597
- const user = this.extractUserFromVerification(verified);
598
- if (!user) throw new Error("Invalid credentials");
599
- if (!this.config.skipCredentialVerification) {
600
- if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) throw new Error("Credentials no longer valid");
601
- }
602
- return user;
603
- }
604
- /**
605
- * Verify the auth bearer token
606
- */
607
- async verifyToken(token) {
608
- return await verifyAuthBearerToken(token);
609
- }
610
- /**
611
- * Extract user information from verification result
612
- */
613
- extractUserFromVerification(verified) {
614
- try {
615
- const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
616
- if (!address || !chainId || !networkId) return null;
617
- return {
618
- address,
619
- chainId,
620
- networkId
621
- };
622
- } catch {
623
- return null;
624
- }
625
- }
626
- /**
627
- * Get additional context fields for GraphQL
628
- */
629
- getAdditionalContextFields() {
630
- if (!this.config.enabled) return { isAdmin: () => true };
631
- return { isAdmin: (address) => this.config.enabled && this.config.admins?.includes(address.toLowerCase()) };
632
- }
633
- /**
634
- * Get user context for GraphQL
635
- */
636
- getUserContext(user) {
637
- if (!user) return {};
638
- return { user: {
639
- address: user.address.toLowerCase(),
640
- chainId: user.chainId,
641
- networkId: user.networkId
642
- } };
643
- }
644
- /**
645
- * Verify that the credential still exists on the Renown API
646
- */
647
- async verifyCredentialExists(address, chainId, appId) {
648
- const url = `https://www.renown.id/api/auth/credential?address=${address}&chainId=${chainId}&connectId=${appId}&appId=${appId}`;
649
- try {
650
- const response = await fetch(url, { method: "GET" });
651
- const credential = (await response.json()).credential;
652
- const appIdVerfied = credential.credentialSubject.id;
653
- const addressVerfied = credential.issuer.id.split(":")[4];
654
- const chainIdVerfied = credential.issuer.id.split(":")[3];
655
- if (response.status !== 200) return false;
656
- return appIdVerfied === appId && addressVerfied.toLocaleLowerCase() === address.toLocaleLowerCase() && chainIdVerfied === chainId.toString();
657
- } catch {
658
- return false;
659
- }
660
- }
661
- };
662
- //#endregion
663
330
  //#region src/utils/create-schema.ts
664
331
  const logger = childLogger(["reactor-api", "create-schema"]);
665
332
  /**
@@ -683,6 +350,49 @@ const stripScalarDefinitions = (doc) => {
683
350
  definitions: filteredDefinitions
684
351
  });
685
352
  };
353
+ /**
354
+ * Type-system definition kinds that GraphQL requires to be uniquely named.
355
+ * A subgraph SDL with two definitions sharing a name fails federation
356
+ * composition ("There can be only one type named X"), which is fatal to the
357
+ * whole gateway. We dedupe these by name; field/operation/extension nodes are
358
+ * left untouched.
359
+ */
360
+ const TYPE_DEFINITION_KINDS = new Set([
361
+ Kind.OBJECT_TYPE_DEFINITION,
362
+ Kind.ENUM_TYPE_DEFINITION,
363
+ Kind.INPUT_OBJECT_TYPE_DEFINITION,
364
+ Kind.INTERFACE_TYPE_DEFINITION,
365
+ Kind.UNION_TYPE_DEFINITION,
366
+ Kind.SCALAR_TYPE_DEFINITION
367
+ ]);
368
+ /**
369
+ * Drop duplicate type-system definitions by name, keeping the first occurrence.
370
+ * Deduping is by name across ALL kinds (a `type` and an `enum` sharing a name
371
+ * collide in GraphQL too), so the first definition of a given name wins
372
+ * regardless of kind. State types are emitted before operation types, so
373
+ * keep-first preserves the authoritative state definition. This heals a document
374
+ * model that defines the same name twice (global+local, state+operation, or
375
+ * twice in one scope) so the assembled subgraph SDL composes instead of crashing
376
+ * the gateway (Sentry #917).
377
+ */
378
+ const dedupeTypeDefinitions = (doc) => {
379
+ const seen = /* @__PURE__ */ new Set();
380
+ const definitions = doc.definitions.filter((def) => {
381
+ if (!TYPE_DEFINITION_KINDS.has(def.kind)) return true;
382
+ const name = def.name?.value;
383
+ if (!name) return true;
384
+ if (seen.has(name)) {
385
+ logger.debug(`Dropping duplicate type definition: ${name}`);
386
+ return false;
387
+ }
388
+ seen.add(name);
389
+ return true;
390
+ });
391
+ return {
392
+ kind: Kind.DOCUMENT,
393
+ definitions
394
+ };
395
+ };
686
396
  const buildSubgraphSchemaModule = (documentModels, resolvers, typeDefs) => {
687
397
  const newResolvers = {
688
398
  ...resolvers,
@@ -748,7 +458,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
748
458
  stateJSON: JSONObject
749
459
  }\n`;
750
460
  });
751
- return gql`
461
+ return dedupeTypeDefinitions(gql`
752
462
  scalar JSONObject
753
463
  ${typeDefs.join("\n").replaceAll(";", "")}
754
464
 
@@ -820,7 +530,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
820
530
  }
821
531
 
822
532
  ${stripScalarDefinitions(typeDefs$1)}
823
- `;
533
+ `);
824
534
  };
825
535
  /**
826
536
  * Extract type names from a GraphQL schema.
@@ -2122,6 +1832,17 @@ function toGqlDocumentChangeEvent(event) {
2122
1832
  } : null
2123
1833
  };
2124
1834
  }
1835
+ /**
1836
+ * Distinct document ids a change event references: affected documents plus the
1837
+ * parent/child ids in `context` (delete/relationship events carry only context).
1838
+ */
1839
+ function collectChangedDocumentIds(event) {
1840
+ const ids = /* @__PURE__ */ new Set();
1841
+ for (const doc of event.documents) if (doc.header.id) ids.add(doc.header.id);
1842
+ if (event.context?.parentId) ids.add(event.context.parentId);
1843
+ if (event.context?.childId) ids.add(event.context.childId);
1844
+ return [...ids];
1845
+ }
2125
1846
  function matchesSearchFilter(event, search) {
2126
1847
  if (search.type) {
2127
1848
  if (!event.documents.some((doc) => doc.header.documentType === search.type)) return false;
@@ -2593,7 +2314,7 @@ async function touchChannel(syncManager, args) {
2593
2314
  };
2594
2315
  const options = { sinceTimestampUtcMs: args.input.sinceTimestampUtcMs };
2595
2316
  try {
2596
- await syncManager.add(args.input.name, args.input.collectionId, {
2317
+ await syncManager.add(args.input.name, DriveCollectionId.fromKey(args.input.collectionId), {
2597
2318
  type: "polling",
2598
2319
  parameters: {}
2599
2320
  }, filter, options, args.input.id);
@@ -2616,14 +2337,14 @@ async function touchChannel(syncManager, args) {
2616
2337
  * client ordinal the switchboard has successfully applied, so the client
2617
2338
  * can trim its own outbox)
2618
2339
  */
2619
- function pollSyncEnvelopes(syncManager, args) {
2340
+ function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new Set()) {
2620
2341
  let remote;
2621
2342
  try {
2622
2343
  remote = syncManager.getById(args.channelId);
2623
2344
  } catch (error) {
2624
2345
  throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
2625
2346
  }
2626
- const deadLetters = remote.channel.deadLetter.items.map((syncOp) => ({
2347
+ const deadLetters = remote.channel.deadLetter.items.filter((syncOp) => !forbiddenIds.has(syncOp.documentId)).map((syncOp) => ({
2627
2348
  documentId: syncOp.documentId,
2628
2349
  error: syncOp.error?.message ?? "Unknown error",
2629
2350
  jobId: syncOp.jobId,
@@ -2647,6 +2368,12 @@ function pollSyncEnvelopes(syncManager, args) {
2647
2368
  }
2648
2369
  }
2649
2370
  const operations = remote.channel.outbox.items;
2371
+ if (forbiddenIds.size > 0) {
2372
+ for (const syncOp of operations) if (forbiddenIds.has(syncOp.documentId)) {
2373
+ syncOp.deliveredCount = syncOp.operations.length;
2374
+ syncOp.emittedCount = syncOp.operations.length;
2375
+ }
2376
+ }
2650
2377
  if (operations.length === 0) return {
2651
2378
  envelopes: [],
2652
2379
  ackOrdinal: remote.channel.inbox.ackOrdinal,
@@ -2694,7 +2421,7 @@ function pollSyncEnvelopes(syncManager, args) {
2694
2421
  context: op.context
2695
2422
  })),
2696
2423
  cursor: {
2697
- remoteName: remote.name,
2424
+ remoteName: remote.meta.name,
2698
2425
  cursorOrdinal: 0,
2699
2426
  lastSyncedAtUtcMs: Date.now().toString()
2700
2427
  },
@@ -2739,7 +2466,7 @@ function pushSyncEnvelopes(syncManager, args) {
2739
2466
  throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
2740
2467
  }
2741
2468
  if (!envelope.operations || envelope.operations.length === 0) continue;
2742
- const syncOps = envelopesToSyncOperations(envelope, remote.name);
2469
+ const syncOps = envelopesToSyncOperations(envelope, remote.meta.name);
2743
2470
  if (!remoteSyncOps.has(remote)) remoteSyncOps.set(remote, []);
2744
2471
  remoteSyncOps.get(remote).push(...syncOps);
2745
2472
  }
@@ -2838,7 +2565,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2838
2565
  view
2839
2566
  });
2840
2567
  if (result.document.documentType !== documentType) throw new GraphQLError(`Document with id ${identifier} is not of type ${documentType}`);
2841
- await this.assertCanRead(result.document.id, ctx);
2568
+ await this.assertCanReadCanonical(result.document.id, ctx);
2842
2569
  return result;
2843
2570
  },
2844
2571
  documents: async (_, args, ctx) => {
@@ -2847,7 +2574,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2847
2574
  search: { type: documentType },
2848
2575
  paging
2849
2576
  });
2850
- if (!this.hasGlobalAdminAccess(ctx) && this.documentPermissionService) {
2577
+ if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
2851
2578
  const filteredItems = [];
2852
2579
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
2853
2580
  return {
@@ -2868,7 +2595,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2868
2595
  view,
2869
2596
  paging
2870
2597
  });
2871
- if (!this.hasGlobalAdminAccess(ctx) && this.documentPermissionService) {
2598
+ if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
2872
2599
  const filteredItems = [];
2873
2600
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
2874
2601
  return {
@@ -2880,10 +2607,10 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2880
2607
  return result;
2881
2608
  },
2882
2609
  documentOutgoingRelationships: async (_, args, ctx) => {
2883
- const { sourceIdentifier, relationshipType, view, paging } = args;
2884
- await this.assertCanRead(sourceIdentifier, ctx);
2610
+ const { relationshipType, view, paging } = args;
2611
+ const handle = await this.assertCanRead(args.sourceIdentifier, ctx);
2885
2612
  const result = await documentOutgoingRelationships(this.reactorClient, {
2886
- sourceIdentifier,
2613
+ sourceIdentifier: handle.fetchIdentifier,
2887
2614
  relationshipType,
2888
2615
  view,
2889
2616
  paging
@@ -2896,10 +2623,10 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2896
2623
  };
2897
2624
  },
2898
2625
  documentIncomingRelationships: async (_, args, ctx) => {
2899
- const { targetIdentifier, relationshipType, view, paging } = args;
2900
- await this.assertCanRead(targetIdentifier, ctx);
2626
+ const { relationshipType, view, paging } = args;
2627
+ const handle = await this.assertCanRead(args.targetIdentifier, ctx);
2901
2628
  return documentIncomingRelationships(this.reactorClient, {
2902
- targetIdentifier,
2629
+ targetIdentifier: handle.fetchIdentifier,
2903
2630
  relationshipType,
2904
2631
  view,
2905
2632
  paging
@@ -2909,11 +2636,10 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2909
2636
  Mutation: { [documentName]: () => ({}) },
2910
2637
  [`${documentName}Mutations`]: {
2911
2638
  createDocument: async (_, args, ctx) => {
2912
- const { parentIdentifier, name, slug, preferredEditor, initialState } = args;
2913
- if (parentIdentifier) await this.assertCanWrite(parentIdentifier, ctx);
2914
- else if (this.authorizationService) {
2915
- if (!ctx.user?.address) throw new GraphQLError("Forbidden: authentication required to create documents");
2916
- } else if (!this.hasGlobalAdminAccess(ctx)) throw new GraphQLError("Forbidden: insufficient permissions to create documents");
2639
+ const { name, slug, preferredEditor, initialState } = args;
2640
+ let parentIdentifier = args.parentIdentifier;
2641
+ if (parentIdentifier) parentIdentifier = (await this.assertCanWrite(parentIdentifier, ctx)).fetchIdentifier;
2642
+ else this.assertCanCreate(ctx);
2917
2643
  let createdDoc;
2918
2644
  if (initialState || preferredEditor) createdDoc = await createDocumentWithInitialState(this.reactorClient, {
2919
2645
  documentType,
@@ -2928,46 +2654,42 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
2928
2654
  parentIdentifier,
2929
2655
  name
2930
2656
  }, this.graphqlManager.reactorDriveClient);
2931
- if (this.authorizationService && ctx.user?.address && createdDoc?.id) await this.documentPermissionService?.initializeDocumentProtection(createdDoc.id, ctx.user.address, this.authorizationService.config.defaultProtection);
2657
+ if (ctx.user?.address && createdDoc?.id) await this.documentPermissionService?.initializeDocumentProtection(createdDoc.id, ctx.user.address, this.authorizationService.config.defaultProtection);
2932
2658
  if (!initialState && !preferredEditor && name && createdDoc.name !== name) return toGqlPhDocument(await this.reactorClient.execute(createdDoc.id, "main", [setName(name)]));
2933
2659
  return createdDoc;
2934
2660
  },
2935
2661
  createEmptyDocument: async (_, args, ctx) => {
2936
- const { parentIdentifier } = args;
2937
- if (parentIdentifier) await this.assertCanWrite(parentIdentifier, ctx);
2938
- else if (this.authorizationService) {
2939
- if (!ctx.user?.address) throw new GraphQLError("Forbidden: authentication required to create documents");
2940
- } else if (!this.hasGlobalAdminAccess(ctx)) throw new GraphQLError("Forbidden: insufficient permissions to create documents");
2662
+ let parentIdentifier = args.parentIdentifier;
2663
+ if (parentIdentifier) parentIdentifier = (await this.assertCanWrite(parentIdentifier, ctx)).fetchIdentifier;
2664
+ else this.assertCanCreate(ctx);
2941
2665
  const result = await createEmptyDocument(this.reactorClient, {
2942
2666
  documentType,
2943
2667
  parentIdentifier
2944
2668
  }, this.graphqlManager.reactorDriveClient);
2945
- if (this.authorizationService && ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
2669
+ if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
2946
2670
  return result;
2947
2671
  },
2948
2672
  ...operations.reduce((mutations, op) => {
2949
2673
  mutations[camelCase(op.name)] = async (_, args, ctx) => {
2950
2674
  const { docId, input } = args;
2951
- if (!this.authorizationService) await this.assertCanWrite(docId, ctx);
2952
- await this.assertCanExecuteOperation(docId, op.name, ctx);
2953
- if ((await this.reactorClient.get(docId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
2675
+ const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
2676
+ if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
2954
2677
  const action = this.documentModel.actions[camelCase(op.name)];
2955
2678
  if (!action) throw new GraphQLError(`Action ${op.name} not found`);
2956
2679
  try {
2957
- return toGqlPhDocument(await this.reactorClient.execute(docId, "main", [action(input)]));
2680
+ return toGqlPhDocument(await this.reactorClient.execute(effectiveDocId, "main", [action(input)]));
2958
2681
  } catch (error) {
2959
2682
  throw new GraphQLError(error instanceof Error ? error.message : `Failed to ${op.name}`);
2960
2683
  }
2961
2684
  };
2962
2685
  mutations[`${camelCase(op.name)}Async`] = async (_, args, ctx) => {
2963
2686
  const { docId, input } = args;
2964
- if (!this.authorizationService) await this.assertCanWrite(docId, ctx);
2965
- await this.assertCanExecuteOperation(docId, op.name, ctx);
2966
- if ((await this.reactorClient.get(docId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
2687
+ const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
2688
+ if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
2967
2689
  const action = this.documentModel.actions[camelCase(op.name)];
2968
2690
  if (!action) throw new GraphQLError(`Action ${op.name} not found`);
2969
2691
  try {
2970
- return (await this.reactorClient.executeAsync(docId, "main", [action(input)])).id;
2692
+ return (await this.reactorClient.executeAsync(effectiveDocId, "main", [action(input)])).id;
2971
2693
  } catch (error) {
2972
2694
  throw new GraphQLError(error instanceof Error ? error.message : `Failed to ${op.name}`);
2973
2695
  }
@@ -3146,7 +2868,6 @@ var GraphQLManager = class {
3146
2868
  coreSubgraphsMap = /* @__PURE__ */ new Map();
3147
2869
  contextFields = {};
3148
2870
  subgraphs = /* @__PURE__ */ new Map();
3149
- authService = null;
3150
2871
  subgraphWsDisposers = /* @__PURE__ */ new Map();
3151
2872
  #authMiddleware;
3152
2873
  #driveMiddleware;
@@ -3160,7 +2881,8 @@ var GraphQLManager = class {
3160
2881
  * it for reactor-drive parents.
3161
2882
  */
3162
2883
  reactorDriveClient;
3163
- constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authConfig, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService, reactorDriveClient) {
2884
+ authorizationService;
2885
+ constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authService, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService, reactorDriveClient) {
3164
2886
  this.path = path;
3165
2887
  this.httpServer = httpServer;
3166
2888
  this.wsServer = wsServer;
@@ -3171,13 +2893,13 @@ var GraphQLManager = class {
3171
2893
  this.logger = logger;
3172
2894
  this.httpAdapter = httpAdapter;
3173
2895
  this.gatewayAdapter = gatewayAdapter;
3174
- this.authConfig = authConfig;
2896
+ this.authService = authService;
3175
2897
  this.documentPermissionService = documentPermissionService;
3176
2898
  this.featureFlags = featureFlags;
3177
2899
  this.port = port;
2900
+ if (!authorizationService) throw new Error("GraphQLManager requires an authorizationService");
3178
2901
  this.authorizationService = authorizationService;
3179
2902
  this.reactorDriveClient = reactorDriveClient;
3180
- if (this.authConfig) this.authService = new AuthService(this.authConfig);
3181
2903
  this.driveOwnershipCache = new DriveOwnershipCache(this.reactorClient);
3182
2904
  this.wsServer.setMaxListeners(0);
3183
2905
  }
@@ -3297,6 +3019,14 @@ var GraphQLManager = class {
3297
3019
  getBasePath() {
3298
3020
  return this.path;
3299
3021
  }
3022
+ /**
3023
+ * Get the authorization service shared with subgraphs. Use this when
3024
+ * constructing a subgraph instance externally for
3025
+ * {@link registerSubgraphInstance}.
3026
+ */
3027
+ getAuthorizationService() {
3028
+ return this.authorizationService;
3029
+ }
3300
3030
  async registerSubgraph(subgraph, supergraph = "", core = false) {
3301
3031
  const subgraphInstance = new subgraph({
3302
3032
  relationalDb: this.relationalDb,
@@ -3356,8 +3086,7 @@ var GraphQLManager = class {
3356
3086
  db: this.relationalDb,
3357
3087
  ...this.getAdditionalContextFields(),
3358
3088
  driveId,
3359
- user: authCtx?.user,
3360
- isAdmin: authCtx ? (addr) => !authCtx.auth_enabled ? true : authCtx.admins.includes(addr.toLowerCase()) : () => true
3089
+ user: authCtx?.user
3361
3090
  });
3362
3091
  };
3363
3092
  }
@@ -3501,8 +3230,8 @@ var GraphQLManager = class {
3501
3230
  };
3502
3231
  //#endregion
3503
3232
  //#region src/graphql/packages/resolvers.ts
3504
- function requireAdmin(ctx) {
3505
- if (!(ctx.isAdmin?.(ctx.user?.address ?? "") ?? false)) throw new GraphQLError("Admin access required");
3233
+ function requireAdmin(authorizationService, ctx) {
3234
+ if (!authorizationService.isSupremeAdmin(ctx.user?.address)) throw new GraphQLError("Admin access required");
3506
3235
  }
3507
3236
  function formatPackageInfo(info) {
3508
3237
  return {
@@ -3520,16 +3249,16 @@ async function installedPackage(service, args) {
3520
3249
  const pkg = await service.getInstalledPackage(args.name);
3521
3250
  return pkg ? formatPackageInfo(pkg) : null;
3522
3251
  }
3523
- async function installPackage(service, args, ctx) {
3524
- requireAdmin(ctx);
3252
+ async function installPackage(service, authorizationService, args, ctx) {
3253
+ requireAdmin(authorizationService, ctx);
3525
3254
  const result = await service.installPackage(args.name, args.registryUrl ?? void 0);
3526
3255
  return {
3527
3256
  package: formatPackageInfo(result.package),
3528
3257
  documentModelsLoaded: result.documentModelsLoaded
3529
3258
  };
3530
3259
  }
3531
- async function uninstallPackage(service, args, ctx) {
3532
- requireAdmin(ctx);
3260
+ async function uninstallPackage(service, authorizationService, args, ctx) {
3261
+ requireAdmin(authorizationService, ctx);
3533
3262
  return service.uninstallPackage(args.name);
3534
3263
  }
3535
3264
  //#endregion
@@ -3575,7 +3304,7 @@ var PackagesSubgraph = class extends BaseSubgraph {
3575
3304
  installPackage: async (_parent, args, ctx) => {
3576
3305
  this.logger.debug("installPackage(@args)", args);
3577
3306
  try {
3578
- return await installPackage(this.packageManagementService, args, ctx);
3307
+ return await installPackage(this.packageManagementService, this.authorizationService, args, ctx);
3579
3308
  } catch (error) {
3580
3309
  this.logger.error("Error in installPackage: @error", error);
3581
3310
  throw error;
@@ -3584,7 +3313,7 @@ var PackagesSubgraph = class extends BaseSubgraph {
3584
3313
  uninstallPackage: async (_parent, args, ctx) => {
3585
3314
  this.logger.debug("uninstallPackage(@args)", args);
3586
3315
  try {
3587
- return await uninstallPackage(this.packageManagementService, args, ctx);
3316
+ return await uninstallPackage(this.packageManagementService, this.authorizationService, args, ctx);
3588
3317
  } catch (error) {
3589
3318
  this.logger.error("Error in uninstallPackage: @error", error);
3590
3319
  throw error;
@@ -3951,7 +3680,8 @@ function ensureJobSubscription(reactorClient, jobId) {
3951
3680
  error: jobInfo.error?.message ?? null,
3952
3681
  result: jobInfo.result ?? {}
3953
3682
  },
3954
- jobId
3683
+ jobId,
3684
+ documentId: jobInfo.documentId
3955
3685
  };
3956
3686
  pubSub.publish(SUBSCRIPTION_TRIGGERS.JOB_CHANGES, payload);
3957
3687
  const isTerminal = String(jobInfo.status) === "FAILED" || String(jobInfo.status) === "READ_MODELS_READY" || jobInfo.completedAtUtcIso !== void 0;
@@ -4001,12 +3731,14 @@ var ReactorSubgraph = class extends BaseSubgraph {
4001
3731
  * Delegates to base assertCanExecuteOperation for each action.
4002
3732
  */
4003
3733
  async assertCanExecuteOperations(documentId, actions, ctx) {
3734
+ let handle = AuthorizedDocumentHandle.skipped(documentId);
4004
3735
  for (const action of actions) {
4005
3736
  if (!action || typeof action !== "object") continue;
4006
3737
  const operationType = action.type;
4007
3738
  if (typeof operationType !== "string") continue;
4008
- await this.assertCanExecuteOperation(documentId, operationType, ctx);
3739
+ handle = await this.assertCanExecuteOperation(documentId, operationType, ctx);
4009
3740
  }
3741
+ return handle;
4010
3742
  }
4011
3743
  /**
4012
3744
  * Returns the drive id when the given identifier (id or slug) refers
@@ -4023,11 +3755,25 @@ var ReactorSubgraph = class extends BaseSubgraph {
4023
3755
  return;
4024
3756
  }
4025
3757
  }
3758
+ /**
3759
+ * Adds to `forbidden` the canonical document ids in `syncOps` that the caller
3760
+ * cannot read, checking each distinct id once. Sync operation document ids are
3761
+ * canonical (never slugs), so no resolution is needed.
3762
+ */
3763
+ async #collectForbiddenDocuments(syncOps, forbidden, ctx) {
3764
+ const checked = /* @__PURE__ */ new Set();
3765
+ for (const syncOp of syncOps) {
3766
+ const documentId = syncOp.documentId;
3767
+ if (checked.has(documentId) || forbidden.has(documentId)) continue;
3768
+ checked.add(documentId);
3769
+ if (!await this.canReadDocument(documentId, ctx)) forbidden.add(documentId);
3770
+ }
3771
+ }
4026
3772
  typeDefs = gql(schema_default);
4027
3773
  resolvers = {
4028
3774
  PHDocument: { operations: async (parent, args, ctx) => {
4029
3775
  this.logger.debug("PHDocument.operations(@parent.id, @args)", parent.id, args);
4030
- await this.assertCanRead(parent.id, ctx);
3776
+ await this.assertCanReadCanonical(parent.id, ctx);
4031
3777
  try {
4032
3778
  const filter = {
4033
3779
  documentId: parent.id,
@@ -4060,8 +3806,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4060
3806
  document: async (_parent, args, ctx) => {
4061
3807
  this.logger.debug("document(@args)", args);
4062
3808
  try {
4063
- await this.assertCanRead(args.identifier, ctx);
4064
- return await document(this.reactorClient, args);
3809
+ const handle = await this.assertCanRead(args.identifier, ctx);
3810
+ return await document(this.reactorClient, {
3811
+ ...args,
3812
+ identifier: handle.fetchIdentifier
3813
+ });
4065
3814
  } catch (error) {
4066
3815
  this.logger.error("Error in document: @Error", error);
4067
3816
  throw error;
@@ -4070,8 +3819,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4070
3819
  documentOutgoingRelationships: async (_parent, args, ctx) => {
4071
3820
  this.logger.debug("documentOutgoingRelationships(@args)", args);
4072
3821
  try {
4073
- await this.assertCanRead(args.sourceIdentifier, ctx);
4074
- return await documentOutgoingRelationships(this.reactorClient, args);
3822
+ const handle = await this.assertCanRead(args.sourceIdentifier, ctx);
3823
+ return await documentOutgoingRelationships(this.reactorClient, {
3824
+ ...args,
3825
+ sourceIdentifier: handle.fetchIdentifier
3826
+ });
4075
3827
  } catch (error) {
4076
3828
  this.logger.error("Error in documentOutgoingRelationships: @Error", error);
4077
3829
  throw error;
@@ -4080,9 +3832,12 @@ var ReactorSubgraph = class extends BaseSubgraph {
4080
3832
  documentIncomingRelationships: async (_parent, args, ctx) => {
4081
3833
  this.logger.debug("documentIncomingRelationships(@args)", args);
4082
3834
  try {
4083
- await this.assertCanRead(args.targetIdentifier, ctx);
4084
- const result = await documentIncomingRelationships(this.reactorClient, args);
4085
- if (!this.hasGlobalAdminAccess(ctx) && this.documentPermissionService) {
3835
+ const handle = await this.assertCanRead(args.targetIdentifier, ctx);
3836
+ const result = await documentIncomingRelationships(this.reactorClient, {
3837
+ ...args,
3838
+ targetIdentifier: handle.fetchIdentifier
3839
+ });
3840
+ if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
4086
3841
  const filteredItems = [];
4087
3842
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
4088
3843
  return {
@@ -4103,7 +3858,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
4103
3858
  ...args,
4104
3859
  search: args.search ?? {}
4105
3860
  });
4106
- if (!this.hasGlobalAdminAccess(ctx) && this.documentPermissionService) {
3861
+ if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
4107
3862
  const filteredItems = [];
4108
3863
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
4109
3864
  return {
@@ -4129,18 +3884,38 @@ var ReactorSubgraph = class extends BaseSubgraph {
4129
3884
  documentOperations: async (_parent, args, ctx) => {
4130
3885
  this.logger.debug("documentOperations(@args)", args);
4131
3886
  try {
4132
- await this.assertCanRead(args.filter.documentId, ctx);
4133
- return await documentOperations(this.reactorClient, args);
3887
+ const handle = await this.assertCanRead(args.filter.documentId, ctx);
3888
+ return await documentOperations(this.reactorClient, {
3889
+ ...args,
3890
+ filter: {
3891
+ ...args.filter,
3892
+ documentId: handle.fetchIdentifier
3893
+ }
3894
+ });
4134
3895
  } catch (error) {
4135
3896
  this.logger.error("Error in documentOperations: @Error", error);
4136
3897
  throw error;
4137
3898
  }
4138
3899
  },
4139
- pollSyncEnvelopes: (_parent, args) => {
3900
+ pollSyncEnvelopes: async (_parent, args, ctx) => {
4140
3901
  this.logger.debug("pollSyncEnvelopes(@args)", args);
4141
3902
  try {
4142
- const { envelopes, ackOrdinal, deadLetters, hasMore } = pollSyncEnvelopes(this.syncManager, args);
4143
- return {
3903
+ let remote;
3904
+ try {
3905
+ remote = this.syncManager.getById(args.channelId);
3906
+ } catch (error) {
3907
+ throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
3908
+ }
3909
+ const driveId = remote.meta.collectionId.driveId;
3910
+ const isAdmin = this.authorizationService.isSupremeAdmin(ctx.user?.address);
3911
+ if (!isAdmin) await this.assertCanReadCanonical(driveId, ctx);
3912
+ const forbiddenIds = /* @__PURE__ */ new Set();
3913
+ if (!isAdmin) {
3914
+ if (await this.authorizationService.canRead(driveId, void 0)) await this.#collectForbiddenDocuments(remote.channel.outbox.items, forbiddenIds, ctx);
3915
+ await this.#collectForbiddenDocuments(remote.channel.deadLetter.items, forbiddenIds, ctx);
3916
+ }
3917
+ const { envelopes, ackOrdinal, deadLetters, hasMore } = pollSyncEnvelopes(this.syncManager, args, forbiddenIds);
3918
+ return {
4144
3919
  envelopes,
4145
3920
  ackOrdinal,
4146
3921
  deadLetters,
@@ -4158,13 +3933,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4158
3933
  try {
4159
3934
  if (args.parentIdentifier) {
4160
3935
  const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
4161
- await this.assertCanWrite(parent.document.id, ctx);
4162
- } else if (this.authorizationService) {
4163
- if (!ctx.user?.address) throw new GraphQLError("Forbidden: authentication required to create documents");
4164
- } else if (!this.hasGlobalAdminAccess(ctx)) throw new GraphQLError("Forbidden: insufficient permissions to create documents");
3936
+ await this.assertCanWriteCanonical(parent.document.id, ctx);
3937
+ } else this.assertCanCreate(ctx);
4165
3938
  const result = await createDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
4166
3939
  if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
4167
- if (this.authorizationService && ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
3940
+ if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
4168
3941
  return result;
4169
3942
  } catch (error) {
4170
3943
  this.logger.error("Error in createDocument(@args): @Error", args, error);
@@ -4176,13 +3949,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4176
3949
  try {
4177
3950
  if (args.parentIdentifier) {
4178
3951
  const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
4179
- await this.assertCanWrite(parent.document.id, ctx);
4180
- } else if (this.authorizationService) {
4181
- if (!ctx.user?.address) throw new GraphQLError("Forbidden: authentication required to create documents");
4182
- } else if (!this.hasGlobalAdminAccess(ctx)) throw new GraphQLError("Forbidden: insufficient permissions to create documents");
3952
+ await this.assertCanWriteCanonical(parent.document.id, ctx);
3953
+ } else this.assertCanCreate(ctx);
4183
3954
  const result = await createEmptyDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
4184
3955
  if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
4185
- if (this.authorizationService && ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
3956
+ if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
4186
3957
  return result;
4187
3958
  } catch (error) {
4188
3959
  this.logger.error("Error in createEmptyDocument(@args): @Error", args, error);
@@ -4192,9 +3963,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4192
3963
  mutateDocument: async (_parent, args, ctx) => {
4193
3964
  this.logger.debug("mutateDocument(@args)", args);
4194
3965
  try {
4195
- if (!this.authorizationService) await this.assertCanWrite(args.documentIdentifier, ctx);
4196
- await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
4197
- return await mutateDocument(this.reactorClient, args);
3966
+ const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
3967
+ return await mutateDocument(this.reactorClient, {
3968
+ ...args,
3969
+ documentIdentifier: handle.fetchIdentifier
3970
+ });
4198
3971
  } catch (error) {
4199
3972
  this.logger.error("Error in mutateDocument(@args): @Error", args, error);
4200
3973
  throw error;
@@ -4203,9 +3976,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4203
3976
  mutateDocumentAsync: async (_parent, args, ctx) => {
4204
3977
  this.logger.debug("mutateDocumentAsync(@args)", args);
4205
3978
  try {
4206
- if (!this.authorizationService) await this.assertCanWrite(args.documentIdentifier, ctx);
4207
- await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
4208
- return await mutateDocumentAsync(this.reactorClient, args);
3979
+ const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
3980
+ return await mutateDocumentAsync(this.reactorClient, {
3981
+ ...args,
3982
+ documentIdentifier: handle.fetchIdentifier
3983
+ });
4209
3984
  } catch (error) {
4210
3985
  this.logger.error("Error in mutateDocumentAsync(@args): @Error", args, error);
4211
3986
  throw error;
@@ -4214,8 +3989,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4214
3989
  renameDocument: async (_parent, args, ctx) => {
4215
3990
  this.logger.debug("renameDocument(@args)", args);
4216
3991
  try {
4217
- await this.assertCanWrite(args.documentIdentifier, ctx);
4218
- return await renameDocument(this.reactorClient, args);
3992
+ const handle = await this.assertCanWrite(args.documentIdentifier, ctx);
3993
+ return await renameDocument(this.reactorClient, {
3994
+ ...args,
3995
+ documentIdentifier: handle.fetchIdentifier
3996
+ });
4219
3997
  } catch (error) {
4220
3998
  this.logger.error("Error in renameDocument(@args): @Error", args, error);
4221
3999
  throw error;
@@ -4224,8 +4002,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4224
4002
  setPreferredEditor: async (_parent, args, ctx) => {
4225
4003
  this.logger.debug("setPreferredEditor(@args)", args);
4226
4004
  try {
4227
- await this.assertCanWrite(args.documentIdentifier, ctx);
4228
- return await setPreferredEditor(this.reactorClient, args);
4005
+ const handle = await this.assertCanWrite(args.documentIdentifier, ctx);
4006
+ return await setPreferredEditor(this.reactorClient, {
4007
+ ...args,
4008
+ documentIdentifier: handle.fetchIdentifier
4009
+ });
4229
4010
  } catch (error) {
4230
4011
  this.logger.error("Error in setPreferredEditor(@args): @Error", args, error);
4231
4012
  throw error;
@@ -4234,8 +4015,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4234
4015
  addRelationship: async (_parent, args, ctx) => {
4235
4016
  this.logger.debug("addRelationship(@args)", args);
4236
4017
  try {
4237
- await this.assertCanWrite(args.sourceIdentifier, ctx);
4238
- return await addRelationship(this.reactorClient, args);
4018
+ const handle = await this.assertCanWrite(args.sourceIdentifier, ctx);
4019
+ return await addRelationship(this.reactorClient, {
4020
+ ...args,
4021
+ sourceIdentifier: handle.fetchIdentifier
4022
+ });
4239
4023
  } catch (error) {
4240
4024
  this.logger.error("Error in addRelationship(@args): @Error", args, error);
4241
4025
  throw error;
@@ -4244,8 +4028,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
4244
4028
  removeRelationship: async (_parent, args, ctx) => {
4245
4029
  this.logger.debug("removeRelationship(@args)", args);
4246
4030
  try {
4247
- await this.assertCanWrite(args.sourceIdentifier, ctx);
4248
- return await removeRelationship(this.reactorClient, args);
4031
+ const handle = await this.assertCanWrite(args.sourceIdentifier, ctx);
4032
+ return await removeRelationship(this.reactorClient, {
4033
+ ...args,
4034
+ sourceIdentifier: handle.fetchIdentifier
4035
+ });
4249
4036
  } catch (error) {
4250
4037
  this.logger.error("Error in removeRelationship(@args): @Error", args, error);
4251
4038
  throw error;
@@ -4254,9 +4041,13 @@ var ReactorSubgraph = class extends BaseSubgraph {
4254
4041
  moveRelationship: async (_parent, args, ctx) => {
4255
4042
  this.logger.debug("moveRelationship(@args)", args);
4256
4043
  try {
4257
- await this.assertCanWrite(args.sourceParentIdentifier, ctx);
4258
- await this.assertCanWrite(args.targetParentIdentifier, ctx);
4259
- return await moveRelationship(this.reactorClient, args);
4044
+ const sourceHandle = await this.assertCanWrite(args.sourceParentIdentifier, ctx);
4045
+ const targetHandle = await this.assertCanWrite(args.targetParentIdentifier, ctx);
4046
+ return await moveRelationship(this.reactorClient, {
4047
+ ...args,
4048
+ sourceParentIdentifier: sourceHandle.fetchIdentifier,
4049
+ targetParentIdentifier: targetHandle.fetchIdentifier
4050
+ });
4260
4051
  } catch (error) {
4261
4052
  this.logger.error("Error in moveRelationship(@args): @Error @args", error, args);
4262
4053
  throw error;
@@ -4265,9 +4056,12 @@ var ReactorSubgraph = class extends BaseSubgraph {
4265
4056
  deleteDocument: async (_parent, args, ctx) => {
4266
4057
  this.logger.debug("deleteDocument(@args)", args);
4267
4058
  try {
4268
- await this.assertCanWrite(args.identifier, ctx);
4269
- const driveIdToInvalidate = await this.#resolveDriveId(args.identifier);
4270
- const result = await deleteDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
4059
+ const identifier = (await this.assertCanWrite(args.identifier, ctx)).fetchIdentifier;
4060
+ const driveIdToInvalidate = await this.#resolveDriveId(identifier);
4061
+ const result = await deleteDocument(this.reactorClient, {
4062
+ ...args,
4063
+ identifier
4064
+ }, this.graphqlManager.reactorDriveClient);
4271
4065
  if (result && driveIdToInvalidate) this.graphqlManager.driveOwnershipCache.remove(driveIdToInvalidate);
4272
4066
  return result;
4273
4067
  } catch (error) {
@@ -4278,25 +4072,49 @@ var ReactorSubgraph = class extends BaseSubgraph {
4278
4072
  deleteDocuments: async (_parent, args, ctx) => {
4279
4073
  this.logger.debug("deleteDocuments(@args)", args);
4280
4074
  try {
4281
- for (const identifier of args.identifiers) await this.assertCanWrite(identifier, ctx);
4282
- return await deleteDocuments(this.reactorClient, args);
4075
+ const identifiers = [];
4076
+ for (const identifier of args.identifiers) {
4077
+ const handle = await this.assertCanWrite(identifier, ctx);
4078
+ identifiers.push(handle.fetchIdentifier);
4079
+ }
4080
+ return await deleteDocuments(this.reactorClient, {
4081
+ ...args,
4082
+ identifiers
4083
+ });
4283
4084
  } catch (error) {
4284
4085
  this.logger.error("Error in deleteDocuments(@args): @Error", args, error);
4285
4086
  throw error;
4286
4087
  }
4287
4088
  },
4288
- touchChannel: async (_parent, args) => {
4089
+ touchChannel: async (_parent, args, ctx) => {
4289
4090
  this.logger.debug("touchChannel(@args)", args);
4290
4091
  try {
4092
+ if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
4093
+ const driveId = DriveCollectionId.fromKey(args.input.collectionId).driveId;
4094
+ await this.assertCanReadCanonical(driveId, ctx);
4095
+ }
4291
4096
  return await touchChannel(this.syncManager, args);
4292
4097
  } catch (error) {
4293
4098
  this.logger.error("Error in touchChannel(@args): @Error", args, error);
4294
4099
  throw error;
4295
4100
  }
4296
4101
  },
4297
- pushSyncEnvelopes: async (_parent, args) => {
4102
+ pushSyncEnvelopes: async (_parent, args, ctx) => {
4298
4103
  this.logger.debug("pushSyncEnvelopes(@args)", args);
4299
4104
  try {
4105
+ const checkedOperations = /* @__PURE__ */ new Map();
4106
+ for (const envelope of args.envelopes) for (const op of envelope.operations ?? []) {
4107
+ const documentId = op.context.documentId;
4108
+ const operationType = op.operation.action.type;
4109
+ let checkedTypes = checkedOperations.get(documentId);
4110
+ if (!checkedTypes) {
4111
+ checkedTypes = /* @__PURE__ */ new Set();
4112
+ checkedOperations.set(documentId, checkedTypes);
4113
+ }
4114
+ if (checkedTypes.has(operationType)) continue;
4115
+ checkedTypes.add(operationType);
4116
+ await this.assertCanExecuteOperationCanonical(documentId, operationType, ctx);
4117
+ }
4300
4118
  const mutableArgs = { envelopes: args.envelopes.map((envelope) => ({
4301
4119
  type: envelope.type,
4302
4120
  channelMeta: { id: envelope.channelMeta.id },
@@ -4327,31 +4145,43 @@ var ReactorSubgraph = class extends BaseSubgraph {
4327
4145
  },
4328
4146
  Subscription: {
4329
4147
  documentChanges: {
4330
- subscribe: withFilter((() => {
4148
+ subscribe: (rootValue, args, ctx, info) => {
4331
4149
  this.logger.debug("documentChanges subscription started");
4332
- ensureGlobalDocumentSubscription(this.reactorClient);
4333
- return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.DOCUMENT_CHANGES);
4334
- }), ((payload, args) => {
4335
- if (!payload) return false;
4336
- const search = {
4337
- type: args.search?.type ?? void 0,
4338
- parentId: args.search?.parentId ?? void 0
4339
- };
4340
- return matchesSearchFilter(payload.documentChanges, search);
4341
- })),
4150
+ return withFilter(() => {
4151
+ ensureGlobalDocumentSubscription(this.reactorClient);
4152
+ return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.DOCUMENT_CHANGES);
4153
+ }, async (payload, filterArgs, filterCtx) => {
4154
+ if (!payload) return false;
4155
+ const search = {
4156
+ type: filterArgs?.search?.type ?? void 0,
4157
+ parentId: filterArgs?.search?.parentId ?? void 0
4158
+ };
4159
+ if (!matchesSearchFilter(payload.documentChanges, search)) return false;
4160
+ if (this.authorizationService.isSupremeAdmin(filterCtx?.user?.address)) return true;
4161
+ const documentIds = collectChangedDocumentIds(payload.documentChanges);
4162
+ if (documentIds.length === 0) return false;
4163
+ for (const documentId of documentIds) if (!await this.canReadDocument(documentId, filterCtx)) return false;
4164
+ return true;
4165
+ })(rootValue, args, ctx, info);
4166
+ },
4342
4167
  resolve: (payload) => {
4343
4168
  return toGqlDocumentChangeEvent(payload.documentChanges);
4344
4169
  }
4345
4170
  },
4346
4171
  jobChanges: {
4347
- subscribe: withFilter(((_parent, args) => {
4172
+ subscribe: (rootValue, args, ctx, info) => {
4348
4173
  this.logger.debug("jobChanges(@args) subscription started", args);
4349
- ensureJobSubscription(this.reactorClient, args.jobId);
4350
- return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.JOB_CHANGES);
4351
- }), ((payload, args) => {
4352
- if (!payload) return false;
4353
- return matchesJobFilter(payload, args);
4354
- })),
4174
+ return withFilter(() => {
4175
+ ensureJobSubscription(this.reactorClient, args.jobId);
4176
+ return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.JOB_CHANGES);
4177
+ }, async (payload, filterArgs, filterCtx) => {
4178
+ if (!payload || !filterArgs) return false;
4179
+ if (!matchesJobFilter(payload, filterArgs)) return false;
4180
+ if (this.authorizationService.isSupremeAdmin(filterCtx?.user?.address)) return true;
4181
+ if (!payload.documentId) return false;
4182
+ return this.canReadDocument(payload.documentId, filterCtx);
4183
+ })(rootValue, args, ctx, info);
4184
+ },
4355
4185
  resolve: (payload) => {
4356
4186
  return payload.jobChanges;
4357
4187
  }
@@ -4375,10 +4205,10 @@ const ADMIN_USERS = getAdminUsers();
4375
4205
  //#endregion
4376
4206
  //#region src/graphql/system/version.ts
4377
4207
  function getVersion() {
4378
- return "6.2.0-dev.4";
4208
+ return "6.2.0-dev.41";
4379
4209
  }
4380
4210
  function getGitHash() {
4381
- return "79386a1a3182a6350dbd6c359927889a9f1f9c2d";
4211
+ return "e8127a8841bc768f5b494738716eb7d460a4e2c6";
4382
4212
  }
4383
4213
  function getGitUrl() {
4384
4214
  return buildTreeUrl(getGitHash());
@@ -4861,10 +4691,10 @@ const DefaultCoreSubgraphs = [AnalyticsSubgraph, SystemSubgraph];
4861
4691
  //#endregion
4862
4692
  //#region src/migrations/001_create_document_permissions.ts
4863
4693
  var _001_create_document_permissions_exports = /* @__PURE__ */ __exportAll({
4864
- down: () => down$1,
4865
- up: () => up$1
4694
+ down: () => down$2,
4695
+ up: () => up$2
4866
4696
  });
4867
- async function up$1(db) {
4697
+ async function up$2(db) {
4868
4698
  await sql`
4869
4699
  CREATE TABLE IF NOT EXISTS "DocumentPermission" (
4870
4700
  "id" SERIAL PRIMARY KEY,
@@ -4938,7 +4768,7 @@ async function up$1(db) {
4938
4768
  await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_documentid_index" ON "OperationGroupPermission" ("documentId")`.execute(db);
4939
4769
  await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_groupid_index" ON "OperationGroupPermission" ("groupId")`.execute(db);
4940
4770
  }
4941
- async function down$1(db) {
4771
+ async function down$2(db) {
4942
4772
  await sql`DROP TABLE IF EXISTS "OperationGroupPermission"`.execute(db);
4943
4773
  await sql`DROP TABLE IF EXISTS "OperationUserPermission"`.execute(db);
4944
4774
  await sql`DROP TABLE IF EXISTS "DocumentGroupPermission"`.execute(db);
@@ -4949,10 +4779,10 @@ async function down$1(db) {
4949
4779
  //#endregion
4950
4780
  //#region src/migrations/002_add_document_protection.ts
4951
4781
  var _002_add_document_protection_exports = /* @__PURE__ */ __exportAll({
4952
- down: () => down,
4953
- up: () => up
4782
+ down: () => down$1,
4783
+ up: () => up$1
4954
4784
  });
4955
- async function up(db) {
4785
+ async function up$1(db) {
4956
4786
  await sql`
4957
4787
  CREATE TABLE IF NOT EXISTS "DocumentProtection" (
4958
4788
  "documentId" VARCHAR(255) PRIMARY KEY,
@@ -4965,10 +4795,80 @@ async function up(db) {
4965
4795
  await sql`CREATE INDEX IF NOT EXISTS "documentprotection_owneraddress_index" ON "DocumentProtection" ("ownerAddress")`.execute(db);
4966
4796
  await sql`CREATE INDEX IF NOT EXISTS "documentprotection_protected_index" ON "DocumentProtection" ("protected")`.execute(db);
4967
4797
  }
4968
- async function down(db) {
4798
+ async function down$1(db) {
4969
4799
  await sql`DROP TABLE IF EXISTS "DocumentProtection"`.execute(db);
4970
4800
  }
4971
4801
  //#endregion
4802
+ //#region src/migrations/003_remove_group_tables.ts
4803
+ var _003_remove_group_tables_exports = /* @__PURE__ */ __exportAll({
4804
+ down: () => down,
4805
+ up: () => up
4806
+ });
4807
+ /**
4808
+ * Removes the group-permission feature: the Group, UserGroup,
4809
+ * DocumentGroupPermission, and OperationGroupPermission tables. The feature had
4810
+ * no consumers and disclosed ACL/membership metadata (AUTH_REVIEW S-H3/S-H5);
4811
+ * it is retired in favor of direct user grants, ownership, and protection.
4812
+ *
4813
+ * Tables are dropped in reverse dependency order. There are no DB-enforced
4814
+ * foreign keys, so the drops cannot violate referential integrity and do not
4815
+ * touch the surviving DocumentPermission / OperationUserPermission /
4816
+ * DocumentProtection tables.
4817
+ */
4818
+ async function up(db) {
4819
+ await sql`DROP TABLE IF EXISTS "OperationGroupPermission"`.execute(db);
4820
+ await sql`DROP TABLE IF EXISTS "DocumentGroupPermission"`.execute(db);
4821
+ await sql`DROP TABLE IF EXISTS "UserGroup"`.execute(db);
4822
+ await sql`DROP TABLE IF EXISTS "Group"`.execute(db);
4823
+ }
4824
+ async function down(db) {
4825
+ await sql`
4826
+ CREATE TABLE IF NOT EXISTS "Group" (
4827
+ "id" SERIAL PRIMARY KEY,
4828
+ "name" VARCHAR(255) NOT NULL UNIQUE,
4829
+ "description" TEXT,
4830
+ "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
4831
+ "updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
4832
+ )
4833
+ `.execute(db);
4834
+ await sql`
4835
+ CREATE TABLE IF NOT EXISTS "UserGroup" (
4836
+ "userAddress" VARCHAR(255) NOT NULL,
4837
+ "groupId" INTEGER NOT NULL,
4838
+ "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
4839
+ PRIMARY KEY ("userAddress", "groupId")
4840
+ )
4841
+ `.execute(db);
4842
+ await sql`CREATE INDEX IF NOT EXISTS "usergroup_groupid_index" ON "UserGroup" ("groupId")`.execute(db);
4843
+ await sql`
4844
+ CREATE TABLE IF NOT EXISTS "DocumentGroupPermission" (
4845
+ "id" SERIAL PRIMARY KEY,
4846
+ "documentId" VARCHAR(255) NOT NULL,
4847
+ "groupId" INTEGER NOT NULL,
4848
+ "permission" VARCHAR(20) NOT NULL CHECK ("permission" IN ('READ', 'WRITE', 'ADMIN')),
4849
+ "grantedBy" VARCHAR(255) NOT NULL,
4850
+ "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
4851
+ "updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
4852
+ UNIQUE ("documentId", "groupId")
4853
+ )
4854
+ `.execute(db);
4855
+ await sql`CREATE INDEX IF NOT EXISTS "documentgrouppermission_documentid_index" ON "DocumentGroupPermission" ("documentId")`.execute(db);
4856
+ await sql`CREATE INDEX IF NOT EXISTS "documentgrouppermission_groupid_index" ON "DocumentGroupPermission" ("groupId")`.execute(db);
4857
+ await sql`
4858
+ CREATE TABLE IF NOT EXISTS "OperationGroupPermission" (
4859
+ "id" SERIAL PRIMARY KEY,
4860
+ "documentId" VARCHAR(255) NOT NULL,
4861
+ "operationType" VARCHAR(255) NOT NULL,
4862
+ "groupId" INTEGER NOT NULL,
4863
+ "grantedBy" VARCHAR(255) NOT NULL,
4864
+ "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
4865
+ UNIQUE ("documentId", "operationType", "groupId")
4866
+ )
4867
+ `.execute(db);
4868
+ await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_documentid_index" ON "OperationGroupPermission" ("documentId")`.execute(db);
4869
+ await sql`CREATE INDEX IF NOT EXISTS "operationgrouppermission_groupid_index" ON "OperationGroupPermission" ("groupId")`.execute(db);
4870
+ }
4871
+ //#endregion
4972
4872
  //#region src/migrations/index.ts
4973
4873
  /**
4974
4874
  * Custom migration provider that loads migrations from imported modules
@@ -4977,7 +4877,8 @@ var StaticMigrationProvider = class {
4977
4877
  getMigrations() {
4978
4878
  return Promise.resolve({
4979
4879
  "001_create_document_permissions": _001_create_document_permissions_exports,
4980
- "002_add_document_protection": _002_add_document_protection_exports
4880
+ "002_add_document_protection": _002_add_document_protection_exports,
4881
+ "003_remove_group_tables": _003_remove_group_tables_exports
4981
4882
  });
4982
4883
  }
4983
4884
  };
@@ -4999,104 +4900,159 @@ async function runMigrations(db) {
4999
4900
  }
5000
4901
  }
5001
4902
  //#endregion
5002
- //#region src/services/authorization.service.ts
5003
- /**
5004
- * Central authorization service — single source of truth for all permission checks.
5005
- *
5006
- * Authorization model:
5007
- * 1. Supreme admin (ADMINS env) → ALLOW ALL
5008
- * 2. Is document protected?
5009
- * a. NOT protected:
5010
- * - READ: anyone (even anonymous) → ALLOW
5011
- * - WRITE: authenticated user → ALLOW
5012
- * b. PROTECTED:
5013
- * - READ: requires explicit READ/WRITE/ADMIN grant (direct or via group/parent)
5014
- * - WRITE: requires explicit WRITE/ADMIN grant (direct or via group/parent)
5015
- * 3. Operation restricted? → Check OperationUserPermission
5016
- * 4. Document owner = implicit ADMIN
5017
- * 5. Drive protected = all children effectively protected
5018
- */
5019
- var AuthorizationService = class {
4903
+ //#region src/services/auth.service.ts
4904
+ const DEFAULT_CREDENTIAL_CACHE_TTL_MS = 6e4;
4905
+ const CREDENTIAL_CACHE_MAX_ENTRIES = 1e3;
4906
+ var AuthService = class {
5020
4907
  config;
5021
- constructor(documentPermissionService, config) {
5022
- this.documentPermissionService = documentPermissionService;
4908
+ credentialCache = /* @__PURE__ */ new Map();
4909
+ constructor(config) {
5023
4910
  this.config = config;
5024
4911
  }
4912
+ async authenticateRequest(request) {
4913
+ if (!this.config.enabled) return {
4914
+ user: void 0,
4915
+ admins: [],
4916
+ auth_enabled: false
4917
+ };
4918
+ const method = request.method;
4919
+ if (method === "OPTIONS" || method === "GET") return {
4920
+ user: void 0,
4921
+ admins: this.config.admins,
4922
+ auth_enabled: true
4923
+ };
4924
+ return this.verifyBearer(request.headers.get("authorization") ?? void 0);
4925
+ }
5025
4926
  /**
5026
- * Check if a user is a supreme admin (from ADMINS env var).
4927
+ * Verify a Bearer token regardless of HTTP method. Use this from non-GraphQL
4928
+ * middleware that must enforce authentication on every request.
5027
4929
  */
5028
- isSupremeAdmin(userAddress) {
5029
- if (!userAddress) return false;
5030
- return this.config.admins.includes(userAddress.toLowerCase());
4930
+ async verifyBearer(authorization) {
4931
+ if (!this.config.enabled) return {
4932
+ user: void 0,
4933
+ admins: [],
4934
+ auth_enabled: false
4935
+ };
4936
+ const token = authorization?.split(" ")[1];
4937
+ if (!token) return {
4938
+ user: void 0,
4939
+ admins: this.config.admins,
4940
+ auth_enabled: true
4941
+ };
4942
+ try {
4943
+ const verified = await this.verifyToken(token);
4944
+ if (!verified) return new Response(JSON.stringify({ error: "Verification failed" }), { status: 401 });
4945
+ const user = this.extractUserFromVerification(verified);
4946
+ if (!user) return new Response(JSON.stringify({ error: "Missing credentials" }), { status: 401 });
4947
+ if (!this.config.skipCredentialVerification) {
4948
+ if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) return new Response(JSON.stringify({ error: "Credentials no longer valid" }), { status: 401 });
4949
+ }
4950
+ return {
4951
+ user,
4952
+ admins: this.config.admins,
4953
+ auth_enabled: true
4954
+ };
4955
+ } catch {
4956
+ return new Response(JSON.stringify({ error: "Authentication failed" }), { status: 401 });
4957
+ }
4958
+ }
4959
+ async authenticateWebSocketConnection(connectionParams) {
4960
+ if (!this.config.enabled) return null;
4961
+ const authHeader = connectionParams.authorization;
4962
+ if (!authHeader) throw new Error("Missing authorization in connection parameters");
4963
+ const token = authHeader.split(" ")[1];
4964
+ if (!token) throw new Error("Invalid authorization format");
4965
+ const verified = await this.verifyToken(token);
4966
+ if (!verified) throw new Error("Token verification failed");
4967
+ const user = this.extractUserFromVerification(verified);
4968
+ if (!user) throw new Error("Invalid credentials");
4969
+ if (!this.config.skipCredentialVerification) {
4970
+ if (!await this.verifyCredentialExists(user.address, user.chainId, verified.issuer)) throw new Error("Credentials no longer valid");
4971
+ }
4972
+ return user;
5031
4973
  }
5032
4974
  /**
5033
- * Check if a user can read a document.
5034
- *
5035
- * - Supreme admin → yes
5036
- * - Not protected → anyone can read (even anonymous)
5037
- * - Protected → requires READ/WRITE/ADMIN grant (direct, group, or parent inheritance)
5038
- * - Owner → yes (implicit ADMIN)
4975
+ * Verify the auth bearer token
5039
4976
  */
5040
- async canRead(documentId, userAddress, getParentIds) {
5041
- if (this.isSupremeAdmin(userAddress)) return true;
5042
- if (!(getParentIds ? await this.documentPermissionService.isProtectedWithAncestors(documentId, getParentIds) : await this.documentPermissionService.isDocumentProtected(documentId))) return true;
5043
- if (!userAddress) return false;
5044
- const owner = await this.documentPermissionService.getDocumentOwner(documentId);
5045
- if (owner && owner === userAddress.toLowerCase()) return true;
5046
- if (getParentIds) return this.documentPermissionService.canRead(documentId, userAddress, getParentIds);
5047
- return this.documentPermissionService.canReadDocument(documentId, userAddress);
4977
+ async verifyToken(token) {
4978
+ return await verifyAuthBearerToken(token);
5048
4979
  }
5049
4980
  /**
5050
- * Check if a user can write to a document.
5051
- *
5052
- * - Supreme admin → yes
5053
- * - Not protected → anyone can write (even anonymous)
5054
- * - Protected → requires authentication + WRITE/ADMIN grant
5055
- * - Owner → yes (implicit ADMIN)
4981
+ * Extract user information from verification result
5056
4982
  */
5057
- async canWrite(documentId, userAddress, getParentIds) {
5058
- if (this.isSupremeAdmin(userAddress)) return true;
5059
- if (!(getParentIds ? await this.documentPermissionService.isProtectedWithAncestors(documentId, getParentIds) : await this.documentPermissionService.isDocumentProtected(documentId))) return true;
5060
- if (!userAddress) return false;
5061
- const owner = await this.documentPermissionService.getDocumentOwner(documentId);
5062
- if (owner && owner === userAddress.toLowerCase()) return true;
5063
- if (getParentIds) return this.documentPermissionService.canWrite(documentId, userAddress, getParentIds);
5064
- return this.documentPermissionService.canWriteDocument(documentId, userAddress);
4983
+ extractUserFromVerification(verified) {
4984
+ try {
4985
+ const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
4986
+ if (!address || !chainId || !networkId) return null;
4987
+ return {
4988
+ address,
4989
+ chainId,
4990
+ networkId
4991
+ };
4992
+ } catch {
4993
+ return null;
4994
+ }
5065
4995
  }
5066
4996
  /**
5067
- * Check if a user can manage a document (change permissions, protection, transfer ownership).
4997
+ * Verify that the credential still exists on the Renown API.
5068
4998
  *
5069
- * - Supreme admin yes
5070
- * - Owner yes
5071
- * - Has ADMIN grant yes
4999
+ * Results are cached per (address, chainId, issuer) for a short TTL so the
5000
+ * blocking external round-trip is not paid on every request. Concurrent
5001
+ * checks for the same key share a single in-flight request, and entries
5002
+ * that resolve to false are evicted immediately so failed or revoked
5003
+ * credentials are re-checked on the next request.
5072
5004
  */
5073
- async canManage(documentId, userAddress, _getParentIds) {
5074
- if (this.isSupremeAdmin(userAddress)) return true;
5075
- if (!userAddress) return false;
5076
- const owner = await this.documentPermissionService.getDocumentOwner(documentId);
5077
- if (owner && owner === userAddress.toLowerCase()) return true;
5078
- return this.documentPermissionService.canManageDocument(documentId, userAddress);
5005
+ verifyCredentialExists(address, chainId, appId) {
5006
+ const ttlMs = this.config.credentialVerificationCacheTtlMs ?? DEFAULT_CREDENTIAL_CACHE_TTL_MS;
5007
+ if (ttlMs <= 0) return this.fetchCredentialExists(address, chainId, appId);
5008
+ const key = `${address.toLowerCase()}:${chainId}:${appId}`;
5009
+ const now = Date.now();
5010
+ const cached = this.credentialCache.get(key);
5011
+ if (cached && cached.expiresAt > now) return cached.exists;
5012
+ this.pruneCredentialCache(now);
5013
+ const entry = {
5014
+ exists: this.fetchCredentialExists(address, chainId, appId).then((exists) => {
5015
+ if (!exists && this.credentialCache.get(key) === entry) this.credentialCache.delete(key);
5016
+ return exists;
5017
+ }),
5018
+ expiresAt: now + ttlMs
5019
+ };
5020
+ this.credentialCache.set(key, entry);
5021
+ return entry.exists;
5079
5022
  }
5080
5023
  /**
5081
- * Check if a user can execute a specific operation.
5082
- * If the operation is not restricted, falls through to the standard write check.
5083
- * If the operation is restricted, requires an explicit OperationUserPermission grant.
5024
+ * Enforce the cache size cap before inserting a new entry: drop expired
5025
+ * entries first, then evict oldest-inserted entries (insertion order
5026
+ * matches expiry order since the TTL is constant) until under the cap, so
5027
+ * a flood of distinct keys cannot grow the map without bound.
5084
5028
  */
5085
- async canExecuteOperation(documentId, operationType, userAddress, getParentIds) {
5086
- if (this.isSupremeAdmin(userAddress)) return true;
5087
- if (!await this.documentPermissionService.isOperationRestricted(documentId, operationType)) return this.canWrite(documentId, userAddress, getParentIds);
5088
- return this.documentPermissionService.canExecuteOperation(documentId, operationType, userAddress?.toLowerCase());
5029
+ pruneCredentialCache(now) {
5030
+ if (this.credentialCache.size < CREDENTIAL_CACHE_MAX_ENTRIES) return;
5031
+ for (const [key, entry] of this.credentialCache) if (entry.expiresAt <= now) this.credentialCache.delete(key);
5032
+ while (this.credentialCache.size >= CREDENTIAL_CACHE_MAX_ENTRIES) {
5033
+ const oldestKey = this.credentialCache.keys().next().value;
5034
+ if (oldestKey === void 0) break;
5035
+ this.credentialCache.delete(oldestKey);
5036
+ }
5089
5037
  }
5090
5038
  /**
5091
- * Combined check for mutations: can the user write + execute the operation?
5092
- * This enables READ-only users with operation grants to execute specific operations.
5093
- * For restricted operations, only the operation grant is checked (bypasses write check),
5094
- * allowing READ-only users with an explicit operation grant to execute that operation.
5039
+ * Fetch the credential from the Renown API and validate it against the
5040
+ * expected address, chainId and issuer. Never throws; returns false on any
5041
+ * network or validation failure.
5095
5042
  */
5096
- async canMutate(documentId, operationType, userAddress, getParentIds) {
5097
- if (this.isSupremeAdmin(userAddress)) return true;
5098
- if (await this.documentPermissionService.isOperationRestricted(documentId, operationType)) return this.documentPermissionService.canExecuteOperation(documentId, operationType, userAddress?.toLowerCase());
5099
- return this.canWrite(documentId, userAddress, getParentIds);
5043
+ async fetchCredentialExists(address, chainId, appId) {
5044
+ const url = `https://www.renown.id/api/auth/credential?address=${address}&chainId=${chainId}&connectId=${appId}&appId=${appId}`;
5045
+ try {
5046
+ const response = await fetch(url, { method: "GET" });
5047
+ if (response.status !== 200) return false;
5048
+ const credential = (await response.json()).credential;
5049
+ const appIdVerfied = credential.credentialSubject.id;
5050
+ const addressVerfied = credential.issuer.id.split(":")[4];
5051
+ const chainIdVerfied = credential.issuer.id.split(":")[3];
5052
+ return appIdVerfied === appId && addressVerfied.toLocaleLowerCase() === address.toLocaleLowerCase() && chainIdVerfied === chainId.toString();
5053
+ } catch {
5054
+ return false;
5055
+ }
5100
5056
  }
5101
5057
  };
5102
5058
  //#endregion
@@ -5110,7 +5066,7 @@ var AuthorizationService = class {
5110
5066
  * - ADMIN: Can manage document permissions and settings
5111
5067
  *
5112
5068
  * Operation permissions:
5113
- * - Users and groups can be granted permission to execute specific operations
5069
+ * - Users can be granted permission to execute specific operations
5114
5070
  */
5115
5071
  var DocumentPermissionService = class {
5116
5072
  config;
@@ -5189,209 +5145,7 @@ var DocumentPermissionService = class {
5189
5145
  */
5190
5146
  async deleteAllDocumentPermissions(documentId) {
5191
5147
  await this.db.deleteFrom("DocumentPermission").where("documentId", "=", documentId).execute();
5192
- await this.db.deleteFrom("DocumentGroupPermission").where("documentId", "=", documentId).execute();
5193
5148
  await this.db.deleteFrom("OperationUserPermission").where("documentId", "=", documentId).execute();
5194
- await this.db.deleteFrom("OperationGroupPermission").where("documentId", "=", documentId).execute();
5195
- }
5196
- /**
5197
- * Check if a user can read a document.
5198
- * Returns true if user has READ, WRITE, or ADMIN permission (direct or via group)
5199
- */
5200
- async canReadDocument(documentId, userAddress) {
5201
- if (!userAddress) return false;
5202
- if (await this.getUserPermission(documentId, userAddress) !== null) return true;
5203
- return await this.getUserGroupPermission(documentId, userAddress) !== null;
5204
- }
5205
- /**
5206
- * Check if a user can write to a document.
5207
- * Returns true if user has WRITE or ADMIN permission (direct or via group)
5208
- */
5209
- async canWriteDocument(documentId, userAddress) {
5210
- if (!userAddress) return false;
5211
- const directPermission = await this.getUserPermission(documentId, userAddress);
5212
- if (directPermission === "WRITE" || directPermission === "ADMIN") return true;
5213
- const groupPermission = await this.getUserGroupPermission(documentId, userAddress);
5214
- return groupPermission === "WRITE" || groupPermission === "ADMIN";
5215
- }
5216
- /**
5217
- * Check if a user can manage a document (change permissions, settings).
5218
- * Returns true if user has ADMIN permission (direct or via group)
5219
- */
5220
- async canManageDocument(documentId, userAddress) {
5221
- if (!userAddress) return false;
5222
- if (await this.getUserPermission(documentId, userAddress) === "ADMIN") return true;
5223
- return await this.getUserGroupPermission(documentId, userAddress) === "ADMIN";
5224
- }
5225
- /**
5226
- * Check if a user can read a document, including parent permission inheritance.
5227
- * Returns true if user has permission on the document OR any parent in the hierarchy.
5228
- */
5229
- async canRead(documentId, userAddress, getParentIds) {
5230
- if (await this.canReadDocument(documentId, userAddress)) return true;
5231
- const parentIds = await getParentIds(documentId);
5232
- for (const parentId of parentIds) if (await this.canRead(parentId, userAddress, getParentIds)) return true;
5233
- return false;
5234
- }
5235
- /**
5236
- * Check if a user can write to a document, including parent permission inheritance.
5237
- * Returns true if user has write permission on the document OR any parent in the hierarchy.
5238
- */
5239
- async canWrite(documentId, userAddress, getParentIds) {
5240
- if (await this.canWriteDocument(documentId, userAddress)) return true;
5241
- const parentIds = await getParentIds(documentId);
5242
- for (const parentId of parentIds) if (await this.canWrite(parentId, userAddress, getParentIds)) return true;
5243
- return false;
5244
- }
5245
- /**
5246
- * Filter a list of document IDs to only include those the user can read.
5247
- */
5248
- async filterReadableDocuments(documentIds, userAddress, getParentIds) {
5249
- const results = [];
5250
- for (const docId of documentIds) if (await this.canRead(docId, userAddress, getParentIds)) results.push(docId);
5251
- return results;
5252
- }
5253
- /**
5254
- * Create a new group
5255
- */
5256
- async createGroup(name, description) {
5257
- const now = /* @__PURE__ */ new Date();
5258
- await this.db.insertInto("Group").values({
5259
- name,
5260
- description: description ?? null,
5261
- createdAt: now,
5262
- updatedAt: now
5263
- }).execute();
5264
- return await this.db.selectFrom("Group").select([
5265
- "id",
5266
- "name",
5267
- "description",
5268
- "createdAt",
5269
- "updatedAt"
5270
- ]).where("name", "=", name).executeTakeFirstOrThrow();
5271
- }
5272
- /**
5273
- * Delete a group and all its associations
5274
- */
5275
- async deleteGroup(groupId) {
5276
- await this.db.deleteFrom("OperationGroupPermission").where("groupId", "=", groupId).execute();
5277
- await this.db.deleteFrom("DocumentGroupPermission").where("groupId", "=", groupId).execute();
5278
- await this.db.deleteFrom("UserGroup").where("groupId", "=", groupId).execute();
5279
- await this.db.deleteFrom("Group").where("id", "=", groupId).execute();
5280
- }
5281
- /**
5282
- * Get a group by ID
5283
- */
5284
- async getGroup(groupId) {
5285
- return await this.db.selectFrom("Group").select([
5286
- "id",
5287
- "name",
5288
- "description",
5289
- "createdAt",
5290
- "updatedAt"
5291
- ]).where("id", "=", groupId).executeTakeFirst() ?? null;
5292
- }
5293
- /**
5294
- * List all groups
5295
- */
5296
- async listGroups() {
5297
- return this.db.selectFrom("Group").select([
5298
- "id",
5299
- "name",
5300
- "description",
5301
- "createdAt",
5302
- "updatedAt"
5303
- ]).execute();
5304
- }
5305
- /**
5306
- * Add a user to a group
5307
- */
5308
- async addUserToGroup(userAddress, groupId) {
5309
- const now = /* @__PURE__ */ new Date();
5310
- const normalizedAddress = userAddress.toLowerCase();
5311
- await this.db.insertInto("UserGroup").values({
5312
- userAddress: normalizedAddress,
5313
- groupId,
5314
- createdAt: now
5315
- }).onConflict((oc) => oc.columns(["userAddress", "groupId"]).doNothing()).execute();
5316
- }
5317
- /**
5318
- * Remove a user from a group
5319
- */
5320
- async removeUserFromGroup(userAddress, groupId) {
5321
- await this.db.deleteFrom("UserGroup").where("userAddress", "=", userAddress.toLowerCase()).where("groupId", "=", groupId).execute();
5322
- }
5323
- /**
5324
- * Get all groups a user belongs to
5325
- */
5326
- async getUserGroups(userAddress) {
5327
- return this.db.selectFrom("UserGroup").innerJoin("Group", "Group.id", "UserGroup.groupId").select([
5328
- "Group.id",
5329
- "Group.name",
5330
- "Group.description",
5331
- "Group.createdAt",
5332
- "Group.updatedAt"
5333
- ]).where("UserGroup.userAddress", "=", userAddress.toLowerCase()).execute();
5334
- }
5335
- /**
5336
- * Get all members of a group
5337
- */
5338
- async getGroupMembers(groupId) {
5339
- return (await this.db.selectFrom("UserGroup").select("userAddress").where("groupId", "=", groupId).execute()).map((r) => r.userAddress);
5340
- }
5341
- /**
5342
- * Grant a group permission on a document
5343
- */
5344
- async grantGroupPermission(documentId, groupId, permission, grantedBy) {
5345
- const now = /* @__PURE__ */ new Date();
5346
- await this.db.insertInto("DocumentGroupPermission").values({
5347
- documentId,
5348
- groupId,
5349
- permission,
5350
- grantedBy: grantedBy.toLowerCase(),
5351
- createdAt: now,
5352
- updatedAt: now
5353
- }).onConflict((oc) => oc.columns(["documentId", "groupId"]).doUpdateSet({
5354
- permission,
5355
- grantedBy: grantedBy.toLowerCase(),
5356
- updatedAt: now
5357
- })).execute();
5358
- return await this.db.selectFrom("DocumentGroupPermission").select([
5359
- "documentId",
5360
- "groupId",
5361
- "permission",
5362
- "grantedBy",
5363
- "createdAt",
5364
- "updatedAt"
5365
- ]).where("documentId", "=", documentId).where("groupId", "=", groupId).executeTakeFirstOrThrow();
5366
- }
5367
- /**
5368
- * Revoke a group's permission on a document
5369
- */
5370
- async revokeGroupPermission(documentId, groupId) {
5371
- await this.db.deleteFrom("DocumentGroupPermission").where("documentId", "=", documentId).where("groupId", "=", groupId).execute();
5372
- }
5373
- /**
5374
- * Get all group permissions for a document
5375
- */
5376
- async getDocumentGroupPermissions(documentId) {
5377
- return this.db.selectFrom("DocumentGroupPermission").select([
5378
- "documentId",
5379
- "groupId",
5380
- "permission",
5381
- "grantedBy",
5382
- "createdAt",
5383
- "updatedAt"
5384
- ]).where("documentId", "=", documentId).execute();
5385
- }
5386
- /**
5387
- * Get best permission level a user has on a document via groups
5388
- */
5389
- async getUserGroupPermission(documentId, userAddress) {
5390
- const result = await this.db.selectFrom("DocumentGroupPermission").innerJoin("UserGroup", "UserGroup.groupId", "DocumentGroupPermission.groupId").select("DocumentGroupPermission.permission").where("DocumentGroupPermission.documentId", "=", documentId).where("UserGroup.userAddress", "=", userAddress.toLowerCase()).execute();
5391
- if (result.length === 0) return null;
5392
- if (result.some((r) => r.permission === "ADMIN")) return "ADMIN";
5393
- if (result.some((r) => r.permission === "WRITE")) return "WRITE";
5394
- return "READ";
5395
5149
  }
5396
5150
  /**
5397
5151
  * Grant a user permission to execute an operation on a document
@@ -5425,36 +5179,6 @@ var DocumentPermissionService = class {
5425
5179
  await this.db.deleteFrom("OperationUserPermission").where("documentId", "=", documentId).where("operationType", "=", operationType).where("userAddress", "=", userAddress.toLowerCase()).execute();
5426
5180
  }
5427
5181
  /**
5428
- * Grant a group permission to execute an operation on a document
5429
- */
5430
- async grantGroupOperationPermission(documentId, operationType, groupId, grantedBy) {
5431
- const now = /* @__PURE__ */ new Date();
5432
- await this.db.insertInto("OperationGroupPermission").values({
5433
- documentId,
5434
- operationType,
5435
- groupId,
5436
- grantedBy: grantedBy.toLowerCase(),
5437
- createdAt: now
5438
- }).onConflict((oc) => oc.columns([
5439
- "documentId",
5440
- "operationType",
5441
- "groupId"
5442
- ]).doNothing()).execute();
5443
- return await this.db.selectFrom("OperationGroupPermission").select([
5444
- "documentId",
5445
- "operationType",
5446
- "groupId",
5447
- "grantedBy",
5448
- "createdAt"
5449
- ]).where("documentId", "=", documentId).where("operationType", "=", operationType).where("groupId", "=", groupId).executeTakeFirstOrThrow();
5450
- }
5451
- /**
5452
- * Revoke a group's permission to execute an operation
5453
- */
5454
- async revokeGroupOperationPermission(documentId, operationType, groupId) {
5455
- await this.db.deleteFrom("OperationGroupPermission").where("documentId", "=", documentId).where("operationType", "=", operationType).where("groupId", "=", groupId).execute();
5456
- }
5457
- /**
5458
5182
  * Get all users with permission to execute an operation
5459
5183
  */
5460
5184
  async getOperationUserPermissions(documentId, operationType) {
@@ -5467,35 +5191,18 @@ var DocumentPermissionService = class {
5467
5191
  ]).where("documentId", "=", documentId).where("operationType", "=", operationType).execute();
5468
5192
  }
5469
5193
  /**
5470
- * Get all groups with permission to execute an operation
5194
+ * Whether an operation-permission row exists for the user on this operation.
5471
5195
  */
5472
- async getOperationGroupPermissions(documentId, operationType) {
5473
- return this.db.selectFrom("OperationGroupPermission").select([
5474
- "documentId",
5475
- "operationType",
5476
- "groupId",
5477
- "grantedBy",
5478
- "createdAt"
5479
- ]).where("documentId", "=", documentId).where("operationType", "=", operationType).execute();
5480
- }
5481
- /**
5482
- * Check if a user can execute a specific operation on a document.
5483
- * Returns true if user has direct permission or is in a group with permission.
5484
- */
5485
- async canExecuteOperation(documentId, operationType, userAddress) {
5486
- if (!userAddress) return false;
5196
+ async hasOperationGrant(documentId, operationType, userAddress) {
5487
5197
  const normalizedAddress = userAddress.toLowerCase();
5488
- if (await this.db.selectFrom("OperationUserPermission").select("userAddress").where("documentId", "=", documentId).where("operationType", "=", operationType).where("userAddress", "=", normalizedAddress).executeTakeFirst()) return true;
5489
- return !!await this.db.selectFrom("OperationGroupPermission").innerJoin("UserGroup", "UserGroup.groupId", "OperationGroupPermission.groupId").select("OperationGroupPermission.groupId").where("OperationGroupPermission.documentId", "=", documentId).where("OperationGroupPermission.operationType", "=", operationType).where("UserGroup.userAddress", "=", normalizedAddress).executeTakeFirst();
5198
+ return !!await this.db.selectFrom("OperationUserPermission").select("userAddress").where("documentId", "=", documentId).where("operationType", "=", operationType).where("userAddress", "=", normalizedAddress).executeTakeFirst();
5490
5199
  }
5491
5200
  /**
5492
5201
  * Check if an operation has any permissions set (is restricted)
5493
5202
  */
5494
5203
  async isOperationRestricted(documentId, operationType) {
5495
5204
  const userPermCount = await this.db.selectFrom("OperationUserPermission").select(sql`count(*)`.as("count")).where("documentId", "=", documentId).where("operationType", "=", operationType).executeTakeFirst();
5496
- if (userPermCount && Number(userPermCount.count) > 0) return true;
5497
- const groupPermCount = await this.db.selectFrom("OperationGroupPermission").select(sql`count(*)`.as("count")).where("documentId", "=", documentId).where("operationType", "=", operationType).executeTakeFirst();
5498
- return groupPermCount !== void 0 && Number(groupPermCount.count) > 0;
5205
+ return userPermCount !== void 0 && Number(userPermCount.count) > 0;
5499
5206
  }
5500
5207
  /**
5501
5208
  * Check if a specific document has a protection row set to true.
@@ -5610,6 +5317,60 @@ var DocumentPermissionService = class {
5610
5317
  }
5611
5318
  };
5612
5319
  //#endregion
5320
+ //#region src/services/get-parent-ids.ts
5321
+ /**
5322
+ * The canonical parent-document resolver used for permission inheritance:
5323
+ * a document's parents are the sources of its incoming "child"
5324
+ * relationships. Lookup failures resolve to "no parents" so a relationship
5325
+ * store outage degrades to no inherited permissions rather than an error.
5326
+ */
5327
+ function createGetParentIdsFn(reactorClient) {
5328
+ return async (documentId) => {
5329
+ try {
5330
+ return (await reactorClient.getIncomingRelationships(documentId, "child")).results.map((doc) => doc.header.id);
5331
+ } catch {
5332
+ return [];
5333
+ }
5334
+ };
5335
+ }
5336
+ //#endregion
5337
+ //#region src/services/mcp-request-authorizer.ts
5338
+ /**
5339
+ * Authorization gate for the /mcp endpoint (AUTH_REVIEW S-C1). MCP tools have
5340
+ * unrestricted reactor access, so access is limited to supreme admins when
5341
+ * auth is enabled. OPEN policy is public; any other policy without an
5342
+ * AuthService fails closed.
5343
+ */
5344
+ function createMcpRequestAuthorizer(authService, authorizationService) {
5345
+ return async (req) => {
5346
+ if (!authService) {
5347
+ if (authorizationService.config.policy === AuthorizationPolicy.OPEN) return { authorized: true };
5348
+ return {
5349
+ authorized: false,
5350
+ status: 401,
5351
+ message: "Authentication required"
5352
+ };
5353
+ }
5354
+ const context = await authService.verifyBearer(req.headers.authorization);
5355
+ if (context instanceof Response) return {
5356
+ authorized: false,
5357
+ status: context.status,
5358
+ message: "Authentication failed"
5359
+ };
5360
+ if (!context.user) return {
5361
+ authorized: false,
5362
+ status: 401,
5363
+ message: "Authentication required"
5364
+ };
5365
+ if (!authorizationService.isSupremeAdmin(context.user.address)) return {
5366
+ authorized: false,
5367
+ status: 403,
5368
+ message: "Forbidden: MCP access requires an administrator"
5369
+ };
5370
+ return { authorized: true };
5371
+ };
5372
+ }
5373
+ //#endregion
5613
5374
  //#region src/utils/db.ts
5614
5375
  function isPG(connectionString) {
5615
5376
  if (connectionString.startsWith("postgresql://") || connectionString.startsWith("postgres://")) return true;
@@ -5735,6 +5496,24 @@ const initAnalyticsStoreSql = [
5735
5496
  //#region src/server.ts
5736
5497
  const defaultLogger = childLogger(["reactor-api", "server"]);
5737
5498
  const DEFAULT_PORT = 4e3;
5499
+ /**
5500
+ * Doc-perms require auth: with auth off no `user` is ever resolved, so every
5501
+ * authorization check fails closed. Refuse to boot rather than run broken.
5502
+ */
5503
+ function assertAuthRequiredForDocumentPermissions(authEnabled, documentPermissionsRequested) {
5504
+ if (!authEnabled && documentPermissionsRequested) throw new Error("Document permissions require authentication: AUTH_ENABLED is false but document permissions were requested (DOCUMENT_PERMISSIONS_ENABLED=true or a documentPermissionService was provided). Enable authentication (AUTH_ENABLED=true, or auth.enabled in the config file) or disable document permissions.");
5505
+ }
5506
+ /**
5507
+ * Refuses SKIP_CREDENTIAL_VERIFICATION at boot outside tests or an explicit
5508
+ * opt-in: it removes the only binding between a token's claimed address and its
5509
+ * signing key. Fail-closed — unset NODE_ENV counts as production.
5510
+ */
5511
+ function assertSkipCredentialVerificationAllowed(authEnabled, skipCredentialVerification, env) {
5512
+ if (!authEnabled || !skipCredentialVerification) return;
5513
+ const inAutomatedTest = env.VITEST === "true" || env.NODE_ENV === "test";
5514
+ const acknowledged = env.ALLOW_INSECURE_SKIP_CREDENTIAL_VERIFICATION === "true";
5515
+ if (!inAutomatedTest && !acknowledged) throw new Error("SKIP_CREDENTIAL_VERIFICATION is set but refused: it disables the live Renown credential check — the only check binding a token's claimed address to the key that signed it — so honoring it allows identity spoofing, including of admins. It is never safe in production. For local or sandbox use, also set ALLOW_INSECURE_SKIP_CREDENTIAL_VERIFICATION=true to acknowledge the risk; automated test runs (VITEST=true or NODE_ENV=test) are exempt.");
5516
+ }
5738
5517
  function createReadinessGate() {
5739
5518
  let ready = false;
5740
5519
  return {
@@ -5782,11 +5561,8 @@ function makeDbClosers(knexInstance, pglite) {
5782
5561
  /**
5783
5562
  * Sets up the subgraph manager and registers subgraphs
5784
5563
  */
5785
- async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, auth, documentPermissionService, enableDocumentModelSubgraphs, port, authorizationService, reactorDriveClient) {
5786
- const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), {
5787
- enabled: auth?.enabled ?? false,
5788
- admins: auth?.admins ?? []
5789
- }, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient);
5564
+ async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient) {
5565
+ const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), authService, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient);
5790
5566
  await graphqlManager.init(subgraphs.core, authFetchMiddleware);
5791
5567
  for (const [, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql");
5792
5568
  await graphqlManager.updateRouter();
@@ -5860,6 +5636,7 @@ async function startServer(httpAdapter, port, httpsOptions, logger) {
5860
5636
  async function _setupCommonInfrastructure(options) {
5861
5637
  const port = options.port ?? DEFAULT_PORT;
5862
5638
  const { adapter: httpAdapter } = await createHttpAdapter("express");
5639
+ const logger = options.logger ?? defaultLogger;
5863
5640
  let admins = [];
5864
5641
  let authEnabled = false;
5865
5642
  if (options.configFile) {
@@ -5870,16 +5647,23 @@ async function _setupCommonInfrastructure(options) {
5870
5647
  admins = options.auth.admins.map((a) => a.toLowerCase());
5871
5648
  authEnabled = options.auth.enabled;
5872
5649
  }
5873
- const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION } = process.env;
5650
+ const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION, CREDENTIAL_VERIFICATION_CACHE_TTL_MS } = process.env;
5874
5651
  if (AUTH_ENABLED !== void 0) authEnabled = AUTH_ENABLED === "true";
5875
5652
  if (ADMINS !== void 0) admins = ADMINS.split(",").map((a) => a.toLowerCase());
5876
5653
  let defaultProtection = false;
5877
5654
  if (DEFAULT_PROTECTION !== void 0) defaultProtection = DEFAULT_PROTECTION.toLowerCase() === "true";
5878
- const { USERS, GUESTS, FREE_ENTRY } = process.env;
5879
- if (USERS || GUESTS || FREE_ENTRY) console.warn("[DEPRECATION WARNING] The USERS, GUESTS, and FREE_ENTRY environment variables are no longer supported. Access control is now managed per-document via the DocumentProtection system. Use DEFAULT_PROTECTION=true for strict mode, or manage protection per document via the GraphQL API. See the auth documentation for migration guidance.");
5880
5655
  let skipCredentialVerification = false;
5881
5656
  if (SKIP_CREDENTIAL_VERIFICATION !== void 0) skipCredentialVerification = SKIP_CREDENTIAL_VERIFICATION === "true";
5882
- const logger = options.logger ?? defaultLogger;
5657
+ let credentialVerificationCacheTtlMs;
5658
+ if (CREDENTIAL_VERIFICATION_CACHE_TTL_MS !== void 0) {
5659
+ const parsed = Number(CREDENTIAL_VERIFICATION_CACHE_TTL_MS);
5660
+ if (CREDENTIAL_VERIFICATION_CACHE_TTL_MS.trim() !== "" && Number.isFinite(parsed) && parsed >= 0) credentialVerificationCacheTtlMs = parsed;
5661
+ else logger.warn(`Ignoring invalid CREDENTIAL_VERIFICATION_CACHE_TTL_MS="${CREDENTIAL_VERIFICATION_CACHE_TTL_MS}" (expected a non-negative number of milliseconds; 0 disables caching) — using the default TTL`);
5662
+ }
5663
+ const documentPermissionsRequested = options.documentPermissionService !== void 0 || DOCUMENT_PERMISSIONS_ENABLED === "true";
5664
+ assertAuthRequiredForDocumentPermissions(authEnabled, documentPermissionsRequested);
5665
+ assertSkipCredentialVerificationAllowed(authEnabled, skipCredentialVerification, process.env);
5666
+ if (authEnabled && skipCredentialVerification) logger.warn("SECURITY: SKIP_CREDENTIAL_VERIFICATION is enabled — Renown credential verification is disabled and a bearer token's claimed address is NOT cryptographically bound to its signing key. Identity is unverifiable; use only in development or test.");
5883
5667
  httpAdapter.getRoute("/health", () => new Response("OK", { status: 200 }));
5884
5668
  const readiness = createReadinessGate();
5885
5669
  httpAdapter.getRoute("/ready", () => readiness.isReady() ? new Response("OK", { status: 200 }) : new Response("starting", { status: 503 }));
@@ -5898,7 +5682,8 @@ async function _setupCommonInfrastructure(options) {
5898
5682
  authService = new AuthService({
5899
5683
  enabled: authEnabled,
5900
5684
  admins,
5901
- skipCredentialVerification
5685
+ skipCredentialVerification,
5686
+ credentialVerificationCacheTtlMs
5902
5687
  });
5903
5688
  authFetchMiddleware = createAuthFetchMiddleware(authService);
5904
5689
  }
@@ -5914,14 +5699,12 @@ async function _setupCommonInfrastructure(options) {
5914
5699
  documentPermissionService = new DocumentPermissionService(db, { defaultProtection });
5915
5700
  logger.info("Document permission service initialized");
5916
5701
  }
5917
- let authorizationService;
5918
- if (documentPermissionService) {
5919
- authorizationService = new AuthorizationService(documentPermissionService, {
5920
- admins,
5921
- defaultProtection
5922
- });
5923
- logger.info("Authorization service initialized");
5924
- }
5702
+ const policy = documentPermissionService ? AuthorizationPolicy.DOCUMENT_PERMISSIONS : authEnabled ? AuthorizationPolicy.ADMIN_ONLY : AuthorizationPolicy.OPEN;
5703
+ const authorizationConfig = {
5704
+ admins,
5705
+ defaultProtection,
5706
+ policy
5707
+ };
5925
5708
  const attachmentStoragePath = resolveAttachmentStoragePath(options);
5926
5709
  await mkdir(attachmentStoragePath, { recursive: true });
5927
5710
  const { db: attachmentDb, knex: attachmentKnex, pglite: attachmentPglite } = getDbClient(options.dbPath, options.pgliteFactory);
@@ -5941,14 +5724,10 @@ async function _setupCommonInfrastructure(options) {
5941
5724
  httpAdapter,
5942
5725
  authFetchMiddleware,
5943
5726
  authService,
5944
- auth: {
5945
- enabled: authEnabled,
5946
- admins
5947
- },
5948
5727
  relationalDb,
5949
5728
  analyticsStore,
5950
5729
  documentPermissionService,
5951
- authorizationService,
5730
+ authorizationConfig,
5952
5731
  attachments,
5953
5732
  packages,
5954
5733
  dbClosers,
@@ -5958,7 +5737,7 @@ async function _setupCommonInfrastructure(options) {
5958
5737
  /**
5959
5738
  * Private helper function containing common setup logic for API initialization
5960
5739
  */
5961
- async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, auth, processorApp, readModels, attachments, authorizationService, documentModelRegistry, dbClosers = [], reactorDriveClient) {
5740
+ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
5962
5741
  const hostModule = {
5963
5742
  relationalDb,
5964
5743
  analyticsStore,
@@ -6003,6 +5782,8 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
6003
5782
  }))).flat());
6004
5783
  }
6005
5784
  const { httpServer, wsServer } = await startServer(httpAdapter, port, options.https, logger);
5785
+ const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
5786
+ logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
6006
5787
  const coreSubgraphs = DefaultCoreSubgraphs.slice();
6007
5788
  coreSubgraphs.push(ReactorSubgraph);
6008
5789
  if (documentPermissionService) {
@@ -6012,12 +5793,13 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
6012
5793
  const graphqlManager = await setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, {
6013
5794
  extended: subgraphs,
6014
5795
  core: coreSubgraphs
6015
- }, logger.child(["graphql-manager"]), auth, documentPermissionService, options.enableDocumentModelSubgraphs, port, authorizationService, reactorDriveClient);
5796
+ }, logger.child(["graphql-manager"]), authorizationService, authService, documentPermissionService, options.enableDocumentModelSubgraphs, port, reactorDriveClient);
6016
5797
  setupEventListeners(packages, graphqlManager, reactorProcessorManager, hostModule, documentModelRegistry);
6017
5798
  if (mcpServerEnabled) {
6018
5799
  await setupMcpServer({
6019
5800
  client: reactorClient,
6020
- syncManager
5801
+ syncManager,
5802
+ authorizeRequest: createMcpRequestAuthorizer(authService, authorizationService)
6021
5803
  }, httpAdapter);
6022
5804
  logger.info(`MCP server available at http://localhost:${port}/mcp`);
6023
5805
  }
@@ -6074,18 +5856,18 @@ function buildApiDispose(args) {
6074
5856
  };
6075
5857
  }
6076
5858
  async function initializeAndStartAPI(clientInitializer, options, processorApp) {
6077
- const { port, httpAdapter, authFetchMiddleware, authService, auth, relationalDb, analyticsStore, documentPermissionService, authorizationService, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
5859
+ const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
6078
5860
  const { documentModels, processors, subgraphs } = await packages.init();
6079
5861
  const { module: reactorClientModule, reactorDriveClient } = await clientInitializer(documentModels);
6080
5862
  const reactorClient = reactorClientModule.client;
6081
5863
  const syncManager = reactorClientModule.reactorModule?.syncModule?.syncManager;
6082
- if (!syncManager) throw new Error("SyncManager not available from ReactorClientModule");
5864
+ if (!syncManager) throw new Error("SyncManager not available from InProcessReactorClientModule");
6083
5865
  const reactorProcessorManager = reactorClientModule.reactorModule?.processorManager;
6084
- if (!reactorProcessorManager) throw new Error("ProcessorManager not available from ReactorClientModule");
5866
+ if (!reactorProcessorManager) throw new Error("ProcessorManager not available from InProcessReactorClientModule");
6085
5867
  const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
6086
- if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from ReactorClientModule");
5868
+ if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from InProcessReactorClientModule");
6087
5869
  return {
6088
- ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, auth, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, authorizationService, documentModelRegistry, dbClosers, reactorDriveClient),
5870
+ ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient),
6089
5871
  client: reactorClient,
6090
5872
  syncManager,
6091
5873
  documentModelRegistry,
@@ -6193,7 +5975,7 @@ var PackageManagementService = class {
6193
5975
  }
6194
5976
  };
6195
5977
  //#endregion
6196
- export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AuthService, AuthSubgraph, BaseSubgraph, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
5978
+ export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
6197
5979
 
6198
5980
  //# sourceMappingURL=index.mjs.map
6199
- //# debugId=5769db0f-d60a-5870-9d50-dc4f5aca8086
5981
+ //# debugId=6300f5e6-16a2-5335-a19f-9064a271f52d