@powerhousedao/reactor-api 6.2.3-dev.40 → 6.2.3-dev.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
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]="a50faed2-4e40-54ba-873f-1808cedc1e5c")}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]="65e47e18-15f4-5a67-b94b-f6e795c1eb9b")}catch(e){}}();
3
3
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
4
4
  import { A as RefusalNotRecordedError, C as AuthorizationPolicy, D as AuthenticationRequiredError, E as AuthEvaluationUnsupportedError, O as ForbiddenError, S as createCanonicalDocumentIdResolver, T as createAuthorizationService, _ as loadProcessors, a as pieceListLocation, b as callerSubject, c as buildGraphQlDocument, d as buildGraphqlOperations, f as isSubgraphClass, g as loadDocumentModels, k as MalformedStoredOperationError, l as buildGraphQlDriveDocument, m as extractUpgradeManifests, o as piecesFromCdnList, p as debounce, r as PIECE_SOURCE_LIST, s as piecesFromListModule, u as buildGraphqlOperation, v as loadSubgraphs, w as AuthorizedDocumentHandle, x as CanonicalDocumentIdResolutionError, y as BaseSubgraph } from "./pieces-DRY6haAO.mjs";
5
+ import { findPackageJSON, isBuiltin } from "node:module";
5
6
  import { AnalyticsQueryEngine } from "@powerhousedao/analytics-engine-core";
6
7
  import { AnalyticsModel, AnalyticsResolvers, typedefs } from "@powerhousedao/analytics-engine-graphql";
7
8
  import { gql } from "graphql-tag";
@@ -15,7 +16,7 @@ import posix from "node:path/posix";
15
16
  import { Readable } from "node:stream";
16
17
  import fs, { existsSync, readFileSync, watchFile } from "node:fs";
17
18
  import { pipeline } from "node:stream/promises";
18
- import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
19
+ import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
19
20
  import { pathToFileURL } from "node:url";
20
21
  import { buildSubgraphSchema } from "@apollo/subgraph";
21
22
  import { typeDefs } from "@powerhousedao/document-engineering/graphql";
@@ -28,6 +29,8 @@ import lzString from "lz-string";
28
29
  import { PubSub, withFilter } from "graphql-subscriptions";
29
30
  import dotenv from "dotenv";
30
31
  import { buildTreeUrl } from "@powerhousedao/shared";
32
+ import { init, parse as parse$1 } from "es-module-lexer";
33
+ import { chmod, lstat, mkdir, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
31
34
  import { getConfig } from "@powerhousedao/config/node";
32
35
  import { reactorDriveDocumentModelModule } from "@powerhousedao/reactor-drive";
33
36
  import { ReactorGroupV1 } from "@powerhousedao/reactor-group";
@@ -37,7 +40,6 @@ import { PostgresAnalyticsStore } from "@powerhousedao/analytics-engine-pg";
37
40
  import { AttachmentBuilder, AttachmentNotFound, AttachmentReferenceIndexBuilder, createRef, createS3AttachmentBackend, parseAttachmentStorageConfig, parseRef, validateReserveMetadata } from "@powerhousedao/reactor-attachments";
38
41
  import { createAttachmentClient } from "@powerhousedao/reactor-attachments/client";
39
42
  import { setupMcpServer } from "@powerhousedao/reactor-mcp";
40
- import { mkdir } from "node:fs/promises";
41
43
  import { tmpdir } from "node:os";
42
44
  import { WebSocketServer } from "ws";
43
45
  import { createRelationalDb } from "@powerhousedao/shared/processors";
@@ -6824,10 +6826,10 @@ const ADMIN_USERS = getAdminUsers();
6824
6826
  //#endregion
6825
6827
  //#region src/graphql/system/version.ts
6826
6828
  function getVersion() {
6827
- return "6.2.3-dev.40";
6829
+ return "6.2.3-dev.41";
6828
6830
  }
6829
6831
  function getGitHash() {
6830
- return "9a25fe4dcac53275b3e4e818fa6259f56a2532bb";
6832
+ return "84f56c54bfdb09f7789448e0c77f6c57454a47a6";
6831
6833
  }
6832
6834
  function getGitUrl() {
6833
6835
  return buildTreeUrl(getGitHash());
@@ -6855,11 +6857,446 @@ var SystemSubgraph = class extends BaseSubgraph {
6855
6857
  }) } };
6856
6858
  };
6857
6859
  //#endregion
6858
- //#region src/packages/http-loader.ts
6860
+ //#region src/packages/registry-cache.ts
6861
+ /** Entry files a registry package's node build is served from, CDN-relative. */
6862
+ const PACKAGE_ENTRIES = {
6863
+ documentModels: "node/document-models/index.mjs",
6864
+ subgraphs: "node/subgraphs/index.mjs",
6865
+ processors: "node/processors/index.mjs"
6866
+ };
6867
+ const REGISTRY_ENTRY_ABSENT = "ERR_REGISTRY_ENTRY_ABSENT";
6868
+ const MANIFEST_FILE = "manifest.json";
6869
+ function defaultRegistryCacheDir(cwd = process.cwd()) {
6870
+ return path.join(cwd, ".ph", "registry-packages");
6871
+ }
6859
6872
  const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
