@powerhousedao/switchboard 6.2.3-dev.44 → 6.2.3-dev.46
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/.tsbuild/test/durable-node-fs-pg16.test.d.ts +2 -0
- package/.tsbuild/test/durable-node-fs-pg16.test.d.ts.map +1 -0
- package/.tsbuild/test/pglite-preflight.test.d.ts +2 -0
- package/.tsbuild/test/pglite-preflight.test.d.ts.map +1 -0
- package/.tsbuild/test/snapshot-writer.d.ts +18 -0
- package/.tsbuild/test/snapshot-writer.d.ts.map +1 -0
- package/.tsbuild/test/workflow-reactor-access.test.d.ts +2 -0
- package/.tsbuild/test/workflow-reactor-access.test.d.ts.map +1 -0
- package/.tsbuild/test/workflow-reactor-enforcement.test.d.ts +2 -0
- package/.tsbuild/test/workflow-reactor-enforcement.test.d.ts.map +1 -0
- package/.tsbuild/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +46 -0
- package/dist/index.mjs +1 -1
- package/dist/{server-Bk_IZzCn.mjs → server-Dvw8_NeM.mjs} +256 -77
- package/dist/server-Dvw8_NeM.mjs.map +1 -0
- package/dist/server.d.mts +3 -1
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +3 -3
- package/package.json +15 -16
- package/test/boot-unsupported-stored-documents.test.ts +5 -5
- package/test/durable-node-fs-pg16.test.ts +80 -0
- package/test/pglite-dialect.test.ts +28 -0
- package/test/pglite-preflight.test.ts +313 -0
- package/test/pglite-version.test.ts +9 -1
- package/test/snapshot-writer.ts +94 -0
- package/test/worker-pool.test.ts +27 -1
- package/test/workflow/resolvers.test.ts +26 -0
- package/test/workflow-reactor-access.test.ts +384 -0
- package/test/workflow-reactor-enforcement.test.ts +805 -0
- package/test/workflow-read-gate.test.ts +125 -60
- package/test/workflow-runtime.test.ts +158 -169
- package/tsconfig.json +0 -3
- package/dist/server-Bk_IZzCn.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
+
## 6.2.3-dev.46 (2026-10-07)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **connect:** load local project models in the reactor worker ([6e2929ce60](https://github.com/powerhouse-inc/powerhouse/commit/6e2929ce60))
|
|
6
|
+
|
|
7
|
+
### 🩹 Fixes
|
|
8
|
+
|
|
9
|
+
- **builder-tools:** make the worker bundle survive consumer projects ([6361a66b6d](https://github.com/powerhouse-inc/powerhouse/commit/6361a66b6d))
|
|
10
|
+
- **builder-tools:** assert the vendor-dir mode only where modes exist ([f47c32a020](https://github.com/powerhouse-inc/powerhouse/commit/f47c32a020))
|
|
11
|
+
- **switchboard:** never let observability lower a fatal exit code ([0498342d22](https://github.com/powerhouse-inc/powerhouse/commit/0498342d22))
|
|
12
|
+
- **switchboard:** survive a logger that fails during fatal shutdown ([ecc8975282](https://github.com/powerhouse-inc/powerhouse/commit/ecc8975282))
|
|
13
|
+
|
|
14
|
+
### ❤️ Thank You
|
|
15
|
+
|
|
16
|
+
- Claude Fable 5
|
|
17
|
+
- Wouter Kampmann
|
|
18
|
+
|
|
19
|
+
## 6.2.3-dev.45 (2026-10-06)
|
|
20
|
+
|
|
21
|
+
### 🚀 Features
|
|
22
|
+
|
|
23
|
+
- ⚠️ **reactor-workflow:** let any workflow piece read and write documents through the reactor ([fcccf0dfca](https://github.com/powerhouse-inc/powerhouse/commit/fcccf0dfca))
|
|
24
|
+
- **switchboard:** open PGlite stores over the durable NodeFS ([b5126c3056](https://github.com/powerhouse-inc/powerhouse/commit/b5126c3056))
|
|
25
|
+
|
|
26
|
+
### 🩹 Fixes
|
|
27
|
+
|
|
28
|
+
- **pglite:** bound the old-dir removal after conversion ([0aef46f0ca](https://github.com/powerhouse-inc/powerhouse/commit/0aef46f0ca))
|
|
29
|
+
- **pglite:** open files read-write for the tree sync ([ce0fa23af5](https://github.com/powerhouse-inc/powerhouse/commit/ce0fa23af5))
|
|
30
|
+
- **switchboard:** resolve the legacy pg_dump wasm path on Windows ([c35148183a](https://github.com/powerhouse-inc/powerhouse/commit/c35148183a))
|
|
31
|
+
- **switchboard:** dump legacy PG16 stores from an in-memory copy ([8084fa5b1e](https://github.com/powerhouse-inc/powerhouse/commit/8084fa5b1e))
|
|
32
|
+
|
|
33
|
+
### 🔥 Performance
|
|
34
|
+
|
|
35
|
+
- **pglite:** sync the tree once after initdb, conversion and migration ([9ec8caf970](https://github.com/powerhouse-inc/powerhouse/commit/9ec8caf970))
|
|
36
|
+
|
|
37
|
+
### ⚠️ Breaking Changes
|
|
38
|
+
|
|
39
|
+
- **reactor-workflow:** let any workflow piece read and write documents through the reactor ([fcccf0dfca](https://github.com/powerhouse-inc/powerhouse/commit/fcccf0dfca))
|
|
40
|
+
|
|
41
|
+
### ❤️ Thank You
|
|
42
|
+
|
|
43
|
+
- acaldas
|
|
44
|
+
- Benjamin Jordan
|
|
45
|
+
- Claude Fable 5.1
|
|
46
|
+
|
|
1
47
|
## 6.2.3-dev.44 (2026-10-06)
|
|
2
48
|
|
|
3
49
|
### 🩹 Fixes
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
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]="1fbb2406-730e-5d3e-a4cf-ac7dea3cedd5")}catch(e){}}();
|
|
4
|
-
import { i as StoredDocumentsRefusedError, r as startSwitchboard, s as parseForcePgVersion } from "./server-
|
|
4
|
+
import { i as StoredDocumentsRefusedError, r as startSwitchboard, s as parseForcePgVersion } from "./server-Dvw8_NeM.mjs";
|
|
5
5
|
import { metrics } from "@opentelemetry/api";
|
|
6
6
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
7
7
|
import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
|
|
@@ -1,5 +1,5 @@
|
|
|
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]="b9c02424-1406-5d63-abbd-6c32caa53205")}catch(e){}}();
|
|
3
3
|
import { n as addDefaultReactorDrive, r as isPostgresUrl, t as addDefaultDrive } from "./utils-uaBlzyjS.mjs";
|
|
4
4
|
import { createRequire, register } from "node:module";
|
|
5
5
|
import { ReactorInstrumentation } from "@powerhousedao/opentelemetry-instrumentation-reactor";
|
|
@@ -9,7 +9,7 @@ import dotenv from "dotenv";
|
|
|
9
9
|
import { getConfig } from "@powerhousedao/config/node";
|
|
10
10
|
import { existsSync, promises, realpathSync } from "node:fs";
|
|
11
11
|
import path, { resolve } from "node:path";
|
|
12
|
-
import {
|
|
12
|
+
import { createDurableNodeFs, preparePgliteDataDir, removeStalePgliteFiles, resolvePgliteFsync, syncTree } from "@powerhousedao/reactor-api/pglite-node";
|
|
13
13
|
import { ChannelScheme, DriveCollectionId, EventBus, REACTOR_SCHEMA, ReactorBuilder, ReactorClientBuilder, UnsupportedStoredProtocolError, createHybridProjectionCoordinatorFactory, instrumentPgPool, parseDriveUrl, supportsLiveReadModelRegistration } from "@powerhousedao/reactor";
|
|
14
14
|
import { AuthorizationPolicy, BaseSubgraph, ForbiddenError, HttpPackageLoader, ImportPackageLoader, PGLITE_UTC_PARSERS, PackageManagementService, PackagesSubgraph, PrivacyPermissionAdapter, callerSubject, createCanonicalDocumentIdResolver, getUniqueDocumentModels, initializeAndStartAPI, resolveRenownConfig } from "@powerhousedao/reactor-api";
|
|
15
15
|
import { httpsHooksPath } from "@powerhousedao/reactor-api/https-hooks";
|
|
@@ -29,7 +29,7 @@ import { driveDocumentModelModule } from "@powerhousedao/shared/document-drive";
|
|
|
29
29
|
import { DEFAULT_SIGNATURE_POLICY, isSignaturePolicy } from "@powerhousedao/shared/document-model";
|
|
30
30
|
import { readFile } from "node:fs/promises";
|
|
31
31
|
import os from "node:os";
|
|
32
|
-
import { fileURLToPath } from "node:url";
|
|
32
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
33
33
|
import { EnvVarProvider } from "@openfeature/env-var-provider";
|
|
34
34
|
import { OpenFeature } from "@openfeature/server-sdk";
|
|
35
35
|
import { GraphQLError, Kind } from "graphql";
|
|
@@ -65,6 +65,19 @@ async function loadPGliteModule(major) {
|
|
|
65
65
|
if (major === 16) return await import("pglite-legacy-02");
|
|
66
66
|
return import("@electric-sql/pglite");
|
|
67
67
|
}
|
|
68
|
+
/** The stock NodeFS class of the PGlite module that opens `major` dirs. */
|
|
69
|
+
async function loadNodeFsClass(major) {
|
|
70
|
+
if (major === 16) return (await import("pglite-legacy-02/nodefs")).NodeFS;
|
|
71
|
+
return (await import("@electric-sql/pglite/nodefs")).NodeFS;
|
|
72
|
+
}
|
|
73
|
+
/** Opens a converted dir over stock NodeFS so the conversion can check it. */
|
|
74
|
+
async function openForVerify(major, dataDir) {
|
|
75
|
+
if (!isSupportedMajor(major)) throw new Error(`Cannot verify PGlite data dir ${dataDir}: PG_VERSION=${major} is not supported (expected one of ${SUPPORTED_PG_MAJORS.join(", ")})`);
|
|
76
|
+
const [{ PGlite }, NodeFS] = await Promise.all([loadPGliteModule(major), loadNodeFsClass(major)]);
|
|
77
|
+
const pg = new PGlite({ fs: new NodeFS(dataDir) });
|
|
78
|
+
await pg.waitReady;
|
|
79
|
+
return pg;
|
|
80
|
+
}
|
|
68
81
|
async function loadPgDump(major) {
|
|
69
82
|
if (major === 16) return (await import("pglite-tools-legacy-02/pg_dump")).pgDump;
|
|
70
83
|
return (await import("@electric-sql/pglite-tools/pg_dump")).pgDump;
|
|
@@ -798,9 +811,11 @@ const fatalHandlers = /* @__PURE__ */ new WeakMap();
|
|
|
798
811
|
/**
|
|
799
812
|
* Sends an uncaught exception or unhandled rejection through the SIGTERM
|
|
800
813
|
* shutdown that `withSignalHandlers()` installs, so the reactor and read-model
|
|
801
|
-
* PGlite stores
|
|
802
|
-
* Node's default is to exit at once, which
|
|
803
|
-
*
|
|
814
|
+
* PGlite stores close with a shutdown checkpoint before the process exits
|
|
815
|
+
* with code 1. Node's default is to exit at once, which leaves both stores
|
|
816
|
+
* to WAL recovery on the next open. A store whose wasm runtime aborted
|
|
817
|
+
* (ENOSPC) rejects its close; the builder logs that and still exits. A
|
|
818
|
+
* shutdown that hangs is cut off after FORCED_EXIT_MS.
|
|
804
819
|
*
|
|
805
820
|
* A rejection is fatal only when this is its sole listener: Node exits on an
|
|
806
821
|
* unhandled rejection only when nothing listens for it, and Sentry's
|
|
@@ -1048,6 +1063,20 @@ const BASE_MODEL_SPECIFIERS = [
|
|
|
1048
1063
|
"@powerhousedao/reactor-drive",
|
|
1049
1064
|
"@powerhousedao/reactor-group/document-models"
|
|
1050
1065
|
];
|
|
1066
|
+
function isModelModule(value) {
|
|
1067
|
+
const candidate = value;
|
|
1068
|
+
return typeof candidate?.reducer === "function" && typeof candidate.documentModel?.global?.id === "string";
|
|
1069
|
+
}
|
|
1070
|
+
async function keepImportableSources(sources, logger) {
|
|
1071
|
+
const kept = [];
|
|
1072
|
+
for (const source of sources) try {
|
|
1073
|
+
const exports = await import(pathToFileURL(source.filePath).href);
|
|
1074
|
+
if (Object.values(exports).some(isModelModule)) kept.push(source);
|
|
1075
|
+
} catch (error) {
|
|
1076
|
+
logger.warn(`Worker model sources: "${source.filePath}" does not import, so workflow pieces will not see its models: ${String(error)}`);
|
|
1077
|
+
}
|
|
1078
|
+
return kept;
|
|
1079
|
+
}
|
|
1051
1080
|
/**
|
|
1052
1081
|
* Resolves base models and configured package identifiers to `{ filePath }`
|
|
1053
1082
|
* sources for the reactor builder. Resolution to absolute paths happens here
|
|
@@ -1362,6 +1391,7 @@ function toStepRecord(row) {
|
|
|
1362
1391
|
output: parseJson(row.output),
|
|
1363
1392
|
port: row.port,
|
|
1364
1393
|
error: row.error,
|
|
1394
|
+
errorName: row.error_name,
|
|
1365
1395
|
startedAt: row.started_at,
|
|
1366
1396
|
endedAt: row.ended_at,
|
|
1367
1397
|
pieceVersion: row.piece_version,
|
|
@@ -1380,6 +1410,7 @@ function toRunRecord(row, steps) {
|
|
|
1380
1410
|
triggerPayload: parseJson(row.trigger_payload),
|
|
1381
1411
|
status: row.status,
|
|
1382
1412
|
error: row.error,
|
|
1413
|
+
errorName: row.error_name,
|
|
1383
1414
|
startedAt: row.started_at,
|
|
1384
1415
|
endedAt: row.ended_at,
|
|
1385
1416
|
rerunOf: row.rerun_of,
|
|
@@ -1388,6 +1419,14 @@ function toRunRecord(row, steps) {
|
|
|
1388
1419
|
steps: steps.map(toStepRecord)
|
|
1389
1420
|
};
|
|
1390
1421
|
}
|
|
1422
|
+
const UNKNOWN_ACCESS = {
|
|
1423
|
+
authEnforcement: true,
|
|
1424
|
+
authConditions: false,
|
|
1425
|
+
identity: null
|
|
1426
|
+
};
|
|
1427
|
+
function signedIn(ctx) {
|
|
1428
|
+
return Boolean(ctx.user?.address);
|
|
1429
|
+
}
|
|
1391
1430
|
function isAbsolute(url) {
|
|
1392
1431
|
return /^https?:\/\//i.test(url);
|
|
1393
1432
|
}
|
|
@@ -1416,13 +1455,21 @@ function returnUrlFor(requested, ctx) {
|
|
|
1416
1455
|
if (!url || !origin || url.origin !== origin) throw new GraphQLError("returnUrl must be on the requesting page's origin");
|
|
1417
1456
|
return url.href;
|
|
1418
1457
|
}
|
|
1419
|
-
const getResolvers = (runtime, authorizationService, oauth) => {
|
|
1458
|
+
const getResolvers = (runtime, authorizationService, oauth, access = UNKNOWN_ACCESS, assertCanRead) => {
|
|
1420
1459
|
return {
|
|
1421
1460
|
Query: { workflowRuntime: () => ({}) },
|
|
1422
1461
|
WorkflowRuntimeQueries: {
|
|
1423
1462
|
health: () => "ok",
|
|
1463
|
+
authEnforcement: () => access.authEnforcement,
|
|
1464
|
+
reactorIdentity: (_parent, _args, ctx) => signedIn(ctx) ? access.identity : null,
|
|
1465
|
+
authConditions: (_parent, _args, ctx) => signedIn(ctx) ? access.authConditions : null,
|
|
1466
|
+
reactorAccessDenial: async (_parent, args, ctx) => {
|
|
1467
|
+
if (!assertCanRead) throw new GraphQLError("No read check configured");
|
|
1468
|
+
await assertCanRead(args.workflowId, ctx);
|
|
1469
|
+
return runtime.reactorAccessDenial(args.workflowId) ?? null;
|
|
1470
|
+
},
|
|
1424
1471
|
blockDescriptor: (_parent, args) => runtime.blockDescriptor(blockRef(args.block)),
|
|
1425
|
-
blockOptions: (_parent, args, ctx) => runtime.blockOptions(blockRef(args.block), args.propName, args.input, args.connectionId ?? void 0, ctx, args.searchValue ?? void 0),
|
|
1472
|
+
blockOptions: (_parent, args, ctx) => runtime.blockOptions(blockRef(args.block), args.propName, args.input, args.connectionId ?? void 0, ctx, args.searchValue ?? void 0, args.reactorConnectionId ?? void 0),
|
|
1426
1473
|
pieceCatalog: () => runtime.pieceCatalog(),
|
|
1427
1474
|
pieceActions: (_parent, args) => runtime.pieceActions(args.packageName, args.version ?? void 0),
|
|
1428
1475
|
pieceTriggers: (_parent, args) => runtime.pieceTriggers(args.packageName, args.version ?? void 0),
|
|
@@ -1533,6 +1580,28 @@ const schema = gql`
|
|
|
1533
1580
|
type WorkflowRuntimeQueries {
|
|
1534
1581
|
health: String!
|
|
1535
1582
|
"""
|
|
1583
|
+
Whether this reactor enforces document auth (REACTOR_AUTH_ENFORCEMENT).
|
|
1584
|
+
On, a workflow needs a signed publish, and the host grants, to reach
|
|
1585
|
+
documents. Answered to any caller.
|
|
1586
|
+
"""
|
|
1587
|
+
authEnforcement: Boolean!
|
|
1588
|
+
"""
|
|
1589
|
+
Who this host signs as: grant it on each document a step writes. Null for
|
|
1590
|
+
a caller who is not signed in, or a host with no signer.
|
|
1591
|
+
"""
|
|
1592
|
+
reactorIdentity: ReactorIdentity
|
|
1593
|
+
"""
|
|
1594
|
+
REACTOR_AUTH_CONDITIONS. True: grant the host by key, with
|
|
1595
|
+
{ match: subject.key == key }; false: by address. Null for a caller who
|
|
1596
|
+
is not signed in.
|
|
1597
|
+
"""
|
|
1598
|
+
authConditions: Boolean
|
|
1599
|
+
"""
|
|
1600
|
+
Why the workflow's last publish or enable got no reactor access, naming
|
|
1601
|
+
the publisher and the connection; null when the check passed.
|
|
1602
|
+
"""
|
|
1603
|
+
reactorAccessDenial(workflowId: String!): String
|
|
1604
|
+
"""
|
|
1536
1605
|
Persisted runs, newest first. Scope them to one workflow, or to every
|
|
1537
1606
|
workflow a drive holds; workflowId wins when both are given.
|
|
1538
1607
|
"""
|
|
@@ -1573,6 +1642,8 @@ const schema = gql`
|
|
|
1573
1642
|
connectionId: String
|
|
1574
1643
|
"What the author typed, for a DROPDOWN declared with refreshOnSearch."
|
|
1575
1644
|
searchValue: String
|
|
1645
|
+
"The step's reactor connection; its filter narrows what the resolver reads."
|
|
1646
|
+
reactorConnectionId: String
|
|
1576
1647
|
): Unknown
|
|
1577
1648
|
"""
|
|
1578
1649
|
Every piece this reactor offers: the core piece, its package pieces, and
|
|
@@ -1750,6 +1821,13 @@ const schema = gql`
|
|
|
1750
1821
|
accountLabel: String
|
|
1751
1822
|
}
|
|
1752
1823
|
|
|
1824
|
+
type ReactorIdentity {
|
|
1825
|
+
"The address the host signs as; null without a signed-in identity."
|
|
1826
|
+
address: String
|
|
1827
|
+
"The did:key that signs the host's operations."
|
|
1828
|
+
key: String!
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1753
1831
|
type ConnectionCheckResult {
|
|
1754
1832
|
ok: Boolean!
|
|
1755
1833
|
detail: String
|
|
@@ -1811,6 +1889,8 @@ const schema = gql`
|
|
|
1811
1889
|
output: Unknown
|
|
1812
1890
|
port: String
|
|
1813
1891
|
error: String
|
|
1892
|
+
"The thrown error's name, e.g. ReactorAccessDeniedError."
|
|
1893
|
+
errorName: String
|
|
1814
1894
|
startedAt: String
|
|
1815
1895
|
endedAt: String
|
|
1816
1896
|
"The piece version that ran; null for an unresolved block."
|
|
@@ -1832,6 +1912,7 @@ const schema = gql`
|
|
|
1832
1912
|
triggerPayload: Unknown
|
|
1833
1913
|
status: String!
|
|
1834
1914
|
error: String
|
|
1915
|
+
errorName: String
|
|
1835
1916
|
"When the run began executing; a PENDING run's is when it was journaled."
|
|
1836
1917
|
startedAt: String!
|
|
1837
1918
|
endedAt: String
|
|
@@ -1869,12 +1950,14 @@ const schema = gql`
|
|
|
1869
1950
|
output: Unknown
|
|
1870
1951
|
port: String
|
|
1871
1952
|
error: String
|
|
1953
|
+
errorName: String
|
|
1872
1954
|
}
|
|
1873
1955
|
|
|
1874
1956
|
type WorkflowRunPayload {
|
|
1875
1957
|
runId: String
|
|
1876
1958
|
status: String!
|
|
1877
1959
|
error: String
|
|
1960
|
+
errorName: String
|
|
1878
1961
|
steps: [WorkflowStepRun!]!
|
|
1879
1962
|
}
|
|
1880
1963
|
|
|
@@ -1885,6 +1968,7 @@ const schema = gql`
|
|
|
1885
1968
|
status: String!
|
|
1886
1969
|
output: Unknown
|
|
1887
1970
|
error: String
|
|
1971
|
+
errorName: String
|
|
1888
1972
|
durationMs: Int!
|
|
1889
1973
|
}
|
|
1890
1974
|
|
|
@@ -1969,11 +2053,11 @@ const schema = gql`
|
|
|
1969
2053
|
//#region src/workflow/subgraph.ts
|
|
1970
2054
|
/** The runtime's read/write surface. The runtime itself is composed by the
|
|
1971
2055
|
* host, so the subgraph only serves what it is handed. */
|
|
1972
|
-
function createWorkflowRuntimeSubgraph(runtime, oauth) {
|
|
2056
|
+
function createWorkflowRuntimeSubgraph(runtime, oauth, access) {
|
|
1973
2057
|
return class WorkflowRuntimeSubgraph extends BaseSubgraph {
|
|
1974
2058
|
name = "workflow-runtime";
|
|
1975
2059
|
typeDefs = schema;
|
|
1976
|
-
resolvers = getResolvers(runtime, this.authorizationService, oauth);
|
|
2060
|
+
resolvers = getResolvers(runtime, this.authorizationService, oauth, access, (documentId, ctx) => this.assertCanRead(documentId, ctx));
|
|
1977
2061
|
additionalContextFields = {};
|
|
1978
2062
|
};
|
|
1979
2063
|
}
|
|
@@ -1999,6 +2083,12 @@ async function assertWorkflowPackageLoadable(load = () => import("@powerhousedao
|
|
|
1999
2083
|
throw new Error(`Workflows are enabled but ${WORKFLOW_PACKAGE_NAME} could not be loaded`, { cause: error });
|
|
2000
2084
|
}
|
|
2001
2085
|
}
|
|
2086
|
+
function modelManifestSource(builder) {
|
|
2087
|
+
return {
|
|
2088
|
+
modelManifest: () => builder.getResolvedModelManifest() ?? [],
|
|
2089
|
+
modelEntries: (documentType) => builder.getImportableEntries(documentType)
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2002
2092
|
function readAssertion(authorizationService, reactorClient) {
|
|
2003
2093
|
const resolveCanonical = createCanonicalDocumentIdResolver(reactorClient);
|
|
2004
2094
|
const hostCanRead = async (identifier, ctx) => {
|
|
@@ -2040,6 +2130,37 @@ function writeAssertion(authorizationService, reactorClient) {
|
|
|
2040
2130
|
if (!await authorizationService.canWrite(documentId, ctx.user?.address)) throw new ForbiddenError("to write this document");
|
|
2041
2131
|
};
|
|
2042
2132
|
}
|
|
2133
|
+
/** What the reactor enforces and who it signs as; unknown counts as enforced. */
|
|
2134
|
+
function reactorAccessOf(clientModule) {
|
|
2135
|
+
const flags = clientModule?.reactorModule?.featureFlags;
|
|
2136
|
+
const signer = clientModule?.signer;
|
|
2137
|
+
const key = signer?.app?.key;
|
|
2138
|
+
const identity = key ? {
|
|
2139
|
+
address: signer.user?.address ?? null,
|
|
2140
|
+
key
|
|
2141
|
+
} : null;
|
|
2142
|
+
return {
|
|
2143
|
+
authEnforcement: flags ? flags.authEnforcement : true,
|
|
2144
|
+
authConditions: flags?.authConditions === true,
|
|
2145
|
+
identity
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
/** Whom a run's created documents also grant: the host, as §8 grants it. */
|
|
2149
|
+
function hostPrincipalOf(access) {
|
|
2150
|
+
const identity = access.identity;
|
|
2151
|
+
if (!identity) return void 0;
|
|
2152
|
+
if (access.authConditions) return { match: { eq: [{ attr: "subject.key" }, { lit: identity.key }] } };
|
|
2153
|
+
return identity.address ? { address: identity.address } : void 0;
|
|
2154
|
+
}
|
|
2155
|
+
/** Who the host signs as, so a publish it signs makes no run user. */
|
|
2156
|
+
function hostIdentityOf(access) {
|
|
2157
|
+
const identity = access.identity;
|
|
2158
|
+
if (!identity) return void 0;
|
|
2159
|
+
return identity.address ? {
|
|
2160
|
+
address: identity.address,
|
|
2161
|
+
key: identity.key
|
|
2162
|
+
} : { key: identity.key };
|
|
2163
|
+
}
|
|
2043
2164
|
/** A step reads any well-formed attachment, as it reads any document: a run
|
|
2044
2165
|
* carries no caller to check against. */
|
|
2045
2166
|
function canReadAttachmentRef(_documentId, ref) {
|
|
@@ -2082,6 +2203,8 @@ async function composeWorkflowRuntime(deps) {
|
|
|
2082
2203
|
}
|
|
2083
2204
|
engine.setPieceRegistryUrl(deps.pieceRegistryUrl);
|
|
2084
2205
|
if (deps.pieces) bindPackagePieces(engine.packagePieces, deps.pieces);
|
|
2206
|
+
const access = reactorAccessOf(deps.clientModule);
|
|
2207
|
+
const models = deps.models;
|
|
2085
2208
|
const runtime = engine.createWorkflowRuntime({
|
|
2086
2209
|
relationalDb: deps.relationalDb,
|
|
2087
2210
|
secretsKeyFile: deps.secretsKeyFile,
|
|
@@ -2089,6 +2212,13 @@ async function composeWorkflowRuntime(deps) {
|
|
|
2089
2212
|
assertCanRead: readAssertion(deps.authorizationService, deps.reactorClient),
|
|
2090
2213
|
subjectOf: (caller) => callerSubject(caller.user),
|
|
2091
2214
|
assertCanWrite: writeAssertion(deps.authorizationService, deps.reactorClient),
|
|
2215
|
+
...deps.clientModule?.reactorModule ? { authEnforcement: access.authEnforcement } : {},
|
|
2216
|
+
hostPrincipal: hostPrincipalOf(access),
|
|
2217
|
+
hostIdentity: hostIdentityOf(access),
|
|
2218
|
+
...models ? {
|
|
2219
|
+
modelManifest: () => models.modelManifest(),
|
|
2220
|
+
modelEntries: (documentType) => models.modelEntries(documentType)
|
|
2221
|
+
} : {},
|
|
2092
2222
|
webhooks: deps.webhooks,
|
|
2093
2223
|
attachments: deps.attachments,
|
|
2094
2224
|
canReadAttachmentRef,
|
|
@@ -2100,7 +2230,7 @@ async function composeWorkflowRuntime(deps) {
|
|
|
2100
2230
|
let stopped = false;
|
|
2101
2231
|
const oauthCallback = deps.http ? registerOAuthCallback(deps.http, runtime) : void 0;
|
|
2102
2232
|
return {
|
|
2103
|
-
subgraph: createWorkflowRuntimeSubgraph(runtime, deps.http ? { callbackUrl: callbackUrlOf(deps.http) } : void 0),
|
|
2233
|
+
subgraph: createWorkflowRuntimeSubgraph(runtime, deps.http ? { callbackUrl: callbackUrlOf(deps.http) } : void 0, access),
|
|
2104
2234
|
triggers,
|
|
2105
2235
|
async start() {
|
|
2106
2236
|
await runtime.registerWebhookEndpoint();
|
|
@@ -2129,7 +2259,12 @@ var ClosablePGliteDialect = class extends PGliteDialect {
|
|
|
2129
2259
|
const innerDestroy = driver.destroy.bind(driver);
|
|
2130
2260
|
driver.destroy = async () => {
|
|
2131
2261
|
await innerDestroy();
|
|
2132
|
-
if (
|
|
2262
|
+
if (pglite.closed) return;
|
|
2263
|
+
try {
|
|
2264
|
+
await pglite.close();
|
|
2265
|
+
} catch (err) {
|
|
2266
|
+
if (!/PGlite aborted/.test(String(err))) throw err;
|
|
2267
|
+
}
|
|
2133
2268
|
};
|
|
2134
2269
|
return driver;
|
|
2135
2270
|
}
|
|
@@ -2184,7 +2319,15 @@ async function migratePgliteDir(dataDir, logger) {
|
|
|
2184
2319
|
try {
|
|
2185
2320
|
const [legacyMod, pgDump] = await Promise.all([loadPGliteModule(major), loadPgDump(major)]);
|
|
2186
2321
|
const LegacyPGlite = legacyMod.PGlite;
|
|
2187
|
-
const
|
|
2322
|
+
const onDisk = new LegacyPGlite(backupDir);
|
|
2323
|
+
let tar;
|
|
2324
|
+
try {
|
|
2325
|
+
await onDisk.waitReady;
|
|
2326
|
+
tar = await onDisk.dumpDataDir("none");
|
|
2327
|
+
} finally {
|
|
2328
|
+
await onDisk.close();
|
|
2329
|
+
}
|
|
2330
|
+
const pg = new LegacyPGlite({ loadDataDir: tar });
|
|
2188
2331
|
try {
|
|
2189
2332
|
await pg.waitReady;
|
|
2190
2333
|
sql = await (await pgDump({ pg })).text();
|
|
@@ -2214,6 +2357,7 @@ async function migratePgliteDir(dataDir, logger) {
|
|
|
2214
2357
|
} finally {
|
|
2215
2358
|
await pg.close();
|
|
2216
2359
|
}
|
|
2360
|
+
syncTree(dataDir);
|
|
2217
2361
|
} catch (err) {
|
|
2218
2362
|
await rollback(dataDir, backupDir, err, logger);
|
|
2219
2363
|
throw err;
|
|
@@ -2234,6 +2378,59 @@ async function rollback(dataDir, backupDir, originalError, logger) {
|
|
|
2234
2378
|
logger.error(`[pglite-migration] Migration failed for ${dataDir}; rolled back from ${backupDir}. Original error: ${String(originalError)}`);
|
|
2235
2379
|
}
|
|
2236
2380
|
//#endregion
|
|
2381
|
+
//#region src/pglite-preflight.ts
|
|
2382
|
+
const RM_OPTIONS = {
|
|
2383
|
+
recursive: true,
|
|
2384
|
+
force: true,
|
|
2385
|
+
maxRetries: 10,
|
|
2386
|
+
retryDelay: 100
|
|
2387
|
+
};
|
|
2388
|
+
/**
|
|
2389
|
+
* Prepares every local PGlite dir before the first open and returns the
|
|
2390
|
+
* PG major detected on disk per dir. Conversion of an AtomicNodeFs snapshot
|
|
2391
|
+
* runs for every dir, before the major is read, so a pure-snapshot PG16 dir
|
|
2392
|
+
* is converted and then migrated.
|
|
2393
|
+
*/
|
|
2394
|
+
async function runPglitePreflight(options) {
|
|
2395
|
+
const { dirs, logger } = options;
|
|
2396
|
+
const detectedMajors = /* @__PURE__ */ new Map();
|
|
2397
|
+
if (options.inMemory || dirs.length === 0) return detectedMajors;
|
|
2398
|
+
if (options.forcePgVersion !== void 0) {
|
|
2399
|
+
if (options.migratePglite) logger.warn("PH_FORCE_PG_VERSION is set; ignoring --migrate-pglite/PH_MIGRATE_PGLITE because the data dirs will be wiped.");
|
|
2400
|
+
logger.warn(`PH_FORCE_PG_VERSION=${options.forcePgVersion} set; wiping PGLite data dirs and re-initializing at PG${options.forcePgVersion}.`);
|
|
2401
|
+
for (const dir of dirs) {
|
|
2402
|
+
for (const p of [
|
|
2403
|
+
dir,
|
|
2404
|
+
`${dir}.converting`,
|
|
2405
|
+
`${dir}.old`
|
|
2406
|
+
]) await promises.rm(p, RM_OPTIONS);
|
|
2407
|
+
logger.info(`Wiped PGLite data dir ${dir}`);
|
|
2408
|
+
}
|
|
2409
|
+
return detectedMajors;
|
|
2410
|
+
}
|
|
2411
|
+
const deps = {
|
|
2412
|
+
openForVerify: options.openForVerify ?? openForVerify,
|
|
2413
|
+
logger
|
|
2414
|
+
};
|
|
2415
|
+
for (const dir of dirs) {
|
|
2416
|
+
await preparePgliteDataDir(dir, deps);
|
|
2417
|
+
const major = await readPgVersionFile(dir);
|
|
2418
|
+
if (major !== null) detectedMajors.set(dir, major);
|
|
2419
|
+
}
|
|
2420
|
+
if (options.migratePglite) for (const [dir, major] of detectedMajors) {
|
|
2421
|
+
if (major === 17) continue;
|
|
2422
|
+
await migratePgliteDir(dir, logger);
|
|
2423
|
+
await removeStalePgliteFiles(dir, logger);
|
|
2424
|
+
const after = await readPgVersionFile(dir);
|
|
2425
|
+
if (after !== null) detectedMajors.set(dir, after);
|
|
2426
|
+
}
|
|
2427
|
+
else for (const [dir, major] of detectedMajors) {
|
|
2428
|
+
if (major === 17) continue;
|
|
2429
|
+
logger.warn(`PGLite data dir at ${dir} was created with PG${major} but Switchboard ships PG17. Running on legacy PGLite. Re-start with --migrate-pglite (or PH_MIGRATE_PGLITE=true) to upgrade.`);
|
|
2430
|
+
}
|
|
2431
|
+
return detectedMajors;
|
|
2432
|
+
}
|
|
2433
|
+
//#endregion
|
|
2237
2434
|
//#region src/reactor-feature-flags.mts
|
|
2238
2435
|
/**
|
|
2239
2436
|
* Enforcement flags from the REACTOR_* env vars. Each flag requires the ones
|
|
@@ -2422,14 +2619,29 @@ const DOCUMENT_MODEL_SUBGRAPHS_ENABLED = "DOCUMENT_MODEL_SUBGRAPHS_ENABLED";
|
|
|
2422
2619
|
const DOCUMENT_MODEL_SUBGRAPHS_ENABLED_DEFAULT = true;
|
|
2423
2620
|
const DEFAULT_PORT = process.env.PORT ? Number(process.env.PORT) : 4001;
|
|
2424
2621
|
const PORT_FALLBACK_ATTEMPTS = 20;
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
if (
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2622
|
+
let flushIntervalWarned = false;
|
|
2623
|
+
function warnIgnoredFlushInterval(logger) {
|
|
2624
|
+
if (flushIntervalWarned) return;
|
|
2625
|
+
if (process.env.PGLITE_FLUSH_INTERVAL_MS === void 0) return;
|
|
2626
|
+
flushIntervalWarned = true;
|
|
2627
|
+
logger.warn("PGLITE_FLUSH_INTERVAL_MS is set but ignored: PGlite stores no longer snapshot on a timer.");
|
|
2628
|
+
}
|
|
2629
|
+
function onPgliteAbort(logger, what) {
|
|
2630
|
+
if (!triggerFatalShutdown("PGlite aborted", what)) logger.error("PGlite aborted: @error", what);
|
|
2631
|
+
}
|
|
2632
|
+
function pgliteFsyncFor(major, dir, logger) {
|
|
2633
|
+
const wanted = resolvePgliteFsync(process.env);
|
|
2634
|
+
if (wanted && major === 16) logger.warn(`PGlite data dir ${dir} is PG16 on pglite-legacy-02, which does not sync commits to disk; only checkpoints are synced until --migrate-pglite upgrades it.`);
|
|
2635
|
+
return wanted;
|
|
2636
|
+
}
|
|
2637
|
+
/** Preloads the module's NodeFS so the returned factory can be synchronous. */
|
|
2638
|
+
async function loadDurableFsFactory(major, logger) {
|
|
2639
|
+
const NodeFS = await loadNodeFsClass(major);
|
|
2640
|
+
return (dir) => createDurableNodeFs(NodeFS, dir, {
|
|
2641
|
+
fsync: pgliteFsyncFor(major, dir, logger),
|
|
2642
|
+
logger,
|
|
2643
|
+
onAbort: (what) => onPgliteAbort(logger, what)
|
|
2644
|
+
});
|
|
2433
2645
|
}
|
|
2434
2646
|
const PGLITE_IN_MEMORY = process.env.PH_PGLITE_IN_MEMORY === "1";
|
|
2435
2647
|
/**
|
|
@@ -2476,7 +2688,7 @@ async function resolveServerPort(requested, strictPort, logger) {
|
|
|
2476
2688
|
return requested;
|
|
2477
2689
|
}
|
|
2478
2690
|
async function createReactorKysely(opts) {
|
|
2479
|
-
const { reactorDbUrl, reactorPgliteDir, reactorPgliteMajor, inMemory,
|
|
2691
|
+
const { reactorDbUrl, reactorPgliteDir, reactorPgliteMajor, inMemory, hostPoolSize, logger } = opts;
|
|
2480
2692
|
if (reactorDbUrl && isPostgresUrl(reactorDbUrl)) {
|
|
2481
2693
|
const connectionString = reactorDbUrl.includes("?") ? reactorDbUrl : `${reactorDbUrl}?sslmode=disable`;
|
|
2482
2694
|
const poolSize = hostPoolSize();
|
|
@@ -2493,11 +2705,7 @@ async function createReactorKysely(opts) {
|
|
|
2493
2705
|
}
|
|
2494
2706
|
if (!reactorPgliteDir || reactorPgliteMajor === null) throw new Error("Reactor PGLite directory not resolved");
|
|
2495
2707
|
const { PGlite } = await loadPGliteModule(reactorPgliteMajor);
|
|
2496
|
-
const pglite = inMemory ? new PGlite() : new PGlite({ fs:
|
|
2497
|
-
logger,
|
|
2498
|
-
flushIntervalMs,
|
|
2499
|
-
onFlushError: onPgliteFlushError
|
|
2500
|
-
}) });
|
|
2708
|
+
const pglite = inMemory ? new PGlite() : new PGlite({ fs: (await loadDurableFsFactory(reactorPgliteMajor, logger))(reactorPgliteDir) });
|
|
2501
2709
|
logger.info(inMemory ? `Using in-memory PGlite (PG${reactorPgliteMajor}) for reactor storage [PH_PGLITE_IN_MEMORY=1]` : `Using PGlite (PG${reactorPgliteMajor}) for reactor storage at ${reactorPgliteDir}`);
|
|
2502
2710
|
return {
|
|
2503
2711
|
kysely: new Kysely({ dialect: new ClosablePGliteDialect(pglite) }),
|
|
@@ -2525,42 +2733,14 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2525
2733
|
const reactorPgliteDir = options.reactor ? null : !reactorDbUrl || !isPostgresUrl(reactorDbUrl) ? reactorPath : null;
|
|
2526
2734
|
const readModelPgliteDir = !dbPath || !isPostgresUrl(dbPath) ? readModelPath : null;
|
|
2527
2735
|
const pgliteDirs = [reactorPgliteDir, readModelPgliteDir].filter((d) => d !== null);
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
if (options.forcePgVersion !== void 0 && pgliteDirs.length > 0) {
|
|
2539
|
-
if (options.migratePglite) logger.warn("PH_FORCE_PG_VERSION is set; ignoring --migrate-pglite/PH_MIGRATE_PGLITE because the data dirs will be wiped.");
|
|
2540
|
-
logger.warn(`PH_FORCE_PG_VERSION=${options.forcePgVersion} set; wiping PGLite data dirs and re-initializing at PG${options.forcePgVersion}.`);
|
|
2541
|
-
for (const dir of pgliteDirs) {
|
|
2542
|
-
await promises.rm(dir, {
|
|
2543
|
-
recursive: true,
|
|
2544
|
-
force: true
|
|
2545
|
-
});
|
|
2546
|
-
logger.info(`Wiped PGLite data dir ${dir}`);
|
|
2547
|
-
}
|
|
2548
|
-
} else if (options.forcePgVersion === void 0) {
|
|
2549
|
-
for (const dir of pgliteDirs) {
|
|
2550
|
-
const major = await readPgVersionFile(dir);
|
|
2551
|
-
if (major !== null) detectedMajors.set(dir, major);
|
|
2552
|
-
}
|
|
2553
|
-
if (options.migratePglite) for (const [dir, major] of detectedMajors) {
|
|
2554
|
-
if (major === 17) continue;
|
|
2555
|
-
await migratePgliteDir(dir, logger);
|
|
2556
|
-
const after = await readPgVersionFile(dir);
|
|
2557
|
-
if (after !== null) detectedMajors.set(dir, after);
|
|
2558
|
-
}
|
|
2559
|
-
else for (const [dir, major] of detectedMajors) {
|
|
2560
|
-
if (major === 17) continue;
|
|
2561
|
-
logger.warn(`PGLite data dir at ${dir} was created with PG${major} but Switchboard ships PG17. Running on legacy PGLite. Re-start with --migrate-pglite (or PH_MIGRATE_PGLITE=true) to upgrade.`);
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2736
|
+
warnIgnoredFlushInterval(logger);
|
|
2737
|
+
const detectedMajors = await runPglitePreflight({
|
|
2738
|
+
dirs: pgliteDirs,
|
|
2739
|
+
forcePgVersion: options.forcePgVersion,
|
|
2740
|
+
migratePglite: options.migratePglite,
|
|
2741
|
+
inMemory: PGLITE_IN_MEMORY,
|
|
2742
|
+
logger
|
|
2743
|
+
});
|
|
2564
2744
|
function resolvePgliteMajorForDir(dir) {
|
|
2565
2745
|
if (options.forcePgVersion !== void 0) return options.forcePgVersion;
|
|
2566
2746
|
const detected = detectedMajors.get(dir);
|
|
@@ -2627,6 +2807,7 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2627
2807
|
if (!packages.includes("@powerhousedao/workflow")) packages.push(WORKFLOW_PACKAGE_NAME);
|
|
2628
2808
|
}
|
|
2629
2809
|
let ownedReactorModule;
|
|
2810
|
+
let workerModels;
|
|
2630
2811
|
let localRenownRequest;
|
|
2631
2812
|
const initializeClient = async (documentModels, { attachmentReferenceWriter, upgradeManifests }) => {
|
|
2632
2813
|
if (options.reactor) {
|
|
@@ -2645,7 +2826,6 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2645
2826
|
reactorPgliteDir,
|
|
2646
2827
|
reactorPgliteMajor,
|
|
2647
2828
|
inMemory: PGLITE_IN_MEMORY,
|
|
2648
|
-
flushIntervalMs: PGLITE_FLUSH_INTERVAL_MS,
|
|
2649
2829
|
hostPoolSize: () => resolveHostPoolSize(process.env),
|
|
2650
2830
|
logger
|
|
2651
2831
|
});
|
|
@@ -2681,10 +2861,14 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2681
2861
|
switchboardUrl: renownConfig.switchboardUrl
|
|
2682
2862
|
}, renown) : void 0
|
|
2683
2863
|
});
|
|
2864
|
+
if (workerPool || projectionWorker || workflowsEnabled) {
|
|
2865
|
+
const sources = await resolveWorkerModelSources(packages, reactorLogger, { resolveRemote: resolveRegistryModels });
|
|
2866
|
+
reactorBuilder.withDocumentModelSources(workerPool || projectionWorker ? sources : await keepImportableSources(sources, reactorLogger));
|
|
2867
|
+
}
|
|
2868
|
+
workerModels = modelManifestSource(reactorBuilder);
|
|
2684
2869
|
if (workerPool) {
|
|
2685
2870
|
if (!reactorDbUrl) throw new Error("unreachable: worker pool enabled without a reactor database URL");
|
|
2686
|
-
|
|
2687
|
-
reactorBuilder.withDocumentModelSources(workerSources).withWorkerPool({
|
|
2871
|
+
reactorBuilder.withWorkerPool({
|
|
2688
2872
|
numWorkers: workerPool.numWorkers,
|
|
2689
2873
|
db: buildWorkerDbConfig(reactorDbUrl, workerPool)
|
|
2690
2874
|
});
|
|
@@ -2701,10 +2885,6 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2701
2885
|
});
|
|
2702
2886
|
if (projectionWorker) {
|
|
2703
2887
|
if (!reactorDbUrl) throw new Error("unreachable: projection worker enabled without a reactor database URL");
|
|
2704
|
-
if (!workerPool) {
|
|
2705
|
-
const workerSources = await resolveWorkerModelSources(packages, reactorLogger, { resolveRemote: resolveRegistryModels });
|
|
2706
|
-
reactorBuilder.withDocumentModelSources(workerSources);
|
|
2707
|
-
}
|
|
2708
2888
|
const db = {
|
|
2709
2889
|
...buildWorkerDbConfig(reactorDbUrl, {
|
|
2710
2890
|
dbPoolSizePerWorker: projectionWorker.dbPoolSize,
|
|
@@ -2798,12 +2978,9 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2798
2978
|
let pgliteFactory;
|
|
2799
2979
|
if (readModelPgliteDir && readModelPgliteMajor !== null) {
|
|
2800
2980
|
const { PGlite: ReadModelPGlite } = await loadPGliteModule(readModelPgliteMajor);
|
|
2981
|
+
const durableFs = await loadDurableFsFactory(readModelPgliteMajor, logger);
|
|
2801
2982
|
pgliteFactory = PGLITE_IN_MEMORY ? () => new ReadModelPGlite({ parsers: PGLITE_UTC_PARSERS }) : (connectionString) => new ReadModelPGlite({
|
|
2802
|
-
fs:
|
|
2803
|
-
logger,
|
|
2804
|
-
flushIntervalMs: PGLITE_FLUSH_INTERVAL_MS,
|
|
2805
|
-
onFlushError: onPgliteFlushError
|
|
2806
|
-
}),
|
|
2983
|
+
fs: durableFs(connectionString ?? readModelPgliteDir),
|
|
2807
2984
|
parsers: PGLITE_UTC_PARSERS
|
|
2808
2985
|
});
|
|
2809
2986
|
}
|
|
@@ -2852,6 +3029,7 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2852
3029
|
authorizationService: api.authorizationService,
|
|
2853
3030
|
pieces: api.packageManager,
|
|
2854
3031
|
pieceRegistryUrl: registryUrl,
|
|
3032
|
+
models: workerModels,
|
|
2855
3033
|
logger: logger.child(["workflow-runtime"])
|
|
2856
3034
|
});
|
|
2857
3035
|
const WorkflowRuntimeSubgraph = workflows.subgraph;
|
|
@@ -2989,6 +3167,7 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2989
3167
|
attachmentReferenceProjection: api.attachmentReferenceProjection,
|
|
2990
3168
|
workflowTriggers: workflows?.triggers,
|
|
2991
3169
|
workflowsEnabled,
|
|
3170
|
+
modelManifest: () => workerModels?.modelManifest() ?? [],
|
|
2992
3171
|
privacy: privacy ? { erasure: privacy.erasure } : void 0,
|
|
2993
3172
|
mcpEnabled: options.mcp !== false,
|
|
2994
3173
|
renown,
|
|
@@ -3061,5 +3240,5 @@ if (import.meta.main) await startSwitchboard({ fatalErrorShutdown: true });
|
|
|
3061
3240
|
//#endregion
|
|
3062
3241
|
export { resolveUnsupportedStoredDocuments as a, StoredDocumentsRefusedError as i, isPortAvailable as n, applySwitchboardReactorDefaults as o, startSwitchboard as r, parseForcePgVersion as s, deriveAttachmentServiceConfig as t };
|
|
3063
3242
|
|
|
3064
|
-
//# sourceMappingURL=server-
|
|
3065
|
-
//# debugId=
|
|
3243
|
+
//# sourceMappingURL=server-Dvw8_NeM.mjs.map
|
|
3244
|
+
//# debugId=b9c02424-1406-5d63-abbd-6c32caa53205
|