@powerhousedao/reactor-api 6.2.3-dev.50 → 6.2.3-dev.52

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,14 +1,14 @@
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]="dc27a51b-c565-5dcc-a85a-eac64c78db3e")}catch(e){}}();
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]="980be7cc-16d9-5965-b60e-0c7ca7a4e3c8")}catch(e){}}();
3
3
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
4
- import { A as AuthenticationRequiredError, C as callerSubject, D as AuthorizedDocumentHandle, E as AuthorizationPolicy, M as MalformedStoredOperationError, N as RefusalNotRecordedError, O as createAuthorizationService, S as BaseSubgraph, T as createCanonicalDocumentIdResolver, b as loadSubgraphs, c as piecesFromListModule, d as buildGraphQlDriveDocument, f as buildGraphqlOperation, g as extractUpgradeManifests, h as debounce, i as PIECE_SOURCE_LIST, j as ForbiddenError, k as AuthEvaluationUnsupportedError, l as extractDocumentModels, m as isSubgraphClass, o as pieceListLocation, p as buildGraphqlOperations, s as piecesFromCdnList, t as extractSubgraphs, u as buildGraphQlDocument, v as loadDocumentModels, w as CanonicalDocumentIdResolutionError, x as manifestDeclaredSubgraphs, y as loadProcessors } from "./subgraph-extraction-DTFoN_bS.mjs";
4
+ import { A as AuthenticationRequiredError, C as callerSubject, D as AuthorizedDocumentHandle, E as AuthorizationPolicy, M as MalformedStoredOperationError, N as RefusalNotRecordedError, O as createAuthorizationService, S as BaseSubgraph, T as createCanonicalDocumentIdResolver, b as loadSubgraphs, c as piecesFromListModule, d as buildGraphQlDriveDocument, f as buildGraphqlOperation, g as extractUpgradeManifests, h as debounce, i as PIECE_SOURCE_LIST, j as ForbiddenError, k as AuthEvaluationUnsupportedError, l as extractDocumentModels, m as isSubgraphClass, o as pieceListLocation, p as buildGraphqlOperations, s as piecesFromCdnList, t as extractSubgraphs, u as buildGraphQlDocument, v as loadDocumentModels, w as CanonicalDocumentIdResolutionError, x as manifestDeclaredSubgraphs, y as loadProcessors } from "./subgraph-extraction-sQlZLLPH.mjs";
5
5
  import { l as createDurableNodeFs, n as openCurrentPgliteForVerify, r as preparePgliteDataDir, u as resolvePgliteFsync } from "./pglite-node-Ow9i5Rb4.mjs";
6
6
  import { findPackageJSON, isBuiltin } from "node:module";
7
7
  import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
8
8
  import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
9
9
  import { gql } from "graphql-tag";
10
- import { GraphQLError, Kind, Lexer, Source, TokenKind, parse, print, visit } from "graphql";
11
- import { AuthEnforcementDisabledError, BareReadGate, BatchJobFailedError, ChannelError, ChannelErrorSource, DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, JobStatus, ModelReadGate, PropagationMode as PropagationMode$1, SyncOperation, SyncScopeGate, consolidateSyncOperations, createReactorHostModuleBase, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, readDecisionModel, supportsPurgeRefusals, syncOperationErrorType } from "@powerhousedao/reactor";
10
+ import { GraphQLError, Kind, Lexer, OperationTypeNode, Source, TokenKind, parse, print, valueFromASTUntyped, visit } from "graphql";
11
+ import { AuthEnforcementDisabledError, BareReadGate, BatchJobFailedError, ChannelError, ChannelErrorSource, DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, DriveInspection, InvalidDeadLetterCursorError, JOB_NOT_FOUND_ERROR_NAME, JobStatus, ModelReadGate, PropagationMode as PropagationMode$1, READ_ONLY_ACCESS, SyncOperation, SyncScopeGate, consolidateSyncOperations, createReactorHostModuleBase, createReactorInspector, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, readDecisionModel, supportsPurgeRefusals, syncOperationErrorType, toWireDeadLetter, toWireQueueState } from "@powerhousedao/reactor";
12
12
  import { deserializeSignature, readPeerManifest, serializeSignature, setName, withSignaturePolicy } from "@powerhousedao/shared/document-model";
13
13
  import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
14
14
  import path, { isAbsolute, join } from "node:path";
@@ -44,6 +44,7 @@ import { setupMcpServer } from "@powerhousedao/reactor-mcp";
44
44
  import { tmpdir } from "node:os";
45
45
  import { WebSocketServer } from "ws";
46
46
  import { createRelationalDb } from "@powerhousedao/shared/processors";
47
+ import { isAttachmentHash, readGateAllowsAttachmentRead, scopeGateAllowsAttachmentRead } from "@powerhousedao/reactor-attachments/replication";
47
48
  import { Kysely, Migrator, sql } from "kysely";
48
49
  import { fetchDelegationCredential, resolveSwitchboardEndpoint, verifyAuthBearerToken } from "@renown/sdk";
49
50
  import { PGlite } from "@electric-sql/pglite";
@@ -1290,8 +1291,10 @@ var WebhookService = class {
1290
1291
  res.end(challenge);
1291
1292
  return;
1292
1293
  }
