@powerhousedao/reactor-api 6.2.2-dev.16 → 6.2.2-dev.18

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,6 +1,6 @@
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]="de4031dc-bf09-5f6e-b2dd-33b3b67532db")}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-DZOYeLnm.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]="cb4acb17-dffb-5545-a8f2-04134029dcb8")}catch(e){}}();
3
+ import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, f as CanonicalDocumentIdResolutionError, g as createAuthorizationService, h as AuthorizedDocumentHandle, i as buildGraphqlOperations, l as loadProcessors, m as AuthorizationPolicy, n as buildGraphQlDriveDocument, o as debounce, p as createCanonicalDocumentIdResolver, r as buildGraphqlOperation, t as buildGraphQlDocument, u as loadSubgraphs } from "./utils-BdPg2whY.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";
@@ -26,7 +26,7 @@ import { driveDocumentModelModule } from "@powerhousedao/shared/document-drive";
26
26
  import EventEmitter from "node:events";
27
27
  import fs, { watchFile } from "node:fs";
28
28
  import { PostgresAnalyticsStore } from "@powerhousedao/analytics-engine-pg";
29
- import { AttachmentBuilder } from "@powerhousedao/reactor-attachments";
29
+ import { AttachmentBuilder, AttachmentReferenceIndexBuilder, createRef, createS3AttachmentBackend, parseAttachmentStorageConfig, parseRef } from "@powerhousedao/reactor-attachments";
30
30
  import { createAttachmentClient } from "@powerhousedao/reactor-attachments/client";
31
31
  import { setupMcpServer } from "@powerhousedao/reactor-mcp";
32
32
  import { mkdir } from "node:fs/promises";
@@ -460,6 +460,7 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
460
460
  });
