@powerhousedao/reactor-api 6.2.2-dev.7 → 6.2.2-dev.71

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,19 +1,19 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e58cea2c-2319-52a8-90cd-3e99d94ba98f")}catch(e){}}();
3
- import { a as isSubgraphClass, c as loadDocumentModels, d as BaseSubgraph, f as AuthorizationPolicy, i as buildGraphqlOperations, l as loadProcessors, m as createAuthorizationService, n as buildGraphQlDriveDocument, o as debounce, p as AuthorizedDocumentHandle, r as buildGraphqlOperation, t as buildGraphQlDocument, u as loadSubgraphs } from "./utils-iibOQ50e.mjs";
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7b24e842-b774-59b1-8a44-51c953533e4c")}catch(e){}}();
3
+ import { _ as createAuthorizationService, a as isSubgraphClass, b as MalformedStoredOperationError, d as loadSubgraphs, f as BaseSubgraph, g as AuthorizedDocumentHandle, h as AuthorizationPolicy, i as buildGraphqlOperations, l as loadDocumentModels, m as createCanonicalDocumentIdResolver, n as buildGraphQlDriveDocument, o as debounce, p as CanonicalDocumentIdResolutionError, r as buildGraphqlOperation, s as extractUpgradeManifests, t as buildGraphQlDocument, u as loadProcessors, v as AuthEvaluationUnsupportedError, y as ForbiddenError } from "./utils-DeM5P0e8.mjs";
4
4
  import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
5
5
  import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
6
6
  import { gql } from "graphql-tag";
7
7
  import { GraphQLError, Kind, parse, print } from "graphql";
8
- import { DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, PropagationMode as PropagationMode$1, consolidateSyncOperations, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl } from "@powerhousedao/reactor";
8
+ import { AuthEnforcementDisabledError, DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, ModelReadGate, PropagationMode as PropagationMode$1, SyncScopeGate, consolidateSyncOperations, createReactorHostModuleBase, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, readDecisionModel, syncOperationErrorType } from "@powerhousedao/reactor";
9
9
  import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
10
10
  import path from "node:path";
11
11
  import { match } from "path-to-regexp";
12
12
  import { buildSubgraphSchema } from "@apollo/subgraph";
13
13
  import { typeDefs } from "@powerhousedao/document-engineering/graphql";
14
14
  import { camelCase, kebabCase, pascalCase } from "change-case";
15
- import { GraphQLJSONObject } from "graphql-type-json";
16
- import { setName } from "@powerhousedao/shared/document-model";
15
+ import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
16
+ import { deserializeSignature, serializeSignature, setName } from "@powerhousedao/shared/document-model";
17
17
  import * as z$1 from "zod";
18
18
  import { z } from "zod";
19
19
  import { createHandler } from "graphql-sse/lib/use/fetch";
@@ -22,11 +22,12 @@ import dotenv from "dotenv";
22
22
  import { buildTreeUrl } from "@powerhousedao/shared";
23
23
  import { getConfig } from "@powerhousedao/config/node";
24
24
  import { reactorDriveDocumentModelModule } from "@powerhousedao/reactor-drive";
25
+ import { ReactorGroupV1 } from "@powerhousedao/reactor-group";
25
26
  import { driveDocumentModelModule } from "@powerhousedao/shared/document-drive";
26
27
  import EventEmitter from "node:events";
27
28
  import fs, { watchFile } from "node:fs";
28
29
  import { PostgresAnalyticsStore } from "@powerhousedao/analytics-engine-pg";
29
- import { AttachmentBuilder } from "@powerhousedao/reactor-attachments";
30
+ import { AttachmentBuilder, AttachmentReferenceIndexBuilder, createRef, createS3AttachmentBackend, parseAttachmentStorageConfig, parseRef } from "@powerhousedao/reactor-attachments";
30
31
  import { createAttachmentClient } from "@powerhousedao/reactor-attachments/client";
31
32
  import { setupMcpServer } from "@powerhousedao/reactor-mcp";
32
33
  import { mkdir } from "node:fs/promises";
@@ -34,7 +35,7 @@ import { tmpdir } from "node:os";
34
35
  import { WebSocketServer } from "ws";
35
36
  import { createRelationalDb } from "@powerhousedao/shared/processors";
36
37
  import { Kysely, Migrator, sql } from "kysely";
37
- import { fetchDelegationCredential, verifyAuthBearerToken } from "@renown/sdk";
38
+ import { fetchDelegationCredential, resolveSwitchboardEndpoint, verifyAuthBearerToken } from "@renown/sdk";
38
39
  import { PGlite } from "@electric-sql/pglite";
39
40
  import { AtomicNodeFs } from "@powerhousedao/pglite-fs";
40
41
  import knex from "knex";
