@powerhousedao/reactor-api 6.2.2-dev.83 → 6.2.2-dev.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
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]="a3bbf8ae-c16f-5c87-aaf9-23e0709811cd")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f43e08dc-7398-57dd-8824-db5d97ccc6ca")}catch(e){}}();
3
3
  import { _ as createAuthorizationService, a as isSubgraphClass, b as MalformedStoredOperationError, d as loadSubgraphs, f as BaseSubgraph, g as AuthorizedDocumentHandle, h as AuthorizationPolicy, i as buildGraphqlOperations, l as loadDocumentModels, m as createCanonicalDocumentIdResolver, n as buildGraphQlDriveDocument, o as debounce, p as CanonicalDocumentIdResolutionError, r as buildGraphqlOperation, s as extractUpgradeManifests, t as buildGraphQlDocument, u as loadProcessors, v as AuthEvaluationUnsupportedError, y as ForbiddenError } from "./utils-lHoPB-ft.mjs";
4
4
  import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
5
5
  import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
@@ -17,6 +17,7 @@ import { deserializeSignature, serializeSignature, setName } from "@powerhouseda
17
17
  import * as z$1 from "zod";
18
18
  import { z } from "zod";
19
19
  import { createHandler } from "graphql-sse/lib/use/fetch";
20
+ import lzString from "lz-string";
20
21
  import { PubSub, withFilter } from "graphql-subscriptions";
21
22
  import dotenv from "dotenv";
22
23
  import { buildTreeUrl } from "@powerhousedao/shared";
