@powerhousedao/reactor-api 6.2.2-dev.5 → 6.2.2-dev.50

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,11 +1,11 @@
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]="e58cea2c-2319-52a8-90cd-3e99d94ba98f")}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";
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]="0fa604a4-1713-5d00-989b-ce10e0cf5cdb")}catch(e){}}();
3
+ import { _ as createAuthorizationService, a as isSubgraphClass, d as loadSubgraphs, f as BaseSubgraph, g as AuthorizedDocumentHandle, h as AuthorizationPolicy, i as buildGraphqlOperations, l as loadDocumentModels, m as createCanonicalDocumentIdResolver, n as buildGraphQlDriveDocument, o as debounce, p as CanonicalDocumentIdResolutionError, r as buildGraphqlOperation, s as extractUpgradeManifests, t as buildGraphQlDocument, u as loadProcessors } from "./utils-bcfC5Kc1.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
+ import { DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, syncOperationErrorType } from "@powerhousedao/reactor";
9
9
  import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
10
10
  import path from "node:path";
11
11
  import { match } from "path-to-regexp";
@@ -22,11 +22,12 @@ import dotenv from "dotenv";
22
22
  import { buildTreeUrl } from "@powerhousedao/shared";
23
23
  import { getConfig } from "@powerhousedao/config/node";
24
24
  import { reactorDriveDocumentModelModule } from "@powerhousedao/reactor-drive";
25
+ import { ReactorGroupV1 } from "@powerhousedao/reactor-group";
25
26
  import { driveDocumentModelModule } from "@powerhousedao/shared/document-drive";
26
27
  import EventEmitter from "node:events";
27
28
  import fs, { watchFile } from "node:fs";
28
29
  import { PostgresAnalyticsStore } from "@powerhousedao/analytics-engine-pg";
29
- import { AttachmentBuilder } from "@powerhousedao/reactor-attachments";
30
+ import { AttachmentBuilder, AttachmentReferenceIndexBuilder, createRef, createS3AttachmentBackend, parseAttachmentStorageConfig, parseRef } from "@powerhousedao/reactor-attachments";
30
31
  import { createAttachmentClient } from "@powerhousedao/reactor-attachments/client";
31
32
  import { setupMcpServer } from "@powerhousedao/reactor-mcp";
32
33
  import { mkdir } from "node:fs/promises";
@@ -34,7 +35,7 @@ import { tmpdir } from "node:os";
34
35
  import { WebSocketServer } from "ws";
35
36
  import { createRelationalDb } from "@powerhousedao/shared/processors";
36
37
  import { Kysely, Migrator, sql } from "kysely";
37
- import { fetchDelegationCredential, verifyAuthBearerToken } from "@renown/sdk";
38
+ import { fetchDelegationCredential, resolveSwitchboardEndpoint, verifyAuthBearerToken } from "@renown/sdk";
38
39
  import { PGlite } from "@electric-sql/pglite";
39
40
  import { AtomicNodeFs } from "@powerhousedao/pglite-fs";
40
41
  import knex from "knex";
@@ -460,6 +461,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
460
461
  });
