@powerhousedao/reactor-api 6.2.3-dev.5 → 6.2.3-dev.50

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.
Files changed (45) hide show
  1. package/README.md +7 -2
  2. package/dist/{adapter-gateway-apollo-CxJNBVyD.mjs → adapter-gateway-apollo-B54G_cXT.mjs} +15 -13
  3. package/dist/{adapter-gateway-apollo-CxJNBVyD.mjs.map → adapter-gateway-apollo-B54G_cXT.mjs.map} +1 -1
  4. package/dist/{adapter-gateway-apollo-DowrX6E_.mjs → adapter-gateway-apollo-D2DNlPY0.mjs} +3 -3
  5. package/dist/{adapter-gateway-mercurius-CZVy88Za.mjs → adapter-gateway-mercurius-rbKlwF9m.mjs} +4 -4
  6. package/dist/{adapter-gateway-mercurius-CZVy88Za.mjs.map → adapter-gateway-mercurius-rbKlwF9m.mjs.map} +1 -1
  7. package/dist/{adapter-gateway-stitching-CaZjvRzx.mjs → adapter-gateway-stitching-Dzv10u8D.mjs} +15 -10
  8. package/dist/{adapter-gateway-stitching-CaZjvRzx.mjs.map → adapter-gateway-stitching-Dzv10u8D.mjs.map} +1 -1
  9. package/dist/{adapter-http-express-B39rPhqH.mjs → adapter-http-express-P0t-mdMV.mjs} +10 -10
  10. package/dist/adapter-http-express-P0t-mdMV.mjs.map +1 -0
  11. package/dist/{adapter-http-fastify-BwXO6rqE.mjs → adapter-http-fastify-D0OP5q9K.mjs} +13 -12
  12. package/dist/adapter-http-fastify-D0OP5q9K.mjs.map +1 -0
  13. package/dist/index.d.mts +1621 -604
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +2622 -617
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/pglite-node-Ow9i5Rb4.mjs +573 -0
  18. package/dist/pglite-node-Ow9i5Rb4.mjs.map +1 -0
  19. package/dist/rolldown-runtime-8H4AJuhK.mjs +16 -0
  20. package/dist/run-node-handler-CQjyjOuN.mjs +52 -0
  21. package/dist/run-node-handler-CQjyjOuN.mjs.map +1 -0
  22. package/dist/src/packages/https-hooks.d.mts +4 -4
  23. package/dist/src/packages/https-hooks.d.mts.map +1 -1
  24. package/dist/src/packages/https-hooks.mjs +2 -2
  25. package/dist/src/packages/https-hooks.mjs.map +1 -1
  26. package/dist/src/packages/vite-loader.d.mts +19 -9
  27. package/dist/src/packages/vite-loader.d.mts.map +1 -1
  28. package/dist/src/packages/vite-loader.mjs +114 -50
  29. package/dist/src/packages/vite-loader.mjs.map +1 -1
  30. package/dist/src/pglite/pglite-node.d.mts +130 -0
  31. package/dist/src/pglite/pglite-node.d.mts.map +1 -0
  32. package/dist/src/pglite/pglite-node.mjs +4 -0
  33. package/dist/{utils-XWVecaNn.mjs → subgraph-extraction-DTFoN_bS.mjs} +266 -11
  34. package/dist/subgraph-extraction-DTFoN_bS.mjs.map +1 -0
  35. package/dist/{types-DcYyNcHJ.d.mts → types-DlTiz6AA.d.mts} +27 -4
  36. package/dist/types-DlTiz6AA.d.mts.map +1 -0
  37. package/dist/{websocket-DncAB9XK.mjs → websocket-_SH5aBcy.mjs} +3 -3
  38. package/dist/{websocket-DncAB9XK.mjs.map → websocket-_SH5aBcy.mjs.map} +1 -1
  39. package/package.json +24 -19
  40. package/dist/adapter-http-express-B39rPhqH.mjs.map +0 -1
  41. package/dist/adapter-http-fastify-BwXO6rqE.mjs.map +0 -1
  42. package/dist/path-normalize-BOQwY9wR.mjs +0 -23
  43. package/dist/path-normalize-BOQwY9wR.mjs.map +0 -1
  44. package/dist/types-DcYyNcHJ.d.mts.map +0 -1
  45. package/dist/utils-XWVecaNn.mjs.map +0 -1