6873
+ const PACKAGE_NAME = /^(@[a-z0-9][-a-z0-9._]*\/)?[a-z0-9][-a-z0-9._]*$/i;
6874
+ function isValidPackageName(name) {
6875
+ return PACKAGE_NAME.test(name) && !name.includes("..") && name.length <= 214;
6876
+ }
6877
+ const MAX_FILE_BYTES = 16777216;
6878
+ const MAX_TOTAL_BYTES = 134217728;
6879
+ const MAX_FILES = 2e3;
6880
+ const MAX_IN_FLIGHT = 4;
6881
+ const FETCH_TIMEOUT_MS = 3e4;
6882
+ const DEAD_PID_GRACE_MS = 6e4;
6883
+ const ABANDONED_AFTER_MS = 36e5;
6884
+ const LEFTOVER = /\.(?:tmp|stale)-(\d+)-[0-9a-f]{8}$/;
6885
+ const STATIC_IMPORT = 1;
6886
+ const DYNAMIC_IMPORT = 2;
6887
+ var RegistryPackageCache = class {
6888
+ cacheDir;
6889
+ registryUrl;
6890
+ logger;
6891
+ fetchTimeoutMs;
6892
+ inFlight = /* @__PURE__ */ new Map();
6893
+ verified = /* @__PURE__ */ new Map();
6894
+ constructor(options) {
6895
+ this.registryUrl = options.registryUrl.endsWith("/") ? options.registryUrl : `${options.registryUrl}/`;
6896
+ this.cacheDir = path.resolve(options.cacheDir ?? defaultRegistryCacheDir());
6897
+ this.logger = options.logger ?? childLogger(["reactor-api", "registry-cache"]);
6898
+ this.fetchTimeoutMs = options.fetchTimeoutMs ?? FETCH_TIMEOUT_MS;
6899
+ }
6900
+ /** Base URL every file of `name@version` is fetched under. */
6901
+ baseUrl(name, version) {
6902
+ return `${this.registryUrl}-/cdn/${name}@${version}/`;
6903
+ }
6904
+ entryDir(name, version) {
6905
+ assertCoordinate(name, version);
6906
+ const dir = path.join(this.cacheDir, `${name}@${version}`);
6907
+ assertWithin(dir, this.cacheDir);
6908
+ return dir;
6909
+ }
6910
+ /** Complete cached versions of `name`, newest-agnostic. */
6911
+ async cachedVersions(name) {
6912
+ if (!isValidPackageName(name)) return [];
6913
+ const parent = path.dirname(path.join(this.cacheDir, `${name}@0.0.0`));
6914
+ const leaf = path.basename(name);
6915
+ let names;
6916
+ try {
6917
+ names = await readdir(parent);
6918
+ } catch {
6919
+ return [];
6920
+ }
6921
+ const versions = [];
6922
+ for (const entry of names) {
6923
+ if (!entry.startsWith(`${leaf}@`)) continue;
6924
+ const version = entry.slice(leaf.length + 1);
6925
+ if (!EXACT_VERSION.test(version)) continue;
6926
+ try {
6927
+ await stat(path.join(parent, entry, MANIFEST_FILE));
6928
+ versions.push(version);
6929
+ } catch {}
6930
+ }
6931
+ return versions;
6932
+ }
6933
+ async ensurePackage(name, version) {
6934
+ const dir = this.entryDir(name, version);
6935
+ const done = this.verified.get(dir);
6936
+ if (done) return {
6937
+ ...done,
6938
+ source: "cache"
6939
+ };
6940
+ const pending = this.inFlight.get(dir);
6941
+ if (pending) return pending;
6942
+ const started = this.resolveEntry(name, version, dir).finally(() => this.inFlight.delete(dir));
6943
+ this.inFlight.set(dir, started);
6944
+ return started;
6945
+ }
6946
+ async resolveEntry(name, version, dir) {
6947
+ let manifest = await this.verify(dir, name, version);
6948
+ let source = "cache";
6949
+ if (!manifest) {
6950
+ manifest = await this.fill(name, version, dir);
6951
+ source = "download";
6952
+ }
6953
+ await this.linkBareImports(manifest.bareImports);
6954
+ const entries = {};
6955
+ for (const [kind, rel] of Object.entries(manifest.entries)) entries[kind] = path.join(dir, rel);
6956
+ const result = {
6957
+ name,
6958
+ version,
6959
+ dir,
6960
+ entries,
6961
+ source
6962
+ };
6963
+ this.verified.set(dir, result);
6964
+ return result;
6965
+ }
6966
+ /** The manifest when every listed file is present and hashes to it. */
6967
+ async verify(dir, name, version) {
6968
+ let manifest;
6969
+ try {
6970
+ manifest = parseManifest(await readFile(path.join(dir, MANIFEST_FILE), "utf8"));
6971
+ } catch (error) {
6972
+ if (error.code === "ENOENT") return null;
6973
+ this.logger.warn("Unreadable cache manifest in @dir: @error", dir, error);
6974
+ await this.discard(dir);
6975
+ return null;
6976
+ }
6977
+ if (manifest.name !== name || manifest.version !== version || Object.values(manifest.entries).some((rel) => !(rel in manifest.files))) {
6978
+ this.logger.warn("Cache manifest in @dir names another package", dir);
6979
+ await this.discard(dir);
6980
+ return null;
6981
+ }
6982
+ for (const [rel, expected] of Object.entries(manifest.files)) {
6983
+ let actual;
6984
+ try {
6985
+ const file = path.join(dir, rel);
6986
+ assertWithin(file, dir);
6987
+ actual = sha256(await readFile(file));
6988
+ } catch {
6989
+ actual = null;
6990
+ }
6991
+ if (actual !== expected) {
6992
+ this.logger.warn("Cached @file of @package failed verification; fetching it again", rel, `${name}@${version}`);
6993
+ await this.discard(dir);
6994
+ return null;
6995
+ }
6996
+ }
6997
+ return manifest;
6998
+ }
6999
+ async discard(dir) {
7000
+ const aside = `${dir}.stale-${process.pid}-${randomUUID().slice(0, 8)}`;
7001
+ try {
7002
+ await rename(dir, aside);
7003
+ } catch {
7004
+ return;
7005
+ }
7006
+ await rm(aside, {
7007
+ recursive: true,
7008
+ force: true
7009
+ });
7010
+ }
7011
+ async fill(name, version, dir) {
7012
+ const staging = `${dir}.tmp-${process.pid}-${randomUUID().slice(0, 8)}`;
7013
+ assertWithin(staging, this.cacheDir);
7014
+ await sweepLeftovers(path.dirname(dir));
7015
+ await mkdir(staging, { recursive: true });
7016
+ try {
7017
+ const manifest = await this.download(name, version, staging);
7018
+ await writeFile(path.join(staging, MANIFEST_FILE), JSON.stringify(manifest, null, 2));
7019
+ await chmod(staging, 493);
7020
+ try {
7021
+ await rename(staging, dir);
7022
+ } catch {
7023
+ const theirs = await this.verify(dir, name, version);
7024
+ if (theirs) return theirs;
7025
+ await this.discard(dir);
7026
+ await rename(staging, dir);
7027
+ }
7028
+ return manifest;
7029
+ } finally {
7030
+ await rm(staging, {
7031
+ recursive: true,
7032
+ force: true
7033
+ });
7034
+ }
7035
+ }
7036
+ async download(name, version, staging) {
7037
+ await init;
7038
+ const base = this.baseUrl(name, version);
7039
+ const files = {};
7040
+ const bare = /* @__PURE__ */ new Set();
7041
+ const entryRels = new Set(Object.values(PACKAGE_ENTRIES));
7042
+ const queue = [...entryRels];
7043
+ const seen = new Set(queue);
7044
+ let totalBytes = 0;
7045
+ const processOne = async (rel) => {
7046
+ const url = new URL(rel, base).href;
7047
+ const body = await this.fetchBounded(url, entryRels.has(rel));
7048
+ if (!body) return;
7049
+ totalBytes += body.byteLength;
7050
+ if (totalBytes > MAX_TOTAL_BYTES) throw new Error(`${name}@${version} exceeds the ${MAX_TOTAL_BYTES}-byte cache cap`);
7051
+ const target = path.join(staging, rel);
7052
+ assertWithin(target, staging);
7053
+ for (const dep of this.dependenciesOf(body.toString("utf8"), url, base)) {
7054
+ if (dep.kind === "bare") {
7055
+ bare.add(dep.specifier);
7056
+ continue;
7057
+ }
7058
+ if (seen.has(dep.rel)) continue;
7059
+ if (seen.size >= MAX_FILES) throw new Error(`${name}@${version} imports more than ${MAX_FILES} files`);
7060
+ seen.add(dep.rel);
7061
+ queue.push(dep.rel);
7062
+ }
7063
+ files[rel] = sha256(body);
7064
+ await mkdir(path.dirname(target), { recursive: true });
7065
+ await writeFile(target, body);
7066
+ };
7067
+ const active = /* @__PURE__ */ new Set();
7068
+ let failure;
7069
+ while ((queue.length > 0 || active.size > 0) && failure === void 0) {
7070
+ while (queue.length > 0 && active.size < MAX_IN_FLIGHT) {
7071
+ const task = processOne(queue.shift()).then(() => {
7072
+ active.delete(task);
7073
+ }, (error) => {
7074
+ active.delete(task);
7075
+ failure ??= error instanceof Error ? error : new Error(String(error));
7076
+ });
7077
+ active.add(task);
7078
+ }
7079
+ if (active.size > 0) await Promise.race(active);
7080
+ }
7081
+ if (failure !== void 0) {
7082
+ await Promise.allSettled(active);
7083
+ throw failure;
7084
+ }
7085
+ const entries = {};
7086
+ for (const [kind, rel] of Object.entries(PACKAGE_ENTRIES)) if (rel in files) entries[kind] = rel;
7087
+ if (Object.keys(entries).length === 0) throw new Error(`${name}@${version} serves no document models, subgraphs or processors`);
7088
+ return {
7089
+ name,
7090
+ version,
7091
+ entries,
7092
+ files: Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b))),
7093
+ bareImports: [...bare].sort()
7094
+ };
7095
+ }
7096
+ dependenciesOf(source, url, base) {
7097
+ const [imports] = parse$1(source, url);
7098
+ const deps = [];
7099
+ for (const imp of imports) {
7100
+ const kind = imp.t;
7101
+ if (kind !== STATIC_IMPORT && kind !== DYNAMIC_IMPORT) continue;
7102
+ const specifier = imp.n;
7103
+ if (specifier === void 0) {
7104
+ if (kind === DYNAMIC_IMPORT) this.logger.warn("Non-literal dynamic import in @url is not cached: @expr", url, source.slice(imp.s, imp.e));
7105
+ continue;
7106
+ }
7107
+ if (isBuiltin(specifier)) continue;
7108
+ if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/") || /^https?:\/\//i.test(specifier)) {
7109
+ deps.push({
7110
+ kind: "file",
7111
+ rel: relativeTo(base, specifier, url)
7112
+ });
7113
+ continue;
7114
+ }
7115
+ if (/^[a-z][a-z0-9+.-]*:/i.test(specifier)) throw new Error(`${url} imports an unsupported URL: ${specifier}`);
7116
+ deps.push({
7117
+ kind: "bare",
7118
+ specifier
7119
+ });
7120
+ }
7121
+ return deps;
7122
+ }
7123
+ async fetchBounded(url, optional = false) {
7124
+ const response = await fetch(url, { signal: AbortSignal.timeout(this.fetchTimeoutMs) });
7125
+ if (optional && response.status === 404) {
7126
+ await response.body?.cancel();
7127
+ return null;
7128
+ }
7129
+ if (!response.ok) {
7130
+ await response.body?.cancel();
7131
+ throw new Error(`${url} responded ${response.status}`);
7132
+ }
7133
+ const declared = response.headers.get("content-length");
7134
+ if (declared && Number(declared) > MAX_FILE_BYTES) {
7135
+ await response.body?.cancel();
7136
+ throw new Error(`${url} exceeds the ${MAX_FILE_BYTES}-byte file cap`);
7137
+ }
7138
+ if (!response.body) return Buffer.from(await response.arrayBuffer());
7139
+ const reader = response.body.getReader();
7140
+ const chunks = [];
7141
+ let total = 0;
7142
+ for (;;) {
7143
+ const { done, value } = await reader.read();
7144
+ if (done) break;
7145
+ total += value.byteLength;
7146
+ if (total > MAX_FILE_BYTES) {
7147
+ await reader.cancel();
7148
+ throw new Error(`${url} exceeds the ${MAX_FILE_BYTES}-byte file cap`);
7149
+ }
7150
+ chunks.push(value);
7151
+ }
7152
+ return Buffer.concat(chunks);
7153
+ }
7154
+ async linkBareImports(specifiers) {
7155
+ const outsideUrl = pathToFileURL(path.join(path.dirname(this.cacheDir), "/")).href;
7156
+ const linkRoot = path.join(this.cacheDir, "node_modules");
7157
+ for (const specifier of specifiers) {
7158
+ const pkg = packageNameOf(specifier);
7159
+ if (!pkg || !isValidPackageName(pkg)) continue;
7160
+ const link = path.join(linkRoot, pkg);
7161
+ assertWithin(link, linkRoot);
7162
+ if (packageRoot(specifier, pkg, outsideUrl)) {
7163
+ await removeLink(link);
7164
+ continue;
7165
+ }
7166
+ const fallback = packageRoot(specifier, pkg, import.meta.url);
7167
+ if (!fallback) {
7168
+ this.logger.warn("Registry package import @specifier resolves from neither the project nor reactor-api", specifier);
7169
+ continue;
7170
+ }
7171
+ try {
7172
+ await ensureLink(link, await realpath(fallback));
7173
+ } catch (error) {
7174
+ this.logger.warn("Could not link @pkg for cached registry packages: @error", pkg, error);
7175
+ }
7176
+ }
7177
+ }
7178
+ };
7179
+ function sha256(data) {
7180
+ return createHash("sha256").update(data).digest("hex");
7181
+ }
7182
+ function assertCoordinate(name, version) {
7183
+ if (!isValidPackageName(name)) throw new Error(`Invalid registry package name: ${name}`);
7184
+ if (!EXACT_VERSION.test(version)) throw new Error(`Invalid registry package version: ${version}`);
7185
+ }
7186
+ function assertWithin(target, root) {
7187
+ const resolvedRoot = path.resolve(root);
7188
+ const resolved = path.resolve(target);
7189
+ if (resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + path.sep)) throw new Error(`Path escapes the registry cache: ${target}`);
7190
+ }
7191
+ function relativeTo(base, specifier, fromUrl) {
7192
+ const resolved = new URL(specifier, fromUrl);
7193
+ resolved.search = "";
7194
+ resolved.hash = "";
7195
+ if (!resolved.href.startsWith(base)) throw new Error(`${fromUrl} imports ${specifier}, which is outside the package`);
7196
+ const rel = decodeURIComponent(resolved.href.slice(base.length));
7197
+ if (rel === "" || rel === "manifest.json" || rel.includes("\\") || rel.includes("\0") || rel.split("/").some((s) => s === "" || s === "." || s === "..")) throw new Error(`${fromUrl} imports an unusable path: ${specifier}`);
7198
+ return rel;
7199
+ }
7200
+ function parseManifest(raw) {
7201
+ const value = JSON.parse(raw);
7202
+ if (typeof value.name !== "string" || typeof value.version !== "string" || typeof value.entries !== "object" || value.entries === null || typeof value.files !== "object" || value.files === null || !Array.isArray(value.bareImports)) throw new Error("malformed manifest");
7203
+ return value;
7204
+ }
7205
+ function packageNameOf(specifier) {
7206
+ const parts = specifier.split("/");
7207
+ if (specifier.startsWith("@")) return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
7208
+ return parts[0] || null;
7209
+ }
7210
+ function packageRoot(specifier, pkg, base) {
7211
+ let found;
7212
+ try {
7213
+ found = findPackageJSON(specifier, base);
7214
+ } catch {
7215
+ return null;
7216
+ }
7217
+ if (!found) return null;
7218
+ let dir = path.dirname(found);
7219
+ for (let i = 0; i < 16; i++) {
7220
+ if (dir.endsWith(`${path.sep}node_modules${path.sep}${pkg}`)) return dir;
7221
+ if (readPackageName(path.join(dir, "package.json")) === pkg) return dir;
7222
+ const parent = path.dirname(dir);
7223
+ if (parent === dir) break;
7224
+ dir = parent;
7225
+ }
7226
+ return null;
7227
+ }
7228
+ function readPackageName(file) {
7229
+ try {
7230
+ return JSON.parse(readFileSync(file, "utf8")).name;
7231
+ } catch {
7232
+ return;
7233
+ }
7234
+ }
7235
+ async function sweepLeftovers(parent) {
7236
+ let names;
7237
+ try {
7238
+ names = await readdir(parent);
7239
+ } catch {
7240
+ return;
7241
+ }
7242
+ const now = Date.now();
7243
+ for (const name of names) {
7244
+ const match = LEFTOVER.exec(name);
7245
+ if (!match) continue;
7246
+ const pid = Number(match[1]);
7247
+ if (pid === process.pid) continue;
7248
+ const full = path.join(parent, name);
7249
+ let age;
7250
+ try {
7251
+ age = now - (await lstat(full)).mtimeMs;
7252
+ } catch {
7253
+ continue;
7254
+ }
7255
+ if (age > ABANDONED_AFTER_MS || age > DEAD_PID_GRACE_MS && !isAlive(pid)) await rm(full, {
7256
+ recursive: true,
7257
+ force: true
7258
+ });
7259
+ }
7260
+ }
7261
+ function isAlive(pid) {
7262
+ try {
7263
+ process.kill(pid, 0);
7264
+ return true;
7265
+ } catch (error) {
7266
+ return error.code === "EPERM";
7267
+ }
7268
+ }
7269
+ async function ensureLink(link, target) {
7270
+ try {
7271
+ if (await readlink(link) === target) return;
7272
+ } catch {}
7273
+ await mkdir(path.dirname(link), { recursive: true });
7274
+ await sweepLeftovers(path.dirname(link));
7275
+ const temp = `${link}.tmp-${process.pid}-${randomUUID().slice(0, 8)}`;
7276
+ await symlink(target, temp, "junction");
7277
+ try {
7278
+ await rename(temp, link);
7279
+ } catch (error) {
7280
+ await rm(temp, { force: true });
7281
+ throw error;
7282
+ }
7283
+ }
7284
+ async function removeLink(link) {
7285
+ try {
7286
+ await readlink(link);
7287
+ } catch {
7288
+ return;
7289
+ }
7290
+ await rm(link, { force: true });
7291
+ }
7292
+ //#endregion
7293
+ //#region src/packages/http-loader.ts
6860
7294
  function piecesBaseUrl(registryUrl, packageName, version) {
6861
7295
  return `${registryUrl.endsWith("/") ? registryUrl : `${registryUrl}/`}-/cdn/${packageName}@${version}/node/pieces/`;
6862
7296
  }