1294
+ let recordedKey;
1293
1295
  if (policy.dedupe) {
1294
1296
  const key = dedupeKey(policy.dedupe.field, queryParams, headers, body);
1297
+ recordedKey = key;
1295
1298
  if (key !== void 0 && await this.#store.seen(token, key, policy.dedupe.ttlSeconds ?? DEFAULT_DEDUPE_TTL_SECONDS)) {
1296
1299
  res.statusCode = 200;
1297
1300
  res.end();
@@ -1307,6 +1310,11 @@ var WebhookService = class {
1307
1310
  raw,
1308
1311
  body
1309
1312
  });
1313
+ if (reply.unprocessed === true && recordedKey !== void 0) try {
1314
+ await this.#store.forget(token, recordedKey);
1315
+ } catch (error) {
1316
+ logger$2.warn("Could not forget an unprocessed webhook delivery: @error", error);
1317
+ }
1310
1318
  res.statusCode = reply.status;
1311
1319
  if (reply.body !== void 0 && method !== "HEAD") {
1312
1320
  res.setHeader("content-type", reply.contentType ?? "text/plain");
@@ -1422,6 +1430,9 @@ var RelationalWebhookStore = class {
1422
1430
  async revoke(namespace, endpoint, ownerKey) {
1423
1431
  await this.#handle.deleteFrom("webhook_endpoints").where("namespace", "=", namespace).where("endpoint", "=", endpoint).where("owner_key", "=", ownerKey).execute();
1424
1432
  }
1433
+ async forget(token, key) {
1434
+ await this.#handle.deleteFrom("webhook_deliveries").where("token", "=", token).where("dedupe_key", "=", key).execute();
1435
+ }
1425
1436
  async seen(token, key, ttlSeconds) {
1426
1437
  const now = Date.now();
1427
1438
  const nowIso = new Date(now).toISOString();
@@ -1470,6 +1481,10 @@ var MemoryWebhookStore = class {
1470
1481
  find(token) {
1471
1482
  return Promise.resolve(this.#byToken.get(token));
1472
1483
  }
1484
+ forget(token, key) {
1485
+ this.#deliveries.delete(`${token}|${key}`);
1486
+ return Promise.resolve();
1487
+ }
1473
1488
  list(namespace, endpoint) {
1474
1489
  return Promise.resolve([...this.#byToken.values()].filter((row) => row.namespace === namespace && (endpoint === void 0 || row.endpoint === endpoint)));
1475
1490
  }
@@ -2651,6 +2666,19 @@ const GetJobStatusDocument = gql`
2651
2666
  }
2652
2667
  ${JobInfoFieldsFragmentDoc}
2653
2668
  `;
2669
+ const GetDocumentServedDocument = gql`
2670
+ query GetDocumentServed($idOrSlug: String!, $view: ViewFilterInput) {
2671
+ documentServed(idOrSlug: $idOrSlug, view: $view)
2672
+ }
2673
+ `;
2674
+ const GetCreateDefaultsDocument = gql`
2675
+ query GetCreateDefaults($parentIdOrSlug: String) {
2676
+ createDefaults(parentIdOrSlug: $parentIdOrSlug) {
2677
+ signaturePolicy
2678
+ protocolVersions
2679
+ }
2680
+ }
2681
+ `;
2654
2682
  const EvaluateActionsDocument = gql`
2655
2683
  query EvaluateActions(
2656
2684
  $documentIdentifier: String!
@@ -3026,6 +3054,12 @@ function getSdk(requester) {
3026
3054
  GetJobStatus(variables, options) {
3027
3055
  return requester(GetJobStatusDocument, variables, options);
3028
3056
  },
3057
+ GetDocumentServed(variables, options) {
3058
+ return requester(GetDocumentServedDocument, variables, options);
3059
+ },
3060
+ GetCreateDefaults(variables, options) {
3061
+ return requester(GetCreateDefaultsDocument, variables, options);
3062
+ },
3029
3063
  EvaluateActions(variables, options) {
3030
3064
  return requester(EvaluateActionsDocument, variables, options);
3031
3065
  },
@@ -3527,6 +3561,14 @@ async function readAsCaller(reactorClient, document, subject, branch) {
3527
3561
  throw new GraphQLError(`Failed to read the document back: ${error instanceof Error ? error.message : "Unknown error"}`);
3528
3562
  }
3529
3563
  }
3564
+ /** The defaults `create` gives a new document; the parent must already be gated. */
3565
+ async function createDefaults(reactorClient, parentIdOrSlug) {
3566
+ const [signaturePolicy, protocolVersions] = await Promise.all([reactorClient.getCreateSignaturePolicy(), reactorClient.getCreateProtocolVersions(parentIdOrSlug)]);
3567
+ return {
3568
+ signaturePolicy,
3569
+ protocolVersions
3570
+ };
3571
+ }
3530
3572
  async function documentModels(reactorClient, args) {
3531
3573
  const namespace = fromInputMaybe(args.namespace);
3532
3574
  let paging;
@@ -3727,7 +3769,7 @@ function unknownJob(jobId) {
3727
3769
  createdAtUtcIso: now,
3728
3770
  completedAtUtcIso: now,
3729
3771
  error: {
3730
- name: "Error",
3772
+ name: JOB_NOT_FOUND_ERROR_NAME,
3731
3773
  message: "Job not found",
3732
3774
  stack: ""
3733
3775
  },
@@ -4602,12 +4644,9 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
4602
4644
  }
4603
4645
  /** Drops the items the host's own ACL refuses the caller. */
4604
4646
  async #readableItems(page, ctx) {
4605
- if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return page;
4606
- const items = [];
4607
- for (const item of page.items) if (await this.canReadDocument(item.id, ctx)) items.push(item);
4608
4647
  return {
4609
4648
  ...page,
4610
- items
4649
+ items: await this.readableByHost(page.items, ctx)
4611
4650
  };
4612
4651
  }
4613
4652
  /**
@@ -4816,11 +4855,13 @@ function getRequestDriveId(request) {
4816
4855
  *
4817
4856
  * - No header → pass through. The LB has already round-robined; nothing
4818
4857
  * to validate here.
4819
- * - Header present and drive in cache → record on the request map (for
4820
- * the context factory to read into `context.driveId`) and pass through.
4821
- * - Header present, drive missing, but the operation is `createDocument`
4822
- * or `createEmptyDocument` → pass through. The drive may be in the
4823
- * process of being created.
4858
+ * - Header present and the drive held here (the cache, else the reactor) →
4859
+ * record on the request map (for the context factory to read into
4860
+ * `context.driveId`) and pass through. A failed lookup passes through.
4861
+ * - Header present, drive missing, but the operation is named
4862
+ * `createDocument` or `createEmptyDocument`, or it creates, with no
4863
+ * parent, the very drive the header names → pass through. The drive is
4864
+ * being created.
4824
4865
  * - Otherwise → return `421 Misdirected Request` with a structured body.
4825
4866
  * The client (or LB) can surface this as a wrong-shard signal.
4826
4867
  */
@@ -4828,25 +4869,59 @@ function createDriveFetchMiddleware(cache) {
4828
4869
  return (next) => async (request) => {
4829
4870
  const driveId = request.headers.get(DRIVE_ID_HEADER) ?? "";
4830
4871
  if (driveId === "") return next(request);
4831
- if (cache.has(driveId)) {
4872
+ let held;
4873
+ try {
4874
+ held = await cache.holds(driveId);
4875
+ } catch {
4876
+ return next(request);
4877
+ }
4878
+ if (held) {
4832
4879
  driveIdMap.set(request, driveId);
4833
4880
  return next(request);
4834
4881
  }
4835
- if (await isCacheBypassOperation(request)) return next(request);
4882
+ if (await isCacheBypassOperation(request, driveId)) return next(request);
4836
4883
  return wrongShardResponse(driveId);
4837
4884
  };
4838
4885
  }
4839
- async function isCacheBypassOperation(request) {
4886
+ async function isCacheBypassOperation(request, driveId) {
4840
4887
  if (request.method !== "POST") return false;
4841
4888
  try {
4842
4889
  const body = await request.clone().json();
4843
- if (typeof body.operationName === "string") return CACHE_BYPASS_OPERATIONS.has(body.operationName);
4844
- if (typeof body.query === "string") return CACHE_BYPASS_OPERATIONS.has(extractOperationName(body.query));
4845
- return false;
4890
+ const operationName = typeof body.operationName === "string" ? body.operationName : void 0;
4891
+ if (operationName !== void 0) {
4892
+ if (CACHE_BYPASS_OPERATIONS.has(operationName)) return true;
4893
+ } else if (typeof body.query === "string" && CACHE_BYPASS_OPERATIONS.has(extractOperationName(body.query))) return true;
4894
+ return typeof body.query === "string" && createsTheNamedDrive(body.query, operationName, isRecord(body.variables) ? body.variables : {}, driveId);
4846
4895
  } catch {
4847
4896
  return false;
4848
4897
  }
4849
4898
  }
4899
+ /**
4900
+ * Whether the operation does nothing but create, with no parent, the drive the
4901
+ * Drive-Id names: whatever the client named it (reactor-browser sends
4902
+ * `CreateDocument`), and with the document resolved through its variables.
4903
+ */
4904
+ function createsTheNamedDrive(query, operationName, variables, driveId) {
4905
+ const operations = parse(query).definitions.filter((definition) => definition.kind === Kind.OPERATION_DEFINITION);
4906
+ const operation = operationName === void 0 ? operations.length === 1 ? operations[0] : void 0 : operations.find((candidate) => candidate.name?.value === operationName);
4907
+ if (!operation || operation.operation !== OperationTypeNode.MUTATION || operation.selectionSet.selections.length !== 1) return false;
4908
+ const [field] = operation.selectionSet.selections;
4909
+ if (field.kind !== Kind.FIELD || field.name.value !== "createDocument") return false;
4910
+ const argument = (name) => {
4911
+ const node = field.arguments?.find((arg) => arg.name.value === name);
4912
+ return node ? valueFromASTUntyped(node.value, variables) : void 0;
4913
+ };
4914
+ if (PARENT_ARGUMENTS.some((name) => {
4915
+ const parent = argument(name);
4916
+ return parent !== void 0 && parent !== null;
4917
+ })) return false;
4918
+ const header = argument("document")?.header;
4919
+ return isRecord(header) && header.id === driveId && typeof header.documentType === "string" && DEFAULT_DRIVE_CONTAINER_TYPES.has(header.documentType);
4920
+ }
4921
+ const PARENT_ARGUMENTS = ["parentIdOrSlug", "parentIdentifier"];
4922
+ function isRecord(value) {
4923
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4924
+ }
4850
4925
  const OPERATION_NAME_PATTERN = /\b(?:mutation|query|subscription)\s+(\w+)/;
4851
4926
  function extractOperationName(query) {
4852
4927
  const match = OPERATION_NAME_PATTERN.exec(query);
@@ -4874,15 +4949,15 @@ function wrongShardResponse(driveId) {
4874
4949
  * 421 response.
4875
4950
  */
4876
4951
  var DriveOwnershipCache = class {
4877
- reactorClient;
4952
+ store;
4878
4953
  drives = /* @__PURE__ */ new Set();
4879
- constructor(reactorClient) {
4880
- this.reactorClient = reactorClient;
4954
+ constructor(store) {
4955
+ this.store = store;
4881
4956
  }
4882
4957
  async init() {
4883
4958
  this.drives.clear();
4884
4959
  for (const driveType of DEFAULT_DRIVE_CONTAINER_TYPES) {
4885
- let page = await this.reactorClient.find({ type: driveType });
4960
+ let page = await this.store.find({ type: driveType });
4886
4961
  while (true) {
4887
4962
  for (const drive of page.results) this.drives.add(drive.header.id);
4888
4963
  if (!page.next) break;
@@ -4893,6 +4968,17 @@ var DriveOwnershipCache = class {
4893
4968
  has(driveId) {
4894
4969
  return this.drives.has(driveId);
4895
4970
  }
4971
+ /**
4972
+ * Whether this host holds the drive, asking the reactor on a miss: drives
4973
+ * also arrive by sync and by writes that bypass the create resolvers. A
4974
+ * positive answer is cached; a failure to ask throws.
4975
+ */
4976
+ async holds(driveId) {
4977
+ if (this.drives.has(driveId)) return true;
4978
+ const held = (await this.store.find({ ids: [driveId] })).results.some((document) => document.header.id === driveId && DEFAULT_DRIVE_CONTAINER_TYPES.has(document.header.documentType));
4979
+ if (held) this.drives.add(driveId);
4980
+ return held;
4981
+ }
4896
4982
  add(driveId) {
4897
4983
  this.drives.add(driveId);
4898
4984
  }
@@ -5043,6 +5129,7 @@ var GraphQLManager = class {
5043
5129
  syncServingGate;
5044
5130
  httpRoutes;
5045
5131
  attachments;
5132
+ inspection;
5046
5133
  constructor(options) {
5047
5134
  this.path = options.path;
5048
5135
  this.httpServer = options.httpServer;
@@ -5063,7 +5150,8 @@ var GraphQLManager = class {
5063
5150
  this.syncServingGate = options.syncServingGate;
5064
5151
  this.httpRoutes = options.httpRoutes;
5065
5152
  this.attachments = options.attachments;
5066
- this.driveOwnershipCache = new DriveOwnershipCache(this.reactorClient);
5153
+ this.inspection = options.inspection;
5154
+ this.driveOwnershipCache = new DriveOwnershipCache(options.driveStore ?? this.reactorClient);
5067
5155
  this.wsServer.setMaxListeners(0);
5068
5156
  }
5069
5157
  async init(coreSubgraphs, authMiddleware, requireAuthMiddleware) {
@@ -5158,7 +5246,8 @@ var GraphQLManager = class {
5158
5246
  documentPermissionService: this.documentPermissionService,
5159
5247
  authorizationService: this.authorizationService,
5160
5248
  syncServingGate: this.syncServingGate,
5161
- attachments: this.attachments
5249
+ attachments: this.attachments,
5250
+ inspection: this.inspection
5162
5251
  });
5163
5252
  await this.#addSubgraphInstance(subgraphInstance, supergraph, false, DOCUMENT_MODEL_SUBGRAPH_SOURCE);
5164
5253
  registeredNames.add(subgraphInstance.name);
@@ -5296,7 +5385,8 @@ var GraphQLManager = class {
5296
5385
  documentPermissionService: this.documentPermissionService,
5297
5386
  authorizationService: this.authorizationService,
5298
5387
  syncServingGate: this.syncServingGate,
5299
- attachments: this.attachments
5388
+ attachments: this.attachments,
5389
+ inspection: this.inspection
5300
5390
  });
5301
5391
  return this.#addSubgraphInstance(subgraphInstance, supergraph, core, packageName);
5302
5392
  }
@@ -5616,6 +5706,453 @@ var GraphQLManager = class {
5616
5706
  }
5617
5707
  };
5618
5708
  //#endregion
5709
+ //#region src/graphql/inspection/resolvers.ts
5710
+ function operatorRequired() {
5711
+ return new ForbiddenError("to read the reactor's operational state");
5712
+ }
5713
+ function noSyncInspector() {
5714
+ return new GraphQLError("This host does not serve sync inspection");
5715
+ }
5716
+ function toOrdinal(value) {
5717
+ return Number(value);
5718
+ }
5719
+ function toWireDrive(drive) {
5720
+ return {
5721
+ ...drive,
5722
+ icon: drive.icon ?? null
5723
+ };
5724
+ }
5725
+ function toWireProcessor(info) {
5726
+ return {
5727
+ processorId: info.processorId,
5728
+ factoryId: info.factoryId,
5729
+ driveId: info.driveId,
5730
+ processorIndex: info.processorIndex,
5731
+ lastOrdinal: toOrdinal(info.lastOrdinal),
5732
+ status: info.status,
5733
+ lastError: info.lastError ?? null,
5734
+ lastErrorTimestampUtcMs: info.lastErrorTimestamp ? info.lastErrorTimestamp.getTime() : null
5735
+ };
5736
+ }
5737
+ function toWireCursor(cursor) {
5738
+ return {
5739
+ cursorType: cursor.cursorType,
5740
+ cursorOrdinal: toOrdinal(cursor.cursorOrdinal),
5741
+ lastSyncedAtUtcMs: cursor.lastSyncedAtUtcMs ?? null,
5742
+ liveAckOrdinal: toOrdinal(cursor.liveAckOrdinal),
5743
+ liveLatestOrdinal: toOrdinal(cursor.liveLatestOrdinal)
5744
+ };
5745
+ }
5746
+ function toWireRemotes(inspections, remotes) {
5747
+ const byName = new Map(remotes.map((remote) => [remote.meta.name, remote]));
5748
+ return inspections.map((inspection) => ({
5749
+ remoteName: inspection.remoteName,
5750
+ remoteId: inspection.remoteId,
5751
+ inboxCursor: toWireCursor(inspection.inboxCursor),
5752
+ outboxCursor: toWireCursor(inspection.outboxCursor),
5753
+ mailboxDepths: inspection.mailboxDepths,
5754
+ connection: {
5755
+ snapshot: inspection.connection.snapshot,
5756
+ neverSucceeded: inspection.connection.neverSucceeded,
5757
+ stalenessMs: inspection.connection.stalenessMs ?? null
5758
+ },
5759
+ meta: byName.get(inspection.remoteName)?.meta ?? {
5760
+ id: inspection.remoteId,
5761
+ name: inspection.remoteName
5762
+ }
5763
+ }));
5764
+ }
5765
+ function servedIssues(result, scopes) {
5766
+ const keyframeIssues = result.keyframeIssues.filter((i) => scopes.has(i.scope));
5767
+ const snapshotIssues = result.snapshotIssues.filter((i) => scopes.has(i.scope));
5768
+ const streamOrderIssues = result.streamOrderIssues.filter((i) => scopes.has(i.scope));
5769
+ return {
5770
+ documentId: result.documentId,
5771
+ isConsistent: keyframeIssues.length === 0 && snapshotIssues.length === 0 && streamOrderIssues.length === 0,
5772
+ keyframeIssues,
5773
+ snapshotIssues,
5774
+ streamOrderIssues
5775
+ };
5776
+ }
5777
+ /** The read half of the inspection surface; no lever has a field here. */
5778
+ function createInspectionResolvers(source, sync, gate) {
5779
+ const inspector = source.inspector;
5780
+ const operator = (run) => (_parent, args, ctx) => {
5781
+ if (!gate.isOperator(ctx)) throw operatorRequired();
5782
+ return run(args);
5783
+ };
5784
+ const syncInspector = () => {
5785
+ if (!source.syncInspector) throw noSyncInspector();
5786
+ return source.syncInspector;
5787
+ };
5788
+ const accessFor = (ctx, access) => gate.isOperator(ctx) ? access : null;
5789
+ return {
5790
+ Query: { inspection: () => ({}) },
5791
+ ReactorInspection: {
5792
+ info: async (_parent, _args, ctx) => {
5793
+ const info = await inspector.info();
5794
+ return {
5795
+ ...info,
5796
+ access: accessFor(ctx, info.access)
5797
+ };
5798
+ },
5799
+ documentModels: () => inspector.listDocumentModels(),
5800
+ drives: async (_parent, args, ctx) => {
5801
+ const page = await new DriveInspection(gate.readerFor(ctx), source.documentModelRegistry).listDrives(args.cursor ?? void 0, args.limit ?? void 0);
5802
+ const readable = await gate.readableIds(page.results.map((drive) => drive.driveId), ctx);
5803
+ return {
5804
+ results: page.results.filter((drive) => readable.has(drive.driveId)).map(toWireDrive),
5805
+ nextCursor: page.nextCursor ?? null
5806
+ };
5807
+ },
5808
+ driveIntegrity: async (_parent, args, ctx) => {
5809
+ const identifier = await gate.assertCanRead(args.driveId, ctx);
5810
+ const served = await gate.servedDocument(identifier, args.branch, ctx);
5811
+ if (!served) throw new ForbiddenError("to read this document");
5812
+ return new DriveInspection(gate.readerFor(ctx), source.documentModelRegistry).checkDriveIntegrity(served.id, args.branch);
5813
+ },
5814
+ attachmentInfo: operator(async () => {
5815
+ const info = await inspector.getAttachmentInfo();
5816
+ return {
5817
+ ...info,
5818
+ lastError: info.lastError ?? null
5819
+ };
5820
+ }),
5821
+ queueState: operator(async () => toWireQueueState(await inspector.getQueueState())),
5822
+ processors: operator(async () => (await inspector.getProcessors()).map(toWireProcessor)),
5823
+ catchUpStatus: operator(() => inspector.getCatchUpStatus()),
5824
+ storageHealth: operator(() => inspector.getStorageHealth()),
5825
+ validateDocument: async (_parent, args, ctx) => {
5826
+ const branch = args.branch ?? void 0;
5827
+ const identifier = await gate.assertCanRead(args.documentId, ctx);
5828
+ const served = await gate.servedDocument(identifier, branch, ctx);
5829
+ if (!served) throw new ForbiddenError("to read this document");
5830
+ return servedIssues(await inspector.validateDocument(served.id, branch), served.scopes);
5831
+ },
5832
+ remotes: operator(async () => toWireRemotes(await syncInspector().inspectRemotes(), sync.list())),
5833
+ remote: operator(async (args) => {
5834
+ return toWireRemotes([await syncInspector().inspectRemote(args.remoteName)], sync.list())[0];
5835
+ }),
5836
+ deadLetters: operator(async (args) => {
5837
+ let page;
5838
+ try {
5839
+ page = await syncInspector().listDeadLetters(args.remoteName, args.cursor ?? void 0, args.limit ?? void 0);
5840
+ } catch (error) {
5841
+ if (error instanceof InvalidDeadLetterCursorError) throw new GraphQLError(error.message, { extensions: { code: "BAD_USER_INPUT" } });
5842
+ throw error;
5843
+ }
5844
+ return {
5845
+ remoteName: page.remoteName,
5846
+ results: page.results.map(toWireDeadLetter),
5847
+ nextCursor: page.nextCursor ?? null
5848
+ };
5849
+ })
5850
+ }
5851
+ };
5852
+ }
5853
+ //#endregion
5854
+ //#region src/graphql/inspection/schema.ts
5855
+ const inspectionTypeDefs = gql`
5856
+ type ReactorStorageFacts {
5857
+ engine: String!
5858
+ persistence: String!
5859
+ durable: Boolean!
5860
+ selfHeal: Boolean!
5861
+ }
5862
+
5863
+ type InspectorAccess {
5864
+ admin: Boolean!
5865
+ sql: Boolean!
5866
+ }
5867
+
5868
+ """
5869
+ What this reactor reports about itself. access is null unless the caller is
5870
+ one of the host's admins.
5871
+ """
5872
+ type ReactorInfo {
5873
+ storage: ReactorStorageFacts!
5874
+ workflows: Boolean!
5875
+ syncChannels: [String!]!
5876
+ access: InspectorAccess
5877
+ }
5878
+
5879
+ type InspectionDocumentModel {
5880
+ documentType: String!
5881
+ name: String!
5882
+ version: Int!
5883
+ supportedVersions: [Int!]!
5884
+ }
5885
+
5886
+ type InspectionDrive {
5887
+ driveId: String!
5888
+ name: String!
5889
+ branch: String!
5890
+ collectionId: String!
5891
+ documentType: String!
5892
+ nodeCount: Int!
5893
+ fileCount: Int!
5894
+ folderCount: Int!
5895
+ otherNodeCount: Int!
5896
+ unreadableNodeCount: Int!
5897
+ icon: String
5898
+ }
5899
+
5900
+ type InspectionDrivePage {
5901
+ results: [InspectionDrive!]!
5902
+ nextCursor: String
5903
+ }
5904
+
5905
+ type InspectionDriveIntegrityRef {
5906
+ id: String!
5907
+ documentType: String!
5908
+ }
5909
+
5910
+ type InspectionDriveIntegrity {
5911
+ driveId: String!
5912
+ checkedNodeCount: Int!
5913
+ totalFileNodeCount: Int!
5914
+ """
5915
+ File nodes whose document is missing or not served to you; the two are
5916
+ reported alike so a withheld document's existence does not leak.
5917
+ """
5918
+ missingDocuments: [InspectionDriveIntegrityRef!]!
5919
+ unsupportedTypes: [InspectionDriveIntegrityRef!]!
5920
+ }
5921
+
5922
+ type InspectionAttachmentInfo {
5923
+ present: Boolean!
5924
+ storeKind: String!
5925
+ hasReplicator: Boolean!
5926
+ replicatorRunning: Boolean!
5927
+ backlogScanned: Boolean!
5928
+ refsSeen: Int!
5929
+ held: Int!
5930
+ bytesHeld: Float!
5931
+ queued: Int!
5932
+ fetching: Int!
5933
+ pendingFetches: Int!
5934
+ waiting: Int!
5935
+ notFound: Int!
5936
+ failed: Int!
5937
+ lastError: String
5938
+ }
5939
+
5940
+ """
5941
+ A queued job without its actions or operations.
5942
+ """
5943
+ type InspectionQueueJob {
5944
+ id: String!
5945
+ kind: String!
5946
+ documentId: String!
5947
+ scope: String!
5948
+ branch: String!
5949
+ status: String!
5950
+ actionCount: Int!
5951
+ operationCount: Int!
5952
+ retryCount: Int!
5953
+ }
5954
+
5955
+ type InspectionQueueState {
5956
+ isPaused: Boolean!
5957
+ totalPending: Int!
5958
+ totalExecuting: Int!
5959
+ pendingJobs: [InspectionQueueJob!]!
5960
+ executingJobs: [InspectionQueueJob!]!
5961
+ }
5962
+
5963
+ type InspectionProcessor {
5964
+ processorId: String!
5965
+ factoryId: String!
5966
+ driveId: String!
5967
+ processorIndex: Int!
5968
+ lastOrdinal: Float!
5969
+ status: String!
5970
+ lastError: String
5971
+ lastErrorTimestampUtcMs: Float
5972
+ }
5973
+
5974
+ """
5975
+ healthy means something only when tracked; an untracked store reports false.
5976
+ """
5977
+ type InspectionStorageHealth {
5978
+ tracked: Boolean!
5979
+ healthy: Boolean!
5980
+ everRecreated: Boolean!
5981
+ recreateCount: Int!
5982
+ }
5983
+
5984
+ """
5985
+ Issues in scopes the caller may not read are left out.
5986
+ """
5987
+ type InspectionValidation {
5988
+ documentId: String!
5989
+ isConsistent: Boolean!
5990
+ keyframeIssues: [JSONObject!]!
5991
+ snapshotIssues: [JSONObject!]!
5992
+ streamOrderIssues: [JSONObject!]!
5993
+ }
5994
+
5995
+ type InspectionCursor {
5996
+ cursorType: String!
5997
+ cursorOrdinal: Float!
5998
+ lastSyncedAtUtcMs: Float
5999
+ liveAckOrdinal: Float!
6000
+ liveLatestOrdinal: Float!
6001
+ }
6002
+
6003
+ type InspectionMailboxDepths {
6004
+ inbox: Int!
6005
+ outbox: Int!
6006
+ deadLetter: Int!
6007
+ }
6008
+
6009
+ type InspectionConnectionHealth {
6010
+ snapshot: JSONObject!
6011
+ neverSucceeded: Boolean!
6012
+ stalenessMs: Float
6013
+ }
6014
+
6015
+ type InspectionRemote {
6016
+ remoteName: String!
6017
+ remoteId: String!
6018
+ inboxCursor: InspectionCursor!
6019
+ outboxCursor: InspectionCursor!
6020
+ mailboxDepths: InspectionMailboxDepths!
6021
+ connection: InspectionConnectionHealth!
6022
+ meta: JSONObject!
6023
+ }
6024
+
6025
+ """
6026
+ A dead letter without its operations.
6027
+ """
6028
+ type InspectionDeadLetter {
6029
+ id: String!
6030
+ jobId: String!
6031
+ documentId: String!
6032
+ branch: String!
6033
+ scopes: [String!]!
6034
+ errorType: String!
6035
+ errorMessage: String!
6036
+ operationCount: Int!
6037
+ }
6038
+
6039
+ type InspectionDeadLetterPage {
6040
+ remoteName: String!
6041
+ results: [InspectionDeadLetter!]!
6042
+ nextCursor: String
6043
+ }
6044
+
6045
+ """
6046
+ Reads only. info and documentModels serve every caller; drive and document
6047
+ reads are made as the caller through the reactor read gate; the rest need
6048
+ one of the host's admins.
6049
+ """
6050
+ type ReactorInspection {
6051
+ info: ReactorInfo!
6052
+ documentModels: [InspectionDocumentModel!]!
6053
+ drives(cursor: String, limit: Int): InspectionDrivePage!
6054
+ driveIntegrity(driveId: String!, branch: String!): InspectionDriveIntegrity!
6055
+ attachmentInfo: InspectionAttachmentInfo!
6056
+ queueState: InspectionQueueState!
6057
+ processors: [InspectionProcessor!]!
6058
+ catchUpStatus: JSONObject!
6059
+ storageHealth: InspectionStorageHealth!
6060
+ """
6061
+ Replays the whole document; expensive.
6062
+ """
6063
+ validateDocument(documentId: String!, branch: String): InspectionValidation!
6064
+ remotes: [InspectionRemote!]!
6065
+ remote(remoteName: String!): InspectionRemote!
6066
+ deadLetters(
6067
+ remoteName: String!
6068
+ cursor: String
6069
+ limit: Int
6070
+ ): InspectionDeadLetterPage!
6071
+ }
6072
+
6073
+ type Query {
6074
+ inspection: ReactorInspection!
6075
+ }
6076
+ `;
6077
+ //#endregion
6078
+ //#region src/graphql/inspection/source.ts
6079
+ function createReactorInspectionSource(module, options = {}) {
6080
+ const inspector = createReactorInspector(module, {
6081
+ storageHealth: options.storageHealth,
6082
+ attachmentStore: options.attachmentStore,
6083
+ workflows: options.workflows,
6084
+ access: READ_ONLY_ACCESS
6085
+ });
6086
+ return {
6087
+ inspector,
6088
+ syncInspector: module.syncModule?.syncInspector,
6089
+ documentModelRegistry: module.documentModelRegistry,
6090
+ facts: inspector
6091
+ };
6092
+ }
6093
+ //#endregion
6094
+ //#region src/graphql/inspection/subgraph.ts
6095
+ function isListedAdmin(config, address) {
6096
+ if (!address) return false;
6097
+ const caller = address.toLowerCase();
6098
+ return config.admins.some((admin) => admin.toLowerCase() === caller);
6099
+ }
6100
+ /** The read half of the reactor's inspection surface. */
6101
+ var InspectionSubgraph = class extends BaseSubgraph {
6102
+ name = "inspection";
6103
+ hasSubscriptions = false;
6104
+ typeDefs = inspectionTypeDefs;
6105
+ resolvers;
6106
+ constructor(args) {
6107
+ super(args);
6108
+ if (!args.inspection) throw new Error("InspectionSubgraph requires an inspection source; the host did not provide one");
6109
+ this.resolvers = createInspectionResolvers(args.inspection, this.syncManager, this.callerGate());
6110
+ }
6111
+ callerGate() {
6112
+ return {
6113
+ isOperator: (ctx) => isListedAdmin(this.authorizationService.config, ctx.user?.address),
6114
+ readerFor: (ctx) => this.readerFor(ctx),
6115
+ assertCanRead: async (identifier, ctx) => (await this.assertCanRead(identifier, ctx)).fetchIdentifier,
6116
+ readableIds: async (ids, ctx) => {
6117
+ const readable = await this.readableByHost(ids.map((id) => ({ id })), ctx);
6118
+ return new Set(readable.map((item) => item.id));
6119
+ },
6120
+ servedDocument: (identifier, branch, ctx) => this.servedDocument(identifier, branch, ctx)
6121
+ };
6122
+ }
6123
+ readerFor(ctx) {
6124
+ const subject = this.viewSubject(ctx);
6125
+ return {
6126
+ find: (search, view, paging) => this.reactorClient.find(search, {
6127
+ ...view,
6128
+ subject
6129
+ }, paging),
6130
+ get: (id, view) => this.reactorClient.get(id, {
6131
+ ...view,
6132
+ subject
6133
+ })
6134
+ };
6135
+ }
6136
+ async servedDocument(identifier, branch, ctx) {
6137
+ const view = {
6138
+ branch,
6139
+ subject: this.viewSubject(ctx)
6140
+ };
6141
+ let served;
6142
+ try {
6143
+ served = await this.reactorClient.isServed(identifier, view);
6144
+ } catch {
6145
+ return;
6146
+ }
6147
+ if (!served) return;
6148
+ const document = await this.reactorClient.get(identifier, view);
6149
+ return {
6150
+ id: document.header.id,
6151
+ scopes: new Set(Object.keys(document.state ?? {}))
6152
+ };
6153
+ }
6154
+ };
6155
+ //#endregion
5619
6156
  //#region src/graphql/packages/resolvers.ts
5620
6157
  function requireAdmin(authorizationService, ctx) {
5621
6158
  if (!authorizationService.isSupremeAdmin(ctx.user?.address)) throw new GraphQLError("Admin access required");
@@ -6009,6 +6546,10 @@ const DocumentChangeEventDTO = z.object({
6009
6546
  documents: z.array(PHDocumentDTO),
6010
6547
  context: DocumentChangeContextDTO.nullable().optional()
6011
6548
  }).strip();
6549
+ const CreateDefaultsDTO = z.object({
6550
+ signaturePolicy: z.string(),
6551
+ protocolVersions: JSONObjectDTO
6552
+ }).strip();
6012
6553
  const ActionEvaluationDTO = z.object({
6013
6554
  decision: z.enum(["ALLOW", "DENY"]),
6014
6555
  reason: z.string().nullable().optional()
@@ -6059,6 +6600,10 @@ const operationValidators = {
6059
6600
  GetJobStatus: (data) => {
6060
6601
  if (data.jobStatus) JobInfoFieldsDTO.parse(data.jobStatus);
6061
6602
  },
6603
+ GetDocumentServed: () => {},
6604
+ GetCreateDefaults: (data) => {
6605
+ if (data.createDefaults) CreateDefaultsDTO.parse(data.createDefaults);
6606
+ },
6062
6607
  EvaluateActions: (data) => {
6063
6608
  if (data.evaluateActions) ActionEvaluationsDTO.parse(data.evaluateActions);
6064
6609
  },
@@ -6130,7 +6675,7 @@ function createReactorGraphQLClient(url, fetchImpl = fetch, headers) {
6130
6675
  }
6131
6676
  //#endregion
6132
6677
  //#region src/graphql/reactor/schema.graphql
6133
- 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 @deprecated(reason: \"Ignored. Filter by type or parentId.\")\n}\n\ninput OperationsFilterInput {\n documentIdOrSlug: String\n documentId: String @deprecated(reason: \"Use documentIdOrSlug.\")\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 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 meta: JSONObject\n protocolVersions: JSONObject\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 hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\n# A directed edge between two documents, with the metadata recorded against it.\ntype DocumentRelationship {\n sourceId: String!\n targetId: String!\n relationshipType: String!\n metadata: JSONObject\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\ntype DocumentRelationshipResultPage {\n items: [DocumentRelationship!]!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\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 # The server's current manifest revision\n manifestRevision: String\n # The client's manifest revision as the server holds it\n peerManifestRevision: String\n}\n\ntype SyncHoldReason {\n protocol: String!\n version: Int!\n peerSupports: [Int!]!\n}\n\n# A document held back from a remote whose peer cannot run its versions\ntype SyncHold {\n remoteName: String!\n documentId: String!\n branch: String!\n reason: SyncHoldReason!\n heldAtUtcMs: String!\n}\n\ntype PeerAgreementMember {\n remoteName: String!\n # False for a peer that announced nothing: it gets the baselines\n announced: Boolean!\n protocols: JSONObject!\n features: JSONObject!\n}\n\ntype PeerAgreementLimit {\n protocol: String!\n # The remotes whose peers lack the version new documents would take\n remoteNames: [String!]!\n}\n\ntype PeerAgreement {\n collectionId: String!\n local: JSONObject!\n members: [PeerAgreementMember!]!\n limitedBy: [PeerAgreementLimit!]!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype ConsistencyCoordinate {\n documentId: String!\n scope: String!\n branch: String!\n operationIndex: Int!\n}\n\n\"\"\"\nThe write positions a job reached, for read-after-write. Only meaningful once\nthe job has completed.\n\"\"\"\ntype ConsistencyToken {\n version: Int!\n createdAtUtcIso: String!\n coordinates: [ConsistencyCoordinate!]!\n}\n\n\"The batch a job was submitted in.\"\ntype JobBatchMeta {\n batchId: String!\n batchJobIds: [String!]!\n}\n\ntype JobInfo {\n id: String!\n\n \"\"\"\n The document the job operates on. Empty for a job this server does not know,\n which then comes back FAILED with \"Job not found\".\n \"\"\"\n documentId: String!\n status: String!\n\n \"\"\"\n What the job produced, once it has produced anything. Null until then, which\n is the state every job is in when it is handed back from a submission.\n \"\"\"\n result: JSONObject\n\n error: String\n\n \"The error's class name, such as `DocumentAlreadyExistsError`, when it failed.\"\n errorName: String\n createdAt: DateTime!\n completedAt: DateTime\n consistencyToken: ConsistencyToken!\n meta: JobBatchMeta!\n}\n\n\"\"\"\nOne job's outcome within a batch, pairing the plan key the caller gave with the\njob that applied it. Mirrors one entry of the `Record<string, JobInfo>` that\n`IReactor.executeBatch` returns (packages/reactor/src/core/types.ts).\n\"\"\"\ntype BatchJobResult {\n key: String!\n job: JobInfo!\n}\n\n\"\"\"\nThe outcome of a batch mutation, one entry per job the request named. The wire\nshape of `BatchExecutionResult` (`{ jobs }`), keyed on each entry by the plan\nkey rather than by position so the client can rebuild the record. The batch is\nnot atomic: each job commits on its own and a failed job releases its\ndependents, so each entry's status says what happened to that job, and\nre-submitting re-applies the jobs that succeeded.\n\"\"\"\ntype BatchExecutionResult {\n jobs: [BatchJobResult!]!\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\n \"\"\"\n What the job produced, once it has produced anything. Nullable for the same\n reason as `JobInfo.result`: a job that has not written yet has produced\n nothing, and an empty object would claim otherwise.\n \"\"\"\n result: JSONObject\n\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\"\"\"\nOne operation an authorization preflight predicts a verdict for.\n\n`input` is what a conditional grant reads, so a candidate standing for a\nfilled-in form carries that form's input. Omitting it predicts the verdict an\nempty input earns, not the verdict the filled-in form will get.\n\"\"\"\ninput ActionCandidateInput {\n scope: String!\n type: String!\n input: JSONObject\n}\n\nenum AuthDecision {\n ALLOW\n DENY\n}\n\n\"\"\"\nOne candidate's predicted verdict. `reason` carries the refusal a DENY is\nrecorded with, and is null on an ALLOW.\n\"\"\"\ntype ActionEvaluation {\n decision: AuthDecision!\n reason: String\n}\n\n\"\"\"\nThe predicted verdicts for a set of candidates, in the order they were given,\nwith the aggregates a caller branches on.\n\nThe aggregates are redundant -- a verdict is binary -- and all four are returned\nso a caller reads the one its question is phrased in rather than negating\nanother. Over no candidates every aggregate is false: nothing is allowed and\nnothing is denied.\n\"\"\"\ntype ActionEvaluations {\n evaluations: [ActionEvaluation!]!\n allAllowed: Boolean!\n anyAllowed: Boolean!\n allDenied: Boolean!\n anyDenied: Boolean!\n}\n\n# Input types for sync operations\n\"\"\"\nAn action's context, as a caller submits it.\n\nDeclares every field `ActionContext` carries, because an input object rejects a\nfield it does not declare: an action stamped with the head it applies to - which\nis what a signing client sends - is refused outright if these are missing, and\nthat refusal is a whole-request one, not a per-field one.\n\n`prevOpHash` and `prevOpIndex` are the scope head the action was composed\nagainst. They are recorded rather than enforced; a signature is verified from\nthe params carried in its own tuple, which do not include them.\n\"\"\"\ninput ActionContextInput {\n \"\"\"\n The index of the last operation in the scope the action applies to.\n \"\"\"\n prevOpIndex: Int\n\n \"\"\"\n The hash of the state the action applies to.\n \"\"\"\n prevOpHash: String\n\n \"\"\"\n Covers replay of an operation that would otherwise be a no-op.\n \"\"\"\n nonce: String\n\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\"\"\"\nOne mutation job of a batch, mirroring the reactor's `ExecutionJobPlan`\n(packages/reactor/src/core/types.ts). The batch runs its jobs in dependency\norder only, not atomically: each job commits on its own with no batch rollback,\nand a failed job still releases its dependents.\n\n`documentIdOrSlug` is the document the job's actions apply to -- an id that does\nnot yet exist when the job's actions create it. `actions` are coerced against\n`ActionInput` and passed through exactly as `execute` does, so a client-signed\naction keeps its signature. `scope` is the single scope every action in the job\nshares, and `dependsOn` names the plan keys this job is ordered after. `branch`\ndefaults to `main`.\n\"\"\"\ninput ExecutionJobInput {\n key: String!\n documentIdOrSlug: String!\n scope: String!\n branch: String\n actions: [ActionInput!]!\n dependsOn: [String!]!\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 SyncRefusalInput {\n documentId: String!\n branch: String!\n # \"marker\" for a refused purge marker; absent for UNSUPPORTED_PROTOCOL\n kind: String\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n # The client's peer manifest; absent from a client without peer agreement\n manifest: JSONObject\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n # The server's peer manifest\n manifest: JSONObject\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(\n idOrSlug: String\n identifier: String @deprecated(reason: \"Use idOrSlug.\")\n view: ViewFilterInput\n ): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\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 targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get the outgoing relationship edges of a source document. Unlike\n # documentOutgoingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentOutgoingRelationshipEdges(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Get the incoming relationship edges of a target document. Unlike\n # documentIncomingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentIncomingRelationshipEdges(\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\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 \"\"\"\n Predicts whether the calling subject would be admitted to execute each of a\n set of candidate operations, without submitting any of them. A UI asks this to\n disable a control rather than offer an action that fails on submit.\n\n The answer is a prediction, not a promise:\n\n - Real admission compiles an append condition over everything it read and the\n store enforces it at write time. A preflight reads no future, so a policy\n change landing between this answer and the submit changes the verdict. The\n submit path stays the only authority.\n - The verdict is evaluated at the stream heads, so it is correct for a\n candidate about to be submitted. A backdated submission is out of contract;\n the reactor decides that one by position.\n - A candidate whose verdict depends on its input has to carry that input, since\n a conditional grant reads it.\n\n The subject is the authenticated caller and cannot be named in the request:\n answering for an arbitrary subject would disclose what a policy grants\n somebody else. It carries both the caller's address and the did:key of the app\n instance whose token authenticated the request, so a policy naming either\n matches the same principal the write path presents.\n\n Requires the reactor's authEnforcement feature flag. Without it the reactor\n holds no decision model, so this fails with extensions.code\n AUTH_EVALUATION_UNSUPPORTED rather than guessing.\n \"\"\"\n evaluateActions(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n branch: String\n candidates: [ActionCandidateInput!]!\n ): ActionEvaluations!\n\n \"\"\"\n Polls for sync envelopes from a channel.\n\n An operation stored before the API required an action id cannot be\n represented by this schema, and rather than serving a partial response beside\n a bare non-null violation, this fails with extensions.code\n MALFORMED_STORED_OPERATION naming the operation. That code means the failure\n is worth polling through: the document holding the operation needs repairing,\n but the channel serves every other document, and a peer that stopped polling\n would stop receiving those too.\n \"\"\"\n pollSyncEnvelopes(\n channelId: String!\n outboxAck: Int!\n outboxLatest: Int!\n # The client's manifest revision; absent from a client without peer agreement\n manifestRevision: String\n # Documents the client refused from earlier polls: held, or a refused marker\n refusals: [SyncRefusalInput!]\n ): PollSyncEnvelopesResult!\n\n # Supreme admin, or the address the named remote's channel is bound to\n syncHolds(remoteName: String, documentId: String): [SyncHold!]!\n\n # Supreme admin, or an address bound to a channel in the collection\n peerAgreement(collectionId: String!): PeerAgreement!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(\n document: JSONObject!\n parentIdOrSlug: String\n parentIdentifier: String @deprecated(reason: \"Use parentIdOrSlug.\")\n ): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdOrSlug: String\n parentIdentifier: String @deprecated(reason: \"Use parentIdOrSlug.\")\n ): PHDocument!\n\n \"\"\"\n Applies actions to a document and waits for the result.\n\n Each action is coerced against `ActionInput`, so one missing a field the wire\n declares is refused before any work starts. The id in particular: it is hashed\n into the operation id and replay dedupes by it, so an action without one is\n stored under an operation id shared by every id-less operation on the same\n document, scope and branch.\n\n Named for `IReactorClient.execute`, which this is the wire form of. `branch`\n defaults to `main`.\n \"\"\"\n execute(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n actions: [ActionInput!]!\n branch: String\n ): PHDocument!\n\n \"\"\"\n Submits actions to a document and returns the job that will apply them.\n\n Coerced exactly as `execute` is, so a malformed action is refused here rather\n than surfacing later as a failed job. Returns the job rather than only its id,\n so a caller has the status and the creation time it would otherwise have to\n ask for; `result` is null until the job produces one.\n \"\"\"\n executeAsync(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n actions: [ActionInput!]!\n branch: String\n ): JobInfo!\n\n \"\"\"\n Applies multiple mutation jobs in dependency order and waits for all of them,\n the wire form of `IReactorClient.executeBatch`. Each job's actions are coerced\n against `ActionInput` exactly as `execute` is, so client-signed actions pass\n through unchanged, and `dependsOn` orders the jobs. It is synchronous: the jobs\n are applied and awaited before the result returns, so every returned `JobInfo`\n is a completed one rather than the pending receipt the in-process reactor hands\n back. A batch is ordering, not a transaction: each job commits on its own, and\n a failed job still releases the jobs that depend on it, so any job, before or\n after a failure, may have committed. A failed job is therefore returned as a\n FAILED entry with its `error` and `errorName`, beside the final state of every\n other job, rather than failing the mutation. The mutation itself fails only\n when no job state can be reported.\n\n A job carrying `CREATE_DOCUMENT` must be the batch's only job, with the create\n as its first action naming the job's document. Its other actions may only be\n an `UPGRADE_DOCUMENT` of the new document and `ADD_RELATIONSHIP`s targeting\n it. It is authorized like `createDocument`: write on every source of those\n relationships, or the right to create when there is none.\n \"\"\"\n executeBatch(jobs: [ExecutionJobInput!]!): BatchExecutionResult!\n\n # Apply actions to a document (synchronous)\n mutateDocument(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): PHDocument!\n @deprecated(\n reason: \"Use execute. Actions here are untyped, so a malformed one is refused by a hand-written check rather than by the schema, and `view.scopes` is accepted but ignored.\"\n )\n\n # Submit actions to a document (asynchronous)\n mutateDocumentAsync(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): String!\n @deprecated(\n reason: \"Use executeAsync, which is typed and returns the job rather than only its id.\"\n )\n\n # Rename a document\n renameDocument(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\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 documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents. Adding one that already exists is\n # a no-op, metadata included -- use updateRelationship to change an edge.\n addRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Replace the metadata of an existing relationship, preserving its createdAt.\n # Omitting metadata, or passing null, clears it.\n updateRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdOrSlug: String\n sourceParentIdentifier: String\n @deprecated(reason: \"Use sourceParentIdOrSlug.\")\n targetParentIdOrSlug: String\n targetParentIdentifier: String\n @deprecated(reason: \"Use targetParentIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(\n idOrSlug: String\n identifier: String @deprecated(reason: \"Use idOrSlug.\")\n propagate: PropagationMode\n ): Boolean!\n\n # Delete multiple documents\n deleteDocuments(\n idsOrSlugs: [String!]\n identifiers: [String!] @deprecated(reason: \"Use idsOrSlugs.\")\n propagate: PropagationMode\n ): 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(\n envelopes: [SyncEnvelopeInput!]!\n # The server's manifest revision the client gated these envelopes under\n peerManifestRevision: String\n ): 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";
6678
+ 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 @deprecated(reason: \"Ignored. Filter by type or parentId.\")\n}\n\ninput OperationsFilterInput {\n documentIdOrSlug: String\n documentId: String @deprecated(reason: \"Use documentIdOrSlug.\")\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 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 meta: JSONObject\n protocolVersions: JSONObject\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 hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\n# A directed edge between two documents, with the metadata recorded against it.\ntype DocumentRelationship {\n sourceId: String!\n targetId: String!\n relationshipType: String!\n metadata: JSONObject\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\ntype DocumentRelationshipResultPage {\n items: [DocumentRelationship!]!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\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 # The server's current manifest revision\n manifestRevision: String\n # The client's manifest revision as the server holds it\n peerManifestRevision: String\n}\n\ntype SyncHoldReason {\n protocol: String!\n version: Int!\n peerSupports: [Int!]!\n}\n\n# A document held back from a remote whose peer cannot run its versions\ntype SyncHold {\n remoteName: String!\n documentId: String!\n branch: String!\n reason: SyncHoldReason!\n heldAtUtcMs: String!\n}\n\ntype PeerAgreementMember {\n remoteName: String!\n # False for a peer that announced nothing: it gets the baselines\n announced: Boolean!\n protocols: JSONObject!\n features: JSONObject!\n}\n\ntype PeerAgreementLimit {\n protocol: String!\n # The remotes whose peers lack the version new documents would take\n remoteNames: [String!]!\n}\n\ntype PeerAgreement {\n collectionId: String!\n local: JSONObject!\n members: [PeerAgreementMember!]!\n limitedBy: [PeerAgreementLimit!]!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\n# What a new document gets when its creator chooses nothing\ntype CreateDefaults {\n # \"legacy\" or \"v2-required\"; a client refuses a value it does not know\n signaturePolicy: String!\n protocolVersions: JSONObject!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype ConsistencyCoordinate {\n documentId: String!\n scope: String!\n branch: String!\n operationIndex: Int!\n}\n\n\"\"\"\nThe write positions a job reached, for read-after-write. Only meaningful once\nthe job has completed.\n\"\"\"\ntype ConsistencyToken {\n version: Int!\n createdAtUtcIso: String!\n coordinates: [ConsistencyCoordinate!]!\n}\n\n\"The batch a job was submitted in.\"\ntype JobBatchMeta {\n batchId: String!\n batchJobIds: [String!]!\n}\n\ntype JobInfo {\n id: String!\n\n \"\"\"\n The document the job operates on. Empty for a job this server does not know,\n which then comes back FAILED with \"Job not found\".\n \"\"\"\n documentId: String!\n status: String!\n\n \"\"\"\n What the job produced, once it has produced anything. Null until then, which\n is the state every job is in when it is handed back from a submission.\n \"\"\"\n result: JSONObject\n\n error: String\n\n \"The error's class name, such as `DocumentAlreadyExistsError`, when it failed.\"\n errorName: String\n createdAt: DateTime!\n completedAt: DateTime\n consistencyToken: ConsistencyToken!\n meta: JobBatchMeta!\n}\n\n\"\"\"\nOne job's outcome within a batch, pairing the plan key the caller gave with the\njob that applied it. Mirrors one entry of the `Record<string, JobInfo>` that\n`IReactor.executeBatch` returns (packages/reactor/src/core/types.ts).\n\"\"\"\ntype BatchJobResult {\n key: String!\n job: JobInfo!\n}\n\n\"\"\"\nThe outcome of a batch mutation, one entry per job the request named. The wire\nshape of `BatchExecutionResult` (`{ jobs }`), keyed on each entry by the plan\nkey rather than by position so the client can rebuild the record. The batch is\nnot atomic: each job commits on its own and a failed job releases its\ndependents, so each entry's status says what happened to that job, and\nre-submitting re-applies the jobs that succeeded.\n\"\"\"\ntype BatchExecutionResult {\n jobs: [BatchJobResult!]!\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\n \"\"\"\n What the job produced, once it has produced anything. Nullable for the same\n reason as `JobInfo.result`: a job that has not written yet has produced\n nothing, and an empty object would claim otherwise.\n \"\"\"\n result: JSONObject\n\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\"\"\"\nOne operation an authorization preflight predicts a verdict for.\n\n`input` is what a conditional grant reads, so a candidate standing for a\nfilled-in form carries that form's input. Omitting it predicts the verdict an\nempty input earns, not the verdict the filled-in form will get.\n\"\"\"\ninput ActionCandidateInput {\n scope: String!\n type: String!\n input: JSONObject\n}\n\nenum AuthDecision {\n ALLOW\n DENY\n}\n\n\"\"\"\nOne candidate's predicted verdict. `reason` carries the refusal a DENY is\nrecorded with, and is null on an ALLOW.\n\"\"\"\ntype ActionEvaluation {\n decision: AuthDecision!\n reason: String\n}\n\n\"\"\"\nThe predicted verdicts for a set of candidates, in the order they were given,\nwith the aggregates a caller branches on.\n\nThe aggregates are redundant -- a verdict is binary -- and all four are returned\nso a caller reads the one its question is phrased in rather than negating\nanother. Over no candidates every aggregate is false: nothing is allowed and\nnothing is denied.\n\"\"\"\ntype ActionEvaluations {\n evaluations: [ActionEvaluation!]!\n allAllowed: Boolean!\n anyAllowed: Boolean!\n allDenied: Boolean!\n anyDenied: Boolean!\n}\n\n# Input types for sync operations\n\"\"\"\nAn action's context, as a caller submits it.\n\nDeclares every field `ActionContext` carries, because an input object rejects a\nfield it does not declare: an action stamped with the head it applies to - which\nis what a signing client sends - is refused outright if these are missing, and\nthat refusal is a whole-request one, not a per-field one.\n\n`prevOpHash` and `prevOpIndex` are the scope head the action was composed\nagainst. They are recorded rather than enforced; a signature is verified from\nthe params carried in its own tuple, which do not include them.\n\"\"\"\ninput ActionContextInput {\n \"\"\"\n The index of the last operation in the scope the action applies to.\n \"\"\"\n prevOpIndex: Int\n\n \"\"\"\n The hash of the state the action applies to.\n \"\"\"\n prevOpHash: String\n\n \"\"\"\n Covers replay of an operation that would otherwise be a no-op.\n \"\"\"\n nonce: String\n\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\"\"\"\nOne mutation job of a batch, mirroring the reactor's `ExecutionJobPlan`\n(packages/reactor/src/core/types.ts). The batch runs its jobs in dependency\norder only, not atomically: each job commits on its own with no batch rollback,\nand a failed job still releases its dependents.\n\n`documentIdOrSlug` is the document the job's actions apply to -- an id that does\nnot yet exist when the job's actions create it. `actions` are coerced against\n`ActionInput` and passed through exactly as `execute` does, so a client-signed\naction keeps its signature. `scope` is the single scope every action in the job\nshares, and `dependsOn` names the plan keys this job is ordered after. `branch`\ndefaults to `main`.\n\"\"\"\ninput ExecutionJobInput {\n key: String!\n documentIdOrSlug: String!\n scope: String!\n branch: String\n actions: [ActionInput!]!\n dependsOn: [String!]!\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 SyncRefusalInput {\n documentId: String!\n branch: String!\n # \"marker\" for a refused purge marker; absent for UNSUPPORTED_PROTOCOL\n kind: String\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n # The client's peer manifest; absent from a client without peer agreement\n manifest: JSONObject\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n # The server's peer manifest\n manifest: JSONObject\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(\n idOrSlug: String\n identifier: String @deprecated(reason: \"Use idOrSlug.\")\n view: ViewFilterInput\n ): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\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 targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get the outgoing relationship edges of a source document. Unlike\n # documentOutgoingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentOutgoingRelationshipEdges(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Get the incoming relationship edges of a target document. Unlike\n # documentIncomingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentIncomingRelationshipEdges(\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\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 # Whether findDocuments would serve the document to the caller. Absent or\n # withheld is false; a server that cannot decide answers with an error.\n documentServed(idOrSlug: String!, view: ViewFilterInput): Boolean!\n\n # The signature policy and protocol versions a new document gets, under the\n # parent when one is named. A parent the caller may not read is refused.\n createDefaults(parentIdOrSlug: String): CreateDefaults!\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n \"\"\"\n Predicts whether the calling subject would be admitted to execute each of a\n set of candidate operations, without submitting any of them. A UI asks this to\n disable a control rather than offer an action that fails on submit.\n\n The answer is a prediction, not a promise:\n\n - Real admission compiles an append condition over everything it read and the\n store enforces it at write time. A preflight reads no future, so a policy\n change landing between this answer and the submit changes the verdict. The\n submit path stays the only authority.\n - The verdict is evaluated at the stream heads, so it is correct for a\n candidate about to be submitted. A backdated submission is out of contract;\n the reactor decides that one by position.\n - A candidate whose verdict depends on its input has to carry that input, since\n a conditional grant reads it.\n\n The subject is the authenticated caller and cannot be named in the request:\n answering for an arbitrary subject would disclose what a policy grants\n somebody else. It carries both the caller's address and the did:key of the app\n instance whose token authenticated the request, so a policy naming either\n matches the same principal the write path presents.\n\n Requires the reactor's authEnforcement feature flag. Without it the reactor\n holds no decision model, so this fails with extensions.code\n AUTH_EVALUATION_UNSUPPORTED rather than guessing.\n \"\"\"\n evaluateActions(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n branch: String\n candidates: [ActionCandidateInput!]!\n ): ActionEvaluations!\n\n \"\"\"\n Polls for sync envelopes from a channel.\n\n An operation stored before the API required an action id cannot be\n represented by this schema, and rather than serving a partial response beside\n a bare non-null violation, this fails with extensions.code\n MALFORMED_STORED_OPERATION naming the operation. That code means the failure\n is worth polling through: the document holding the operation needs repairing,\n but the channel serves every other document, and a peer that stopped polling\n would stop receiving those too.\n \"\"\"\n pollSyncEnvelopes(\n channelId: String!\n outboxAck: Int!\n outboxLatest: Int!\n # The client's manifest revision; absent from a client without peer agreement\n manifestRevision: String\n # Documents the client refused from earlier polls: held, or a refused marker\n refusals: [SyncRefusalInput!]\n ): PollSyncEnvelopesResult!\n\n # Supreme admin, or the address the named remote's channel is bound to\n syncHolds(remoteName: String, documentId: String): [SyncHold!]!\n\n # Supreme admin, or an address bound to a channel in the collection\n peerAgreement(collectionId: String!): PeerAgreement!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(\n document: JSONObject!\n parentIdOrSlug: String\n parentIdentifier: String @deprecated(reason: \"Use parentIdOrSlug.\")\n ): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdOrSlug: String\n parentIdentifier: String @deprecated(reason: \"Use parentIdOrSlug.\")\n ): PHDocument!\n\n \"\"\"\n Applies actions to a document and waits for the result.\n\n Each action is coerced against `ActionInput`, so one missing a field the wire\n declares is refused before any work starts. The id in particular: it is hashed\n into the operation id and replay dedupes by it, so an action without one is\n stored under an operation id shared by every id-less operation on the same\n document, scope and branch.\n\n Named for `IReactorClient.execute`, which this is the wire form of. `branch`\n defaults to `main`.\n \"\"\"\n execute(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n actions: [ActionInput!]!\n branch: String\n ): PHDocument!\n\n \"\"\"\n Submits actions to a document and returns the job that will apply them.\n\n Coerced exactly as `execute` is, so a malformed action is refused here rather\n than surfacing later as a failed job. Returns the job rather than only its id,\n so a caller has the status and the creation time it would otherwise have to\n ask for; `result` is null until the job produces one.\n \"\"\"\n executeAsync(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n actions: [ActionInput!]!\n branch: String\n ): JobInfo!\n\n \"\"\"\n Applies multiple mutation jobs in dependency order and waits for all of them,\n the wire form of `IReactorClient.executeBatch`. Each job's actions are coerced\n against `ActionInput` exactly as `execute` is, so client-signed actions pass\n through unchanged, and `dependsOn` orders the jobs. It is synchronous: the jobs\n are applied and awaited before the result returns, so every returned `JobInfo`\n is a completed one rather than the pending receipt the in-process reactor hands\n back. A batch is ordering, not a transaction: each job commits on its own, and\n a failed job still releases the jobs that depend on it, so any job, before or\n after a failure, may have committed. A failed job is therefore returned as a\n FAILED entry with its `error` and `errorName`, beside the final state of every\n other job, rather than failing the mutation. The mutation itself fails only\n when no job state can be reported.\n\n A job carrying `CREATE_DOCUMENT` must be the batch's only job, with the create\n as its first action naming the job's document. Its other actions may only be\n an `UPGRADE_DOCUMENT` of the new document and `ADD_RELATIONSHIP`s targeting\n it. It is authorized like `createDocument`: write on every source of those\n relationships, or the right to create when there is none.\n \"\"\"\n executeBatch(jobs: [ExecutionJobInput!]!): BatchExecutionResult!\n\n # Apply actions to a document (synchronous)\n mutateDocument(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): PHDocument!\n @deprecated(\n reason: \"Use execute. Actions here are untyped, so a malformed one is refused by a hand-written check rather than by the schema, and `view.scopes` is accepted but ignored.\"\n )\n\n # Submit actions to a document (asynchronous)\n mutateDocumentAsync(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): String!\n @deprecated(\n reason: \"Use executeAsync, which is typed and returns the job rather than only its id.\"\n )\n\n # Rename a document\n renameDocument(\n documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\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 documentIdOrSlug: String\n documentIdentifier: String @deprecated(reason: \"Use documentIdOrSlug.\")\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents. Adding one that already exists is\n # a no-op, metadata included -- use updateRelationship to change an edge.\n addRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Replace the metadata of an existing relationship, preserving its createdAt.\n # Omitting metadata, or passing null, clears it.\n updateRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdOrSlug: String\n sourceIdentifier: String @deprecated(reason: \"Use sourceIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdOrSlug: String\n sourceParentIdentifier: String\n @deprecated(reason: \"Use sourceParentIdOrSlug.\")\n targetParentIdOrSlug: String\n targetParentIdentifier: String\n @deprecated(reason: \"Use targetParentIdOrSlug.\")\n targetIdOrSlug: String\n targetIdentifier: String @deprecated(reason: \"Use targetIdOrSlug.\")\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(\n idOrSlug: String\n identifier: String @deprecated(reason: \"Use idOrSlug.\")\n propagate: PropagationMode\n ): Boolean!\n\n # Delete multiple documents\n deleteDocuments(\n idsOrSlugs: [String!]\n identifiers: [String!] @deprecated(reason: \"Use idsOrSlugs.\")\n propagate: PropagationMode\n ): 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(\n envelopes: [SyncEnvelopeInput!]!\n # The server's manifest revision the client gated these envelopes under\n peerManifestRevision: String\n ): 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";
6134
6679
  //#endregion
6135
6680
  //#region src/graphql/reactor/pubsub.ts
6136
6681
  const pubSub = new PubSub();
@@ -6477,15 +7022,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
6477
7022
  view: args.view,
6478
7023
  paging: args.paging
6479
7024
  }, this.viewSubject(ctx));
6480
- if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
6481
- const filteredItems = [];
6482
- for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
6483
- return {
6484
- ...result,
6485
- items: filteredItems
6486
- };
6487
- }
6488
- return result;
7025
+ return {
7026
+ ...result,
7027
+ items: await this.readableByHost(result.items, ctx)
7028
+ };
6489
7029
  } catch (error) {
6490
7030
  this.logger.error("Error in documentIncomingRelationships: @Error", error);
6491
7031
  throw error;
@@ -6532,15 +7072,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
6532
7072
  ...args,
6533
7073
  search: args.search ?? {}
6534
7074
  }, this.viewSubject(ctx));
6535
- if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
6536
- const filteredItems = [];
6537
- for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
6538
- return {
6539
- ...result,
6540
- items: filteredItems
6541
- };
6542
- }
6543
- return result;
7075
+ return {
7076
+ ...result,
7077
+ items: await this.readableByHost(result.items, ctx)
7078
+ };
6544
7079
  } catch (error) {
6545
7080
  this.logger.error("Error in findDocuments: @Error", error);
6546
7081
  throw error;
@@ -6565,6 +7100,29 @@ var ReactorSubgraph = class extends BaseSubgraph {
6565
7100
  throw error;
6566
7101
  }
6567
7102
  },
7103
+ documentServed: async (_parent, args, ctx) => {
7104
+ this.logger.debug("documentServed(@args)", args);
7105
+ try {
7106
+ return await this.listsDocument(args.idOrSlug, ctx, {
7107
+ branch: args.view?.branch ?? void 0,
7108
+ scopes: args.view?.scopes ? [...args.view.scopes] : void 0
7109
+ });
7110
+ } catch (error) {
7111
+ this.logger.error("Error in documentServed: @Error", error);
7112
+ throw error;
7113
+ }
7114
+ },
7115
+ createDefaults: async (_parent, args, ctx) => {
7116
+ this.logger.debug("createDefaults(@args)", args);
7117
+ try {
7118
+ const parentIdOrSlug = args.parentIdOrSlug ?? void 0;
7119
+ if (parentIdOrSlug !== void 0 && !await this.listsDocument(parentIdOrSlug, ctx)) throw new ForbiddenError("to read this document");
7120
+ return await createDefaults(this.reactorClient, parentIdOrSlug);
7121
+ } catch (error) {
7122
+ this.logger.error("Error in createDefaults: @Error", error);
7123
+ throw error;
7124
+ }
7125
+ },
6568
7126
  documentOperations: async (_parent, args, ctx) => {
6569
7127
  this.logger.debug("documentOperations(@args)", args);
6570
7128
  try {
@@ -7044,10 +7602,10 @@ const ADMIN_USERS = getAdminUsers();
7044
7602
  //#endregion
7045
7603
  //#region src/graphql/system/version.ts
7046
7604
  function getVersion() {
7047
- return "6.2.3-dev.50";
7605
+ return "6.2.3-dev.52";
7048
7606
  }
7049
7607
  function getGitHash() {
7050
- return "01a5b0227313c401002362834697fee1ddd28fed";
7608
+ return "a37f3ca1923c9cdd7aca3c82e8cc7ef5f3abc035";
7051
7609
  }
7052
7610
  function getGitUrl() {
7053
7611
  return buildTreeUrl(getGitHash());
@@ -7083,6 +7641,8 @@ const PACKAGE_ENTRIES = {
7083
7641
  processors: "node/processors/index.mjs"
7084
7642
  };
7085
7643
  const REGISTRY_ENTRY_ABSENT = "ERR_REGISTRY_ENTRY_ABSENT";
7644
+ const REGISTRY_EXTERNAL_DEPS_MISSING = "ERR_REGISTRY_EXTERNAL_DEPS_MISSING";
7645
+ const PACKAGE_MANIFEST = "powerhouse.manifest.json";
7086
7646
  const MANIFEST_FILE = "manifest.json";
7087
7647
  function defaultRegistryCacheDir(cwd = process.cwd()) {
7088
7648
  return path.join(cwd, ".ph", "registry-packages");
@@ -7168,7 +7728,8 @@ var RegistryPackageCache = class {
7168
7728
  manifest = await this.fill(name, version, dir);
7169
7729
  source = "download";
7170
7730
  }
7171
- await this.linkBareImports(manifest.bareImports);
7731
+ const external = Object.keys(manifest.externalDependencies);
7732
+ await this.linkBareImports(manifest.bareImports, external);
7172
7733
  const entries = {};
7173
7734
  for (const [kind, rel] of Object.entries(manifest.entries)) entries[kind] = path.join(dir, rel);
7174
7735
  const result = {
@@ -7176,7 +7737,12 @@ var RegistryPackageCache = class {
7176
7737
  version,
7177
7738
  dir,
7178
7739
  entries,
7179
- source
7740
+ source,
7741
+ externalDependencies: manifest.externalDependencies,
7742
+ missingExternalDependencies: external.filter((pkg) => {
7743
+ const imported = manifest.bareImports.filter((s) => s === pkg || s.startsWith(`${pkg}/`));
7744
+ return !(imported.length > 0 ? imported : [pkg]).some((s) => this.resolvesForCachedPackages(s, pkg));
7745
+ })
7180
7746
  };
7181
7747
  this.verified.set(dir, result);
7182
7748
  return result;
@@ -7192,6 +7758,10 @@ var RegistryPackageCache = class {
7192
7758
  await this.discard(dir);
7193
7759
  return null;
7194
7760
  }
7761
+ if (typeof manifest.externalDependencies !== "object") {
7762
+ await this.discard(dir);
7763
+ return null;
7764
+ }
7195
7765
  if (manifest.name !== name || manifest.version !== version || Object.values(manifest.entries).some((rel) => !(rel in manifest.files))) {
7196
7766
  this.logger.warn("Cache manifest in @dir names another package", dir);
7197
7767
  await this.discard(dir);
@@ -7303,12 +7873,14 @@ var RegistryPackageCache = class {
7303
7873
  const entries = {};
7304
7874
  for (const [kind, rel] of Object.entries(PACKAGE_ENTRIES)) if (rel in files) entries[kind] = rel;
7305
7875
  if (Object.keys(entries).length === 0) throw new Error(`${name}@${version} serves no document models, subgraphs or processors`);
7876
+ const packageManifest = await this.fetchBounded(new URL(PACKAGE_MANIFEST, base).href, true);
7306
7877
  return {
7307
7878
  name,
7308
7879
  version,
7309
7880
  entries,
7310
7881
  files: Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b))),
7311
- bareImports: [...bare].sort()
7882
+ bareImports: [...bare].sort(),
7883
+ externalDependencies: externalDependenciesOf(packageManifest)
7312
7884
  };
7313
7885
  }
7314
7886
  dependenciesOf(source, url, base) {
@@ -7369,8 +7941,15 @@ var RegistryPackageCache = class {
7369
7941
  }
7370
7942
  return Buffer.concat(chunks);
7371
7943
  }
7372
- async linkBareImports(specifiers) {
7373
- const outsideUrl = pathToFileURL(path.join(path.dirname(this.cacheDir), "/")).href;
7944
+ outsideUrl() {
7945
+ return pathToFileURL(path.join(path.dirname(this.cacheDir), "/")).href;
7946
+ }
7947
+ /** Whether `specifier` resolves from the project or from reactor-api. */
7948
+ resolvesForCachedPackages(specifier, pkg) {
7949
+ return packageRoot(specifier, pkg, this.outsideUrl()) !== null || packageRoot(specifier, pkg, import.meta.url) !== null;
7950
+ }
7951
+ async linkBareImports(specifiers, external) {
7952
+ const outsideUrl = this.outsideUrl();
7374
7953
  const linkRoot = path.join(this.cacheDir, "node_modules");
7375
7954
  for (const specifier of specifiers) {
7376
7955
  const pkg = packageNameOf(specifier);
@@ -7383,6 +7962,7 @@ var RegistryPackageCache = class {
7383
7962
  }
7384
7963
  const fallback = packageRoot(specifier, pkg, import.meta.url);
7385
7964
  if (!fallback) {
7965
+ if (external.includes(pkg)) continue;
7386
7966
  this.logger.warn("Registry package import @specifier resolves from neither the project nor reactor-api", specifier);
7387
7967
  continue;
7388
7968
  }
@@ -7420,6 +8000,17 @@ function parseManifest(raw) {
7420
8000
  if (typeof value.name !== "string" || typeof value.version !== "string" || typeof value.entries !== "object" || value.entries === null || typeof value.files !== "object" || value.files === null || !Array.isArray(value.bareImports)) throw new Error("malformed manifest");
7421
8001
  return value;
7422
8002
  }
8003
+ function externalDependenciesOf(body) {
8004
+ if (!body) return {};
8005
+ let value;
8006
+ try {
8007
+ value = JSON.parse(body.toString("utf8")).externalDependencies;
8008
+ } catch {
8009
+ return {};
8010
+ }
8011
+ if (typeof value !== "object" || value === null) return {};
8012
+ return Object.fromEntries(Object.entries(value).filter(([name, version]) => isValidPackageName(name) && typeof version === "string"));
8013
+ }
7423
8014
  function packageNameOf(specifier) {
7424
8015
  const parts = specifier.split("/");
7425
8016
  if (specifier.startsWith("@")) return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
@@ -7542,6 +8133,7 @@ var HttpPackageLoader = class {
7542
8133
  documentModelLoader;
7543
8134
  cache;
7544
8135
  versions = /* @__PURE__ */ new Map();
8136
+ reportedExternal = /* @__PURE__ */ new Set();
7545
8137
  constructor(options) {
7546
8138
  this.registryUrl = options.registryUrl.endsWith("/") ? options.registryUrl : `${options.registryUrl}/`;
7547
8139
  this.cache = new RegistryPackageCache({
@@ -7624,6 +8216,7 @@ var HttpPackageLoader = class {
7624
8216
  if (cached) {
7625
8217
  const filePath = cached.entries[kind];
7626
8218
  if (!filePath) throw Object.assign(/* @__PURE__ */ new Error(`${packageName}@${cached.version} serves no ${kind}`), { code: REGISTRY_ENTRY_ABSENT });
8219
+ if (kind !== "documentModels") this.assertExternalDependencies(cached);
7627
8220
  return {
7628
8221
  module: await import(
7629
8222
  /* @vite-ignore */
@@ -7640,6 +8233,17 @@ var HttpPackageLoader = class {
7640
8233
  url
7641
8234
  ) };
7642
8235
  }
8236
+ assertExternalDependencies(cached) {
8237
+ const missing = cached.missingExternalDependencies;
8238
+ if (missing.length === 0) return;
8239
+ const id = `${cached.name}@${cached.version}`;
8240
+ const list = missing.map((dep) => `${dep}@${cached.externalDependencies[dep]}`).join(", ");
8241
+ if (!this.reportedExternal.has(id)) {
8242
+ this.reportedExternal.add(id);
8243
+ this.logger.error("@package needs packages with native addons or WebAssembly modules that are not installed (@deps); its subgraphs and processors are not loaded, its document models and pieces are. Install it locally with `ph install --local @name`, or list it in PH_PACKAGES for a Docker image.", id, list, cached.name);
8244
+ }
8245
+ throw Object.assign(/* @__PURE__ */ new Error(`${id} needs external dependencies that are not installed: ${list}`), { code: REGISTRY_EXTERNAL_DEPS_MISSING });
8246
+ }
7643
8247
  importDocumentModels(packageSpec) {
7644
8248
  return this.importEntry(packageSpec, "documentModels");
7645
8249
  }
@@ -8080,7 +8684,7 @@ var PackageManager = class {
8080
8684
  maybeWarnAllLoadersFailed(kind, pkg, succeeded, failures) {
8081
8685
  if (succeeded || failures.length === 0) return;
8082
8686
  const subPath = kind === "document models" ? "document-models" : kind;
8083
- const realFailures = failures.filter(({ error }) => !isExpectedLoaderMiss(error, pkg, subPath));
8687
+ const realFailures = failures.filter(({ error }) => !isExpectedLoaderMiss(error, pkg, subPath) && error?.code !== "ERR_REGISTRY_EXTERNAL_DEPS_MISSING");
8084
8688
  if (realFailures.length === 0) return;
8085
8689
  const details = realFailures.map(({ loader, error }) => {
8086
8690
  return ` [${loader}] ${error instanceof Error ? error.message : String(error)}`;
@@ -8266,11 +8870,6 @@ async function createStartupAttachmentBackend({ db, env = process.env, s3Depende
8266
8870
  }
8267
8871
  //#endregion
8268
8872
  //#region src/services/attachment-access.service.ts
8269
- /** The scope an attachment's bytes belong to: the document's own domain state. */
8270
- const ATTACHMENT_SCOPE = "global";
8271
- /** The branch an attachment reference is resolved against. */
8272
- const ATTACHMENT_BRANCH = "main";
8273
- const HASH_PATTERN = /^[a-f0-9]{64}$/;
8274
8873
  /**
8275
8874
  * Composes document authorization with the projected document/ref
8276
8875
  * relationship. Order is fixed: validate ref, resolve the canonical document
@@ -8327,29 +8926,10 @@ var AttachmentAccessService = class {
8327
8926
  return { kind: "denied" };
8328
8927
  }
8329
8928
  if (!await this.canReadDocument(documentId, request)) return { kind: "denied" };
8330
- const subject = {
8929
+ if (!await readGateAllowsAttachmentRead(this.readGate, this.references, documentId, ref, {
8331
8930
  address: request.userAddress,
8332
8931
  key: request.appKey
8333
- };
8334
- let served;
8335
- try {
8336
- served = await this.readGate.isServed(documentId, { subject });
8337
- } catch {
8338
- return { kind: "denied" };
8339
- }
8340
- if (!served) return { kind: "denied" };
8341
- const scopes = await this.references.referencingScopes(documentId, ref);
8342
- if (scopes.length === 0) return { kind: "denied" };
8343
- let held;
8344
- try {
8345
- held = (await this.readGate.get(documentId, {
8346
- subject,
8347
- scopes
8348
- })).state;
8349
- } catch {
8350
- return { kind: "denied" };
8351
- }
8352
- if (!scopes.some((scope) => scope in held)) return { kind: "denied" };
8932
+ })) return { kind: "denied" };
8353
8933
  return {
8354
8934
  kind: "allowed",
8355
8935
  documentId,
@@ -8366,10 +8946,10 @@ var AttachmentAccessService = class {
8366
8946
  */
8367
8947
  async canReadDocument(documentId, request) {
8368
8948
  if (!this.scopeGate) return this.authorization.canRead(documentId, request.userAddress);
8369
- return (await this.scopeGate.scopePredicateById(documentId, {
8949
+ return scopeGateAllowsAttachmentRead(this.scopeGate, documentId, {
8370
8950
  address: request.userAddress,
8371
8951
  key: request.appKey
8372
- }, ATTACHMENT_BRANCH))(ATTACHMENT_SCOPE);
8952
+ });
8373
8953
  }
8374
8954
  };
8375
8955
  /**
@@ -8387,7 +8967,7 @@ function normalizeAttachmentRef(value) {
8387
8967
  } catch {
8388
8968
  return null;
8389
8969
  }
8390
- if (version !== 1 || !HASH_PATTERN.test(hash)) return null;
8970
+ if (version !== 1 || !isAttachmentHash(hash)) return null;
8391
8971
  return createRef(hash, version);
8392
8972
  }
8393
8973
  //#endregion
@@ -9568,6 +10148,25 @@ function createReadinessGate() {
9568
10148
  function getExplorerPrefix(basePath) {
9569
10149
  return path.posix.join(basePath, "explorer");
9570
10150
  }
10151
+ function attachmentInspectionStore(attachments) {
10152
+ return { getAttachmentInfo: async () => ({
10153
+ present: true,
10154
+ storeKind: "kysely",
10155
+ hasReplicator: false,
10156
+ replicatorRunning: false,
10157
+ backlogScanned: false,
10158
+ refsSeen: 0,
10159
+ held: 0,
10160
+ bytesHeld: await attachments.store.storageUsed(),
10161
+ queued: 0,
10162
+ fetching: 0,
10163
+ pendingFetches: 0,
10164
+ waiting: 0,
10165
+ notFound: 0,
10166
+ failed: 0,
10167
+ lastError: void 0
10168
+ }) };
10169
+ }
9571
10170
  function resolveAttachmentStoragePath(options) {
9572
10171
  if (options.attachmentStoragePath) return options.attachmentStoragePath;
9573
10172
  if (options.dbPath && !options.dbPath.startsWith("postgres")) return path.resolve(options.dbPath, "..", "attachments");
@@ -9628,7 +10227,7 @@ function resolveGatewayAdapterType(logger) {
9628
10227
  /**
9629
10228
  * Sets up the subgraph manager and registers subgraphs
9630
10229
  */
9631
- async function setupGraphQLManager({ httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient, syncServingGate, httpRoutes, attachments }) {
10230
+ async function setupGraphQLManager({ httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient, syncServingGate, httpRoutes, attachments, inspection, driveStore }) {
9632
10231
  const graphqlManager = new GraphQLManager({
9633
10232
  path: config.basePath,
9634
10233
  httpServer,
@@ -9648,7 +10247,9 @@ async function setupGraphQLManager({ httpAdapter, authFetchMiddleware, requireAu
9648
10247
  reactorDriveClient,
9649
10248
  syncServingGate,
9650
10249
  httpRoutes,
9651
- attachments
10250
+ attachments,
10251
+ inspection,
10252
+ driveStore
9652
10253
  });
9653
10254
  await graphqlManager.init(subgraphs.core, authFetchMiddleware, requireAuthFetchMiddleware);
9654
10255
  for (const [packageName, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql", false, packageName);
@@ -9947,7 +10548,7 @@ async function _setupCommonInfrastructure(options) {
9947
10548
  /**
9948
10549
  * Private helper function containing common setup logic for API initialization
9949
10550
  */
9950
- async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, authEnabled, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient, syncServingGate, httpRoutes, attachmentReadsFollowDocumentPolicy = false) {
10551
+ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, authEnabled, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient, syncServingGate, httpRoutes, attachmentReadsFollowDocumentPolicy = false, inspection, driveStore) {
9951
10552
  const hostModuleBase = {
9952
10553
  ...createReactorHostModuleBase({
9953
10554
  client: reactorClient,
@@ -9998,6 +10599,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
9998
10599
  const attachmentClientProvider = new AttachmentClientProvider(attachments.service, attachmentAccess, logger);
9999
10600
  const coreSubgraphs = DefaultCoreSubgraphs.slice();
10000
10601
  coreSubgraphs.push(ReactorSubgraph);
10602
+ if (inspection) coreSubgraphs.push(InspectionSubgraph);
10001
10603
  if (documentPermissionService) {
10002
10604
  coreSubgraphs.push(AuthSubgraph);
10003
10605
  logger.info("Auth subgraph registered (document permissions enabled)");
@@ -10025,7 +10627,9 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
10025
10627
  reactorDriveClient,
10026
10628
  syncServingGate,
10027
10629
  httpRoutes,
10028
- attachments: attachmentClientProvider
10630
+ attachments: attachmentClientProvider,
10631
+ inspection,
10632
+ driveStore
10029
10633
  });
10030
10634
  setupEventListeners(packages, graphqlManager, reactorProcessorManager, moduleFor, documentModelRegistry);
10031
10635
  if (mcpServerEnabled) {
@@ -10057,6 +10661,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
10057
10661
  requireAuthenticatedCaller: floor,
10058
10662
  authorizationService,
10059
10663
  relationalDb,
10664
+ inspection,
10060
10665
  dispose
10061
10666
  };
10062
10667
  }
@@ -10120,7 +10725,10 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
10120
10725
  const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
10121
10726
  if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from InProcessReactorClientModule");
10122
10727
  return {
10123
- ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, authEnabled, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient, buildSyncServingGate(reactorClientModule.reactorModule, authorizationConfig, options.logger ?? defaultLogger), httpRoutes, attachmentReadsFollowDocumentPolicy),
10728
+ ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, authEnabled, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient, buildSyncServingGate(reactorClientModule.reactorModule, authorizationConfig, options.logger ?? defaultLogger), httpRoutes, attachmentReadsFollowDocumentPolicy, reactorClientModule.reactorModule ? createReactorInspectionSource(reactorClientModule.reactorModule, {
10729
+ ...options.inspection,
10730
+ attachmentStore: attachmentInspectionStore(attachments)
10731
+ }) : void 0, reactorClientModule.reactorModule?.reactor),
10124
10732
  client: reactorClient,
10125
10733
  syncManager,
10126
10734
  documentModelRegistry,
@@ -10303,7 +10911,7 @@ var PackageManagementService = class {
10303
10911
  }
10304
10912
  };
10305
10913
  //#endregion
10306
- export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessFailed, AttachmentAccessService, AttachmentAccessUnavailable, AttachmentClientProvider, AuthDecision, AuthDecisionSchema, AuthEvaluationUnsupportedError, AuthService, AuthSubgraph, AuthenticationRequiredError, AuthorizationPolicy, AuthorizedAttachmentService, AuthorizedDocumentHandle, BaseSubgraph, CORE_PACKAGE_NAME, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, DocumentRelationshipFieldsFragmentDoc, EvaluateActionsDocument, ExecuteBatchDocument, ExecutionJobInputSchema, FindDocumentsDocument, ForbiddenError, GetDocumentDocument, GetDocumentIncomingRelationshipEdgesDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipEdgesDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, HttpRouteService, ImportPackageLoader, InMemoryPackageStorage, InvalidNamespaceError, JobChangesDocument, JobInfoFieldsFragmentDoc, MalformedStoredOperationError, MemoryWebhookStore, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PACKAGE_ROUTE_SEGMENT, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PrivacyPermissionAdapter, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RefusalNotRecordedError, RelationalWebhookStore, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SyncRefusalInputSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, UpdateRelationshipDocument, ViewFilterInputSchema, WEBHOOK_SEGMENT, WS_AUTH_CLOSE_REASONS, WS_CLOSE_REASON_AUTHENTICATION_REQUIRED, WS_CLOSE_REASON_BEARER_REJECTED, WebhookService, assertAttachmentPolicyReadsAllowed, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertHostMountPath, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, callerSubject, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, createWsAuthHandlers, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphs, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isPostgresConnectionString, isSubgraphClass, namespacePath, namespaceSegments, newWebhookToken, parseDriveUrl, parseWebhookBody, piecesBaseUrl, redactHeaders, renderGraphqlPlayground, resolvePackageName, resolveRenownConfig, verifyWebhook };
10914
+ export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessFailed, AttachmentAccessService, AttachmentAccessUnavailable, AttachmentClientProvider, AuthDecision, AuthDecisionSchema, AuthEvaluationUnsupportedError, AuthService, AuthSubgraph, AuthenticationRequiredError, AuthorizationPolicy, AuthorizedAttachmentService, AuthorizedDocumentHandle, BaseSubgraph, CORE_PACKAGE_NAME, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, DocumentRelationshipFieldsFragmentDoc, EvaluateActionsDocument, ExecuteBatchDocument, ExecutionJobInputSchema, FindDocumentsDocument, ForbiddenError, GetCreateDefaultsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipEdgesDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipEdgesDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentServedDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, HttpRouteService, ImportPackageLoader, InMemoryPackageStorage, InspectionSubgraph, InvalidNamespaceError, JobChangesDocument, JobInfoFieldsFragmentDoc, MalformedStoredOperationError, MemoryWebhookStore, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PACKAGE_ROUTE_SEGMENT, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PrivacyPermissionAdapter, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RefusalNotRecordedError, RelationalWebhookStore, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SyncRefusalInputSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, UpdateRelationshipDocument, ViewFilterInputSchema, WEBHOOK_SEGMENT, WS_AUTH_CLOSE_REASONS, WS_CLOSE_REASON_AUTHENTICATION_REQUIRED, WS_CLOSE_REASON_BEARER_REJECTED, WebhookService, assertAttachmentPolicyReadsAllowed, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertHostMountPath, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, callerSubject, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createInspectionResolvers, createMergedSchema, createReactorGraphQLClient, createReactorInspectionSource, createRenownCredentialVerifier, createSchema, createWsAuthHandlers, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphs, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, inspectionTypeDefs, isDefinedNonNullAny, isExpectedLoaderMiss, isListedAdmin, isPostgresConnectionString, isSubgraphClass, namespacePath, namespaceSegments, newWebhookToken, parseDriveUrl, parseWebhookBody, piecesBaseUrl, redactHeaders, renderGraphqlPlayground, resolvePackageName, resolveRenownConfig, verifyWebhook };
10307
10915
 
10308
10916
  //# sourceMappingURL=index.mjs.map
10309
- //# debugId=dc27a51b-c565-5dcc-a85a-eac64c78db3e
10917
+ //# debugId=980be7cc-16d9-5965-b60e-0c7ca7a4e3c8