@@ -305,7 +306,7 @@ function createAuthFetchMiddleware(authService) {
305
306
  async function createGatewayAdapter(type, logger) {
306
307
  switch (type) {
307
308
  case "apollo": {
308
- const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-CxV_hMTv.mjs");
309
+ const { ApolloGatewayAdapter } = await import("./adapter-gateway-apollo-DdamNLPT.mjs");
309
310
  return new ApolloGatewayAdapter(logger);
310
311
  }
311
312
  case "mercurius": {
@@ -396,7 +397,8 @@ const dedupeTypeDefinitions = (doc) => {
396
397
  const buildSubgraphSchemaModule = (documentModels, resolvers, typeDefs) => {
397
398
  const newResolvers = {
398
399
  ...resolvers,
399
- JSONObject: GraphQLJSONObject
400
+ JSONObject: GraphQLJSONObject,
401
+ Unknown: GraphQLJSON
400
402
  };
401
403
  return {
402
404
  typeDefs: getDocumentModelTypeDefs(documentModels, typeDefs),
@@ -404,7 +406,7 @@ const buildSubgraphSchemaModule = (documentModels, resolvers, typeDefs) => {
404
406
  };
405
407
  };
406
408
  const createSchema = (documentModels, resolvers, typeDefs) => {
407
- return buildSubgraphSchema(buildSubgraphSchemaModule(documentModels, resolvers, typeDefs));
409
+ return buildSubgraphSchema([buildSubgraphSchemaModule(documentModels, resolvers, typeDefs)]);
408
410
  };
409
411
  /**
410
412
  * Create a merged GraphQL schema from multiple subgraph modules.
@@ -460,6 +462,10 @@ const getDocumentModelTypeDefs = (documentModels, typeDefs$1) => {
460
462
  });
461
463
  return dedupeTypeDefinitions(gql`
462
464
  scalar JSONObject
465
+ scalar AttachmentRef
466
+ # Codegen scalars not in the document-engineering set.
467
+ scalar Unknown
468
+ scalar Address
463
469
  ${typeDefs.join("\n").replaceAll(";", "")}
464
470
 
465
471
  type PHOperationContext {
@@ -878,6 +884,7 @@ function generateNewApiSchema(documentName, specification, _stateSchemaTypes, pr
878
884
  return gql`
879
885
  scalar DateTime
880
886
  scalar JSONObject
887
+ scalar AttachmentRef
881
888
 
882
889
  ${revisionType}
883
890
 
@@ -926,6 +933,11 @@ function generateNewApiSchema(documentName, specification, _stateSchemaTypes, pr
926
933
  }
927
934
  //#endregion
928
935
  //#region src/graphql/reactor/gen/graphql.ts
936
+ let AuthDecision = /* @__PURE__ */ function(AuthDecision) {
937
+ AuthDecision["Allow"] = "ALLOW";
938
+ AuthDecision["Deny"] = "DENY";
939
+ return AuthDecision;
940
+ }({});
929
941
  let DocumentChangeType = /* @__PURE__ */ function(DocumentChangeType) {
930
942
  DocumentChangeType["ChildAdded"] = "CHILD_ADDED";
931
943
  DocumentChangeType["ChildRemoved"] = "CHILD_REMOVED";
@@ -948,11 +960,24 @@ let SyncEnvelopeType = /* @__PURE__ */ function(SyncEnvelopeType) {
948
960
  }({});
949
961
  const isDefinedNonNullAny = (v) => v !== void 0 && v !== null;
950
962
  const definedNonNullAnySchema = z$1.any().refine((v) => isDefinedNonNullAny(v));
963
+ const AuthDecisionSchema = z$1.enum(AuthDecision);
951
964
  const DocumentChangeTypeSchema = z$1.enum(DocumentChangeType);
952
965
  const PropagationModeSchema = z$1.enum(PropagationMode);
953
966
  const SyncEnvelopeTypeSchema = z$1.enum(SyncEnvelopeType);
967
+ function ActionCandidateInputSchema() {
968
+ return z$1.object({
969
+ input: z$1.custom((v) => v != null).nullish(),
970
+ scope: z$1.string(),
971
+ type: z$1.string()
972
+ });
973
+ }
954
974
  function ActionContextInputSchema() {
955
- return z$1.object({ signer: z$1.lazy(() => ReactorSignerInputSchema().nullish()) });
975
+ return z$1.object({
976
+ nonce: z$1.string().nullish(),
977
+ prevOpHash: z$1.string().nullish(),
978
+ prevOpIndex: z$1.number().nullish(),
979
+ signer: z$1.lazy(() => ReactorSignerInputSchema().nullish())
980
+ });
956
981
  }
957
982
  function ActionInputSchema() {
958
983
  return z$1.object({
@@ -989,6 +1014,7 @@ function OperationContextInputSchema() {
989
1014
  function OperationInputSchema() {
990
1015
  return z$1.object({
991
1016
  action: z$1.lazy(() => ActionInputSchema()),
1017
+ deniedReason: z$1.string().nullish(),
992
1018
  error: z$1.string().nullish(),
993
1019
  hash: z$1.string(),
994
1020
  id: z$1.string().nullish(),
@@ -1147,6 +1173,7 @@ const GetDocumentWithOperationsDocument = gql`
1147
1173
  hash
1148
1174
  skip
1149
1175
  error
1176
+ deniedReason
1150
1177
  id
1151
1178
  action {
1152
1179
  id
@@ -1259,6 +1286,7 @@ const GetDocumentOperationsDocument = gql`
1259
1286
  hash
1260
1287
  skip
1261
1288
  error
1289
+ deniedReason
1262
1290
  id
1263
1291
  action {
1264
1292
  id
@@ -1301,6 +1329,28 @@ const GetJobStatusDocument = gql`
1301
1329
  }
1302
1330
  }
1303
1331
  `;
1332
+ const EvaluateActionsDocument = gql`
1333
+ query EvaluateActions(
1334
+ $documentIdentifier: String!
1335
+ $branch: String
1336
+ $candidates: [ActionCandidateInput!]!
1337
+ ) {
1338
+ evaluateActions(
1339
+ documentIdentifier: $documentIdentifier
1340
+ branch: $branch
1341
+ candidates: $candidates
1342
+ ) {
1343
+ evaluations {
1344
+ decision
1345
+ reason
1346
+ }
1347
+ allAllowed
1348
+ anyAllowed
1349
+ allDenied
1350
+ anyDenied
1351
+ }
1352
+ }
1353
+ `;
1304
1354
  const CreateDocumentDocument = gql`
1305
1355
  mutation CreateDocument($document: JSONObject!, $parentIdentifier: String) {
1306
1356
  createDocument(document: $document, parentIdentifier: $parentIdentifier) {
@@ -1326,13 +1376,13 @@ const CreateEmptyDocumentDocument = gql`
1326
1376
  const MutateDocumentDocument = gql`
1327
1377
  mutation MutateDocument(
1328
1378
  $documentIdentifier: String!
1329
- $actions: [JSONObject!]!
1330
- $view: ViewFilterInput
1379
+ $actions: [ActionInput!]!
1380
+ $branch: String
1331
1381
  ) {
1332
- mutateDocument(
1382
+ mutateDocument: execute(
1333
1383
  documentIdentifier: $documentIdentifier
1334
1384
  actions: $actions
1335
- view: $view
1385
+ branch: $branch
1336
1386
  ) {
1337
1387
  ...PHDocumentFields
1338
1388
  }
@@ -1342,14 +1392,21 @@ const MutateDocumentDocument = gql`
1342
1392
  const MutateDocumentAsyncDocument = gql`
1343
1393
  mutation MutateDocumentAsync(
1344
1394
  $documentIdentifier: String!
1345
- $actions: [JSONObject!]!
1346
- $view: ViewFilterInput
1395
+ $actions: [ActionInput!]!
1396
+ $branch: String
1347
1397
  ) {
1348
- mutateDocumentAsync(
1398
+ mutateDocumentAsync: executeAsync(
1349
1399
  documentIdentifier: $documentIdentifier
1350
1400
  actions: $actions
1351
- view: $view
1352
- )
1401
+ branch: $branch
1402
+ ) {
1403
+ id
1404
+ status
1405
+ result
1406
+ error
1407
+ createdAt
1408
+ completedAt
1409
+ }
1353
1410
  }
1354
1411
  `;
1355
1412
  const RenameDocumentDocument = gql`
@@ -1509,6 +1566,7 @@ const PollSyncEnvelopesDocument = gql`
1509
1566
  hash
1510
1567
  skip
1511
1568
  error
1569
+ deniedReason
1512
1570
  id
1513
1571
  action {
1514
1572
  id
@@ -1551,6 +1609,7 @@ const PollSyncEnvelopesDocument = gql`
1551
1609
  deadLetters {
1552
1610
  documentId
1553
1611
  error
1612
+ errorType
1554
1613
  }
1555
1614
  hasMore
1556
1615
  }
@@ -1595,6 +1654,9 @@ function getSdk(requester) {
1595
1654
  GetJobStatus(variables, options) {
1596
1655
  return requester(GetJobStatusDocument, variables, options);
1597
1656
  },
1657
+ EvaluateActions(variables, options) {
1658
+ return requester(EvaluateActionsDocument, variables, options);
1659
+ },
1598
1660
  CreateDocument(variables, options) {
1599
1661
  return requester(CreateDocumentDocument, variables, options);
1600
1662
  },
@@ -1745,28 +1807,132 @@ function toReactorPropagationMode(gqlMode) {
1745
1807
  }
1746
1808
  }
1747
1809
  /**
1810
+ * Maps one predicted verdict to its GraphQL shape. A refusal's reason is the
1811
+ * consensus string the reactor would record for it, carried through verbatim so
1812
+ * a client can tell a deletion refusal from a missing grant; an allow has none.
1813
+ */
1814
+ function toGqlActionEvaluation(evaluation) {
1815
+ return evaluation.decision === "allow" ? {
1816
+ decision: AuthDecision.Allow,
1817
+ reason: null
1818
+ } : {
1819
+ decision: AuthDecision.Deny,
1820
+ reason: evaluation.reason
1821
+ };
1822
+ }
1823
+ /**
1748
1824
  * Converts readonly arrays to mutable arrays for ReactorClient
1749
1825
  */
1750
1826
  function toMutableArray(arr) {
1751
1827
  return arr ? [...arr] : void 0;
1752
1828
  }
1753
1829
  /**
1754
- * Validates that a JSONObject represents a valid Action structure
1830
+ * Validates that an incoming value has the structure of an Action.
1831
+ *
1832
+ * This is the whole guard on the way in. `mutateDocument` takes untyped JSON, so
1833
+ * nothing has checked these fields before a resolver runs, and the id in
1834
+ * particular has to be checked here: it is hashed into the operation id and
1835
+ * replay dedupes by it, so an action without one is stored under an operation id
1836
+ * shared by every id-less operation on the same document, scope and branch.
1837
+ *
1838
+ * The storage constraint would refuse such a row anyway, but only after a job
1839
+ * has been enqueued and failed. Refusing here answers the caller instead.
1755
1840
  */
1756
1841
  function validateActionStructure(obj) {
1757
1842
  if (!obj || typeof obj !== "object") return false;
1758
1843
  const action = obj;
1844
+ if (typeof action.id !== "string" || !action.id) return false;
1759
1845
  if (typeof action.type !== "string" || !action.type) return false;
1760
1846
  if (typeof action.scope !== "string" || !action.scope) return false;
1847
+ if (typeof action.timestampUtcMs !== "string" || !action.timestampUtcMs) return false;
1761
1848
  if (!("input" in action)) return false;
1762
1849
  return true;
1763
1850
  }
1764
1851
  /**
1852
+ * Restores an action's signatures to the tuples verification reads.
1853
+ *
1854
+ * A signature is a five-param tuple, and GraphQL declares `signatures` as a
1855
+ * list of strings, so a client joins each one for transport. The sync path
1856
+ * already splits them on arrival; this is the same step for the mutation path,
1857
+ * which had none - an action submitted there kept whatever shape it arrived in,
1858
+ * so the same signature was persisted as a tuple or as one joined string
1859
+ * depending on which door it came through.
1860
+ *
1861
+ * A tuple passes through untouched, so an in-process caller handing over real
1862
+ * signatures is unaffected.
1863
+ */
1864
+ function withDeserializedSignatures(action) {
1865
+ const signer = action.context?.signer;
1866
+ if (!signer?.signatures || signer.signatures.length === 0) return action;
1867
+ return {
1868
+ ...action,
1869
+ context: {
1870
+ ...action.context,
1871
+ signer: {
1872
+ ...signer,
1873
+ signatures: signer.signatures.map(deserializeSignature)
1874
+ }
1875
+ }
1876
+ };
1877
+ }
1878
+ /**
1879
+ * Converts actions the schema has already coerced into the actions the reactor
1880
+ * applies.
1881
+ *
1882
+ * The one conversion the schema cannot express is the signature: GraphQL
1883
+ * declares `signatures` as a list of strings, not of lists, so each arrives
1884
+ * joined and has to be read back as the tuple verification indexes into.
1885
+ *
1886
+ * Nothing is validated here. `ActionInput` states every field the reactor needs,
1887
+ * so anything that reached this point has them - unlike the untyped path, which
1888
+ * has to check by hand.
1889
+ */
1890
+ function toSubmittableActions(actions) {
1891
+ return actions.map((action) => {
1892
+ const context = toSubmittableContext(action.context);
1893
+ const submittable = {
1894
+ id: action.id,
1895
+ type: action.type,
1896
+ timestampUtcMs: action.timestampUtcMs,
1897
+ input: action.input,
1898
+ scope: action.scope
1899
+ };
1900
+ return context ? {
1901
+ ...submittable,
1902
+ context
1903
+ } : submittable;
1904
+ });
1905
+ }
1906
+ /**
1907
+ * `ReactorSignerInput` leaves `user` and `app` optional where `ActionSigner`
1908
+ * requires them, so an incomplete signer cannot be represented. It is carried
1909
+ * through as it arrived rather than filled in: the signature verifier is what
1910
+ * should refuse it, and inventing either field here would be inventing the part
1911
+ * that says who signed.
1912
+ */
1913
+ function toSubmittableContext(context) {
1914
+ if (!context) return;
1915
+ const submittable = {};
1916
+ const prevOpIndex = fromInputMaybe(context.prevOpIndex);
1917
+ if (prevOpIndex !== void 0) submittable.prevOpIndex = prevOpIndex;
1918
+ const prevOpHash = fromInputMaybe(context.prevOpHash);
1919
+ if (prevOpHash !== void 0) submittable.prevOpHash = prevOpHash;
1920
+ const nonce = fromInputMaybe(context.nonce);
1921
+ if (nonce !== void 0) submittable.nonce = nonce;
1922
+ const signer = fromInputMaybe(context.signer);
1923
+ if (signer) submittable.signer = {
1924
+ user: fromInputMaybe(signer.user),
1925
+ app: fromInputMaybe(signer.app),
1926
+ signatures: signer.signatures.map(deserializeSignature)
1927
+ };
1928
+ return Object.keys(submittable).length > 0 ? submittable : void 0;
1929
+ }
1930
+ /**
1765
1931
  * Converts a JSONObject to an Action, validating basic structure
1766
1932
  */
1767
1933
  function jsonObjectToAction(obj) {
1768
- if (!validateActionStructure(obj)) throw new GraphQLError("Invalid action structure. Actions must have: type (string), scope (string), and input (any)");
1769
- return obj;
1934
+ if (!validateActionStructure(obj)) throw new GraphQLError("Invalid action structure. Actions must have: id (string), type (string), scope (string), timestampUtcMs (string), and input (any)");
1935
+ return withDeserializedSignatures(obj);
1770
1936
  }
1771
1937
  /**
1772
1938
  * Validates a list of actions by converting them from JSON and checking structure
@@ -1780,10 +1946,57 @@ function validateActions(actions) {
1780
1946
  }
1781
1947
  return convertedActions;
1782
1948
  }
1949
+ /** The operation fields the schema declares non-null. */
1950
+ const REQUIRED_OPERATION_FIELDS = [
1951
+ "index",
1952
+ "timestampUtcMs",
1953
+ "hash",
1954
+ "skip"
1955
+ ];
1956
+ /** The action fields the schema declares non-null. */
1957
+ const REQUIRED_ACTION_FIELDS = [
1958
+ "id",
1959
+ "type",
1960
+ "timestampUtcMs",
1961
+ "input",
1962
+ "scope"
1963
+ ];
1964
+ /**
1965
+ * Names an operation well enough to find the row it came from, without assuming
1966
+ * the fields that might be the missing ones.
1967
+ */
1968
+ function describeOperation(stored) {
1969
+ const parts = [typeof stored.id === "string" && stored.id ? `operation ${stored.id}` : "operation with no id"];
1970
+ if (typeof stored.index === "number") parts.push(`index ${stored.index}`);
1971
+ const scope = stored.action?.scope;
1972
+ if (typeof scope === "string" && scope) parts.push(`scope ${scope}`);
1973
+ return parts.join(", ");
1974
+ }
1975
+ /**
1976
+ * Refuses an operation the schema cannot represent, naming the field.
1977
+ *
1978
+ * The alternative is what execution does unaided: nullify the field, bubble that
1979
+ * up the non-null chain until it takes out the envelope's whole operation list,
1980
+ * and report a bare "Cannot return null for non-nullable field" with no way to
1981
+ * tell which of a hundred operations was at fault. Checked before the response
1982
+ * is built so the failure is typed and attributable.
1983
+ *
1984
+ * Presence is all that is checked. The schema's own serialization still rejects
1985
+ * a value of the wrong type, and inventing further rules here would put a second
1986
+ * contract beside the one the schema already states.
1987
+ */
1988
+ function assertServable(operation) {
1989
+ const stored = operation;
1990
+ for (const field of REQUIRED_OPERATION_FIELDS) if (stored[field] === void 0 || stored[field] === null) throw new MalformedStoredOperationError(`${describeOperation(stored)} has no ${field}`);
1991
+ const action = stored.action;
1992
+ if (action === void 0 || action === null) throw new MalformedStoredOperationError(`${describeOperation(stored)} has no action`);
1993
+ for (const field of REQUIRED_ACTION_FIELDS) if (action[field] === void 0 || action[field] === null) throw new MalformedStoredOperationError(`${describeOperation(stored)} has an action with no ${field}`);
1994
+ }
1783
1995
  /**
1784
1996
  * Transforms an operation to serialize signatures from tuples to strings for GraphQL compatibility.
1785
1997
  */
1786
1998
  function serializeOperationForGraphQL(operation) {
1999
+ assertServable(operation);
1787
2000
  const signer = operation.action.context?.signer;
1788
2001
  if (!signer?.signatures) return operation;
1789
2002
  return {
@@ -1794,7 +2007,7 @@ function serializeOperationForGraphQL(operation) {
1794
2007
  ...operation.action.context,
1795
2008
  signer: {
1796
2009
  ...signer,
1797
- signatures: signer.signatures.map((sig) => Array.isArray(sig) ? sig.join(", ") : sig)
2010
+ signatures: signer.signatures.map(serializeSignature)
1798
2011
  }
1799
2012
  }
1800
2013
  }
@@ -1904,9 +2117,10 @@ async function documentModels(reactorClient, args) {
1904
2117
  throw new GraphQLError(`Failed to convert document models to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1905
2118
  }
1906
2119
  }
1907
- async function document(reactorClient, args) {
1908
- let view;
2120
+ async function document(reactorClient, args, subject) {
2121
+ let view = subject ? { subject } : void 0;
1909
2122
  if (args.view) view = {
2123
+ subject,
1910
2124
  branch: fromInputMaybe(args.view.branch),
1911
2125
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1912
2126
  };
@@ -1931,9 +2145,10 @@ async function document(reactorClient, args) {
1931
2145
  throw new GraphQLError(`Failed to convert document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1932
2146
  }
1933
2147
  }
1934
- async function documentOutgoingRelationships(reactorClient, args) {
1935
- let view;
2148
+ async function documentOutgoingRelationships(reactorClient, args, subject) {
2149
+ let view = subject ? { subject } : void 0;
1936
2150
  if (args.view) view = {
2151
+ subject,
1937
2152
  branch: fromInputMaybe(args.view.branch),
1938
2153
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1939
2154
  };
@@ -1958,9 +2173,10 @@ async function documentOutgoingRelationships(reactorClient, args) {
1958
2173
  throw new GraphQLError(`Failed to convert outgoing relationships to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1959
2174
  }
1960
2175
  }
1961
- async function documentIncomingRelationships(reactorClient, args) {
1962
- let view;
2176
+ async function documentIncomingRelationships(reactorClient, args, subject) {
2177
+ let view = subject ? { subject } : void 0;
1963
2178
  if (args.view) view = {
2179
+ subject,
1964
2180
  branch: fromInputMaybe(args.view.branch),
1965
2181
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1966
2182
  };
@@ -1985,9 +2201,10 @@ async function documentIncomingRelationships(reactorClient, args) {
1985
2201
  throw new GraphQLError(`Failed to convert incoming relationships to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
1986
2202
  }
1987
2203
  }
1988
- async function findDocuments(reactorClient, args) {
1989
- let view;
2204
+ async function findDocuments(reactorClient, args, subject) {
2205
+ let view = subject ? { subject } : void 0;
1990
2206
  if (args.view) view = {
2207
+ subject,
1991
2208
  branch: fromInputMaybe(args.view.branch),
1992
2209
  scopes: toMutableArray(fromInputMaybe(args.view.scopes))
1993
2210
  };
@@ -2029,11 +2246,12 @@ async function jobStatus(reactorClient, args) {
2029
2246
  throw new GraphQLError(`Failed to convert job status to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
2030
2247
  }
2031
2248
  }
2032
- async function documentOperations(reactorClient, args) {
2033
- let view;
2249
+ async function documentOperations(reactorClient, args, subject) {
2250
+ let view = subject ? { subject } : void 0;
2034
2251
  const branch = fromInputMaybe(args.filter.branch);
2035
2252
  const scopes = toMutableArray(fromInputMaybe(args.filter.scopes));
2036
2253
  if (branch || scopes) view = {
2254
+ subject,
2037
2255
  branch,
2038
2256
  scopes
2039
2257
  };
@@ -2069,6 +2287,41 @@ async function documentOperations(reactorClient, args) {
2069
2287
  throw new GraphQLError(`Failed to convert operations to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
2070
2288
  }
2071
2289
  }
2290
+ /**
2291
+ * Predicts the admission verdict for each candidate operation, deciding against
2292
+ * the document policy through the reactor's own decision model.
2293
+ *
2294
+ * The subject is supplied by the subgraph from the authenticated request, never
2295
+ * by the caller: answering for an arbitrary subject would disclose what a policy
2296
+ * grants somebody else. It carries the caller's address and the app key their
2297
+ * token authenticated with, which is the same pair a signed action presents, so
2298
+ * a grant naming either matches here exactly as it would at admission.
2299
+ *
2300
+ * A reactor without authEnforcement raises the named error, which becomes an
2301
+ * "unsupported" code rather than a denial.
2302
+ */
2303
+ async function evaluateActions(reactorClient, args, subject) {
2304
+ const branch = fromInputMaybe(args.branch) ?? "main";
2305
+ const candidates = args.candidates.map((candidate) => ({
2306
+ scope: candidate.scope,
2307
+ type: candidate.type,
2308
+ input: fromInputMaybe(candidate.input)
2309
+ }));
2310
+ let result;
2311
+ try {
2312
+ result = await reactorClient.evaluateActions(args.documentIdentifier, branch, candidates, subject);
2313
+ } catch (error) {
2314
+ if (AuthEnforcementDisabledError.isError(error)) throw new AuthEvaluationUnsupportedError();
2315
+ throw new GraphQLError(`Failed to evaluate actions: ${error instanceof Error ? error.message : "Unknown error"}`);
2316
+ }
2317
+ return {
2318
+ evaluations: result.evaluations.map(toGqlActionEvaluation),
2319
+ allAllowed: result.allAllowed,
2320
+ anyAllowed: result.anyAllowed,
2321
+ allDenied: result.allDenied,
2322
+ anyDenied: result.anyDenied
2323
+ };
2324
+ }
2072
2325
  async function createDocument(reactorClient, args, reactorDriveClient) {
2073
2326
  if (!args.document || typeof args.document !== "object") throw new GraphQLError("Invalid document: must be an object");
2074
2327
  const document = args.document;
@@ -2167,6 +2420,49 @@ async function createDocumentWithInitialState(reactorClient, args, reactorDriveC
2167
2420
  throw new GraphQLError(`Failed to convert created document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
2168
2421
  }
2169
2422
  }
2423
+ /** The branch a mutation applies to when the caller names none. */
2424
+ const DEFAULT_BRANCH = "main";
2425
+ /**
2426
+ * Applies actions to a document and waits for the result.
2427
+ *
2428
+ * The wire form of `IReactorClient.execute`, and named for it. Takes the actions
2429
+ * already coerced against `ActionInput`, so the only conversion left is the one
2430
+ * the schema cannot express: a signature travels as one string, and verification
2431
+ * reads it as the tuple it was.
2432
+ */
2433
+ async function execute(reactorClient, args) {
2434
+ const actions = toSubmittableActions(args.actions);
2435
+ const branch = fromInputMaybe(args.branch) ?? DEFAULT_BRANCH;
2436
+ let result;
2437
+ try {
2438
+ result = await reactorClient.execute(args.documentIdentifier, branch, actions);
2439
+ } catch (error) {
2440
+ throw new GraphQLError(`Failed to execute actions: ${error instanceof Error ? error.message : "Unknown error"}`);
2441
+ }
2442
+ try {
2443
+ return toGqlPhDocument(result);
2444
+ } catch (error) {
2445
+ throw new GraphQLError(`Failed to convert executed document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
2446
+ }
2447
+ }
2448
+ /**
2449
+ * Submits actions to a document and returns the job that will apply them.
2450
+ *
2451
+ * Returns the job rather than only its id, matching
2452
+ * `IReactorClient.executeAsync`. A job handed back from a submission has no
2453
+ * result yet, which is why `JobInfo.result` is nullable.
2454
+ */
2455
+ async function executeAsync(reactorClient, args) {
2456
+ const actions = toSubmittableActions(args.actions);
2457
+ const branch = fromInputMaybe(args.branch) ?? DEFAULT_BRANCH;
2458
+ let job;
2459
+ try {
2460
+ job = await reactorClient.executeAsync(args.documentIdentifier, branch, actions);
2461
+ } catch (error) {
2462
+ throw new GraphQLError(`Failed to submit actions: ${error instanceof Error ? error.message : "Unknown error"}`);
2463
+ }
2464
+ return toGqlJobInfo(job);
2465
+ }
2170
2466
  async function mutateDocument(reactorClient, args) {
2171
2467
  let validatedActions;
2172
2468
  try {
@@ -2300,7 +2596,14 @@ async function deleteDocuments(reactorClient, args) {
2300
2596
  throw new GraphQLError(`Failed to delete documents: ${error instanceof Error ? error.message : "Unknown error"}`);
2301
2597
  }
2302
2598
  }
2303
- async function touchChannel(syncManager, args) {
2599
+ /**
2600
+ * Registers a sync channel, or reports the state of one already registered.
2601
+ *
2602
+ * `boundAddress` claims a channel at creation for the subject that created it.
2603
+ * An anonymously created channel is left unbound, which is not a permission but
2604
+ * an unanswered question: the first authenticated subject to use it adopts it.
2605
+ */
2606
+ async function touchChannel(syncManager, args, boundAddress) {
2304
2607
  try {
2305
2608
  return {
2306
2609
  success: true,
@@ -2312,7 +2615,10 @@ async function touchChannel(syncManager, args) {
2312
2615
  scope: [...args.input.filter.scope],
2313
2616
  branch: args.input.filter.branch
2314
2617
  };
2315
- const options = { sinceTimestampUtcMs: args.input.sinceTimestampUtcMs };
2618
+ const options = {
2619
+ sinceTimestampUtcMs: args.input.sinceTimestampUtcMs,
2620
+ boundAddress
2621
+ };
2316
2622
  try {
2317
2623
  await syncManager.add(args.input.name, DriveCollectionId.fromKey(args.input.collectionId), {
2318
2624
  type: "polling",
@@ -2327,9 +2633,50 @@ async function touchChannel(syncManager, args) {
2327
2633
  };
2328
2634
  }
2329
2635
  /**
2636
+ * The sync operations in `syncOps` this subject may not be served.
2637
+ *
2638
+ * A sync operation is one document, one branch and (from the outbox) one scope,
2639
+ * so the answer is per entry rather than per operation: an entry is withheld
2640
+ * whole or served whole, which is what keeps a scope's operation sequence
2641
+ * unbroken for whoever does receive it. A dead letter can name several scopes,
2642
+ * and is withheld only when the policy serves none of them -- the metadata
2643
+ * scopes are readable by every holder, so in practice a dead letter naming one
2644
+ * is always served.
2645
+ *
2646
+ * The policy is read once per document and branch, and the memo lives no longer
2647
+ * than this call: a poll must not answer from a policy that changed between
2648
+ * polls, since the widening case is exactly the one holding exists to serve.
2649
+ *
2650
+ * A read that fails for any reason other than the document being absent is
2651
+ * allowed to propagate. The poll then fails, the puller retries, and nothing is
2652
+ * served in the meantime -- which is the safe direction, and a visible one.
2653
+ */
2654
+ async function collectHeldSyncOperations(syncOps, gate, subject, signal) {
2655
+ const held = /* @__PURE__ */ new Set();
2656
+ const predicates = /* @__PURE__ */ new Map();
2657
+ for (const syncOp of syncOps) {
2658
+ const key = `${syncOp.documentId}\u0000${syncOp.branch}`;
2659
+ let predicate = predicates.get(key);
2660
+ if (!predicate) {
2661
+ predicate = gate.scopePredicateById(syncOp.documentId, subject, syncOp.branch, signal);
2662
+ predicates.set(key, predicate);
2663
+ }
2664
+ const readable = await predicate;
2665
+ if (!syncOp.scopes.some((scope) => readable(scope))) held.add(syncOp.id);
2666
+ }
2667
+ return held;
2668
+ }
2669
+ /**
2330
2670
  * Polls the switchboard for new sync envelopes and acknowledges previously
2331
2671
  * received operations.
2332
2672
  *
2673
+ * `forbiddenIds` and `heldOpIds` are two different refusals. A forbidden
2674
+ * document is consumed: its delivery counters are advanced as though it had
2675
+ * been sent, so it is never reconsidered. A held sync operation is withheld:
2676
+ * nothing about it is touched, so a policy that later widens serves it whole on
2677
+ * a subsequent poll. Which one applies is the caller's decision, and the two
2678
+ * are intersected, so a document refused either way is not served.
2679
+ *
2333
2680
  * Ordinal frames of reference:
2334
2681
  * - `outboxAck` / `outboxLatest`: switchboard's ordinals (used to trim/filter
2335
2682
  * the switchboard's outbox)
@@ -2337,16 +2684,17 @@ async function touchChannel(syncManager, args) {
2337
2684
  * client ordinal the switchboard has successfully applied, so the client
2338
2685
  * can trim its own outbox)
2339
2686
  */
2340
- function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new Set()) {
2687
+ function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new Set(), heldOpIds = /* @__PURE__ */ new Set()) {
2341
2688
  let remote;
2342
2689
  try {
2343
2690
  remote = syncManager.getById(args.channelId);
2344
2691
  } catch (error) {
2345
2692
  throw new GraphQLError(`Channel not found: ${error instanceof Error ? error.message : "Unknown error"}`);
2346
2693
  }
2347
- const deadLetters = remote.channel.deadLetter.items.filter((syncOp) => !forbiddenIds.has(syncOp.documentId)).map((syncOp) => ({
2694
+ const deadLetters = remote.channel.deadLetter.items.filter((syncOp) => !forbiddenIds.has(syncOp.documentId) && !heldOpIds.has(syncOp.id)).map((syncOp) => ({
2348
2695
  documentId: syncOp.documentId,
2349
2696
  error: syncOp.error?.message ?? "Unknown error",
2697
+ errorType: syncOperationErrorType(syncOp.error),
2350
2698
  jobId: syncOp.jobId,
2351
2699
  branch: syncOp.branch,
2352
2700
  scopes: syncOp.scopes,
@@ -2392,6 +2740,7 @@ function pollSyncEnvelopes(syncManager, args, forbiddenIds = /* @__PURE__ */ new
2392
2740
  hasMore = true;
2393
2741
  break;
2394
2742
  }
2743
+ if (heldOpIds.has(syncOp.id)) continue;
2395
2744
  syncOp.deliveredCount ??= 0;
2396
2745
  syncOp.emittedCount ??= 0;
2397
2746
  while (syncOp.deliveredCount < syncOp.emittedCount && syncOp.operations[syncOp.deliveredCount].context.ordinal <= args.outboxLatest) syncOp.deliveredCount += 1;
@@ -2875,6 +3224,9 @@ var GraphQLManager = class {
2875
3224
  /** Cached document models for schema generation - updated on init and regenerate */
2876
3225
  cachedDocumentModels = [];
2877
3226
  subgraphHandlerCache = /* @__PURE__ */ new Map();
3227
+ /** Per-subgraph handlers by name, before auth/drive middleware is applied,
3228
+ * for in-process queries by trusted internals. */
3229
+ #internalSubgraphHandlers = /* @__PURE__ */ new Map();
2878
3230
  /**
2879
3231
  * Optional reactor-drive client. When the switchboard is configured with a
2880
3232
  * reactor-drive container, this is provided and the resolvers dispatch to
@@ -2882,7 +3234,7 @@ var GraphQLManager = class {
2882
3234
  */
2883
3235
  reactorDriveClient;
2884
3236
  authorizationService;
2885
- constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authService, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService, reactorDriveClient) {
3237
+ constructor(path, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, logger, httpAdapter, gatewayAdapter, authService, documentPermissionService, featureFlags = DefaultFeatureFlags, port = 4001, authorizationService, reactorDriveClient, syncServingGate) {
2886
3238
  this.path = path;
2887
3239
  this.httpServer = httpServer;
2888
3240
  this.wsServer = wsServer;
@@ -2897,6 +3249,7 @@ var GraphQLManager = class {
2897
3249
  this.documentPermissionService = documentPermissionService;
2898
3250
  this.featureFlags = featureFlags;
2899
3251
  this.port = port;
3252
+ this.syncServingGate = syncServingGate;
2900
3253
  if (!authorizationService) throw new Error("GraphQLManager requires an authorizationService");
2901
3254
  this.authorizationService = authorizationService;
2902
3255
  this.reactorDriveClient = reactorDriveClient;
@@ -2914,7 +3267,7 @@ var GraphQLManager = class {
2914
3267
  if (!models.find((it) => it.documentModel.global.name === "DocumentDrive")) throw new Error("DocumentDrive model required");
2915
3268
  await this.gatewayAdapter.start(this.httpServer);
2916
3269
  this.httpAdapter.setupMiddleware({ bodyLimit: "50mb" });
2917
- const driveRoutePath = path.join(this.path, "d/:drive");
3270
+ const driveRoutePath = path.posix.join(this.path, "d/:drive");
2918
3271
  const driveMatcher = match(driveRoutePath);
2919
3272
  this.httpAdapter.mount(driveRoutePath, async (request) => {
2920
3273
  const url = new URL(request.url);
@@ -2982,7 +3335,8 @@ var GraphQLManager = class {
2982
3335
  syncManager: this.syncManager,
2983
3336
  path: this.path,
2984
3337
  documentPermissionService: this.documentPermissionService,
2985
- authorizationService: this.authorizationService
3338
+ authorizationService: this.authorizationService,
3339
+ syncServingGate: this.syncServingGate
2986
3340
  });
2987
3341
  await this.#addSubgraphInstance(subgraphInstance, supergraph, false);
2988
3342
  } catch (error) {
@@ -3036,10 +3390,49 @@ var GraphQLManager = class {
3036
3390
  syncManager: this.syncManager,
3037
3391
  path: this.path,
3038
3392
  documentPermissionService: this.documentPermissionService,
3039
- authorizationService: this.authorizationService
3393
+ authorizationService: this.authorizationService,
3394
+ syncServingGate: this.syncServingGate
3040
3395
  });
3041
3396
  return this.#addSubgraphInstance(subgraphInstance, supergraph, core);
3042
3397
  }
3398
+ /** Find a registered subgraph by name, core or package-contributed. */
3399
+ getSubgraphByName(name) {
3400
+ for (const subgraphs of [...this.coreSubgraphsMap.values(), ...this.subgraphs.values()]) {
3401
+ const found = subgraphs.find((subgraph) => subgraph.name === name);
3402
+ if (found) return found;
3403
+ }
3404
+ }
3405
+ /** Whether a registered subgraph has a live handler, i.e. whether
3406
+ * {@link executeSubgraphQuery} can reach it right now. */
3407
+ hasSubgraphHandler(name) {
3408
+ return this.#internalSubgraphHandlers.has(name);
3409
+ }
3410
+ /** Run a query against one registered subgraph in-process: the subgraph's own
3411
+ * handler, without a socket or the auth/drive middleware. Trusted callers. */
3412
+ async executeSubgraphQuery(subgraphName, query, variables) {
3413
+ const entry = this.#internalSubgraphHandlers.get(subgraphName);
3414
+ if (!entry) throw new Error(this.getSubgraphByName(subgraphName) ? `Subgraph "${subgraphName}" is registered but has no handler yet` : `Subgraph "${subgraphName}" is not registered`);
3415
+ const url = new URL(entry.path, `http://localhost:${this.port}/`);
3416
+ const response = await entry.handler(new Request(url, {
3417
+ method: "POST",
3418
+ headers: {
3419
+ "content-type": "application/json",
3420
+ accept: "application/json"
3421
+ },
3422
+ body: JSON.stringify({
3423
+ query,
3424
+ variables
3425
+ })
3426
+ }));
3427
+ if (!response.ok) {
3428
+ const detail = await response.text().catch(() => "");
3429
+ throw new Error(`Subgraph "${subgraphName}" query failed: ${response.status}${detail ? ` ${detail}` : ""}`);
3430
+ }
3431
+ const body = await response.json();
3432
+ if (body.errors?.length) throw new Error(body.errors.map((error) => error.message).join("; "));
3433
+ if (!body.data) throw new Error(`Subgraph "${subgraphName}" returned no data`);
3434
+ return body.data;
3435
+ }
3043
3436
  updateRouter = debounce(this._updateRouter.bind(this), 1e3);
3044
3437
  async _updateRouter() {
3045
3438
  this.logger.debug("Updating router");
@@ -3050,7 +3443,7 @@ var GraphQLManager = class {
3050
3443
  } catch (error) {
3051
3444
  this.logger.error("Failed to update Apollo Gateway supergraph: @error", error);
3052
3445
  }
3053
- const superGraphPath = path.join(this.path, "graphql");
3446
+ const superGraphPath = path.posix.join(this.path, "graphql");
3054
3447
  this.#setupSupergraphSSE(superGraphPath);
3055
3448
  }
3056
3449
  getAdditionalContextFields = () => {
@@ -3113,7 +3506,21 @@ var GraphQLManager = class {
3113
3506
  });
3114
3507
  }
3115
3508
  #getSubgraphPath(subgraph, supergraph) {
3116
- return path.join(subgraph.path ?? "", supergraph, subgraph.name);
3509
+ return path.posix.join(subgraph.path ?? "", supergraph, subgraph.name);
3510
+ }
3511
+ /** The in-process handler map is keyed by bare name, so two distinct
3512
+ * subgraphs sharing one name would shadow each other: keep the first. */
3513
+ #setInternalSubgraphHandler(subgraph, subgraphPath, handler) {
3514
+ const existing = this.#internalSubgraphHandlers.get(subgraph.name);
3515
+ if (existing && existing.subgraph !== subgraph) {
3516
+ this.logger.warn("Two subgraphs are named @name (@kept and @ignored); in-process queries keep the first", subgraph.name, existing.path, subgraphPath);
3517
+ return;
3518
+ }
3519
+ this.#internalSubgraphHandlers.set(subgraph.name, {
3520
+ subgraph,
3521
+ path: subgraphPath,
3522
+ handler
3523
+ });
3117
3524
  }
3118
3525
  async #setupSubgraphs(subgraphsMap) {
3119
3526
  for (const [supergraph, subgraphs] of subgraphsMap.entries()) for (const subgraph of subgraphs) {
@@ -3125,6 +3532,7 @@ var GraphQLManager = class {
3125
3532
  const rawHandler = await this.gatewayAdapter.createHandler(schema, this.#makeContextFactory());
3126
3533
  const fetchHandler = this.#composeFetchMiddleware(rawHandler);
3127
3534
  this.subgraphHandlerCache.set(subgraphPath, fetchHandler);
3535
+ this.#setInternalSubgraphHandler(subgraph, subgraphPath, rawHandler);
3128
3536
  this.httpAdapter.mount(subgraphPath, fetchHandler);
3129
3537
  if (subgraph.hasSubscriptions) {
3130
3538
  try {
@@ -3171,7 +3579,7 @@ var GraphQLManager = class {
3171
3579
  }));
3172
3580
  }
3173
3581
  async #createSupergraphGateway() {
3174
- const superGraphPath = path.join(this.path, "graphql");
3582
+ const superGraphPath = path.posix.join(this.path, "graphql");
3175
3583
  const rawHandler = await this.gatewayAdapter.createSupergraphHandler(() => this.#getSubgraphDefinitions(), this.httpServer, this.#makeContextFactory());
3176
3584
  const fetchHandler = this.#composeFetchMiddleware(rawHandler);
3177
3585
  this.httpAdapter.mount(superGraphPath, fetchHandler);
@@ -3536,6 +3944,17 @@ const DocumentChangeEventDTO = z.object({
3536
3944
  documents: z.array(PHDocumentDTO),
3537
3945
  context: DocumentChangeContextDTO.nullable().optional()
3538
3946
  }).strip();
3947
+ const ActionEvaluationDTO = z.object({
3948
+ decision: z.enum(["ALLOW", "DENY"]),
3949
+ reason: z.string().nullable().optional()
3950
+ }).strip();
3951
+ const ActionEvaluationsDTO = z.object({
3952
+ evaluations: z.array(ActionEvaluationDTO),
3953
+ allAllowed: z.boolean(),
3954
+ anyAllowed: z.boolean(),
3955
+ allDenied: z.boolean(),
3956
+ anyDenied: z.boolean()
3957
+ }).strip();
3539
3958
  const JobChangeEventDTO = z.object({
3540
3959
  jobId: z.string(),
3541
3960
  status: z.string(),
@@ -3569,6 +3988,9 @@ const operationValidators = {
3569
3988
  GetJobStatus: (data) => {
3570
3989
  if (data.jobStatus) JobInfoDTO.parse(data.jobStatus);
3571
3990
  },
3991
+ EvaluateActions: (data) => {
3992
+ if (data.evaluateActions) ActionEvaluationsDTO.parse(data.evaluateActions);
3993
+ },
3572
3994
  CreateDocument: (data) => {
3573
3995
  if (data.createDocument) PHDocumentDTO.parse(data.createDocument);
3574
3996
  },
@@ -3631,7 +4053,7 @@ function createReactorGraphQLClient(url, fetchImpl = fetch, headers) {
3631
4053
  }
3632
4054
  //#endregion
3633
4055
  //#region src/graphql/reactor/schema.graphql
3634
- var schema_default = "# Scalar types (for codegen - also defined in create-schema.ts)\nscalar JSONObject\nscalar DateTime\n\n# Input types\ninput PagingInput {\n limit: Int\n offset: Int\n cursor: String\n}\n\ninput ViewFilterInput {\n branch: String\n scopes: [String!]\n}\n\ninput SearchFilterInput {\n type: String\n parentId: String\n identifiers: [String!]\n}\n\ninput OperationsFilterInput {\n documentId: String!\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\ninput DocumentOperationsFilterInput {\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\n# Enums\nenum PropagationMode {\n CASCADE\n ORPHAN\n}\n\nenum DocumentChangeType {\n CREATED\n DELETED\n UPDATED\n PARENT_ADDED\n PARENT_REMOVED\n CHILD_ADDED\n CHILD_REMOVED\n}\n\n# Object types\ntype DocumentModelGlobalState {\n id: String!\n name: String!\n namespace: String\n version: String\n specification: JSONObject!\n}\n\ntype DocumentModelResultPage {\n items: [DocumentModelGlobalState!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype Revision {\n scope: String!\n revision: Int!\n}\n\ntype PHDocument {\n id: String!\n slug: String\n preferredEditor: String\n name: String!\n documentType: String!\n state: JSONObject!\n revisionsList: [Revision!]!\n createdAtUtcIso: DateTime!\n lastModifiedAtUtcIso: DateTime!\n operations(\n filter: DocumentOperationsFilterInput\n paging: PagingInput\n ): ReactorOperationResultPage\n}\n\ntype PHDocumentResultPage {\n items: [PHDocument!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype DeadLetterInfo {\n documentId: String!\n error: String!\n jobId: String!\n branch: String!\n scopes: [String!]!\n operationCount: Int!\n}\n\ntype PollSyncEnvelopesResult {\n envelopes: [SyncEnvelope!]!\n ackOrdinal: Int!\n deadLetters: [DeadLetterInfo!]!\n hasMore: Boolean!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype JobInfo {\n id: String!\n status: String!\n result: JSONObject!\n error: String\n createdAt: DateTime!\n completedAt: DateTime\n}\n\ntype DocumentChangeEvent {\n type: DocumentChangeType!\n documents: [PHDocument!]!\n context: DocumentChangeContext\n}\n\ntype DocumentChangeContext {\n parentId: String\n childId: String\n}\n\ntype JobChangeEvent {\n jobId: String!\n status: String!\n result: JSONObject!\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n# Input types for sync operations\ninput ActionContextInput {\n signer: ReactorSignerInput\n}\n\ninput ReactorSignerInput {\n user: ReactorSignerUserInput\n app: ReactorSignerAppInput\n signatures: [String!]!\n}\n\ninput ReactorSignerUserInput {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ninput ReactorSignerAppInput {\n name: String!\n key: String!\n}\n\ninput ActionInput {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContextInput\n}\n\n# Synchronization types\ntype ReactorOperation {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n id: String\n action: Action!\n}\n\ninput OperationInput {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n id: String\n action: ActionInput!\n}\n\ntype OperationContext {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ninput OperationContextInput {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ntype OperationWithContext {\n operation: ReactorOperation!\n context: OperationContext!\n}\n\ninput OperationWithContextInput {\n operation: OperationInput!\n context: OperationContextInput!\n}\n\ntype ChannelMeta {\n id: String!\n}\n\ninput ChannelMetaInput {\n id: String!\n}\n\ntype RemoteCursor {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\ninput RemoteCursorInput {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\nenum SyncEnvelopeType {\n OPERATIONS\n ACK\n}\n\ntype SyncEnvelope {\n type: SyncEnvelopeType!\n channelMeta: ChannelMeta!\n operations: [OperationWithContext!]\n cursor: RemoteCursor\n key: String\n dependsOn: [String!]\n}\n\ninput SyncEnvelopeInput {\n type: SyncEnvelopeType!\n channelMeta: ChannelMetaInput!\n operations: [OperationWithContextInput!]\n cursor: RemoteCursorInput\n key: String\n dependsOn: [String!]\n}\n\ninput RemoteFilterInput {\n documentId: [String!]!\n scope: [String!]!\n branch: String!\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n}\n\ntype Query {\n # Get document models for a namespace\n documentModels(\n namespace: String\n paging: PagingInput\n ): DocumentModelResultPage!\n\n # Get a specific document by ID or slug\n document(identifier: String!, view: ViewFilterInput): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get incoming relationships of a given type to a target document\n documentIncomingRelationships(\n targetIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n # Poll for sync envelopes from a channel\n pollSyncEnvelopes(\n channelId: String!\n outboxAck: Int!\n outboxLatest: Int!\n ): PollSyncEnvelopesResult!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(document: JSONObject!, parentIdentifier: String): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdentifier: String\n ): PHDocument!\n\n # Apply actions to a document (synchronous)\n mutateDocument(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): PHDocument!\n\n # Submit actions to a document (asynchronous)\n mutateDocumentAsync(\n documentIdentifier: String!\n actions: [JSONObject!]!\n view: ViewFilterInput\n ): String!\n\n # Rename a document\n renameDocument(\n documentIdentifier: String!\n name: String!\n branch: String\n ): PHDocument!\n\n # Update the preferred editor recorded in the document header meta.\n # Pass null/omit to clear it.\n setPreferredEditor(\n documentIdentifier: String!\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdentifier: String!\n targetParentIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(identifier: String!, propagate: PropagationMode): Boolean!\n\n # Delete multiple documents\n deleteDocuments(identifiers: [String!]!, propagate: PropagationMode): Boolean!\n\n # Touch (create or update) a channel for sync\n touchChannel(input: TouchChannelInput!): TouchChannelResult!\n\n # Push sync envelopes to a channel\n pushSyncEnvelopes(envelopes: [SyncEnvelopeInput!]!): Boolean!\n}\n\ntype Subscription {\n # Subscribe to document changes\n documentChanges(\n search: SearchFilterInput\n view: ViewFilterInput\n ): DocumentChangeEvent!\n\n # Subscribe to job changes\n jobChanges(jobId: String!): JobChangeEvent!\n}\n";
4056
+ var schema_default = "# Scalar types (for codegen - also defined in create-schema.ts)\nscalar JSONObject\nscalar DateTime\n\n# Input types\ninput PagingInput {\n limit: Int\n offset: Int\n cursor: String\n}\n\ninput ViewFilterInput {\n branch: String\n scopes: [String!]\n}\n\ninput SearchFilterInput {\n type: String\n parentId: String\n identifiers: [String!]\n}\n\ninput OperationsFilterInput {\n documentId: String!\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\ninput DocumentOperationsFilterInput {\n branch: String\n scopes: [String!]\n actionTypes: [String!]\n sinceRevision: Int\n timestampFrom: String\n timestampTo: String\n}\n\n# Enums\nenum PropagationMode {\n CASCADE\n ORPHAN\n}\n\nenum DocumentChangeType {\n CREATED\n DELETED\n UPDATED\n PARENT_ADDED\n PARENT_REMOVED\n CHILD_ADDED\n CHILD_REMOVED\n}\n\n# Object types\ntype DocumentModelGlobalState {\n id: String!\n name: String!\n namespace: String\n version: String\n specification: JSONObject!\n}\n\ntype DocumentModelResultPage {\n items: [DocumentModelGlobalState!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype Revision {\n scope: String!\n revision: Int!\n}\n\ntype PHDocument {\n id: String!\n slug: String\n preferredEditor: String\n name: String!\n documentType: String!\n state: JSONObject!\n revisionsList: [Revision!]!\n createdAtUtcIso: DateTime!\n lastModifiedAtUtcIso: DateTime!\n operations(\n filter: DocumentOperationsFilterInput\n paging: PagingInput\n ): ReactorOperationResultPage\n}\n\ntype PHDocumentResultPage {\n items: [PHDocument!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\n totalCount: Int!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype DeadLetterInfo {\n documentId: String!\n error: String!\n \"\"\"\n How the origin classified the failure. A peer needs this to mirror the origin's\n quarantine decision; without it a held auth operation would freeze the document\n on the peer side while the origin kept syncing.\n \"\"\"\n errorType: String\n jobId: String!\n branch: String!\n scopes: [String!]!\n operationCount: Int!\n}\n\ntype PollSyncEnvelopesResult {\n envelopes: [SyncEnvelope!]!\n ackOrdinal: Int!\n deadLetters: [DeadLetterInfo!]!\n hasMore: Boolean!\n}\n\ntype DocumentWithChildren {\n document: PHDocument!\n childIds: [String!]!\n}\n\ntype MoveRelationshipResult {\n source: PHDocument!\n target: PHDocument!\n}\n\ntype JobInfo {\n id: String!\n status: String!\n\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 createdAt: DateTime!\n completedAt: DateTime\n}\n\ntype DocumentChangeEvent {\n type: DocumentChangeType!\n documents: [PHDocument!]!\n context: DocumentChangeContext\n}\n\ntype DocumentChangeContext {\n parentId: String\n childId: String\n}\n\ntype JobChangeEvent {\n jobId: String!\n status: String!\n result: JSONObject!\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n\"\"\"\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 \"\"\"The index of the last operation in the scope the action applies to.\"\"\"\n prevOpIndex: Int\n\n \"\"\"The hash of the state the action applies to.\"\"\"\n prevOpHash: String\n\n \"\"\"Covers replay of an operation that would otherwise be a no-op.\"\"\"\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# Synchronization types\ntype ReactorOperation {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n deniedReason: String\n id: String\n action: Action!\n}\n\ninput OperationInput {\n index: Int!\n timestampUtcMs: String!\n hash: String!\n skip: Int!\n error: String\n deniedReason: String\n id: String\n action: ActionInput!\n}\n\ntype OperationContext {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ninput OperationContextInput {\n documentId: String!\n documentType: String!\n scope: String!\n branch: String!\n ordinal: Int!\n}\n\ntype OperationWithContext {\n operation: ReactorOperation!\n context: OperationContext!\n}\n\ninput OperationWithContextInput {\n operation: OperationInput!\n context: OperationContextInput!\n}\n\ntype ChannelMeta {\n id: String!\n}\n\ninput ChannelMetaInput {\n id: String!\n}\n\ntype RemoteCursor {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\ninput RemoteCursorInput {\n remoteName: String!\n cursorOrdinal: Int!\n lastSyncedAtUtcMs: String\n}\n\nenum SyncEnvelopeType {\n OPERATIONS\n ACK\n}\n\ntype SyncEnvelope {\n type: SyncEnvelopeType!\n channelMeta: ChannelMeta!\n operations: [OperationWithContext!]\n cursor: RemoteCursor\n key: String\n dependsOn: [String!]\n}\n\ninput SyncEnvelopeInput {\n type: SyncEnvelopeType!\n channelMeta: ChannelMetaInput!\n operations: [OperationWithContextInput!]\n cursor: RemoteCursorInput\n key: String\n dependsOn: [String!]\n}\n\ninput RemoteFilterInput {\n documentId: [String!]!\n scope: [String!]!\n branch: String!\n}\n\ninput TouchChannelInput {\n id: String!\n name: String!\n collectionId: String!\n filter: RemoteFilterInput!\n sinceTimestampUtcMs: String!\n}\n\ntype TouchChannelResult {\n success: Boolean!\n ackOrdinal: Int!\n}\n\ntype Query {\n # Get document models for a namespace\n documentModels(\n namespace: String\n paging: PagingInput\n ): DocumentModelResultPage!\n\n # Get a specific document by ID or slug\n document(identifier: String!, view: ViewFilterInput): DocumentWithChildren\n\n # Get outgoing relationships of a given type from a source document\n documentOutgoingRelationships(\n sourceIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get incoming relationships of a given type to a target document\n documentIncomingRelationships(\n targetIdentifier: String!\n relationshipType: String!\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n \"\"\"\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 documentIdentifier: String!\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 ): PollSyncEnvelopesResult!\n}\n\ntype Mutation {\n # Create a new document\n createDocument(document: JSONObject!, parentIdentifier: String): PHDocument!\n\n # Create an empty document of specified type\n createEmptyDocument(\n documentType: String!\n parentIdentifier: String\n ): PHDocument!\n\n \"\"\"\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 documentIdentifier: String!\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 documentIdentifier: String!\n actions: [ActionInput!]!\n branch: String\n ): JobInfo!\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 documentIdentifier: String!\n name: String!\n branch: String\n ): PHDocument!\n\n # Update the preferred editor recorded in the document header meta.\n # Pass null/omit to clear it.\n setPreferredEditor(\n documentIdentifier: String!\n preferredEditor: String\n branch: String\n ): PHDocument!\n\n # Add a relationship between two documents\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Remove a relationship between two documents\n removeRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): PHDocument!\n\n # Move a relationship from one source to another\n moveRelationship(\n sourceParentIdentifier: String!\n targetParentIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n branch: String\n ): MoveRelationshipResult!\n\n # Delete a single document\n deleteDocument(identifier: String!, propagate: PropagationMode): Boolean!\n\n # Delete multiple documents\n deleteDocuments(identifiers: [String!]!, propagate: PropagationMode): Boolean!\n\n # Touch (create or update) a channel for sync\n touchChannel(input: TouchChannelInput!): TouchChannelResult!\n\n # Push sync envelopes to a channel\n pushSyncEnvelopes(envelopes: [SyncEnvelopeInput!]!): Boolean!\n}\n\ntype Subscription {\n # Subscribe to document changes\n documentChanges(\n search: SearchFilterInput\n view: ViewFilterInput\n ): DocumentChangeEvent!\n\n # Subscribe to job changes\n jobChanges(jobId: String!): JobChangeEvent!\n}\n";
3635
4057
  //#endregion
3636
4058
  //#region src/graphql/reactor/pubsub.ts
3637
4059
  const pubSub = new PubSub();
@@ -3769,6 +4191,46 @@ var ReactorSubgraph = class extends BaseSubgraph {
3769
4191
  if (!await this.canReadDocument(documentId, ctx)) forbidden.add(documentId);
3770
4192
  }
3771
4193
  }
4194
+ /**
4195
+ * Enforces which subject a sync channel answers to, adopting an unclaimed one.
4196
+ *
4197
+ * A channel is a private queue: it names a collection, and it accumulates
4198
+ * exactly the operations one subject is owed, so serving it to a second
4199
+ * subject would hand over that subject's cursor as well as its contents. An
4200
+ * unbound channel -- created anonymously, or created before binding existed --
4201
+ * is claimed by the first authenticated subject to use it, and answers to that
4202
+ * address alone thereafter. Adoption is safe precisely because serving
4203
+ * withholds rather than consumes: before it was claimed the channel served
4204
+ * only what an anonymous subject may read, and everything withheld is still
4205
+ * queued for the adopter.
4206
+ *
4207
+ * The refusal is the shape a read denial takes, so a puller treats it as a
4208
+ * signal to authenticate again rather than as a transport failure.
4209
+ *
4210
+ * Nothing is enforced or adopted without a serving gate. Below
4211
+ * `authEnforcement` there is no policy being enforced for the channel to
4212
+ * belong to, and refusing a poll there would break sync for no gain.
4213
+ */
4214
+ async #bindOrRefuseChannel(channelId, ctx) {
4215
+ if (!this.syncServingGate) return;
4216
+ let remote;
4217
+ try {
4218
+ remote = this.syncManager.getById(channelId);
4219
+ } catch {
4220
+ return;
4221
+ }
4222
+ const bound = remote.meta.options.boundAddress;
4223
+ const address = ctx.user?.address;
4224
+ if (bound === void 0) {
4225
+ if (address !== void 0) await this.syncManager.bindRemote(channelId, address);
4226
+ return;
4227
+ }
4228
+ if (bound !== address) throw new ForbiddenError("to poll this sync channel");
4229
+ }
4230
+ /** The address a channel created by this request belongs to, if any. */
4231
+ #creatorBinding(ctx) {
4232
+ return this.syncServingGate ? ctx.user?.address : void 0;
4233
+ }
3772
4234
  typeDefs = gql(schema_default);
3773
4235
  resolvers = {
3774
4236
  PHDocument: { operations: async (parent, args, ctx) => {
@@ -3787,7 +4249,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3787
4249
  return await documentOperations(this.reactorClient, {
3788
4250
  filter,
3789
4251
  paging: args.paging
3790
- });
4252
+ }, this.viewSubject(ctx));
3791
4253
  } catch (error) {
3792
4254
  this.logger.error("Error in PHDocument.operations: @Error", error);
3793
4255
  throw error;
@@ -3810,7 +4272,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3810
4272
  return await document(this.reactorClient, {
3811
4273
  ...args,
3812
4274
  identifier: handle.fetchIdentifier
3813
- });
4275
+ }, this.viewSubject(ctx));
3814
4276
  } catch (error) {
3815
4277
  this.logger.error("Error in document: @Error", error);
3816
4278
  throw error;
@@ -3823,7 +4285,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3823
4285
  return await documentOutgoingRelationships(this.reactorClient, {
3824
4286
  ...args,
3825
4287
  sourceIdentifier: handle.fetchIdentifier
3826
- });
4288
+ }, this.viewSubject(ctx));
3827
4289
  } catch (error) {
3828
4290
  this.logger.error("Error in documentOutgoingRelationships: @Error", error);
3829
4291
  throw error;
@@ -3836,7 +4298,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3836
4298
  const result = await documentIncomingRelationships(this.reactorClient, {
3837
4299
  ...args,
3838
4300
  targetIdentifier: handle.fetchIdentifier
3839
- });
4301
+ }, this.viewSubject(ctx));
3840
4302
  if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
3841
4303
  const filteredItems = [];
3842
4304
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
@@ -3857,7 +4319,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
3857
4319
  const result = await findDocuments(this.reactorClient, {
3858
4320
  ...args,
3859
4321
  search: args.search ?? {}
3860
- });
4322
+ }, this.viewSubject(ctx));
3861
4323
  if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
3862
4324
  const filteredItems = [];
3863
4325
  for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
@@ -3891,12 +4353,25 @@ var ReactorSubgraph = class extends BaseSubgraph {
3891
4353
  ...args.filter,
3892
4354
  documentId: handle.fetchIdentifier
3893
4355
  }
3894
- });
4356
+ }, this.viewSubject(ctx));
3895
4357
  } catch (error) {
3896
4358
  this.logger.error("Error in documentOperations: @Error", error);
3897
4359
  throw error;
3898
4360
  }
3899
4361
  },
4362
+ evaluateActions: async (_parent, args, ctx) => {
4363
+ this.logger.debug("evaluateActions(@args)", args);
4364
+ try {
4365
+ const handle = await this.assertCanRead(args.documentIdentifier, ctx);
4366
+ return await evaluateActions(this.reactorClient, {
4367
+ ...args,
4368
+ documentIdentifier: handle.fetchIdentifier
4369
+ }, this.viewSubject(ctx));
4370
+ } catch (error) {
4371
+ this.logger.error("Error in evaluateActions: @Error", error);
4372
+ throw error;
4373
+ }
4374
+ },
3900
4375
  pollSyncEnvelopes: async (_parent, args, ctx) => {
3901
4376
  this.logger.debug("pollSyncEnvelopes(@args)", args);
3902
4377
  try {
@@ -3909,12 +4384,14 @@ var ReactorSubgraph = class extends BaseSubgraph {
3909
4384
  const driveId = remote.meta.collectionId.driveId;
3910
4385
  const isAdmin = this.authorizationService.isSupremeAdmin(ctx.user?.address);
3911
4386
  if (!isAdmin) await this.assertCanReadCanonical(driveId, ctx);
4387
+ await this.#bindOrRefuseChannel(args.channelId, ctx);
3912
4388
  const forbiddenIds = /* @__PURE__ */ new Set();
3913
4389
  if (!isAdmin) {
3914
4390
  if (await this.authorizationService.canRead(driveId, void 0)) await this.#collectForbiddenDocuments(remote.channel.outbox.items, forbiddenIds, ctx);
3915
4391
  await this.#collectForbiddenDocuments(remote.channel.deadLetter.items, forbiddenIds, ctx);
3916
4392
  }
3917
- const { envelopes, ackOrdinal, deadLetters, hasMore } = pollSyncEnvelopes(this.syncManager, args, forbiddenIds);
4393
+ const heldOpIds = this.syncServingGate ? await collectHeldSyncOperations([...remote.channel.outbox.items, ...remote.channel.deadLetter.items], this.syncServingGate, this.viewSubject(ctx)) : /* @__PURE__ */ new Set();
4394
+ const { envelopes, ackOrdinal, deadLetters, hasMore } = pollSyncEnvelopes(this.syncManager, args, forbiddenIds, heldOpIds);
3918
4395
  return {
3919
4396
  envelopes,
3920
4397
  ackOrdinal,
@@ -3960,6 +4437,32 @@ var ReactorSubgraph = class extends BaseSubgraph {
3960
4437
  throw error;
3961
4438
  }
3962
4439
  },
4440
+ execute: async (_parent, args, ctx) => {
4441
+ this.logger.debug("execute(@args)", args);
4442
+ try {
4443
+ const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
4444
+ return await execute(this.reactorClient, {
4445
+ ...args,
4446
+ documentIdentifier: handle.fetchIdentifier
4447
+ });
4448
+ } catch (error) {
4449
+ this.logger.error("Error in execute(@args): @Error", args, error);
4450
+ throw error;
4451
+ }
4452
+ },
4453
+ executeAsync: async (_parent, args, ctx) => {
4454
+ this.logger.debug("executeAsync(@args)", args);
4455
+ try {
4456
+ const handle = await this.assertCanExecuteOperations(args.documentIdentifier, args.actions, ctx);
4457
+ return await executeAsync(this.reactorClient, {
4458
+ ...args,
4459
+ documentIdentifier: handle.fetchIdentifier
4460
+ });
4461
+ } catch (error) {
4462
+ this.logger.error("Error in executeAsync(@args): @Error", args, error);
4463
+ throw error;
4464
+ }
4465
+ },
3963
4466
  mutateDocument: async (_parent, args, ctx) => {
3964
4467
  this.logger.debug("mutateDocument(@args)", args);
3965
4468
  try {
@@ -4093,7 +4596,8 @@ var ReactorSubgraph = class extends BaseSubgraph {
4093
4596
  const driveId = DriveCollectionId.fromKey(args.input.collectionId).driveId;
4094
4597
  await this.assertCanReadCanonical(driveId, ctx);
4095
4598
  }
4096
- return await touchChannel(this.syncManager, args);
4599
+ await this.#bindOrRefuseChannel(args.input.id, ctx);
4600
+ return await touchChannel(this.syncManager, args, this.#creatorBinding(ctx));
4097
4601
  } catch (error) {
4098
4602
  this.logger.error("Error in touchChannel(@args): @Error", args, error);
4099
4603
  throw error;
@@ -4205,10 +4709,10 @@ const ADMIN_USERS = getAdminUsers();
4205
4709
  //#endregion
4206
4710
  //#region src/graphql/system/version.ts
4207
4711
  function getVersion() {
4208
- return "6.2.2-dev.7";
4712
+ return "6.2.2-dev.71";
4209
4713
  }
4210
4714
  function getGitHash() {
4211
- return "cf61e36ec3ba12c9edb828e6e9870842ce2388e4";
4715
+ return "7e1fac621574fdab950bbfebc8baeb15ec6c477a";
4212
4716
  }
4213
4717
  function getGitUrl() {
4214
4718
  return buildTreeUrl(getGitHash());
@@ -4306,6 +4810,13 @@ var HttpPackageLoader = class {
4306
4810
  this.logger.verbose(`Loaded ${models.length} document models from ${packageName}`);
4307
4811
  return models;
4308
4812
  }
4813
+ async loadUpgradeManifests(packageSpec) {
4814
+ const { name: packageName } = this.parsePackageSpec(packageSpec);
4815
+ if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
4816
+ const manifests = extractUpgradeManifests(await import(`${this.registryUrl}-/cdn/${packageSpec}/node/document-models/index.mjs`));
4817
+ if (manifests.length > 0) this.logger.verbose(`Loaded ${manifests.length} upgrade manifests from ${packageName}`);
4818
+ return manifests;
4819
+ }
4309
4820
  async loadSubgraphs(packageSpec) {
4310
4821
  const { name: packageName } = this.parsePackageSpec(packageSpec);
4311
4822
  if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
@@ -4431,7 +4942,7 @@ var ImportPackageLoader = class {
4431
4942
  this.logger.verbose(`Loading document models from package: ${identifier}`);
4432
4943
  const pkgModule = await loadDocumentModels(identifier);
4433
4944
  if (pkgModule) {
4434
- const models = Object.values(pkgModule);
4945
+ const models = Object.values(pkgModule).filter((m) => m !== null && typeof m === "object" && "documentModel" in m);
4435
4946
  this.logger.verbose(` ➜ Loaded ${models.length} Document Models from: ${identifier}`);
4436
4947
  return models;
4437
4948
  } else {
@@ -4439,6 +4950,13 @@ var ImportPackageLoader = class {
4439
4950
  return [];
4440
4951
  }
4441
4952
  }
4953
+ async loadUpgradeManifests(identifier) {
4954
+ const pkgModule = await loadDocumentModels(identifier);
4955
+ if (!pkgModule) return [];
4956
+ const manifests = extractUpgradeManifests(pkgModule);
4957
+ if (manifests.length > 0) this.logger.verbose(` ➜ Loaded ${manifests.length} Upgrade Manifests from: ${identifier}`);
4958
+ return manifests;
4959
+ }
4442
4960
  async loadSubgraphs(identifier) {
4443
4961
  this.logger.verbose(`Loading subgraphs from package: ${identifier}`);
4444
4962
  const pkgModule = await loadSubgraphs(identifier);
@@ -4487,13 +5005,19 @@ function isExpectedLoaderMiss(error, pkg) {
4487
5005
  }
4488
5006
  function getUniqueDocumentModels(...documentModels) {
4489
5007
  const uniqueModels = /* @__PURE__ */ new Map();
4490
- for (const models of documentModels) for (const model of models) uniqueModels.set(model.documentModel.global.id, model);
5008
+ for (const models of documentModels) for (const model of models) uniqueModels.set(`${model.documentModel.global.id}@${model.version ?? 1}`, model);
4491
5009
  return Array.from(uniqueModels.values());
4492
5010
  }
5011
+ function getUniqueUpgradeManifests(...upgradeManifests) {
5012
+ const uniqueManifests = /* @__PURE__ */ new Map();
5013
+ for (const manifests of upgradeManifests) for (const manifest of manifests) uniqueManifests.set(manifest.documentType, manifest);
5014
+ return Array.from(uniqueManifests.values());
5015
+ }
4493
5016
  var PackageManager = class {
4494
5017
  logger = childLogger(["reactor-api", "package-manager"]);
4495
5018
  loaders;
4496
5019
  docModelsMap = /* @__PURE__ */ new Map();
5020
+ upgradeManifestsMap = /* @__PURE__ */ new Map();
4497
5021
  subgraphsMap = /* @__PURE__ */ new Map();
4498
5022
  processorMap = /* @__PURE__ */ new Map();
4499
5023
  configWatcher;
@@ -4511,8 +5035,10 @@ var PackageManager = class {
4511
5035
  async loadPackages(packages) {
4512
5036
  this.logger.info("Loading packages: @packages", packages.join(", "));
4513
5037
  const documentModelsMap = await this.loadDocumentModels(packages);
5038
+ const upgradeManifestsMap = await this.loadUpgradeManifests(packages);
4514
5039
  const subgraphsMap = await this.loadSubgraphs(packages);
4515
5040
  const processorsMap = await this.loadProcessors(packages);
5041
+ this.upgradeManifestsMap = upgradeManifestsMap;
4516
5042
  this.updatePackagesMap(documentModelsMap);
4517
5043
  this.updateSubgraphsMap(subgraphsMap);
4518
5044
  this.updateProcessorsMap(processorsMap);
@@ -4523,6 +5049,7 @@ var PackageManager = class {
4523
5049
  }
4524
5050
  return {
4525
5051
  documentModels: getUniqueDocumentModels(...Array.from(documentModelsMap.values())),
5052
+ upgradeManifests: this.getUniqueUpgradeManifests(),
4526
5053
  subgraphs: subgraphsMap,
4527
5054
  processors: processorsMap
4528
5055
  };
@@ -4533,6 +5060,7 @@ var PackageManager = class {
4533
5060
  documentModelModuleMap.set("document-drive", [driveDocumentModelModule]);
4534
5061
  documentModelModuleMap.set("document-model", [documentModelDocumentModelModule]);
4535
5062
  documentModelModuleMap.set("reactor-drive", [reactorDriveDocumentModelModule]);
5063
+ documentModelModuleMap.set("reactor-group", [ReactorGroupV1]);
4536
5064
  for (const pkg of packages) {
4537
5065
  const allDocumentModels = [];
4538
5066
  const failures = [];
@@ -4555,6 +5083,26 @@ var PackageManager = class {
4555
5083
  }
4556
5084
  return documentModelModuleMap;
4557
5085
  }
5086
+ /** Upgrade manifests currently loaded across all packages, one per type. */
5087
+ getUniqueUpgradeManifests() {
5088
+ return getUniqueUpgradeManifests(...Array.from(this.upgradeManifestsMap.values()));
5089
+ }
5090
+ async loadUpgradeManifests(packages) {
5091
+ const manifestsMap = /* @__PURE__ */ new Map();
5092
+ for (const pkg of packages) for (const loader of this.loaders) {
5093
+ if (!loader.loadUpgradeManifests) continue;
5094
+ try {
5095
+ const manifests = await loader.loadUpgradeManifests(pkg);
5096
+ if (manifests.length > 0) {
5097
+ manifestsMap.set(pkg, manifests);
5098
+ break;
5099
+ }
5100
+ } catch (error) {
5101
+ this.logger.debug(`[${loader.name}] Failed to load upgrade manifests from package @pkg: @error`, pkg, error);
5102
+ }
5103
+ }
5104
+ return manifestsMap;
5105
+ }
4558
5106
  async loadSubgraphs(packages) {
4559
5107
  this.logger.debug(`Loading subgraphs from packages: ${packages.join(", ")}`);
4560
5108
  const subgraphsMap = /* @__PURE__ */ new Map();
@@ -4616,6 +5164,10 @@ var PackageManager = class {
4616
5164
  async updateDocumentModelsForPackage(pkg) {
4617
5165
  this.logger.debug(`Updating document models for package: ${pkg}`);
4618
5166
  const documentModels = await this.loadDocumentModels([pkg]);
5167
+ const upgradeManifests = await this.loadUpgradeManifests([pkg]);
5168
+ const upgradeManifestsMap = new Map(this.upgradeManifestsMap);
5169
+ upgradeManifestsMap.set(pkg, upgradeManifests.get(pkg) ?? []);
5170
+ this.upgradeManifestsMap = upgradeManifestsMap;
4619
5171
  const documentModelsMap = new Map(this.docModelsMap);
4620
5172
  documentModelsMap.set(pkg, documentModels.get(pkg) ?? []);
4621
5173
  this.updatePackagesMap(documentModelsMap);
@@ -4695,6 +5247,70 @@ var PackageManager = class {
4695
5247
  const config = { basePath: process.env.BASE_PATH || "/" };
4696
5248
  const DefaultCoreSubgraphs = [AnalyticsSubgraph, SystemSubgraph];
4697
5249
  //#endregion
5250
+ //#region src/attachment-backend.ts
5251
+ /** Filesystem is the default; explicit S3 is readiness-gated without fallback. */
5252
+ async function createStartupAttachmentBackend({ db, env = process.env, s3Dependencies }) {
5253
+ const storage = parseAttachmentStorageConfig(env);
5254
+ if (storage.kind === "filesystem") return void 0;
5255
+ const backend = createS3AttachmentBackend(db.withSchema("attachments"), storage.s3, s3Dependencies);
5256
+ if (!(await backend.health()).ready) throw new Error("S3 attachment backend readiness check failed");
5257
+ return backend;
5258
+ }
5259
+ //#endregion
5260
+ //#region src/services/attachment-access.service.ts
5261
+ const HASH_PATTERN = /^[a-f0-9]{64}$/;
5262
+ /**
5263
+ * Composes document authorization with the projected document/ref
5264
+ * relationship. Order is fixed: validate ref, resolve the canonical document
5265
+ * id, check `canRead`, then check the reference index. A denied document
5266
+ * never reaches the reference reader, and the facade never touches
5267
+ * attachment metadata, storage backends, or presigners.
5268
+ */
5269
+ var AttachmentAccessService = class {
5270
+ constructor(resolveCanonicalId, authorization, references, projection) {
5271
+ this.resolveCanonicalId = resolveCanonicalId;
5272
+ this.authorization = authorization;
5273
+ this.references = references;
5274
+ this.projection = projection;
5275
+ }
5276
+ async canReadAttachment(request) {
5277
+ if (this.projection.status !== "available") return { kind: "projection-unavailable" };
5278
+ const ref = normalizeAttachmentRef(request.attachmentRef);
5279
+ if (ref === null) return { kind: "denied" };
5280
+ let documentId;
5281
+ try {
5282
+ documentId = await this.resolveCanonicalId(request.documentId);
5283
+ } catch {
5284
+ return { kind: "denied" };
5285
+ }
5286
+ if (!await this.authorization.canRead(documentId, request.userAddress)) return { kind: "denied" };
5287
+ if (!await this.references.hasReference(documentId, ref)) return { kind: "denied" };
5288
+ return {
5289
+ kind: "allowed",
5290
+ documentId,
5291
+ ref
5292
+ };
5293
+ }
5294
+ };
5295
+ /**
5296
+ * Parses and canonicalizes a caller-supplied ref through the shared parser.
5297
+ * Returns null for anything that is not a v1 ref over a 64-char SHA-256 hex
5298
+ * hash; hex case is normalized so index lookups use the canonical form.
5299
+ */
5300
+ function normalizeAttachmentRef(value) {
5301
+ let hash;
5302
+ let version;
5303
+ try {
5304
+ const parsed = parseRef(value);
5305
+ hash = parsed.hash.toLowerCase();
5306
+ version = parsed.version;
5307
+ } catch {
5308
+ return null;
5309
+ }
5310
+ if (version !== 1 || !HASH_PATTERN.test(hash)) return null;
5311
+ return createRef(hash, version);
5312
+ }
5313
+ //#endregion
4698
5314
  //#region src/migrations/001_create_document_permissions.ts
4699
5315
  var _001_create_document_permissions_exports = /* @__PURE__ */ __exportAll({
4700
5316
  down: () => down$2,
@@ -4921,8 +5537,7 @@ var AuthService = class {
4921
5537
  admins: [],
4922
5538
  auth_enabled: false
4923
5539
  };
4924
- const method = request.method;
4925
- if (method === "OPTIONS" || method === "GET") return {
5540
+ if (request.method === "OPTIONS") return {
4926
5541
  user: void 0,
4927
5542
  admins: this.config.admins,
4928
5543
  auth_enabled: true
@@ -4984,16 +5599,23 @@ var AuthService = class {
4984
5599
  return await verifyAuthBearerToken(token);
4985
5600
  }
4986
5601
  /**
4987
- * Extract user information from verification result
5602
+ * Extract user information from verification result.
5603
+ *
5604
+ * The issuer is required along with the credential subject. A credential that
5605
+ * verified must carry one -- resolving it is how the signature was checked --
5606
+ * so this rejects a shape that cannot have come from verification rather than
5607
+ * admitting a keyless principal.
4988
5608
  */
4989
5609
  extractUserFromVerification(verified) {
4990
5610
  try {
4991
5611
  const { address, chainId, networkId } = verified.verifiableCredential.credentialSubject;
4992
- if (!address || !chainId || !networkId) return null;
5612
+ const appKey = verified.issuer;
5613
+ if (!address || !chainId || !networkId || !appKey) return null;
4993
5614
  return {
4994
5615
  address,
4995
5616
  chainId,
4996
- networkId
5617
+ networkId,
5618
+ appKey
4997
5619
  };
4998
5620
  } catch {
4999
5621
  return null;
@@ -5042,14 +5664,33 @@ var AuthService = class {
5042
5664
  }
5043
5665
  }
5044
5666
  async fetchCredentialExists(address, chainId, appId) {
5045
- return await fetchDelegationCredential({
5667
+ if (!this.config.verifyCredential) throw new Error("AuthService: credential verification is enabled but no verifyCredential was provided");
5668
+ return this.config.verifyCredential({
5046
5669
  address,
5047
5670
  chainId,
5048
- appDid: appId
5049
- }) !== void 0;
5671
+ appId
5672
+ });
5050
5673
  }
5051
5674
  };
5052
5675
  //#endregion
5676
+ //#region src/services/renown-credential-verifier.ts
5677
+ async function createRenownCredentialVerifier(config = {}) {
5678
+ const switchboardUrl = await resolveSwitchboardEndpoint({
5679
+ switchboardUrl: config.switchboardUrl,
5680
+ baseUrl: config.renownUrl
5681
+ });
5682
+ return async ({ address, chainId, appId }) => {
5683
+ return await fetchDelegationCredential({
5684
+ address,
5685
+ chainId,
5686
+ appDid: appId,
5687
+ switchboardUrl,
5688
+ baseUrl: switchboardUrl ? void 0 : config.renownUrl,
5689
+ discover: false
5690
+ }) !== void 0;
5691
+ };
5692
+ }
5693
+ //#endregion
5053
5694
  //#region src/services/document-permission.service.ts
5054
5695
  /**
5055
5696
  * Service for managing document-level permissions.
@@ -5365,7 +6006,35 @@ function createMcpRequestAuthorizer(authService, authorizationService) {
5365
6006
  };
5366
6007
  }
5367
6008
  //#endregion
6009
+ //#region src/services/renown-config.ts
6010
+ /** Reading credentials from this reactor's own read model needs a host-supplied
6011
+ * verifier: core has no idea which subgraph serves that read model. */
6012
+ function assertCredentialVerifierForSource(source, hasVerifier) {
6013
+ if (source !== "self" || hasVerifier) return;
6014
+ throw new Error("Renown credential verification is set to \"self\" (auth.renown.source or RENOWN_SOURCE) but no verifyCredential was provided. A host that reads its own read model must pass one — see apps/switchboard, which builds it from @renown/sdk createLocalCredentialVerifier — or set RENOWN_SOURCE=remote to verify against a Renown instance.");
6015
+ }
6016
+ function envOverride(value) {
6017
+ const trimmed = value?.trim();
6018
+ return trimmed ? trimmed : void 0;
6019
+ }
6020
+ /** Resolve `auth.renown`; RENOWN_SOURCE / RENOWN_URL / SWITCHBOARD_URL win
6021
+ * over the config file, which wins over the SDK defaults. */
6022
+ function resolveRenownConfig(configured, env, logger) {
6023
+ return {
6024
+ source: resolveRenownSource(envOverride(env.RENOWN_SOURCE) ?? configured?.source, logger),
6025
+ url: envOverride(env.RENOWN_URL) ?? configured?.url,
6026
+ switchboardUrl: envOverride(env.SWITCHBOARD_URL) ?? configured?.switchboardUrl
6027
+ };
6028
+ }
6029
+ function resolveRenownSource(value, logger) {
6030
+ if (value === void 0 || value === null) return "remote";
6031
+ if (value === "self" || value === "remote") return value;
6032
+ logger.warn(`Ignoring invalid renown source "${value}" (expected "self" or "remote") — using "remote"`);
6033
+ return "remote";
6034
+ }
6035
+ //#endregion
5368
6036
  //#region src/utils/db.ts
6037
+ const PGLITE_UTC_PARSERS = { 1114: (value) => /* @__PURE__ */ new Date(`${value.replace(" ", "T")}Z`) };
5369
6038
  function isPG(connectionString) {
5370
6039
  if (connectionString.startsWith("postgresql://") || connectionString.startsWith("postgres://")) return true;
5371
6040
  return false;
@@ -5396,7 +6065,10 @@ function getDbClient(connectionString = void 0, pgliteFactory) {
5396
6065
  if (cached) return cached;
5397
6066
  const isPg = connectionString && isPG(connectionString);
5398
6067
  const client = isPg ? "pg" : ClientPgLite;
5399
- const pgliteInstance = isPg ? void 0 : pgliteFactory ? pgliteFactory(connectionString) : connectionString ? new PGlite({ fs: new AtomicNodeFs(connectionString) }) : new PGlite();
6068
+ const pgliteInstance = isPg ? void 0 : pgliteFactory ? pgliteFactory(connectionString) : connectionString ? new PGlite({
6069
+ fs: new AtomicNodeFs(connectionString),
6070
+ parsers: PGLITE_UTC_PARSERS
6071
+ }) : new PGlite({ parsers: PGLITE_UTC_PARSERS });
5400
6072
  const connection = isPg ? { connectionString } : { pglite: pgliteInstance };
5401
6073
  if (connectionString && !isPg) {
5402
6074
  const dirPath = path.resolve(connectionString, "..");
@@ -5553,10 +6225,31 @@ function makeDbClosers(knexInstance, pglite) {
5553
6225
  return closers;
5554
6226
  }
5555
6227
  /**
6228
+ * The gate sync serving evaluates a document's own policy through, or undefined
6229
+ * when there is none to evaluate.
6230
+ *
6231
+ * It is built here rather than taken off the reactor client because it is not
6232
+ * the same gate reads use: it carries the host's closes-by-default setting,
6233
+ * which withholds the domain scopes of a document nobody has policied yet. That
6234
+ * answer belongs to serving alone -- replay must keep reading an uninitialized
6235
+ * document in full -- so the two gates are deliberately separate objects over
6236
+ * the same model.
6237
+ *
6238
+ * Undefined below `authEnforcement`, where the registered model ignores the auth
6239
+ * scope: gating through it would serve every domain scope of a policied document
6240
+ * to anyone, which is worse than not gating at all.
6241
+ */
6242
+ function buildSyncServingGate(reactorModule, authorizationConfig, logger) {
6243
+ if (!reactorModule) return;
6244
+ const model = readDecisionModel(reactorModule.featureFlags, reactorModule.documentModelRegistry);
6245
+ if (!model) return;
6246
+ return new SyncScopeGate(new ModelReadGate(model, reactorModule.documentView, reactorModule.featureFlags.authGroups, reactorModule.operationIndex, logger, { withholdUninitialized: authorizationConfig.defaultProtection }), reactorModule.documentView, logger);
6247
+ }
6248
+ /**
5556
6249
  * Sets up the subgraph manager and registers subgraphs
5557
6250
  */
5558
- async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient) {
5559
- const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), authService, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient);
6251
+ async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient, syncServingGate) {
6252
+ const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), authService, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient, syncServingGate);
5560
6253
  await graphqlManager.init(subgraphs.core, authFetchMiddleware);
5561
6254
  for (const [, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql");
5562
6255
  await graphqlManager.updateRouter();
@@ -5568,15 +6261,20 @@ async function setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer,
5568
6261
  function setupEventListeners(pkgManager, graphqlManager, reactorProcessorManager, module, documentModelRegistry) {
5569
6262
  pkgManager.onDocumentModelsChange((packagedModels) => {
5570
6263
  if (documentModelRegistry) {
5571
- const newModules = Object.values(packagedModels).flat();
5572
- const registeredModules = documentModelRegistry.getAllModules();
5573
- const registeredTypes = new Set(registeredModules.map((m) => m.documentModel.global.id));
5574
- const modulesToRegister = newModules.filter((mod) => !registeredTypes.has(mod.documentModel.global.id));
5575
- if (modulesToRegister.length > 0) {
5576
- const results = documentModelRegistry.registerModules(...modulesToRegister);
5577
- for (const result of results) if (result.status === "success") defaultLogger.info(`Registered new document model: ${result.item.documentModel.global.id}`);
6264
+ const newModules = getUniqueDocumentModels(Object.values(packagedModels).flat());
6265
+ const incomingTypes = new Set(newModules.map((m) => m.documentModel.global.id));
6266
+ if (incomingTypes.size > 0) {
6267
+ documentModelRegistry.unregisterModules(...incomingTypes);
6268
+ const results = documentModelRegistry.registerModules(...newModules);
6269
+ for (const result of results) if (result.status === "success") defaultLogger.info(`Registered document model: ${result.item.documentModel.global.id} v${result.item.version ?? 1}`);
5578
6270
  else defaultLogger.error(`Failed to register document model: ${result.error.message}`);
5579
6271
  }
6272
+ const manifests = pkgManager.getUniqueUpgradeManifests();
6273
+ if (manifests.length > 0) {
6274
+ documentModelRegistry.unregisterUpgradeManifests(...manifests.map((m) => m.documentType));
6275
+ const manifestResults = documentModelRegistry.registerUpgradeManifests(...manifests);
6276
+ for (const result of manifestResults) if (result.status === "error") defaultLogger.error(`Failed to register upgrade manifest: ${result.error.message}`);
6277
+ }
5580
6278
  }
5581
6279
  graphqlManager.regenerateDocumentModelSubgraphs();
5582
6280
  });
@@ -5633,10 +6331,12 @@ async function _setupCommonInfrastructure(options) {
5633
6331
  const logger = options.logger ?? defaultLogger;
5634
6332
  let admins = [];
5635
6333
  let authEnabled = false;
6334
+ let configuredRenown;
5636
6335
  if (options.configFile) {
5637
6336
  const config = getConfig(options.configFile);
5638
6337
  admins = config.auth?.admins.map((a) => a.toLowerCase()) ?? [];
5639
6338
  authEnabled = config.auth?.enabled ?? false;
6339
+ configuredRenown = config.auth?.renown;
5640
6340
  } else if (options.auth) {
5641
6341
  admins = options.auth.admins.map((a) => a.toLowerCase());
5642
6342
  authEnabled = options.auth.enabled;
@@ -5673,11 +6373,19 @@ async function _setupCommonInfrastructure(options) {
5673
6373
  let authService;
5674
6374
  if (authEnabled) {
5675
6375
  logger.info("Setting up Auth middleware");
6376
+ const renown = options.renown ?? resolveRenownConfig(configuredRenown, process.env, logger);
6377
+ assertCredentialVerifierForSource(renown.source, options.verifyCredential !== void 0);
6378
+ if (options.verifyCredential) logger.info("Renown credentials will be verified by the host's verifier");
6379
+ else logger.info("Renown credentials will be verified against @url", renown.switchboardUrl ?? renown.url ?? "the default Renown instance");
5676
6380
  authService = new AuthService({
5677
6381
  enabled: authEnabled,
5678
6382
  admins,
5679
6383
  skipCredentialVerification,
5680
- credentialVerificationCacheTtlMs
6384
+ credentialVerificationCacheTtlMs,
6385
+ verifyCredential: options.verifyCredential ?? await createRenownCredentialVerifier({
6386
+ renownUrl: renown.url,
6387
+ switchboardUrl: renown.switchboardUrl
6388
+ })
5681
6389
  });
5682
6390
  authFetchMiddleware = createAuthFetchMiddleware(authService);
5683
6391
  }
@@ -5703,7 +6411,12 @@ async function _setupCommonInfrastructure(options) {
5703
6411
  await mkdir(attachmentStoragePath, { recursive: true });
5704
6412
  const { db: attachmentDb, knex: attachmentKnex, pglite: attachmentPglite } = getDbClient(options.dbPath, options.pgliteFactory);
5705
6413
  dbClosers.push(...makeDbClosers(attachmentKnex, attachmentPglite));
5706
- const attachments = await new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(3600 * 1e3).build();
6414
+ const ATTACHMENT_SWEEP_INTERVAL_MS = 3600 * 1e3;
6415
+ const attachmentBackend = await createStartupAttachmentBackend({ db: attachmentDb });
6416
+ const attachmentBuilder = new AttachmentBuilder(attachmentDb, attachmentStoragePath).withReservationSweepMs(ATTACHMENT_SWEEP_INTERVAL_MS);
6417
+ if (attachmentBackend) attachmentBuilder.withBackend(attachmentBackend);
6418
+ const attachments = await attachmentBuilder.build();
6419
+ const attachmentReferenceIndex = await new AttachmentReferenceIndexBuilder(attachmentDb).build();
5707
6420
  dbClosers.push(() => {
5708
6421
  attachments.destroy();
5709
6422
  return Promise.resolve();
@@ -5723,6 +6436,7 @@ async function _setupCommonInfrastructure(options) {
5723
6436
  documentPermissionService,
5724
6437
  authorizationConfig,
5725
6438
  attachments,
6439
+ attachmentReferenceIndex,
5726
6440
  packages,
5727
6441
  dbClosers,
5728
6442
  readiness
@@ -5731,26 +6445,17 @@ async function _setupCommonInfrastructure(options) {
5731
6445
  /**
5732
6446
  * Private helper function containing common setup logic for API initialization
5733
6447
  */
5734
- async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient) {
6448
+ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, readModels, attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers = [], reactorDriveClient, syncServingGate) {
5735
6449
  const hostModule = {
5736
- relationalDb,
5737
- analyticsStore,
5738
- processorApp,
5739
- config: options.processorConfig,
5740
- client: reactorClient,
5741
- attachments: createAttachmentClient(attachments.service),
5742
- dispatch: { async execute(docId, branch, actions, signal) {
5743
- const jobInfo = await reactorClient.executeAsync(docId, branch, actions, signal);
5744
- return {
5745
- id: jobInfo.id,
5746
- status: jobInfo.status
5747
- };
5748
- } },
5749
- getReadModel(name) {
5750
- const model = readModels.find((m) => m.name === name);
5751
- if (!model) throw new Error(`Read model "${name}" not found`);
5752
- return model;
5753
- }
6450
+ ...createReactorHostModuleBase({
6451
+ client: reactorClient,
6452
+ readModels,
6453
+ relationalDb,
6454
+ analyticsStore,
6455
+ processorApp,
6456
+ config: options.processorConfig
6457
+ }),
6458
+ attachments: createAttachmentClient(attachments.service)
5754
6459
  };
5755
6460
  const mcpServerEnabled = options.mcp ?? true;
5756
6461
  const logger = options.logger ?? defaultLogger;
@@ -5778,6 +6483,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5778
6483
  const { httpServer, wsServer } = await startServer(httpAdapter, port, options.https, logger);
5779
6484
  const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
5780
6485
  logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
6486
+ const attachmentAccess = new AttachmentAccessService(createCanonicalDocumentIdResolver(reactorClient), authorizationService, attachmentReferenceIndex.store, attachmentReferenceProjection);
5781
6487
  const coreSubgraphs = DefaultCoreSubgraphs.slice();
5782
6488
  coreSubgraphs.push(ReactorSubgraph);
5783
6489
  if (documentPermissionService) {
@@ -5787,7 +6493,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5787
6493
  const graphqlManager = await setupGraphQLManager(httpAdapter, authFetchMiddleware, httpServer, wsServer, reactorClient, relationalDb, analyticsStore, syncManager, {
5788
6494
  extended: subgraphs,
5789
6495
  core: coreSubgraphs
5790
- }, logger.child(["graphql-manager"]), authorizationService, authService, documentPermissionService, options.enableDocumentModelSubgraphs, port, reactorDriveClient);
6496
+ }, logger.child(["graphql-manager"]), authorizationService, authService, documentPermissionService, options.enableDocumentModelSubgraphs, port, reactorDriveClient, syncServingGate);
5791
6497
  setupEventListeners(packages, graphqlManager, reactorProcessorManager, hostModule, documentModelRegistry);
5792
6498
  if (mcpServerEnabled) {
5793
6499
  await setupMcpServer({
@@ -5802,6 +6508,8 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
5802
6508
  graphqlManager,
5803
6509
  packages,
5804
6510
  attachments,
6511
+ attachmentReferenceIndex,
6512
+ attachmentAccess,
5805
6513
  authService,
5806
6514
  dispose: buildApiDispose({
5807
6515
  graphqlManager,
@@ -5850,9 +6558,15 @@ function buildApiDispose(args) {
5850
6558
  };
5851
6559
  }
5852
6560
  async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5853
- const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
5854
- const { documentModels, processors, subgraphs } = await packages.init();
5855
- const { module: reactorClientModule, reactorDriveClient } = await clientInitializer(documentModels);
6561
+ const { port, httpAdapter, authFetchMiddleware, authService, relationalDb, analyticsStore, documentPermissionService, authorizationConfig, attachments, attachmentReferenceIndex, packages, dbClosers, readiness } = await _setupCommonInfrastructure(options);
6562
+ const { documentModels, upgradeManifests, processors, subgraphs } = await packages.init();
6563
+ const { module: reactorClientModule, reactorDriveClient, attachmentReferenceProjection = {
6564
+ status: "unavailable",
6565
+ reason: "initializer-did-not-report"
6566
+ } } = await clientInitializer(documentModels, {
6567
+ attachmentReferenceWriter: attachmentReferenceIndex.store,
6568
+ upgradeManifests
6569
+ });
5856
6570
  const reactorClient = reactorClientModule.client;
5857
6571
  const syncManager = reactorClientModule.reactorModule?.syncModule?.syncManager;
5858
6572
  if (!syncManager) throw new Error("SyncManager not available from InProcessReactorClientModule");
@@ -5861,11 +6575,12 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
5861
6575
  const documentModelRegistry = reactorClientModule.reactorModule?.documentModelRegistry;
5862
6576
  if (!documentModelRegistry) throw new Error("DocumentModelRegistry not available from InProcessReactorClientModule");
5863
6577
  return {
5864
- ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient),
6578
+ ...await _setupAPI(reactorClient, syncManager, reactorProcessorManager, httpAdapter, authFetchMiddleware, authService, port, packages, relationalDb, analyticsStore, documentPermissionService, processors, subgraphs, options, processorApp, (reactorClientModule.reactorModule?.readModelCoordinator)?.readModels ?? [], attachments, attachmentReferenceIndex, attachmentReferenceProjection, authorizationConfig, documentModelRegistry, dbClosers, reactorDriveClient, buildSyncServingGate(reactorClientModule.reactorModule, authorizationConfig, options.logger ?? defaultLogger)),
5865
6579
  client: reactorClient,
5866
6580
  syncManager,
5867
6581
  documentModelRegistry,
5868
- readiness
6582
+ readiness,
6583
+ attachmentReferenceProjection
5869
6584
  };
5870
6585
  }
5871
6586
  //#endregion
@@ -5969,7 +6684,7 @@ var PackageManagementService = class {
5969
6684
  }
5970
6685
  };
5971
6686
  //#endregion
5972
- export { ADMIN_USERS, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground };
6687
+ export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthDecision, AuthDecisionSchema, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, EvaluateActionsDocument, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground, resolveRenownConfig };
5973
6688
 
5974
6689
  //# sourceMappingURL=index.mjs.map
5975
- //# debugId=e58cea2c-2319-52a8-90cd-3e99d94ba98f
6690
+ //# debugId=7b24e842-b774-59b1-8a44-51c953533e4c