@@ -318,11 +319,11 @@ async function createGatewayAdapter(type, logger) {
318
319
  async function createHttpAdapter(type) {
319
320
  switch (type) {
320
321
  case "express": {
321
- const { createExpressHttpAdapter } = await import("./adapter-http-express-D5OpUIbV.mjs");
322
+ const { createExpressHttpAdapter } = await import("./adapter-http-express-CgVPgEMm.mjs");
322
323
  return createExpressHttpAdapter();
323
324
  }
324
325
  case "fastify": {
325
- const { createFastifyHttpAdapter } = await import("./adapter-http-fastify-B248RJtm.mjs");
326
+ const { createFastifyHttpAdapter } = await import("./adapter-http-fastify-D98iRljY.mjs");
326
327
  return createFastifyHttpAdapter();
327
328
  }
328
329
  }
@@ -3212,6 +3213,12 @@ function filterLatestDocumentModelVersions(documentModels) {
3212
3213
  return Array.from(latestByName.values());
3213
3214
  }
3214
3215
  const DefaultFeatureFlags = { enableDocumentModelSubgraphs: true };
3216
+ /**
3217
+ * The registration-source label for document-model-generated subgraphs, so
3218
+ * they can be pruned like package-contributed ones (see
3219
+ * regenerateDocumentModelSubgraphs).
3220
+ */
3221
+ const DOCUMENT_MODEL_SUBGRAPH_SOURCE = "document-models";
3215
3222
  var GraphQLManager = class {
3216
3223
  initialized = false;
3217
3224
  coreSubgraphsMap = /* @__PURE__ */ new Map();
@@ -3225,6 +3232,16 @@ var GraphQLManager = class {
3225
3232
  /** Cached document models for schema generation - updated on init and regenerate */
3226
3233
  cachedDocumentModels = [];
3227
3234
  subgraphHandlerCache = /* @__PURE__ */ new Map();
3235
+ /** subgraphPath → the http adapter handle of its mounted route. */
3236
+ subgraphRouteHandles = /* @__PURE__ */ new Map();
3237
+ /** Handle of the currently mounted supergraph SSE route, if any. */
3238
+ sseRouteHandle;
3239
+ /**
3240
+ * Package name → the subgraph instances registered from it, keyed by
3241
+ * subgraph name. Tracks the package association so that removed packages
3242
+ * can be torn down (see unregisterPackage / prunePackageSubgraphs).
3243
+ */
3244
+ packageSubgraphs = /* @__PURE__ */ new Map();
3228
3245
  /** Per-subgraph handlers by name, before auth/drive middleware is applied,
3229
3246
  * for in-process queries by trusted internals. */
3230
3247
  #internalSubgraphHandlers = /* @__PURE__ */ new Map();
@@ -3307,7 +3324,8 @@ var GraphQLManager = class {
3307
3324
  try {
3308
3325
  const models = (await this.reactorClient.getDocumentModelModules()).results;
3309
3326
  this.cachedDocumentModels = models;
3310
- await this.#setupDocumentModelSubgraphs("graphql", models);
3327
+ const registeredNames = await this.#setupDocumentModelSubgraphs("graphql", models);
3328
+ await this.prunePackageSubgraphs(DOCUMENT_MODEL_SUBGRAPH_SOURCE, registeredNames);
3311
3329
  await this.updateRouter();
3312
3330
  this.logger.info("Regenerated document model subgraphs with @count models", models.length);
3313
3331
  } catch (error) {
@@ -3325,6 +3343,7 @@ var GraphQLManager = class {
3325
3343
  }
3326
3344
  async #setupDocumentModelSubgraphs(supergraph, documentModels) {
3327
3345
  const latestDocumentModels = filterLatestDocumentModelVersions(documentModels);
3346
+ const registeredNames = /* @__PURE__ */ new Set();
3328
3347
  for (const documentModel of latestDocumentModels) {
3329
3348
  if (DOCUMENT_MODELS_TO_EXCLUDE.includes(documentModel.documentModel.global.id)) continue;
3330
3349
  if (!hasOperationSchemas(documentModel)) continue;
@@ -3340,42 +3359,71 @@ var GraphQLManager = class {
3340
3359
  authorizationService: this.authorizationService,
3341
3360
  syncServingGate: this.syncServingGate
3342
3361
  });
3343
- await this.#addSubgraphInstance(subgraphInstance, supergraph, false);
3362
+ await this.#addSubgraphInstance(subgraphInstance, supergraph, false, DOCUMENT_MODEL_SUBGRAPH_SOURCE);
3363
+ registeredNames.add(subgraphInstance.name);
3344
3364
  } catch (error) {
3345
3365
  this.logger.error(`Failed to setup document model subgraph for ${documentModel.documentModel.global.id}`, error instanceof Error ? error.message : error);
3346
3366
  this.logger.debug("@error", error);
3347
3367
  }
3348
3368
  }
3369
+ return registeredNames;
3349
3370
  }
3350
- async #addSubgraphInstance(subgraphInstance, supergraph = "", core = false) {
3371
+ /** Core subgraph names are reserved: they register with core=true during
3372
+ * init() and no package subgraph may take one of their names. */
3373
+ #isCoreNameReserved(name) {
3374
+ for (const subgraphs of this.coreSubgraphsMap.values()) if (subgraphs.some((it) => it.name === name)) return true;
3375
+ return false;
3376
+ }
3377
+ async #addSubgraphInstance(subgraphInstance, supergraph = "", core = false, packageName) {
3378
+ if (!core && this.#isCoreNameReserved(subgraphInstance.name)) {
3379
+ this.logger.error("Rejecting subgraph @name: name is reserved by a registered core subgraph", subgraphInstance.name);
3380
+ return;
3381
+ }
3351
3382
  const subgraphsMap = core ? this.coreSubgraphsMap : this.subgraphs;
3352
3383
  const existingSubgraph = subgraphsMap.get(supergraph)?.find((it) => it.name === subgraphInstance.name);
3353
3384
  if (existingSubgraph === subgraphInstance) {
3354
3385
  this.logger.debug(`Skipping duplicate subgraph: ${subgraphInstance.name}`);
3355
3386
  return existingSubgraph;
3356
3387
  }
3357
- if (existingSubgraph) await this.#removeSubgraphInstance(existingSubgraph, subgraphsMap);
3388
+ if (existingSubgraph) await this.#removeSubgraphInstance(existingSubgraph);
3358
3389
  await subgraphInstance.onSetup?.();
3359
3390
  const subgraphs = subgraphsMap.get(supergraph) ?? [];
3360
3391
  subgraphs.push(subgraphInstance);
3361
3392
  subgraphsMap.set(supergraph, subgraphs);
3362
3393
  if (supergraph !== "" && supergraph !== "graphql") subgraphsMap.get("graphql")?.push(subgraphInstance);
3394
+ if (packageName !== void 0) {
3395
+ let byName = this.packageSubgraphs.get(packageName);
3396
+ if (!byName) {
3397
+ byName = /* @__PURE__ */ new Map();
3398
+ this.packageSubgraphs.set(packageName, byName);
3399
+ }
3400
+ byName.set(subgraphInstance.name, subgraphInstance);
3401
+ }
3363
3402
  this.logger.info(`${existingSubgraph ? "Replaced" : "Registered"} ${this.path.endsWith("/") ? this.path : this.path + "/"}${supergraph ? supergraph + "/" : ""}${subgraphInstance.name} subgraph.`);
3364
3403
  return subgraphInstance;
3365
3404
  }
3366
- /** Tear down a replaced subgraph: onDisconnect, drop it from every bucket,
3367
- * and invalidate its cached handlers so #setupSubgraphs rebuilds them. */
3368
- async #removeSubgraphInstance(instance, subgraphsMap) {
3405
+ /**
3406
+ * Tear down a subgraph instance: disconnect it, drop it from every
3407
+ * bucket, unmount its route, dispose its WebSocket handler, and remove
3408
+ * its internal handler. Used when a subgraph is replaced or its package
3409
+ * is removed; #setupSubgraphs then never re-mounts it.
3410
+ */
3411
+ async #removeSubgraphInstance(instance) {
3369
3412
  try {
3370
3413
  await instance.onDisconnect?.();
3371
3414
  } catch (error) {
3372
3415
  this.logger.error("Error disconnecting subgraph @name: @error", instance.name, error);
3373
3416
  }
3374
- for (const [bucket, subgraphs] of subgraphsMap.entries()) {
3417
+ for (const subgraphsMap of [this.coreSubgraphsMap, this.subgraphs]) for (const [bucket, subgraphs] of subgraphsMap.entries()) {
3375
3418
  if (!subgraphs.includes(instance)) continue;
3376
3419
  subgraphsMap.set(bucket, subgraphs.filter((it) => it !== instance));
3377
3420
  const subgraphPath = this.#getSubgraphPath(instance, bucket);
3378
3421
  this.subgraphHandlerCache.delete(subgraphPath);
3422
+ const routeHandle = this.subgraphRouteHandles.get(subgraphPath);
3423
+ if (routeHandle !== void 0) {
3424
+ this.httpAdapter.unmount(routeHandle);
3425
+ this.subgraphRouteHandles.delete(subgraphPath);
3426
+ }
3379
3427
  const wsDisposer = this.subgraphWsDisposers.get(subgraphPath);
3380
3428
  if (wsDisposer) {
3381
3429
  this.subgraphWsDisposers.delete(subgraphPath);
@@ -3387,6 +3435,11 @@ var GraphQLManager = class {
3387
3435
  }
3388
3436
  }
3389
3437
  if (this.#internalSubgraphHandlers.get(instance.name)?.subgraph === instance) this.#internalSubgraphHandlers.delete(instance.name);
3438
+ for (const [packageName, byName] of this.packageSubgraphs) {
3439
+ if (byName.get(instance.name) !== instance) continue;
3440
+ byName.delete(instance.name);
3441
+ if (byName.size === 0) this.packageSubgraphs.delete(packageName);
3442
+ }
3390
3443
  }
3391
3444
  /**
3392
3445
  * Register a pre-constructed subgraph instance.
@@ -3409,7 +3462,13 @@ var GraphQLManager = class {
3409
3462
  getAuthorizationService() {
3410
3463
  return this.authorizationService;
3411
3464
  }
3412
- async registerSubgraph(subgraph, supergraph = "", core = false) {
3465
+ /**
3466
+ * Register a subgraph class. `packageName` labels the resulting instance
3467
+ * with its contributing package so the instance can be torn down when the
3468
+ * package is removed or drops the subgraph (see unregisterPackage and
3469
+ * prunePackageSubgraphs).
3470
+ */
3471
+ async registerSubgraph(subgraph, supergraph = "", core = false, packageName) {
3413
3472
  const subgraphInstance = new subgraph({
3414
3473
  relationalDb: this.relationalDb,
3415
3474
  analyticsStore: this.analyticsStore,
@@ -3421,7 +3480,35 @@ var GraphQLManager = class {
3421
3480
  authorizationService: this.authorizationService,
3422
3481
  syncServingGate: this.syncServingGate
3423
3482
  });
3424
- return this.#addSubgraphInstance(subgraphInstance, supergraph, core);
3483
+ return this.#addSubgraphInstance(subgraphInstance, supergraph, core, packageName);
3484
+ }
3485
+ /**
3486
+ * Tear down every subgraph instance registered from `packageName`:
3487
+ * disconnects the instances, unmounts their routes, disposes their
3488
+ * WebSocket handlers, then refreshes the router so the supergraph SDL and
3489
+ * the supergraph SSE handler no longer reference them. No-op if the
3490
+ * package has no registered subgraphs.
3491
+ */
3492
+ async unregisterPackage(packageName) {
3493
+ const byName = this.packageSubgraphs.get(packageName);
3494
+ if (!byName || byName.size === 0) return;
3495
+ for (const instance of Array.from(byName.values())) await this.#removeSubgraphInstance(instance);
3496
+ await this.updateRouter();
3497
+ }
3498
+ /**
3499
+ * Remove this package's registered subgraphs whose names are not in
3500
+ * `keepNames` — the package is still loaded but dropped some of its
3501
+ * subgraphs. Refreshes the router if anything was removed.
3502
+ */
3503
+ async prunePackageSubgraphs(packageName, keepNames) {
3504
+ const byName = this.packageSubgraphs.get(packageName);
3505
+ if (!byName) return;
3506
+ let removed = 0;
3507
+ for (const [name, instance] of Array.from(byName.entries())) if (!keepNames.has(name)) {
3508
+ await this.#removeSubgraphInstance(instance);
3509
+ removed++;
3510
+ }
3511
+ if (removed > 0) await this.updateRouter();
3425
3512
  }
3426
3513
  /** Find a registered subgraph by name, core or package-contributed. */
3427
3514
  getSubgraphByName(name) {
@@ -3515,10 +3602,18 @@ var GraphQLManager = class {
3515
3602
  return (connectionParams) => this.#createWebSocketContext(connectionParams);
3516
3603
  }
3517
3604
  setSupergraph(supergraph, subgraphs) {
3518
- this.subgraphs.set(supergraph, subgraphs);
3605
+ const accepted = [];
3606
+ for (const subgraph of subgraphs) {
3607
+ if (this.#isCoreNameReserved(subgraph.name)) {
3608
+ this.logger.error("Rejecting subgraph @name: name is reserved by a registered core subgraph", subgraph.name);
3609
+ continue;
3610
+ }
3611
+ accepted.push(subgraph);
3612
+ }
3613
+ this.subgraphs.set(supergraph, accepted);
3519
3614
  const globalSubgraphs = this.subgraphs.get("graphql");
3520
- if (globalSubgraphs) this.subgraphs.set("graphql", [...globalSubgraphs, ...subgraphs]);
3521
- else this.subgraphs.set("graphql", subgraphs);
3615
+ if (globalSubgraphs) this.subgraphs.set("graphql", [...globalSubgraphs, ...accepted]);
3616
+ else this.subgraphs.set("graphql", accepted);
3522
3617
  return this.updateRouter();
3523
3618
  }
3524
3619
  async shutdown() {
@@ -3534,7 +3629,7 @@ var GraphQLManager = class {
3534
3629
  });
3535
3630
  }
3536
3631
  #getSubgraphPath(subgraph, supergraph) {
3537
- return path.posix.join(subgraph.path ?? "", supergraph, subgraph.name);
3632
+ return path.posix.join(this.path, supergraph, subgraph.name);
3538
3633
  }
3539
3634
  /** The in-process handler map is keyed by bare name, so two distinct
3540
3635
  * subgraphs sharing one name would shadow each other: keep the first. */
@@ -3555,13 +3650,20 @@ var GraphQLManager = class {
3555
3650
  this.logger.debug(`Setting up subgraph ${subgraph.name}`);
3556
3651
  const subgraphPath = this.#getSubgraphPath(subgraph, supergraph);
3557
3652
  try {
3558
- if (this.subgraphHandlerCache.has(subgraphPath)) continue;
3653
+ const cached = this.subgraphHandlerCache.get(subgraphPath);
3654
+ if (cached) {
3655
+ if (cached.subgraph !== subgraph) this.logger.warn("Subgraph path @path already mounted by @kept; @name is shadowed and will not be mounted", subgraphPath, cached.subgraph.name, subgraph.name);
3656
+ continue;
3657
+ }
3559
3658
  const schema = createSchema(this.cachedDocumentModels, subgraph.resolvers, subgraph.typeDefs);
3560
3659
  const rawHandler = await this.gatewayAdapter.createHandler(schema, this.#makeContextFactory());
3561
3660
  const fetchHandler = this.#composeFetchMiddleware(rawHandler);
3562
- this.subgraphHandlerCache.set(subgraphPath, fetchHandler);
3661
+ this.subgraphHandlerCache.set(subgraphPath, {
3662
+ handler: fetchHandler,
3663
+ subgraph
3664
+ });
3563
3665
  this.#setInternalSubgraphHandler(subgraph, subgraphPath, rawHandler);
3564
- this.httpAdapter.mount(subgraphPath, fetchHandler);
3666
+ this.subgraphRouteHandles.set(subgraphPath, this.httpAdapter.mount(subgraphPath, fetchHandler));
3565
3667
  if (subgraph.hasSubscriptions) {
3566
3668
  try {
3567
3669
  if (this.subgraphWsDisposers.get(subgraphPath)) this.logger.warn("Overwriting existing WebSocket disposer for subgraph at path @path", subgraphPath);
@@ -3626,7 +3728,13 @@ var GraphQLManager = class {
3626
3728
  #setupSupergraphSSE(superGraphPath) {
3627
3729
  const allSubgraphs = this.#getAllSubgraphs();
3628
3730
  const modules = Array.from(allSubgraphs.values()).filter((subgraph) => subgraph.hasSubscriptions).map((subgraph) => this.#buildSubgraphSchemaModule(subgraph));
3629
- if (modules.length === 0) return;
3731
+ if (modules.length === 0) {
3732
+ if (this.sseRouteHandle !== void 0) {
3733
+ this.httpAdapter.unmount(this.sseRouteHandle);
3734
+ this.sseRouteHandle = void 0;
3735
+ }
3736
+ return;
3737
+ }
3630
3738
  try {
3631
3739
  const mergedSchema = createMergedSchema(modules);
3632
3740
  this.#setupSSEHandler(mergedSchema, superGraphPath);
@@ -3650,7 +3758,8 @@ var GraphQLManager = class {
3650
3758
  contextFactory: this.#makeContextFactory()
3651
3759
  });
3652
3760
  const handler = this.#composeFetchMiddleware(rawHandler);
3653
- this.httpAdapter.mount(ssePath, handler, { exact: true });
3761
+ if (this.sseRouteHandle !== void 0) this.httpAdapter.unmount(this.sseRouteHandle);
3762
+ this.sseRouteHandle = this.httpAdapter.mount(ssePath, handler, { exact: true });
3654
3763
  }
3655
3764
  /**
3656
3765
  * Compose the request-level fetch middleware chain. Auth runs first
@@ -3768,6 +3877,31 @@ var PackagesSubgraph = class extends BaseSubgraph {
3768
3877
  //#endregion
3769
3878
  //#region src/graphql/playground.ts
3770
3879
  /**
3880
+ * Decodes an `explorerURLState` URL parameter. Returns `null` for anything
3881
+ * that is not a valid lz-compressed payload with a non-empty `document`.
3882
+ */
3883
+ function decodeExplorerUrlState(encoded) {
3884
+ try {
3885
+ const decompressed = lzString.decompressFromEncodedURIComponent(encoded);
3886
+ if (!decompressed) return null;
3887
+ const payload = JSON.parse(decompressed);
3888
+ if (typeof payload.document !== "string" || payload.document.length === 0) return null;
3889
+ const variables = typeof payload.variables === "string" ? payload.variables : void 0;
3890
+ let headers;
3891
+ if (typeof payload.headers === "string") {
3892
+ const parsed = JSON.parse(payload.headers);
3893
+ if (parsed && typeof parsed === "object") headers = parsed;
3894
+ }
3895
+ return {
3896
+ query: payload.document,
3897
+ variables,
3898
+ headers
3899
+ };
3900
+ } catch {
3901
+ return null;
3902
+ }
3903
+ }
3904
+ /**
3771
3905
  * Pinned CDN versions for GraphiQL playground dependencies.
3772
3906
  * Using pinned versions avoids unpkg.com redirect issues that can
3773
3907
  * trigger CORS errors in the browser.
@@ -3778,7 +3912,29 @@ const CDN_VERSIONS = {
3778
3912
  graphiql: "3.8.3",
3779
3913
  pluginExplorer: "4.0.0"
3780
3914
  };
3781
- function renderGraphqlPlayground(url, query, headers = {}) {
3915
+ /**
3916
+ * Escape a string for embedding as a single-quoted JS string literal inside
3917
+ * an HTML `<script>` block. The payload reaches the browser through an
3918
+ * attacker-controllable URL parameter, so the embedding must survive two
3919
+ * parsers: HTML (a `<\/script>` inside the string would end the script
3920
+ * element and start a new, attacker-authored one) and JS (backticks,
3921
+ * `${...}` and quotes in the content would otherwise break out of the
3922
+ * literal). Backslash, quote, CR and LF escapes keep the literal valid;
3923
+ * every `<` becomes `\u003c`, which no HTML parser reads as a tag opener.
3924
+ * The escapes resolve when the script parses, so the in-memory value is
3925
+ * byte-identical to the input.
3926
+ */
3927
+ function jsStringEscape(value) {
3928
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/</g, "\\u003c");
3929
+ }
3930
+ /**
3931
+ * JSON is already a valid JS object/string literal; the only HTML-hostile
3932
+ * byte left is `<` (e.g. `<\/script>` inside a string value).
3933
+ */
3934
+ function jsJsonEscape(json) {
3935
+ return json.replace(/</g, "\\u003c");
3936
+ }
3937
+ function renderGraphqlPlayground(url, query, headers = {}, variables) {
3782
3938
  return `<!doctype html>
3783
3939
  <html lang="en">
3784
3940
  <head>
@@ -3818,16 +3974,21 @@ function renderGraphqlPlayground(url, query, headers = {}) {
3818
3974
  <div id="graphiql">Loading...</div>
3819
3975
  <script>
3820
3976
  var fetcher = GraphiQL.createFetcher({
3821
- url: '${url}',
3822
- headers: ${JSON.stringify(headers)}
3977
+ url: '${jsStringEscape(url)}',
3978
+ headers: ${jsJsonEscape(JSON.stringify(headers))}
3823
3979
  });
3824
- var defaultQuery = ${query ? `\`${query}\`` : void 0};
3980
+ var defaultQuery = ${query ? `'${jsStringEscape(query)}'` : "undefined"};
3981
+ var defaultVariables = ${variables ? `'${jsStringEscape(variables)}'` : "undefined"};
3825
3982
 
3826
- if (defaultQuery) {
3827
- var sessionQuery = localStorage.getItem("graphiql:query");
3828
- if (sessionQuery) {
3829
- localStorage.setItem("graphiql:query", defaultQuery);
3830
- }
3983
+ // GraphiQL only applies defaultQuery/variables when the editor has
3984
+ // no persisted value. Overwrite any stale persisted state so a
3985
+ // returning user still gets the document-scoped query/variables
3986
+ // instead of their last session's.
3987
+ if (defaultQuery && localStorage.getItem("graphiql:query")) {
3988
+ localStorage.setItem("graphiql:query", defaultQuery);
3989
+ }
3990
+ if (defaultVariables && localStorage.getItem("graphiql:variables")) {
3991
+ localStorage.setItem("graphiql:variables", defaultVariables);
3831
3992
  }
3832
3993
 
3833
3994
  var explorerPlugin = GraphiQLPluginExplorer.explorerPlugin();
@@ -3837,7 +3998,8 @@ function renderGraphqlPlayground(url, query, headers = {}) {
3837
3998
  fetcher: fetcher,
3838
3999
  defaultEditorToolsVisibility: true,
3839
4000
  plugins: [explorerPlugin],
3840
- defaultQuery
4001
+ defaultQuery: defaultQuery,
4002
+ variables: defaultVariables
3841
4003
  });
3842
4004
  }
3843
4005
 
@@ -4740,10 +4902,10 @@ const ADMIN_USERS = getAdminUsers();
4740
4902
  //#endregion
4741
4903
  //#region src/graphql/system/version.ts
4742
4904
  function getVersion() {
4743
- return "6.2.2-dev.83";
4905
+ return "6.2.2-dev.85";
4744
4906
  }
4745
4907
  function getGitHash() {
4746
- return "ebe44292295ea32a81913f3968be5723fd916ac4";
4908
+ return "e2a529de5211f831b48f2d22cdbac2919dd35fdb";
4747
4909
  }
4748
4910
  function getGitUrl() {
4749
4911
  return buildTreeUrl(getGitHash());
@@ -5196,24 +5358,32 @@ var PackageManager = class {
5196
5358
  const documentModels = await this.loadDocumentModels([pkg]);
5197
5359
  const upgradeManifests = await this.loadUpgradeManifests([pkg]);
5198
5360
  const upgradeManifestsMap = new Map(this.upgradeManifestsMap);
5199
- upgradeManifestsMap.set(pkg, upgradeManifests.get(pkg) ?? []);
5361
+ const pkgManifests = upgradeManifests.get(pkg) ?? [];
5362
+ if (pkgManifests.length === 0) upgradeManifestsMap.delete(pkg);
5363
+ else upgradeManifestsMap.set(pkg, pkgManifests);
5200
5364
  this.upgradeManifestsMap = upgradeManifestsMap;
5201
5365
  const documentModelsMap = new Map(this.docModelsMap);
5202
- documentModelsMap.set(pkg, documentModels.get(pkg) ?? []);
5366
+ const pkgModels = documentModels.get(pkg) ?? [];
5367
+ if (pkgModels.length === 0) documentModelsMap.delete(pkg);
5368
+ else documentModelsMap.set(pkg, pkgModels);
5203
5369
  this.updatePackagesMap(documentModelsMap);
5204
5370
  }
5205
5371
  async updateSubgraphsForPackage(pkg) {
5206
5372
  this.logger.debug(`Updating subgraphs for package: ${pkg}`);
5207
5373
  const subgraphs = await this.loadSubgraphs([pkg]);
5208
5374
  const subgraphsMap = new Map(this.subgraphsMap);
5209
- subgraphsMap.set(pkg, subgraphs.get(pkg) ?? []);
5375
+ const pkgSubgraphs = subgraphs.get(pkg) ?? [];
5376
+ if (pkgSubgraphs.length === 0) subgraphsMap.delete(pkg);
5377
+ else subgraphsMap.set(pkg, pkgSubgraphs);
5210
5378
  this.updateSubgraphsMap(subgraphsMap);
5211
5379
  }
5212
5380
  async updateProcessorsForPackage(pkg) {
5213
5381
  this.logger.debug(`Updating processors for package: ${pkg}`);
5214
5382
  const processors = await this.loadProcessors([pkg]);
5215
5383
  const processorsMap = new Map(this.processorMap);
5216
- processorsMap.set(pkg, processors.get(pkg) ?? []);
5384
+ const pkgProcessors = processors.get(pkg) ?? [];
5385
+ if (pkgProcessors.length === 0) processorsMap.delete(pkg);
5386
+ else processorsMap.set(pkg, pkgProcessors);
5217
5387
  this.updateProcessorsMap(processorsMap);
5218
5388
  }
5219
5389
  /** Debounced per key so repeated subscribePackages calls reuse one timer. */
@@ -5285,6 +5455,28 @@ var PackageManager = class {
5285
5455
  this.processorMap = processorsMap;
5286
5456
  this.eventEmitter.emit("processorsChange", processorsMap);
5287
5457
  }
5458
+ /**
5459
+ * Remove `pkg` from all package maps and emit the change events, tearing
5460
+ * down everything the package registered (subgraphs, processors, document
5461
+ * models). For an uninstalled package no loader can serve it again, so
5462
+ * nothing can re-add it.
5463
+ */
5464
+ removePackage(pkg) {
5465
+ this.logger.info(`Removing package: ${pkg}`);
5466
+ const dropKey = (map) => {
5467
+ if (!map.has(pkg)) return map;
5468
+ const next = new Map(map);
5469
+ next.delete(pkg);
5470
+ return next;
5471
+ };
5472
+ this.docModelsMap = dropKey(this.docModelsMap);
5473
+ this.upgradeManifestsMap = dropKey(this.upgradeManifestsMap);
5474
+ this.subgraphsMap = dropKey(this.subgraphsMap);
5475
+ this.processorMap = dropKey(this.processorMap);
5476
+ this.updatePackagesMap(this.docModelsMap);
5477
+ this.updateSubgraphsMap(this.subgraphsMap);
5478
+ this.updateProcessorsMap(this.processorMap);
5479
+ }
5288
5480
  onDocumentModelsChange(handler) {
5289
5481
  this.eventEmitter.on("documentModelsChange", handler);
5290
5482
  }
@@ -6297,6 +6489,15 @@ function createReadinessGate() {
6297
6489
  }
6298
6490
  };
6299
6491
  }
6492
+ /**
6493
+ * The GraphiQL explorer page's mount prefix. `path.posix.join` normalizes the
6494
+ * join with `basePath` — a naive template literal would produce `//explorer`
6495
+ * for the default `/` basePath, which express compiles into a route that only
6496
+ * matches `//explorer`, leaving `GET /explorer` a 404.
6497
+ */
6498
+ function getExplorerPrefix(basePath) {
6499
+ return path.posix.join(basePath, "explorer");
6500
+ }
6300
6501
  function resolveAttachmentStoragePath(options) {
6301
6502
  if (options.attachmentStoragePath) return options.attachmentStoragePath;
6302
6503
  if (options.dbPath && !options.dbPath.startsWith("postgres")) return path.resolve(options.dbPath, "..", "attachments");
@@ -6359,7 +6560,7 @@ function buildSyncServingGate(reactorModule, authorizationConfig, logger) {
6359
6560
  async function setupGraphQLManager(httpAdapter, authFetchMiddleware, requireAuthFetchMiddleware, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, subgraphs, logger, authorizationService, authService, documentPermissionService, enableDocumentModelSubgraphs, port, reactorDriveClient, syncServingGate) {
6360
6561
  const graphqlManager = new GraphQLManager(config.basePath, httpServer, wsServer, client, relationalDb, analyticsStore, syncManager, logger, httpAdapter, await createGatewayAdapter("apollo", logger), authService, documentPermissionService, { enableDocumentModelSubgraphs }, port, authorizationService, reactorDriveClient, syncServingGate);
6361
6562
  await graphqlManager.init(subgraphs.core, authFetchMiddleware, requireAuthFetchMiddleware);
6362
- for (const [, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql");
6563
+ for (const [packageName, collection] of subgraphs.extended.entries()) for (const subgraph of collection) await graphqlManager.registerSubgraph(subgraph, "graphql", false, packageName);
6363
6564
  await graphqlManager.updateRouter();
6364
6565
  return graphqlManager;
6365
6566
  }
@@ -6386,14 +6587,28 @@ function setupEventListeners(pkgManager, graphqlManager, reactorProcessorManager
6386
6587
  }
6387
6588
  graphqlManager.regenerateDocumentModelSubgraphs();
6388
6589
  });
6590
+ let knownSubgraphPackages = /* @__PURE__ */ new Set();
6389
6591
  pkgManager.onSubgraphsChange((packagedSubgraphs) => {
6390
6592
  (async () => {
6391
- for (const [, subgraphs] of packagedSubgraphs) for (const subgraph of subgraphs) await graphqlManager.registerSubgraph(subgraph, "graphql");
6593
+ for (const [packageName, subgraphs] of packagedSubgraphs) {
6594
+ const incomingNames = /* @__PURE__ */ new Set();
6595
+ for (const subgraph of subgraphs) {
6596
+ const instance = await graphqlManager.registerSubgraph(subgraph, "graphql", false, packageName);
6597
+ if (!instance) continue;
6598
+ incomingNames.add(instance.name);
6599
+ }
6600
+ await graphqlManager.prunePackageSubgraphs(packageName, incomingNames);
6601
+ }
6602
+ for (const packageName of knownSubgraphPackages) if (!packagedSubgraphs.has(packageName)) await graphqlManager.unregisterPackage(packageName);
6603
+ knownSubgraphPackages = new Set(packagedSubgraphs.keys());
6392
6604
  await graphqlManager.updateRouter();
6393
6605
  })();
6394
6606
  });
6607
+ let knownProcessorPackages = /* @__PURE__ */ new Set();
6395
6608
  pkgManager.onProcessorsChange((processors) => {
6396
6609
  (async () => {
6610
+ for (const packageName of knownProcessorPackages) if (!processors.has(packageName)) await reactorProcessorManager.unregisterFactory(packageName);
6611
+ knownProcessorPackages = new Set(processors.keys());
6397
6612
  for (const [packageName, fns] of processors) {
6398
6613
  await reactorProcessorManager.unregisterFactory(packageName);
6399
6614
  const validBuilders = fns.map((fn) => fn(module)).filter((factory) => typeof factory === "function");
@@ -6503,13 +6718,14 @@ async function _setupCommonInfrastructure(options) {
6503
6718
  httpAdapter.getRoute("/health", () => new Response("OK", { status: 200 }));
6504
6719
  const readiness = createReadinessGate();
6505
6720
  httpAdapter.getRoute("/ready", () => readiness.isReady() ? new Response("OK", { status: 200 }) : new Response("starting", { status: 503 }));
6506
- const explorerPrefix = `${config.basePath}/explorer`;
6721
+ const explorerPrefix = getExplorerPrefix(config.basePath);
6507
6722
  httpAdapter.getRoute(`${explorerPrefix}/:endpoint?`, (request) => {
6508
6723
  const url = new URL(request.url);
6509
6724
  const suffix = url.pathname.slice(explorerPrefix.length).replace(/^\//, "");
6510
6725
  const endpoint = suffix ? `/${suffix}` : "/graphql";
6511
- const query = url.searchParams.get("query") ?? void 0;
6512
- return new Response(renderGraphqlPlayground(endpoint, query), { headers: { "Content-Type": "text/html" } });
6726
+ const explorerState = decodeExplorerUrlState(url.searchParams.get("explorerURLState") ?? "");
6727
+ const query = explorerState?.query ?? url.searchParams.get("query") ?? void 0;
6728
+ return new Response(renderGraphqlPlayground(endpoint, query, explorerState?.headers ?? {}, explorerState?.variables), { headers: { "Content-Type": "text/html" } });
6513
6729
  });
6514
6730
  let authFetchMiddleware;
6515
6731
  let requireAuthFetchMiddleware;
@@ -6729,7 +6945,8 @@ async function initializeAndStartAPI(clientInitializer, options, processorApp) {
6729
6945
  syncManager,
6730
6946
  documentModelRegistry,
6731
6947
  readiness,
6732
- attachmentReferenceProjection
6948
+ attachmentReferenceProjection,
6949
+ packageManager: packages
6733
6950
  };
6734
6951
  }
6735
6952
  //#endregion
@@ -6760,6 +6977,7 @@ var PackageManagementService = class {
6760
6977
  defaultRegistryUrl;
6761
6978
  httpLoader;
6762
6979
  documentModelRegistry;
6980
+ packageManager;
6763
6981
  logger = childLogger(["reactor-api", "package-management-service"]);
6764
6982
  onModelsChanged;
6765
6983
  loadedModulesCache = /* @__PURE__ */ new Map();
@@ -6768,6 +6986,7 @@ var PackageManagementService = class {
6768
6986
  this.defaultRegistryUrl = options.defaultRegistryUrl;
6769
6987
  this.httpLoader = options.httpLoader;
6770
6988
  this.documentModelRegistry = options.documentModelRegistry;
6989
+ this.packageManager = options.packageManager;
6771
6990
  }
6772
6991
  setOnModelsChanged(callback) {
6773
6992
  this.onModelsChanged = callback;
@@ -6810,6 +7029,7 @@ var PackageManagementService = class {
6810
7029
  this.loadedModulesCache.delete(name);
6811
7030
  if (this.httpLoader) this.httpLoader.documentModelLoader.removeFromCache(name);
6812
7031
  if (this.documentModelRegistry) this.documentModelRegistry.unregisterModules(...existing.documentTypes);
7032
+ if (this.packageManager) this.packageManager.removePackage(name);
6813
7033
  this.triggerModelsChanged();
6814
7034
  this.logger.info("Uninstalled package @name", name);
6815
7035
  return true;
@@ -6833,7 +7053,7 @@ var PackageManagementService = class {
6833
7053
  }
6834
7054
  };
6835
7055
  //#endregion
6836
- export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthDecision, AuthDecisionSchema, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, EvaluateActionsDocument, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground, resolveRenownConfig };
7056
+ export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessService, AuthDecision, AuthDecisionSchema, AuthService, AuthSubgraph, AuthorizationPolicy, AuthorizedDocumentHandle, BaseSubgraph, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, EvaluateActionsDocument, FindDocumentsDocument, GetDocumentDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, ImportPackageLoader, InMemoryPackageStorage, JobChangesDocument, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, ViewFilterInputSchema, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, parseDriveUrl, renderGraphqlPlayground, resolveRenownConfig };
6837
7057
 
6838
7058
  //# sourceMappingURL=index.mjs.map
6839
- //# debugId=a3bbf8ae-c16f-5c87-aaf9-23e0709811cd
7059
+ //# debugId=f43e08dc-7398-57dd-8824-db5d97ccc6ca