@powerhousedao/reactor-api 6.2.0-dev.9 → 6.2.0-rc.0

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.
@@ -1,6 +1,6 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1ea545f7-1fd6-5398-9906-11c687b497c5")}catch(e){}}();
3
- import { a as isSubgraphClass, o as debounce, s as isSubpath } from "../../utils-CtC8sjRo.mjs";
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="02fc7276-e4d6-5195-b4ce-5791df961225")}catch(e){}}();
3
+ import { a as isSubgraphClass, o as debounce, s as isSubpath } from "../../utils-iibOQ50e.mjs";
4
4
  import { childLogger } from "document-model";
5
5
  import path from "node:path";
6
6
  import { viteCommonjs } from "@originjs/vite-plugin-commonjs";
@@ -142,4 +142,4 @@ async function startViteServer(root, logger) {
142
142
  export { VitePackageLoader, createViteLogger, startViteServer };
143
143
 
144
144
  //# sourceMappingURL=vite-loader.mjs.map
145
- //# debugId=1ea545f7-1fd6-5398-9906-11c687b497c5
145
+ //# debugId=02fc7276-e4d6-5195-b4ce-5791df961225
@@ -1 +1 @@
1
- {"version":3,"file":"vite-loader.mjs","sources":["../../../src/packages/vite-loader.mts"],"sourcesContent":["import { viteCommonjs } from \"@originjs/vite-plugin-commonjs\";\nimport type {\n ProcessorFactoryBuilder,\n SubgraphClass,\n} from \"@powerhousedao/reactor-api\";\nimport type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { childLogger, type ILogger } from \"document-model\";\nimport path from \"node:path\";\nimport { readPackage } from \"read-pkg\";\nimport type { Logger, ViteDevServer } from \"vite\";\nimport { createLogger, createServer } from \"vite\";\nimport { isSubgraphClass } from \"../graphql/utils.js\";\nimport type {\n ISubscribablePackageLoader,\n ISubscriptionOptions,\n} from \"./types.js\";\nimport { debounce, isSubpath } from \"./util.js\";\n\nexport function createViteLogger(logger: ILogger, prefix = \"\") {\n const customLogger = createLogger(\"info\", {\n prefix,\n });\n customLogger.info = logger.info;\n customLogger.warn = logger.warn;\n customLogger.error = logger.error;\n return customLogger;\n}\n\nexport class VitePackageLoader implements ISubscribablePackageLoader {\n private readonly logger = childLogger([\"reactor-api\", \"vite-loader\"]);\n\n private readonly vite: ViteDevServer;\n\n readonly name = \"VitePackageLoader\";\n\n static build(vite: ViteDevServer) {\n return new VitePackageLoader(vite);\n }\n\n constructor(vite: ViteDevServer) {\n this.vite = vite;\n }\n\n private getDocumentModelsPath(identifier: string): string {\n return path.join(identifier, \"./document-models\");\n }\n\n private getSubgraphsPath(identifier: string): string {\n return path.join(identifier, \"./subgraphs\");\n }\n\n private getProcessorsPath(identifier: string): string {\n return path.join(identifier, \"./processors\");\n }\n\n public loadDocumentModels(identifier: string, immediate = false) {\n return this.#loadDocumentModelsWithDebounce(immediate, identifier);\n }\n\n #loadDocumentModelsWithDebounce = debounce(\n this.#loadDocumentModels.bind(this),\n 500,\n );\n\n async #loadDocumentModels(\n identifier: string,\n ): Promise<DocumentModelModule[]> {\n const fullPath = this.getDocumentModelsPath(identifier);\n this.logger.debug(\"Loading document models from\", fullPath);\n\n try {\n const localDMs = (await this.vite.ssrLoadModule(fullPath)) as Record<\n string,\n DocumentModelModule\n >;\n\n const exports = Object.values(localDMs);\n\n // duck type\n const documentModels: DocumentModelModule[] = [];\n for (const dm of exports) {\n if (dm.documentModel) {\n documentModels.push(dm);\n }\n }\n\n this.logger.verbose(\n ` ➜ Loaded ${documentModels.length} Document Models from: ${identifier}`,\n );\n\n return documentModels;\n } catch (e) {\n this.logger.debug(` ➜ No Document Models found for: ${identifier}`, e);\n }\n\n return [];\n }\n\n async loadSubgraphs(identifier: string): Promise<SubgraphClass[]> {\n const fullPath = this.getSubgraphsPath(identifier);\n\n this.logger.verbose(\"Loading subgraphs from\", fullPath);\n\n let localSubgraphs: Record<string, Record<string, SubgraphClass>> = {};\n try {\n localSubgraphs = await this.vite.ssrLoadModule(fullPath);\n } catch (e) {\n this.logger.debug(` ➜ No Subgraphs found for: ${identifier}`, e);\n return [];\n }\n\n const subgraphs: SubgraphClass[] = [];\n for (const [name, subgraph] of Object.entries(localSubgraphs)) {\n const SubgraphClass = subgraph[name];\n if (isSubgraphClass(SubgraphClass)) {\n subgraphs.push(SubgraphClass);\n }\n }\n\n this.logger.debug(\n ` ➜ Loaded ${subgraphs.length} Subgraphs from: ${identifier}`,\n );\n\n return subgraphs;\n }\n\n async loadProcessors(\n identifier: string,\n ): Promise<ProcessorFactoryBuilder | null> {\n const fullPath = this.getProcessorsPath(identifier);\n\n this.logger.verbose(\"Loading processors from\", fullPath);\n\n try {\n const pkgModule = await this.vite.ssrLoadModule(fullPath);\n\n const factory = (\n pkgModule as Record<string, ProcessorFactoryBuilder | undefined>\n )?.processorFactory;\n\n if (factory && typeof factory === \"function\") {\n this.logger.verbose(\n ` ➜ Loaded Processor Factory from: ${identifier}`,\n );\n return factory;\n }\n } catch (e) {\n this.logger.debug(\n ` ➜ No Processor Factory found for: ${identifier}`,\n e,\n );\n }\n\n this.logger.verbose(` ➜ No Processor Factory found for: ${identifier}`);\n\n // return empty processor factory\n return null;\n }\n\n onDocumentModelsChange(\n identifier: string,\n handler: (documentModels: DocumentModelModule[]) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const listener = async (changedPath: string) => {\n const documentModelsPath = this.getDocumentModelsPath(identifier);\n if (isSubpath(documentModelsPath, changedPath)) {\n const documentModels = await this.loadDocumentModels(identifier);\n handler(documentModels);\n }\n };\n\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n\n onSubgraphsChange(\n identifier: string,\n handler: (subgraphs: SubgraphClass[]) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const subgraphsPath = this.getSubgraphsPath(identifier);\n const listener = debounce(async (changedPath: string) => {\n if (isSubpath(subgraphsPath, changedPath)) {\n const subgraphs = await this.loadSubgraphs(identifier);\n handler(subgraphs);\n }\n }, options?.debounce ?? 100);\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n\n onProcessorsChange(\n identifier: string,\n handler: (processors: ProcessorFactoryBuilder | null) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const processorsPath = this.getProcessorsPath(identifier);\n const listener = async (changedPath: string) => {\n if (isSubpath(processorsPath, changedPath)) {\n const processors = await this.loadProcessors(identifier);\n handler(processors);\n }\n };\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n}\n\nexport async function startViteServer(root: string, logger?: Logger) {\n const packageJson = await readPackage({ cwd: root });\n\n const vite = await createServer({\n root,\n configFile: false,\n logger,\n server: { middlewareMode: true, watch: { ignored: [\"**/.ph/**\"] } },\n appType: \"custom\",\n build: {\n rollupOptions: {\n input: [],\n },\n },\n resolve: {\n tsconfigPaths: true,\n alias: {\n [packageJson.name]: root,\n },\n },\n plugins: [\n viteCommonjs(),\n {\n name: \"suppress-hmr\",\n handleHotUpdate() {\n return []; // return empty array to suppress server refresh\n },\n },\n ],\n });\n\n return vite;\n}\n"],"names":["#loadDocumentModelsWithDebounce","#loadDocumentModels"],"mappings":"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","debug_id":"1ea545f7-1fd6-5398-9906-11c687b497c5"}
1
+ {"version":3,"file":"vite-loader.mjs","sources":["../../../src/packages/vite-loader.mts"],"sourcesContent":["import { viteCommonjs } from \"@originjs/vite-plugin-commonjs\";\nimport type {\n ProcessorFactoryBuilder,\n SubgraphClass,\n} from \"@powerhousedao/reactor-api\";\nimport type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { childLogger, type ILogger } from \"document-model\";\nimport path from \"node:path\";\nimport { readPackage } from \"read-pkg\";\nimport type { Logger, PluginOption, ViteDevServer } from \"vite\";\nimport { createLogger, createServer } from \"vite\";\nimport { isSubgraphClass } from \"../graphql/utils.js\";\nimport type {\n ISubscribablePackageLoader,\n ISubscriptionOptions,\n} from \"./types.js\";\nimport { debounce, isSubpath } from \"./util.js\";\n\nexport function createViteLogger(logger: ILogger, prefix = \"\") {\n const customLogger = createLogger(\"info\", {\n prefix,\n });\n customLogger.info = logger.info;\n customLogger.warn = logger.warn;\n customLogger.error = logger.error;\n return customLogger;\n}\n\nexport class VitePackageLoader implements ISubscribablePackageLoader {\n private readonly logger = childLogger([\"reactor-api\", \"vite-loader\"]);\n\n private readonly vite: ViteDevServer;\n\n readonly name = \"VitePackageLoader\";\n\n static build(vite: ViteDevServer) {\n return new VitePackageLoader(vite);\n }\n\n constructor(vite: ViteDevServer) {\n this.vite = vite;\n }\n\n private getDocumentModelsPath(identifier: string): string {\n return path.join(identifier, \"./document-models\");\n }\n\n private getSubgraphsPath(identifier: string): string {\n return path.join(identifier, \"./subgraphs\");\n }\n\n private getProcessorsPath(identifier: string): string {\n return path.join(identifier, \"./processors\");\n }\n\n public loadDocumentModels(identifier: string, immediate = false) {\n return this.#loadDocumentModelsWithDebounce(immediate, identifier);\n }\n\n #loadDocumentModelsWithDebounce = debounce(\n this.#loadDocumentModels.bind(this),\n 500,\n );\n\n async #loadDocumentModels(\n identifier: string,\n ): Promise<DocumentModelModule[]> {\n const fullPath = this.getDocumentModelsPath(identifier);\n this.logger.debug(\"Loading document models from\", fullPath);\n\n try {\n const localDMs = (await this.vite.ssrLoadModule(fullPath)) as Record<\n string,\n DocumentModelModule\n >;\n\n const exports = Object.values(localDMs);\n\n // duck type\n const documentModels: DocumentModelModule[] = [];\n for (const dm of exports) {\n if (dm.documentModel) {\n documentModels.push(dm);\n }\n }\n\n this.logger.verbose(\n ` ➜ Loaded ${documentModels.length} Document Models from: ${identifier}`,\n );\n\n return documentModels;\n } catch (e) {\n this.logger.debug(` ➜ No Document Models found for: ${identifier}`, e);\n }\n\n return [];\n }\n\n async loadSubgraphs(identifier: string): Promise<SubgraphClass[]> {\n const fullPath = this.getSubgraphsPath(identifier);\n\n this.logger.verbose(\"Loading subgraphs from\", fullPath);\n\n let localSubgraphs: Record<string, Record<string, SubgraphClass>> = {};\n try {\n localSubgraphs = await this.vite.ssrLoadModule(fullPath);\n } catch (e) {\n this.logger.debug(` ➜ No Subgraphs found for: ${identifier}`, e);\n return [];\n }\n\n const subgraphs: SubgraphClass[] = [];\n for (const [name, subgraph] of Object.entries(localSubgraphs)) {\n const SubgraphClass = subgraph[name];\n if (isSubgraphClass(SubgraphClass)) {\n subgraphs.push(SubgraphClass);\n }\n }\n\n this.logger.debug(\n ` ➜ Loaded ${subgraphs.length} Subgraphs from: ${identifier}`,\n );\n\n return subgraphs;\n }\n\n async loadProcessors(\n identifier: string,\n ): Promise<ProcessorFactoryBuilder | null> {\n const fullPath = this.getProcessorsPath(identifier);\n\n this.logger.verbose(\"Loading processors from\", fullPath);\n\n try {\n const pkgModule = await this.vite.ssrLoadModule(fullPath);\n\n const factory = (\n pkgModule as Record<string, ProcessorFactoryBuilder | undefined>\n )?.processorFactory;\n\n if (factory && typeof factory === \"function\") {\n this.logger.verbose(\n ` ➜ Loaded Processor Factory from: ${identifier}`,\n );\n return factory;\n }\n } catch (e) {\n this.logger.debug(\n ` ➜ No Processor Factory found for: ${identifier}`,\n e,\n );\n }\n\n this.logger.verbose(` ➜ No Processor Factory found for: ${identifier}`);\n\n // return empty processor factory\n return null;\n }\n\n onDocumentModelsChange(\n identifier: string,\n handler: (documentModels: DocumentModelModule[]) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const listener = async (changedPath: string) => {\n const documentModelsPath = this.getDocumentModelsPath(identifier);\n if (isSubpath(documentModelsPath, changedPath)) {\n const documentModels = await this.loadDocumentModels(identifier);\n handler(documentModels);\n }\n };\n\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n\n onSubgraphsChange(\n identifier: string,\n handler: (subgraphs: SubgraphClass[]) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const subgraphsPath = this.getSubgraphsPath(identifier);\n const listener = debounce(async (changedPath: string) => {\n if (isSubpath(subgraphsPath, changedPath)) {\n const subgraphs = await this.loadSubgraphs(identifier);\n handler(subgraphs);\n }\n }, options?.debounce ?? 100);\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n\n onProcessorsChange(\n identifier: string,\n handler: (processors: ProcessorFactoryBuilder | null) => void,\n options?: ISubscriptionOptions,\n ): () => void {\n const processorsPath = this.getProcessorsPath(identifier);\n const listener = async (changedPath: string) => {\n if (isSubpath(processorsPath, changedPath)) {\n const processors = await this.loadProcessors(identifier);\n handler(processors);\n }\n };\n this.vite.watcher.on(\"change\", listener);\n\n return () => {\n this.vite.watcher.off(\"change\", listener);\n };\n }\n}\n\nexport async function startViteServer(root: string, logger?: Logger) {\n const packageJson = await readPackage({ cwd: root });\n\n const vite = await createServer({\n root,\n configFile: false,\n logger,\n server: { middlewareMode: true, watch: { ignored: [\"**/.ph/**\"] } },\n appType: \"custom\",\n build: {\n rollupOptions: {\n input: [],\n },\n },\n resolve: {\n tsconfigPaths: true,\n alias: {\n [packageJson.name]: root,\n },\n },\n plugins: [\n // cast: the plugin ships types built against an older vite, and the\n // mismatched Plugin type crashes tsc 6 during assignability checking\n viteCommonjs() as PluginOption,\n {\n name: \"suppress-hmr\",\n handleHotUpdate() {\n return []; // return empty array to suppress server refresh\n },\n },\n ],\n });\n\n return vite;\n}\n"],"names":["#loadDocumentModelsWithDebounce","#loadDocumentModels"],"mappings":"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","debug_id":"02fc7276-e4d6-5195-b4ce-5791df961225"}
@@ -1,10 +1,49 @@
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]="143f3840-f36d-5b22-91f1-5b5328f2e537")}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]="bb45ff30-0d48-5614-9063-64b33d486341")}catch(e){}}();
3
3
  import { gql } from "graphql-tag";