@@ -0,0 +1,130 @@
1
+ import { ILogger } from "document-model";
2
+ import nodeFs from "node:fs";
3
+ import { PGlite } from "@electric-sql/pglite";
4
+ import { NodeFS } from "@electric-sql/pglite/nodefs";
5
+ //#region src/pglite/maintenance.d.ts
6
+ export declare const DEFAULT_MAINTENANCE_INTERVAL_MS: number;
7
+ export declare const DEFAULT_VACUUM_FULL_ABOVE_BYTES: number;
8
+ type MaintenanceOutcome = "vacuum" | "vacuum-full" | "idle" | "not-ready" | "in-transaction" | "failed";
9
+ interface MaintenanceLogger {
10
+ warn(message: string): void;
11
+ }
12
+ interface MaintenanceOptions {
13
+ /** VACUUM + CHECKPOINT cadence; PGlite has no autovacuum. Default 5 min; 0 disables. */
14
+ intervalMs?: number;
15
+ /** First pass runs VACUUM FULL when `base/` exceeds this at open. Default 256 MB; 0 disables. */
16
+ vacuumFullAboveBytes?: number;
17
+ logger?: MaintenanceLogger;
18
+ }
19
+ export declare class PgliteMaintenance {
20
+ private readonly intervalMs;
21
+ private readonly vacuumFullAboveBytes;
22
+ private readonly logger?;
23
+ private pg?;
24
+ private label;
25
+ private closing;
26
+ private syncsSinceMaintenance;
27
+ private vacuumFullPending;
28
+ private timer?;
29
+ private inFlight?;
30
+ constructor(options?: MaintenanceOptions);
31
+ get scheduled(): boolean;
32
+ get running(): boolean;
33
+ start(pg: PGlite, label: string, baseBytes: number): void;
34
+ noteSync(): void;
35
+ stop(): Promise<void>;
36
+ run(): Promise<MaintenanceOutcome>;
37
+ private schedule;
38
+ private cancel;
39
+ }
40
+ //#endregion
41
+ //#region src/pglite/durable-node-fs.d.ts
42
+ type NodeFsClass = typeof NodeFS;
43
+ interface DurableNodeFsOptions {
44
+ /** Default true; `PH_PGLITE_FSYNC=0` turns it off (power-loss durability off). */
45
+ fsync?: boolean;
46
+ /** Default 5 min; 0 disables. */
47
+ maintenanceIntervalMs?: number;
48
+ /** `base/` size at open; default 256 MB; 0 disables. */
49
+ vacuumFullAboveBytes?: number;
50
+ logger?: {
51
+ warn(message: string): void;
52
+ };
53
+ /** Emscripten abort (a Postgres PANIC, e.g. ENOSPC on WAL). */
54
+ onAbort?: (what: unknown) => void;
55
+ hostFs?: Pick<typeof nodeFs, "fsyncSync" | "fdatasyncSync">;
56
+ }
57
+ type DurableNodeFs = InstanceType<NodeFsClass> & {
58
+ readonly maintenance: PgliteMaintenance;
59
+ };
60
+ export declare function resolvePgliteFsync(env: Record<string, string | undefined>): boolean;
61
+ /** Built over the passed-in class so PG16 dirs on `pglite-legacy-02` get the same behaviour. */
62
+ export declare function createDurableNodeFs(Base: NodeFsClass, dataDir: string, options?: DurableNodeFsOptions): DurableNodeFs;
63
+ //#endregion
64
+ //#region src/pglite/sync-tree.d.ts
65
+ interface SyncTreeHostFs {
66
+ fsyncSync(fd: number): void;
67
+ }
68
+ interface SyncTreeResult {
69
+ files: number;
70
+ dirs: number;
71
+ }
72
+ /** One pass over a finished tree; interleaving fsync with writes costs seconds. */
73
+ export declare function syncTree(root: string, hostFs?: SyncTreeHostFs): SyncTreeResult;
74
+ /** fsyncs a directory's entries; false where the platform refuses (Windows). */
75
+ export declare function syncDirectory(dir: string, hostFs?: SyncTreeHostFs): boolean;
76
+ //#endregion
77
+ //#region src/pglite/snapshot-reader.d.ts
78
+ export declare const SNAPSHOT_FILE_NAME = "snapshot.bin";
79
+ interface ExtractSnapshotOptions {
80
+ chunkSize?: number;
81
+ /** Default true: one fsync pass over the tree after the last write. */
82
+ sync?: boolean;
83
+ hostFs?: SyncTreeHostFs;
84
+ }
85
+ interface ExtractedSnapshot {
86
+ pgVersionMajor: number;
87
+ /** `global/pg_control` bytes; absent only in synthetic trees. */
88
+ pgControl?: Buffer;
89
+ /** Entries written to disk; skipped entries are not counted. */
90
+ entries: number;
91
+ /** Size of the snapshot file. */
92
+ bytes: number;
93
+ }
94
+ /** Unpacks a snapshot into the existing `outDir`. */
95
+ export declare function extractSnapshot(snapshotPath: string, outDir: string, options?: ExtractSnapshotOptions): Promise<ExtractedSnapshot>;
96
+ //#endregion
97
+ //#region src/pglite/convert-snapshot-dir.d.ts
98
+ interface VerifyHandle {
99
+ query<T>(sql: string): Promise<{
100
+ rows: T[];
101
+ }>;
102
+ close(): Promise<void>;
103
+ }
104
+ type ConversionStep = "extract" | "verify" | "renameOld" | "renameNew" | "removeOld";
105
+ interface ConversionDeps {
106
+ /** Opens `dataDir` with the stock NodeFS of the PGlite module for `major`. */
107
+ openForVerify: (major: number, dataDir: string) => Promise<VerifyHandle>;
108
+ logger: ILogger;
109
+ /** Test hook: throw after a named step to simulate a crash there. */
110
+ afterStep?: (step: ConversionStep) => void | Promise<void>;
111
+ hostFs?: SyncTreeHostFs;
112
+ }
113
+ /** Settles the debris of an interrupted conversion; runs before it at boot. */
114
+ export declare function recoverConversion(dir: string, deps: ConversionDeps): Promise<void>;
115
+ /** Extracts into `.converting`, verifies by opening it, swaps through `.old`. */
116
+ export declare function convertSnapshotDir(dir: string, deps: ConversionDeps): Promise<"converted" | "none">;
117
+ //#endregion
118
+ //#region src/pglite/preflight.d.ts
119
+ /** PG major of the pinned @electric-sql/pglite; it exports none, so the test checks initdb. */
120
+ export declare const CURRENT_PGLITE_MAJOR = 17;
121
+ /** `openForVerify` over the loaded PGlite module only. */
122
+ export declare function openCurrentPgliteForVerify(major: number, dataDir: string): Promise<VerifyHandle>;
123
+ /** Converts a snapshot store and clears the files a previous run leaves behind. */
124
+ export declare function preparePgliteDataDir(dir: string, deps: ConversionDeps): Promise<void>;
125
+ /** PGlite leaves `postmaster.pid` after every close and `pg_wal/xlogtemp.*` after some. */
126
+ export declare function removeStalePgliteFiles(dir: string, logger: Pick<ILogger, "debug">): Promise<void>;
127
+ //#endregion
128
+ export type { ConversionDeps, ConversionStep, DurableNodeFs, DurableNodeFsOptions, ExtractSnapshotOptions, ExtractedSnapshot, MaintenanceLogger, MaintenanceOptions, MaintenanceOutcome, NodeFsClass, SyncTreeHostFs, SyncTreeResult, VerifyHandle };
129
+ export {};
130
+ //# sourceMappingURL=pglite-node.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-node.d.mts","names":[],"sources":["../../../src/pglite/maintenance.ts","../../../src/pglite/durable-node-fs.ts","../../../src/pglite/sync-tree.ts","../../../src/pglite/snapshot-reader.ts","../../../src/pglite/convert-snapshot-dir.ts","../../../src/pglite/preflight.ts"],"mappings":";;;;;qBAIa;qBACA;KAGD;UAQK;EACf,KAAK;;UAGU;;EAEf;;EAEA;EACA,SAAS;;qBAGE;mBACM;mBACA;mBACA;UAET;UACA;UACA;UACA;UACA;UACA;UACA;EAER,YAAY,UAAS;MAYjB;MAIA;EAIJ,MAAM,IAAI,QAAQ,eAAe;EAQjC;EAIM,QAAQ;EAMR,OAAO,QAAQ;UA+Cb;UAoBA;;;;KC1IE,qBAAqB;UAKhB;;EAEf;;EAEA;;EAEA;EACA;IAAW,KAAK;;;EAEhB,WAAW;EACX,SAAS,YAAY;;KAGX,gBAAgB,aAAa;WAC9B,aAAa;;wBAGR,mBACd,KAAK;;wBAmES,oBACd,MAAM,aACN,iBACA,UAAS,uBACR;;;UCnGc;EACf,UAAU;;UAGK;EACf;EACA;;;wBAIc,SACd,cACA,SAAQ,iBACP;;wBA+Ba,cACd,aACA,SAAQ;;;qBC3CG;UASI;EACf;;EAEA;EACA,SAAS;;UAGM;EACf;;EAEA,YAAY;;EAEZ;;EAEA;;;wBAIoB,gBACpB,sBACA,gBACA,UAAS,yBACR,QAAQ;;;UC/BM;EACf,MAAM,GAAG,cAAc;IAAU,MAAM;;EACvC,SAAS;;KAGC;UAOK;;EAEf,gBAAgB,eAAe,oBAAoB,QAAQ;EAC3D,QAAQ;;EAER,aAAa,MAAM,0BAA0B;EAC7C,SAAS;;;wBA6BW,kBACpB,aACA,MAAM,iBACL;;wBA6CmB,mBACpB,aACA,MAAM,iBACL;;;;qBC3FU;;wBAGS,2BACpB,eACA,kBACC,QAAQ;;wBAYW,qBACpB,aACA,MAAM,iBACL;;wBAOmB,uBACpB,aACA,QAAQ,KAAK,oBACZ"}
@@ -0,0 +1,4 @@
1
+ import { a as convertSnapshotDir, c as extractSnapshot, d as syncDirectory, f as syncTree, h as PgliteMaintenance, i as removeStalePgliteFiles, l as createDurableNodeFs, m as DEFAULT_VACUUM_FULL_ABOVE_BYTES, n as openCurrentPgliteForVerify, o as recoverConversion, p as DEFAULT_MAINTENANCE_INTERVAL_MS, r as preparePgliteDataDir, s as SNAPSHOT_FILE_NAME, t as CURRENT_PGLITE_MAJOR, u as resolvePgliteFsync } from "../../pglite-node-Ow9i5Rb4.mjs";
2
+ export { CURRENT_PGLITE_MAJOR, DEFAULT_MAINTENANCE_INTERVAL_MS, DEFAULT_VACUUM_FULL_ABOVE_BYTES, PgliteMaintenance, SNAPSHOT_FILE_NAME, convertSnapshotDir, createDurableNodeFs, extractSnapshot, openCurrentPgliteForVerify, preparePgliteDataDir, recoverConversion, removeStalePgliteFiles, resolvePgliteFsync, syncDirectory, syncTree };
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]="6dd4c09b-2fd7-5402-83f6-a92c933064b1")}catch(e){}}();
4
+ //# debugId=6dd4c09b-2fd7-5402-83f6-a92c933064b1
@@ -1,11 +1,15 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="df700b05-17ec-5ac9-b31f-f6f9cfcfa5f5")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="17c0a5a3-a356-551f-9500-bdab5f8930f9")}catch(e){}}();
3
+ import { createRequire } from "node:module";
3
4
  import { gql } from "graphql-tag";