461
462
  return dedupeTypeDefinitions(gql`
462
463
  scalar JSONObject
464
+ scalar AttachmentRef
463
465
  ${typeDefs.join("\n").replaceAll(";", "")}
464
466
 
465
467
  type PHOperationContext {
@@ -878,6 +880,7 @@ function generateNewApiSchema(documentName, specification, _stateSchemaTypes, pr
878
880
  return gql`
879
881
  scalar DateTime
880
882
  scalar JSONObject
883
+ scalar AttachmentRef
881
884
 
882
885
  ${revisionType}
883
886
 
@@ -989,6 +992,7 @@ function OperationContextInputSchema() {
989
992
  function OperationInputSchema() {
990
993
  return z$1.object({
991
994
  action: z$1.lazy(() => ActionInputSchema()),
995
+ deniedReason: z$1.string().nullish(),
992
996
  error: z$1.string().nullish(),
993
997
  hash: z$1.string(),
994
998
  id: z$1.string().nullish(),
@@ -1147,6 +1151,7 @@ const GetDocumentWithOperationsDocument = gql`
1147
1151
  hash
1148
1152
  skip
1149
1153
  error
1154
+ deniedReason
1150
1155
  id
1151
1156
  action {
1152
1157
  id
@@ -1259,6 +1264,7 @@ const GetDocumentOperationsDocument = gql`
1259
1264
  hash
1260
1265
  skip
1261
1266
  error
1267
+ deniedReason
1262
1268
  id
1263
1269
  action {
1264
1270
  id
@@ -1509,6 +1515,7 @@ const PollSyncEnvelopesDocument = gql`
1509
1515
  hash
1510
1516
  skip
1511
1517
  error
1518
+ deniedReason
1512
1519
  id
1513
1520
  action {
1514
1521
  id
@@ -1551,6 +1558,7 @@ const PollSyncEnvelopesDocument = gql`
1551
1558
  deadLetters {
1552
1559
  documentId
1553
1560
  error
1561
+ errorType
1554
1562
  }
1555
1563
  hasMore
1556
1564
  }
@@ -1904,9 +1912,10 @@ async function documentModels(reactorClient, args) {
1904
1912
  throw new GraphQLError(`Failed to convert document models to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1905
1913
  }
1906
1914
  }
1907
- async function document(reactorClient, args) {
1908
- let view;
1915
+ async function document(reactorClient, args, subject) {
1916
+ let view = subject ? { subject } : void 0;
1909
1917
  if (args.view) view = {
1918
+ subject,
1910
1919
  branch: fromInputMaybe(args.view.branch),
1911
1920
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1912
1921
  };
@@ -1931,9 +1940,10 @@ async function document(reactorClient, args) {
1931
1940
  throw new GraphQLError(`Failed to convert document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1932
1941
  }
1933
1942
  }
1934
- async function documentOutgoingRelationships(reactorClient, args) {
1935
- let view;
1943
+ async function documentOutgoingRelationships(reactorClient, args, subject) {
1944
+ let view = subject ? { subject } : void 0;
1936
1945
  if (args.view) view = {
1946
+ subject,
1937
1947
  branch: fromInputMaybe(args.view.branch),
1938
1948
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1939
1949
  };
@@ -1958,9 +1968,10 @@ async function documentOutgoingRelationships(reactorClient, args) {
1958
1968
  throw new GraphQLError(`Failed to convert outgoing relationships to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1959
1969
  }
1960
1970
  }
1961
- async function documentIncomingRelationships(reactorClient, args) {
1962
- let view;
1971
+ async function documentIncomingRelationships(reactorClient, args, subject) {
1972
+ let view = subject ? { subject } : void 0;
1963
1973
  if (args.view) view = {
1974
+ subject,
1964
1975
  branch: fromInputMaybe(args.view.branch),
1965
1976
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1966
1977
  };
@@ -1985,9 +1996,10 @@ async function documentIncomingRelationships(reactorClient, args) {
1985
1996
  throw new GraphQLError(`Failed to convert incoming relationships to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1986
1997
  }
1987
1998
  }
1988
- async function findDocuments(reactorClient, args) {
1989
- let view;
1999
+ async function findDocuments(reactorClient, args, subject) {
2000
+ let view = subject ? { subject } : void 0;
1990
2001
  if (args.view) view = {
2002
+ subject,
1991
2003
  branch: fromInputMaybe(args.view.branch),
1992
2004
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1993
2005
  };
@@ -2029,11 +2041,12 @@ async function jobStatus(reactorClient, args) {
2029
2041
  throw new GraphQLError(`Failed to convert job status to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
2030
2042
  }
2031
2043
  }
2032
- async function documentOperations(reactorClient, args) {
2033
- let view;
2044
+ async function documentOperations(reactorClient, args, subject) {
2045
+ let view = subject ? { subject } : void 0;
2034
2046
  const branch = fromInputMaybe(args.filter.branch);
2035
2047
  const scopes = toMutableArray(fromInputMaybe(args.filter.scopes));
2036
2048
  if (branch || scopes) view = {
2049
+ subject,
2037
2050
  branch,
2038
2051
  scopes
2039
2052
  };
@@ -2347,6 +2360,7 @@ function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new
2347
2360
  const deadLetters = remote.channel.deadLetter.items.filter((syncOp) => !forbiddenIds.has(syncOp.documentId)).map((syncOp) => ({
2348
2361
  documentId: syncOp.documentId,
2349
2362
  error: syncOp.error?.message ?? "Unknown error",
2363
+ errorType: syncOperationErrorType(syncOp.error),
2350
2364
  jobId: syncOp.jobId,
2351
2365
  branch: syncOp.branch,
2352
2366
  scopes: syncOp.scopes,
@@ -3631,7 +3645,7 @@ function createReactorGraphQLClient(url, fetchImpl = fetch, headers) {
3631
3645
  }
3632
3646
  //#endregion
3633
3647
  //#region src/graphql/reactor/schema.graphql
3634
- var schema_default = "# Scalar types (for codegen - also defined in create-schema.ts)\nscalar JSONObject\nscalar DateTime\n\n# Input types\ninput PagingInput {\n limit: Int\n offset: Int\n cursor: String\n}\n\ninput ViewFilterInput {\n branch: String\n scopes: [String!]\n}\n\ninput SearchFilterInput {\n type: String\n parentId: String\n identifiers: [String!]\n}\n\ninput OperationsFilterInput {\n documentId: String!\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\ninput DocumentOperationsFilterInput {\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\n# Enums\nenum PropagationMode {\n CASCADE\n ORPHAN\n}\n\nenum DocumentChangeType {\n CREATED\n DELETED\n UPDATED\n PARENT_ADDED\n PARENT_REMOVED\n CHILD_ADDED\n CHILD_REMOVED\n}\n\n# Object types\ntype DocumentModelGlobalState {\n id: String!\n name: String!\n namespace: String\n version: String\n specification: JSONObject!\n}\n\ntype DocumentModelResultPage {\n items: [DocumentModelGlobalState!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype Revision {\n scope: String!\n revision: Int!\n}\n\ntype PHDocument {\n id: String!\n slug: String\n preferredEditor: String\n name: String!\n documentType: String!\n state: JSONObject!\n revisionsList: [Revision!]!\n createdAtUtcIso: DateTime!\n lastModifiedAtUtcIso: DateTime!\n operations(\n filter: DocumentOperationsFilterInput\n paging: PagingInput\n ): ReactorOperationResultPage\n}\n\ntype PHDocumentResultPage {\n items: [PHDocument!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype DeadLetterInfo {\n documentId: String!\n error: String!\n jobId: String!\n branch: String!\n scopes: [String!]!\n operationCount: Int!\n}\n\ntype PollSyncEnvelopesResult {\n envelopes: [SyncEnvelope!]!\n ackOrdinal: Int!\n deadLetters: [DeadLetterInfo!]!\n hasMore: Boolean!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype JobInfo {\n id: String!\n status: String!\n result: JSONObject!\n error: String\n createdAt: DateTime!\n completedAt: DateTime\n}\n\ntype DocumentChangeEvent {\n type: DocumentChangeType!\n documents: [PHDocument!]!\n context: DocumentChangeContext\n}\n\ntype DocumentChangeContext {\n parentId: String\n childId: String\n}\n\ntype JobChangeEvent {\n jobId: String!\n status: String!\n result: JSONObject!\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n# Input types for sync operations\ninput ActionContextInput {\n signer: ReactorSignerInput\n}\n\ninput ReactorSignerInput {\n user: ReactorSignerUserInput\n app: ReactorSignerAppInput\n signatures: [String!]!\n}\n\ninput ReactorSignerUserInput {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ninput ReactorSignerAppInput {\n name: String!\n key: String!\n}\n\ninput ActionInput {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContextInput\n}\n\n# Synchronization types\ntype ReactorOperation {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n id: String\n action: Action!\n}\n\ninput OperationInput {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n id: String\n action: ActionInput!\n}\n\ntype OperationContext {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ninput OperationContextInput {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ntype OperationWithContext {\n operation: ReactorOperation!\n context: OperationContext!\n}\n\ninput OperationWithContextInput {\n operation: OperationInput!\n context: OperationContextInput!\n}\n\ntype ChannelMeta {\n id: String!\n}\n\ninput ChannelMetaInput {\n id: String!\n}\n\ntype RemoteCursor {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\ninput RemoteCursorInput {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\nenum SyncEnvelopeType {\n OPERATIONS\n ACK\n}\n\ntype SyncEnvelope {\n type: SyncEnvelopeType!\n channelMeta: ChannelMeta!\n operations: [OperationWithContext!]\n cursor: RemoteCursor\n key: String\n dependsOn: [String!]\n}\n\ninput SyncEnvelopeInput {\n type: SyncEnvelopeType!\n channelMeta: ChannelMetaInput!\n operations: [OperationWithContextInput!]\n cursor: RemoteCursorInput\n key: String\n dependsOn: [String!]\n}\n\ninput RemoteFilterInput {\n documentId: [String!]!\n scope: [String!]!\n branch: String!\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n}\n\ntype Query {\n # Get document models for a namespace\n documentModels(\n namespace: String\n paging: PagingInput\n ): DocumentModelResultPage!\n\n # Get a specific document by ID or slug\n document(identifier: String!, view: ViewFilterInput): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get incoming relationships of a given type to a target document\n documentIncomingRelationships(\n targetIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n # Poll for sync envelopes from a channel\n pollSyncEnvelopes(\n channelId: String!\n outboxAck: Int!\n outboxLatest: Int!\n ): PollSyncEnvelopesResult!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(document: JSONObject!, parentIdentifier: String): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdentifier: String\n ): PHDocument!\n\n # Apply actions to a document (synchronous)\n mutateDocument(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): PHDocument!\n\n # Submit actions to a document (asynchronous)\n mutateDocumentAsync(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): String!\n\n # Rename a document\n renameDocument(\n documentIdentifier: String!\n name: String!\n branch: String\n ): PHDocument!\n\n # Update the preferred editor recorded in the document header meta.\n # Pass null/omit to clear it.\n setPreferredEditor(\n documentIdentifier: String!\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdentifier: String!\n targetParentIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(identifier: String!, propagate: PropagationMode): Boolean!\n\n # Delete multiple documents\n deleteDocuments(identifiers: [String!]!, propagate: PropagationMode): Boolean!\n\n # Touch (create or update) a channel for sync\n touchChannel(input: TouchChannelInput!): TouchChannelResult!\n\n # Push sync envelopes to a channel\n pushSyncEnvelopes(envelopes: [SyncEnvelopeInput!]!): Boolean!\n}\n\ntype Subscription {\n # Subscribe to document changes\n documentChanges(\n search: SearchFilterInput\n view: ViewFilterInput\n ): DocumentChangeEvent!\n\n # Subscribe to job changes\n jobChanges(jobId: String!): JobChangeEvent!\n}\n";
3648
+ var schema_default = "# Scalar types (for codegen - also defined in create-schema.ts)\nscalar JSONObject\nscalar DateTime\n\n# Input types\ninput PagingInput {\n limit: Int\n offset: Int\n cursor: String\n}\n\ninput ViewFilterInput {\n branch: String\n scopes: [String!]\n}\n\ninput SearchFilterInput {\n type: String\n parentId: String\n identifiers: [String!]\n}\n\ninput OperationsFilterInput {\n documentId: String!\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\ninput DocumentOperationsFilterInput {\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\n# Enums\nenum PropagationMode {\n CASCADE\n ORPHAN\n}\n\nenum DocumentChangeType {\n CREATED\n DELETED\n UPDATED\n PARENT_ADDED\n PARENT_REMOVED\n CHILD_ADDED\n CHILD_REMOVED\n}\n\n# Object types\ntype DocumentModelGlobalState {\n id: String!\n name: String!\n namespace: String\n version: String\n specification: JSONObject!\n}\n\ntype DocumentModelResultPage {\n items: [DocumentModelGlobalState!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype Revision {\n scope: String!\n revision: Int!\n}\n\ntype PHDocument {\n id: String!\n slug: String\n preferredEditor: String\n name: String!\n documentType: String!\n state: JSONObject!\n revisionsList: [Revision!]!\n createdAtUtcIso: DateTime!\n lastModifiedAtUtcIso: DateTime!\n operations(\n filter: DocumentOperationsFilterInput\n paging: PagingInput\n ): ReactorOperationResultPage\n}\n\ntype PHDocumentResultPage {\n items: [PHDocument!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype DeadLetterInfo {\n documentId: String!\n error: String!\n \"\"\"\n How the origin classified the failure. A peer needs this to mirror the origin's\n quarantine decision; without it a held auth operation would freeze the document\n on the peer side while the origin kept syncing.\n \"\"\"\n errorType: String\n jobId: String!\n branch: String!\n scopes: [String!]!\n operationCount: Int!\n}\n\ntype PollSyncEnvelopesResult {\n envelopes: [SyncEnvelope!]!\n ackOrdinal: Int!\n deadLetters: [DeadLetterInfo!]!\n hasMore: Boolean!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype JobInfo {\n id: String!\n status: String!\n result: JSONObject!\n error: String\n createdAt: DateTime!\n completedAt: DateTime\n}\n\ntype DocumentChangeEvent {\n type: DocumentChangeType!\n documents: [PHDocument!]!\n context: DocumentChangeContext\n}\n\ntype DocumentChangeContext {\n parentId: String\n childId: String\n}\n\ntype JobChangeEvent {\n jobId: String!\n status: String!\n result: JSONObject!\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n# Input types for sync operations\ninput ActionContextInput {\n signer: ReactorSignerInput\n}\n\ninput ReactorSignerInput {\n user: ReactorSignerUserInput\n app: ReactorSignerAppInput\n signatures: [String!]!\n}\n\ninput ReactorSignerUserInput {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ninput ReactorSignerAppInput {\n name: String!\n key: String!\n}\n\ninput ActionInput {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContextInput\n}\n\n# Synchronization types\ntype ReactorOperation {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n deniedReason: String\n id: String\n action: Action!\n}\n\ninput OperationInput {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n deniedReason: String\n id: String\n action: ActionInput!\n}\n\ntype OperationContext {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ninput OperationContextInput {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ntype OperationWithContext {\n operation: ReactorOperation!\n context: OperationContext!\n}\n\ninput OperationWithContextInput {\n operation: OperationInput!\n context: OperationContextInput!\n}\n\ntype ChannelMeta {\n id: String!\n}\n\ninput ChannelMetaInput {\n id: String!\n}\n\ntype RemoteCursor {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\ninput RemoteCursorInput {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\nenum SyncEnvelopeType {\n OPERATIONS\n ACK\n}\n\ntype SyncEnvelope {\n type: SyncEnvelopeType!\n channelMeta: ChannelMeta!\n operations: [OperationWithContext!]\n cursor: RemoteCursor\n key: String\n dependsOn: [String!]\n}\n\ninput SyncEnvelopeInput {\n type: SyncEnvelopeType!\n channelMeta: ChannelMetaInput!\n operations: [OperationWithContextInput!]\n cursor: RemoteCursorInput\n key: String\n dependsOn: [String!]\n}\n\ninput RemoteFilterInput {\n documentId: [String!]!\n scope: [String!]!\n branch: String!\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n}\n\ntype Query {\n # Get document models for a namespace\n documentModels(\n namespace: String\n paging: PagingInput\n ): DocumentModelResultPage!\n\n # Get a specific document by ID or slug\n document(identifier: String!, view: ViewFilterInput): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get incoming relationships of a given type to a target document\n documentIncomingRelationships(\n targetIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n # Poll for sync envelopes from a channel\n pollSyncEnvelopes(\n channelId: String!\n outboxAck: Int!\n outboxLatest: Int!\n ): PollSyncEnvelopesResult!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(document: JSONObject!, parentIdentifier: String): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdentifier: String\n ): PHDocument!\n\n # Apply actions to a document (synchronous)\n mutateDocument(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): PHDocument!\n\n # Submit actions to a document (asynchronous)\n mutateDocumentAsync(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): String!\n\n # Rename a document\n renameDocument(\n documentIdentifier: String!\n name: String!\n branch: String\n ): PHDocument!\n\n # Update the preferred editor recorded in the document header meta.\n # Pass null/omit to clear it.\n setPreferredEditor(\n documentIdentifier: String!\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdentifier: String!\n targetParentIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(identifier: String!, propagate: PropagationMode): Boolean!\n\n # Delete multiple documents\n deleteDocuments(identifiers: [String!]!, propagate: PropagationMode): Boolean!\n\n # Touch (create or update) a channel for sync\n touchChannel(input: TouchChannelInput!): TouchChannelResult!\n\n # Push sync envelopes to a channel\n pushSyncEnvelopes(envelopes: [SyncEnvelopeInput!]!): Boolean!\n}\n\ntype Subscription {\n # Subscribe to document changes\n documentChanges(\n search: SearchFilterInput\n view: ViewFilterInput\n ): DocumentChangeEvent!\n\n # Subscribe to job changes\n jobChanges(jobId: String!): JobChangeEvent!\n}\n";
3635
3649
  //#endregion
3636
3650
  //#region src/graphql/reactor/pubsub.ts
3637
3651
  const pubSub = new PubSub();
@@ -3787,7 +3801,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3787
3801
  return await documentOperations(this.reactorClient, {
3788
3802
  filter,
3789
3803
  paging: args.paging
3790
- });
3804
+ }, this.viewSubject(ctx));
3791
3805
  } catch (error) {
3792
3806
  this.logger.error("Error in PHDocument.operations: @Error", error);
3793
3807
  throw error;
@@ -3810,7 +3824,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3810
3824
  return await document(this.reactorClient, {
3811
3825
  ...args,
3812
3826
  identifier: handle.fetchIdentifier
3813
- });
3827
+ }, this.viewSubject(ctx));
3814
3828
  } catch (error) {
3815
3829
  this.logger.error("Error in document: @Error", error);
3816
3830
  throw error;
@@ -3823,7 +3837,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3823
3837
  return await documentOutgoingRelationships(this.reactorClient, {
3824
3838
  ...args,
3825
3839
  sourceIdentifier: handle.fetchIdentifier
3826
- });
3840
+ }, this.viewSubject(ctx));
3827
3841
  } catch (error) {
3828
3842
  this.logger.error("Error in documentOutgoingRelationships: @Error", error);
3829
3843
  throw error;
@@ -3836,7 +3850,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3836
3850
  const result = await documentIncomingRelationships(this.reactorClient, {
3837
3851
  ...args,
3838
3852
  targetIdentifier: handle.fetchIdentifier
3839
- });
3853
+ }, this.viewSubject(ctx));
3840
3854
  if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
3841
3855
  const filteredItems = [];
3842
3856
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
@@ -3857,7 +3871,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3857
3871
  const result = await findDocuments(this.reactorClient, {
3858
3872
  ...args,
3859
3873
  search: args.search ?? {}
3860
- });
3874
+ }, this.viewSubject(ctx));
3861
3875
  if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
3862
3876
  const filteredItems = [];
3863
3877
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
@@ -3891,7 +3905,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3891
3905
  ...args.filter,
3892
3906
  documentId: handle.fetchIdentifier
3893
3907
  }
3894
- });
3908
+ }, this.viewSubject(ctx));
3895
3909
  } catch (error) {
3896
3910
  this.logger.error("Error in documentOperations: @Error", error);
3897
3911
  throw error;
@@ -4205,10 +4219,10 @@ const ADMIN_USERS = getAdminUsers();
4205
4219
  //#endregion
4206
4220
  //#region src/graphql/system/version.ts
4207
4221
  function getVersion() {
4208
- return "6.2.2-dev.5";
4222
+ return "6.2.2-dev.50";
4209
4223
  }
4210
4224
  function getGitHash() {
4211
- return "3e3d6f16e8cc52be039a91438fcb45a85afb08a9";
4225
+ return "3c0b735b95e21535a33d3cbe71f7478bd68c45f1";
4212
4226
  }
4213
4227
  function getGitUrl() {
4214
4228
  return buildTreeUrl(getGitHash());
@@ -4306,6 +4320,13 @@ var HttpPackageLoader = class {
4306
4320
  this.logger.verbose(`Loaded ${models.length} document models from ${packageName}`);
4307
4321
  return models;
4308
4322
  }
4323
+ async loadUpgradeManifests(packageSpec) {
4324
+ const { name: packageName } = this.parsePackageSpec(packageSpec);
4325
+ if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
4326
+ const manifests = extractUpgradeManifests(await import(`${this.registryUrl}-/cdn/${packageSpec}/node/document-models/index.mjs`));
4327
+ if (manifests.length > 0) this.logger.verbose(`Loaded ${manifests.length} upgrade manifests from ${packageName}`);
4328
+ return manifests;
4329
+ }
4309
4330
  async loadSubgraphs(packageSpec) {
4310
4331
  const { name: packageName } = this.parsePackageSpec(packageSpec);
4311
4332
  if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
@@ -4431,7 +4452,7 @@ var ImportPackageLoader = class {
4431
4452
  this.logger.verbose(`Loading document models from package: ${identifier}`);
4432
4453
  const pkgModule = await loadDocumentModels(identifier);
4433
4454
  if (pkgModule) {
4434
- const models = Object.values(pkgModule);
4455
+ const models = Object.values(pkgModule).filter((m) => m !== null && typeof m === "object" && "documentModel" in m);
4435
4456
  this.logger.verbose(` ➜ Loaded ${models.length} Document Models from: ${identifier}`);
4436
4457
  return models;
4437
4458
  } else {
@@ -4439,6 +4460,13 @@ var ImportPackageLoader = class {
4439
4460
  return [];
4440
4461
  }
4441
4462
  }
4463
+ async loadUpgradeManifests(identifier) {
4464
+ const pkgModule = await loadDocumentModels(identifier);
4465
+ if (!pkgModule) return [];
4466
+ const manifests = extractUpgradeManifests(pkgModule);
4467
+ if (manifests.length > 0) this.logger.verbose(` ➜ Loaded ${manifests.length} Upgrade Manifests from: ${identifier}`);
4468
+ return manifests;
4469
+ }
4442
4470
  async loadSubgraphs(identifier) {
4443
4471
  this.logger.verbose(`Loading subgraphs from package: ${identifier}`);
4444
4472
  const pkgModule = await loadSubgraphs(identifier);
@@ -4487,13 +4515,19 @@ function isExpectedLoaderMiss(error, pkg) {
4487
4515
  }
4488
4516
  function getUniqueDocumentModels(...documentModels) {
4489
4517
  const uniqueModels = /* @__PURE__ */ new Map();
4490
- for (const models of documentModels) for (const model of models) uniqueModels.set(model.documentModel.global.id, model);
4518
+ for (const models of documentModels) for (const model of models) uniqueModels.set(`${model.documentModel.global.id}@${model.version ?? 1}`, model);
4491
4519
  return Array.from(uniqueModels.values());
4492
4520
  }
4521
+ function getUniqueUpgradeManifests(...upgradeManifests) {
4522
+ const uniqueManifests = /* @__PURE__ */ new Map();
4523
+ for (const manifests of upgradeManifests) for (const manifest of manifests) uniqueManifests.set(manifest.documentType, manifest);
4524
+ return Array.from(uniqueManifests.values());
4525
+ }
4493
4526
  var PackageManager = class {
4494
4527
  logger = childLogger(["reactor-api", "package-manager"]);
4495
4528
  loaders;
4496
4529
  docModelsMap = /* @__PURE__ */ new Map();
4530
+ upgradeManifestsMap = /* @__PURE__ */ new Map();
4497
4531
  subgraphsMap = /* @__PURE__ */ new Map();
4498
4532
  processorMap = /* @__PURE__ */ new Map();
4499
4533
  configWatcher;
@@ -4511,8 +4545,10 @@ var PackageManager = class {
4511
4545
  async loadPackages(packages) {
4512
4546
  this.logger.info("Loading packages: @packages", packages.join(", "));
4513
4547
  const documentModelsMap = await this.loadDocumentModels(packages);
4548
+ const upgradeManifestsMap = await this.loadUpgradeManifests(packages);
4514
4549
  const subgraphsMap = await this.loadSubgraphs(packages);
4515
4550
  const processorsMap = await this.loadProcessors(packages);
4551
+ this.upgradeManifestsMap = upgradeManifestsMap;
4516
4552
  this.updatePackagesMap(documentModelsMap);
4517
4553
  this.updateSubgraphsMap(subgraphsMap);
4518
4554
  this.updateProcessorsMap(processorsMap);
@@ -4523,6 +4559,7 @@ var PackageManager = class {
4523
4559
  }
4524
4560
  return {
4525
4561
  documentModels: getUniqueDocumentModels(...Array.from(documentModelsMap.values())),
4562
+ upgradeManifests: this.getUniqueUpgradeManifests(),
4526
4563
  subgraphs: subgraphsMap,
4527
4564
  processors: processorsMap
4528
4565
  };
@@ -4533,6 +4570,7 @@ var PackageManager = class {
4533
4570
  documentModelModuleMap.set("document-drive", [driveDocumentModelModule]);
4534
4571
  documentModelModuleMap.set("document-model", [documentModelDocumentModelModule]);
4535
4572
  documentModelModuleMap.set("reactor-drive", [reactorDriveDocumentModelModule]);
4573
+ documentModelModuleMap.set("reactor-group", [ReactorGroupV1]);
4536
4574
  for (const pkg of packages) {
4537
4575
  const allDocumentModels = [];
4538
4576
  const failures = [];
@@ -4555,6 +4593,26 @@ var PackageManager = class {
4555
4593
  }
4556
4594
  return documentModelModuleMap;
4557
4595
  }
4596
+ /** Upgrade manifests currently loaded across all packages, one per type. */
4597
+ getUniqueUpgradeManifests() {
4598
+ return getUniqueUpgradeManifests(...Array.from(this.upgradeManifestsMap.values()));
4599
+ }
4600
+ async loadUpgradeManifests(packages) {
4601
+ const manifestsMap = /* @__PURE__ */ new Map();
4602
+ for (const pkg of packages) for (const loader of this.loaders) {
4603
+ if (!loader.loadUpgradeManifests) continue;
4604
+ try {
4605
+ const manifests = await loader.loadUpgradeManifests(pkg);
4606
+ if (manifests.length > 0) {
4607
+ manifestsMap.set(pkg, manifests);
4608
+ break;
4609
+ }
4610
+ } catch (error) {
4611
+ this.logger.debug(`[${loader.name}] Failed to load upgrade manifests from package @pkg: @error`, pkg, error);
4612
+ }
4613
+ }
4614
+ return manifestsMap;
4615
+ }
4558
4616
  async loadSubgraphs(packages) {
4559
4617
  this.logger.debug(`Loading subgraphs from packages: ${packages.join(", ")}`);
4560
4618
  const subgraphsMap = /* @__PURE__ */ new Map();
@@ -4616,6 +4674,10 @@ var PackageManager = class {
4616
4674
  async updateDocumentModelsForPackage(pkg) {
4617
4675
  this.logger.debug(`Updating document models for package: ${pkg}`);
4618
4676
  const documentModels = await this.loadDocumentModels([pkg]);
4677
+ const upgradeManifests = await this.loadUpgradeManifests([pkg]);
4678
+ const upgradeManifestsMap = new Map(this.upgradeManifestsMap);
4679
+ upgradeManifestsMap.set(pkg, upgradeManifests.get(pkg) ?? []);
4680
+ this.upgradeManifestsMap = upgradeManifestsMap;
4619
4681
  const documentModelsMap = new Map(this.docModelsMap);
4620
4682
  documentModelsMap.set(pkg, documentModels.get(pkg) ?? []);
4621
4683
  this.updatePackagesMap(documentModelsMap);
@@ -4695,6 +4757,70 @@ var PackageManager = class {
4695
4757
  const config = { basePath: process.env.BASE_PATH || "/" };
4696
4758
  const DefaultCoreSubgraphs = [AnalyticsSubgraph, SystemSubgraph];
4697
4759
  //#endregion
4760
+ //#region src/attachment-backend.ts
4761
+ /** Filesystem is the default; explicit S3 is readiness-gated without fallback. */
4762
+ async function createStartupAttachmentBackend({ db, env = process.env, s3Dependencies }) {
4763
+ const storage = parseAttachmentStorageConfig(env);
4764
+ if (storage.kind === "filesystem") return void 0;
4765
+ const backend = createS3AttachmentBackend(db.withSchema("attachments"), storage.s3, s3Dependencies);
4766
+ if (!(await backend.health()).ready) throw new Error("S3 attachment backend readiness check failed");
4767
+ return backend;
4768
+ }
4769
+ //#endregion
4770
+ //#region src/services/attachment-access.service.ts
4771
+ const HASH_PATTERN = /^[a-f0-9]{64}$/;
4772
+ /**
4773
+ * Composes document authorization with the projected document/ref
4774
+ * relationship. Order is fixed: validate ref, resolve the canonical document
4775
+ * id, check `canRead`, then check the reference index. A denied document
4776
+ * never reaches the reference reader, and the facade never touches
4777
+ * attachment metadata, storage backends, or presigners.
4778
+ */
4779
+ var AttachmentAccessService = class {
4780
+ constructor(resolveCanonicalId, authorization, references, projection) {
4781
+ this.resolveCanonicalId = resolveCanonicalId;
4782
+ this.authorization = authorization;
4783
+ this.references = references;
4784
+ this.projection = projection;
4785
+ }
4786
+ async canReadAttachment(request) {
4787
+ if (this.projection.status !== "available") return { kind: "projection-unavailable" };
4788
+ const ref = normalizeAttachmentRef(request.attachmentRef);
4789
+ if (ref === null) return { kind: "denied" };
4790
+ let documentId;
4791
+ try {
4792
+ documentId = await this.resolveCanonicalId(request.documentId);
4793
+ } catch {
4794
+ return { kind: "denied" };
4795
+ }
4796
+ if (!await this.authorization.canRead(documentId, request.userAddress)) return { kind: "denied" };
4797
+ if (!await this.references.hasReference(documentId, ref)) return { kind: "denied" };
4798
+ return {
4799
+ kind: "allowed",
4800
+ documentId,
4801
+ ref
4802
+ };
4803
+ }
4804
+ };
4805
+ /**
4806
+ * Parses and canonicalizes a caller-supplied ref through the shared parser.
4807
+ * Returns null for anything that is not a v1 ref over a 64-char SHA-256 hex
4808
+ * hash; hex case is normalized so index lookups use the canonical form.
4809
+ */
4810
+ function normalizeAttachmentRef(value) {
4811
+ let hash;
4812
+ let version;
4813
+ try {
4814
+ const parsed = parseRef(value);
4815
+ hash = parsed.hash.toLowerCase();
4816
+ version = parsed.version;
4817
+ } catch {
4818
+ return null;
4819
+ }
4820
+ if (version !== 1 || !HASH_PATTERN.test(hash)) return null;
4821
+ return createRef(hash, version);
4822
+ }
4823
+ //#endregion
4698
4824
  //#region src/migrations/001_create_document_permissions.ts
4699
4825
  var _001_create_document_permissions_exports = /* @__PURE__ */ __exportAll({
4700
4826
  down: () => down$2,
@@ -5042,14 +5168,33 @@ var AuthService = class {
5042
5168
  }
5043
5169
  }
5044
5170
  async fetchCredentialExists(address, chainId, appId) {
5045
- return await fetchDelegationCredential({
5171
+ if (!this.config.verifyCredential) throw new Error("AuthService: credential verification is enabled but no verifyCredential was provided");
5172
+ return this.config.verifyCredential({
5046
5173
  address,
5047
5174
  chainId,
5048
- appDid: appId
5049
- }) !== void 0;
5175
+ appId
5176
+ });
5050
5177
  }
5051
5178
  };
5052
5179
  //#endregion
5180
+ //#region src/services/renown-credential-verifier.ts
5181
+ async function createRenownCredentialVerifier(config = {}) {
5182
+ const switchboardUrl = await resolveSwitchboardEndpoint({
5183
+ switchboardUrl: config.switchboardUrl,
5184
+ baseUrl: config.renownUrl
5185
+ });
5186
+ return async ({ address, chainId, appId }) => {
5187
+ return await fetchDelegationCredential({
5188
+ address,
5189
+ chainId,
5190
+ appDid: appId,
5191
+ switchboardUrl,
5192
+ baseUrl: switchboardUrl ? void 0 : config.renownUrl,
5193
+ discover: false
5194
+ }) !== void 0;
5195
+ };
5196
+ }
5197
+ //#endregion
5053
5198
  //#region src/services/document-permission.service.ts
5054
5199
  /**
5055
5200
  * Service for managing document-level permissions.
@@ -5568,15 +5713,20 @@ async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer,
5568
5713
  function setupEventListeners(pkgManager, graphqlManager, reactorProcessorManager, module, documentModelRegistry) {
5569
5714
  pkgManager.onDocumentModelsChange((packagedModels) => {
5570
5715
  if (documentModelRegistry) {
5571
- const newModules = Object.values(packagedModels).flat();
5572
- const registeredModules = documentModelRegistry.getAllModules();
5573
- const registeredTypes = new Set(registeredModules.map((m) => m.documentModel.global.id));
5574
- const modulesToRegister = newModules.filter((mod) => !registeredTypes.has(mod.documentModel.global.id));
5575
- if (modulesToRegister.length > 0) {
5576
- const results = documentModelRegistry.registerModules(...modulesToRegister);
5577
- for (const result of results) if (result.status === "success") defaultLogger.info(`Registered new document model: ${result.item.documentModel.global.id}`);
5716
+ const newModules = getUniqueDocumentModels(Object.values(packagedModels).flat());
5717
+ const incomingTypes = new Set(newModules.map((m) => m.documentModel.global.id));
5718
+ if (incomingTypes.size > 0) {
5719
+ documentModelRegistry.unregisterModules(...incomingTypes);
5720
+ const results = documentModelRegistry.registerModules(...newModules);
5721
+ for (const result of results) if (result.status === "success") defaultLogger.info(`Registered document model: ${result.item.documentModel.global.id} v${result.item.version ?? 1}`);
5578
5722
  else defaultLogger.error(`Failed to register document model: ${result.error.message}`);
5579
5723
  }
5724
+ const manifests = pkgManager.getUniqueUpgradeManifests();
5725
+ if (manifests.length > 0) {
5726
+ documentModelRegistry.unregisterUpgradeManifests(...manifests.map((m) => m.documentType));
5727
+ const manifestResults = documentModelRegistry.registerUpgradeManifests(...manifests);
5728
+ for (const result of manifestResults) if (result.status === "error") defaultLogger.error(`Failed to register upgrade manifest: ${result.error.message}`);
5729
+ }
5580
5730
  }
5581
5731
  graphqlManager.regenerateDocumentModelSubgraphs();
5582
5732
  });
@@ -5641,7 +5791,7 @@ async function _setupCommonInfrastructure(options) {
5641
5791
  admins = options.auth.admins.map((a) => a.toLowerCase());
5642
5792
  authEnabled = options.auth.enabled;
5643
5793
  }
5644
- const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION, CREDENTIAL_VERIFICATION_CACHE_TTL_MS } = process.env;
5794
+ const { AUTH_ENABLED, ADMINS, DEFAULT_PROTECTION, DOCUMENT_PERMISSIONS_ENABLED, SKIP_CREDENTIAL_VERIFICATION, CREDENTIAL_VERIFICATION_CACHE_TTL_MS, RENOWN_URL, SWITCHBOARD_URL } = process.env;
5645
5795
  if (AUTH_ENABLED !== void 0) authEnabled = AUTH_ENABLED === "true";
5646
5796
  if (ADMINS !== void 0) admins = ADMINS.split(",").map((a) => a.toLowerCase());
5647
5797
  let defaultProtection = false;
@@ -5677,7 +5827,11 @@ async function _setupCommonInfrastructure(options) {
5677
5827
  enabled: authEnabled,
5678
5828
  admins,
5679
5829
  skipCredentialVerification,
5680
- credentialVerificationCacheTtlMs
5830
+ credentialVerificationCacheTtlMs,
5831
+ verifyCredential: await createRenownCredentialVerifier({
5832
+ renownUrl: RENOWN_URL,
5833
+ switchboardUrl: SWITCHBOARD_URL
5834
+ })
5681
5835
  });
5682
5836
  authFetchMiddleware = createAuthFetchMiddleware(authService);
5683
5837
  }
@@ -5703,7 +5857,12 @@ async function _setupCommonInfrastructure(options) {
5703
5857
  await mkdir(attachmentStoragePath, { recursive: true });
5704
5858
  const { db: attachmentDb, knex: attachmentKnex, pglite: attachmentPglite } = getDbClient(options.dbPath, options.pgliteFactory);
5705
5859
  dbClosers.push(...makeDbClosers(attachmentKnex, attachmentPglite));
5706
- const attachments = await new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(3600 * 1e3).build();
5860
+ const ATTACHMENT_SWEEP_INTERVAL_MS = 3600 * 1e3;
5861
+ const attachmentBackend = await createStartupAttachmentBackend({ db: attachmentDb });
5862
+ const attachmentBuilder = new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(ATTACHMENT_SWEEP_INTERVAL_MS);
5863
+ if (attachmentBackend) attachmentBuilder.withBackend(attachmentBackend);
5864
+ const attachments = await attachmentBuilder.build();
5865
+ const attachmentReferenceIndex = await new AttachmentReferenceIndexBuilder(attachmentDb).build();
5707
5866
  dbClosers.push(() => {
5708
5867
  attachments.destroy();
5709
5868
  return Promise.resolve();
@@ -5723,6 +5882,7 @@ async function _setupCommonInfrastructure(options) {
5723
5882
  documentPermissionService,
5724
5883
  authorizationConfig,
5725
5884
  attachments,
5885
+ attachmentReferenceIndex,
5726
5886
  packages,
5727
5887
  dbClosers,
5728
5888
  readiness
@@ -5731,7 +5891,7 @@ async function _setupCommonInfrastructure(options) {
5731
5891
  /**
5732
5892
  * Private helper function containing common setup logic for API initialization
5733
5893
  */
5734
- async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
5894
+ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
5735
5895
  const hostModule = {
5736
5896
  relationalDb,
5737
5897
  analyticsStore,
@@ -5778,6 +5938,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5778
5938
  const { httpServer, wsServer } = await startServer(httpAdapter, port, options.https, logger);
5779
5939
  const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
5780
5940
  logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
5941
+ const attachmentAccess = new AttachmentAccessService(createCanonicalDocumentIdResolver(reactorClient), authorizationService, attachmentReferenceIndex.store, attachmentReferenceProjection);
5781
5942
  const coreSubgraphs = DefaultCoreSubgraphs.slice();
5782
5943
  coreSubgraphs.push(ReactorSubgraph);
5783
5944
  if (documentPermissionService) {
@@ -5802,6 +5963,8 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5802
5963
  graphqlManager,
5803
5964
  packages,
5804
5965
  attachments,
5966
+ attachmentReferenceIndex,
5967
+ attachmentAccess,
5805
5968
  authService,
5806
5969
  dispose: buildApiDispose({
5807
5970
  graphqlManager,
@@ -5850,9 +6013,15 @@ function buildApiDispose(args) {
5850
6013
  };
5851
6014
  }
5852
6015
  async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5853
- const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
5854
- const { documentModels, processors, subgraphs } = await packages.init();
5855
- const { module: reactorClientModule, reactorDriveClient } = await clientInitializer(documentModels);
6016
+ const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, attachmentReferenceIndex, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
6017
+ const { documentModels, upgradeManifests, processors, subgraphs } = await packages.init();
6018
+ const { module: reactorClientModule, reactorDriveClient, attachmentReferenceProjection = {
6019
+ status: "unavailable",
6020
+ reason: "initializer-did-not-report"
6021
+ } } = await clientInitializer(documentModels, {
6022
+ attachmentReferenceWriter: attachmentReferenceIndex.store,
6023
+ upgradeManifests
6024
+ });
5856
6025
  const reactorClient = reactorClientModule.client;
5857
6026
  const syncManager = reactorClientModule.reactorModule?.syncModule?.syncManager;
5858
6027
  if (!syncManager) throw new Error("SyncManager not available from InProcessReactorClientModule");
@@ -5861,11 +6030,12 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5861
6030
  const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
5862
6031
  if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from InProcessReactorClientModule");
5863
6032
  return {
5864
- ...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),
6033
+ ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient),
5865
6034
  client: reactorClient,
5866
6035
  syncManager,
5867
6036
  documentModelRegistry,
5868
- readiness
6037
+ readiness,
6038
+ attachmentReferenceProjection
5869
6039
  };
5870
6040
  }
5871
6041
  //#endregion
@@ -5969,7 +6139,7 @@ var PackageManagementService = class {
5969
6139
  }
5970
6140
  };
5971
6141
  //#endregion
5972
- 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 };
6142
+ export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CanonicalDocumentIdResolutionError, 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, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
5973
6143
 
5974
6144
  //# sourceMappingURL=index.mjs.map
5975
- //# debugId=e58cea2c-2319-52a8-90cd-3e99d94ba98f
6145
+ //# debugId=0fa604a4-1713-5d00-989b-ce10e0cf5cdb