@powerhousedao/reactor-api 6.2.3-dev.5 → 6.2.3-dev.51
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/README.md +7 -2
- package/dist/{adapter-gateway-apollo-CxJNBVyD.mjs → adapter-gateway-apollo-B54G_cXT.mjs} +15 -13
- package/dist/{adapter-gateway-apollo-CxJNBVyD.mjs.map → adapter-gateway-apollo-B54G_cXT.mjs.map} +1 -1
- package/dist/{adapter-gateway-apollo-DowrX6E_.mjs → adapter-gateway-apollo-D2DNlPY0.mjs} +3 -3
- package/dist/{adapter-gateway-mercurius-CZVy88Za.mjs → adapter-gateway-mercurius-rbKlwF9m.mjs} +4 -4
- package/dist/{adapter-gateway-mercurius-CZVy88Za.mjs.map → adapter-gateway-mercurius-rbKlwF9m.mjs.map} +1 -1
- package/dist/{adapter-gateway-stitching-CaZjvRzx.mjs → adapter-gateway-stitching-Dzv10u8D.mjs} +15 -10
- package/dist/{adapter-gateway-stitching-CaZjvRzx.mjs.map → adapter-gateway-stitching-Dzv10u8D.mjs.map} +1 -1
- package/dist/{adapter-http-express-B39rPhqH.mjs → adapter-http-express-P0t-mdMV.mjs} +10 -10
- package/dist/adapter-http-express-P0t-mdMV.mjs.map +1 -0
- package/dist/{adapter-http-fastify-BwXO6rqE.mjs → adapter-http-fastify-D0OP5q9K.mjs} +13 -12
- package/dist/adapter-http-fastify-D0OP5q9K.mjs.map +1 -0
- package/dist/index.d.mts +1785 -618
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3371 -758
- package/dist/index.mjs.map +1 -1
- package/dist/pglite-node-Ow9i5Rb4.mjs +573 -0
- package/dist/pglite-node-Ow9i5Rb4.mjs.map +1 -0
- package/dist/rolldown-runtime-8H4AJuhK.mjs +16 -0
- package/dist/run-node-handler-CQjyjOuN.mjs +52 -0
- package/dist/run-node-handler-CQjyjOuN.mjs.map +1 -0
- package/dist/src/packages/https-hooks.d.mts +4 -4
- package/dist/src/packages/https-hooks.d.mts.map +1 -1
- package/dist/src/packages/https-hooks.mjs +2 -2
- package/dist/src/packages/https-hooks.mjs.map +1 -1
- package/dist/src/packages/vite-loader.d.mts +19 -9
- package/dist/src/packages/vite-loader.d.mts.map +1 -1
- package/dist/src/packages/vite-loader.mjs +114 -50
- package/dist/src/packages/vite-loader.mjs.map +1 -1
- package/dist/src/pglite/pglite-node.d.mts +130 -0
- package/dist/src/pglite/pglite-node.d.mts.map +1 -0
- package/dist/src/pglite/pglite-node.mjs +4 -0
- package/dist/{utils-XWVecaNn.mjs → subgraph-extraction-sQlZLLPH.mjs} +290 -11
- package/dist/subgraph-extraction-sQlZLLPH.mjs.map +1 -0
- package/dist/{types-DcYyNcHJ.d.mts → types-DlTiz6AA.d.mts} +27 -4
- package/dist/types-DlTiz6AA.d.mts.map +1 -0
- package/dist/{websocket-DncAB9XK.mjs → websocket-_SH5aBcy.mjs} +3 -3
- package/dist/{websocket-DncAB9XK.mjs.map → websocket-_SH5aBcy.mjs.map} +1 -1
- package/package.json +24 -19
- package/dist/adapter-http-express-B39rPhqH.mjs.map +0 -1
- package/dist/adapter-http-fastify-BwXO6rqE.mjs.map +0 -1
- package/dist/path-normalize-BOQwY9wR.mjs +0 -23
- package/dist/path-normalize-BOQwY9wR.mjs.map +0 -1
- package/dist/types-DcYyNcHJ.d.mts.map +0 -1
- package/dist/utils-XWVecaNn.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subgraph-extraction-sQlZLLPH.mjs","sources":["../src/graphql/errors.ts","../src/services/authorization.service.ts","../src/services/canonical-document-id.ts","../src/graphql/base-subgraph.ts","../src/packages/import-resolver.ts","../src/packages/util.ts","../src/graphql/utils.ts","../src/packages/document-model-detection.ts","../src/packages/pieces.ts","../src/packages/subgraph-extraction.ts"],"sourcesContent":["import { GraphQLError } from \"graphql\";\nimport {\n DRIVE_AUTH_ERROR_MESSAGES,\n RECOVERABLE_GRAPHQL_ERROR_CODES,\n} 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\n/**\n * The reactor holds no decision model, so no authorization preflight can be\n * answered.\n *\n * Distinct from a denial, and deliberately so: a caller that cannot get a\n * prediction should render its controls as it did before it asked and let the\n * submit path refuse. Reading this as a denial would disable every control on a\n * switchboard running without authEnforcement. The code is what a client\n * branches on, since the message is not a contract.\n */\nexport class AuthEvaluationUnsupportedError extends GraphQLError {\n constructor() {\n super(\n \"Authorization evaluation is unavailable: this reactor runs without the \" +\n \"authEnforcement feature flag, so it holds no decision model\",\n { extensions: { code: \"AUTH_EVALUATION_UNSUPPORTED\" } },\n );\n }\n}\n\n/**\n * A stored operation cannot be represented in the schema, so it is reported\n * rather than served.\n *\n * The schema declares an action's id, type, timestamp, input and scope\n * non-null; an operation written before the API rejected an action without an\n * id violates that. Left to the executor, the missing value nullifies the\n * action, which bubbles up through the non-null chain until it nullifies the\n * envelope's whole operation list, and the caller receives a partial response\n * beside \"Cannot return null for non-nullable field Action.id\" - which says\n * nothing about which operation, and which a sync client can only read as a\n * permanent GraphQL rejection, stopping its poll timer for good.\n *\n * Thrown instead, with a code that says the failure is worth polling through:\n * the document holding the operation needs repairing, but the channel serves\n * every other document, and a peer that stopped polling would stop receiving\n * those too. The code is what a client branches on, since the message is not a\n * contract.\n */\nexport class MalformedStoredOperationError extends GraphQLError {\n constructor(detail: string) {\n super(`Stored operation cannot be served: ${detail}`, {\n extensions: {\n code: RECOVERABLE_GRAPHQL_ERROR_CODES.malformedStoredOperation,\n },\n });\n }\n}\n\n/**\n * The marker refusals a poll carried were not recorded, so the poll fails.\n * The code is recoverable: the client keeps polling and reports them again.\n */\nexport class RefusalNotRecordedError extends GraphQLError {\n constructor(cause: unknown) {\n super(\"A reported marker refusal was not recorded; report it again\", {\n originalError: cause instanceof Error ? cause : new Error(String(cause)),\n extensions: {\n code: RECOVERABLE_GRAPHQL_ERROR_CODES.refusalNotRecorded,\n },\n });\n }\n}\n","import { PURGE_DOCUMENT } from \"@powerhousedao/shared/document-model\";\nimport 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 createCanonicalDocumentIdResolver, 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 // A purge marker needs a document admin whatever the operation rows say.\n if (operationType === PURGE_DOCUMENT) {\n return this.#isDocumentAdmin(documentId, userAddress);\n }\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 { IReactorClient } from \"@powerhousedao/reactor\";\nimport type { CanonicalDocumentId } from \"./authorization.service.js\";\n\n/**\n * Raised when a caller-supplied identifier cannot be resolved to a canonical\n * document id. Carries no detail on purpose: resolution failures must not act\n * as a document-existence oracle, so consumers map this to their generic\n * fail-closed response.\n */\nexport class CanonicalDocumentIdResolutionError extends Error {\n constructor() {\n super(\"Document identifier could not be resolved\");\n this.name = \"CanonicalDocumentIdResolutionError\";\n }\n}\n\nexport type CanonicalDocumentIdResolver = (\n identifier: string,\n) => Promise<CanonicalDocumentId>;\n\n/**\n * The sanctioned string-to-CanonicalDocumentId cast: both the GraphQL layer\n * and server routes must resolve caller identifiers through this helper so\n * decision and data layers always agree on the subject document.\n */\nexport function createCanonicalDocumentIdResolver(\n client: Pick<IReactorClient, \"resolveIdOrSlug\">,\n): CanonicalDocumentIdResolver {\n return async (identifier: string) => {\n let resolved: string;\n try {\n resolved = await client.resolveIdOrSlug(identifier);\n } catch {\n throw new CanonicalDocumentIdResolutionError();\n }\n return resolved as CanonicalDocumentId;\n };\n}\n","import type {\n IReactorClient,\n IRelationalDb,\n ISyncManager,\n SyncScopeGate,\n ViewFilter,\n} from \"@powerhousedao/reactor\";\nimport type { IAttachmentClient } from \"@powerhousedao/reactor-attachments/client\";\nimport type { AuthSubject } from \"@powerhousedao/shared/document-model\";\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 {\n createCanonicalDocumentIdResolver,\n type CanonicalDocumentIdResolver,\n} from \"../services/canonical-document-id.js\";\nimport type { IAttachmentClientProvider } from \"../services/authorized-attachment.service.js\";\nimport type { DocumentPermissionService } from \"../services/document-permission.service.js\";\nimport type { Context } from \"./types.js\";\n\n/**\n * The subject a request reads as: its address and the app key its token\n * carries. Anonymous is an empty subject, never the host's signer.\n */\nexport function callerSubject(user: Context[\"user\"]): AuthSubject {\n return { address: user?.address, key: user?.appKey };\n}\n\nexport class BaseSubgraph implements ISubgraph {\n name = \"example\";\n // The host's base path as injected by the GraphQL manager. Routing\n // ignores it: the manager mounts every subgraph under its own base path.\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 /** This package's namespaced slice of the HTTP surface. */\n readonly http: SubgraphArgs[\"http\"];\n graphqlManager: GraphQLManager;\n relationalDb: IRelationalDb;\n syncManager: ISyncManager;\n documentPermissionService?: DocumentPermissionService;\n authorizationService: IAuthorizationService;\n syncServingGate?: SyncScopeGate;\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 readonly #resolveCanonical: CanonicalDocumentIdResolver;\n\n readonly #attachments?: IAttachmentClientProvider;\n\n readonly #attachmentClientMemo = new WeakMap<object, IAttachmentClient>();\n\n constructor(args: SubgraphArgs) {\n this.#resolveCanonical = createCanonicalDocumentIdResolver(\n args.reactorClient,\n );\n this.http = args.http;\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.syncServingGate = args.syncServingGate;\n this.path = args.path ?? \"\";\n this.#attachments = args.attachments;\n }\n\n /**\n * An attachment client bound to this request's caller, memoized per request.\n *\n * @throws Error when the host provides no attachment client to subgraphs.\n */\n attachmentsFor(ctx: Context): IAttachmentClient {\n if (!this.#attachments) {\n throw new Error(\"This host provides no attachment client to subgraphs\");\n }\n let client = this.#attachmentClientMemo.get(ctx);\n if (!client) {\n client = this.#attachments.forSubject(callerSubject(ctx.user));\n this.#attachmentClientMemo.set(ctx, client);\n }\n return client;\n }\n\n async onSetup() {\n // noop\n }\n\n async onDisconnect(): Promise<void> {\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 canonical: CanonicalDocumentId;\n try {\n canonical = await this.#resolveCanonical(identifier);\n } catch {\n throw new ForbiddenError();\n }\n\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 /** Drops the items the host's own ACL refuses the caller. */\n async readableByHost<T extends { id: string }>(\n items: readonly T[],\n ctx: Context,\n ): Promise<T[]> {\n if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) {\n return [...items];\n }\n const readable: T[] = [];\n for (const item of items) {\n if (await this.canReadDocument(item.id as CanonicalDocumentId, ctx)) {\n readable.push(item);\n }\n }\n return readable;\n }\n\n /**\n * The principal a request decides as: the authenticated caller's address, and\n * the did:key of the app instance whose token authenticated it.\n *\n * Both, because a policy can name either. The key is what a document records\n * as its creator, so a subject built without one cannot match the creator's\n * standing permission over the auth scope -- the document's own creator would\n * be refused an operation the write path accepts, since a signed action\n * carries the same key. Anonymous callers supply neither.\n */\n protected viewSubject(ctx: Context): AuthSubject {\n return callerSubject(ctx.user);\n }\n\n /** Whether a listing would serve the document to the caller; fails closed. */\n protected async servesDocument(\n documentId: string,\n ctx: Context,\n ): Promise<boolean> {\n if (!documentId) {\n return false;\n }\n try {\n return await this.servesDocumentOrThrows(documentId, ctx);\n } catch {\n return false;\n }\n }\n\n /** {@link servesDocument}, but a reactor that cannot answer throws. */\n protected servesDocumentOrThrows(\n identifier: string,\n ctx: Context,\n view?: Pick<ViewFilter, \"branch\" | \"scopes\">,\n ): Promise<boolean> {\n return this.reactorClient.isServed(identifier, {\n ...view,\n subject: this.viewSubject(ctx),\n });\n }\n\n /**\n * Whether a listing would serve the document to the caller: the reactor read\n * gate, then the host's legacy layer, as `findDocuments` applies them. Absent\n * or withheld is `false`; a failure to decide throws.\n */\n protected async listsDocument(\n identifier: string,\n ctx: Context,\n view?: Pick<ViewFilter, \"branch\" | \"scopes\">,\n ): Promise<boolean> {\n if (!(await this.servesDocumentOrThrows(identifier, ctx, view))) {\n return false;\n }\n if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) {\n return true;\n }\n return this.canReadDocument(\n await this.resolveCanonicalDocumentId(identifier, ctx),\n ctx,\n );\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\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\n/**\n * Imports a specifier that may be either a bare package specifier or a\n * filesystem path. Absolute paths are converted to file:// URLs, because the\n * ESM loader reads the drive letter in a Windows path as a URL scheme\n * (`import('D:\\\\...')` fails with ERR_UNSUPPORTED_ESM_URL_SCHEME).\n */\nasync function importSpecifier<T>(target: string): Promise<T> {\n const specifier = path.isAbsolute(target)\n ? pathToFileURL(target).href\n : target;\n return (await import(/* @vite-ignore */ specifier)) as T;\n}\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 importSpecifier<T>(suggestedPath);\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 // fileURLToPath, not URL.pathname: the latter yields \"/D:/...\" on Windows.\n const packageRoot = path.dirname(fileURLToPath(resolvedUrl));\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 importSpecifier<T>(attemptPath);\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 importSpecifier<T>(workspacePath);\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 {\n DocumentModelModule,\n UpgradeManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport { childLogger } from \"document-model\";\nimport { execSync } from \"node:child_process\";\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\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 // A local package is identified by an absolute path, which has to become a\n // file:// URL: the ESM loader reads the drive letter in a Windows path as a\n // URL scheme.\n const fullPath = path.isAbsolute(packageName)\n ? pathToFileURL(path.join(packageName, subPath)).href\n : `${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 // ERR_UNSUPPORTED_ESM_URL_SCHEME joins the list because a directory\n // specifier that is a plain path fails with it on Windows where POSIX\n // reports ERR_UNSUPPORTED_DIR_IMPORT.\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 e.code === \"ERR_UNSUPPORTED_ESM_URL_SCHEME\")\n ) {\n const result = await resolveLinkedPackage<T>(packageName, subPath);\n if (result) return result;\n }\n throw e;\n }\n}\n\n/**\n * The subgraphs a package's `powerhouse.manifest.json` declares, when the\n * manifest is reachable — which it is for a package identified by an\n * absolute path (how every local project reaches the loaders). Lets the\n * package manager warn when a manifest promises subgraphs and the loaders\n * deliver none, instead of failing silently. For a bare package name the\n * manifest would have to be resolved through the module system; until a\n * caller needs that, a bare name answers [] rather than guessing.\n */\nexport function manifestDeclaredSubgraphs(identifier: string): string[] {\n if (!path.isAbsolute(identifier)) return [];\n try {\n const raw = readFileSync(\n path.join(identifier, \"powerhouse.manifest.json\"),\n \"utf-8\",\n );\n const manifest = JSON.parse(raw) as { subgraphs?: unknown };\n if (!Array.isArray(manifest.subgraphs)) return [];\n return manifest.subgraphs\n .map((subgraph: unknown) => {\n if (typeof subgraph !== \"object\" || subgraph === null) {\n return undefined;\n }\n const { id, name } = subgraph as { id?: unknown; name?: unknown };\n return id ?? name;\n })\n .filter(\n (value): value is string =>\n typeof value === \"string\" && value.length > 0,\n );\n } catch {\n // No manifest, or an unreadable one: nothing was promised, so nothing\n // to warn about.\n return [];\n }\n}\n\nfunction isUpgradeManifest(\n value: unknown,\n): value is UpgradeManifest<readonly number[]> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"documentType\" in value &&\n \"supportedVersions\" in value &&\n \"upgrades\" in value\n );\n}\n\n/**\n * Collects upgrade manifests from a package's document-models namespace.\n * Handles both the aggregate `upgradeManifests` array export and individual\n * manifest exports; dedupes by documentType.\n */\nexport function extractUpgradeManifests(\n namespace: Record<string, unknown>,\n): UpgradeManifest<readonly number[]>[] {\n const manifests = new Map<string, UpgradeManifest<readonly number[]>>();\n const add = (value: unknown) => {\n if (isUpgradeManifest(value)) {\n manifests.set(value.documentType, value);\n }\n };\n for (const value of Object.values(namespace)) {\n if (Array.isArray(value)) {\n value.forEach(add);\n } else {\n add(value);\n }\n }\n return Array.from(manifests.values());\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 // Walk the candidate's constructor chain. Identity first: package builds\n // keep `@powerhousedao/reactor-api` external precisely so every subgraph\n // extends this very BaseSubgraph (see nodeNeverBundle in\n // packages/shared/clis/build-config.mts). The name check is the fallback\n // for a module graph that carries its own copy — a project served by the\n // vite dev server resolves reactor-api from its own node_modules, which\n // need not be the host's instance.\n //\n // The previous version asked `isPrototypeOf` in the wrong direction\n // (whether the candidate's proto was in BaseSubgraph's chain), which\n // `Function.prototype` always satisfies — so every function passed, and\n // junk exports registered as subgraphs.\n let proto: unknown = Object.getPrototypeOf(candidate);\n while (typeof proto === \"function\") {\n if (proto === BaseSubgraph) return true;\n if (proto.name === \"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","import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\n\n/**\n * The one acceptance rule for a package's `document-models` module.\n *\n * The three package loaders duck-typed this independently and disagreed on\n * one case: the http loader required `documentModel` to be non-null, while\n * the import and vite loaders accepted any object merely carrying the key,\n * so an export shaped `{ documentModel: null }` registered as a model under\n * two loaders and vanished under the third. The strict rule is the correct\n * one — a module without its documentModel cannot define a document type —\n * and this predicate is now the single rule all three share, the same way\n * extractSubgraphs unified the subgraph rule.\n */\nexport function isDocumentModelModule(\n candidate: unknown,\n): candidate is DocumentModelModule {\n if (candidate === null || typeof candidate !== \"object\") return false;\n if (!(\"documentModel\" in candidate)) return false;\n const model = (candidate as { documentModel: unknown }).documentModel;\n return model !== null && model !== undefined;\n}\n\n/** Collects the document-model modules among a namespace's exports. */\nexport function extractDocumentModels(\n namespace: Record<string, unknown>,\n): DocumentModelModule[] {\n return Object.values(namespace).filter(isDocumentModelModule);\n}\n","// The pieces a package ships, located on disk for a host that never runs them.\n\n// Only the list module is ever imported here: it holds no piece code, and a\n// piece itself runs in a forked worker, handed the path this reports.\nimport { packageJsonExports } from \"@powerhousedao/shared/clis/constants\";\nimport type { ILogger } from \"document-model\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, isAbsolute, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { PackagePiece, PackagePieceEntry } from \"./types.js\";\n\n/** The sub-entry every loader asks a package for its pieces through. */\nexport const PIECES_SUBPATH = \"pieces\";\n\n// Where the node build leaves the list, read from the export map a generated\n// package ships so the build and the host cannot disagree about the path.\nexport const BUILT_PIECE_LIST = packageJsonExports[\"./pieces\"].node;\n\n/** Where a package keeps its piece sources, for telling unbuilt from absent. */\nexport const PIECE_SOURCE_LIST = join(PIECES_SUBPATH, \"index.ts\");\n\n/** The nearest enclosing package.json, which `exports` usually hides. */\nexport function packageRootOf(dir: string): string | undefined {\n let current = dir;\n while (!existsSync(join(current, \"package.json\"))) {\n const parent = dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n return current;\n}\n\nexport interface PieceListLocation {\n /** Absolute; a declared entry is relative to it. */\n root: string;\n /** Absolute path of the list module, built or not. */\n listPath: string;\n}\n\n// Resolved from this module first, then from the host's own dependencies,\n// which is where document models are found too; the first error is the one kept.\nfunction resolvePieceList(specifier: string, hostDir: string): string {\n try {\n return fileURLToPath(import.meta.resolve(specifier));\n } catch (error) {\n try {\n return createRequire(join(hostDir, \"package.json\")).resolve(specifier);\n } catch {\n throw error;\n }\n }\n}\n\n// Where a package's piece list sits and what its entries are relative to. A\n// path identifier is its own root; a name resolves as a host would import it.\nexport function pieceListLocation(\n identifier: string,\n hostDir = process.cwd(),\n): PieceListLocation {\n if (isAbsolute(identifier)) {\n return { root: identifier, listPath: join(identifier, BUILT_PIECE_LIST) };\n }\n // Left to throw, with the ESM resolver's error codes: they're what the\n // package manager reads a miss from.\n const listPath = resolvePieceList(`${identifier}/${PIECES_SUBPATH}`, hostDir);\n const root = packageRootOf(dirname(listPath));\n if (!root) {\n throw new Error(`No package.json above ${listPath}`);\n }\n return { root, listPath };\n}\n\n// A piece's version is its package's, so it is read from the package root.\nexport function packageVersionAt(root: string): string | undefined {\n try {\n const pkg = JSON.parse(\n readFileSync(join(root, \"package.json\"), \"utf8\"),\n ) as { version?: unknown };\n return typeof pkg.version === \"string\" && pkg.version !== \"\"\n ? pkg.version\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n// A declared piece counts only once its code is on disk: one nobody built is\n// reported rather than dropped, because a block type names it either way.\nexport function locatePieces(\n declared: readonly PackagePiece[],\n context: {\n root: string;\n identifier: string;\n logger: ILogger;\n version: string;\n },\n): PackagePieceEntry[] {\n const { root, identifier, logger, version } = context;\n const located: PackagePieceEntry[] = [];\n for (const piece of declared) {\n const where = piece.entry ?? piece.bundle;\n if (typeof where !== \"string\" || where === \"\") {\n // Every name goes through a logger value: `@`-prefixed tokens are\n // substituted, so a scoped one printed inline comes out as null/pack.\n logger.warn(\n \"Piece @piece of @pkg declares neither a bundle nor an entry\",\n piece.name,\n identifier,\n );\n continue;\n }\n const path = isAbsolute(where) ? where : join(root, where);\n const present = piece.entry\n ? existsSync(path)\n : existsSync(join(path, \"package.json\"));\n if (!present) {\n logger.warn(\n \"Piece @piece of @pkg is declared but not built at @path\",\n piece.name,\n identifier,\n path,\n );\n continue;\n }\n located.push({\n name: piece.name,\n version,\n ...(piece.entry ? { entryPath: path } : { bundleDir: path }),\n });\n }\n return located;\n}\n\n// A package's `pieces` array, however the list module was obtained.\nfunction declaredPieces(\n module: unknown,\n identifier: string,\n logger: ILogger,\n): PackagePiece[] | undefined {\n const namespace = (module ?? {}) as { pieces?: unknown; default?: unknown };\n const declared = namespace.pieces ?? namespace.default;\n if (!Array.isArray(declared)) {\n logger.warn(\n 'The pieces list of @pkg exports no \"pieces\" array; it contributes none',\n identifier,\n );\n return undefined;\n }\n return declared as PackagePiece[];\n}\n\n// The same list, served by a registry rather than read off a disk. Nothing is\n// checked for existence here: the CDN is asked when the piece is run.\n\n// `entry` is relative to the package root while the CDN serves beneath\n// `dist/`, so that prefix comes off before the base is applied.\nexport function piecesFromCdnList(\n module: unknown,\n baseUrl: string,\n identifier: string,\n logger: ILogger,\n version: string,\n): PackagePieceEntry[] {\n const declared = declaredPieces(module, identifier, logger);\n if (!declared) return [];\n const located: PackagePieceEntry[] = [];\n for (const piece of declared) {\n const where = piece.entry ?? piece.bundle;\n if (typeof where !== \"string\" || where === \"\") {\n logger.warn(\n \"Piece @piece of @pkg declares neither a bundle nor an entry\",\n piece.name,\n identifier,\n );\n continue;\n }\n located.push({\n name: piece.name,\n version,\n entryUrl: `${baseUrl}${where.replace(/^dist\\/node\\/pieces\\//, \"\")}`,\n });\n }\n return located;\n}\n\n// What a loaded list module contributes. A module without a `pieces` array is\n// not a list, and saying so beats reporting the package as shipping none.\nexport function piecesFromListModule(\n module: unknown,\n context: { root: string; identifier: string; logger: ILogger },\n): PackagePieceEntry[] {\n const namespace = (module ?? {}) as { pieces?: unknown; default?: unknown };\n const declared = namespace.pieces ?? namespace.default;\n if (!Array.isArray(declared)) {\n context.logger.warn(\n 'The pieces list of @pkg exports no \"pieces\" array; it contributes none',\n context.identifier,\n );\n return [];\n }\n const version = packageVersionAt(context.root);\n if (!version) {\n context.logger.warn(\n \"@pkg has no version in @root/package.json, and a piece takes its package's version; its pieces contribute none\",\n context.identifier,\n context.root,\n );\n return [];\n }\n return locatePieces(declared as PackagePiece[], { ...context, version });\n}\n","import type { SubgraphClass } from \"@powerhousedao/reactor-api\";\nimport { isSubgraphClass } from \"../graphql/utils.js\";\n\n/**\n * The one acceptance rule for a package's `subgraphs` module.\n *\n * All three package loaders (vite, import, http) consume the same generated\n * `subgraphs/index` file, and each used to apply its own rule — the vite\n * loader required a namespace whose alias equalled the class name inside it,\n * the http loader took any function one level deep, and the import loader\n * took every nested value. An export that registered under one loader could\n * silently vanish under another (the codegen-does-not-register-subgraphs\n * report). This is now the single rule they all share:\n *\n * - an export that is itself a subgraph class is accepted (named or default\n * export of the class);\n * - any subgraph class among the values of an exported object is accepted\n * (a `export * as Alias from ...` namespace under any alias, or a\n * default-exported object of classes);\n * - everything else — constants, configs, functions and classes that do not\n * extend BaseSubgraph — is dropped.\n *\n * Codegen emits `export * as <ClassName> from \"./<dir>/index.js\"`\n * (makeSubgraphsIndexFile in packages/codegen), which the namespace branch\n * accepts regardless of the alias; hand-written packages may re-export a\n * class directly, which the direct branch accepts. A class reached through\n * both branches is returned once.\n */\nexport function extractSubgraphs(\n namespace: Record<string, unknown>,\n): SubgraphClass[] {\n const found: SubgraphClass[] = [];\n const add = (candidate: unknown) => {\n if (isSubgraphClass(candidate) && !found.includes(candidate)) {\n found.push(candidate);\n }\n };\n for (const value of Object.values(namespace)) {\n add(value);\n if (value !== null && typeof value === \"object\") {\n for (const inner of Object.values(value)) {\n add(inner);\n }\n }\n }\n return found;\n}\n"],"names":[],"mappings":"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","debug_id":"a7006388-c7a4-51a6-82d1-b4ec96082701"}
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import { ILogger } from "document-model";
|
|
2
1
|
import { DocumentModelModule, UpgradeManifest } from "@powerhousedao/shared/document-model";
|
|
2
|
+
import { ILogger } from "document-model";
|
|
3
3
|
import { ProcessorFactoryBuilder, SubgraphClass } from "@powerhousedao/reactor-api";
|
|
4
|
-
|
|
5
4
|
//#region src/packages/types.d.ts
|
|
5
|
+
interface PackagePiece {
|
|
6
|
+
name: string;
|
|
7
|
+
/** Built output, relative to the package root: a directory in npm shape. */
|
|
8
|
+
bundle?: string;
|
|
9
|
+
/** Built output, relative to the package root: a single module file. */
|
|
10
|
+
entry?: string;
|
|
11
|
+
}
|
|
12
|
+
interface PackagePieceEntry {
|
|
13
|
+
name: string;
|
|
14
|
+
/** The version of the package that ships it. */
|
|
15
|
+
version: string;
|
|
16
|
+
entryPath?: string;
|
|
17
|
+
bundleDir?: string;
|
|
18
|
+
entryUrl?: string;
|
|
19
|
+
}
|
|
6
20
|
interface IPackageLoader {
|
|
7
21
|
name: string;
|
|
8
22
|
loadDocumentModels(identifier: string, immediate?: boolean): Promise<DocumentModelModule[]>;
|
|
@@ -13,6 +27,8 @@ interface IPackageLoader {
|
|
|
13
27
|
loadUpgradeManifests?(identifier: string): Promise<UpgradeManifest<readonly number[]>[]>;
|
|
14
28
|
loadSubgraphs(identifier: string, immediate?: boolean): Promise<SubgraphClass[]>;
|
|
15
29
|
loadProcessors(identifier: string, immediate?: boolean): Promise<ProcessorFactoryBuilder | null>;
|
|
30
|
+
/** The pieces a package ships, each located absolutely on this disk. */
|
|
31
|
+
loadPieces(identifier: string, immediate?: boolean): Promise<PackagePieceEntry[]>;
|
|
16
32
|
}
|
|
17
33
|
interface ISubscriptionOptions {
|
|
18
34
|
debounce?: number;
|
|
@@ -21,6 +37,7 @@ interface ISubscribablePackageLoader extends IPackageLoader {
|
|
|
21
37
|
onDocumentModelsChange?(identifier: string, handler: (documentModels: DocumentModelModule[]) => void, options?: ISubscriptionOptions): () => void;
|
|
22
38
|
onSubgraphsChange?(identifier: string, handler: (subgraphs: SubgraphClass[]) => void, options?: ISubscriptionOptions): () => void;
|
|
23
39
|
onProcessorsChange?(identifier: string, handler: (processors: ProcessorFactoryBuilder | null) => void, options?: ISubscriptionOptions): () => void;
|
|
40
|
+
onPiecesChange?(identifier: string, handler: (pieces: PackagePieceEntry[]) => void, options?: ISubscriptionOptions): () => void;
|
|
24
41
|
}
|
|
25
42
|
interface IPackageManager {
|
|
26
43
|
onDocumentModelsChange(handler: (documentModels: Record<string, DocumentModelModule[]>) => void): void;
|
|
@@ -42,7 +59,13 @@ type PackageManagerResult = {
|
|
|
42
59
|
upgradeManifests: UpgradeManifest<readonly number[]>[];
|
|
43
60
|
subgraphs: Map<string, SubgraphClass[]>;
|
|
44
61
|
processors: Map<string, ProcessorFactoryBuilder[]>;
|
|
62
|
+
pieces: Map<string, PackagePieceEntry[]>;
|
|
45
63
|
};
|
|
64
|
+
interface IPackagePieceSource {
|
|
65
|
+
getPieces(): Map<string, PackagePieceEntry[]>;
|
|
66
|
+
onPiecesChange(handler: (pieces: Map<string, PackagePieceEntry[]>) => void): void;
|
|
67
|
+
}
|
|
46
68
|
//#endregion
|
|
47
|
-
export {
|
|
48
|
-
|
|
69
|
+
export { IPackagePieceSource as a, PackageManagerResult as c, IPackageManagerOptions as i, PackagePiece as l, IPackageLoaderOptions as n, ISubscribablePackageLoader as o, IPackageManager as r, ISubscriptionOptions as s, IPackageLoader as t, PackagePieceEntry as u };
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=types-DlTiz6AA.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DlTiz6AA.d.mts","names":[],"sources":["../src/packages/types.ts"],"mappings":";;;;UAYiB;EACf;;EAEA;;EAEA;;UAQe;EACf;;EAEA;EACA;EACA;EACA;;UAGe;EACf;EACA,mBACE,oBACA,sBACC,QAAQ;;;;;EAKX,sBACE,qBACC,QAAQ;EACX,cACE,oBACA,sBACC,QAAQ;EACX,eACE,oBACA,sBACC,QAAQ;;EAEX,WACE,oBACA,sBACC,QAAQ;;UAGI;EACf;;UAGe,mCAAmC;EAClD,wBACE,oBACA,UAAU,gBAAgB,gCAC1B,UAAU;EAEZ,mBACE,oBACA,UAAU,WAAW,0BACrB,UAAU;EAEZ,oBACE,oBACA,UAAU,YAAY,yCACtB,UAAU;EAEZ,gBACE,oBACA,UAAU,QAAQ,8BAClB,UAAU;;UAIG;EACf,uBACE,UAAU,gBAAgB,eAAe;;;;;EAM3C,cAAc;;KAGJ;EACV,SAAS;;KAGC;EACV;EACA;;KAWU;EACV,gBAAgB;EAChB,kBAAkB;EAClB,WAAW,YAAY;EACvB,YAAY,YAAY;EACxB,QAAQ,YAAY;;UAKL;EACf,aAAa,YAAY;EACzB,eACE,UAAU,QAAQ,YAAY"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/graphql/websocket.ts
|
|
2
2
|
|
|
3
|
-
!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]="
|
|
3
|
+
!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]="bddc5b47-cb23-58b9-bb18-09b08c412430")}catch(e){}}();
|
|
4
4
|
let useServerImpl = null;
|
|
5
5
|
if (process.env.VITEST !== "true" && process.env.NODE_ENV !== "test") try {
|
|
6
6
|
const { useServer } = await import("graphql-ws/use/ws");
|
|
@@ -14,5 +14,5 @@ const useServer = useServerImpl || (() => {
|
|
|
14
14
|
//#endregion
|
|
15
15
|
export { useServer as t };
|
|
16
16
|
|
|
17
|
-
//# sourceMappingURL=websocket-
|
|
18
|
-
//# debugId=
|
|
17
|
+
//# sourceMappingURL=websocket-_SH5aBcy.mjs.map
|
|
18
|
+
//# debugId=bddc5b47-cb23-58b9-bb18-09b08c412430
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-
|
|
1
|
+
{"version":3,"file":"websocket-_SH5aBcy.mjs","sources":["../src/graphql/websocket.ts"],"sourcesContent":["import type { useServer as wsUseServer } from \"graphql-ws/use/ws\";\n\nlet useServerImpl: typeof wsUseServer | null = null;\n\nif (process.env.VITEST !== \"true\" && process.env.NODE_ENV !== \"test\") {\n try {\n const { useServer } = await import(\"graphql-ws/use/ws\");\n useServerImpl = useServer;\n } catch (error) {\n console.warn(\n \"Failed to load graphql-ws WebSocket server. Subscriptions will not work.\",\n error,\n );\n }\n}\n\nexport const useServer =\n useServerImpl ||\n (() => {\n throw new Error(\n \"WebSocket server not available in test mode. Set NODE_ENV=production to enable.\",\n );\n });\n"],"names":[],"mappings":";;;AAEA,IAAI,gBAA2C;AAE/C,IAAI,QAAQ,IAAI,WAAW,UAAU,QAAQ,IAAI,aAAa,QAC5D,IAAI;CACF,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,gBAAgB;AAClB,SAAS,OAAO;CACd,QAAQ,KACN,4EACA,KACF;AACF;AAGF,MAAa,YACX,wBACO;CACL,MAAM,IAAI,MACR,iFACF;AACF","debug_id":"bddc5b47-cb23-58b9-bb18-09b08c412430"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/reactor-api",
|
|
3
|
-
"version": "6.2.3-dev.
|
|
3
|
+
"version": "6.2.3-dev.51",
|
|
4
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": {
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"types": "./dist/https-hooks.d.mts",
|
|
25
25
|
"import": "./dist/src/packages/https-hooks.mjs"
|
|
26
26
|
},
|
|
27
|
+
"./pglite-node": {
|
|
28
|
+
"source": "./src/pglite/pglite-node.ts",
|
|
29
|
+
"types": "./dist/src/pglite/pglite-node.d.mts",
|
|
30
|
+
"import": "./dist/src/pglite/pglite-node.mjs"
|
|
31
|
+
},
|
|
27
32
|
"./test": {
|
|
28
33
|
"import": "./test/index.ts"
|
|
29
34
|
}
|
|
@@ -59,26 +64,26 @@
|
|
|
59
64
|
"@graphql-tools/schema": "10.0.31",
|
|
60
65
|
"@graphql-tools/utils": "11.0.0",
|
|
61
66
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
62
|
-
"@powerhousedao/analytics-engine-core": "6.2.3-dev.
|
|
63
|
-
"@powerhousedao/analytics-engine-graphql": "6.2.3-dev.
|
|
64
|
-
"@powerhousedao/analytics-engine-pg": "6.2.3-dev.
|
|
65
|
-
"@powerhousedao/config": "6.2.3-dev.
|
|
67
|
+
"@powerhousedao/analytics-engine-core": "6.2.3-dev.51",
|
|
68
|
+
"@powerhousedao/analytics-engine-graphql": "6.2.3-dev.51",
|
|
69
|
+
"@powerhousedao/analytics-engine-pg": "6.2.3-dev.51",
|
|
70
|
+
"@powerhousedao/config": "6.2.3-dev.51",
|
|
66
71
|
"@powerhousedao/document-engineering": "1.40.5",
|
|
67
|
-
"@powerhousedao/
|
|
68
|
-
"@powerhousedao/reactor": "6.2.3-dev.
|
|
69
|
-
"@powerhousedao/reactor-
|
|
70
|
-
"@powerhousedao/reactor-
|
|
71
|
-
"@powerhousedao/reactor-
|
|
72
|
-
"@powerhousedao/
|
|
73
|
-
"@
|
|
74
|
-
"@renown/sdk": "6.2.3-dev.5",
|
|
72
|
+
"@powerhousedao/reactor": "6.2.3-dev.51",
|
|
73
|
+
"@powerhousedao/reactor-attachments": "6.2.3-dev.51",
|
|
74
|
+
"@powerhousedao/reactor-drive": "6.2.3-dev.51",
|
|
75
|
+
"@powerhousedao/reactor-group": "6.2.3-dev.51",
|
|
76
|
+
"@powerhousedao/reactor-mcp": "6.2.3-dev.51",
|
|
77
|
+
"@powerhousedao/shared": "6.2.3-dev.51",
|
|
78
|
+
"@renown/sdk": "6.2.3-dev.51",
|
|
75
79
|
"body-parser": "^1.20.3",
|
|
76
80
|
"change-case": "5.4.4",
|
|
77
81
|
"cors": "^2.8.5",
|
|
78
82
|
"devcert": "^1.2.2",
|
|
79
|
-
"document-model": "6.2.3-dev.
|
|
83
|
+
"document-model": "6.2.3-dev.51",
|
|
80
84
|
"dotenv": "^16.4.5",
|
|
81
|
-
"
|
|
85
|
+
"es-module-lexer": "^2.0.0",
|
|
86
|
+
"express": "^4.22.1",
|
|
82
87
|
"graphql-sse": "2.6.0",
|
|
83
88
|
"graphql-subscriptions": "^3.0.0",
|
|
84
89
|
"graphql-type-json": "^0.3.2",
|
|
@@ -115,9 +120,9 @@
|
|
|
115
120
|
"@types/ws": "^8.18.1",
|
|
116
121
|
"graphql": "^16",
|
|
117
122
|
"graphql-tag": "^2",
|
|
118
|
-
"tsdown": "0.
|
|
123
|
+
"tsdown": "0.23.0",
|
|
119
124
|
"msw": "^2.7.3",
|
|
120
|
-
"vitest": "
|
|
125
|
+
"vitest": "5.0.3"
|
|
121
126
|
},
|
|
122
127
|
"peerDependencies": {
|
|
123
128
|
"@fastify/cors": "^11.0.1",
|
|
@@ -157,11 +162,11 @@
|
|
|
157
162
|
"scripts": {
|
|
158
163
|
"tsc": "tsc",
|
|
159
164
|
"build": "tsdown",
|
|
160
|
-
"lint": "
|
|
165
|
+
"lint": "cd ../.. && oxlint packages/reactor-api",
|
|
161
166
|
"test": "vitest --run",
|
|
162
167
|
"test:integration": "cross-env RUN_HUB_SPOKE_INTEGRATION=1 vitest --run test/hub-spoke-catchup.integration.test.ts",
|
|
163
168
|
"bench": "vitest bench",
|
|
164
|
-
"codegen": "graphql-codegen --config codegen.ts &&
|
|
169
|
+
"codegen": "graphql-codegen --config codegen.ts && oxlint --fix src/graphql/reactor/gen/*.ts && oxfmt src/graphql/reactor/gen/*.ts",
|
|
165
170
|
"codegen:watch": "graphql-codegen --config codegen.ts --watch",
|
|
166
171
|
"dev": "tsx src/dev.ts",
|
|
167
172
|
"capture-hub-dump": "tsx scripts/capture-hub-dump.ts",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-http-express-B39rPhqH.mjs","sources":["../src/graphql/gateway/adapter-http-express.ts"],"sourcesContent":["import bodyParser from \"body-parser\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport devcert from \"devcert\";\nimport type express from \"express\";\nimport type { Express } from \"express\";\nimport { Router } from \"express\";\nimport expressLib from \"express\";\nimport type { IRouter } from \"express\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as WebReadableStream } from \"node:stream/web\";\nimport { match, type MatchFunction, type ParamData } from \"path-to-regexp\";\nimport type {\n FetchHandler,\n HttpMethod,\n IHttpAdapter,\n NodeRouteOptions,\n AdapterRouteHandle,\n TlsOptions,\n} from \"./types.js\";\nimport { normalizePath } from \"./path-normalize.js\";\n\ntype GetHandler = (r: Request) => Response | Promise<Response>;\n\ntype NodeHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body?: unknown,\n) => void | Promise<void>;\n\ntype RouteEntry =\n | {\n kind: \"fetch\";\n path: string;\n matcher: MatchFunction<ParamData>;\n prefix: boolean;\n handler: FetchHandler;\n }\n | {\n kind: \"get\";\n path: string;\n matcher: MatchFunction<ParamData>;\n handler: GetHandler;\n }\n | {\n kind: \"node\";\n method: string;\n path: string;\n matcher: MatchFunction<ParamData>;\n handler: NodeHandler;\n /** Dispatched ahead of the body parsers, with the stream unread. */\n rawBody: boolean;\n };\n\nexport class ExpressHttpAdapter implements IHttpAdapter {\n readonly #app: Express;\n readonly #router: IRouter;\n\n /**\n * Every route lives in this registry and is served by one\n * permanently-registered dispatcher (installed by setupMiddleware), because\n * Express 4 has no route-removal API: routes registered directly on the\n * app or router can never be taken back. Entries iterate in registration\n * order; mounting a path that already holds a route of the same kind (and\n * method, for node routes) replaces the old entry, so re-mounting is\n * last-write-wins without accumulating.\n */\n readonly #routes = new Map<number, RouteEntry>();\n #nextHandle = 0;\n\n constructor(existingApp?: Express) {\n this.#app = existingApp ?? expressLib();\n this.#router = Router();\n this.#app.use(this.#router);\n }\n\n setupSentryErrorHandler(sentry: object): void {\n const s = sentry as {\n setupExpressErrorHandler(app: Express): void;\n };\n s.setupExpressErrorHandler(this.#app);\n }\n\n get handle(): unknown {\n return this.#app;\n }\n\n mountRawMiddleware(middleware: unknown): void {\n // Connect/Express middleware shape; the caller owns its typing.\n this.#app.use(middleware as express.RequestHandler);\n }\n\n mountNodeRoute(\n method: HttpMethod,\n path: string,\n handler: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body?: unknown,\n ) => void | Promise<void>,\n { rawBody = false, prefix = false }: NodeRouteOptions = {},\n ): AdapterRouteHandle {\n this.#replaceDuplicates(\"node\", path, method.toUpperCase());\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"node\",\n method: method.toUpperCase(),\n path,\n matcher: match(normalizePath(path), { end: !prefix }),\n rawBody,\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n setupMiddleware({\n corsOptions,\n bodyLimit = \"50mb\",\n }: {\n corsOptions?: CorsOptions;\n bodyLimit?: string;\n }): void {\n this.#router.use(cors(corsOptions));\n\n // Ahead of the parsers: a raw route's whole point is that nothing has read\n // the stream. Behind them, `req` is a consumed stream and only a\n // re-encoded `req.body` survives — which cannot carry a signature over the\n // octets the client actually sent.\n this.#router.use((req, res, next) => {\n this.#dispatchNodeRoute(req, res, next, true);\n });\n\n this.#router.use(bodyParser.json({ limit: bodyLimit }));\n this.#router.use(\n bodyParser.urlencoded({ extended: true, limit: bodyLimit }),\n );\n\n // The single dispatch point for every registered route, registered\n // AFTER the body parsers so node routes receive a parsed req.body.\n // A request matching no route falls through to app-level middleware\n // (mountRawMiddleware) and then Express's default 404.\n this.#router.use((req, res, next) => {\n const pathname = req.path;\n\n // Node routes first, then GET routes, then Fetch mounts — the same\n // order the Fastify adapter uses. A node route is the most specific\n // registration there is (method plus exact path), while a Fetch mount is\n // often a prefix; letting the prefix win would silently shadow an exact\n // route, and would do it in only one of the two adapters.\n if (this.#dispatchNodeRoute(req, res, () => undefined, false)) return;\n\n if (req.method === \"GET\" || req.method === \"HEAD\") {\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"get\" || !entry.matcher(pathname)) continue;\n this.#serveGetEntry(entry, req, res);\n return;\n }\n }\n\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"fetch\" || !entry.matcher(pathname)) continue;\n this.#serveFetchHandler(entry.handler, req, res, next);\n return;\n }\n\n next();\n });\n }\n\n /**\n * Serves the first node route matching this request, or calls `next()`.\n * `raw` selects which half of the registry to consider, so the two passes —\n * one before the body parsers, one after — never serve the same route twice.\n */\n #dispatchNodeRoute(\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n raw: boolean,\n ): boolean {\n const pathname = req.path;\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"node\") continue;\n if (entry.rawBody !== raw) continue;\n if (entry.method !== req.method) continue;\n const matched = entry.matcher(pathname);\n if (!matched) continue;\n req.params = matched.params as Record<string, string>;\n // Fire-and-forget, as before: the node handler manages its own response\n // and the adapter does not await its promise.\n void entry.handler(req, res, entry.rawBody ? undefined : req.body);\n return true;\n }\n next();\n return false;\n }\n\n mount(\n path: string,\n handler: FetchHandler,\n { prefix, exact }: { prefix?: boolean; exact?: boolean } = {},\n ): AdapterRouteHandle {\n // `exact` never meant exact: it selected prefix matching. Kept as an alias\n // so existing callers keep working while the name is retired.\n const asPrefix = prefix ?? exact ?? false;\n this.#replaceDuplicates(\"fetch\", path);\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"fetch\",\n path,\n matcher: match(normalizePath(path), { end: !asPrefix }),\n prefix: asPrefix,\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n getRoute(\n path: string,\n handler: (request: Request) => Response | Promise<Response>,\n ): AdapterRouteHandle {\n this.#replaceDuplicates(\"get\", path);\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"get\",\n path,\n matcher: match(normalizePath(path)),\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n /**\n * Drop any existing entry the new registration would duplicate (same kind\n * and path, plus method for node routes) so a re-mount replaces instead of\n * shadowing. Map iteration tolerates concurrent deletion.\n */\n #replaceDuplicates(\n kind: RouteEntry[\"kind\"],\n path: string,\n method?: string,\n ): void {\n for (const [handle, entry] of this.#routes) {\n if (entry.kind !== kind || entry.path !== path) continue;\n if (kind === \"node\" && entry.kind === \"node\" && entry.method !== method) {\n continue;\n }\n this.#routes.delete(handle);\n }\n }\n\n async listen(port: number, tls?: TlsOptions): Promise<http.Server> {\n let server: http.Server;\n\n if (tls === true) {\n const { cert, key } = (await devcert.certificateFor(\"localhost\")) as {\n cert: Buffer;\n key: Buffer;\n };\n if (!cert || !key) {\n throw new Error(\"Invalid certificate generated\");\n }\n server = https.createServer({ cert, key }, this.#app);\n } else if (tls && \"keyPath\" in tls) {\n const currentDir = process.cwd();\n server = https.createServer(\n {\n key: fs.readFileSync(path.join(currentDir, tls.keyPath)),\n cert: fs.readFileSync(path.join(currentDir, tls.certPath)),\n },\n this.#app,\n );\n } else if (tls && \"cert\" in tls) {\n server = https.createServer({ cert: tls.cert, key: tls.key }, this.#app);\n } else {\n server = http.createServer(this.#app);\n }\n\n return new Promise<http.Server>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => {\n server.off(\"error\", reject);\n resolve(server);\n });\n });\n }\n\n #serveFetchHandler(\n handler: FetchHandler,\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ): void {\n // Build the full URL for the Fetch Request\n const protocol = req.protocol;\n const host = req.get(\"host\") ?? \"localhost\";\n const url = `${protocol}://${host}${req.originalUrl}`;\n\n // Convert Node.js incoming headers to Fetch Headers\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n\n // bodyParser has already run, so req.body is a parsed JS value.\n // Re-serialize it so the Fetch Request body stream is readable.\n let body: string | undefined;\n if (\n req.method !== \"GET\" &&\n req.method !== \"HEAD\" &&\n req.body !== undefined\n ) {\n body = JSON.stringify(req.body);\n }\n\n const fetchRequest = new Request(url, {\n method: req.method,\n headers,\n body,\n });\n\n Promise.resolve(handler(fetchRequest))\n .then(async (response) => {\n res.status(response.status);\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n if (response.body === null) {\n res.send(await response.text());\n return;\n }\n // Stream the body to the socket instead of awaiting .text(): a\n // stream-backed body (e.g. graphql-sse) only closes when the\n // subscription completes, so awaiting would hang the request.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n const nodeStream = Readable.fromWeb(bodyStream);\n req.on(\"close\", () => {\n // The client went away mid-stream; stop pulling from the source.\n nodeStream.destroy();\n });\n nodeStream.on(\"error\", (err) => {\n if (res.headersSent) {\n // The response has already started; nothing left to say.\n res.destroy();\n } else {\n next(err);\n }\n });\n nodeStream.pipe(res);\n })\n .catch(next);\n }\n\n #serveGetEntry(\n entry: Extract<RouteEntry, { kind: \"get\" }>,\n req: express.Request,\n res: express.Response,\n ): void {\n const protocol = req.protocol;\n const host = req.get(\"host\") ?? \"localhost\";\n const url = `${protocol}://${host}${req.originalUrl}`;\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n // GET is forced (matching the pre-refactor behavior, which also\n // normalized HEAD requests through the GET route handler).\n const fetchRequest = new Request(url, { method: \"GET\", headers });\n Promise.resolve(entry.handler(fetchRequest))\n .then(async (response) => {\n res.status(response.status);\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n if (response.body === null) {\n res.send(await response.text());\n return;\n }\n // Stream the body to the socket instead of awaiting .text(): a\n // stream-backed body (e.g. graphql-sse) only closes when the\n // subscription completes, so awaiting would hang the request.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n const nodeStream = Readable.fromWeb(bodyStream);\n req.on(\"close\", () => {\n // The client went away mid-stream; stop pulling from the source.\n nodeStream.destroy();\n });\n nodeStream.on(\"error\", (err) => {\n if (res.headersSent) {\n // The response has already started; nothing left to say.\n res.destroy();\n } else {\n res.status(500).send(String(err));\n }\n });\n nodeStream.pipe(res);\n })\n .catch((err: unknown) => {\n res.status(500).send(String(err));\n });\n }\n}\n\nexport function createExpressHttpAdapter(existingApp?: Express): {\n adapter: IHttpAdapter;\n} {\n return { adapter: new ExpressHttpAdapter(existingApp) };\n}\n"],"names":["#app","#router","#routes","#replaceDuplicates","#nextHandle","#dispatchNodeRoute","#serveGetEntry","#serveFetchHandler"],"mappings":"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","debug_id":"af482776-5e68-5345-a965-0937af984537"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-http-fastify-BwXO6rqE.mjs","sources":["../src/graphql/gateway/adapter-http-fastify.ts"],"sourcesContent":["import fastifyCors from \"@fastify/cors\";\nimport fastifyFormbody from \"@fastify/formbody\";\nimport fastifyMiddie from \"@fastify/middie\";\nimport type { CorsOptions } from \"cors\";\nimport devcert from \"devcert\";\nimport Fastify from \"fastify\";\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport nodePath from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as WebReadableStream } from \"node:stream/web\";\nimport { match, type MatchFunction, type ParamData } from \"path-to-regexp\";\nimport type {\n FetchHandler,\n HttpMethod,\n IHttpAdapter,\n NodeRouteOptions,\n AdapterRouteHandle,\n TlsOptions,\n} from \"./types.js\";\nimport { normalizePath } from \"./path-normalize.js\";\n\n/** Parses body-limit strings like \"50mb\" to bytes. */\nfunction parseBodyLimit(limit: string): number {\n const m = /^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb)?$/i.exec(limit.trim());\n if (!m) return 52_428_800; // 50 MB fallback\n const n = parseFloat(m[1]);\n const units: Record<string, number> = {\n b: 1,\n kb: 1_024,\n mb: 1_048_576,\n gb: 1_073_741_824,\n };\n return Math.round(n * (units[(m[2] ?? \"b\").toLowerCase()] ?? 1));\n}\n\ntype FetchEntry = {\n path: string;\n handler: FetchHandler;\n matcher: MatchFunction<ParamData>;\n prefix: boolean;\n};\n\ntype GetEntry = {\n path: string;\n handler: (r: Request) => Response | Promise<Response>;\n matcher: MatchFunction<ParamData>;\n};\n\ntype NodeHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body?: unknown,\n) => void;\n\ntype NodeEntry = {\n path: string;\n method: HttpMethod;\n matcher: MatchFunction<ParamData>;\n handler: NodeHandler;\n /** Claimed in the onRequest hook, before any body parsing. */\n rawBody: boolean;\n};\n\n// Pre-listen configuration ops (need the Fastify instance to apply).\ntype SetupOp =\n | { kind: \"cors\"; options?: CorsOptions; bodyLimit: number }\n | { kind: \"middie\"; middleware: unknown };\n\nexport class FastifyHttpAdapter implements IHttpAdapter {\n // Dispatch maps, keyed by route handle and iterated in registration\n // order — populated at any time (before or after listen). Mounting a\n // path that already holds a route of the same kind (and method, for\n // node routes) replaces the old entry, so re-mounting is last-write-\n // wins without accumulating; a handle's dispose() removes its own entry.\n readonly #fetchRoutes = new Map<number, FetchEntry>();\n readonly #getRoutes = new Map<number, GetEntry>();\n readonly #nodeRoutes = new Map<number, NodeEntry>();\n #nextHandle = 0;\n\n // Ops that need the Fastify instance (CORS config, Connect middleware).\n readonly #setupOps: SetupOp[] = [];\n\n #instance: FastifyInstance | undefined;\n\n get handle(): unknown {\n return this.#instance;\n }\n\n setupMiddleware({\n corsOptions,\n bodyLimit = \"50mb\",\n }: {\n corsOptions?: CorsOptions;\n bodyLimit?: string;\n }): void {\n this.#setupOps.push({\n kind: \"cors\",\n options: corsOptions,\n bodyLimit: parseBodyLimit(bodyLimit),\n });\n }\n\n mount(\n path: string,\n handler: FetchHandler,\n { prefix, exact }: { prefix?: boolean; exact?: boolean } = {},\n ): AdapterRouteHandle {\n // `exact` never meant exact: it selected prefix matching. Kept as an alias\n // so existing callers keep working while the name is retired.\n const asPrefix = prefix ?? exact ?? false;\n for (const [handle, entry] of this.#fetchRoutes) {\n if (entry.path === path) this.#fetchRoutes.delete(handle);\n }\n const handle = this.#nextHandle++;\n this.#fetchRoutes.set(handle, {\n path,\n handler,\n matcher: match(normalizePath(path), { end: !asPrefix }),\n prefix: asPrefix,\n });\n return { dispose: () => this.#fetchRoutes.delete(handle) };\n }\n\n getRoute(\n path: string,\n handler: (r: Request) => Response | Promise<Response>,\n ): AdapterRouteHandle {\n for (const [handle, entry] of this.#getRoutes) {\n if (entry.path === path) this.#getRoutes.delete(handle);\n }\n const handle = this.#nextHandle++;\n this.#getRoutes.set(handle, {\n path,\n handler,\n matcher: match(normalizePath(path)),\n });\n return { dispose: () => this.#getRoutes.delete(handle) };\n }\n\n mountNodeRoute(\n method: HttpMethod,\n path: string,\n handler: NodeHandler,\n { rawBody = false, prefix = false }: NodeRouteOptions = {},\n ): AdapterRouteHandle {\n for (const [handle, entry] of this.#nodeRoutes) {\n if (entry.path === path && entry.method === method) {\n this.#nodeRoutes.delete(handle);\n }\n }\n const handle = this.#nextHandle++;\n this.#nodeRoutes.set(handle, {\n path,\n method,\n matcher: match(normalizePath(path), { end: !prefix }),\n handler,\n rawBody,\n });\n return { dispose: () => this.#nodeRoutes.delete(handle) };\n }\n\n mountRawMiddleware(middleware: unknown): void {\n if (this.#instance) {\n // @fastify/middie is always registered, so .use() is available post-listen.\n (\n this.#instance as FastifyInstance & {\n use(middleware: unknown): FastifyInstance;\n }\n ).use(middleware);\n } else {\n this.#setupOps.push({ kind: \"middie\", middleware });\n }\n }\n\n setupSentryErrorHandler(sentry: object): void {\n if (!this.#instance) return;\n const s = sentry as {\n setupFastifyErrorHandler(app: FastifyInstance): void;\n };\n s.setupFastifyErrorHandler(this.#instance);\n }\n\n async listen(port: number, tls?: TlsOptions): Promise<http.Server> {\n let httpServer: http.Server;\n\n if (tls === true) {\n const { cert, key } = (await devcert.certificateFor(\"localhost\")) as {\n cert: Buffer;\n key: Buffer;\n };\n if (!cert || !key) throw new Error(\"Invalid certificate generated\");\n httpServer = https.createServer({ cert, key });\n } else if (tls && \"keyPath\" in tls) {\n const cwd = process.cwd();\n httpServer = https.createServer({\n key: fs.readFileSync(nodePath.join(cwd, tls.keyPath)),\n cert: fs.readFileSync(nodePath.join(cwd, tls.certPath)),\n });\n } else if (tls && \"cert\" in tls) {\n httpServer = https.createServer({ cert: tls.cert, key: tls.key });\n } else {\n httpServer = http.createServer();\n }\n\n const corsOp = this.#setupOps.find(\n (op): op is Extract<SetupOp, { kind: \"cors\" }> => op.kind === \"cors\",\n );\n const bodyLimit = corsOp?.bodyLimit ?? 52_428_800;\n\n const instance = Fastify({\n serverFactory: (handler) => {\n httpServer.on(\"request\", handler);\n return httpServer;\n },\n bodyLimit,\n logger: false,\n });\n\n this.#instance = instance;\n\n // Always register middie first so .use() is available post-listen.\n await instance.register(fastifyMiddie);\n await instance.register(fastifyFormbody);\n\n for (const op of this.#setupOps) {\n if (op.kind === \"cors\") {\n await instance.register(\n fastifyCors,\n op.options as Parameters<typeof fastifyCors>[1],\n );\n } else {\n (\n instance as FastifyInstance & {\n use(middleware: unknown): FastifyInstance;\n }\n ).use(op.middleware);\n }\n }\n\n // Raw routes are claimed here, in the earliest hook there is: Fastify\n // parses the body before the handler runs, and it 415s an unknown content\n // type outright, so a route that needs the octets as sent cannot wait for\n // dispatch. `hijack()` takes the reply out of Fastify's lifecycle and the\n // handler owns the socket from here.\n instance.addHook(\"onRequest\", (req, reply, done) => {\n const claimed = this.#matchNodeRoute(req, true);\n if (!claimed) {\n done();\n return;\n }\n (req.raw as http.IncomingMessage & { params?: ParamData }).params =\n claimed.params;\n reply.hijack();\n claimed.route.handler(req.raw, reply.raw, undefined);\n });\n\n // Single catch-all route — all dispatching is done via the Maps above so\n // that routes registered after listen() are picked up automatically.\n // OPTIONS is excluded because @fastify/cors registers its own OPTIONS /*\n // handler for preflight; including it here would cause a duplicate-route\n // conflict at startup.\n instance.route({\n method: [\"DELETE\", \"GET\", \"HEAD\", \"PATCH\", \"POST\", \"PUT\"],\n url: \"/*\",\n handler: (req, reply) => this.#dispatch(req, reply),\n });\n\n await instance.ready();\n\n return new Promise<http.Server>((resolve, reject) => {\n httpServer.once(\"error\", reject);\n httpServer.listen(port, () => {\n httpServer.off(\"error\", reject);\n resolve(httpServer);\n });\n });\n }\n\n /**\n * Finds a node route for this request. `raw` selects which half of the\n * registry to consider: raw routes are claimed in the onRequest hook, the\n * rest in the catch-all handler once the body is parsed.\n */\n #matchNodeRoute(\n req: FastifyRequest,\n raw: boolean,\n ): { route: NodeEntry; params: ParamData } | undefined {\n const pathname = new URL(req.url, \"http://localhost\").pathname;\n const method = req.method.toUpperCase();\n for (const entry of this.#nodeRoutes.values()) {\n if (entry.rawBody !== raw) continue;\n if (entry.method !== method) continue;\n const result = entry.matcher(pathname);\n if (result) return { route: entry, params: result.params };\n }\n return undefined;\n }\n\n #dispatch(\n req: FastifyRequest,\n reply: FastifyReply,\n ): void | Promise<FastifyReply> {\n const pathname = new URL(req.url, \"http://localhost\").pathname;\n const method = req.method.toUpperCase() as\n | \"DELETE\"\n | \"GET\"\n | \"HEAD\"\n | \"PATCH\"\n | \"POST\"\n | \"PUT\";\n\n // 1. Node routes — path-to-regexp match + method, handler manages raw\n // response. Attach decoded params onto `req.raw` so downstream handlers\n // can read them via the same `req.params` API the Express adapter exposes.\n const node = this.#matchNodeRoute(req, false);\n if (node) {\n (req.raw as http.IncomingMessage & { params?: ParamData }).params =\n node.params;\n reply.hijack();\n node.route.handler(req.raw, reply.raw, req.body);\n return;\n }\n\n // 2. GET-specific routes (health, explorer, etc.).\n if (method === \"GET\") {\n for (const entry of this.#getRoutes.values()) {\n if (entry.matcher(pathname)) {\n return this.#serveGetEntry(entry, req, reply);\n }\n }\n }\n\n // 3. Fetch routes (GraphQL handlers, SSE, etc.).\n // Iterate in reverse so that the last-mounted handler wins when the same\n // path is mounted more than once (e.g. supergraph remount after reload).\n for (const entry of [...this.#fetchRoutes.values()].reverse()) {\n if (entry.matcher(pathname)) {\n return serveFetchHandler(entry.handler, req, reply);\n }\n }\n\n void reply.status(404).send({\n message: `Route ${req.method}:${pathname} not found`,\n error: \"Not Found\",\n statusCode: 404,\n });\n }\n\n async #serveGetEntry(\n entry: GetEntry,\n req: FastifyRequest,\n reply: FastifyReply,\n ): Promise<FastifyReply> {\n const url = buildUrl(req);\n const headers = buildHeaders(req);\n const fetchReq = new Request(url, { method: \"GET\", headers });\n const response = await entry.handler(fetchReq);\n writeResponse(reply, response);\n if (response.body === null) {\n return reply.send(await response.text());\n }\n // Stream the body instead of awaiting .text(): a stream-backed body\n // (e.g. graphql-sse) only closes when the subscription completes, so\n // awaiting would hang the request. Fastify tears the source down on\n // client disconnect and routes pre-header stream errors to the\n // error handler.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n return reply.send(Readable.fromWeb(bodyStream));\n }\n}\n\nasync function serveFetchHandler(\n handler: FetchHandler,\n req: FastifyRequest,\n reply: FastifyReply,\n): Promise<FastifyReply> {\n const url = buildUrl(req);\n const headers = buildHeaders(req);\n let body: string | undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\" && req.body !== undefined) {\n body = JSON.stringify(req.body);\n }\n const response = await handler(\n new Request(url, { method: req.method, headers, body }),\n );\n writeResponse(reply, response);\n if (response.body === null) {\n return reply.send(await response.text());\n }\n // Stream the body instead of awaiting .text(): a stream-backed body\n // (e.g. graphql-sse) only closes when the subscription completes, so\n // awaiting would hang the request. Fastify tears the source down on\n // client disconnect and routes pre-header stream errors to the\n // error handler.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n return reply.send(Readable.fromWeb(bodyStream));\n}\n\nfunction buildUrl(req: FastifyRequest): string {\n const protocol = req.protocol;\n const host = req.headers.host ?? \"localhost\";\n return `${protocol}://${host}${req.url}`;\n}\n\nfunction buildHeaders(req: FastifyRequest): Headers {\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n return headers;\n}\n\nfunction writeResponse(reply: FastifyReply, response: Response): void {\n reply.statusCode = response.status;\n response.headers.forEach((value, key) => {\n reply.header(key, value);\n });\n}\n\nexport function createFastifyHttpAdapter(): { adapter: IHttpAdapter } {\n return { adapter: new FastifyHttpAdapter() };\n}\n"],"names":["#fetchRoutes","#getRoutes","#nodeRoutes","#setupOps","#instance","#nextHandle","nodePath","#matchNodeRoute","#dispatch","#serveGetEntry"],"mappings":";;;;;;;;;;;;;;;;AAyBA,SAAS,eAAe,OAAuB;CAC7C,MAAM,IAAI,qCAAqC,KAAK,MAAM,MAAM,CAAC;AACjE,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,IAAI,WAAW,EAAE,GAAG;AAO1B,QAAO,KAAK,MAAM,KANoB;EACpC,GAAG;EACH,IAAI;EACJ,IAAI;EACJ,IAAI;EACL,EAC6B,EAAE,MAAM,KAAK,aAAa,KAAK,GAAG;;AAoClE,IAAa,qBAAb,MAAwD;CAMtD,+BAAwB,IAAI,KAAyB;CACrD,6BAAsB,IAAI,KAAuB;CACjD,8BAAuB,IAAI,KAAwB;CACnD,cAAc;CAGd,YAAgC,EAAE;CAElC;CAEA,IAAI,SAAkB;AACpB,SAAO,MAAA;;CAGT,gBAAgB,EACd,aACA,YAAY,UAIL;AACP,QAAA,SAAe,KAAK;GAClB,MAAM;GACN,SAAS;GACT,WAAW,eAAe,UAAU;GACrC,CAAC;;CAGJ,MACE,MACA,SACA,EAAE,QAAQ,UAAiD,EAAE,EACzC;EAGpB,MAAM,WAAW,UAAU,SAAS;AACpC,OAAK,MAAM,CAAC,QAAQ,UAAU,MAAA,YAC5B,KAAI,MAAM,SAAS,KAAM,OAAA,YAAkB,OAAO,OAAO;EAE3D,MAAM,SAAS,MAAA;AACf,QAAA,YAAkB,IAAI,QAAQ;GAC5B;GACA;GACA,SAAS,MAAM,cAAc,KAAK,EAAE,EAAE,KAAK,CAAC,UAAU,CAAC;GACvD,QAAQ;GACT,CAAC;AACF,SAAO,EAAE,eAAe,MAAA,YAAkB,OAAO,OAAO,EAAE;;CAG5D,SACE,MACA,SACoB;AACpB,OAAK,MAAM,CAAC,QAAQ,UAAU,MAAA,UAC5B,KAAI,MAAM,SAAS,KAAM,OAAA,UAAgB,OAAO,OAAO;EAEzD,MAAM,SAAS,MAAA;AACf,QAAA,UAAgB,IAAI,QAAQ;GAC1B;GACA;GACA,SAAS,MAAM,cAAc,KAAK,CAAC;GACpC,CAAC;AACF,SAAO,EAAE,eAAe,MAAA,UAAgB,OAAO,OAAO,EAAE;;CAG1D,eACE,QACA,MACA,SACA,EAAE,UAAU,OAAO,SAAS,UAA4B,EAAE,EACtC;AACpB,OAAK,MAAM,CAAC,QAAQ,UAAU,MAAA,WAC5B,KAAI,MAAM,SAAS,QAAQ,MAAM,WAAW,OAC1C,OAAA,WAAiB,OAAO,OAAO;EAGnC,MAAM,SAAS,MAAA;AACf,QAAA,WAAiB,IAAI,QAAQ;GAC3B;GACA;GACA,SAAS,MAAM,cAAc,KAAK,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC;GACrD;GACA;GACD,CAAC;AACF,SAAO,EAAE,eAAe,MAAA,WAAiB,OAAO,OAAO,EAAE;;CAG3D,mBAAmB,YAA2B;AAC5C,MAAI,MAAA,SAGA,OAAA,SAGA,IAAI,WAAW;MAEjB,OAAA,SAAe,KAAK;GAAE,MAAM;GAAU;GAAY,CAAC;;CAIvD,wBAAwB,QAAsB;AAC5C,MAAI,CAAC,MAAA,SAAgB;AACX,SAGR,yBAAyB,MAAA,SAAe;;CAG5C,MAAM,OAAO,MAAc,KAAwC;EACjE,IAAI;AAEJ,MAAI,QAAQ,MAAM;GAChB,MAAM,EAAE,MAAM,QAAS,MAAM,QAAQ,eAAe,YAAY;AAIhE,OAAI,CAAC,QAAQ,CAAC,IAAK,OAAM,IAAI,MAAM,gCAAgC;AACnE,gBAAa,MAAM,aAAa;IAAE;IAAM;IAAK,CAAC;aACrC,OAAO,aAAa,KAAK;GAClC,MAAM,MAAM,QAAQ,KAAK;AACzB,gBAAa,MAAM,aAAa;IAC9B,KAAK,GAAG,aAAaM,KAAS,KAAK,KAAK,IAAI,QAAQ,CAAC;IACrD,MAAM,GAAG,aAAaA,KAAS,KAAK,KAAK,IAAI,SAAS,CAAC;IACxD,CAAC;aACO,OAAO,UAAU,IAC1B,cAAa,MAAM,aAAa;GAAE,MAAM,IAAI;GAAM,KAAK,IAAI;GAAK,CAAC;MAEjE,cAAa,KAAK,cAAc;EAQlC,MAAM,WAAW,QAAQ;GACvB,gBAAgB,YAAY;AAC1B,eAAW,GAAG,WAAW,QAAQ;AACjC,WAAO;;GAET,WAVa,MAAA,SAAe,MAC3B,OAAiD,GAAG,SAAS,OAC/D,EACyB,aAAa;GAQrC,QAAQ;GACT,CAAC;AAEF,QAAA,WAAiB;AAGjB,QAAM,SAAS,SAAS,cAAc;AACtC,QAAM,SAAS,SAAS,gBAAgB;AAExC,OAAK,MAAM,MAAM,MAAA,SACf,KAAI,GAAG,SAAS,OACd,OAAM,SAAS,SACb,aACA,GAAG,QACJ;MAGC,UAGA,IAAI,GAAG,WAAW;AASxB,WAAS,QAAQ,cAAc,KAAK,OAAO,SAAS;GAClD,MAAM,UAAU,MAAA,eAAqB,KAAK,KAAK;AAC/C,OAAI,CAAC,SAAS;AACZ,UAAM;AACN;;AAED,OAAI,IAAsD,SACzD,QAAQ;AACV,SAAM,QAAQ;AACd,WAAQ,MAAM,QAAQ,IAAI,KAAK,MAAM,KAAK,KAAA,EAAU;IACpD;AAOF,WAAS,MAAM;GACb,QAAQ;IAAC;IAAU;IAAO;IAAQ;IAAS;IAAQ;IAAM;GACzD,KAAK;GACL,UAAU,KAAK,UAAU,MAAA,SAAe,KAAK,MAAM;GACpD,CAAC;AAEF,QAAM,SAAS,OAAO;AAEtB,SAAO,IAAI,SAAsB,SAAS,WAAW;AACnD,cAAW,KAAK,SAAS,OAAO;AAChC,cAAW,OAAO,YAAY;AAC5B,eAAW,IAAI,SAAS,OAAO;AAC/B,YAAQ,WAAW;KACnB;IACF;;;;;;;CAQJ,gBACE,KACA,KACqD;EACrD,MAAM,WAAW,IAAI,IAAI,IAAI,KAAK,mBAAmB,CAAC;EACtD,MAAM,SAAS,IAAI,OAAO,aAAa;AACvC,OAAK,MAAM,SAAS,MAAA,WAAiB,QAAQ,EAAE;AAC7C,OAAI,MAAM,YAAY,IAAK;AAC3B,OAAI,MAAM,WAAW,OAAQ;GAC7B,MAAM,SAAS,MAAM,QAAQ,SAAS;AACtC,OAAI,OAAQ,QAAO;IAAE,OAAO;IAAO,QAAQ,OAAO;IAAQ;;;CAK9D,UACE,KACA,OAC8B;EAC9B,MAAM,WAAW,IAAI,IAAI,IAAI,KAAK,mBAAmB,CAAC;EACtD,MAAM,SAAS,IAAI,OAAO,aAAa;EAWvC,MAAM,OAAO,MAAA,eAAqB,KAAK,MAAM;AAC7C,MAAI,MAAM;AACP,OAAI,IAAsD,SACzD,KAAK;AACP,SAAM,QAAQ;AACd,QAAK,MAAM,QAAQ,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK;AAChD;;AAIF,MAAI,WAAW;QACR,MAAM,SAAS,MAAA,UAAgB,QAAQ,CAC1C,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO,MAAA,cAAoB,OAAO,KAAK,MAAM;;AAQnD,OAAK,MAAM,SAAS,CAAC,GAAG,MAAA,YAAkB,QAAQ,CAAC,CAAC,SAAS,CAC3D,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO,kBAAkB,MAAM,SAAS,KAAK,MAAM;AAIlD,QAAM,OAAO,IAAI,CAAC,KAAK;GAC1B,SAAS,SAAS,IAAI,OAAO,GAAG,SAAS;GACzC,OAAO;GACP,YAAY;GACb,CAAC;;CAGJ,OAAA,cACE,OACA,KACA,OACuB;EACvB,MAAM,MAAM,SAAS,IAAI;EACzB,MAAM,UAAU,aAAa,IAAI;EACjC,MAAM,WAAW,IAAI,QAAQ,KAAK;GAAE,QAAQ;GAAO;GAAS,CAAC;EAC7D,MAAM,WAAW,MAAM,MAAM,QAAQ,SAAS;AAC9C,gBAAc,OAAO,SAAS;AAC9B,MAAI,SAAS,SAAS,KACpB,QAAO,MAAM,KAAK,MAAM,SAAS,MAAM,CAAC;EAU1C,MAAM,aAAa,SAAS;AAC5B,SAAO,MAAM,KAAK,SAAS,QAAQ,WAAW,CAAC;;;AAInD,eAAe,kBACb,SACA,KACA,OACuB;CACvB,MAAM,MAAM,SAAS,IAAI;CACzB,MAAM,UAAU,aAAa,IAAI;CACjC,IAAI;AACJ,KAAI,IAAI,WAAW,SAAS,IAAI,WAAW,UAAU,IAAI,SAAS,KAAA,EAChE,QAAO,KAAK,UAAU,IAAI,KAAK;CAEjC,MAAM,WAAW,MAAM,QACrB,IAAI,QAAQ,KAAK;EAAE,QAAQ,IAAI;EAAQ;EAAS;EAAM,CAAC,CACxD;AACD,eAAc,OAAO,SAAS;AAC9B,KAAI,SAAS,SAAS,KACpB,QAAO,MAAM,KAAK,MAAM,SAAS,MAAM,CAAC;CAU1C,MAAM,aAAa,SAAS;AAC5B,QAAO,MAAM,KAAK,SAAS,QAAQ,WAAW,CAAC;;AAGjD,SAAS,SAAS,KAA6B;AAG7C,QAAO,GAFU,IAAI,SAEF,KADN,IAAI,QAAQ,QAAQ,cACF,IAAI;;AAGrC,SAAS,aAAa,KAA8B;CAClD,MAAM,UAAU,IAAI,SAAS;AAC7B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,QAAQ,CACpD,KAAI,OAAO,UAAU,SACnB,SAAQ,IAAI,KAAK,MAAM;UACd,MAAM,QAAQ,MAAM,CAC7B,SAAQ,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC;AAGtC,QAAO;;AAGT,SAAS,cAAc,OAAqB,UAA0B;AACpE,OAAM,aAAa,SAAS;AAC5B,UAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,QAAM,OAAO,KAAK,MAAM;GACxB;;AAGJ,SAAgB,2BAAsD;AACpE,QAAO,EAAE,SAAS,IAAI,oBAAoB,EAAE","debug_id":"07c62958-83d2-55bc-b138-149275988013"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//#region src/graphql/gateway/path-normalize.ts
|
|
2
|
-
/**
|
|
3
|
-
* Normalises a route path for path-to-regexp v8:
|
|
4
|
-
* - Collapses duplicate slashes (e.g. "//explorer" → "/explorer")
|
|
5
|
-
* - Converts legacy optional-param syntax "/:param?" → "{/:param}". The
|
|
6
|
-
* slash must move inside the group: in p-r v8 a group whose content
|
|
7
|
-
* begins with "/" is what makes the slash and the parameter optional
|
|
8
|
-
* together, while a literal "/" left in front of the group compiles to
|
|
9
|
-
* a pattern that matches nothing (and v8 rejects a bare ":param?").
|
|
10
|
-
*
|
|
11
|
-
* Shared by both http adapters so registry dispatch matches the same path
|
|
12
|
-
* shapes regardless of which framework is in use.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
!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]="f73d6dcb-dc2c-52e3-abd2-3653df07260f")}catch(e){}}();
|
|
16
|
-
function normalizePath(path) {
|
|
17
|
-
return path.replace(/\/+/g, "/").replace(/\/:(\w+)\?/g, "{/:$1}");
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { normalizePath as t };
|
|
21
|
-
|
|
22
|
-
//# sourceMappingURL=path-normalize-BOQwY9wR.mjs.map
|
|
23
|
-
//# debugId=f73d6dcb-dc2c-52e3-abd2-3653df07260f
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"path-normalize-BOQwY9wR.mjs","sources":["../src/graphql/gateway/path-normalize.ts"],"sourcesContent":["/**\n * Normalises a route path for path-to-regexp v8:\n * - Collapses duplicate slashes (e.g. \"//explorer\" → \"/explorer\")\n * - Converts legacy optional-param syntax \"/:param?\" → \"{/:param}\". The\n * slash must move inside the group: in p-r v8 a group whose content\n * begins with \"/\" is what makes the slash and the parameter optional\n * together, while a literal \"/\" left in front of the group compiles to\n * a pattern that matches nothing (and v8 rejects a bare \":param?\").\n *\n * Shared by both http adapters so registry dispatch matches the same path\n * shapes regardless of which framework is in use.\n */\nexport function normalizePath(path: string): string {\n return path.replace(/\\/+/g, \"/\").replace(/\\/:(\\w+)\\?/g, \"{/:$1}\");\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAYA,SAAgB,cAAc,MAAsB;AAClD,QAAO,KAAK,QAAQ,QAAQ,IAAI,CAAC,QAAQ,eAAe,SAAS","debug_id":"f73d6dcb-dc2c-52e3-abd2-3653df07260f"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types-DcYyNcHJ.d.mts","names":[],"sources":["../src/packages/types.ts"],"mappings":";;;;;UAUiB,cAAA;EACf,IAAA;EACA,kBAAA,CACE,UAAA,UACA,SAAA,aACC,OAAA,CAAQ,mBAAA;EALkB;;;;EAU7B,oBAAA,EACE,UAAA,WACC,OAAA,CAAQ,eAAA;EACX,aAAA,CACE,UAAA,UACA,SAAA,aACC,OAAA,CAAQ,aAAA;EACX,cAAA,CACE,UAAA,UACA,SAAA,aACC,OAAA,CAAQ,uBAAA;AAAA;AAAA,UAGI,oBAAA;EACf,QAAA;AAAA;AAAA,UAGe,0BAAA,SAAmC,cAAA;EAClD,sBAAA,EACE,UAAA,UACA,OAAA,GAAU,cAAA,EAAgB,mBAAA,aAC1B,OAAA,GAAU,oBAAA;EAEZ,iBAAA,EACE,UAAA,UACA,OAAA,GAAU,SAAA,EAAW,aAAA,aACrB,OAAA,GAAU,oBAAA;EAEZ,kBAAA,EACE,UAAA,UACA,OAAA,GAAU,UAAA,EAAY,uBAAA,kBACtB,OAAA,GAAU,oBAAA;AAAA;AAAA,UAIG,eAAA;EACf,sBAAA,CACE,OAAA,GAAU,cAAA,EAAgB,MAAA,SAAe,mBAAA;EArC3C;;;;EA2CA,aAAA,CAAc,WAAA;AAAA;AAAA,KAGJ,qBAAA;EACV,MAAA,GAAS,OAAA;AAAA;AAAA,KAGC,sBAAA;EACV,QAAA;EACA,UAAA;AAAA;AAAA,KAWU,oBAAA;EACV,cAAA,EAAgB,mBAAA;EAChB,gBAAA,EAAkB,eAAA;EAClB,SAAA,EAAW,GAAA,SAAY,aAAA;EACvB,UAAA,EAAY,GAAA,SAAY,uBAAA;AAAA"}
|