4
5
  import { GraphQLError } from "graphql";
5
6
  import { DRIVE_AUTH_ERROR_MESSAGES, RECOVERABLE_GRAPHQL_ERROR_CODES } from "@powerhousedao/reactor";
7
+ import { PURGE_DOCUMENT } from "@powerhousedao/shared/document-model";
6
8
  import { childLogger } from "document-model";
7
- import path from "node:path";
9
+ import path, { dirname, isAbsolute, join } from "node:path";
10
+ import { existsSync, readFileSync } from "node:fs";
8
11
  import { fileURLToPath, pathToFileURL } from "node:url";
12
+ import { packageJsonExports } from "@powerhousedao/shared/clis/constants";
9
13
  //#region src/graphql/errors.ts
10
14
  /** Caller (authenticated or anonymous) lacks permission for the document. */
11
15
  var ForbiddenError = class extends GraphQLError {
@@ -60,6 +64,18 @@ var MalformedStoredOperationError = class extends GraphQLError {
60
64
  super(`Stored operation cannot be served: ${detail}`, { extensions: { code: RECOVERABLE_GRAPHQL_ERROR_CODES.malformedStoredOperation } });
61
65
  }
62
66
  };
67
+ /**
68
+ * The marker refusals a poll carried were not recorded, so the poll fails.
69
+ * The code is recoverable: the client keeps polling and reports them again.
70
+ */
71
+ var RefusalNotRecordedError = class extends GraphQLError {
72
+ constructor(cause) {
73
+ super("A reported marker refusal was not recorded; report it again", {
74
+ originalError: cause instanceof Error ? cause : new Error(String(cause)),
75
+ extensions: { code: RECOVERABLE_GRAPHQL_ERROR_CODES.refusalNotRecorded }
76
+ });
77
+ }
78
+ };
63
79
  //#endregion
