@powerhousedao/reactor-api 6.2.3-dev.25 → 6.2.3-dev.27
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/{adapter-http-express-Drbaxszc.mjs → adapter-http-express-CWqziB1p.mjs} +5 -5
- package/dist/adapter-http-express-CWqziB1p.mjs.map +1 -0
- package/dist/{adapter-http-fastify-DMPW_huG.mjs → adapter-http-fastify-CYZQ6FFv.mjs} +5 -5
- package/dist/adapter-http-fastify-CYZQ6FFv.mjs.map +1 -0
- package/dist/index.d.mts +34 -32
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +322 -174
- package/dist/index.mjs.map +1 -1
- package/dist/{pieces-BOwV3Ql9.mjs → pieces-DMQotfIR.mjs} +24 -8
- package/dist/pieces-DMQotfIR.mjs.map +1 -0
- package/dist/src/packages/vite-loader.mjs +1 -1
- package/package.json +15 -15
- package/dist/adapter-http-express-Drbaxszc.mjs.map +0 -1
- package/dist/adapter-http-fastify-DMPW_huG.mjs.map +0 -1
- package/dist/pieces-BOwV3Ql9.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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]="
|
|
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]="8ab62893-8127-51eb-9d6f-a5900b1823de")}catch(e){}}();
|
|
3
3
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
4
|
-
import { C as
|
|
4
|
+
import { C as AuthorizedDocumentHandle, D as ForbiddenError, E as AuthenticationRequiredError, O as MalformedStoredOperationError, S as AuthorizationPolicy, T as AuthEvaluationUnsupportedError, _ as loadSubgraphs, a as piecesFromCdnList, b as CanonicalDocumentIdResolutionError, c as buildGraphQlDriveDocument, d as isSubgraphClass, f as debounce, g as loadProcessors, h as loadDocumentModels, i as pieceListLocation, l as buildGraphqlOperation, o as piecesFromListModule, p as extractUpgradeManifests, r as PIECE_SOURCE_LIST, s as buildGraphQlDocument, u as buildGraphqlOperations, v as BaseSubgraph, w as createAuthorizationService, x as createCanonicalDocumentIdResolver, y as callerSubject } from "./pieces-DMQotfIR.mjs";
|
|
5
5
|
import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
|
|
6
6
|
import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
|
|
7
7
|
import { gql } from "graphql-tag";
|
|
8
8
|
import { GraphQLError, Kind, parse, print } from "graphql";
|
|
9
|
-
import { AuthEnforcementDisabledError, DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, ModelReadGate, PropagationMode as PropagationMode$1, SyncScopeGate, consolidateSyncOperations, createReactorHostModuleBase, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, readDecisionModel, syncOperationErrorType } from "@powerhousedao/reactor";
|
|
9
|
+
import { AuthEnforcementDisabledError, BareReadGate, DEFAULT_DRIVE_CONTAINER_TYPES, DriveCollectionId, JobStatus, ModelReadGate, PropagationMode as PropagationMode$1, SyncScopeGate, consolidateSyncOperations, createReactorHostModuleBase, driveIdFromUrl, envelopesToSyncOperations, parseDriveUrl, readDecisionModel, syncOperationErrorType } from "@powerhousedao/reactor";
|
|
10
10
|
import { ConsoleLogger, childLogger, documentModelDocumentModelModule } from "document-model";
|
|
11
11
|
import path, { isAbsolute, join } from "node:path";
|
|
12
12
|
import { match } from "path-to-regexp";
|
|
@@ -49,6 +49,7 @@ import knex from "knex";
|
|
|
49
49
|
import ClientPgLite from "knex-pglite";
|
|
50
50
|
import { KyselyKnexDialect, PGColdDialect } from "kysely-knex";
|
|
51
51
|
//#region src/graphql/analytics-subgraph.ts
|
|
52
|
+
const analyticsResolvers = AnalyticsResolvers;
|
|
52
53
|
var AnalyticsSubgraph = class extends BaseSubgraph {
|
|
53
54
|
analyticsStore;
|
|
54
55
|
analyticsModel;
|
|
@@ -56,13 +57,33 @@ var AnalyticsSubgraph = class extends BaseSubgraph {
|
|
|
56
57
|
typeDefs = gql`
|
|
57
58
|
${typedefs}
|
|
58
59
|
`;
|
|
59
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Every analytics field hangs off `Query.analytics`, so gating it gates them
|
|
62
|
+
* all. Series are aggregates a processor built from many documents and carry
|
|
63
|
+
* no document to decide on, so only a caller with policy-wide read (anyone
|
|
64
|
+
* under OPEN, an admin otherwise) may read them.
|
|
65
|
+
*/
|
|
66
|
+
resolvers = {
|
|
67
|
+
...analyticsResolvers,
|
|
68
|
+
Query: {
|
|
69
|
+
...analyticsResolvers.Query,
|
|
70
|
+
analytics: (parent, args, ctx) => {
|
|
71
|
+
this.#assertCanReadAnalytics(ctx);
|
|
72
|
+
return analyticsResolvers.Query.analytics(parent, args, ctx);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
60
76
|
constructor(args) {
|
|
61
77
|
super(args);
|
|
62
78
|
this.analyticsStore = args.analyticsStore;
|
|
63
79
|
this.analyticsModel = new AnalyticsModel(new AnalyticsQueryEngine(this.analyticsStore));
|
|
64
80
|
args.graphqlManager.setAdditionalContextFields({ dataSources: { db: { Analytics: this.analyticsModel } } });
|
|
65
81
|
}
|
|
82
|
+
#assertCanReadAnalytics(ctx) {
|
|
83
|
+
if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return;
|
|
84
|
+
if (ctx.user?.address) throw new ForbiddenError("to read analytics");
|
|
85
|
+
throw new AuthenticationRequiredError("to read analytics");
|
|
86
|
+
}
|
|
66
87
|
};
|
|
67
88
|
//#endregion
|
|
68
89
|
//#region src/graphql/auth/schema.graphql
|
|
@@ -317,11 +338,11 @@ async function createGatewayAdapter(type, logger) {
|
|
|
317
338
|
async function createHttpAdapter(type) {
|
|
318
339
|
switch (type) {
|
|
319
340
|
case "express": {
|
|
320
|
-
const { createExpressHttpAdapter } = await import("./adapter-http-express-
|
|
341
|
+
const { createExpressHttpAdapter } = await import("./adapter-http-express-CWqziB1p.mjs");
|
|
321
342
|
return createExpressHttpAdapter();
|
|
322
343
|
}
|
|
323
344
|
case "fastify": {
|
|
324
|
-
const { createFastifyHttpAdapter } = await import("./adapter-http-fastify-
|
|
345
|
+
const { createFastifyHttpAdapter } = await import("./adapter-http-fastify-CYZQ6FFv.mjs");
|
|
325
346
|
return createFastifyHttpAdapter();
|
|
326
347
|
}
|
|
327
348
|
}
|
|
@@ -1917,7 +1938,6 @@ function generateNewApiSchema(documentName, specification, _stateSchemaTypes, pr
|
|
|
1917
1938
|
"""
|
|
1918
1939
|
type ${documentName}_DocumentResultPage {
|
|
1919
1940
|
items: [${documentName}MutationResult!]!
|
|
1920
|
-
totalCount: Int!
|
|
1921
1941
|
hasNextPage: Boolean!
|
|
1922
1942
|
hasPreviousPage: Boolean!
|
|
1923
1943
|
cursor: String
|
|
@@ -2243,7 +2263,6 @@ const GetDocumentModelsDocument = gql`
|
|
|
2243
2263
|
version
|
|
2244
2264
|
specification
|
|
2245
2265
|
}
|
|
2246
|
-
totalCount
|
|
2247
2266
|
hasNextPage
|
|
2248
2267
|
hasPreviousPage
|
|
2249
2268
|
cursor
|
|
@@ -2302,7 +2321,6 @@ const GetDocumentWithOperationsDocument = gql`
|
|
|
2302
2321
|
}
|
|
2303
2322
|
}
|
|
2304
2323
|
}
|
|
2305
|
-
totalCount
|
|
2306
2324
|
hasNextPage
|
|
2307
2325
|
hasPreviousPage
|
|
2308
2326
|
cursor
|
|
@@ -2329,7 +2347,6 @@ const GetDocumentOutgoingRelationshipsDocument = gql`
|
|
|
2329
2347
|
items {
|
|
2330
2348
|
...PHDocumentFields
|
|
2331
2349
|
}
|
|
2332
|
-
totalCount
|
|
2333
2350
|
hasNextPage
|
|
2334
2351
|
hasPreviousPage
|
|
2335
2352
|
cursor
|
|
@@ -2353,7 +2370,6 @@ const GetDocumentIncomingRelationshipsDocument = gql`
|
|
|
2353
2370
|
items {
|
|
2354
2371
|
...PHDocumentFields
|
|
2355
2372
|
}
|
|
2356
|
-
totalCount
|
|
2357
2373
|
hasNextPage
|
|
2358
2374
|
hasPreviousPage
|
|
2359
2375
|
cursor
|
|
@@ -2377,7 +2393,6 @@ const GetDocumentOutgoingRelationshipEdgesDocument = gql`
|
|
|
2377
2393
|
items {
|
|
2378
2394
|
...DocumentRelationshipFields
|
|
2379
2395
|
}
|
|
2380
|
-
totalCount
|
|
2381
2396
|
hasNextPage
|
|
2382
2397
|
hasPreviousPage
|
|
2383
2398
|
cursor
|
|
@@ -2401,7 +2416,6 @@ const GetDocumentIncomingRelationshipEdgesDocument = gql`
|
|
|
2401
2416
|
items {
|
|
2402
2417
|
...DocumentRelationshipFields
|
|
2403
2418
|
}
|
|
2404
|
-
totalCount
|
|
2405
2419
|
hasNextPage
|
|
2406
2420
|
hasPreviousPage
|
|
2407
2421
|
cursor
|
|
@@ -2419,7 +2433,6 @@ const FindDocumentsDocument = gql`
|
|
|
2419
2433
|
items {
|
|
2420
2434
|
...PHDocumentFields
|
|
2421
2435
|
}
|
|
2422
|
-
totalCount
|
|
2423
2436
|
hasNextPage
|
|
2424
2437
|
hasPreviousPage
|
|
2425
2438
|
cursor
|
|
@@ -2463,7 +2476,6 @@ const GetDocumentOperationsDocument = gql`
|
|
|
2463
2476
|
}
|
|
2464
2477
|
}
|
|
2465
2478
|
}
|
|
2466
|
-
totalCount
|
|
2467
2479
|
hasNextPage
|
|
2468
2480
|
hasPreviousPage
|
|
2469
2481
|
cursor
|
|
@@ -2902,8 +2914,7 @@ function toDocumentModelResultPage(result) {
|
|
|
2902
2914
|
cursor: result.nextCursor ?? null,
|
|
2903
2915
|
hasNextPage: !!result.nextCursor,
|
|
2904
2916
|
hasPreviousPage: !!result.options.cursor,
|
|
2905
|
-
items: models.map(toGqlDocumentModelState)
|
|
2906
|
-
totalCount: result.results.length
|
|
2917
|
+
items: models.map(toGqlDocumentModelState)
|
|
2907
2918
|
};
|
|
2908
2919
|
}
|
|
2909
2920
|
/**
|
|
@@ -2935,8 +2946,7 @@ function toPhDocumentResultPage(result) {
|
|
|
2935
2946
|
cursor: result.nextCursor ?? null,
|
|
2936
2947
|
hasNextPage: !!result.nextCursor,
|
|
2937
2948
|
hasPreviousPage: !!result.options.cursor,
|
|
2938
|
-
items: result.results.map(toGqlPhDocument)
|
|
2939
|
-
totalCount: result.totalCount ?? result.results.length
|
|
2949
|
+
items: result.results.map(toGqlPhDocument)
|
|
2940
2950
|
};
|
|
2941
2951
|
}
|
|
2942
2952
|
/**
|
|
@@ -2961,8 +2971,7 @@ function toDocumentRelationshipResultPage(result) {
|
|
|
2961
2971
|
cursor: result.nextCursor ?? null,
|
|
2962
2972
|
hasNextPage: !!result.nextCursor,
|
|
2963
2973
|
hasPreviousPage: !!result.options.cursor,
|
|
2964
|
-
items: result.results.map(toGqlDocumentRelationship)
|
|
2965
|
-
totalCount: result.totalCount ?? result.results.length
|
|
2974
|
+
items: result.results.map(toGqlDocumentRelationship)
|
|
2966
2975
|
};
|
|
2967
2976
|
}
|
|
2968
2977
|
/**
|
|
@@ -3231,8 +3240,7 @@ function toOperationResultPage(result) {
|
|
|
3231
3240
|
cursor: result.nextCursor ?? null,
|
|
3232
3241
|
hasNextPage: !!result.nextCursor,
|
|
3233
3242
|
hasPreviousPage: !!result.options.cursor && result.options.cursor !== "0",
|
|
3234
|
-
items: result.results.map(serializeOperationForGraphQL)
|
|
3235
|
-
totalCount: result.results.length
|
|
3243
|
+
items: result.results.map(serializeOperationForGraphQL)
|
|
3236
3244
|
};
|
|
3237
3245
|
}
|
|
3238
3246
|
function toGqlDocumentChangeEvent(event) {
|
|
@@ -3304,6 +3312,21 @@ function pickDriveClient(reactorClient, driveContainerType, reactorDriveClient)
|
|
|
3304
3312
|
}
|
|
3305
3313
|
return reactorClient.drives;
|
|
3306
3314
|
}
|
|
3315
|
+
/**
|
|
3316
|
+
* A mutation's document re-read as the caller, since the client reads it back
|
|
3317
|
+
* as its signer. A caller who may write but not read gets it stripped.
|
|
3318
|
+
*/
|
|
3319
|
+
async function readAsCaller(reactorClient, document, subject, branch) {
|
|
3320
|
+
if (!subject) return document;
|
|
3321
|
+
try {
|
|
3322
|
+
return await reactorClient.get(document.header.id, {
|
|
3323
|
+
subject,
|
|
3324
|
+
branch
|
|
3325
|
+
});
|
|
3326
|
+
} catch (error) {
|
|
3327
|
+
throw new GraphQLError(`Failed to read the document back: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3307
3330
|
async function documentModels(reactorClient, args) {
|
|
3308
3331
|
const namespace = fromInputMaybe(args.namespace);
|
|
3309
3332
|
let paging;
|
|
@@ -3491,13 +3514,42 @@ async function findDocuments(reactorClient, args, subject) {
|
|
|
3491
3514
|
throw new GraphQLError(`Failed to convert documents to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3492
3515
|
}
|
|
3493
3516
|
}
|
|
3494
|
-
|
|
3517
|
+
/**
|
|
3518
|
+
* The answer for a job the caller may not see, shaped as the reactor answers
|
|
3519
|
+
* an unknown one so that the two cannot be told apart.
|
|
3520
|
+
*/
|
|
3521
|
+
function unknownJob(jobId) {
|
|
3522
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3523
|
+
return {
|
|
3524
|
+
id: jobId,
|
|
3525
|
+
documentId: "",
|
|
3526
|
+
status: JobStatus.FAILED,
|
|
3527
|
+
createdAtUtcIso: now,
|
|
3528
|
+
completedAtUtcIso: now,
|
|
3529
|
+
error: {
|
|
3530
|
+
name: "Error",
|
|
3531
|
+
message: "Job not found",
|
|
3532
|
+
stack: ""
|
|
3533
|
+
},
|
|
3534
|
+
consistencyToken: {
|
|
3535
|
+
version: 1,
|
|
3536
|
+
createdAtUtcIso: now,
|
|
3537
|
+
coordinates: []
|
|
3538
|
+
},
|
|
3539
|
+
meta: {
|
|
3540
|
+
batchId: jobId,
|
|
3541
|
+
batchJobIds: [jobId]
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
async function jobStatus(reactorClient, args, serves) {
|
|
3495
3546
|
let result;
|
|
3496
3547
|
try {
|
|
3497
3548
|
result = await reactorClient.getJobStatus(args.jobId);
|
|
3498
3549
|
} catch (error) {
|
|
3499
3550
|
throw new GraphQLError(`Failed to fetch job status: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3500
3551
|
}
|
|
3552
|
+
if (!await serves(result.documentId)) result = unknownJob(args.jobId);
|
|
3501
3553
|
try {
|
|
3502
3554
|
return toGqlJobInfo(result);
|
|
3503
3555
|
} catch (error) {
|
|
@@ -3580,7 +3632,7 @@ async function evaluateActions(reactorClient, args, subject) {
|
|
|
3580
3632
|
anyDenied: result.anyDenied
|
|
3581
3633
|
};
|
|
3582
3634
|
}
|
|
3583
|
-
async function createDocument(reactorClient, args, reactorDriveClient) {
|
|
3635
|
+
async function createDocument(reactorClient, args, reactorDriveClient, subject) {
|
|
3584
3636
|
if (!args.document || typeof args.document !== "object") throw new GraphQLError("Invalid document: must be an object");
|
|
3585
3637
|
const document = args.document;
|
|
3586
3638
|
if (!document.header || typeof document.header !== "object") throw new GraphQLError("Invalid document: missing or invalid header");
|
|
@@ -3588,26 +3640,27 @@ async function createDocument(reactorClient, args, reactorDriveClient) {
|
|
|
3588
3640
|
let result;
|
|
3589
3641
|
try {
|
|
3590
3642
|
if (parentIdentifier) {
|
|
3591
|
-
const parent = await reactorClient.get(parentIdentifier);
|
|
3643
|
+
const parent = await reactorClient.get(parentIdentifier, subject && { subject });
|
|
3592
3644
|
if (isDriveContainerType(parent.header.documentType)) result = await pickDriveClient(reactorClient, parent.header.documentType, reactorDriveClient).addFile(parentIdentifier, document);
|
|
3593
3645
|
else result = await reactorClient.create(document, parentIdentifier);
|
|
3594
3646
|
} else result = await reactorClient.create(document);
|
|
3595
3647
|
} catch (error) {
|
|
3596
3648
|
throw new GraphQLError(`Failed to create document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3597
3649
|
}
|
|
3650
|
+
result = await readAsCaller(reactorClient, result, subject);
|
|
3598
3651
|
try {
|
|
3599
3652
|
return toGqlPhDocument(result);
|
|
3600
3653
|
} catch (error) {
|
|
3601
3654
|
throw new GraphQLError(`Failed to convert created document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3602
3655
|
}
|
|
3603
3656
|
}
|
|
3604
|
-
async function createEmptyDocument(reactorClient, args, reactorDriveClient) {
|
|
3657
|
+
async function createEmptyDocument(reactorClient, args, reactorDriveClient, subject) {
|
|
3605
3658
|
const parentIdentifier = fromInputMaybe(args.parentIdentifier);
|
|
3606
3659
|
const name = fromInputMaybe(args.name);
|
|
3607
3660
|
let result;
|
|
3608
3661
|
try {
|
|
3609
3662
|
if (parentIdentifier) {
|
|
3610
|
-
const parent = await reactorClient.get(parentIdentifier);
|
|
3663
|
+
const parent = await reactorClient.get(parentIdentifier, subject && { subject });
|
|
3611
3664
|
if (isDriveContainerType(parent.header.documentType)) {
|
|
3612
3665
|
const module = await reactorClient.getDocumentModelModule(args.documentType);
|
|
3613
3666
|
const document = withSignaturePolicy(module.utils.createDocument(), await reactorClient.getCreateSignaturePolicy());
|
|
@@ -3618,13 +3671,14 @@ async function createEmptyDocument(reactorClient, args, reactorDriveClient) {
|
|
|
3618
3671
|
} catch (error) {
|
|
3619
3672
|
throw new GraphQLError(`Failed to create empty document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3620
3673
|
}
|
|
3674
|
+
result = await readAsCaller(reactorClient, result, subject);
|
|
3621
3675
|
try {
|
|
3622
3676
|
return toGqlPhDocument(result);
|
|
3623
3677
|
} catch (error) {
|
|
3624
3678
|
throw new GraphQLError(`Failed to convert created document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3625
3679
|
}
|
|
3626
3680
|
}
|
|
3627
|
-
async function createDocumentWithInitialState(reactorClient, args, reactorDriveClient) {
|
|
3681
|
+
async function createDocumentWithInitialState(reactorClient, args, reactorDriveClient, subject) {
|
|
3628
3682
|
const parentIdentifier = fromInputMaybe(args.parentIdentifier);
|
|
3629
3683
|
const name = fromInputMaybe(args.name);
|
|
3630
3684
|
const slug = fromInputMaybe(args.slug);
|
|
@@ -3652,7 +3706,7 @@ async function createDocumentWithInitialState(reactorClient, args, reactorDriveC
|
|
|
3652
3706
|
if (parentIdentifier) {
|
|
3653
3707
|
let parent;
|
|
3654
3708
|
try {
|
|
3655
|
-
parent = await reactorClient.get(parentIdentifier);
|
|
3709
|
+
parent = await reactorClient.get(parentIdentifier, subject && { subject });
|
|
3656
3710
|
} catch (error) {
|
|
3657
3711
|
throw new GraphQLError(`Parent document not found: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3658
3712
|
}
|
|
@@ -3673,6 +3727,7 @@ async function createDocumentWithInitialState(reactorClient, args, reactorDriveC
|
|
|
3673
3727
|
} catch (error) {
|
|
3674
3728
|
throw new GraphQLError(`Failed to create document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3675
3729
|
}
|
|
3730
|
+
result = await readAsCaller(reactorClient, result, subject);
|
|
3676
3731
|
try {
|
|
3677
3732
|
return toGqlPhDocument(result);
|
|
3678
3733
|
} catch (error) {
|
|
@@ -3689,12 +3744,12 @@ const DEFAULT_BRANCH = "main";
|
|
|
3689
3744
|
* the schema cannot express: a signature travels as one string, and verification
|
|
3690
3745
|
* reads it as the tuple it was.
|
|
3691
3746
|
*/
|
|
3692
|
-
async function execute(reactorClient, args) {
|
|
3747
|
+
async function execute(reactorClient, args, subject) {
|
|
3693
3748
|
const actions = toSubmittableActions(args.actions);
|
|
3694
3749
|
const branch = fromInputMaybe(args.branch) ?? DEFAULT_BRANCH;
|
|
3695
3750
|
let result;
|
|
3696
3751
|
try {
|
|
3697
|
-
result = await reactorClient.execute(args.documentIdentifier, branch, actions);
|
|
3752
|
+
result = await reactorClient.execute(args.documentIdentifier, branch, actions, void 0, subject);
|
|
3698
3753
|
} catch (error) {
|
|
3699
3754
|
throw new GraphQLError(`Failed to execute actions: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3700
3755
|
}
|
|
@@ -3722,7 +3777,7 @@ async function executeAsync(reactorClient, args) {
|
|
|
3722
3777
|
}
|
|
3723
3778
|
return toGqlJobInfo(job);
|
|
3724
3779
|
}
|
|
3725
|
-
async function mutateDocument(reactorClient, args) {
|
|
3780
|
+
async function mutateDocument(reactorClient, args, subject) {
|
|
3726
3781
|
let validatedActions;
|
|
3727
3782
|
try {
|
|
3728
3783
|
validatedActions = validateActions(args.actions);
|
|
@@ -3733,7 +3788,7 @@ async function mutateDocument(reactorClient, args) {
|
|
|
3733
3788
|
const branch = args.view?.branch ?? "main";
|
|
3734
3789
|
let result;
|
|
3735
3790
|
try {
|
|
3736
|
-
result = await reactorClient.execute(args.documentIdentifier, branch, validatedActions);
|
|
3791
|
+
result = await reactorClient.execute(args.documentIdentifier, branch, validatedActions, void 0, subject);
|
|
3737
3792
|
} catch (error) {
|
|
3738
3793
|
throw new GraphQLError(`Failed to mutate document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3739
3794
|
}
|
|
@@ -3760,7 +3815,7 @@ async function mutateDocumentAsync(reactorClient, args) {
|
|
|
3760
3815
|
}
|
|
3761
3816
|
return result.id;
|
|
3762
3817
|
}
|
|
3763
|
-
async function renameDocument(reactorClient, args, signal) {
|
|
3818
|
+
async function renameDocument(reactorClient, args, signal, subject) {
|
|
3764
3819
|
const branch = fromInputMaybe(args.branch);
|
|
3765
3820
|
let result;
|
|
3766
3821
|
try {
|
|
@@ -3768,13 +3823,14 @@ async function renameDocument(reactorClient, args, signal) {
|
|
|
3768
3823
|
} catch (error) {
|
|
3769
3824
|
throw new GraphQLError(`Failed to rename document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3770
3825
|
}
|
|
3826
|
+
result = await readAsCaller(reactorClient, result, subject, branch);
|
|
3771
3827
|
try {
|
|
3772
3828
|
return toGqlPhDocument(result);
|
|
3773
3829
|
} catch (error) {
|
|
3774
3830
|
throw new GraphQLError(`Failed to convert renamed document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3775
3831
|
}
|
|
3776
3832
|
}
|
|
3777
|
-
async function setPreferredEditor(reactorClient, args, signal) {
|
|
3833
|
+
async function setPreferredEditor(reactorClient, args, signal, subject) {
|
|
3778
3834
|
const branch = fromInputMaybe(args.branch);
|
|
3779
3835
|
const preferredEditor = fromInputMaybe(args.preferredEditor) ?? null;
|
|
3780
3836
|
let result;
|
|
@@ -3783,13 +3839,14 @@ async function setPreferredEditor(reactorClient, args, signal) {
|
|
|
3783
3839
|
} catch (error) {
|
|
3784
3840
|
throw new GraphQLError(`Failed to set preferred editor: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3785
3841
|
}
|
|
3842
|
+
result = await readAsCaller(reactorClient, result, subject, branch);
|
|
3786
3843
|
try {
|
|
3787
3844
|
return toGqlPhDocument(result);
|
|
3788
3845
|
} catch (error) {
|
|
3789
3846
|
throw new GraphQLError(`Failed to convert updated document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3790
3847
|
}
|
|
3791
3848
|
}
|
|
3792
|
-
async function addRelationship(reactorClient, args) {
|
|
3849
|
+
async function addRelationship(reactorClient, args, subject) {
|
|
3793
3850
|
const branch = fromInputMaybe(args.branch);
|
|
3794
3851
|
const metadata = fromInputMaybe(args.metadata);
|
|
3795
3852
|
let result;
|
|
@@ -3798,13 +3855,14 @@ async function addRelationship(reactorClient, args) {
|
|
|
3798
3855
|
} catch (error) {
|
|
3799
3856
|
throw new GraphQLError(`Failed to add relationship: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3800
3857
|
}
|
|
3858
|
+
result = await readAsCaller(reactorClient, result, subject, branch);
|
|
3801
3859
|
try {
|
|
3802
3860
|
return toGqlPhDocument(result);
|
|
3803
3861
|
} catch (error) {
|
|
3804
3862
|
throw new GraphQLError(`Failed to convert document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3805
3863
|
}
|
|
3806
3864
|
}
|
|
3807
|
-
async function updateRelationship(reactorClient, args) {
|
|
3865
|
+
async function updateRelationship(reactorClient, args, subject) {
|
|
3808
3866
|
const branch = fromInputMaybe(args.branch);
|
|
3809
3867
|
const metadata = fromInputMaybe(args.metadata) ?? null;
|
|
3810
3868
|
let result;
|
|
@@ -3813,13 +3871,14 @@ async function updateRelationship(reactorClient, args) {
|
|
|
3813
3871
|
} catch (error) {
|
|
3814
3872
|
throw new GraphQLError(`Failed to update relationship: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3815
3873
|
}
|
|
3874
|
+
result = await readAsCaller(reactorClient, result, subject, branch);
|
|
3816
3875
|
try {
|
|
3817
3876
|
return toGqlPhDocument(result);
|
|
3818
3877
|
} catch (error) {
|
|
3819
3878
|
throw new GraphQLError(`Failed to convert document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3820
3879
|
}
|
|
3821
3880
|
}
|
|
3822
|
-
async function removeRelationship(reactorClient, args) {
|
|
3881
|
+
async function removeRelationship(reactorClient, args, subject) {
|
|
3823
3882
|
const branch = fromInputMaybe(args.branch);
|
|
3824
3883
|
let result;
|
|
3825
3884
|
try {
|
|
@@ -3827,13 +3886,14 @@ async function removeRelationship(reactorClient, args) {
|
|
|
3827
3886
|
} catch (error) {
|
|
3828
3887
|
throw new GraphQLError(`Failed to remove relationship: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3829
3888
|
}
|
|
3889
|
+
result = await readAsCaller(reactorClient, result, subject, branch);
|
|
3830
3890
|
try {
|
|
3831
3891
|
return toGqlPhDocument(result);
|
|
3832
3892
|
} catch (error) {
|
|
3833
3893
|
throw new GraphQLError(`Failed to convert document to GraphQL: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3834
3894
|
}
|
|
3835
3895
|
}
|
|
3836
|
-
async function moveRelationship(reactorClient, args) {
|
|
3896
|
+
async function moveRelationship(reactorClient, args, subject) {
|
|
3837
3897
|
const branch = fromInputMaybe(args.branch);
|
|
3838
3898
|
let result;
|
|
3839
3899
|
try {
|
|
@@ -3841,6 +3901,10 @@ async function moveRelationship(reactorClient, args) {
|
|
|
3841
3901
|
} catch (error) {
|
|
3842
3902
|
throw new GraphQLError(`Failed to move relationship: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3843
3903
|
}
|
|
3904
|
+
result = {
|
|
3905
|
+
source: await readAsCaller(reactorClient, result.source, subject, branch),
|
|
3906
|
+
target: await readAsCaller(reactorClient, result.target, subject, branch)
|
|
3907
|
+
};
|
|
3844
3908
|
try {
|
|
3845
3909
|
return {
|
|
3846
3910
|
source: toGqlPhDocument(result.source),
|
|
@@ -4130,6 +4194,16 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4130
4194
|
const documentName = getDocumentModelSchemaName(this.documentModel.documentModel.global);
|
|
4131
4195
|
return this.resolvers[`${documentName}Mutations`];
|
|
4132
4196
|
}
|
|
4197
|
+
/** Drops the items the host's own ACL refuses the caller. */
|
|
4198
|
+
async #readableItems(page, ctx) {
|
|
4199
|
+
if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return page;
|
|
4200
|
+
const items = [];
|
|
4201
|
+
for (const item of page.items) if (await this.canReadDocument(item.id, ctx)) items.push(item);
|
|
4202
|
+
return {
|
|
4203
|
+
...page,
|
|
4204
|
+
items
|
|
4205
|
+
};
|
|
4206
|
+
}
|
|
4133
4207
|
/**
|
|
4134
4208
|
* Generate __resolveType functions for union types found in the document model schema.
|
|
4135
4209
|
* Parses the state schema to find union definitions and their member types,
|
|
@@ -4190,7 +4264,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4190
4264
|
const result = await document(this.reactorClient, {
|
|
4191
4265
|
identifier,
|
|
4192
4266
|
view
|
|
4193
|
-
});
|
|
4267
|
+
}, this.viewSubject(ctx));
|
|
4194
4268
|
if (result.document.documentType !== documentType) throw new GraphQLError(`Document with id ${identifier} is not of type ${documentType}`);
|
|
4195
4269
|
await this.assertCanReadCanonical(result.document.id, ctx);
|
|
4196
4270
|
return result;
|
|
@@ -4200,17 +4274,8 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4200
4274
|
const result = await findDocuments(this.reactorClient, {
|
|
4201
4275
|
search: { type: documentType },
|
|
4202
4276
|
paging
|
|
4203
|
-
});
|
|
4204
|
-
|
|
4205
|
-
const filteredItems = [];
|
|
4206
|
-
for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
|
|
4207
|
-
return {
|
|
4208
|
-
...result,
|
|
4209
|
-
items: filteredItems,
|
|
4210
|
-
totalCount: filteredItems.length
|
|
4211
|
-
};
|
|
4212
|
-
}
|
|
4213
|
-
return result;
|
|
4277
|
+
}, this.viewSubject(ctx));
|
|
4278
|
+
return this.#readableItems(result, ctx);
|
|
4214
4279
|
},
|
|
4215
4280
|
findDocuments: async (_, args, ctx) => {
|
|
4216
4281
|
const { search, view, paging } = args;
|
|
@@ -4221,17 +4286,8 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4221
4286
|
},
|
|
4222
4287
|
view,
|
|
4223
4288
|
paging
|
|
4224
|
-
});
|
|
4225
|
-
|
|
4226
|
-
const filteredItems = [];
|
|
4227
|
-
for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
|
|
4228
|
-
return {
|
|
4229
|
-
...result,
|
|
4230
|
-
items: filteredItems,
|
|
4231
|
-
totalCount: filteredItems.length
|
|
4232
|
-
};
|
|
4233
|
-
}
|
|
4234
|
-
return result;
|
|
4289
|
+
}, this.viewSubject(ctx));
|
|
4290
|
+
return this.#readableItems(result, ctx);
|
|
4235
4291
|
},
|
|
4236
4292
|
documentOutgoingRelationships: async (_, args, ctx) => {
|
|
4237
4293
|
const { relationshipType, view, paging } = args;
|
|
@@ -4241,23 +4297,24 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4241
4297
|
relationshipType,
|
|
4242
4298
|
view,
|
|
4243
4299
|
paging
|
|
4244
|
-
});
|
|
4245
|
-
const
|
|
4300
|
+
}, this.viewSubject(ctx));
|
|
4301
|
+
const readable = await this.#readableItems(result, ctx);
|
|
4302
|
+
const filteredItems = readable.items.filter((item) => item.documentType === documentType);
|
|
4246
4303
|
return {
|
|
4247
|
-
...
|
|
4248
|
-
items: filteredItems
|
|
4249
|
-
totalCount: filteredItems.length
|
|
4304
|
+
...readable,
|
|
4305
|
+
items: filteredItems
|
|
4250
4306
|
};
|
|
4251
4307
|
},
|
|
4252
4308
|
documentIncomingRelationships: async (_, args, ctx) => {
|
|
4253
4309
|
const { relationshipType, view, paging } = args;
|
|
4254
4310
|
const handle = await this.assertCanRead(args.targetIdentifier, ctx);
|
|
4255
|
-
|
|
4311
|
+
const result = await documentIncomingRelationships(this.reactorClient, {
|
|
4256
4312
|
targetIdentifier: handle.fetchIdentifier,
|
|
4257
4313
|
relationshipType,
|
|
4258
4314
|
view,
|
|
4259
4315
|
paging
|
|
4260
|
-
});
|
|
4316
|
+
}, this.viewSubject(ctx));
|
|
4317
|
+
return this.#readableItems(result, ctx);
|
|
4261
4318
|
}
|
|
4262
4319
|
},
|
|
4263
4320
|
Mutation: { [documentName]: () => ({}) },
|
|
@@ -4275,14 +4332,14 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4275
4332
|
slug,
|
|
4276
4333
|
preferredEditor,
|
|
4277
4334
|
initialState: initialState ?? {}
|
|
4278
|
-
}, this.graphqlManager.reactorDriveClient);
|
|
4335
|
+
}, this.graphqlManager.reactorDriveClient, this.viewSubject(ctx));
|
|
4279
4336
|
else createdDoc = await createEmptyDocument(this.reactorClient, {
|
|
4280
4337
|
documentType,
|
|
4281
4338
|
parentIdentifier,
|
|
4282
4339
|
name
|
|
4283
|
-
}, this.graphqlManager.reactorDriveClient);
|
|
4340
|
+
}, this.graphqlManager.reactorDriveClient, this.viewSubject(ctx));
|
|
4284
4341
|
if (ctx.user?.address && createdDoc?.id) await this.documentPermissionService?.initializeDocumentProtection(createdDoc.id, ctx.user.address, this.authorizationService.config.defaultProtection);
|
|
4285
|
-
if (!initialState && !preferredEditor && name && createdDoc.name !== name) return toGqlPhDocument(await this.reactorClient.execute(createdDoc.id, "main", [setName(name)]));
|
|
4342
|
+
if (!initialState && !preferredEditor && name && createdDoc.name !== name) return toGqlPhDocument(await this.reactorClient.execute(createdDoc.id, "main", [setName(name)], void 0, this.viewSubject(ctx)));
|
|
4286
4343
|
return createdDoc;
|
|
4287
4344
|
},
|
|
4288
4345
|
createEmptyDocument: async (_, args, ctx) => {
|
|
@@ -4292,7 +4349,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4292
4349
|
const result = await createEmptyDocument(this.reactorClient, {
|
|
4293
4350
|
documentType,
|
|
4294
4351
|
parentIdentifier
|
|
4295
|
-
}, this.graphqlManager.reactorDriveClient);
|
|
4352
|
+
}, this.graphqlManager.reactorDriveClient, this.viewSubject(ctx));
|
|
4296
4353
|
if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
|
|
4297
4354
|
return result;
|
|
4298
4355
|
},
|
|
@@ -4300,11 +4357,11 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4300
4357
|
mutations[camelCase(op.name)] = async (_, args, ctx) => {
|
|
4301
4358
|
const { docId, input } = args;
|
|
4302
4359
|
const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
|
|
4303
|
-
if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
4360
|
+
if ((await this.reactorClient.get(effectiveDocId, { subject: this.viewSubject(ctx) })).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
4304
4361
|
const action = this.documentModel.actions[camelCase(op.name)];
|
|
4305
4362
|
if (!action) throw new GraphQLError(`Action ${op.name} not found`);
|
|
4306
4363
|
try {
|
|
4307
|
-
return toGqlPhDocument(await this.reactorClient.execute(effectiveDocId, "main", [action(input)]));
|
|
4364
|
+
return toGqlPhDocument(await this.reactorClient.execute(effectiveDocId, "main", [action(input)], void 0, this.viewSubject(ctx)));
|
|
4308
4365
|
} catch (error) {
|
|
4309
4366
|
throw new GraphQLError(error instanceof Error ? error.message : `Failed to ${op.name}`);
|
|
4310
4367
|
}
|
|
@@ -4312,7 +4369,7 @@ var DocumentModelSubgraph = class extends BaseSubgraph {
|
|
|
4312
4369
|
mutations[`${camelCase(op.name)}Async`] = async (_, args, ctx) => {
|
|
4313
4370
|
const { docId, input } = args;
|
|
4314
4371
|
const effectiveDocId = (await this.assertCanExecuteOperation(docId, op.name, ctx)).fetchIdentifier;
|
|
4315
|
-
if ((await this.reactorClient.get(effectiveDocId)).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
4372
|
+
if ((await this.reactorClient.get(effectiveDocId, { subject: this.viewSubject(ctx) })).header.documentType !== documentType) throw new GraphQLError(`Document with id ${docId} is not of type ${documentType}`);
|
|
4316
4373
|
const action = this.documentModel.actions[camelCase(op.name)];
|
|
4317
4374
|
if (!action) throw new GraphQLError(`Action ${op.name} not found`);
|
|
4318
4375
|
try {
|
|
@@ -4616,19 +4673,21 @@ var GraphQLManager = class {
|
|
|
4616
4673
|
const matched = driveMatcher(url.pathname);
|
|
4617
4674
|
const driveIdOrSlug = matched ? matched.params.drive : void 0;
|
|
4618
4675
|
if (!driveIdOrSlug) return Response.json({ error: "Drive ID or slug is required" }, { status: 400 });
|
|
4676
|
+
const user = getAuthContext(request)?.user;
|
|
4619
4677
|
try {
|
|
4620
|
-
const driveDoc = await this.reactorClient.get(driveIdOrSlug);
|
|
4621
|
-
if (!await this.authorizationService.canRead(driveDoc.header.id,
|
|
4678
|
+
const driveDoc = await this.reactorClient.get(driveIdOrSlug, { subject: callerSubject(user) });
|
|
4679
|
+
if (!await this.authorizationService.canRead(driveDoc.header.id, user?.address)) {
|
|
4622
4680
|
this.logger.debug(`Drive read refused: ${driveIdOrSlug}`);
|
|
4623
4681
|
return Response.json({ error: "Drive not found" }, { status: 404 });
|
|
4624
4682
|
}
|
|
4625
4683
|
const graphqlEndpoint = `${(request.headers.get("x-forwarded-proto")?.split(",")[0].trim() ?? url.protocol.replace(":", "")) + ":"}//${request.headers.get("x-forwarded-host")?.split(",")[0].trim() ?? request.headers.get("host") ?? ""}${(request.headers.get("x-forwarded-prefix")?.split(",")[0].trim().replace(/\/$/, "") ?? "") + (this.path === "/" ? "" : this.path)}/graphql/r`;
|
|
4684
|
+
const global = driveDoc.state.global;
|
|
4626
4685
|
return Response.json({
|
|
4627
4686
|
id: driveDoc.header.id,
|
|
4628
4687
|
slug: driveDoc.header.slug,
|
|
4629
4688
|
meta: driveDoc.header.meta,
|
|
4630
|
-
name:
|
|
4631
|
-
icon:
|
|
4689
|
+
name: global?.name || driveDoc.header.name,
|
|
4690
|
+
icon: global?.icon ?? void 0,
|
|
4632
4691
|
...graphqlEndpoint && { graphqlEndpoint }
|
|
4633
4692
|
});
|
|
4634
4693
|
} catch (error) {
|
|
@@ -5412,7 +5471,6 @@ const DocumentModelStateDTO = z.object({
|
|
|
5412
5471
|
}).strip();
|
|
5413
5472
|
const DocumentModelResultPageDTO = z.object({
|
|
5414
5473
|
items: z.array(DocumentModelStateDTO),
|
|
5415
|
-
totalCount: z.number().int(),
|
|
5416
5474
|
hasNextPage: z.boolean(),
|
|
5417
5475
|
hasPreviousPage: z.boolean(),
|
|
5418
5476
|
cursor: z.string().nullable().optional()
|
|
@@ -5433,7 +5491,6 @@ const PHDocumentDTO = z.object({
|
|
|
5433
5491
|
}).strip();
|
|
5434
5492
|
const PHDocumentResultPageDTO = z.object({
|
|
5435
5493
|
items: z.array(PHDocumentDTO),
|
|
5436
|
-
totalCount: z.number().int(),
|
|
5437
5494
|
hasNextPage: z.boolean(),
|
|
5438
5495
|
hasPreviousPage: z.boolean(),
|
|
5439
5496
|
cursor: z.string().nullable().optional()
|
|
@@ -5448,7 +5505,6 @@ const DocumentRelationshipDTO = z.object({
|
|
|
5448
5505
|
}).strip();
|
|
5449
5506
|
const DocumentRelationshipResultPageDTO = z.object({
|
|
5450
5507
|
items: z.array(DocumentRelationshipDTO),
|
|
5451
|
-
totalCount: z.number().int(),
|
|
5452
5508
|
hasNextPage: z.boolean(),
|
|
5453
5509
|
hasPreviousPage: z.boolean(),
|
|
5454
5510
|
cursor: z.string().nullable().optional()
|
|
@@ -5503,7 +5559,6 @@ const OperationDTO = z.object({
|
|
|
5503
5559
|
}).strip();
|
|
5504
5560
|
const OperationResultPageDTO = z.object({
|
|
5505
5561
|
items: z.array(OperationDTO),
|
|
5506
|
-
totalCount: z.number().int(),
|
|
5507
5562
|
hasNextPage: z.boolean(),
|
|
5508
5563
|
hasPreviousPage: z.boolean(),
|
|
5509
5564
|
cursor: z.string().nullable().optional()
|
|
@@ -5639,7 +5694,7 @@ function createReactorGraphQLClient(url, fetchImpl = fetch, headers) {
|
|
|
5639
5694
|
}
|
|
5640
5695
|
//#endregion
|
|
5641
5696
|
//#region src/graphql/reactor/schema.graphql
|
|
5642
|
-
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\n# A directed edge between two documents, with the metadata recorded against it.\ntype DocumentRelationship {\n sourceId: String!\n targetId: String!\n relationshipType: String!\n metadata: JSONObject\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\ntype DocumentRelationshipResultPage {\n items: [DocumentRelationship!]!\n 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\n \"\"\"\n What the job produced, once it has produced anything. Nullable for the same\n reason as `JobInfo.result`: a job that has not written yet has produced\n nothing, and an empty object would claim otherwise.\n \"\"\"\n result: JSONObject\n\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n\"\"\"\nOne operation an authorization preflight predicts a verdict for.\n\n`input` is what a conditional grant reads, so a candidate standing for a\nfilled-in form carries that form's input. Omitting it predicts the verdict an\nempty input earns, not the verdict the filled-in form will get.\n\"\"\"\ninput ActionCandidateInput {\n scope: String!\n type: String!\n input: JSONObject\n}\n\nenum AuthDecision {\n ALLOW\n DENY\n}\n\n\"\"\"\nOne candidate's predicted verdict. `reason` carries the refusal a DENY is\nrecorded with, and is null on an ALLOW.\n\"\"\"\ntype ActionEvaluation {\n decision: AuthDecision!\n reason: String\n}\n\n\"\"\"\nThe predicted verdicts for a set of candidates, in the order they were given,\nwith the aggregates a caller branches on.\n\nThe aggregates are redundant -- a verdict is binary -- and all four are returned\nso a caller reads the one its question is phrased in rather than negating\nanother. Over no candidates every aggregate is false: nothing is allowed and\nnothing is denied.\n\"\"\"\ntype ActionEvaluations {\n evaluations: [ActionEvaluation!]!\n allAllowed: Boolean!\n anyAllowed: Boolean!\n allDenied: Boolean!\n anyDenied: Boolean!\n}\n\n# Input types for sync operations\n\"\"\"\nAn action's context, as a caller submits it.\n\nDeclares every field `ActionContext` carries, because an input object rejects a\nfield it does not declare: an action stamped with the head it applies to - which\nis what a signing client sends - is refused outright if these are missing, and\nthat refusal is a whole-request one, not a per-field one.\n\n`prevOpHash` and `prevOpIndex` are the scope head the action was composed\nagainst. They are recorded rather than enforced; a signature is verified from\nthe params carried in its own tuple, which do not include them.\n\"\"\"\ninput ActionContextInput {\n \"\"\"\n The index of the last operation in the scope the action applies to.\n \"\"\"\n prevOpIndex: Int\n\n \"\"\"\n The hash of the state the action applies to.\n \"\"\"\n prevOpHash: String\n\n \"\"\"\n Covers replay of an operation that would otherwise be a no-op.\n \"\"\"\n nonce: String\n\n signer: ReactorSignerInput\n}\n\ninput ReactorSignerInput {\n user: ReactorSignerUserInput\n app: ReactorSignerAppInput\n signatures: [String!]!\n}\n\ninput ReactorSignerUserInput {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ninput ReactorSignerAppInput {\n name: String!\n key: String!\n}\n\ninput ActionInput {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContextInput\n}\n\n# 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 # Get the outgoing relationship edges of a source document. Unlike\n # documentOutgoingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentOutgoingRelationshipEdges(\n sourceIdentifier: String!\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Get the incoming relationship edges of a target document. Unlike\n # documentIncomingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentIncomingRelationshipEdges(\n targetIdentifier: String!\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n \"\"\"\n Predicts whether the calling subject would be admitted to execute each of a\n set of candidate operations, without submitting any of them. A UI asks this to\n disable a control rather than offer an action that fails on submit.\n\n The answer is a prediction, not a promise:\n\n - Real admission compiles an append condition over everything it read and the\n store enforces it at write time. A preflight reads no future, so a policy\n change landing between this answer and the submit changes the verdict. The\n submit path stays the only authority.\n - The verdict is evaluated at the stream heads, so it is correct for a\n candidate about to be submitted. A backdated submission is out of contract;\n the reactor decides that one by position.\n - A candidate whose verdict depends on its input has to carry that input, since\n a conditional grant reads it.\n\n The subject is the authenticated caller and cannot be named in the request:\n answering for an arbitrary subject would disclose what a policy grants\n somebody else. It carries both the caller's address and the did:key of the app\n instance whose token authenticated the request, so a policy naming either\n matches the same principal the write path presents.\n\n Requires the reactor's authEnforcement feature flag. Without it the reactor\n holds no decision model, so this fails with extensions.code\n AUTH_EVALUATION_UNSUPPORTED rather than guessing.\n \"\"\"\n evaluateActions(\n 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. Adding one that already exists is\n # a no-op, metadata included -- use updateRelationship to change an edge.\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Replace the metadata of an existing relationship, preserving its createdAt.\n # Omitting metadata, or passing null, clears it.\n updateRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n metadata: JSONObject\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";
|
|
5697
|
+
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 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 hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\n# A directed edge between two documents, with the metadata recorded against it.\ntype DocumentRelationship {\n sourceId: String!\n targetId: String!\n relationshipType: String!\n metadata: JSONObject\n createdAt: DateTime!\n updatedAt: DateTime!\n}\n\ntype DocumentRelationshipResultPage {\n items: [DocumentRelationship!]!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype ReactorOperationResultPage {\n items: [ReactorOperation!]!\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n cursor: String\n}\n\ntype DeadLetterInfo {\n documentId: String!\n error: String!\n \"\"\"\n How the origin classified the failure. A peer needs this to mirror the origin's\n quarantine decision; without it a held auth operation would freeze the document\n on the peer side while the origin kept syncing.\n \"\"\"\n errorType: String\n jobId: String!\n branch: String!\n scopes: [String!]!\n operationCount: Int!\n}\n\ntype PollSyncEnvelopesResult {\n envelopes: [SyncEnvelope!]!\n ackOrdinal: Int!\n deadLetters: [DeadLetterInfo!]!\n hasMore: Boolean!\n}\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\n \"\"\"\n What the job produced, once it has produced anything. Nullable for the same\n reason as `JobInfo.result`: a job that has not written yet has produced\n nothing, and an empty object would claim otherwise.\n \"\"\"\n result: JSONObject\n\n error: String\n}\n\ntype ReactorSignerUser {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ntype ReactorSignerApp {\n name: String!\n key: String!\n}\n\ntype ReactorSigner {\n user: ReactorSignerUser\n app: ReactorSignerApp\n signatures: [String!]!\n}\n\ntype ActionContext {\n signer: ReactorSigner\n}\n\ntype Action {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContext\n}\n\n\"\"\"\nOne operation an authorization preflight predicts a verdict for.\n\n`input` is what a conditional grant reads, so a candidate standing for a\nfilled-in form carries that form's input. Omitting it predicts the verdict an\nempty input earns, not the verdict the filled-in form will get.\n\"\"\"\ninput ActionCandidateInput {\n scope: String!\n type: String!\n input: JSONObject\n}\n\nenum AuthDecision {\n ALLOW\n DENY\n}\n\n\"\"\"\nOne candidate's predicted verdict. `reason` carries the refusal a DENY is\nrecorded with, and is null on an ALLOW.\n\"\"\"\ntype ActionEvaluation {\n decision: AuthDecision!\n reason: String\n}\n\n\"\"\"\nThe predicted verdicts for a set of candidates, in the order they were given,\nwith the aggregates a caller branches on.\n\nThe aggregates are redundant -- a verdict is binary -- and all four are returned\nso a caller reads the one its question is phrased in rather than negating\nanother. Over no candidates every aggregate is false: nothing is allowed and\nnothing is denied.\n\"\"\"\ntype ActionEvaluations {\n evaluations: [ActionEvaluation!]!\n allAllowed: Boolean!\n anyAllowed: Boolean!\n allDenied: Boolean!\n anyDenied: Boolean!\n}\n\n# Input types for sync operations\n\"\"\"\nAn action's context, as a caller submits it.\n\nDeclares every field `ActionContext` carries, because an input object rejects a\nfield it does not declare: an action stamped with the head it applies to - which\nis what a signing client sends - is refused outright if these are missing, and\nthat refusal is a whole-request one, not a per-field one.\n\n`prevOpHash` and `prevOpIndex` are the scope head the action was composed\nagainst. They are recorded rather than enforced; a signature is verified from\nthe params carried in its own tuple, which do not include them.\n\"\"\"\ninput ActionContextInput {\n \"\"\"\n The index of the last operation in the scope the action applies to.\n \"\"\"\n prevOpIndex: Int\n\n \"\"\"\n The hash of the state the action applies to.\n \"\"\"\n prevOpHash: String\n\n \"\"\"\n Covers replay of an operation that would otherwise be a no-op.\n \"\"\"\n nonce: String\n\n signer: ReactorSignerInput\n}\n\ninput ReactorSignerInput {\n user: ReactorSignerUserInput\n app: ReactorSignerAppInput\n signatures: [String!]!\n}\n\ninput ReactorSignerUserInput {\n address: String!\n networkId: String!\n chainId: Int!\n}\n\ninput ReactorSignerAppInput {\n name: String!\n key: String!\n}\n\ninput ActionInput {\n id: String!\n type: String!\n timestampUtcMs: String!\n input: JSONObject!\n scope: String!\n context: ActionContextInput\n}\n\n# 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 # Get the outgoing relationship edges of a source document. Unlike\n # documentOutgoingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentOutgoingRelationshipEdges(\n sourceIdentifier: String!\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Get the incoming relationship edges of a target document. Unlike\n # documentIncomingRelationships, which returns the documents at the far end,\n # this returns the edges themselves, carrying their metadata and timestamps.\n # Omit relationshipType to return edges of every type.\n documentIncomingRelationshipEdges(\n targetIdentifier: String!\n relationshipType: String\n view: ViewFilterInput\n paging: PagingInput\n ): DocumentRelationshipResultPage!\n\n # Find documents by search criteria\n findDocuments(\n search: SearchFilterInput\n view: ViewFilterInput\n paging: PagingInput\n ): PHDocumentResultPage!\n\n # Get job status\n jobStatus(jobId: String!): JobInfo\n\n # Get operations for a document with filtering and pagination\n documentOperations(\n filter: OperationsFilterInput!\n paging: PagingInput\n ): ReactorOperationResultPage!\n\n \"\"\"\n Predicts whether the calling subject would be admitted to execute each of a\n set of candidate operations, without submitting any of them. A UI asks this to\n disable a control rather than offer an action that fails on submit.\n\n The answer is a prediction, not a promise:\n\n - Real admission compiles an append condition over everything it read and the\n store enforces it at write time. A preflight reads no future, so a policy\n change landing between this answer and the submit changes the verdict. The\n submit path stays the only authority.\n - The verdict is evaluated at the stream heads, so it is correct for a\n candidate about to be submitted. A backdated submission is out of contract;\n the reactor decides that one by position.\n - A candidate whose verdict depends on its input has to carry that input, since\n a conditional grant reads it.\n\n The subject is the authenticated caller and cannot be named in the request:\n answering for an arbitrary subject would disclose what a policy grants\n somebody else. It carries both the caller's address and the did:key of the app\n instance whose token authenticated the request, so a policy naming either\n matches the same principal the write path presents.\n\n Requires the reactor's authEnforcement feature flag. Without it the reactor\n holds no decision model, so this fails with extensions.code\n AUTH_EVALUATION_UNSUPPORTED rather than guessing.\n \"\"\"\n evaluateActions(\n 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. Adding one that already exists is\n # a no-op, metadata included -- use updateRelationship to change an edge.\n addRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n metadata: JSONObject\n branch: String\n ): PHDocument!\n\n # Replace the metadata of an existing relationship, preserving its createdAt.\n # Omitting metadata, or passing null, clears it.\n updateRelationship(\n sourceIdentifier: String!\n targetIdentifier: String!\n relationshipType: String!\n metadata: JSONObject\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";
|
|
5643
5698
|
//#endregion
|
|
5644
5699
|
//#region src/graphql/reactor/pubsub.ts
|
|
5645
5700
|
const pubSub = new PubSub();
|
|
@@ -5650,26 +5705,79 @@ const SUBSCRIPTION_TRIGGERS = {
|
|
|
5650
5705
|
DOCUMENT_CHANGES: "DOCUMENT_CHANGES",
|
|
5651
5706
|
JOB_CHANGES: "JOB_CHANGES"
|
|
5652
5707
|
};
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5708
|
+
function subjectKey(subject) {
|
|
5709
|
+
return `${subject.address ?? ""}|${subject.key ?? ""}`;
|
|
5710
|
+
}
|
|
5711
|
+
/**
|
|
5712
|
+
* The reactor's change feed, read once per distinct subject and shared by that
|
|
5713
|
+
* subject's GraphQL subscriptions. Each feed is read as its subject, so the
|
|
5714
|
+
* client withholds what that subject may not read; one feed read with no view
|
|
5715
|
+
* would gate every subscriber as the host.
|
|
5716
|
+
*/
|
|
5717
|
+
var DocumentChangeFeed = class {
|
|
5718
|
+
reactorClient;
|
|
5719
|
+
#pubSub = new PubSub();
|
|
5720
|
+
#feeds = /* @__PURE__ */ new Map();
|
|
5721
|
+
constructor(reactorClient) {
|
|
5722
|
+
this.reactorClient = reactorClient;
|
|
5723
|
+
}
|
|
5724
|
+
subscribe(subject) {
|
|
5725
|
+
const key = subjectKey(subject);
|
|
5726
|
+
const topic = `${SUBSCRIPTION_TRIGGERS.DOCUMENT_CHANGES}:${key}`;
|
|
5727
|
+
const iterator = this.#pubSub.asyncIterableIterator(topic);
|
|
5728
|
+
this.#acquire(key, topic, subject);
|
|
5729
|
+
let released = false;
|
|
5730
|
+
const release = () => {
|
|
5731
|
+
if (!released) {
|
|
5732
|
+
released = true;
|
|
5733
|
+
this.#release(key);
|
|
5734
|
+
}
|
|
5661
5735
|
};
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5736
|
+
return {
|
|
5737
|
+
next: () => iterator.next(),
|
|
5738
|
+
return: () => {
|
|
5739
|
+
release();
|
|
5740
|
+
return iterator.return();
|
|
5741
|
+
},
|
|
5742
|
+
throw: (error) => {
|
|
5743
|
+
release();
|
|
5744
|
+
return iterator.throw(error);
|
|
5745
|
+
},
|
|
5746
|
+
[Symbol.asyncIterator]() {
|
|
5747
|
+
return this;
|
|
5748
|
+
}
|
|
5749
|
+
};
|
|
5750
|
+
}
|
|
5751
|
+
#acquire(key, topic, subject) {
|
|
5752
|
+
const feed = this.#feeds.get(key);
|
|
5753
|
+
if (feed) {
|
|
5754
|
+
feed.subscribers++;
|
|
5755
|
+
return;
|
|
5670
5756
|
}
|
|
5671
|
-
|
|
5672
|
-
}
|
|
5757
|
+
const view = { subject };
|
|
5758
|
+
const unsubscribe = this.reactorClient.subscribe({}, (event) => {
|
|
5759
|
+
const payload = {
|
|
5760
|
+
documentChanges: event,
|
|
5761
|
+
search: {},
|
|
5762
|
+
view
|
|
5763
|
+
};
|
|
5764
|
+
this.#pubSub.publish(topic, payload);
|
|
5765
|
+
}, view);
|
|
5766
|
+
this.#feeds.set(key, {
|
|
5767
|
+
unsubscribe,
|
|
5768
|
+
subscribers: 1
|
|
5769
|
+
});
|
|
5770
|
+
}
|
|
5771
|
+
#release(key) {
|
|
5772
|
+
const feed = this.#feeds.get(key);
|
|
5773
|
+
if (!feed) return;
|
|
5774
|
+
feed.subscribers--;
|
|
5775
|
+
if (feed.subscribers === 0) {
|
|
5776
|
+
this.#feeds.delete(key);
|
|
5777
|
+
feed.unsubscribe();
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
};
|
|
5673
5781
|
const activeJobSubscriptions = /* @__PURE__ */ new Map();
|
|
5674
5782
|
function ensureJobSubscription(reactorClient, jobId) {
|
|
5675
5783
|
let subscription = activeJobSubscriptions.get(jobId);
|
|
@@ -5734,6 +5842,11 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5734
5842
|
}
|
|
5735
5843
|
name = "r";
|
|
5736
5844
|
hasSubscriptions = true;
|
|
5845
|
+
#documentChanges;
|
|
5846
|
+
get documentChanges() {
|
|
5847
|
+
this.#documentChanges ??= new DocumentChangeFeed(this.reactorClient);
|
|
5848
|
+
return this.#documentChanges;
|
|
5849
|
+
}
|
|
5737
5850
|
/**
|
|
5738
5851
|
* Check operation-level permissions for an array of actions.
|
|
5739
5852
|
* Delegates to base assertCanExecuteOperation for each action.
|
|
@@ -5772,10 +5885,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5772
5885
|
* or they become a way to enumerate around that check. Supreme admins read
|
|
5773
5886
|
* everything, so the per-edge check is skipped for them.
|
|
5774
5887
|
*
|
|
5775
|
-
* The
|
|
5776
|
-
*
|
|
5777
|
-
*
|
|
5778
|
-
*
|
|
5888
|
+
* The cursor describes where to resume in the underlying unfiltered stream,
|
|
5889
|
+
* so it is left as it came. A filtered page is therefore often shorter than
|
|
5890
|
+
* the requested limit, and that is the stream continuing rather than a sign
|
|
5891
|
+
* it ended.
|
|
5779
5892
|
*/
|
|
5780
5893
|
async filterRelationshipEdges(page, farEnd, ctx) {
|
|
5781
5894
|
if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return page;
|
|
@@ -5790,19 +5903,9 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5790
5903
|
}
|
|
5791
5904
|
if (canRead) items.push(edge);
|
|
5792
5905
|
}
|
|
5793
|
-
return this.servedPage(page, items);
|
|
5794
|
-
}
|
|
5795
|
-
/**
|
|
5796
|
-
* Restates a page's totalCount over the items that survived a read filter, so
|
|
5797
|
-
* that the difference between the two cannot be read off as a count of what
|
|
5798
|
-
* the caller was not allowed to see.
|
|
5799
|
-
*/
|
|
5800
|
-
servedPage(page, items) {
|
|
5801
|
-
const withheld = page.items.length - items.length;
|
|
5802
5906
|
return {
|
|
5803
5907
|
...page,
|
|
5804
|
-
items
|
|
5805
|
-
totalCount: Math.max(0, page.totalCount - withheld)
|
|
5908
|
+
items
|
|
5806
5909
|
};
|
|
5807
5910
|
}
|
|
5808
5911
|
/**
|
|
@@ -5838,9 +5941,9 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5838
5941
|
* reports the liveness that keeps it from being reclaimed -- but they never
|
|
5839
5942
|
* adopt, since the push path has no drive check to clear the claimant.
|
|
5840
5943
|
*
|
|
5841
|
-
* Nothing is enforced or adopted without a serving gate
|
|
5842
|
-
*
|
|
5843
|
-
* belong to
|
|
5944
|
+
* Nothing is enforced or adopted without a serving gate: with no gate the
|
|
5945
|
+
* channel serves every subject alike, so there is no subject for it to
|
|
5946
|
+
* belong to.
|
|
5844
5947
|
*/
|
|
5845
5948
|
async #bindOrRefuseChannel(channelId, ctx, options = {}) {
|
|
5846
5949
|
if (!this.syncServingGate) return;
|
|
@@ -5934,7 +6037,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5934
6037
|
if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
|
|
5935
6038
|
const filteredItems = [];
|
|
5936
6039
|
for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
|
|
5937
|
-
return
|
|
6040
|
+
return {
|
|
6041
|
+
...result,
|
|
6042
|
+
items: filteredItems
|
|
6043
|
+
};
|
|
5938
6044
|
}
|
|
5939
6045
|
return result;
|
|
5940
6046
|
} catch (error) {
|
|
@@ -5980,7 +6086,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5980
6086
|
if (!this.authorizationService.isSupremeAdmin(ctx.user?.address)) {
|
|
5981
6087
|
const filteredItems = [];
|
|
5982
6088
|
for (const item of result.items) if (await this.canReadDocument(item.id, ctx)) filteredItems.push(item);
|
|
5983
|
-
return
|
|
6089
|
+
return {
|
|
6090
|
+
...result,
|
|
6091
|
+
items: filteredItems
|
|
6092
|
+
};
|
|
5984
6093
|
}
|
|
5985
6094
|
return result;
|
|
5986
6095
|
} catch (error) {
|
|
@@ -5988,10 +6097,10 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
5988
6097
|
throw error;
|
|
5989
6098
|
}
|
|
5990
6099
|
},
|
|
5991
|
-
jobStatus: async (_parent, args) => {
|
|
6100
|
+
jobStatus: async (_parent, args, ctx) => {
|
|
5992
6101
|
this.logger.debug("jobStatus(@args)", args);
|
|
5993
6102
|
try {
|
|
5994
|
-
return await jobStatus(this.reactorClient, args);
|
|
6103
|
+
return await jobStatus(this.reactorClient, args, (id) => this.servesDocument(id, ctx));
|
|
5995
6104
|
} catch (error) {
|
|
5996
6105
|
this.logger.error("Error in jobStatus: @Error", error);
|
|
5997
6106
|
throw error;
|
|
@@ -6075,7 +6184,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6075
6184
|
const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
|
|
6076
6185
|
await this.assertCanWriteCanonical(parent.document.id, ctx);
|
|
6077
6186
|
} else this.assertCanCreate(ctx);
|
|
6078
|
-
const result = await createDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
|
|
6187
|
+
const result = await createDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient, this.viewSubject(ctx));
|
|
6079
6188
|
if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
|
|
6080
6189
|
if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
|
|
6081
6190
|
return result;
|
|
@@ -6091,7 +6200,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6091
6200
|
const parent = await document(this.reactorClient, { identifier: args.parentIdentifier });
|
|
6092
6201
|
await this.assertCanWriteCanonical(parent.document.id, ctx);
|
|
6093
6202
|
} else this.assertCanCreate(ctx);
|
|
6094
|
-
const result = await createEmptyDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient);
|
|
6203
|
+
const result = await createEmptyDocument(this.reactorClient, args, this.graphqlManager.reactorDriveClient, this.viewSubject(ctx));
|
|
6095
6204
|
if (result?.id && isDriveContainerType(result.documentType)) this.graphqlManager.driveOwnershipCache.add(result.id);
|
|
6096
6205
|
if (ctx.user?.address && result?.id) await this.documentPermissionService?.initializeDocumentProtection(result.id, ctx.user.address, this.authorizationService.config.defaultProtection);
|
|
6097
6206
|
return result;
|
|
@@ -6107,7 +6216,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6107
6216
|
return await execute(this.reactorClient, {
|
|
6108
6217
|
...args,
|
|
6109
6218
|
documentIdentifier: handle.fetchIdentifier
|
|
6110
|
-
});
|
|
6219
|
+
}, this.viewSubject(ctx));
|
|
6111
6220
|
} catch (error) {
|
|
6112
6221
|
this.logger.error("Error in execute(@args): @Error", args, error);
|
|
6113
6222
|
throw error;
|
|
@@ -6133,7 +6242,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6133
6242
|
return await mutateDocument(this.reactorClient, {
|
|
6134
6243
|
...args,
|
|
6135
6244
|
documentIdentifier: handle.fetchIdentifier
|
|
6136
|
-
});
|
|
6245
|
+
}, this.viewSubject(ctx));
|
|
6137
6246
|
} catch (error) {
|
|
6138
6247
|
this.logger.error("Error in mutateDocument(@args): @Error", args, error);
|
|
6139
6248
|
throw error;
|
|
@@ -6159,7 +6268,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6159
6268
|
return await renameDocument(this.reactorClient, {
|
|
6160
6269
|
...args,
|
|
6161
6270
|
documentIdentifier: handle.fetchIdentifier
|
|
6162
|
-
});
|
|
6271
|
+
}, void 0, this.viewSubject(ctx));
|
|
6163
6272
|
} catch (error) {
|
|
6164
6273
|
this.logger.error("Error in renameDocument(@args): @Error", args, error);
|
|
6165
6274
|
throw error;
|
|
@@ -6172,7 +6281,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6172
6281
|
return await setPreferredEditor(this.reactorClient, {
|
|
6173
6282
|
...args,
|
|
6174
6283
|
documentIdentifier: handle.fetchIdentifier
|
|
6175
|
-
});
|
|
6284
|
+
}, void 0, this.viewSubject(ctx));
|
|
6176
6285
|
} catch (error) {
|
|
6177
6286
|
this.logger.error("Error in setPreferredEditor(@args): @Error", args, error);
|
|
6178
6287
|
throw error;
|
|
@@ -6185,7 +6294,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6185
6294
|
return await addRelationship(this.reactorClient, {
|
|
6186
6295
|
...args,
|
|
6187
6296
|
sourceIdentifier: handle.fetchIdentifier
|
|
6188
|
-
});
|
|
6297
|
+
}, this.viewSubject(ctx));
|
|
6189
6298
|
} catch (error) {
|
|
6190
6299
|
this.logger.error("Error in addRelationship(@args): @Error", args, error);
|
|
6191
6300
|
throw error;
|
|
@@ -6198,7 +6307,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6198
6307
|
return await updateRelationship(this.reactorClient, {
|
|
6199
6308
|
...args,
|
|
6200
6309
|
sourceIdentifier: handle.fetchIdentifier
|
|
6201
|
-
});
|
|
6310
|
+
}, this.viewSubject(ctx));
|
|
6202
6311
|
} catch (error) {
|
|
6203
6312
|
this.logger.error("Error in updateRelationship(@args): @Error", args, error);
|
|
6204
6313
|
throw error;
|
|
@@ -6211,7 +6320,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6211
6320
|
return await removeRelationship(this.reactorClient, {
|
|
6212
6321
|
...args,
|
|
6213
6322
|
sourceIdentifier: handle.fetchIdentifier
|
|
6214
|
-
});
|
|
6323
|
+
}, this.viewSubject(ctx));
|
|
6215
6324
|
} catch (error) {
|
|
6216
6325
|
this.logger.error("Error in removeRelationship(@args): @Error", args, error);
|
|
6217
6326
|
throw error;
|
|
@@ -6226,7 +6335,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6226
6335
|
...args,
|
|
6227
6336
|
sourceParentIdentifier: sourceHandle.fetchIdentifier,
|
|
6228
6337
|
targetParentIdentifier: targetHandle.fetchIdentifier
|
|
6229
|
-
});
|
|
6338
|
+
}, this.viewSubject(ctx));
|
|
6230
6339
|
} catch (error) {
|
|
6231
6340
|
this.logger.error("Error in moveRelationship(@args): @Error @args", error, args);
|
|
6232
6341
|
throw error;
|
|
@@ -6337,10 +6446,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6337
6446
|
documentChanges: {
|
|
6338
6447
|
subscribe: (rootValue, args, ctx, info) => {
|
|
6339
6448
|
this.logger.debug("documentChanges subscription started");
|
|
6340
|
-
return withFilter(() => {
|
|
6341
|
-
ensureGlobalDocumentSubscription(this.reactorClient);
|
|
6342
|
-
return getPubSub().asyncIterableIterator(SUBSCRIPTION_TRIGGERS.DOCUMENT_CHANGES);
|
|
6343
|
-
}, async (payload, filterArgs, filterCtx) => {
|
|
6449
|
+
return withFilter(() => this.documentChanges.subscribe(this.viewSubject(ctx)), async (payload, filterArgs, filterCtx) => {
|
|
6344
6450
|
if (!payload) return false;
|
|
6345
6451
|
const search = {
|
|
6346
6452
|
type: filterArgs?.search?.type ?? void 0,
|
|
@@ -6367,6 +6473,7 @@ var ReactorSubgraph = class extends BaseSubgraph {
|
|
|
6367
6473
|
}, async (payload, filterArgs, filterCtx) => {
|
|
6368
6474
|
if (!payload || !filterArgs) return false;
|
|
6369
6475
|
if (!matchesJobFilter(payload, filterArgs)) return false;
|
|
6476
|
+
if (!await this.servesDocument(payload.documentId, filterCtx)) return false;
|
|
6370
6477
|
if (this.authorizationService.isSupremeAdmin(filterCtx?.user?.address)) return true;
|
|
6371
6478
|
if (!payload.documentId) return false;
|
|
6372
6479
|
return this.canReadDocument(payload.documentId, filterCtx);
|
|
@@ -6395,10 +6502,10 @@ const ADMIN_USERS = getAdminUsers();
|
|
|
6395
6502
|
//#endregion
|
|
6396
6503
|
//#region src/graphql/system/version.ts
|
|
6397
6504
|
function getVersion() {
|
|
6398
|
-
return "6.2.3-dev.
|
|
6505
|
+
return "6.2.3-dev.27";
|
|
6399
6506
|
}
|
|
6400
6507
|
function getGitHash() {
|
|
6401
|
-
return "
|
|
6508
|
+
return "5bdbca4f8fd22d4786926c17ec264f0da909e95e";
|
|
6402
6509
|
}
|
|
6403
6510
|
function getGitUrl() {
|
|
6404
6511
|
return buildTreeUrl(getGitHash());
|
|
@@ -7108,9 +7215,14 @@ const HASH_PATTERN = /^[a-f0-9]{64}$/;
|
|
|
7108
7215
|
/**
|
|
7109
7216
|
* Composes document authorization with the projected document/ref
|
|
7110
7217
|
* relationship. Order is fixed: validate ref, resolve the canonical document
|
|
7111
|
-
* id, decide the read,
|
|
7112
|
-
*
|
|
7113
|
-
* attachment
|
|
7218
|
+
* id, decide the read, check the reactor's read gate serves the document, then
|
|
7219
|
+
* check the reference index and that the subject may read a scope whose
|
|
7220
|
+
* operations reference the attachment. A denied document never reaches the
|
|
7221
|
+
* reference reader, and the facade never touches attachment metadata, storage
|
|
7222
|
+
* backends, or presigners.
|
|
7223
|
+
*
|
|
7224
|
+
* The named document decides alone: an attachment several documents reference
|
|
7225
|
+
* is readable through each of them on that document's own terms.
|
|
7114
7226
|
*
|
|
7115
7227
|
* The read is decided by the document's own policy when this composition has a
|
|
7116
7228
|
* model to enforce it with, and by the host's permission tables when it does
|
|
@@ -7128,12 +7240,14 @@ var AttachmentAccessService = class {
|
|
|
7128
7240
|
authorization;
|
|
7129
7241
|
references;
|
|
7130
7242
|
projection;
|
|
7243
|
+
readGate;
|
|
7131
7244
|
scopeGate;
|
|
7132
|
-
constructor(resolveCanonicalId, authorization, references, projection, scopeGate) {
|
|
7245
|
+
constructor(resolveCanonicalId, authorization, references, projection, readGate, scopeGate) {
|
|
7133
7246
|
this.resolveCanonicalId = resolveCanonicalId;
|
|
7134
7247
|
this.authorization = authorization;
|
|
7135
7248
|
this.references = references;
|
|
7136
7249
|
this.projection = projection;
|
|
7250
|
+
this.readGate = readGate;
|
|
7137
7251
|
this.scopeGate = scopeGate;
|
|
7138
7252
|
}
|
|
7139
7253
|
async canReadAttachment(request) {
|
|
@@ -7147,7 +7261,29 @@ var AttachmentAccessService = class {
|
|
|
7147
7261
|
return { kind: "denied" };
|
|
7148
7262
|
}
|
|
7149
7263
|
if (!await this.canReadDocument(documentId, request)) return { kind: "denied" };
|
|
7150
|
-
|
|
7264
|
+
const subject = {
|
|
7265
|
+
address: request.userAddress,
|
|
7266
|
+
key: request.appKey
|
|
7267
|
+
};
|
|
7268
|
+
let served;
|
|
7269
|
+
try {
|
|
7270
|
+
served = await this.readGate.isServed(documentId, { subject });
|
|
7271
|
+
} catch {
|
|
7272
|
+
return { kind: "denied" };
|
|
7273
|
+
}
|
|
7274
|
+
if (!served) return { kind: "denied" };
|
|
7275
|
+
const scopes = await this.references.referencingScopes(documentId, ref);
|
|
7276
|
+
if (scopes.length === 0) return { kind: "denied" };
|
|
7277
|
+
let held;
|
|
7278
|
+
try {
|
|
7279
|
+
held = (await this.readGate.get(documentId, {
|
|
7280
|
+
subject,
|
|
7281
|
+
scopes
|
|
7282
|
+
})).state;
|
|
7283
|
+
} catch {
|
|
7284
|
+
return { kind: "denied" };
|
|
7285
|
+
}
|
|
7286
|
+
if (!scopes.some((scope) => scope in held)) return { kind: "denied" };
|
|
7151
7287
|
return {
|
|
7152
7288
|
kind: "allowed",
|
|
7153
7289
|
documentId,
|
|
@@ -7923,12 +8059,16 @@ function createGetParentIdsFn(reactorClient) {
|
|
|
7923
8059
|
* Authorization gate for the /mcp endpoint (AUTH_REVIEW S-C1). MCP tools have
|
|
7924
8060
|
* unrestricted reactor access, so access is limited to supreme admins when
|
|
7925
8061
|
* auth is enabled. OPEN policy is public; any other policy without an
|
|
7926
|
-
* AuthService fails closed.
|
|
8062
|
+
* AuthService fails closed. The tools read as the caller: anonymous under OPEN,
|
|
8063
|
+
* else the admin's address and the app key its token carries.
|
|
7927
8064
|
*/
|
|
7928
8065
|
function createMcpRequestAuthorizer(authService, authorizationService) {
|
|
7929
8066
|
return async (req) => {
|
|
7930
8067
|
if (!authService) {
|
|
7931
|
-
if (authorizationService.config.policy === AuthorizationPolicy.OPEN) return {
|
|
8068
|
+
if (authorizationService.config.policy === AuthorizationPolicy.OPEN) return {
|
|
8069
|
+
authorized: true,
|
|
8070
|
+
subject: {}
|
|
8071
|
+
};
|
|
7932
8072
|
return {
|
|
7933
8073
|
authorized: false,
|
|
7934
8074
|
status: 401,
|
|
@@ -7951,10 +8091,39 @@ function createMcpRequestAuthorizer(authService, authorizationService) {
|
|
|
7951
8091
|
status: 403,
|
|
7952
8092
|
message: "Forbidden: MCP access requires an administrator"
|
|
7953
8093
|
};
|
|
7954
|
-
return {
|
|
8094
|
+
return {
|
|
8095
|
+
authorized: true,
|
|
8096
|
+
subject: {
|
|
8097
|
+
address: context.user.address,
|
|
8098
|
+
key: context.user.appKey
|
|
8099
|
+
}
|
|
8100
|
+
};
|
|
7955
8101
|
};
|
|
7956
8102
|
}
|
|
7957
8103
|
//#endregion
|
|
8104
|
+
//#region src/services/sync-serving-gate.ts
|
|
8105
|
+
/**
|
|
8106
|
+
* The gate sync serving evaluates a document's own policy through, deciding
|
|
8107
|
+
* what the reactor client's read gate decides at the same flag level.
|
|
8108
|
+
*
|
|
8109
|
+
* Under `authEnforcement` it is built here rather than taken off the client
|
|
8110
|
+
* because it also carries the host's closes-by-default setting, which withholds
|
|
8111
|
+
* the domain scopes of a document nobody has policied yet. That answer belongs
|
|
8112
|
+
* to serving alone -- replay must keep reading an uninitialized document in
|
|
8113
|
+
* full -- so the two gates are separate objects over the same model.
|
|
8114
|
+
*
|
|
8115
|
+
* Below it the registered model ignores the auth scope, so the client reads
|
|
8116
|
+
* through the policy alone, and so does serving: a policy written below the
|
|
8117
|
+
* flag still withholds, where serving ungated would sync to a peer what `get`
|
|
8118
|
+
* refuses it.
|
|
8119
|
+
*/
|
|
8120
|
+
function buildSyncServingGate(reactorModule, authorizationConfig, logger) {
|
|
8121
|
+
if (!reactorModule) return;
|
|
8122
|
+
const model = readDecisionModel(reactorModule.featureFlags, reactorModule.documentModelRegistry);
|
|
8123
|
+
if (!model) return new SyncScopeGate(new BareReadGate(), reactorModule.documentView, logger);
|
|
8124
|
+
return new SyncScopeGate(new ModelReadGate(model, reactorModule.documentView, reactorModule.featureFlags.authGroups, reactorModule.operationIndex, logger, { withholdUninitialized: authorizationConfig.defaultProtection }), reactorModule.documentView, logger);
|
|
8125
|
+
}
|
|
8126
|
+
//#endregion
|
|
7958
8127
|
//#region src/services/renown-config.ts
|
|
7959
8128
|
/** Reading credentials from this reactor's own read model needs a host-supplied
|
|
7960
8129
|
* verifier: core has no idea which subgraph serves that read model. */
|
|
@@ -8220,27 +8389,6 @@ function makeDbClosers(knexInstance, pglite) {
|
|
|
8220
8389
|
return closers;
|
|
8221
8390
|
}
|
|
8222
8391
|
/**
|
|
8223
|
-
* The gate sync serving evaluates a document's own policy through, or undefined
|
|
8224
|
-
* when there is none to evaluate.
|
|
8225
|
-
*
|
|
8226
|
-
* It is built here rather than taken off the reactor client because it is not
|
|
8227
|
-
* the same gate reads use: it carries the host's closes-by-default setting,
|
|
8228
|
-
* which withholds the domain scopes of a document nobody has policied yet. That
|
|
8229
|
-
* answer belongs to serving alone -- replay must keep reading an uninitialized
|
|
8230
|
-
* document in full -- so the two gates are deliberately separate objects over
|
|
8231
|
-
* the same model.
|
|
8232
|
-
*
|
|
8233
|
-
* Undefined below `authEnforcement`, where the registered model ignores the auth
|
|
8234
|
-
* scope: gating through it would serve every domain scope of a policied document
|
|
8235
|
-
* to anyone, which is worse than not gating at all.
|
|
8236
|
-
*/
|
|
8237
|
-
function buildSyncServingGate(reactorModule, authorizationConfig, logger) {
|
|
8238
|
-
if (!reactorModule) return;
|
|
8239
|
-
const model = readDecisionModel(reactorModule.featureFlags, reactorModule.documentModelRegistry);
|
|
8240
|
-
if (!model) return;
|
|
8241
|
-
return new SyncScopeGate(new ModelReadGate(model, reactorModule.documentView, reactorModule.featureFlags.authGroups, reactorModule.operationIndex, logger, { withholdUninitialized: authorizationConfig.defaultProtection }), reactorModule.documentView, logger);
|
|
8242
|
-
}
|
|
8243
|
-
/**
|
|
8244
8392
|
* Resolves the gateway adapter type from the `GATEWAY_ADAPTER` env var.
|
|
8245
8393
|
* Defaults to "apollo" (the federation gateway, production behavior).
|
|
8246
8394
|
* "stitching" selects the in-process graphql-tools merge gateway (#1565
|
|
@@ -8597,7 +8745,7 @@ async function _setupAPI(reactorClient, syncManager, reactorProcessorManager, ht
|
|
|
8597
8745
|
const authorizationService = createAuthorizationService(authorizationConfig, documentPermissionService, createGetParentIdsFn(reactorClient));
|
|
8598
8746
|
logger.info(`Authorization service initialized (policy: ${authorizationConfig.policy})`);
|
|
8599
8747
|
assertAttachmentPolicyReadsAllowed(attachmentReadsFollowDocumentPolicy, syncServingGate !== void 0);
|
|
8600
|
-
const attachmentAccess = new AttachmentAccessService(createCanonicalDocumentIdResolver(reactorClient), authorizationService, attachmentReferenceIndex.store, attachmentReferenceProjection, attachmentReadsFollowDocumentPolicy ? syncServingGate : void 0);
|
|
8748
|
+
const attachmentAccess = new AttachmentAccessService(createCanonicalDocumentIdResolver(reactorClient), authorizationService, attachmentReferenceIndex.store, attachmentReferenceProjection, reactorClient, attachmentReadsFollowDocumentPolicy ? syncServingGate : void 0);
|
|
8601
8749
|
const coreSubgraphs = DefaultCoreSubgraphs.slice();
|
|
8602
8750
|
coreSubgraphs.push(ReactorSubgraph);
|
|
8603
8751
|
if (documentPermissionService) {
|
|
@@ -8833,7 +8981,7 @@ var PackageManagementService = class {
|
|
|
8833
8981
|
}
|
|
8834
8982
|
};
|
|
8835
8983
|
//#endregion
|
|
8836
|
-
export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthDecision, AuthDecisionSchema, AuthEvaluationUnsupportedError, AuthService, AuthSubgraph, AuthenticationRequiredError, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CORE_PACKAGE_NAME, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, DocumentRelationshipFieldsFragmentDoc, EvaluateActionsDocument, FindDocumentsDocument, ForbiddenError, GetDocumentDocument, GetDocumentIncomingRelationshipEdgesDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipEdgesDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, HttpRouteService, ImportPackageLoader, InMemoryPackageStorage, InvalidNamespaceError, JobChangesDocument, MalformedStoredOperationError, MemoryWebhookStore, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PACKAGE_ROUTE_SEGMENT, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RelationalWebhookStore, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, UpdateRelationshipDocument, ViewFilterInputSchema, WEBHOOK_SEGMENT, WS_AUTH_CLOSE_REASONS, WS_CLOSE_REASON_AUTHENTICATION_REQUIRED, WS_CLOSE_REASON_BEARER_REJECTED, WebhookService, assertAttachmentPolicyReadsAllowed, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertHostMountPath, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, createWsAuthHandlers, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, namespacePath, namespaceSegments, newWebhookToken, parseDriveUrl, parseWebhookBody, redactHeaders, renderGraphqlPlayground, resolvePackageName, resolveRenownConfig, verifyWebhook };
|
|
8984
|
+
export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthDecision, AuthDecisionSchema, AuthEvaluationUnsupportedError, AuthService, AuthSubgraph, AuthenticationRequiredError, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CORE_PACKAGE_NAME, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, DocumentRelationshipFieldsFragmentDoc, EvaluateActionsDocument, FindDocumentsDocument, ForbiddenError, GetDocumentDocument, GetDocumentIncomingRelationshipEdgesDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipEdgesDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, HttpRouteService, ImportPackageLoader, InMemoryPackageStorage, InvalidNamespaceError, JobChangesDocument, MalformedStoredOperationError, MemoryWebhookStore, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PACKAGE_ROUTE_SEGMENT, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RelationalWebhookStore, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, UpdateRelationshipDocument, ViewFilterInputSchema, WEBHOOK_SEGMENT, WS_AUTH_CLOSE_REASONS, WS_CLOSE_REASON_AUTHENTICATION_REQUIRED, WS_CLOSE_REASON_BEARER_REJECTED, WebhookService, assertAttachmentPolicyReadsAllowed, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertHostMountPath, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, callerSubject, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, createWsAuthHandlers, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, namespacePath, namespaceSegments, newWebhookToken, parseDriveUrl, parseWebhookBody, redactHeaders, renderGraphqlPlayground, resolvePackageName, resolveRenownConfig, verifyWebhook };
|
|
8837
8985
|
|
|
8838
8986
|
//# sourceMappingURL=index.mjs.map
|
|
8839
|
-
//# debugId=
|
|
8987
|
+
//# debugId=8ab62893-8127-51eb-9d6f-a5900b1823de
|