4
4
  import { GraphQLError } from "graphql";
5
+ import { DRIVE_AUTH_ERROR_MESSAGES } from "@powerhousedao/reactor";
5
6
  import { childLogger } from "document-model";
6
7
  import path from "node:path";
8
+ //#region src/graphql/errors.ts
9
+ /** Caller (authenticated or anonymous) lacks permission for the document. */
10
+ var ForbiddenError = class extends GraphQLError {
11
+ constructor(detail = "") {
12
+ const base = DRIVE_AUTH_ERROR_MESSAGES.forbidden;
13
+ super(`${base}${detail ? ` ${detail}` : ""}`, { extensions: { code: "FORBIDDEN" } });
14
+ }
15
+ };
16
+ /** Anonymous caller on an action that requires logging in. */
17
+ var AuthenticationRequiredError = class extends GraphQLError {
18
+ constructor(detail = "") {
19
+ const base = DRIVE_AUTH_ERROR_MESSAGES.authenticationRequired;
20
+ super(`${base}${detail ? ` ${detail}` : ""}`, { extensions: { code: "UNAUTHENTICATED" } });
21
+ }
22
+ };
23
+ //#endregion
7
24
  //#region src/services/authorization.service.ts
25
+ /**
26
+ * Result of a passed per-document authorization check. `fetchIdentifier` is
27
+ * always safe for the data fetch: the canonical id when resolved, or the raw
28
+ * identifier when the check was skipped for a policy-wide caller.
29
+ */
30
+ var AuthorizedDocumentHandle = class AuthorizedDocumentHandle {
31
+ constructor(fetchIdentifier, isResolved) {
32
+ this.fetchIdentifier = fetchIdentifier;
33
+ this.isResolved = isResolved;
34
+ }
35
+ static resolved(documentId) {
36
+ return new AuthorizedDocumentHandle(documentId, true);
37
+ }
38
+ static skipped(identifier) {
39
+ return new AuthorizedDocumentHandle(identifier, false);
40
+ }
41
+ /** The verified canonical id; throws when resolution was skipped. */
42
+ canonicalId() {
43
+ if (!this.isResolved) throw new Error("Canonical document id is unavailable: the authorization check was skipped for a policy-wide caller");
44
+ return this.fetchIdentifier;
45
+ }
46
+ };
8
47
  const AuthorizationPolicy = {
9
48
  OPEN: "OPEN",
10
49
  ADMIN_ONLY: "ADMIN_ONLY",
@@ -25,6 +64,9 @@ var OpenAuthorizationService = class extends BaseAuthorizationService {
25
64
  isSupremeAdmin() {
26
65
  return true;
27
66
  }
67
+ canCreate() {
68
+ return true;
69
+ }
28
70
  canRead() {
29
71
  return Promise.resolve(true);
30
72
  }
@@ -40,6 +82,9 @@ var OpenAuthorizationService = class extends BaseAuthorizationService {
40
82
  };
41
83
  /** ADMIN_ONLY: authentication on, document permissions off — only ADMINS. */
42
84
  var AdminOnlyAuthorizationService = class extends BaseAuthorizationService {
85
+ canCreate(userAddress) {
86
+ return this.isSupremeAdmin(userAddress);
87
+ }
43
88
  canRead(_documentId, userAddress) {
44
89
  return Promise.resolve(this.isSupremeAdmin(userAddress));
45
90
  }
@@ -53,55 +98,103 @@ var AdminOnlyAuthorizationService = class extends BaseAuthorizationService {
53
98
  return Promise.resolve(this.isSupremeAdmin(userAddress));
54
99
  }
55
100
  };
56
- /** DOCUMENT_PERMISSIONS: the full per-document protection + grant model. */
101
+ const PERMISSION_RANK = {
102
+ READ: 1,
103
+ WRITE: 2,
104
+ ADMIN: 3
105
+ };
106
+ function satisfies(level, required) {
107
+ return level !== null && PERMISSION_RANK[level] >= PERMISSION_RANK[required];
108
+ }
109
+ /**
110
+ * DOCUMENT_PERMISSIONS: the full per-document protection + grant model.
111
+ *
112
+ * All decisions live here; DocumentPermissionService is the data-access
113
+ * layer underneath (grants, protection rows, owners).
114
+ */
57
115
  var DocumentPermissionsAuthorizationService = class extends BaseAuthorizationService {
58
- constructor(permissions, config) {
116
+ #permissions;
117
+ #getParentIds;
118
+ constructor(permissions, getParentIds, config) {
59
119
  super(config);
60
- this.permissions = permissions;
120
+ this.#permissions = permissions;
121
+ this.#getParentIds = getParentIds;
61
122
  }
62
- async canRead(documentId, userAddress, getParentIds) {
63
- if (this.isSupremeAdmin(userAddress)) return true;
64
- if (!(getParentIds ? await this.permissions.isProtectedWithAncestors(documentId, getParentIds) : await this.permissions.isDocumentProtected(documentId))) return true;
65
- if (!userAddress) return false;
66
- const owner = await this.permissions.getDocumentOwner(documentId);
67
- if (owner && owner === userAddress.toLowerCase()) return true;
68
- if (getParentIds) return this.permissions.canRead(documentId, userAddress, getParentIds);
69
- return this.permissions.canReadDocument(documentId, userAddress);
123
+ canCreate(userAddress) {
124
+ return this.isSupremeAdmin(userAddress) || !!userAddress;
125
+ }
126
+ canRead(documentId, userAddress) {
127
+ return this.#canAccess(documentId, "READ", userAddress);
128
+ }
129
+ canWrite(documentId, userAddress) {
130
+ return this.#canAccess(documentId, "WRITE", userAddress);
70
131
  }
71
- async canWrite(documentId, userAddress, getParentIds) {
132
+ canManage(documentId, userAddress) {
133
+ return this.#isDocumentAdmin(documentId, userAddress);
134
+ }
135
+ async canMutate(documentId, operationType, userAddress) {
72
136
  if (this.isSupremeAdmin(userAddress)) return true;
73
- return this.#permissionCanWrite(documentId, userAddress, getParentIds);
137
+ if (await this.#permissions.isOperationRestricted(documentId, operationType)) {
138
+ if (!userAddress) return false;
139
+ if (await this.#isDocumentAdmin(documentId, userAddress)) return true;
140
+ return this.#permissions.hasOperationGrant(documentId, operationType, userAddress);
141
+ }
142
+ return this.#canAccess(documentId, "WRITE", userAddress);
74
143
  }
75
- async canManage(documentId, userAddress) {
144
+ /**
145
+ * The one "administers this document" predicate: supreme admin, document
146
+ * owner, or ADMIN grant on the document itself.
147
+ */
148
+ async #isDocumentAdmin(documentId, userAddress) {
76
149
  if (this.isSupremeAdmin(userAddress)) return true;
77
150
  if (!userAddress) return false;
78
- const owner = await this.permissions.getDocumentOwner(documentId);
151
+ const owner = await this.#permissions.getDocumentOwner(documentId);
79
152
  if (owner && owner === userAddress.toLowerCase()) return true;
80
- return this.permissions.canManageDocument(documentId, userAddress);
153
+ return satisfies(await this.#grantLevel(documentId, userAddress), "ADMIN");
81
154
  }
82
- async canMutate(documentId, operationType, userAddress, getParentIds) {
155
+ /**
156
+ * The one read/write decision shape: supreme admin → unprotected (self or
157
+ * ancestor) → owner → inherited grant of at least the required level.
158
+ */
159
+ async #canAccess(documentId, required, userAddress) {
83
160
  if (this.isSupremeAdmin(userAddress)) return true;
84
- if (await this.permissions.isOperationRestricted(documentId, operationType)) return this.permissions.canExecuteOperation(documentId, operationType, userAddress?.toLowerCase());
85
- return this.#permissionCanWrite(documentId, userAddress, getParentIds);
86
- }
87
- async #permissionCanWrite(documentId, userAddress, getParentIds) {
88
- if (!(getParentIds ? await this.permissions.isProtectedWithAncestors(documentId, getParentIds) : await this.permissions.isDocumentProtected(documentId))) return true;
161
+ if (!await this.#permissions.isProtectedWithAncestors(documentId, this.#getParentIds)) return true;
89
162
  if (!userAddress) return false;
90
- const owner = await this.permissions.getDocumentOwner(documentId);
163
+ const owner = await this.#permissions.getDocumentOwner(documentId);
91
164
  if (owner && owner === userAddress.toLowerCase()) return true;
92
- if (getParentIds) return this.permissions.canWrite(documentId, userAddress, getParentIds);
93
- return this.permissions.canWriteDocument(documentId, userAddress);
165
+ return this.#hasGrantInHierarchy(documentId, userAddress, required);
166
+ }
167
+ /** Best grant the user holds on the document. */
168
+ async #grantLevel(documentId, userAddress) {
169
+ return this.#permissions.getUserPermission(documentId, userAddress);
170
+ }
171
+ /**
172
+ * Walks the parent hierarchy (with cycle protection) looking for a grant of
173
+ * at least the required level on the document or any ancestor.
174
+ */
175
+ async #hasGrantInHierarchy(documentId, userAddress, required) {
176
+ const visited = /* @__PURE__ */ new Set();
177
+ const queue = [documentId];
178
+ while (queue.length > 0) {
179
+ const current = queue.shift();
180
+ if (visited.has(current)) continue;
181
+ visited.add(current);
182
+ if (satisfies(await this.#grantLevel(current, userAddress), required)) return true;
183
+ for (const parentId of await this.#getParentIds(current)) if (!visited.has(parentId)) queue.push(parentId);
184
+ }
185
+ return false;
94
186
  }
95
187
  };
96
188
  /**
97
189
  * Selects the strategy for the configured policy. The strategy classes are
98
190
  * not exported, so this guard is the only construction path.
99
191
  */
100
- function createAuthorizationService(config, documentPermissionService) {
192
+ function createAuthorizationService(config, documentPermissionService, getParentIds) {
101
193
  if (config.policy === AuthorizationPolicy.OPEN) return new OpenAuthorizationService(config);
102
194
  if (config.policy === AuthorizationPolicy.ADMIN_ONLY) return new AdminOnlyAuthorizationService(config);
103
195
  if (!documentPermissionService) throw new Error("DocumentPermissionService is required for the DOCUMENT_PERMISSIONS policy");
104
- return new DocumentPermissionsAuthorizationService(documentPermissionService, config);
196
+ if (!getParentIds) throw new Error("A getParentIds resolver is required for the DOCUMENT_PERMISSIONS policy");
197
+ return new DocumentPermissionsAuthorizationService(documentPermissionService, getParentIds, config);
105
198
  }
106
199
  //#endregion
107
200
  //#region src/graphql/base-subgraph.ts
@@ -120,6 +213,12 @@ var BaseSubgraph = class {
120
213
  syncManager;
121
214
  documentPermissionService;
122
215
  authorizationService;
216
+ /**
217
+ * Per-request memo of raw identifier to canonical document id, keyed on the
218
+ * request context object so entries are released when the request ends. Lets
219
+ * batch resolvers resolve each distinct identifier at most once.
220
+ */
221
+ #canonicalIdMemo = /* @__PURE__ */ new WeakMap();
123
222
  constructor(args) {
124
223
  this.reactorClient = args.reactorClient;
125
224
  this.graphqlManager = args.graphqlManager;
@@ -130,33 +229,100 @@ var BaseSubgraph = class {
130
229
  this.path = args.path ?? "";
131
230
  }
132
231
  async onSetup() {}
133
- getParentIdsFn() {
134
- return async (documentId) => {
135
- try {
136
- return (await this.reactorClient.getIncomingRelationships(documentId, "child")).results.map((doc) => doc.header.id);
137
- } catch {
138
- return [];
139
- }
232
+ /**
233
+ * Resolves a caller-supplied identifier (id or slug) to its canonical
234
+ * document id, memoized per request. Both the decision layer and the data
235
+ * layer must agree on the subject, so this runs the same resolveIdOrSlug
236
+ * lookup the data path uses. A resolution failure (not found, ambiguous, or
237
+ * transient) surfaces as a generic Forbidden, fail-closed, so a bad
238
+ * identifier cannot be used as a document-existence oracle.
239
+ */
240
+ async resolveCanonicalDocumentId(identifier, requestKey) {
241
+ let cache = this.#canonicalIdMemo.get(requestKey);
242
+ if (!cache) {
243
+ cache = /* @__PURE__ */ new Map();
244
+ this.#canonicalIdMemo.set(requestKey, cache);
245
+ }
246
+ const cached = cache.get(identifier);
247
+ if (cached !== void 0) return cached;
248
+ let resolved;
249
+ try {
250
+ resolved = await this.reactorClient.resolveIdOrSlug(identifier);
251
+ } catch {
252
+ throw new ForbiddenError();
253
+ }
254
+ const canonical = resolved;
255
+ cache.set(identifier, canonical);
256
+ return canonical;
257
+ }
258
+ /**
259
+ * Resolves the args' `documentId` to canonical form. Unconditional (unlike the
260
+ * assertCan* helpers, no admin skip): ACL rows are keyed on the canonical id.
261
+ */
262
+ async withCanonicalDocumentId(args, requestKey) {
263
+ const documentId = await this.resolveCanonicalDocumentId(args.documentId, requestKey);
264
+ return {
265
+ ...args,
266
+ documentId
140
267
  };
141
268
  }
269
+ /**
270
+ * Resolves an identifier for a per-document check, or null when the caller has
271
+ * policy-wide access (OPEN or supreme admin) and the check can be skipped.
272
+ * Only DOCUMENT_PERMISSIONS keys on the document id, so other policies fail
273
+ * closed without resolving.
274
+ */
275
+ async #resolveForCheck(identifier, ctx) {
276
+ if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return null;
277
+ if (this.authorizationService.config.policy !== AuthorizationPolicy.DOCUMENT_PERMISSIONS) throw new ForbiddenError();
278
+ return this.resolveCanonicalDocumentId(identifier, ctx);
279
+ }
280
+ /**
281
+ * Read filter for an already-canonical document id (one sourced from the data
282
+ * layer, such as a fetched document's id). Performs no slug resolution.
283
+ */
142
284
  async canReadDocument(documentId, ctx) {
143
- return this.authorizationService.canRead(documentId, ctx.user?.address, this.getParentIdsFn());
285
+ return this.authorizationService.canRead(documentId, ctx.user?.address);
286
+ }
287
+ /**
288
+ * Asserts read access, resolving a slug first. Returns a handle whose
289
+ * `fetchIdentifier` the caller reuses for the data fetch; a denial throws.
290
+ */
291
+ async assertCanRead(identifier, ctx) {
292
+ const documentId = await this.#resolveForCheck(identifier, ctx);
293
+ if (documentId === null) return AuthorizedDocumentHandle.skipped(identifier);
294
+ await this.assertCanReadCanonical(documentId, ctx);
295
+ return AuthorizedDocumentHandle.resolved(documentId);
296
+ }
297
+ async assertCanWrite(identifier, ctx) {
298
+ const documentId = await this.#resolveForCheck(identifier, ctx);
299
+ if (documentId === null) return AuthorizedDocumentHandle.skipped(identifier);
300
+ await this.assertCanWriteCanonical(documentId, ctx);
301
+ return AuthorizedDocumentHandle.resolved(documentId);
302
+ }
303
+ async assertCanExecuteOperation(identifier, operationType, ctx) {
304
+ const documentId = await this.#resolveForCheck(identifier, ctx);
305
+ if (documentId === null) return AuthorizedDocumentHandle.skipped(identifier);
306
+ await this.assertCanExecuteOperationCanonical(documentId, operationType, ctx);
307
+ return AuthorizedDocumentHandle.resolved(documentId);
144
308
  }
145
- async assertCanRead(documentId, ctx) {
146
- if (!await this.authorizationService.canRead(documentId, ctx.user?.address, this.getParentIdsFn())) throw new GraphQLError("Forbidden: insufficient permissions to read this document");
309
+ /**
310
+ * Read assertion for an already-canonical document id. No slug resolution;
311
+ * use only with ids sourced from the data layer or already resolved.
312
+ */
313
+ async assertCanReadCanonical(documentId, ctx) {
314
+ if (!await this.authorizationService.canRead(documentId, ctx.user?.address)) throw new ForbiddenError("to read this document");
147
315
  }
148
- async assertCanWrite(documentId, ctx) {
149
- if (!await this.authorizationService.canWrite(documentId, ctx.user?.address, this.getParentIdsFn())) throw new GraphQLError("Forbidden: insufficient permissions to write to this document");
316
+ async assertCanWriteCanonical(documentId, ctx) {
317
+ if (!await this.authorizationService.canWrite(documentId, ctx.user?.address)) throw new ForbiddenError("to write to this document");
150
318
  }
151
- async assertCanExecuteOperation(documentId, operationType, ctx) {
152
- if (!await this.authorizationService.canMutate(documentId, operationType, ctx.user?.address, this.getParentIdsFn())) throw new GraphQLError(`Forbidden: insufficient permissions to execute operation "${operationType}" on this document`);
319
+ async assertCanExecuteOperationCanonical(documentId, operationType, ctx) {
320
+ if (!await this.authorizationService.canMutate(documentId, operationType, ctx.user?.address)) throw new ForbiddenError(`to execute operation "${operationType}" on this document`);
153
321
  }
154
322
  assertCanCreate(ctx) {
155
- const policy = this.authorizationService.config.policy;
156
- if (policy === AuthorizationPolicy.OPEN) return;
157
- if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return;
158
- if (policy === AuthorizationPolicy.ADMIN_ONLY) throw new GraphQLError("Forbidden: insufficient permissions to create documents");
159
- if (!ctx.user?.address) throw new GraphQLError("Forbidden: authentication required to create documents");
323
+ if (this.authorizationService.canCreate(ctx.user?.address)) return;
324
+ if (ctx.user?.address) throw new ForbiddenError("to create documents");
325
+ throw new AuthenticationRequiredError("to create documents");
160
326
  }
161
327
  };
162
328
  //#endregion
@@ -371,7 +537,7 @@ function buildGraphQlDriveDocument(doc) {
371
537
  };
372
538
  }
373
539
  //#endregion
374
- export { isSubgraphClass as a, loadDocumentModels as c, BaseSubgraph as d, AuthorizationPolicy as f, buildGraphqlOperations as i, loadProcessors as l, buildGraphQlDriveDocument as n, debounce as o, createAuthorizationService as p, buildGraphqlOperation as r, isSubpath as s, buildGraphQlDocument as t, loadSubgraphs as u };
540
+ export { isSubgraphClass as a, loadDocumentModels as c, BaseSubgraph as d, AuthorizationPolicy as f, buildGraphqlOperations as i, loadProcessors as l, createAuthorizationService as m, buildGraphQlDriveDocument as n, debounce as o, AuthorizedDocumentHandle as p, buildGraphqlOperation as r, isSubpath as s, buildGraphQlDocument as t, loadSubgraphs as u };
375
541
 
376
- //# sourceMappingURL=utils-CtC8sjRo.mjs.map
377
- //# debugId=143f3840-f36d-5b22-91f1-5b5328f2e537
542
+ //# sourceMappingURL=utils-iibOQ50e.mjs.map
543
+ //# debugId=bb45ff30-0d48-5614-9063-64b33d486341
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-iibOQ50e.mjs","sources":["../src/graphql/errors.ts","../src/services/authorization.service.ts","../src/graphql/base-subgraph.ts","../src/packages/import-resolver.ts","../src/packages/util.ts","../src/graphql/utils.ts"],"sourcesContent":["import { GraphQLError } from \"graphql\";\nimport { DRIVE_AUTH_ERROR_MESSAGES } from \"@powerhousedao/reactor\";\n\n/** Caller (authenticated or anonymous) lacks permission for the document. */\nexport class ForbiddenError extends GraphQLError {\n constructor(detail = \"\") {\n const base = DRIVE_AUTH_ERROR_MESSAGES.forbidden;\n super(`${base}${detail ? ` ${detail}` : \"\"}`, {\n extensions: { code: \"FORBIDDEN\" },\n });\n }\n}\n\n/** Anonymous caller on an action that requires logging in. */\nexport class AuthenticationRequiredError extends GraphQLError {\n constructor(detail = \"\") {\n const base = DRIVE_AUTH_ERROR_MESSAGES.authenticationRequired;\n super(`${base}${detail ? ` ${detail}` : \"\"}`, {\n extensions: { code: \"UNAUTHENTICATED\" },\n });\n }\n}\n","import type { DocumentPermissionLevel } from \"../utils/db.js\";\nimport type {\n DocumentPermissionService,\n GetParentIdsFn,\n} from \"./document-permission.service.js\";\n\n/**\n * A document id that has been resolved to its canonical form, never a slug.\n *\n * Protection and grant rows are written under the canonical id, while the\n * read/operation data paths accept an id-or-slug identifier. The decision layer\n * must key on the canonical id, so branding it forces every caller to resolve a\n * slug to its id before consulting this service, closing the slug-aliasing\n * bypass (S-C1). The only sanctioned cast from string to CanonicalDocumentId\n * lives in the BaseSubgraph resolution helpers, immediately after the shared\n * resolveIdOrSlug lookup returns.\n */\nexport type CanonicalDocumentId = string & {\n readonly __canonicalDocumentId: unique symbol;\n};\n\n/**\n * Result of a passed per-document authorization check. `fetchIdentifier` is\n * always safe for the data fetch: the canonical id when resolved, or the raw\n * identifier when the check was skipped for a policy-wide caller.\n */\nexport class AuthorizedDocumentHandle {\n private constructor(\n readonly fetchIdentifier: string,\n readonly isResolved: boolean,\n ) {}\n\n static resolved(documentId: CanonicalDocumentId): AuthorizedDocumentHandle {\n return new AuthorizedDocumentHandle(documentId, true);\n }\n\n static skipped(identifier: string): AuthorizedDocumentHandle {\n return new AuthorizedDocumentHandle(identifier, false);\n }\n\n /** The verified canonical id; throws when resolution was skipped. */\n canonicalId(): CanonicalDocumentId {\n if (!this.isResolved) {\n throw new Error(\n \"Canonical document id is unavailable: the authorization check was skipped for a policy-wide caller\",\n );\n }\n return this.fetchIdentifier as CanonicalDocumentId;\n }\n}\n\nexport const AuthorizationPolicy = {\n OPEN: \"OPEN\",\n ADMIN_ONLY: \"ADMIN_ONLY\",\n DOCUMENT_PERMISSIONS: \"DOCUMENT_PERMISSIONS\",\n} as const;\n\nexport type AuthorizationPolicy =\n (typeof AuthorizationPolicy)[keyof typeof AuthorizationPolicy];\n\nexport interface AuthorizationConfig {\n admins: string[];\n defaultProtection: boolean;\n policy: AuthorizationPolicy;\n}\n\n/**\n * Single source of truth for every permission decision. Always present (never\n * null) so callers branch on data, not on the existence of a service.\n *\n * The policy selects an implementation once at boot:\n * - OPEN: authentication disabled — everyone (incl. anonymous) is allowed.\n * - ADMIN_ONLY: authentication on, document permissions off — only ADMINS.\n * - DOCUMENT_PERMISSIONS: the full per-document protection + grant model.\n *\n * Permission inheritance walks the parent-document hierarchy through a\n * parent resolver injected at construction; callers never supply one.\n */\nexport interface IAuthorizationService {\n readonly config: AuthorizationConfig;\n\n /**\n * Whether the user has unrestricted, policy-wide access. Under OPEN this is\n * true for everyone (including anonymous callers) by design: OPEN means \"no\n * restrictions\", and consumers use this check to skip per-document\n * filtering. It does NOT mean the caller is in the ADMINS list.\n */\n isSupremeAdmin(userAddress?: string): boolean;\n\n /**\n * Whether the user may create new documents under the current policy:\n * everyone in OPEN, only admins in ADMIN_ONLY, any authenticated user in\n * DOCUMENT_PERMISSIONS.\n */\n canCreate(userAddress?: string): boolean;\n\n canRead(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n canWrite(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n /**\n * Whether the user administers the document: supreme admin, document\n * owner, or holder of an ADMIN grant.\n */\n canManage(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n canMutate(\n documentId: CanonicalDocumentId,\n operationType: string,\n userAddress?: string,\n ): Promise<boolean>;\n}\n\n/** Shared config holder and admin-list check for the policy strategies. */\nabstract class BaseAuthorizationService implements IAuthorizationService {\n constructor(readonly config: AuthorizationConfig) {}\n\n isSupremeAdmin(userAddress?: string): boolean {\n if (!userAddress) return false;\n return this.config.admins.includes(userAddress.toLowerCase());\n }\n\n abstract canCreate(userAddress?: string): boolean;\n\n abstract canRead(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n abstract canWrite(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n abstract canManage(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean>;\n\n abstract canMutate(\n documentId: CanonicalDocumentId,\n operationType: string,\n userAddress?: string,\n ): Promise<boolean>;\n}\n\n/** OPEN: authentication disabled — everyone (incl. anonymous) is allowed. */\nclass OpenAuthorizationService extends BaseAuthorizationService {\n isSupremeAdmin(): boolean {\n return true;\n }\n\n canCreate(): boolean {\n return true;\n }\n\n canRead(): Promise<boolean> {\n return Promise.resolve(true);\n }\n\n canWrite(): Promise<boolean> {\n return Promise.resolve(true);\n }\n\n canManage(): Promise<boolean> {\n return Promise.resolve(true);\n }\n\n canMutate(): Promise<boolean> {\n return Promise.resolve(true);\n }\n}\n\n/** ADMIN_ONLY: authentication on, document permissions off — only ADMINS. */\nclass AdminOnlyAuthorizationService extends BaseAuthorizationService {\n canCreate(userAddress?: string): boolean {\n return this.isSupremeAdmin(userAddress);\n }\n\n canRead(\n _documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return Promise.resolve(this.isSupremeAdmin(userAddress));\n }\n\n canWrite(\n _documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return Promise.resolve(this.isSupremeAdmin(userAddress));\n }\n\n canManage(\n _documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return Promise.resolve(this.isSupremeAdmin(userAddress));\n }\n\n canMutate(\n _documentId: CanonicalDocumentId,\n _operationType: string,\n userAddress?: string,\n ): Promise<boolean> {\n return Promise.resolve(this.isSupremeAdmin(userAddress));\n }\n}\n\nconst PERMISSION_RANK: Record<DocumentPermissionLevel, number> = {\n READ: 1,\n WRITE: 2,\n ADMIN: 3,\n};\n\nfunction satisfies(\n level: DocumentPermissionLevel | null,\n required: DocumentPermissionLevel,\n): boolean {\n return level !== null && PERMISSION_RANK[level] >= PERMISSION_RANK[required];\n}\n\n/**\n * DOCUMENT_PERMISSIONS: the full per-document protection + grant model.\n *\n * All decisions live here; DocumentPermissionService is the data-access\n * layer underneath (grants, protection rows, owners).\n */\nclass DocumentPermissionsAuthorizationService extends BaseAuthorizationService {\n readonly #permissions: DocumentPermissionService;\n readonly #getParentIds: GetParentIdsFn;\n\n constructor(\n permissions: DocumentPermissionService,\n getParentIds: GetParentIdsFn,\n config: AuthorizationConfig,\n ) {\n super(config);\n this.#permissions = permissions;\n this.#getParentIds = getParentIds;\n }\n\n canCreate(userAddress?: string): boolean {\n return this.isSupremeAdmin(userAddress) || !!userAddress;\n }\n\n canRead(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return this.#canAccess(documentId, \"READ\", userAddress);\n }\n\n canWrite(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return this.#canAccess(documentId, \"WRITE\", userAddress);\n }\n\n canManage(\n documentId: CanonicalDocumentId,\n userAddress?: string,\n ): Promise<boolean> {\n return this.#isDocumentAdmin(documentId, userAddress);\n }\n\n async canMutate(\n documentId: CanonicalDocumentId,\n operationType: string,\n userAddress?: string,\n ): Promise<boolean> {\n if (this.isSupremeAdmin(userAddress)) return true;\n\n const isRestricted = await this.#permissions.isOperationRestricted(\n documentId,\n operationType,\n );\n if (isRestricted) {\n if (!userAddress) return false;\n if (await this.#isDocumentAdmin(documentId, userAddress)) return true;\n return this.#permissions.hasOperationGrant(\n documentId,\n operationType,\n userAddress,\n );\n }\n\n return this.#canAccess(documentId, \"WRITE\", userAddress);\n }\n\n /**\n * The one \"administers this document\" predicate: supreme admin, document\n * owner, or ADMIN grant on the document itself.\n */\n async #isDocumentAdmin(\n documentId: string,\n userAddress?: string,\n ): Promise<boolean> {\n if (this.isSupremeAdmin(userAddress)) return true;\n if (!userAddress) return false;\n\n const owner = await this.#permissions.getDocumentOwner(documentId);\n if (owner && owner === userAddress.toLowerCase()) return true;\n\n return satisfies(await this.#grantLevel(documentId, userAddress), \"ADMIN\");\n }\n\n /**\n * The one read/write decision shape: supreme admin → unprotected (self or\n * ancestor) → owner → inherited grant of at least the required level.\n */\n async #canAccess(\n documentId: string,\n required: DocumentPermissionLevel,\n userAddress?: string,\n ): Promise<boolean> {\n if (this.isSupremeAdmin(userAddress)) return true;\n\n const isProtected = await this.#permissions.isProtectedWithAncestors(\n documentId,\n this.#getParentIds,\n );\n if (!isProtected) return true;\n if (!userAddress) return false;\n\n const owner = await this.#permissions.getDocumentOwner(documentId);\n if (owner && owner === userAddress.toLowerCase()) return true;\n\n return this.#hasGrantInHierarchy(documentId, userAddress, required);\n }\n\n /** Best grant the user holds on the document. */\n async #grantLevel(\n documentId: string,\n userAddress: string,\n ): Promise<DocumentPermissionLevel | null> {\n return this.#permissions.getUserPermission(documentId, userAddress);\n }\n\n /**\n * Walks the parent hierarchy (with cycle protection) looking for a grant of\n * at least the required level on the document or any ancestor.\n */\n async #hasGrantInHierarchy(\n documentId: string,\n userAddress: string,\n required: DocumentPermissionLevel,\n ): Promise<boolean> {\n const visited = new Set<string>();\n const queue = [documentId];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (visited.has(current)) continue;\n visited.add(current);\n\n const level = await this.#grantLevel(current, userAddress);\n if (satisfies(level, required)) return true;\n\n for (const parentId of await this.#getParentIds(current)) {\n if (!visited.has(parentId)) queue.push(parentId);\n }\n }\n\n return false;\n }\n}\n\n/**\n * Selects the strategy for the configured policy. The strategy classes are\n * not exported, so this guard is the only construction path.\n */\nexport function createAuthorizationService(\n config: AuthorizationConfig,\n documentPermissionService?: DocumentPermissionService,\n getParentIds?: GetParentIdsFn,\n): IAuthorizationService {\n if (config.policy === AuthorizationPolicy.OPEN) {\n return new OpenAuthorizationService(config);\n }\n if (config.policy === AuthorizationPolicy.ADMIN_ONLY) {\n return new AdminOnlyAuthorizationService(config);\n }\n if (!documentPermissionService) {\n throw new Error(\n \"DocumentPermissionService is required for the DOCUMENT_PERMISSIONS policy\",\n );\n }\n if (!getParentIds) {\n throw new Error(\n \"A getParentIds resolver is required for the DOCUMENT_PERMISSIONS policy\",\n );\n }\n return new DocumentPermissionsAuthorizationService(\n documentPermissionService,\n getParentIds,\n config,\n );\n}\n","import type {\n IReactorClient,\n IRelationalDb,\n ISyncManager,\n} from \"@powerhousedao/reactor\";\nimport type {\n GraphQLManager,\n ISubgraph,\n SubgraphArgs,\n} from \"@powerhousedao/reactor-api\";\nimport type { DocumentNode } from \"graphql\";\nimport { ForbiddenError, AuthenticationRequiredError } from \"./errors.js\";\nimport { gql } from \"graphql-tag\";\nimport {\n AuthorizationPolicy,\n AuthorizedDocumentHandle,\n type CanonicalDocumentId,\n type IAuthorizationService,\n} from \"../services/authorization.service.js\";\nimport type { DocumentPermissionService } from \"../services/document-permission.service.js\";\nimport type { Context } from \"./types.js\";\n\nexport class BaseSubgraph implements ISubgraph {\n name = \"example\";\n path = \"\";\n resolvers: Record<string, any> = {\n Query: {\n hello: () => this.name,\n },\n };\n typeDefs: DocumentNode = gql`\n type Query {\n hello: String\n }\n `;\n reactorClient: IReactorClient;\n graphqlManager: GraphQLManager;\n relationalDb: IRelationalDb;\n syncManager: ISyncManager;\n documentPermissionService?: DocumentPermissionService;\n authorizationService: IAuthorizationService;\n\n /**\n * Per-request memo of raw identifier to canonical document id, keyed on the\n * request context object so entries are released when the request ends. Lets\n * batch resolvers resolve each distinct identifier at most once.\n */\n readonly #canonicalIdMemo = new WeakMap<\n object,\n Map<string, CanonicalDocumentId>\n >();\n\n constructor(args: SubgraphArgs) {\n this.reactorClient = args.reactorClient;\n this.graphqlManager = args.graphqlManager;\n this.relationalDb = args.relationalDb;\n this.syncManager = args.syncManager;\n this.documentPermissionService = args.documentPermissionService;\n this.authorizationService = args.authorizationService;\n this.path = args.path ?? \"\";\n }\n\n async onSetup() {\n // noop\n }\n\n // ============================================\n // Shared permission helpers\n // ============================================\n\n /**\n * Resolves a caller-supplied identifier (id or slug) to its canonical\n * document id, memoized per request. Both the decision layer and the data\n * layer must agree on the subject, so this runs the same resolveIdOrSlug\n * lookup the data path uses. A resolution failure (not found, ambiguous, or\n * transient) surfaces as a generic Forbidden, fail-closed, so a bad\n * identifier cannot be used as a document-existence oracle.\n */\n async resolveCanonicalDocumentId(\n identifier: string,\n requestKey: object,\n ): Promise<CanonicalDocumentId> {\n let cache = this.#canonicalIdMemo.get(requestKey);\n if (!cache) {\n cache = new Map<string, CanonicalDocumentId>();\n this.#canonicalIdMemo.set(requestKey, cache);\n }\n\n const cached = cache.get(identifier);\n if (cached !== undefined) return cached;\n\n let resolved: string;\n try {\n resolved = await this.reactorClient.resolveIdOrSlug(identifier);\n } catch {\n throw new ForbiddenError();\n }\n\n const canonical = resolved as CanonicalDocumentId;\n cache.set(identifier, canonical);\n return canonical;\n }\n\n /**\n * Resolves the args' `documentId` to canonical form. Unconditional (unlike the\n * assertCan* helpers, no admin skip): ACL rows are keyed on the canonical id.\n */\n async withCanonicalDocumentId<T extends { documentId: string }>(\n args: T,\n requestKey: object,\n ): Promise<T & { documentId: CanonicalDocumentId }> {\n const documentId = await this.resolveCanonicalDocumentId(\n args.documentId,\n requestKey,\n );\n return { ...args, documentId };\n }\n\n /**\n * Resolves an identifier for a per-document check, or null when the caller has\n * policy-wide access (OPEN or supreme admin) and the check can be skipped.\n * Only DOCUMENT_PERMISSIONS keys on the document id, so other policies fail\n * closed without resolving.\n */\n async #resolveForCheck(\n identifier: string,\n ctx: Context,\n ): Promise<CanonicalDocumentId | null> {\n if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) {\n return null;\n }\n if (\n this.authorizationService.config.policy !==\n AuthorizationPolicy.DOCUMENT_PERMISSIONS\n ) {\n throw new ForbiddenError();\n }\n return this.resolveCanonicalDocumentId(identifier, ctx);\n }\n\n /**\n * Read filter for an already-canonical document id (one sourced from the data\n * layer, such as a fetched document's id). Performs no slug resolution.\n */\n async canReadDocument(\n documentId: CanonicalDocumentId,\n ctx: Context,\n ): Promise<boolean> {\n return this.authorizationService.canRead(documentId, ctx.user?.address);\n }\n\n /**\n * Asserts read access, resolving a slug first. Returns a handle whose\n * `fetchIdentifier` the caller reuses for the data fetch; a denial throws.\n */\n async assertCanRead(\n identifier: string,\n ctx: Context,\n ): Promise<AuthorizedDocumentHandle> {\n const documentId = await this.#resolveForCheck(identifier, ctx);\n if (documentId === null)\n return AuthorizedDocumentHandle.skipped(identifier);\n await this.assertCanReadCanonical(documentId, ctx);\n return AuthorizedDocumentHandle.resolved(documentId);\n }\n\n async assertCanWrite(\n identifier: string,\n ctx: Context,\n ): Promise<AuthorizedDocumentHandle> {\n const documentId = await this.#resolveForCheck(identifier, ctx);\n if (documentId === null)\n return AuthorizedDocumentHandle.skipped(identifier);\n await this.assertCanWriteCanonical(documentId, ctx);\n return AuthorizedDocumentHandle.resolved(documentId);\n }\n\n async assertCanExecuteOperation(\n identifier: string,\n operationType: string,\n ctx: Context,\n ): Promise<AuthorizedDocumentHandle> {\n const documentId = await this.#resolveForCheck(identifier, ctx);\n if (documentId === null)\n return AuthorizedDocumentHandle.skipped(identifier);\n await this.assertCanExecuteOperationCanonical(\n documentId,\n operationType,\n ctx,\n );\n return AuthorizedDocumentHandle.resolved(documentId);\n }\n\n /**\n * Read assertion for an already-canonical document id. No slug resolution;\n * use only with ids sourced from the data layer or already resolved.\n */\n async assertCanReadCanonical(\n documentId: CanonicalDocumentId,\n ctx: Context,\n ): Promise<void> {\n const canRead = await this.authorizationService.canRead(\n documentId,\n ctx.user?.address,\n );\n if (!canRead) {\n throw new ForbiddenError(\"to read this document\");\n }\n }\n\n async assertCanWriteCanonical(\n documentId: CanonicalDocumentId,\n ctx: Context,\n ): Promise<void> {\n const canWrite = await this.authorizationService.canWrite(\n documentId,\n ctx.user?.address,\n );\n if (!canWrite) {\n throw new ForbiddenError(\"to write to this document\");\n }\n }\n\n async assertCanExecuteOperationCanonical(\n documentId: CanonicalDocumentId,\n operationType: string,\n ctx: Context,\n ): Promise<void> {\n const canMutate = await this.authorizationService.canMutate(\n documentId,\n operationType,\n ctx.user?.address,\n );\n if (!canMutate) {\n throw new ForbiddenError(\n `to execute operation \"${operationType}\" on this document`,\n );\n }\n }\n\n assertCanCreate(ctx: Context): void {\n if (this.authorizationService.canCreate(ctx.user?.address)) return;\n if (ctx.user?.address) {\n throw new ForbiddenError(\"to create documents\");\n }\n throw new AuthenticationRequiredError(\"to create documents\");\n }\n}\n","import path from \"node:path\";\n\n/**\n * Attempts to import from suggested Node.js paths\n */\nasync function tryNodeSuggestedPaths<T>(\n packageName: string,\n subPath: string,\n): Promise<T | null> {\n const suggestedPaths = [\n `${packageName}/dist/node/${subPath}/index.mjs`,\n `${packageName}/dist/node/${subPath}.mjs`,\n `${packageName}/dist/${subPath}/index.js`,\n `${packageName}/dist/${subPath}.js`,\n ];\n\n for (const suggestedPath of suggestedPaths) {\n try {\n return (await import(/* @vite-ignore */ suggestedPath)) as T;\n } catch {\n // Continue to next attempt\n }\n }\n\n return null;\n}\n\n/**\n * Attempts to resolve package using import.meta.resolve\n */\nasync function tryImportMetaResolve<T>(\n packageName: string,\n subPath: string,\n): Promise<T | null> {\n try {\n const resolvedUrl = import.meta.resolve?.(`${packageName}/package.json`);\n if (!resolvedUrl) return null;\n\n const packageRoot = path.dirname(new URL(resolvedUrl).pathname);\n const pathsToTry = [\n path.join(packageRoot, \"dist\", \"node\", subPath, \"index.mjs\"),\n path.join(packageRoot, \"dist\", \"node\", `${subPath}.mjs`),\n path.join(packageRoot, \"dist\", subPath, \"index.js\"),\n path.join(packageRoot, \"dist\", `${subPath}.js`),\n path.join(packageRoot, subPath, \"index.js\"),\n path.join(packageRoot, `${subPath}.js`),\n ];\n\n for (const attemptPath of pathsToTry) {\n try {\n return (await import(/* @vite-ignore */ attemptPath)) as T;\n } catch {\n // Continue to next attempt\n }\n }\n } catch {\n // import.meta.resolve failed\n }\n\n return null;\n}\n\n/**\n * Resolves symlinks in node_modules to find the real package location\n */\nasync function resolveSymlinkedPaths(\n packageName: string,\n subPath: string,\n): Promise<string[]> {\n const packageBaseName = packageName.includes(\"/\")\n ? packageName.split(\"/\").pop()\n : packageName;\n const nodeModulesPatterns = [\n path.join(process.cwd(), \"node_modules\", packageName),\n path.join(process.cwd(), \"node_modules\", packageBaseName || packageName),\n ];\n\n const workspacePatterns: string[] = [];\n\n for (const nodeModulesPath of nodeModulesPatterns) {\n try {\n const fs = await import(\"node:fs\");\n if (fs.existsSync(nodeModulesPath)) {\n const realPath = fs.realpathSync(nodeModulesPath);\n\n workspacePatterns.push(\n path.join(realPath, \"dist\", \"node\", subPath, \"index.mjs\"),\n path.join(realPath, \"dist\", \"node\", `${subPath}.mjs`),\n path.join(realPath, \"dist\", subPath, \"index.js\"),\n path.join(realPath, \"dist\", `${subPath}.js`),\n path.join(realPath, subPath, \"index.js\"),\n path.join(realPath, `${subPath}.js`),\n );\n }\n } catch {\n // Continue to next attempt\n }\n }\n\n return workspacePatterns;\n}\n\n/**\n * Generates common workspace pattern paths\n */\nfunction getCommonWorkspacePaths(\n packageName: string,\n subPath: string,\n): string[] {\n const packageBaseName = packageName.includes(\"/\")\n ? packageName.split(\"/\").pop()\n : packageName;\n const commonRoots = [process.cwd(), path.dirname(process.cwd())];\n\n const workspacePatterns: string[] = [];\n for (const root of commonRoots) {\n workspacePatterns.push(\n path.join(\n root,\n \"packages\",\n packageBaseName || packageName,\n \"dist\",\n \"node\",\n subPath,\n \"index.mjs\",\n ),\n path.join(\n root,\n \"packages\",\n packageBaseName || packageName,\n \"dist\",\n \"node\",\n `${subPath}.mjs`,\n ),\n path.join(\n root,\n \"packages\",\n packageBaseName || packageName,\n \"dist\",\n subPath,\n \"index.js\",\n ),\n path.join(\n root,\n \"packages\",\n packageBaseName || packageName,\n \"dist\",\n `${subPath}.js`,\n ),\n );\n }\n\n return workspacePatterns;\n}\n\n/**\n * Attempts to import from a list of workspace patterns\n */\nasync function tryWorkspacePatterns<T>(patterns: string[]): Promise<T | null> {\n for (const workspacePath of patterns) {\n try {\n return (await import(/* @vite-ignore */ workspacePath)) as T;\n } catch {\n // Continue to next attempt\n }\n }\n\n return null;\n}\n\n/**\n * Attempts to resolve linked packages using various fallback strategies\n */\nexport async function resolveLinkedPackage<T>(\n packageName: string,\n subPath: string,\n): Promise<T | null> {\n // Try Node.js suggested paths first\n let result = await tryNodeSuggestedPaths<T>(packageName, subPath);\n if (result) return result;\n\n // Try import.meta.resolve\n result = await tryImportMetaResolve<T>(packageName, subPath);\n if (result) return result;\n\n // Try symlink resolution\n const symlinkPaths = await resolveSymlinkedPaths(packageName, subPath);\n result = await tryWorkspacePatterns<T>(symlinkPaths);\n if (result) return result;\n\n // Try common workspace patterns as final fallback\n const commonPaths = getCommonWorkspacePaths(packageName, subPath);\n result = await tryWorkspacePatterns<T>(commonPaths);\n if (result) return result;\n\n return null;\n}\n","import type {\n ProcessorFactoryBuilder,\n SubgraphClass,\n} from \"@powerhousedao/reactor-api\";\nimport type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { childLogger } from \"document-model\";\nimport { execSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { resolveLinkedPackage } from \"./import-resolver.js\";\n\n// Define the expected module export structures\ntype DocumentModelsExport = Record<string, DocumentModelModule>;\ntype SubgraphsExport = Record<string, Record<string, SubgraphClass>>;\ntype ProcessorsExport = {\n processorFactory?: ProcessorFactoryBuilder;\n processorFactoryLegacy?: ProcessorFactoryBuilder;\n};\n\nconst _logger = childLogger([\"reactor-api\", \"packages/util\"]);\n\nexport const installPackages = (packages: string[]): Promise<void> => {\n for (const packageName of packages) {\n execSync(`ph install ${packageName}`);\n }\n return Promise.resolve();\n};\n\nexport const readManifest = () => {\n const manifest = execSync(`ph manifest`).toString();\n return manifest;\n};\n\n/**\n * Tries to import document models from a package. This function cannot throw.\n */\nexport async function loadDocumentModels(\n packageName: string,\n): Promise<DocumentModelsExport | null> {\n return loadDependency(packageName, \"document-models\");\n}\n\n/**\n * Tries to import subgraphs from a package. This function cannot throw.\n */\nexport async function loadSubgraphs(\n packageName: string,\n): Promise<SubgraphsExport | null> {\n return loadDependency(packageName, \"subgraphs\");\n}\n\n/**\n * Tries to import processors from a package. This function cannot throw.\n */\nexport async function loadProcessors(\n packageName: string,\n): Promise<ProcessorsExport | null> {\n return loadDependency(packageName, \"processors\");\n}\n\n/**\n * Generic dependency loader - tries to import a dependency from a package. This function cannot throw.\n * Returns null if the dependency cannot be loaded.\n */\nasync function loadDependency<T = unknown>(\n packageName: string,\n subPath: string,\n): Promise<T> {\n const fullPath = `${packageName}/${subPath}`;\n\n // Try the standard import first\n try {\n // vite does not support this, but that's okay as we have provided the\n // vite-loader for this purpose\n\n const module = (await import(/* @vite-ignore */ fullPath)) as T;\n return module;\n } catch (e) {\n // Handle module not found errors with fallback resolution\n if (\n e instanceof Error &&\n \"code\" in e &&\n (e.code === \"ERR_MODULE_NOT_FOUND\" ||\n e.code === \"ERR_UNSUPPORTED_DIR_IMPORT\")\n ) {\n const result = await resolveLinkedPackage<T>(packageName, subPath);\n if (result) return result;\n }\n throw e;\n }\n}\n\nexport function debounce<T extends unknown[], R>(\n func: (...args: T) => Promise<R>,\n delay = 250,\n) {\n let timer: number;\n return (immediate = false, ...args: T) => {\n if (timer) {\n clearTimeout(timer);\n }\n return new Promise<R>((resolve, reject) => {\n if (immediate) {\n func(...args)\n .then(resolve)\n .catch(reject);\n } else {\n timer = setTimeout(() => {\n func(...args)\n .then(resolve)\n .catch(reject);\n }, delay) as unknown as number;\n }\n });\n };\n}\n\nexport function isSubpath(parent: string, dir: string) {\n const relative = path.relative(parent, dir);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n}\n","import type {\n GqlDocument,\n GqlDriveDocument,\n GqlOperation,\n SubgraphClass,\n} from \"@powerhousedao/reactor-api\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport type {\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { BaseSubgraph } from \"./base-subgraph.js\";\n\nexport function isSubgraphClass(\n candidate: unknown,\n): candidate is SubgraphClass {\n if (typeof candidate !== \"function\") return false;\n\n let proto: unknown = Object.getPrototypeOf(candidate);\n while (proto) {\n if (Object.prototype.isPrototypeOf.call(proto, BaseSubgraph)) return true;\n\n proto = Object.getPrototypeOf(proto);\n }\n\n return false;\n}\n\nexport function buildGraphqlOperations(\n operations: Operation[],\n skip: number,\n first: number,\n): GqlOperation[] {\n return operations.slice(skip, skip + first).map(buildGraphqlOperation);\n}\n\nexport function buildGraphqlOperation(operation: Operation): GqlOperation {\n const signer = operation.action.context?.signer;\n return {\n id: operation.id ?? \"\",\n type: operation.action.type,\n index: operation.index,\n timestampUtcMs: operation.timestampUtcMs,\n hash: operation.hash,\n skip: operation.skip,\n inputText:\n typeof operation.action.input === \"string\"\n ? operation.action.input\n : JSON.stringify(operation.action.input),\n error: operation.error,\n context: {\n signer: signer\n ? {\n user: signer.user,\n app: signer.app,\n signatures: signer.signatures.map((sig) =>\n Array.isArray(sig) ? sig.join(\", \") : sig,\n ),\n }\n : undefined,\n },\n };\n}\n\nexport function buildGraphQlDocument(doc: PHDocument): GqlDocument {\n // Return full state with all scopes (auth, document, global, local)\n // This matches the ReactorSubgraph pattern in adapters.ts\n const state = doc.state;\n const initialState = doc.initialState;\n // For stateJSON, use global state for backward compatibility\n const globalState = \"global\" in doc.state ? doc.state.global : {};\n return {\n id: doc.header.id,\n name: doc.header.name,\n documentType: doc.header.documentType,\n revision: doc.header.revision.global || 0,\n createdAtUtcIso: doc.header.createdAtUtcIso,\n lastModifiedAtUtcIso: doc.header.lastModifiedAtUtcIso,\n operations: [],\n stateJSON: globalState as JSON,\n state,\n initialState,\n };\n}\n\nexport function buildGraphQlDriveDocument(\n doc: DocumentDriveDocument,\n): GqlDriveDocument {\n const gqlDoc = buildGraphQlDocument(doc);\n return {\n ...gqlDoc,\n meta: {\n preferredEditor: doc.header.meta?.preferredEditor,\n },\n slug: doc.header.slug,\n state: doc.state.global,\n initialState: doc.state.global,\n };\n}\n"],"names":["#permissions","#getParentIds","#canAccess","#isDocumentAdmin","#grantLevel","#hasGrantInHierarchy","#canonicalIdMemo","#resolveForCheck"],"mappings":"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","debug_id":"bb45ff30-0d48-5614-9063-64b33d486341"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@powerhousedao/reactor-api",
3
- "version": "6.2.0-dev.9",
4
- "description": "",
3
+ "version": "6.2.0-rc.0",
4
+ "description": "GraphQL API layer for the Powerhouse reactor — auto-generated subgraphs per document model plus the core reactor subgraph for drive and document management.",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/powerhouse-inc/powerhouse"
8
+ "url": "https://github.com/powerhouse-inc/powerhouse",
9
+ "directory": "packages/reactor-api"
9
10
  },
10
11
  "exports": {
11
12
  ".": {
@@ -35,7 +36,17 @@
35
36
  "publishConfig": {
36
37
  "access": "public"
37
38
  },
38
- "keywords": [],
39
+ "keywords": [
40
+ "powerhouse",
41
+ "vetra",
42
+ "document-model",
43
+ "local-first",
44
+ "event-sourcing",
45
+ "reactor",
46
+ "graphql",
47
+ "subgraph",
48
+ "api"
49
+ ],
39
50
  "author": "",
40
51
  "license": "AGPL-3.0-only",
41
52
  "dependencies": {
@@ -65,18 +76,18 @@
65
76
  "read-pkg": "10.1.0",
66
77
  "ws": "^8.18.3",
67
78
  "zod": "4.3.6",
68
- "@powerhousedao/analytics-engine-core": "6.2.0-dev.9",
69
- "@powerhousedao/analytics-engine-graphql": "6.2.0-dev.9",
70
- "@powerhousedao/config": "6.2.0-dev.9",
71
- "@powerhousedao/analytics-engine-pg": "6.2.0-dev.9",
72
- "@powerhousedao/pglite-fs": "6.2.0-dev.9",
73
- "@powerhousedao/reactor": "6.2.0-dev.9",
74
- "@powerhousedao/reactor-attachments": "6.2.0-dev.9",
75
- "@powerhousedao/reactor-drive": "6.2.0-dev.9",
76
- "@powerhousedao/shared": "6.2.0-dev.9",
77
- "document-model": "6.2.0-dev.9",
78
- "@renown/sdk": "6.2.0-dev.9",
79
- "@powerhousedao/reactor-mcp": "6.2.0-dev.9"
79
+ "@powerhousedao/analytics-engine-core": "6.2.0-rc.0",
80
+ "@powerhousedao/analytics-engine-pg": "6.2.0-rc.0",
81
+ "@powerhousedao/config": "6.2.0-rc.0",
82
+ "@powerhousedao/pglite-fs": "6.2.0-rc.0",
83
+ "@powerhousedao/analytics-engine-graphql": "6.2.0-rc.0",
84
+ "@powerhousedao/reactor-attachments": "6.2.0-rc.0",
85
+ "@powerhousedao/reactor-drive": "6.2.0-rc.0",
86
+ "@powerhousedao/reactor-mcp": "6.2.0-rc.0",
87
+ "@powerhousedao/shared": "6.2.0-rc.0",
88
+ "@renown/sdk": "6.2.0-rc.0",
89
+ "document-model": "6.2.0-rc.0",
90
+ "@powerhousedao/reactor": "6.2.0-rc.0"
80
91
  },
81
92
  "devDependencies": {
82
93
  "@fastify/cors": "^11.0.1",
@@ -136,6 +147,7 @@
136
147
  "optional": true
137
148
  }
138
149
  },
150
+ "homepage": "https://academy.vetra.io",
139
151
  "scripts": {
140
152
  "tsc": "tsc",
141
153
  "build": "tsdown",
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-gateway-apollo-IZuGzFaY.mjs","sources":["../src/graphql/gateway/adapter-gateway-apollo.ts"],"sourcesContent":["import type {\n GetDataSourceFunction,\n GraphQLDataSourceProcessOptions,\n ServiceDefinition,\n SubgraphHealthCheckFunction,\n SupergraphSdlUpdateFunction,\n} from \"@apollo/gateway\";\nimport {\n ApolloGateway,\n LocalCompose,\n RemoteGraphQLDataSource,\n} from \"@apollo/gateway\";\nimport { ApolloServer, HeaderMap } from \"@apollo/server\";\nimport { ApolloServerPluginInlineTraceDisabled } from \"@apollo/server/plugin/disabled\";\nimport { ApolloServerPluginDrainHttpServer } from \"@apollo/server/plugin/drainHttpServer\";\nimport { ApolloServerPluginLandingPageLocalDefault } from \"@apollo/server/plugin/landingPage/default\";\nimport type { ILogger } from \"document-model\";\nimport type { GraphQLSchema } from \"graphql\";\nimport type http from \"node:http\";\nimport type { WebSocketServer } from \"ws\";\nimport type { Context } from \"../types.js\";\nimport { useServer } from \"../websocket.js\";\nimport type {\n FetchHandler,\n GatewayContextFactory,\n IGatewayAdapter,\n SubgraphDefinition,\n WsContextFactory,\n WsDisposer,\n} from \"./types.js\";\n\n// Forwards the incoming authorization header to federated subgraph requests.\nclass AuthenticatedDataSource extends RemoteGraphQLDataSource {\n willSendRequest(options: GraphQLDataSourceProcessOptions) {\n const { authorization } = options.context.headers as {\n authorization: string;\n };\n if (authorization && options?.request.http) {\n options.request.http.headers.set(\"authorization\", authorization);\n }\n }\n}\n\nexport class ApolloGatewayAdapter implements IGatewayAdapter<Context> {\n readonly #logger: ILogger;\n readonly #servers: ApolloServer<Context>[] = [];\n\n #supergraphServer: ApolloServer<Context> | null = null;\n #gatewayOptions: {\n update: SupergraphSdlUpdateFunction;\n healthCheck: SubgraphHealthCheckFunction;\n getDataSource: GetDataSourceFunction;\n } | null = null;\n #getSubgraphs: (() => SubgraphDefinition[]) | null = null;\n\n constructor(logger: ILogger) {\n this.#logger = logger;\n }\n\n async start(_httpServer: http.Server): Promise<void> {\n // Per-subgraph Apollo servers start lazily in createHandler.\n // Nothing to do here.\n }\n\n async createHandler(\n schema: GraphQLSchema,\n contextFactory: GatewayContextFactory<Context>,\n ): Promise<FetchHandler> {\n const server = new ApolloServer<Context>({\n schema,\n logger: this.#logger,\n introspection: true,\n // Each ApolloServer otherwise registers its own SIGINT/SIGTERM handler\n // that re-emits the signal via process.kill after server.stop() resolves.\n // With many subgraph servers that re-emit lands while the reactor's\n // graceful-shutdown chain is mid-flight, looking like a \"second SIGINT\"\n // to the reactor and aborting cleanup. The reactor drives our shutdown\n // end-to-end (see ReactorBuilder.attachSignalHandlers), so opt out.\n stopOnTerminationSignals: false,\n plugins: [\n ApolloServerPluginInlineTraceDisabled(),\n ApolloServerPluginLandingPageLocalDefault(),\n ],\n });\n await server.start();\n this.#servers.push(server);\n return createApolloFetchHandler(server, contextFactory);\n }\n\n async createSupergraphHandler(\n getSubgraphs: () => SubgraphDefinition[],\n httpServer: http.Server,\n contextFactory: GatewayContextFactory<Context>,\n ): Promise<FetchHandler> {\n if (this.#supergraphServer) {\n throw new Error(\"Supergraph server is already running\");\n }\n\n this.#getSubgraphs = getSubgraphs;\n\n const gateway = new ApolloGateway({\n supergraphSdl: async (options) => {\n this.#gatewayOptions = options;\n return await this.#buildSupergraphSdl();\n },\n buildService: (serviceConfig) =>\n new AuthenticatedDataSource(serviceConfig),\n });\n\n this.#supergraphServer = new ApolloServer<Context>({\n gateway,\n logger: this.#logger,\n introspection: true,\n stopOnTerminationSignals: false,\n plugins: [\n ApolloServerPluginDrainHttpServer({ httpServer }),\n ApolloServerPluginInlineTraceDisabled(),\n ApolloServerPluginLandingPageLocalDefault(),\n ],\n });\n\n await this.#supergraphServer.start();\n return createApolloFetchHandler(this.#supergraphServer, contextFactory);\n }\n\n async updateSupergraph(): Promise<void> {\n if (!this.#gatewayOptions || !this.#getSubgraphs) {\n // Not yet initialized - no-op.\n return;\n }\n const { supergraphSdl } = await this.#buildSupergraphSdl();\n this.#gatewayOptions.update(supergraphSdl);\n }\n\n async #buildSupergraphSdl() {\n if (!this.#gatewayOptions || !this.#getSubgraphs) {\n throw new Error(\"Gateway is not ready\");\n }\n const serviceList: ServiceDefinition[] = this.#getSubgraphs().map((s) => ({\n name: s.name,\n typeDefs: s.typeDefs,\n url: s.url,\n }));\n const localCompose = new LocalCompose({ localServiceList: serviceList });\n return localCompose.initialize(this.#gatewayOptions);\n }\n\n attachWebSocket(\n wsServer: WebSocketServer,\n schema: GraphQLSchema,\n contextFactory: WsContextFactory<Context>,\n ): WsDisposer {\n return useServer(\n {\n schema,\n context: async (ctx: {\n connectionParams?: Record<string, unknown>;\n }) => {\n const connectionParams = (ctx.connectionParams ?? {}) as Record<\n string,\n unknown\n >;\n return contextFactory(connectionParams);\n },\n },\n wsServer,\n );\n }\n\n async stop(): Promise<void> {\n await Promise.all(this.#servers.map((s) => s.stop()));\n this.#servers.length = 0;\n\n if (this.#supergraphServer) {\n await this.#supergraphServer.stop();\n this.#supergraphServer = null;\n }\n this.#gatewayOptions = null;\n this.#getSubgraphs = null;\n }\n}\n\n/**\n * Wrap an existing ApolloServer as a Fetch API handler.\n * Does not manage server lifecycle; caller is responsible for start/stop.\n */\nexport function createApolloFetchHandler(\n server: ApolloServer<Context>,\n contextFactory: GatewayContextFactory<Context>,\n): FetchHandler {\n return async (request: Request): Promise<Response> => {\n const url = new URL(request.url);\n const headers = new HeaderMap();\n request.headers.forEach((value, key) => headers.set(key, value));\n\n let body: unknown = undefined;\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n try {\n body = (await request.json()) as unknown;\n } catch {\n // body may be empty for some requests\n }\n }\n\n const result = await server.executeHTTPGraphQLRequest({\n httpGraphQLRequest: {\n method: request.method.toUpperCase(),\n headers,\n search: url.search,\n body,\n },\n context: () => contextFactory(request),\n });\n\n const responseHeaders = new Headers();\n for (const [key, value] of result.headers) {\n responseHeaders.set(key, value);\n }\n\n let responseBody: string;\n if (result.body.kind === \"complete\") {\n responseBody = result.body.string;\n } else {\n const chunks: string[] = [];\n for await (const chunk of result.body.asyncIterator) {\n chunks.push(chunk);\n }\n responseBody = chunks.join(\"\");\n }\n\n return new Response(responseBody, {\n status: result.status ?? 200,\n headers: responseHeaders,\n });\n };\n}\n"],"names":["#logger","#servers","#supergraphServer","#getSubgraphs","#gatewayOptions","#buildSupergraphSdl"],"mappings":";;;;;;;;;AAgCA,IAAM,0BAAN,cAAsC,wBAAwB;CAC5D,gBAAgB,SAA0C;EACxD,MAAM,EAAE,kBAAkB,QAAQ,QAAQ;AAG1C,MAAI,iBAAiB,SAAS,QAAQ,KACpC,SAAQ,QAAQ,KAAK,QAAQ,IAAI,iBAAiB,cAAc;;;AAKtE,IAAa,uBAAb,MAAsE;CACpE;CACA,WAA6C,EAAE;CAE/C,oBAAkD;CAClD,kBAIW;CACX,gBAAqD;CAErD,YAAY,QAAiB;AAC3B,QAAA,SAAe;;CAGjB,MAAM,MAAM,aAAyC;CAKrD,MAAM,cACJ,QACA,gBACuB;EACvB,MAAM,SAAS,IAAI,aAAsB;GACvC;GACA,QAAQ,MAAA;GACR,eAAe;GAOf,0BAA0B;GAC1B,SAAS,CACP,uCAAuC,EACvC,2CAA2C,CAC5C;GACF,CAAC;AACF,QAAM,OAAO,OAAO;AACpB,QAAA,QAAc,KAAK,OAAO;AAC1B,SAAO,yBAAyB,QAAQ,eAAe;;CAGzD,MAAM,wBACJ,cACA,YACA,gBACuB;AACvB,MAAI,MAAA,iBACF,OAAM,IAAI,MAAM,uCAAuC;AAGzD,QAAA,eAAqB;AAWrB,QAAA,mBAAyB,IAAI,aAAsB;GACjD,SAVc,IAAI,cAAc;IAChC,eAAe,OAAO,YAAY;AAChC,WAAA,iBAAuB;AACvB,YAAO,MAAM,MAAA,oBAA0B;;IAEzC,eAAe,kBACb,IAAI,wBAAwB,cAAc;IAC7C,CAAC;GAIA,QAAQ,MAAA;GACR,eAAe;GACf,0BAA0B;GAC1B,SAAS;IACP,kCAAkC,EAAE,YAAY,CAAC;IACjD,uCAAuC;IACvC,2CAA2C;IAC5C;GACF,CAAC;AAEF,QAAM,MAAA,iBAAuB,OAAO;AACpC,SAAO,yBAAyB,MAAA,kBAAwB,eAAe;;CAGzE,MAAM,mBAAkC;AACtC,MAAI,CAAC,MAAA,kBAAwB,CAAC,MAAA,aAE5B;EAEF,MAAM,EAAE,kBAAkB,MAAM,MAAA,oBAA0B;AAC1D,QAAA,eAAqB,OAAO,cAAc;;CAG5C,OAAA,qBAA4B;AAC1B,MAAI,CAAC,MAAA,kBAAwB,CAAC,MAAA,aAC5B,OAAM,IAAI,MAAM,uBAAuB;AAQzC,SADqB,IAAI,aAAa,EAAE,kBALC,MAAA,cAAoB,CAAC,KAAK,OAAO;GACxE,MAAM,EAAE;GACR,UAAU,EAAE;GACZ,KAAK,EAAE;GACR,EAAE,EACoE,CAAC,CACpD,WAAW,MAAA,eAAqB;;CAGtD,gBACE,UACA,QACA,gBACY;AACZ,SAAO,UACL;GACE;GACA,SAAS,OAAO,QAEV;AAKJ,WAAO,eAJmB,IAAI,oBAAoB,EAAE,CAIb;;GAE1C,EACD,SACD;;CAGH,MAAM,OAAsB;AAC1B,QAAM,QAAQ,IAAI,MAAA,QAAc,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;AACrD,QAAA,QAAc,SAAS;AAEvB,MAAI,MAAA,kBAAwB;AAC1B,SAAM,MAAA,iBAAuB,MAAM;AACnC,SAAA,mBAAyB;;AAE3B,QAAA,iBAAuB;AACvB,QAAA,eAAqB;;;;;;;AAQzB,SAAgB,yBACd,QACA,gBACc;AACd,QAAO,OAAO,YAAwC;EACpD,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;EAChC,MAAM,UAAU,IAAI,WAAW;AAC/B,UAAQ,QAAQ,SAAS,OAAO,QAAQ,QAAQ,IAAI,KAAK,MAAM,CAAC;EAEhE,IAAI,OAAgB,KAAA;AACpB,MAAI,QAAQ,WAAW,SAAS,QAAQ,WAAW,OACjD,KAAI;AACF,UAAQ,MAAM,QAAQ,MAAM;UACtB;EAKV,MAAM,SAAS,MAAM,OAAO,0BAA0B;GACpD,oBAAoB;IAClB,QAAQ,QAAQ,OAAO,aAAa;IACpC;IACA,QAAQ,IAAI;IACZ;IACD;GACD,eAAe,eAAe,QAAQ;GACvC,CAAC;EAEF,MAAM,kBAAkB,IAAI,SAAS;AACrC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAChC,iBAAgB,IAAI,KAAK,MAAM;EAGjC,IAAI;AACJ,MAAI,OAAO,KAAK,SAAS,WACvB,gBAAe,OAAO,KAAK;OACtB;GACL,MAAM,SAAmB,EAAE;AAC3B,cAAW,MAAM,SAAS,OAAO,KAAK,cACpC,QAAO,KAAK,MAAM;AAEpB,kBAAe,OAAO,KAAK,GAAG;;AAGhC,SAAO,IAAI,SAAS,cAAc;GAChC,QAAQ,OAAO,UAAU;GACzB,SAAS;GACV,CAAC","debug_id":"7f597280-1b64-50b2-9735-754051857add"}