7297
+ function documentModelsOf(module) {
7298
+ return Object.values(module).filter((m) => m !== null && typeof m === "object" && "documentModel" in m && m.documentModel !== null);
7299
+ }
6863
7300
  /**
6864
7301
  * Extract subgraph classes from the imported subgraphs/index.mjs module shape.
6865
7302
  *
@@ -6886,8 +7323,14 @@ var HttpPackageLoader = class {
6886
7323
  logger = childLogger(["reactor-api", "http-loader"]);
6887
7324
  name = "HttpPackageLoader";
6888
7325
  documentModelLoader;
7326
+ cache;
7327
+ versions = /* @__PURE__ */ new Map();
6889
7328
  constructor(options) {
6890
7329
  this.registryUrl = options.registryUrl.endsWith("/") ? options.registryUrl : `${options.registryUrl}/`;
7330
+ this.cache = new RegistryPackageCache({
7331
+ registryUrl: this.registryUrl,
7332
+ cacheDir: options.cacheDir
7333
+ });
6891
7334
  this.documentModelLoader = new HttpDocumentModelLoader(this);
6892
7335
  }
6893
7336
  /**
@@ -6919,39 +7362,95 @@ var HttpPackageLoader = class {
6919
7362
  tag: void 0
6920
7363
  };
6921
7364
  }
7365
+ resolveVersion(packageSpec) {
7366
+ const known = this.versions.get(packageSpec);
7367
+ if (known) return known;
7368
+ const lookup = this.lookUpVersion(packageSpec).then((version) => {
7369
+ if (!version) this.versions.delete(packageSpec);
7370
+ return version;
7371
+ });
7372
+ this.versions.set(packageSpec, lookup);
7373
+ return lookup;
7374
+ }
7375
+ async lookUpVersion(packageSpec) {
7376
+ const { name, tag } = this.parsePackageSpec(packageSpec);
7377
+ if (tag && EXACT_VERSION.test(tag)) return tag;
7378
+ const version = await this.packageVersion(packageSpec);
7379
+ if (version) return version;
7380
+ const cached = await this.cache.cachedVersions(name);
7381
+ if (cached.length === 1) {
7382
+ this.logger.warn("Could not resolve a version for @package; using cached @version", packageSpec, cached[0]);
7383
+ return cached[0];
7384
+ }
7385
+ }
7386
+ /** Caches the pinned version of `packageSpec` on disk. */
7387
+ async cachePackage(packageSpec) {
7388
+ const { name: packageName } = this.parsePackageSpec(packageSpec);
7389
+ if (!isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
7390
+ const version = await this.resolveVersion(packageSpec);
7391
+ if (!version) throw new Error(`No exact version found for ${packageSpec}`);
7392
+ const cached = await this.cache.ensurePackage(packageName, version);
7393
+ this.logger.verbose(`Package ${packageName}@${version} at ${cached.dir} (${cached.source})`);
7394
+ return cached;
7395
+ }
7396
+ /** One entry module of the package, plus the cached file it came from. */
7397
+ async importEntry(packageSpec, kind) {
7398
+ const { name: packageName } = this.parsePackageSpec(packageSpec);
7399
+ if (!isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
7400
+ const version = await this.resolveVersion(packageSpec);
7401
+ let cached;
7402
+ if (version) try {
7403
+ cached = await this.cache.ensurePackage(packageName, version);
7404
+ } catch (error) {
7405
+ this.logger.warn("Could not cache @package, importing it over HTTP: @error", packageSpec, error);
7406
+ }
7407
+ if (cached) {
7408
+ const filePath = cached.entries[kind];
7409
+ if (!filePath) throw Object.assign(/* @__PURE__ */ new Error(`${packageName}@${cached.version} serves no ${kind}`), { code: REGISTRY_ENTRY_ABSENT });
7410
+ return {
7411
+ module: await import(
7412
+ /* @vite-ignore */
7413
+ pathToFileURL(filePath).href
7414
+ ),
7415
+ filePath
7416
+ };
7417
+ }
7418
+ const pinned = version ? `${packageName}@${version}` : packageSpec;
7419
+ const url = `${this.registryUrl}-/cdn/${pinned}/${PACKAGE_ENTRIES[kind]}`;
7420
+ this.logger.verbose(`Importing ${kind} from: ${url}`);
7421
+ return { module: await import(
7422
+ /* @vite-ignore */
7423
+ url
7424
+ ) };
7425
+ }
7426
+ importDocumentModels(packageSpec) {
7427
+ return this.importEntry(packageSpec, "documentModels");
7428
+ }
6922
7429
  async loadDocumentModels(packageSpec) {
6923
7430
  const { name: packageName } = this.parsePackageSpec(packageSpec);
6924
- if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
6925
- const url = `${this.registryUrl}-/cdn/${packageSpec}/node/document-models/index.mjs`;
6926
- this.logger.verbose(`Importing document-models from: ${url}`);
6927
- const module = await import(url);
6928
- const models = Object.values(module).filter((m) => m !== null && typeof m === "object" && "documentModel" in m && m.documentModel !== null);
7431
+ const { module } = await this.importDocumentModels(packageSpec);
7432
+ const models = documentModelsOf(module);
6929
7433
  this.logger.verbose(`Loaded ${models.length} document models from ${packageName}`);
6930
7434
  return models;
6931
7435
  }
6932
7436
  async loadUpgradeManifests(packageSpec) {
6933
7437
  const { name: packageName } = this.parsePackageSpec(packageSpec);
6934
- if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
6935
- const module = await import(`${this.registryUrl}-/cdn/${packageSpec}/node/document-models/index.mjs`);
7438
+ const { module } = await this.importDocumentModels(packageSpec);
6936
7439
  const manifests = extractUpgradeManifests(module);
6937
7440
  if (manifests.length > 0) this.logger.verbose(`Loaded ${manifests.length} upgrade manifests from ${packageName}`);
6938
7441
  return manifests;
6939
7442
  }
6940
7443
  async loadSubgraphs(packageSpec) {
6941
7444
  const { name: packageName } = this.parsePackageSpec(packageSpec);
6942
- if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
6943
- const url = `${this.registryUrl}-/cdn/${packageSpec}/node/subgraphs/index.mjs`;
6944
- this.logger.verbose(`Importing subgraphs from: ${url}`);
6945
- const subgraphs = extractSubgraphsFromModule(await import(url));
7445
+ const { module } = await this.importEntry(packageSpec, "subgraphs");
7446
+ const subgraphs = extractSubgraphsFromModule(module);
6946
7447
  this.logger.verbose(`Loaded ${subgraphs.length} subgraphs from ${packageName}`);
6947
7448
  return subgraphs;
6948
7449
  }
6949
7450
  async loadProcessors(packageSpec) {
6950
7451
  const { name: packageName } = this.parsePackageSpec(packageSpec);
6951
- if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
6952
- const url = `${this.registryUrl}-/cdn/${packageSpec}/node/processors/index.mjs`;
6953
- this.logger.verbose(`Importing processors from: ${url}`);
6954
- const factory = (await import(url)).processorFactory;
7452
+ const { module } = await this.importEntry(packageSpec, "processors");
7453
+ const factory = module.processorFactory;
6955
7454
  if (factory && typeof factory === "function") {
6956
7455
  this.logger.verbose(`Loaded processor factory from ${packageName}`);
6957
7456
  return factory;
@@ -6961,8 +7460,8 @@ var HttpPackageLoader = class {
6961
7460
  }
6962
7461
  async loadPieces(packageSpec) {
6963
7462
  const { name: packageName } = this.parsePackageSpec(packageSpec);
6964
- if (!this.isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
6965
- const version = await this.packageVersion(packageSpec);
7463
+ if (!isValidPackageName(packageName)) throw new Error(`Invalid package name: ${packageName}`);
7464
+ const version = await this.resolveVersion(packageSpec);
6966
7465
  if (!version) {
6967
7466
  this.logger.verbose(`No package version found for: ${packageSpec}`);
6968
7467
  return [];
@@ -7013,21 +7512,13 @@ var HttpPackageLoader = class {
7013
7512
  return;
7014
7513
  }
7015
7514
  }
7016
- isValidPackageName(name) {
7017
- return /^(@[a-z0-9][-a-z0-9._]*\/)?[a-z0-9][-a-z0-9._]*$/i.test(name) && !name.includes("..") && name.length <= 214;
7018
- }
7019
7515
  };
7020
- /**
7021
- * Returns live modules (host-only sources): dynamically loaded models are
7022
- * registered on the host registry but never reach executor worker threads.
7023
- * Making them worker-executable means returning an importable source here
7024
- * (the CDN URL as a package specifier, given process-wide https hooks).
7025
- */
7026
7516
  var HttpDocumentModelLoader = class {
7027
7517
  loader;
7028
7518
  logger = childLogger(["reactor-api", "http-document-model-loader"]);
7029
7519
  documentTypeCache = /* @__PURE__ */ new Map();
7030
7520
  packageModulesCache = /* @__PURE__ */ new Map();
7521
+ packageFileCache = /* @__PURE__ */ new Map();
7031
7522
  onModelLoaded;
7032
7523
  constructor(loader) {
7033
7524
  this.loader = loader;
@@ -7038,15 +7529,16 @@ var HttpDocumentModelLoader = class {
7038
7529
  clearCache() {
7039
7530
  this.documentTypeCache.clear();
7040
7531
  this.packageModulesCache.clear();
7532
+ this.packageFileCache.clear();
7041
7533
  }
7042
7534
  async load(documentType) {
7043
7535
  const packageName = await this.findPackageByDocumentType(documentType);
7044
- let models;
7045
- const cachedModels = this.packageModulesCache.get(packageName);
7046
- if (cachedModels) models = cachedModels;
7047
- else {
7048
- models = await this.loader.loadDocumentModels(packageName);
7536
+ let models = this.packageModulesCache.get(packageName);
7537
+ if (!models) {
7538
+ const { module, filePath } = await this.loader.importDocumentModels(packageName);
7539
+ models = documentModelsOf(module);
7049
7540
  this.packageModulesCache.set(packageName, models);
7541
+ if (filePath) this.packageFileCache.set(packageName, filePath);
7050
7542
  }
7051
7543
  const model = models.find((m) => m.documentModel.global.id === documentType);
7052
7544
  if (!model) {
@@ -7055,7 +7547,8 @@ var HttpDocumentModelLoader = class {
7055
7547
  }
7056
7548
  this.logger.info(`Loaded document model "${documentType}" from package "${packageName}"`);
7057
7549
  if (this.onModelLoaded) this.onModelLoaded(model);
7058
- return model;
7550
+ const filePath = this.packageFileCache.get(packageName);
7551
+ return filePath ? { filePath } : model;
7059
7552
  }
7060
7553
  async findPackageByDocumentType(documentType) {
7061
7554
  const cached = this.documentTypeCache.get(documentType);
@@ -7078,6 +7571,7 @@ var HttpDocumentModelLoader = class {
7078
7571
  }
7079
7572
  removeFromCache(packageName) {
7080
7573
  this.packageModulesCache.delete(packageName);
7574
+ this.packageFileCache.delete(packageName);
7081
7575
  for (const [docType, pkg] of this.documentTypeCache) if (pkg === packageName) this.documentTypeCache.delete(docType);
7082
7576
  }
7083
7577
  };
@@ -7180,6 +7674,7 @@ function isExpectedLoaderMiss(error, pkg, subPath) {
7180
7674
  if (!(error instanceof Error)) return false;
7181
7675
  const code = error.code;
7182
7676
  if (code === "ERR_UNSUPPORTED_DIR_IMPORT") return true;
7677
+ if (code === "ERR_REGISTRY_ENTRY_ABSENT") return true;
7183
7678
  if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED" && subPath !== void 0 && error.message.includes(`'./${subPath}'`)) return true;
7184
7679
  if (subPath !== void 0 && new RegExp(`^Failed to fetch \\S*/${subPath}/index\\.mjs: 404$`).test(error.message)) return true;
7185
7680
  if (error.message.startsWith("Invalid package name:")) return true;
@@ -9598,4 +10093,4 @@ var PackageManagementService = class {
9598
10093
  export { ADMIN_USERS, ActionCandidateInputSchema, ActionContextInputSchema, ActionInputSchema, AddRelationshipDocument, AnalyticsSubgraph, AttachmentAccessFailed, AttachmentAccessService, AttachmentAccessUnavailable, AttachmentClientProvider, AuthDecision, AuthDecisionSchema, AuthEvaluationUnsupportedError, AuthService, AuthSubgraph, AuthenticationRequiredError, AuthorizationPolicy, AuthorizedAttachmentService, AuthorizedDocumentHandle, BaseSubgraph, CORE_PACKAGE_NAME, CanonicalDocumentIdResolutionError, ChannelMetaInputSchema, CreateDocumentDocument, CreateEmptyDocumentDocument, DeleteDocumentDocument, DeleteDocumentsDocument, DocumentChangeType, DocumentChangeTypeSchema, DocumentChangesDocument, DocumentOperationsFilterInputSchema, DocumentPermissionService, DocumentRelationshipFieldsFragmentDoc, EvaluateActionsDocument, FindDocumentsDocument, ForbiddenError, GetDocumentDocument, GetDocumentIncomingRelationshipEdgesDocument, GetDocumentIncomingRelationshipsDocument, GetDocumentModelsDocument, GetDocumentOperationsDocument, GetDocumentOutgoingRelationshipEdgesDocument, GetDocumentOutgoingRelationshipsDocument, GetDocumentWithOperationsDocument, GetJobStatusDocument, GraphQLManager, HttpDocumentModelLoader, HttpPackageLoader, HttpRouteService, ImportPackageLoader, InMemoryPackageStorage, InvalidNamespaceError, JobChangesDocument, MalformedStoredOperationError, MemoryWebhookStore, MoveRelationshipDocument, MutateDocumentAsyncDocument, MutateDocumentDocument, OperationContextInputSchema, OperationInputSchema, OperationWithContextInputSchema, OperationsFilterInputSchema, PACKAGE_ROUTE_SEGMENT, PGLITE_UTC_PARSERS, PackageManagementService, PackageManager, PackagesSubgraph, PagingInputSchema, PhDocumentFieldsFragmentDoc, PollSyncEnvelopesDocument, PrivacyPermissionAdapter, PropagationMode, PropagationModeSchema, PushSyncEnvelopesDocument, ReactorSignerAppInputSchema, ReactorSignerInputSchema, ReactorSignerUserInputSchema, ReactorSubgraph, RefusalNotRecordedError, RelationalWebhookStore, RemoteCursorInputSchema, RemoteFilterInputSchema, RemoveRelationshipDocument, RenameDocumentDocument, SearchFilterInputSchema, SetPreferredEditorDocument, SyncEnvelopeInputSchema, SyncEnvelopeType, SyncEnvelopeTypeSchema, SyncRefusalInputSchema, SystemSubgraph, TouchChannelDocument, TouchChannelInputSchema, UpdateRelationshipDocument, ViewFilterInputSchema, WEBHOOK_SEGMENT, WS_AUTH_CLOSE_REASONS, WS_CLOSE_REASON_AUTHENTICATION_REQUIRED, WS_CLOSE_REASON_BEARER_REJECTED, WebhookService, assertAttachmentPolicyReadsAllowed, assertAuthRequiredForDocumentPermissions, assertCredentialVerifierForSource, assertHostMountPath, assertRequireAuthenticatedCallerAllowed, assertSkipCredentialVerificationAllowed, buildGraphQlDocument, buildGraphQlDriveDocument, buildGraphqlOperation, buildGraphqlOperations, buildSubgraphSchemaModule, callerSubject, createAuthFetchMiddleware, createCanonicalDocumentIdResolver, createGatewayAdapter, createHttpAdapter, createMergedSchema, createReactorGraphQLClient, createRenownCredentialVerifier, createSchema, createWsAuthHandlers, decodeExplorerUrlState, definedNonNullAnySchema, driveIdFromUrl, extractSubgraphsFromModule, generateDocumentModelSchema, getAuthContext, getDbClient, getDocumentModelSchemaName, getDocumentModelTypeDefs, getExplorerPrefix, getGitHash, getGitUrl, getSdk, getUniqueDocumentModels, getUniqueUpgradeManifests, getVersion, initAnalyticsStoreSql, initializeAndStartAPI, isDefinedNonNullAny, isExpectedLoaderMiss, isSubgraphClass, namespacePath, namespaceSegments, newWebhookToken, parseDriveUrl, parseWebhookBody, piecesBaseUrl, redactHeaders, renderGraphqlPlayground, resolvePackageName, resolveRenownConfig, verifyWebhook };
9599
10094
 
9600
10095
  //# sourceMappingURL=index.mjs.map
9601
- //# debugId=a50faed2-4e40-54ba-873f-1808cedc1e5c
10096
+ //# debugId=65e47e18-15f4-5a67-b94b-f6e795c1eb9b