461
461
  return dedupeTypeDefinitions(gql`
462
462
  scalar JSONObject
463
+ scalar AttachmentRef
463
464
  ${typeDefs.join("\n").replaceAll(";", "")}
464
465
 
465
466
  type PHOperationContext {
@@ -878,6 +879,7 @@ function generateNewApiSchema(documentName, specification, _stateSchemaTypes, pr
878
879
  return gql`
879
880
  scalar DateTime
880
881
  scalar JSONObject
882
+ scalar AttachmentRef
881
883
 
882
884
  ${revisionType}
883
885
 
@@ -4210,10 +4212,10 @@ const ADMIN_USERS = getAdminUsers();
4210
4212
  //#endregion
4211
4213
  //#region src/graphql/system/version.ts
4212
4214
  function getVersion() {
4213
- return "6.2.2-dev.16";
4215
+ return "6.2.2-dev.18";
4214
4216
  }
4215
4217
  function getGitHash() {
4216
- return "9ecf9d9d59204095e34708e7656e9c0d82199b5d";
4218
+ return "8e36f7d5190d0667e0d4e8152ec4bad3c8ccfb68";
4217
4219
  }
4218
4220
  function getGitUrl() {
4219
4221
  return buildTreeUrl(getGitHash());
@@ -4700,6 +4702,70 @@ var PackageManager = class {
4700
4702
  const config = { basePath: process.env.BASE_PATH || "/" };
4701
4703
  const DefaultCoreSubgraphs = [AnalyticsSubgraph, SystemSubgraph];
4702
4704
  //#endregion
4705
+ //#region src/attachment-backend.ts
4706
+ /** Filesystem is the default; explicit S3 is readiness-gated without fallback. */
4707
+ async function createStartupAttachmentBackend({ db, env = process.env, s3Dependencies }) {
4708
+ const storage = parseAttachmentStorageConfig(env);
4709
+ if (storage.kind === "filesystem") return void 0;
4710
+ const backend = createS3AttachmentBackend(db.withSchema("attachments"), storage.s3, s3Dependencies);
4711
+ if (!(await backend.health()).ready) throw new Error("S3 attachment backend readiness check failed");
4712
+ return backend;
4713
+ }
4714
+ //#endregion
4715
+ //#region src/services/attachment-access.service.ts
4716
+ const HASH_PATTERN = /^[a-f0-9]{64}$/;
4717
+ /**
4718
+ * Composes document authorization with the projected document/ref
4719
+ * relationship. Order is fixed: validate ref, resolve the canonical document
4720
+ * id, check `canRead`, then check the reference index. A denied document
4721
+ * never reaches the reference reader, and the facade never touches
4722
+ * attachment metadata, storage backends, or presigners.
4723
+ */
4724
+ var AttachmentAccessService = class {
4725
+ constructor(resolveCanonicalId, authorization, references, projection) {
4726
+ this.resolveCanonicalId = resolveCanonicalId;
4727
+ this.authorization = authorization;
4728
+ this.references = references;
4729
+ this.projection = projection;
4730
+ }
4731
+ async canReadAttachment(request) {
4732
+ if (this.projection.status !== "available") return { kind: "projection-unavailable" };
4733
+ const ref = normalizeAttachmentRef(request.attachmentRef);
4734
+ if (ref === null) return { kind: "denied" };
4735
+ let documentId;
4736
+ try {
4737
+ documentId = await this.resolveCanonicalId(request.documentId);
4738
+ } catch {
4739
+ return { kind: "denied" };
4740
+ }
4741
+ if (!await this.authorization.canRead(documentId, request.userAddress)) return { kind: "denied" };
4742
+ if (!await this.references.hasReference(documentId, ref)) return { kind: "denied" };
4743
+ return {
4744
+ kind: "allowed",
4745
+ documentId,
4746
+ ref
4747
+ };
4748
+ }
4749
+ };
4750
+ /**
4751
+ * Parses and canonicalizes a caller-supplied ref through the shared parser.
4752
+ * Returns null for anything that is not a v1 ref over a 64-char SHA-256 hex
4753
+ * hash; hex case is normalized so index lookups use the canonical form.
4754
+ */
4755
+ function normalizeAttachmentRef(value) {
4756
+ let hash;
4757
+ let version;
4758
+ try {
4759
+ const parsed = parseRef(value);
4760
+ hash = parsed.hash.toLowerCase();
4761
+ version = parsed.version;
4762
+ } catch {
4763
+ return null;
4764
+ }
4765
+ if (version !== 1 || !HASH_PATTERN.test(hash)) return null;
4766
+ return createRef(hash, version);
4767
+ }
4768
+ //#endregion
4703
4769
  //#region src/migrations/001_create_document_permissions.ts
4704
4770
  var _001_create_document_permissions_exports = /* @__PURE__ */ __exportAll({
4705
4771
  down: () => down$2,
@@ -5731,7 +5797,12 @@ async function _setupCommonInfrastructure(options) {
5731
5797
  await mkdir(attachmentStoragePath, { recursive: true });
5732
5798
  const { db: attachmentDb, knex: attachmentKnex, pglite: attachmentPglite } = getDbClient(options.dbPath, options.pgliteFactory);
5733
5799
  dbClosers.push(...makeDbClosers(attachmentKnex, attachmentPglite));
5734
- const attachments = await new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(3600 * 1e3).build();
5800
+ const ATTACHMENT_SWEEP_INTERVAL_MS = 3600 * 1e3;
5801
+ const attachmentBackend = await createStartupAttachmentBackend({ db: attachmentDb });
5802
+ const attachmentBuilder = new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(ATTACHMENT_SWEEP_INTERVAL_MS);
5803
+ if (attachmentBackend) attachmentBuilder.withBackend(attachmentBackend);
5804
+ const attachments = await attachmentBuilder.build();
5805
+ const attachmentReferenceIndex = await new AttachmentReferenceIndexBuilder(attachmentDb).build();
5735
5806
  dbClosers.push(() => {
5736
5807
  attachments.destroy();
5737
5808
  return Promise.resolve();
@@ -5751,6 +5822,7 @@ async function _setupCommonInfrastructure(options) {
5751
5822
  documentPermissionService,
5752
5823
  authorizationConfig,
5753
5824
  attachments,
5825
+ attachmentReferenceIndex,
5754
5826
  packages,
5755
5827
  dbClosers,
5756
5828
  readiness
@@ -5759,7 +5831,7 @@ async function _setupCommonInfrastructure(options) {
5759
5831
  /**
5760
5832
  * Private helper function containing common setup logic for API initialization
5761
5833
  */
5762
- async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
5834
+ 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) {
5763
5835
  const hostModule = {
5764
5836
  relationalDb,
5765
5837
  analyticsStore,
@@ -5806,6 +5878,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5806
5878
  const { httpServer, wsServer } = await startServer(httpAdapter, port, options.https, logger);
5807
5879
  const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
5808
5880
  logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
5881
+ const attachmentAccess = new AttachmentAccessService(createCanonicalDocumentIdResolver(reactorClient), authorizationService, attachmentReferenceIndex.store, attachmentReferenceProjection);
5809
5882
  const coreSubgraphs = DefaultCoreSubgraphs.slice();
5810
5883
  coreSubgraphs.push(ReactorSubgraph);
5811
5884
  if (documentPermissionService) {
@@ -5830,6 +5903,8 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5830
5903
  graphqlManager,
5831
5904
  packages,
5832
5905
  attachments,
5906
+ attachmentReferenceIndex,
5907
+ attachmentAccess,
5833
5908
  authService,
5834
5909
  dispose: buildApiDispose({
5835
5910
  graphqlManager,
@@ -5878,9 +5953,12 @@ function buildApiDispose(args) {
5878
5953
  };
5879
5954
  }
5880
5955
  async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5881
- const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
5956
+ const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, attachmentReferenceIndex, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
5882
5957
  const { documentModels, processors, subgraphs } = await packages.init();
5883
- const { module: reactorClientModule, reactorDriveClient } = await clientInitializer(documentModels);
5958
+ const { module: reactorClientModule, reactorDriveClient, attachmentReferenceProjection = {
5959
+ status: "unavailable",
5960
+ reason: "initializer-did-not-report"
5961
+ } } = await clientInitializer(documentModels, { attachmentReferenceWriter: attachmentReferenceIndex.store });
5884
5962
  const reactorClient = reactorClientModule.client;
5885
5963
  const syncManager = reactorClientModule.reactorModule?.syncModule?.syncManager;
5886
5964
  if (!syncManager) throw new Error("SyncManager not available from InProcessReactorClientModule");
@@ -5889,11 +5967,12 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5889
5967
  const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
5890
5968
  if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from InProcessReactorClientModule");
5891
5969
  return {
5892
- ...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),
5970
+ ...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),
5893
5971
  client: reactorClient,
5894
5972
  syncManager,
5895
5973
  documentModelRegistry,
5896
- readiness
5974
+ readiness,
5975
+ attachmentReferenceProjection
5897
5976
  };
5898
5977
  }
5899
5978
  //#endregion
@@ -5997,7 +6076,7 @@ var PackageManagementService = class {
5997
6076
  }
5998
6077
  };
5999
6078
  //#endregion
6000
- 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, createRenownCredentialVerifier, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
6079
+ 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, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
6001
6080
 
6002
6081
  //# sourceMappingURL=index.mjs.map
6003
- //# debugId=de4031dc-bf09-5f6e-b2dd-33b3b67532db
6082
+ //# debugId=cb4acb17-dffb-5545-a8f2-04134029dcb8