@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,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]="
|
|
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]="a7006388-c7a4-51a6-82d1-b4ec96082701")}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() {}
|
|
@@ -361,6 +408,13 @@ var BaseSubgraph = class {
|
|
|
361
408
|
async canReadDocument(documentId, ctx) {
|
|
362
409
|
return this.authorizationService.canRead(documentId, ctx.user?.address);
|
|
363
410
|
}
|
|
411
|
+
/** Drops the items the host's own ACL refuses the caller. */
|
|
412
|
+
async readableByHost(items, ctx) {
|
|
413
|
+
if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return [...items];
|
|
414
|
+
const readable = [];
|
|
415
|
+
for (const item of items) if (await this.canReadDocument(item.id, ctx)) readable.push(item);
|
|
416
|
+
return readable;
|
|
417
|
+
}
|
|
364
418
|
/**
|
|
365
419
|
* The principal a request decides as: the authenticated caller's address, and
|
|
366
420
|
* the did:key of the app instance whose token authenticated it.
|
|
@@ -372,10 +426,33 @@ var BaseSubgraph = class {
|
|
|
372
426
|
* carries the same key. Anonymous callers supply neither.
|
|
373
427
|
*/
|
|
374
428
|
viewSubject(ctx) {
|
|
375
|
-
return
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
429
|
+
return callerSubject(ctx.user);
|
|
430
|
+
}
|
|
431
|
+
/** Whether a listing would serve the document to the caller; fails closed. */
|
|
432
|
+
async servesDocument(documentId, ctx) {
|
|
433
|
+
if (!documentId) return false;
|
|
434
|
+
try {
|
|
435
|
+
return await this.servesDocumentOrThrows(documentId, ctx);
|
|
436
|
+
} catch {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/** {@link servesDocument}, but a reactor that cannot answer throws. */
|
|
441
|
+
servesDocumentOrThrows(identifier, ctx, view) {
|
|
442
|
+
return this.reactorClient.isServed(identifier, {
|
|
443
|
+
...view,
|
|
444
|
+
subject: this.viewSubject(ctx)
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Whether a listing would serve the document to the caller: the reactor read
|
|
449
|
+
* gate, then the host's legacy layer, as `findDocuments` applies them. Absent
|
|
450
|
+
* or withheld is `false`; a failure to decide throws.
|
|
451
|
+
*/
|
|
452
|
+
async listsDocument(identifier, ctx, view) {
|
|
453
|
+
if (!await this.servesDocumentOrThrows(identifier, ctx, view)) return false;
|
|
454
|
+
if (this.authorizationService.isSupremeAdmin(ctx.user?.address)) return true;
|
|
455
|
+
return this.canReadDocument(await this.resolveCanonicalDocumentId(identifier, ctx), ctx);
|
|
379
456
|
}
|
|
380
457
|
/**
|
|
381
458
|
* Asserts read access, resolving a slug first. Returns a handle whose
|
|
@@ -555,6 +632,30 @@ async function loadDependency(packageName, subPath) {
|
|
|
555
632
|
throw e;
|
|
556
633
|
}
|
|
557
634
|
}
|
|
635
|
+
/**
|
|
636
|
+
* The subgraphs a package's `powerhouse.manifest.json` declares, when the
|
|
637
|
+
* manifest is reachable — which it is for a package identified by an
|
|
638
|
+
* absolute path (how every local project reaches the loaders). Lets the
|
|
639
|
+
* package manager warn when a manifest promises subgraphs and the loaders
|
|
640
|
+
* deliver none, instead of failing silently. For a bare package name the
|
|
641
|
+
* manifest would have to be resolved through the module system; until a
|
|
642
|
+
* caller needs that, a bare name answers [] rather than guessing.
|
|
643
|
+
*/
|
|
644
|
+
function manifestDeclaredSubgraphs(identifier) {
|
|
645
|
+
if (!path.isAbsolute(identifier)) return [];
|
|
646
|
+
try {
|
|
647
|
+
const raw = readFileSync(path.join(identifier, "powerhouse.manifest.json"), "utf-8");
|
|
648
|
+
const manifest = JSON.parse(raw);
|
|
649
|
+
if (!Array.isArray(manifest.subgraphs)) return [];
|
|
650
|
+
return manifest.subgraphs.map((subgraph) => {
|
|
651
|
+
if (typeof subgraph !== "object" || subgraph === null) return;
|
|
652
|
+
const { id, name } = subgraph;
|
|
653
|
+
return id ?? name;
|
|
654
|
+
}).filter((value) => typeof value === "string" && value.length > 0);
|
|
655
|
+
} catch {
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
658
|
+
}
|
|
558
659
|
function isUpgradeManifest(value) {
|
|
559
660
|
return typeof value === "object" && value !== null && "documentType" in value && "supportedVersions" in value && "upgrades" in value;
|
|
560
661
|
}
|
|
@@ -593,8 +694,9 @@ function isSubpath(parent, dir) {
|
|
|
593
694
|
function isSubgraphClass(candidate) {
|
|
594
695
|
if (typeof candidate !== "function") return false;
|
|
595
696
|
let proto = Object.getPrototypeOf(candidate);
|
|
596
|
-
while (proto) {
|
|
597
|
-
if (
|
|
697
|
+
while (typeof proto === "function") {
|
|
698
|
+
if (proto === BaseSubgraph) return true;
|
|
699
|
+
if (proto.name === "BaseSubgraph") return true;
|
|
598
700
|
proto = Object.getPrototypeOf(proto);
|
|
599
701
|
}
|
|
600
702
|
return false;
|
|
@@ -647,7 +749,184 @@ function buildGraphQlDriveDocument(doc) {
|
|
|
647
749
|
};
|
|
648
750
|
}
|
|
649
751
|
//#endregion
|
|
650
|
-
|
|
752
|
+
//#region src/packages/document-model-detection.ts
|
|
753
|
+
/**
|
|
754
|
+
* The one acceptance rule for a package's `document-models` module.
|
|
755
|
+
*
|
|
756
|
+
* The three package loaders duck-typed this independently and disagreed on
|
|
757
|
+
* one case: the http loader required `documentModel` to be non-null, while
|
|
758
|
+
* the import and vite loaders accepted any object merely carrying the key,
|
|
759
|
+
* so an export shaped `{ documentModel: null }` registered as a model under
|
|
760
|
+
* two loaders and vanished under the third. The strict rule is the correct
|
|
761
|
+
* one — a module without its documentModel cannot define a document type —
|
|
762
|
+
* and this predicate is now the single rule all three share, the same way
|
|
763
|
+
* extractSubgraphs unified the subgraph rule.
|
|
764
|
+
*/
|
|
765
|
+
function isDocumentModelModule(candidate) {
|
|
766
|
+
if (candidate === null || typeof candidate !== "object") return false;
|
|
767
|
+
if (!("documentModel" in candidate)) return false;
|
|
768
|
+
const model = candidate.documentModel;
|
|
769
|
+
return model !== null && model !== void 0;
|
|
770
|
+
}
|
|
771
|
+
/** Collects the document-model modules among a namespace's exports. */
|
|
772
|
+
function extractDocumentModels(namespace) {
|
|
773
|
+
return Object.values(namespace).filter(isDocumentModelModule);
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/packages/pieces.ts
|
|
777
|
+
/** The sub-entry every loader asks a package for its pieces through. */
|
|
778
|
+
const PIECES_SUBPATH = "pieces";
|
|
779
|
+
const BUILT_PIECE_LIST = packageJsonExports["./pieces"].node;
|
|
780
|
+
/** Where a package keeps its piece sources, for telling unbuilt from absent. */
|
|
781
|
+
const PIECE_SOURCE_LIST = join(PIECES_SUBPATH, "index.ts");
|
|
782
|
+
/** The nearest enclosing package.json, which `exports` usually hides. */
|
|
783
|
+
function packageRootOf(dir) {
|
|
784
|
+
let current = dir;
|
|
785
|
+
while (!existsSync(join(current, "package.json"))) {
|
|
786
|
+
const parent = dirname(current);
|
|
787
|
+
if (parent === current) return void 0;
|
|
788
|
+
current = parent;
|
|
789
|
+
}
|
|
790
|
+
return current;
|
|
791
|
+
}
|
|
792
|
+
function resolvePieceList(specifier, hostDir) {
|
|
793
|
+
try {
|
|
794
|
+
return fileURLToPath(import.meta.resolve(specifier));
|
|
795
|
+
} catch (error) {
|
|
796
|
+
try {
|
|
797
|
+
return createRequire(join(hostDir, "package.json")).resolve(specifier);
|
|
798
|
+
} catch {
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function pieceListLocation(identifier, hostDir = process.cwd()) {
|
|
804
|
+
if (isAbsolute(identifier)) return {
|
|
805
|
+
root: identifier,
|
|
806
|
+
listPath: join(identifier, BUILT_PIECE_LIST)
|
|
807
|
+
};
|
|
808
|
+
const listPath = resolvePieceList(`${identifier}/${PIECES_SUBPATH}`, hostDir);
|
|
809
|
+
const root = packageRootOf(dirname(listPath));
|
|
810
|
+
if (!root) throw new Error(`No package.json above ${listPath}`);
|
|
811
|
+
return {
|
|
812
|
+
root,
|
|
813
|
+
listPath
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function packageVersionAt(root) {
|
|
817
|
+
try {
|
|
818
|
+
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
819
|
+
return typeof pkg.version === "string" && pkg.version !== "" ? pkg.version : void 0;
|
|
820
|
+
} catch {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function locatePieces(declared, context) {
|
|
825
|
+
const { root, identifier, logger, version } = context;
|
|
826
|
+
const located = [];
|
|
827
|
+
for (const piece of declared) {
|
|
828
|
+
const where = piece.entry ?? piece.bundle;
|
|
829
|
+
if (typeof where !== "string" || where === "") {
|
|
830
|
+
logger.warn("Piece @piece of @pkg declares neither a bundle nor an entry", piece.name, identifier);
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const path = isAbsolute(where) ? where : join(root, where);
|
|
834
|
+
if (!(piece.entry ? existsSync(path) : existsSync(join(path, "package.json")))) {
|
|
835
|
+
logger.warn("Piece @piece of @pkg is declared but not built at @path", piece.name, identifier, path);
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
located.push({
|
|
839
|
+
name: piece.name,
|
|
840
|
+
version,
|
|
841
|
+
...piece.entry ? { entryPath: path } : { bundleDir: path }
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
return located;
|
|
845
|
+
}
|
|
846
|
+
function declaredPieces(module, identifier, logger) {
|
|
847
|
+
const namespace = module ?? {};
|
|
848
|
+
const declared = namespace.pieces ?? namespace.default;
|
|
849
|
+
if (!Array.isArray(declared)) {
|
|
850
|
+
logger.warn("The pieces list of @pkg exports no \"pieces\" array; it contributes none", identifier);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
return declared;
|
|
854
|
+
}
|
|
855
|
+
function piecesFromCdnList(module, baseUrl, identifier, logger, version) {
|
|
856
|
+
const declared = declaredPieces(module, identifier, logger);
|
|
857
|
+
if (!declared) return [];
|
|
858
|
+
const located = [];
|
|
859
|
+
for (const piece of declared) {
|
|
860
|
+
const where = piece.entry ?? piece.bundle;
|
|
861
|
+
if (typeof where !== "string" || where === "") {
|
|
862
|
+
logger.warn("Piece @piece of @pkg declares neither a bundle nor an entry", piece.name, identifier);
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
located.push({
|
|
866
|
+
name: piece.name,
|
|
867
|
+
version,
|
|
868
|
+
entryUrl: `${baseUrl}${where.replace(/^dist\/node\/pieces\//, "")}`
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
return located;
|
|
872
|
+
}
|
|
873
|
+
function piecesFromListModule(module, context) {
|
|
874
|
+
const namespace = module ?? {};
|
|
875
|
+
const declared = namespace.pieces ?? namespace.default;
|
|
876
|
+
if (!Array.isArray(declared)) {
|
|
877
|
+
context.logger.warn("The pieces list of @pkg exports no \"pieces\" array; it contributes none", context.identifier);
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
const version = packageVersionAt(context.root);
|
|
881
|
+
if (!version) {
|
|
882
|
+
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);
|
|
883
|
+
return [];
|
|
884
|
+
}
|
|
885
|
+
return locatePieces(declared, {
|
|
886
|
+
...context,
|
|
887
|
+
version
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
//#endregion
|
|
891
|
+
//#region src/packages/subgraph-extraction.ts
|
|
892
|
+
/**
|
|
893
|
+
* The one acceptance rule for a package's `subgraphs` module.
|
|
894
|
+
*
|
|
895
|
+
* All three package loaders (vite, import, http) consume the same generated
|
|
896
|
+
* `subgraphs/index` file, and each used to apply its own rule — the vite
|
|
897
|
+
* loader required a namespace whose alias equalled the class name inside it,
|
|
898
|
+
* the http loader took any function one level deep, and the import loader
|
|
899
|
+
* took every nested value. An export that registered under one loader could
|
|
900
|
+
* silently vanish under another (the codegen-does-not-register-subgraphs
|
|
901
|
+
* report). This is now the single rule they all share:
|
|
902
|
+
*
|
|
903
|
+
* - an export that is itself a subgraph class is accepted (named or default
|
|
904
|
+
* export of the class);
|
|
905
|
+
* - any subgraph class among the values of an exported object is accepted
|
|
906
|
+
* (a `export * as Alias from ...` namespace under any alias, or a
|
|
907
|
+
* default-exported object of classes);
|
|
908
|
+
* - everything else — constants, configs, functions and classes that do not
|
|
909
|
+
* extend BaseSubgraph — is dropped.
|
|
910
|
+
*
|
|
911
|
+
* Codegen emits `export * as <ClassName> from "./<dir>/index.js"`
|
|
912
|
+
* (makeSubgraphsIndexFile in packages/codegen), which the namespace branch
|
|
913
|
+
* accepts regardless of the alias; hand-written packages may re-export a
|
|
914
|
+
* class directly, which the direct branch accepts. A class reached through
|
|
915
|
+
* both branches is returned once.
|
|
916
|
+
*/
|
|
917
|
+
function extractSubgraphs(namespace) {
|
|
918
|
+
const found = [];
|
|
919
|
+
const add = (candidate) => {
|
|
920
|
+
if (isSubgraphClass(candidate) && !found.includes(candidate)) found.push(candidate);
|
|
921
|
+
};
|
|
922
|
+
for (const value of Object.values(namespace)) {
|
|
923
|
+
add(value);
|
|
924
|
+
if (value !== null && typeof value === "object") for (const inner of Object.values(value)) add(inner);
|
|
925
|
+
}
|
|
926
|
+
return found;
|
|
927
|
+
}
|
|
928
|
+
//#endregion
|
|
929
|
+
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
930
|
|
|
652
|
-
//# sourceMappingURL=
|
|
653
|
-
//# debugId=
|
|
931
|
+
//# sourceMappingURL=subgraph-extraction-sQlZLLPH.mjs.map
|
|
932
|
+
//# debugId=a7006388-c7a4-51a6-82d1-b4ec96082701
|