64
80
  //#region src/services/authorization.service.ts
65
81
  /**
@@ -68,6 +84,8 @@ var MalformedStoredOperationError = class extends GraphQLError {
68
84
  * identifier when the check was skipped for a policy-wide caller.
69
85
  */
70
86
  var AuthorizedDocumentHandle = class AuthorizedDocumentHandle {
87
+ fetchIdentifier;
88
+ isResolved;
71
89
  constructor(fetchIdentifier, isResolved) {
72
90
  this.fetchIdentifier = fetchIdentifier;
73
91
  this.isResolved = isResolved;
@@ -91,6 +109,7 @@ const AuthorizationPolicy = {
91
109
  };
92
110
  /** Shared config holder and admin-list check for the policy strategies. */
93
111
  var BaseAuthorizationService = class {
112
+ config;
94
113
  constructor(config) {
95
114
  this.config = config;
96
115
  }
@@ -174,6 +193,7 @@ var DocumentPermissionsAuthorizationService = class extends BaseAuthorizationSer
174
193
  }
175
194
  async canMutate(documentId, operationType, userAddress) {
176
195
  if (this.isSupremeAdmin(userAddress)) return true;
196
+ if (operationType === PURGE_DOCUMENT) return this.#isDocumentAdmin(documentId, userAddress);
177
197
  if (await this.#permissions.isOperationRestricted(documentId, operationType)) {
178
198
  if (!userAddress) return false;
179
199
  if (await this.#isDocumentAdmin(documentId, userAddress)) return true;
@@ -268,6 +288,16 @@ function createCanonicalDocumentIdResolver(client) {
268
288
  }
269
289
  //#endregion
270
290
  //#region src/graphql/base-subgraph.ts
291
+ /**
292
+ * The subject a request reads as: its address and the app key its token
293
+ * carries. Anonymous is an empty subject, never the host's signer.
294
+ */
295
+ function callerSubject(user) {
296
+ return {
297
+ address: user?.address,
298
+ key: user?.appKey
299
+ };
300
+ }
271
301
  var BaseSubgraph = class {
272
302
  name = "example";
273
303
  path = "";
@@ -293,6 +323,8 @@ var BaseSubgraph = class {
293
323
  */
294
324
  #canonicalIdMemo = /* @__PURE__ */ new WeakMap();
295
325
  #resolveCanonical;
326
+ #attachments;
327
+ #attachmentClientMemo = /* @__PURE__ */ new WeakMap();
296
328
  constructor(args) {
297
329
  this.#resolveCanonical = createCanonicalDocumentIdResolver(args.reactorClient);
298
330
  this.http = args.http;
@@ -304,6 +336,21 @@ var BaseSubgraph = class {
304
336
  this.authorizationService = args.authorizationService;
305
337
  this.syncServingGate = args.syncServingGate;
306
338
  this.path = args.path ?? "";
339
+ this.#attachments = args.attachments;
340
+ }
341
+ /**
342
+ * An attachment client bound to this request's caller, memoized per request.
343
+ *
344
+ * @throws Error when the host provides no attachment client to subgraphs.
345
+ */
346
+ attachmentsFor(ctx) {
347
+ if (!this.#attachments) throw new Error("This host provides no attachment client to subgraphs");
348
+ let client = this.#attachmentClientMemo.get(ctx);
349
+ if (!client) {
350
+ client = this.#attachments.forSubject(callerSubject(ctx.user));
351
+ this.#attachmentClientMemo.set(ctx, client);
352
+ }
353
+ return client;
307
354
  }
308
355
  async onSetup() {}
309
356
  async onDisconnect() {}
@@ -372,10 +419,16 @@ var BaseSubgraph = class {
372
419
  * carries the same key. Anonymous callers supply neither.
373
420
  */
374
421
  viewSubject(ctx) {
375
- return {
376
- address: ctx.user?.address,
377
- key: ctx.user?.appKey
378
- };
422
+ return callerSubject(ctx.user);
423
+ }
424
+ /** Whether a listing would serve the document to the caller; fails closed. */
425
+ async servesDocument(documentId, ctx) {
426
+ if (!documentId) return false;
427
+ try {
428
+ return await this.reactorClient.isServed(documentId, { subject: this.viewSubject(ctx) });
429
+ } catch {
430
+ return false;
431
+ }
379
432
  }
380
433
  /**
381
434
  * Asserts read access, resolving a slug first. Returns a handle whose
@@ -555,6 +608,30 @@ async function loadDependency(packageName, subPath) {
555
608
  throw e;
556
609
  }
557
610
  }
611
+ /**
612
+ * The subgraphs a package's `powerhouse.manifest.json` declares, when the
613
+ * manifest is reachable — which it is for a package identified by an
614
+ * absolute path (how every local project reaches the loaders). Lets the
615
+ * package manager warn when a manifest promises subgraphs and the loaders
616
+ * deliver none, instead of failing silently. For a bare package name the
617
+ * manifest would have to be resolved through the module system; until a
618
+ * caller needs that, a bare name answers [] rather than guessing.
619
+ */
620
+ function manifestDeclaredSubgraphs(identifier) {
621
+ if (!path.isAbsolute(identifier)) return [];
622
+ try {
623
+ const raw = readFileSync(path.join(identifier, "powerhouse.manifest.json"), "utf-8");
624
+ const manifest = JSON.parse(raw);
625
+ if (!Array.isArray(manifest.subgraphs)) return [];
626
+ return manifest.subgraphs.map((subgraph) => {
627
+ if (typeof subgraph !== "object" || subgraph === null) return;
628
+ const { id, name } = subgraph;
629
+ return id ?? name;
630
+ }).filter((value) => typeof value === "string" && value.length > 0);
631
+ } catch {
632
+ return [];
633
+ }
634
+ }
558
635
  function isUpgradeManifest(value) {
559
636
  return typeof value === "object" && value !== null && "documentType" in value && "supportedVersions" in value && "upgrades" in value;
560
637
  }
@@ -593,8 +670,9 @@ function isSubpath(parent, dir) {
593
670
  function isSubgraphClass(candidate) {
594
671
  if (typeof candidate !== "function") return false;
595
672
  let proto = Object.getPrototypeOf(candidate);
596
- while (proto) {
597
- if (Object.prototype.isPrototypeOf.call(proto, BaseSubgraph)) return true;
673
+ while (typeof proto === "function") {
674
+ if (proto === BaseSubgraph) return true;
675
+ if (proto.name === "BaseSubgraph") return true;
598
676
  proto = Object.getPrototypeOf(proto);
599
677
  }
600
678
  return false;
@@ -647,7 +725,184 @@ function buildGraphQlDriveDocument(doc) {
647
725
  };
648
726
  }
649
727
  //#endregion
650
- export { createAuthorizationService as _, isSubgraphClass as a, MalformedStoredOperationError as b, isSubpath as c, loadSubgraphs as d, BaseSubgraph as f, AuthorizedDocumentHandle as g, AuthorizationPolicy as h, buildGraphqlOperations as i, loadDocumentModels as l, createCanonicalDocumentIdResolver as m, buildGraphQlDriveDocument as n, debounce as o, CanonicalDocumentIdResolutionError as p, buildGraphqlOperation as r, extractUpgradeManifests as s, buildGraphQlDocument as t, loadProcessors as u, AuthEvaluationUnsupportedError as v, ForbiddenError as y };
728
+ //#region src/packages/document-model-detection.ts
729
+ /**
730
+ * The one acceptance rule for a package's `document-models` module.
731
+ *
732
+ * The three package loaders duck-typed this independently and disagreed on
733
+ * one case: the http loader required `documentModel` to be non-null, while
734
+ * the import and vite loaders accepted any object merely carrying the key,
735
+ * so an export shaped `{ documentModel: null }` registered as a model under
736
+ * two loaders and vanished under the third. The strict rule is the correct
737
+ * one — a module without its documentModel cannot define a document type —
738
+ * and this predicate is now the single rule all three share, the same way
739
+ * extractSubgraphs unified the subgraph rule.
740
+ */
741
+ function isDocumentModelModule(candidate) {
742
+ if (candidate === null || typeof candidate !== "object") return false;
743
+ if (!("documentModel" in candidate)) return false;
744
+ const model = candidate.documentModel;
745
+ return model !== null && model !== void 0;
746
+ }
747
+ /** Collects the document-model modules among a namespace's exports. */
748
+ function extractDocumentModels(namespace) {
749
+ return Object.values(namespace).filter(isDocumentModelModule);
750
+ }
751
+ //#endregion
752
+ //#region src/packages/pieces.ts
753
+ /** The sub-entry every loader asks a package for its pieces through. */
754
+ const PIECES_SUBPATH = "pieces";
755
+ const BUILT_PIECE_LIST = packageJsonExports["./pieces"].node;
756
+ /** Where a package keeps its piece sources, for telling unbuilt from absent. */
757
+ const PIECE_SOURCE_LIST = join(PIECES_SUBPATH, "index.ts");
758
+ /** The nearest enclosing package.json, which `exports` usually hides. */
759
+ function packageRootOf(dir) {
760
+ let current = dir;
761
+ while (!existsSync(join(current, "package.json"))) {
762
+ const parent = dirname(current);
763
+ if (parent === current) return void 0;
764
+ current = parent;
765
+ }
766
+ return current;
767
+ }
768
+ function resolvePieceList(specifier, hostDir) {
769
+ try {
770
+ return fileURLToPath(import.meta.resolve(specifier));
771
+ } catch (error) {
772
+ try {
773
+ return createRequire(join(hostDir, "package.json")).resolve(specifier);
774
+ } catch {
775
+ throw error;
776
+ }
777
+ }
778
+ }
779
+ function pieceListLocation(identifier, hostDir = process.cwd()) {
780
+ if (isAbsolute(identifier)) return {
781
+ root: identifier,
782
+ listPath: join(identifier, BUILT_PIECE_LIST)
783
+ };
784
+ const listPath = resolvePieceList(`${identifier}/${PIECES_SUBPATH}`, hostDir);
785
+ const root = packageRootOf(dirname(listPath));
786
+ if (!root) throw new Error(`No package.json above ${listPath}`);
787
+ return {
788
+ root,
789
+ listPath
790
+ };
791
+ }
792
+ function packageVersionAt(root) {
793
+ try {
794
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
795
+ return typeof pkg.version === "string" && pkg.version !== "" ? pkg.version : void 0;
796
+ } catch {
797
+ return;
798
+ }
799
+ }
800
+ function locatePieces(declared, context) {
801
+ const { root, identifier, logger, version } = context;
802
+ const located = [];
803
+ for (const piece of declared) {
804
+ const where = piece.entry ?? piece.bundle;
805
+ if (typeof where !== "string" || where === "") {
806
+ logger.warn("Piece @piece of @pkg declares neither a bundle nor an entry", piece.name, identifier);
807
+ continue;
808
+ }
809
+ const path = isAbsolute(where) ? where : join(root, where);
810
+ if (!(piece.entry ? existsSync(path) : existsSync(join(path, "package.json")))) {
811
+ logger.warn("Piece @piece of @pkg is declared but not built at @path", piece.name, identifier, path);
812
+ continue;
813
+ }
814
+ located.push({
815
+ name: piece.name,
816
+ version,
817
+ ...piece.entry ? { entryPath: path } : { bundleDir: path }
818
+ });
819
+ }
820
+ return located;
821
+ }
822
+ function declaredPieces(module, identifier, logger) {
823
+ const namespace = module ?? {};
824
+ const declared = namespace.pieces ?? namespace.default;
825
+ if (!Array.isArray(declared)) {
826
+ logger.warn("The pieces list of @pkg exports no \"pieces\" array; it contributes none", identifier);
827
+ return;
828
+ }
829
+ return declared;
830
+ }
831
+ function piecesFromCdnList(module, baseUrl, identifier, logger, version) {
832
+ const declared = declaredPieces(module, identifier, logger);
833
+ if (!declared) return [];
834
+ const located = [];
835
+ for (const piece of declared) {
836
+ const where = piece.entry ?? piece.bundle;
837
+ if (typeof where !== "string" || where === "") {
838
+ logger.warn("Piece @piece of @pkg declares neither a bundle nor an entry", piece.name, identifier);
839
+ continue;
840
+ }
841
+ located.push({
842
+ name: piece.name,
843
+ version,
844
+ entryUrl: `${baseUrl}${where.replace(/^dist\/node\/pieces\//, "")}`
845
+ });
846
+ }
847
+ return located;
848
+ }
849
+ function piecesFromListModule(module, context) {
850
+ const namespace = module ?? {};
851
+ const declared = namespace.pieces ?? namespace.default;
852
+ if (!Array.isArray(declared)) {
853
+ context.logger.warn("The pieces list of @pkg exports no \"pieces\" array; it contributes none", context.identifier);
854
+ return [];
855
+ }
856
+ const version = packageVersionAt(context.root);
857
+ if (!version) {
858
+ context.logger.warn("@pkg has no version in @root/package.json, and a piece takes its package's version; its pieces contribute none", context.identifier, context.root);
859
+ return [];
860
+ }
861
+ return locatePieces(declared, {
862
+ ...context,
863
+ version
864
+ });
865
+ }
866
+ //#endregion
867
+ //#region src/packages/subgraph-extraction.ts
868
+ /**
869
+ * The one acceptance rule for a package's `subgraphs` module.
870
+ *
871
+ * All three package loaders (vite, import, http) consume the same generated
872
+ * `subgraphs/index` file, and each used to apply its own rule — the vite
873
+ * loader required a namespace whose alias equalled the class name inside it,
874
+ * the http loader took any function one level deep, and the import loader
875
+ * took every nested value. An export that registered under one loader could
876
+ * silently vanish under another (the codegen-does-not-register-subgraphs
877
+ * report). This is now the single rule they all share:
878
+ *
879
+ * - an export that is itself a subgraph class is accepted (named or default
880
+ * export of the class);
881
+ * - any subgraph class among the values of an exported object is accepted
882
+ * (a `export * as Alias from ...` namespace under any alias, or a
883
+ * default-exported object of classes);
884
+ * - everything else — constants, configs, functions and classes that do not
885
+ * extend BaseSubgraph — is dropped.
886
+ *
887
+ * Codegen emits `export * as <ClassName> from "./<dir>/index.js"`
888
+ * (makeSubgraphsIndexFile in packages/codegen), which the namespace branch
889
+ * accepts regardless of the alias; hand-written packages may re-export a
890
+ * class directly, which the direct branch accepts. A class reached through
891
+ * both branches is returned once.
892
+ */
893
+ function extractSubgraphs(namespace) {
894
+ const found = [];
895
+ const add = (candidate) => {
896
+ if (isSubgraphClass(candidate) && !found.includes(candidate)) found.push(candidate);
897
+ };
898
+ for (const value of Object.values(namespace)) {
899
+ add(value);
900
+ if (value !== null && typeof value === "object") for (const inner of Object.values(value)) add(inner);
901
+ }
902
+ return found;
903
+ }
904
+ //#endregion
905
+ export { AuthenticationRequiredError as A, callerSubject as C, AuthorizedDocumentHandle as D, AuthorizationPolicy as E, MalformedStoredOperationError as M, RefusalNotRecordedError as N, createAuthorizationService as O, BaseSubgraph as S, createCanonicalDocumentIdResolver as T, isSubpath as _, packageRootOf as a, loadSubgraphs as b, piecesFromListModule as c, buildGraphQlDriveDocument as d, buildGraphqlOperation as f, extractUpgradeManifests as g, debounce as h, PIECE_SOURCE_LIST as i, ForbiddenError as j, AuthEvaluationUnsupportedError as k, extractDocumentModels as l, isSubgraphClass as m, BUILT_PIECE_LIST as n, pieceListLocation as o, buildGraphqlOperations as p, PIECES_SUBPATH as r, piecesFromCdnList as s, extractSubgraphs as t, buildGraphQlDocument as u, loadDocumentModels as v, CanonicalDocumentIdResolutionError as w, manifestDeclaredSubgraphs as x, loadProcessors as y };
651
906
 
652
- //# sourceMappingURL=utils-XWVecaNn.mjs.map
653
- //# debugId=df700b05-17ec-5ac9-b31f-f6f9cfcfa5f5
907
+ //# sourceMappingURL=subgraph-extraction-DTFoN_bS.mjs.map
908
+ //# debugId=17c0a5a3-a356-551f-9500-bdab5f8930f9