@volter/world-core 3.0.38 → 3.0.40
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/app-route.cjs +26 -25
- package/app-route.d.cts +1 -0
- package/dist/app-route.cjs +26 -25
- package/dist/app-route.d.cts +1 -0
- package/dist/generated/pack-facts.json +409 -352
- package/dist/inject.cjs +474 -95
- package/dist/network-policy.cjs +110 -13
- package/dist/network-policy.d.cts +8 -3
- package/dist/src/actions.js +2 -1
- package/dist/src/anthropic-wire.d.ts +2 -0
- package/dist/src/anthropic-wire.js +5 -0
- package/dist/src/attestation.d.ts +6 -0
- package/dist/src/attestation.js +27 -0
- package/dist/src/bytes.d.ts +3 -0
- package/dist/src/bytes.js +17 -0
- package/dist/src/changeset.js +2 -1
- package/dist/src/clickhouse/sql.js +73 -14
- package/dist/src/derived-core.d.ts +210 -32
- package/dist/src/derived-core.js +510 -244
- package/dist/src/derived-real.d.ts +1 -1
- package/dist/src/derived-real.js +704 -93
- package/dist/src/derived.d.ts +18 -2
- package/dist/src/derived.js +21 -2
- package/dist/src/events.d.ts +28 -45
- package/dist/src/events.js +40 -58
- package/dist/src/exact-json.d.ts +10 -0
- package/dist/src/exact-json.js +98 -0
- package/dist/src/executor.d.ts +17 -0
- package/dist/src/executor.js +91 -30
- package/dist/src/file-response.js +1 -1
- package/dist/src/form.d.ts +8 -0
- package/dist/src/form.js +76 -0
- package/dist/src/git/format.d.ts +16 -0
- package/dist/src/git/format.js +110 -0
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +1 -0
- package/dist/src/git/lfs.d.ts +1 -1
- package/dist/src/git/lfs.js +4 -3
- package/dist/src/graphql-wire.d.ts +6 -0
- package/dist/src/graphql-wire.js +6 -3
- package/dist/src/grpc-wire.js +2 -1
- package/dist/src/head.d.ts +1 -0
- package/dist/src/head.js +15 -4
- package/dist/src/held-reads.d.ts +6 -0
- package/dist/src/held-reads.js +60 -0
- package/dist/src/history.js +5 -4
- package/dist/src/host-port.d.ts +5 -0
- package/dist/src/host-port.js +14 -0
- package/dist/src/index.d.ts +11 -3
- package/dist/src/index.js +9 -4
- package/dist/src/log.js +12 -7
- package/dist/src/machines.d.ts +81 -5
- package/dist/src/machines.js +125 -9
- package/dist/src/managed-database.d.ts +15 -11
- package/dist/src/managed-database.js +38 -37
- package/dist/src/multipart.d.ts +21 -3
- package/dist/src/multipart.js +112 -38
- package/dist/src/observe.d.ts +3 -0
- package/dist/src/observe.js +6 -2
- package/dist/src/openai-wire.d.ts +16 -0
- package/dist/src/openai-wire.js +43 -12
- package/dist/src/pack-fetch.d.ts +4 -6
- package/dist/src/pack-fetch.js +189 -57
- package/dist/src/packRegistry.d.ts +7 -1
- package/dist/src/packRegistry.js +20 -3
- package/dist/src/private-transport.d.ts +1 -0
- package/dist/src/private-transport.js +11 -0
- package/dist/src/protobuf.js +10 -4
- package/dist/src/rateBudget.d.ts +8 -1
- package/dist/src/rateBudget.js +1 -0
- package/dist/src/redis/engine.d.ts +14 -5
- package/dist/src/redis/engine.js +271 -72
- package/dist/src/redis/frames.d.ts +17 -0
- package/dist/src/redis/frames.js +123 -0
- package/dist/src/redis/index.d.ts +6 -0
- package/dist/src/redis/index.js +6 -0
- package/dist/src/redis/json.d.ts +2 -0
- package/dist/src/redis/json.js +14 -0
- package/dist/src/redis/lua-libs.d.ts +4 -0
- package/dist/src/redis/lua-libs.js +173 -0
- package/dist/src/redis/lua.d.ts +1 -0
- package/dist/src/redis/lua.js +36 -0
- package/dist/src/redis/member-storage.d.ts +5 -0
- package/dist/src/redis/member-storage.js +75 -0
- package/dist/src/redis/resp2.d.ts +4 -0
- package/dist/src/redis/resp2.js +29 -0
- package/dist/src/redis/stream.d.ts +14 -0
- package/dist/src/redis/stream.js +335 -0
- package/dist/src/remote-execute.d.ts +2 -2
- package/dist/src/request-body.d.ts +11 -0
- package/dist/src/request-body.js +11 -0
- package/dist/src/runtime.d.ts +6 -5
- package/dist/src/runtime.js +5 -5
- package/dist/src/s3/wire.d.ts +4 -4
- package/dist/src/s3/wire.js +5 -3
- package/dist/src/scenario.d.ts +22 -0
- package/dist/src/scenario.js +35 -31
- package/dist/src/serve-http.d.ts +2 -1
- package/dist/src/serve-http.js +10 -1
- package/dist/src/serve.js +7 -4
- package/dist/src/signing.d.ts +23 -5
- package/dist/src/signing.js +74 -11
- package/dist/src/sigv4.d.ts +3 -1
- package/dist/src/sigv4.js +4 -2
- package/dist/src/smtp.js +2 -1
- package/dist/src/sockets.js +8 -3
- package/dist/src/state-system.d.ts +3 -0
- package/dist/src/storage.js +5 -2
- package/dist/src/twin-fetch.d.ts +3 -1
- package/dist/src/twin-fetch.js +3 -2
- package/dist/src/vendor-call.d.ts +10 -3
- package/dist/src/vendor-call.js +58 -14
- package/dist/test-fixtures/attestation.SOURCE.md +1 -0
- package/dist/test-fixtures/attestation.json +50 -0
- package/generated/pack-facts.json +409 -352
- package/inject.cjs +474 -95
- package/network-policy.cjs +110 -13
- package/network-policy.d.cts +8 -3
- package/package.json +11 -4
- package/src/actions.ts +2 -1
- package/src/anthropic-wire.ts +6 -0
- package/src/attestation.ts +23 -0
- package/src/bytes.ts +21 -0
- package/src/changeset.ts +2 -1
- package/src/clickhouse/sql.ts +50 -16
- package/src/derived-core.ts +573 -249
- package/src/derived-real.ts +552 -42
- package/src/derived.ts +23 -4
- package/src/events.ts +54 -85
- package/src/exact-json.ts +71 -0
- package/src/executor.ts +86 -27
- package/src/file-response.ts +1 -1
- package/src/form.ts +66 -0
- package/src/git/format.ts +89 -0
- package/src/git/index.ts +1 -0
- package/src/git/lfs.ts +5 -4
- package/src/graphql-wire.ts +8 -3
- package/src/grpc-wire.ts +2 -1
- package/src/head.ts +15 -5
- package/src/held-reads.ts +54 -0
- package/src/history.ts +5 -4
- package/src/host-port.ts +9 -0
- package/src/index.ts +10 -4
- package/src/log.ts +11 -7
- package/src/machines.ts +135 -13
- package/src/managed-database.ts +48 -40
- package/src/multipart.ts +110 -35
- package/src/observe.ts +6 -3
- package/src/openai-wire.ts +45 -14
- package/src/pack-fetch.ts +173 -50
- package/src/packRegistry.ts +27 -4
- package/src/private-transport.ts +8 -0
- package/src/protobuf.ts +8 -4
- package/src/rateBudget.ts +6 -1
- package/src/redis/engine.ts +168 -63
- package/src/redis/frames.ts +53 -0
- package/src/redis/index.ts +6 -0
- package/src/redis/json.ts +13 -0
- package/src/redis/lua-libs.ts +62 -0
- package/src/redis/lua.ts +20 -0
- package/src/redis/member-storage.ts +58 -0
- package/src/redis/resp2.ts +25 -0
- package/src/redis/stream.ts +102 -0
- package/src/remote-execute.ts +2 -2
- package/src/request-body.ts +12 -0
- package/src/runtime.ts +6 -5
- package/src/s3/wire.ts +8 -5
- package/src/scenario.ts +34 -13
- package/src/serve-http.ts +11 -2
- package/src/serve.ts +4 -3
- package/src/signing.ts +71 -17
- package/src/sigv4.ts +5 -3
- package/src/smtp.ts +3 -2
- package/src/sockets.ts +8 -3
- package/src/state-system.ts +3 -0
- package/src/storage.ts +5 -2
- package/src/twin-fetch.ts +4 -3
- package/src/vendor-call.ts +59 -12
- package/test-fixtures/attestation.json +50 -0
package/dist/src/history.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseExactJson } from "./exact-json.js";
|
|
1
2
|
// Immutable views over shared append-only segments. A position is meaningful within a view.
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -37,7 +38,7 @@ const viewPath = (directory, id) => {
|
|
|
37
38
|
return join(directory, 'views', `${id}.json`);
|
|
38
39
|
};
|
|
39
40
|
function rows(directory, file) {
|
|
40
|
-
return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(
|
|
41
|
+
return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(parseExactJson(line)));
|
|
41
42
|
}
|
|
42
43
|
function sliceLayout(layout, at) {
|
|
43
44
|
const base = layout.base ? historyLength(layout.base) : 0;
|
|
@@ -47,7 +48,7 @@ export function readHistoryView(directory, id) {
|
|
|
47
48
|
const raw = getActiveWorldStore().read(viewPath(directory, id));
|
|
48
49
|
if (raw === null)
|
|
49
50
|
throw new Error(`Missing retained history view ${id}`);
|
|
50
|
-
const { dependencies: _dependencies, ...view } =
|
|
51
|
+
const { dependencies: _dependencies, ...view } = parseExactJson(raw);
|
|
51
52
|
if (view.version !== 1 || historyDigest({ ...view, dependencies: _dependencies }) !== id)
|
|
52
53
|
throw new Error(`Invalid retained history view ${id}`);
|
|
53
54
|
historyLength(view.layout);
|
|
@@ -124,7 +125,7 @@ export function inheritedHistoryKey(service, root) {
|
|
|
124
125
|
}
|
|
125
126
|
if (raw === null)
|
|
126
127
|
return 'missing-view';
|
|
127
|
-
const view =
|
|
128
|
+
const view = parseExactJson(raw);
|
|
128
129
|
const directories = new Set([view.owner?.directory ?? '', ...(view.segments ?? []).map((sp) => sp.directory)]);
|
|
129
130
|
if (!isUrlParent(parent.at))
|
|
130
131
|
directories.add(worldPaths(service, parent.at).dir);
|
|
@@ -166,7 +167,7 @@ export function captureHistory(service, root) {
|
|
|
166
167
|
}
|
|
167
168
|
export function originHead(service, root) {
|
|
168
169
|
const raw = getActiveWorldStore().read(join(worldPaths(service, root).dir, 'origin-head.json'));
|
|
169
|
-
return raw === null ? null :
|
|
170
|
+
return raw === null ? null : parseExactJson(raw);
|
|
170
171
|
}
|
|
171
172
|
/** A complete remote view shares cached rows by content, never by mutable offsets or entry ID. */
|
|
172
173
|
export function publishOriginHistory(service, root, remoteView, layout, entries) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Read a Host authority with the platform URL parser; bracketed IPv6 retains its address. */
|
|
2
|
+
export function readHostPort(authority) {
|
|
3
|
+
try {
|
|
4
|
+
if (!authority || /[\s/\\?#@]/.test(authority))
|
|
5
|
+
return undefined;
|
|
6
|
+
const url = new URL(`http://${authority}`);
|
|
7
|
+
if (url.username || url.password)
|
|
8
|
+
return undefined;
|
|
9
|
+
return { host: url.hostname.toLowerCase(), port: url.port };
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export declare const packOf: (manifest: {
|
|
|
11
11
|
}, surface?: DerivedSurface | Record<string, {
|
|
12
12
|
manifest: DerivedManifest;
|
|
13
13
|
surface: DerivedSurface;
|
|
14
|
+
}>,
|
|
15
|
+
/** the lanes beside a pack that has an API of its own (Clerk's `fapi`, PlanetScale's `api`) */
|
|
16
|
+
beside?: Record<string, {
|
|
17
|
+
manifest: DerivedManifest;
|
|
18
|
+
surface: DerivedSurface;
|
|
14
19
|
}>) => PackDescriptor;
|
|
15
20
|
/** The pack registered, so the World finds it. */
|
|
16
21
|
export declare const registerPack: (pack: PackDescriptor) => PackDescriptor;
|
|
@@ -20,7 +25,7 @@ export { verifyEvent } from './events.js';
|
|
|
20
25
|
export { deriveStateSystem, deriveVendorStateSystem } from './derived-real.js';
|
|
21
26
|
export type { IngestDecl } from './derived-core.js';
|
|
22
27
|
export type { SocketDecl, SocketEngine, SocketSession } from './sockets.js';
|
|
23
|
-
export { machinePool, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec } from './machines.js';
|
|
28
|
+
export { machinePool, isMachineCapabilityGap, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec, type MachineStatus, type MachineFiles, type MachineFileStat } from './machines.js';
|
|
24
29
|
export type { CorsDecl } from './cors.js';
|
|
25
30
|
export type { Handler, HandlerContext, WriteHookContext } from './derived-core.js';
|
|
26
31
|
export type { MultipartPart } from './multipart.js';
|
|
@@ -36,18 +41,19 @@ export { VendorUnreachableError } from './vendor-call.js';
|
|
|
36
41
|
export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER } from './request-scope.js';
|
|
37
42
|
export { serveHttp, serveStream, type HttpHandler, type HttpServer, type ServeHttpOptions } from './serve-http.js';
|
|
38
43
|
export type { TwinStream, TwinStreamConnection, TwinStreamSink } from './twin-fetch.js';
|
|
44
|
+
export { privateTransportKey } from './private-transport.js';
|
|
39
45
|
export { siteLabel, siteUrlOf, twinSiteUrl, twinVendorUrl } from './twin-fetch.js';
|
|
40
46
|
export { statefulTwinManifest, twinManifest } from './scenario.js';
|
|
41
47
|
export { assetContentType, packAsset } from './pack-assets.js';
|
|
42
48
|
export { bytesResponse, contentTypeOf, fileResponse } from './file-response.js';
|
|
43
|
-
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.js';
|
|
49
|
+
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, base62From, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.js';
|
|
44
50
|
export type { HandlerCrypto, Jwk, Jwks, JwtVerdict, SigningKey } from './signing.js';
|
|
45
51
|
export { blobDigest } from './blob-store.js';
|
|
46
52
|
export { gunzip, gzip } from './bytes.js';
|
|
47
53
|
export { ownFields } from './log.js';
|
|
48
54
|
export type { TwinResource } from './serve.js';
|
|
49
55
|
export { passwordHash, personOf, personWith, recordPerson, type Person } from './people.js';
|
|
50
|
-
export { EngineDatabaseError, EngineUnavailableError, quoteIdent
|
|
56
|
+
export { EngineDatabaseError, EngineUnavailableError, quoteIdent } from './managed-database.js';
|
|
51
57
|
export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.js';
|
|
52
58
|
export type { Mail, SmtpRoute } from './smtp.js';
|
|
53
59
|
export * as s3 from './s3/wire.js';
|
|
@@ -64,5 +70,7 @@ export * as redis from './redis/index.js';
|
|
|
64
70
|
export * as git from './git/index.js';
|
|
65
71
|
export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError } from './scenario.js';
|
|
66
72
|
export type { PackScenarioAdapter, ScenarioDecision, ScenarioDocument, ScenarioExtractorSpec, ScenarioFault, ScenarioFaultResult, ScenarioFeatures, ScenarioHandler, ScenarioMatcher, ScenarioMissRecord, ScenarioStatus } from './scenario.js';
|
|
73
|
+
export { verifyAttestationBundle } from './attestation.js';
|
|
67
74
|
export { browserSessionToken, startBrowserSession, endBrowserSession } from './browser-session.js';
|
|
75
|
+
export { readHostPort } from './host-port.js';
|
|
68
76
|
export { assertBrowserName, browserOrigin, validateBrowserState, validateBrowserStorageState, validateBrowserContext, browserAcceptLanguage, cookieMatchesHost, type BrowserCookie, type BrowserState, type BrowserStorageState, type BrowserContext, type BrowserIndexedDB } from './browser-state.js';
|
package/dist/src/index.js
CHANGED
|
@@ -8,12 +8,14 @@ import { bindSemantics as bindAny } from "./derived-core.js";
|
|
|
8
8
|
import { packOf as packOfAny, registerPack as registerAny } from "./packRegistry.js";
|
|
9
9
|
/** The descriptor a manifest declares, with its vendor ("The descriptor"); given the pack's generated surface (a vendor of
|
|
10
10
|
* lanes: each lane's manifest and surface), with the real-system adapters the kernel derives from them. */
|
|
11
|
-
export const packOf = (manifest, surface
|
|
11
|
+
export const packOf = (manifest, surface,
|
|
12
|
+
/** the lanes beside a pack that has an API of its own (Clerk's `fapi`, PlanetScale's `api`) */
|
|
13
|
+
beside) => packOfAny(manifest, surface, beside);
|
|
12
14
|
/** The pack registered, so the World finds it. */
|
|
13
15
|
export const registerPack = (pack) => registerAny(pack);
|
|
14
16
|
export { verifyEvent } from "./events.js";
|
|
15
17
|
export { deriveStateSystem, deriveVendorStateSystem } from "./derived-real.js";
|
|
16
|
-
export { machinePool } from "./machines.js";
|
|
18
|
+
export { machinePool, isMachineCapabilityGap } from "./machines.js";
|
|
17
19
|
/** A pack's handlers, by operationId, bound to the manifest: each is answered over the contract's context. */
|
|
18
20
|
export const bindSemantics = (m, handlers, scope = {}) => bindAny(m, handlers, scope);
|
|
19
21
|
// ── the derived fetch ──────────────────────────────────────────────────────────────────────────
|
|
@@ -28,13 +30,14 @@ export { VendorUnreachableError } from "./vendor-call.js";
|
|
|
28
30
|
export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER } from "./request-scope.js";
|
|
29
31
|
// ── the serve factory ──────────────────────────────────────────────────────────────────────────
|
|
30
32
|
export { serveHttp, serveStream } from "./serve-http.js";
|
|
33
|
+
export { privateTransportKey } from "./private-transport.js";
|
|
31
34
|
// where a page links a hostname its twin answers, inside the World that shows it
|
|
32
35
|
export { siteLabel, siteUrlOf, twinSiteUrl, twinVendorUrl } from "./twin-fetch.js";
|
|
33
36
|
export { statefulTwinManifest, twinManifest } from "./scenario.js";
|
|
34
37
|
export { assetContentType, packAsset } from "./pack-assets.js";
|
|
35
38
|
export { bytesResponse, contentTypeOf, fileResponse } from "./file-response.js";
|
|
36
39
|
// ── the kernel's deterministic helpers ─────────────────────────────────────────────────────────
|
|
37
|
-
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from "./signing.js";
|
|
40
|
+
export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, base62From, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from "./signing.js";
|
|
38
41
|
export { blobDigest } from "./blob-store.js";
|
|
39
42
|
// compressed bytes a wire carries (a SARIF upload), over web streams
|
|
40
43
|
export { gunzip, gzip } from "./bytes.js";
|
|
@@ -43,7 +46,7 @@ export { ownFields } from "./log.js";
|
|
|
43
46
|
// the people a vendor's pages sign in (a `_person` row with its password's hash)
|
|
44
47
|
export { passwordHash, personOf, personWith, recordPerson } from "./people.js";
|
|
45
48
|
// the World's managed Postgres a handler reaches as `ctx.engine` (managed-database.ts), and a mail's route (`ctx.mail`)
|
|
46
|
-
export { EngineDatabaseError, EngineUnavailableError, quoteIdent
|
|
49
|
+
export { EngineDatabaseError, EngineUnavailableError, quoteIdent } from "./managed-database.js";
|
|
47
50
|
// S3's wire (contract "S3's wire is a kernel library too") and the git plane (contract "The git plane is a kernel library")
|
|
48
51
|
export * as s3 from "./s3/wire.js";
|
|
49
52
|
export * as clickhouse from "./clickhouse/index.js";
|
|
@@ -59,5 +62,7 @@ export * as redis from "./redis/index.js";
|
|
|
59
62
|
export * as git from "./git/index.js";
|
|
60
63
|
// ── the scenario grammar (architecture, "Behaviour") ───────────────────────────────────────────
|
|
61
64
|
export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError } from "./scenario.js";
|
|
65
|
+
export { verifyAttestationBundle } from "./attestation.js";
|
|
62
66
|
export { browserSessionToken, startBrowserSession, endBrowserSession } from "./browser-session.js";
|
|
67
|
+
export { readHostPort } from "./host-port.js";
|
|
63
68
|
export { assertBrowserName, browserOrigin, validateBrowserState, validateBrowserStorageState, validateBrowserContext, browserAcceptLanguage, cookieMatchesHost } from "./browser-state.js";
|
package/dist/src/log.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseExactJson } from "./exact-json.js";
|
|
1
2
|
// THE LOG (docs/concepts/the-model.md). One kind of entry, one log per
|
|
2
3
|
// branch, and a branch is a parent plus a position in the parent's log:
|
|
3
4
|
//
|
|
@@ -45,7 +46,7 @@ function parseLines(path, lines, offset, out) {
|
|
|
45
46
|
if (!line.trim())
|
|
46
47
|
continue;
|
|
47
48
|
try {
|
|
48
|
-
out.push(
|
|
49
|
+
out.push(parseExactJson(line));
|
|
49
50
|
}
|
|
50
51
|
catch (error) {
|
|
51
52
|
throw new Error(`${path}:${offset + i + 1}: invalid JSONL row: ${error.message}`);
|
|
@@ -115,7 +116,7 @@ export function readBranchMeta(service, root) {
|
|
|
115
116
|
const raw = getActiveWorldStore().read(branchMetaPath(service, root));
|
|
116
117
|
if (raw === null)
|
|
117
118
|
return null;
|
|
118
|
-
const meta =
|
|
119
|
+
const meta = parseExactJson(raw);
|
|
119
120
|
// a pointer written before positions were one number: the parent's whole log is parent then branch
|
|
120
121
|
if (meta.parent && meta.parent.at === undefined && meta.parent.root !== undefined)
|
|
121
122
|
meta.parent = { at: meta.parent.root, position: (meta.parent.parentCount ?? 0) + (meta.parent.branchCount ?? 0) };
|
|
@@ -274,7 +275,7 @@ export function landedIds(parent) {
|
|
|
274
275
|
export function aliasesFrom(parent) {
|
|
275
276
|
const vendorHeld = new Set();
|
|
276
277
|
for (const e of parent)
|
|
277
|
-
if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || e.landsId !== undefined)
|
|
278
|
+
if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || (e.landsId !== undefined && e.receipt?.status === 'deployed'))
|
|
278
279
|
vendorHeld.add(`${e.subject.type}:${e.subject.id}`);
|
|
279
280
|
const out = new Map();
|
|
280
281
|
for (const e of parent) {
|
|
@@ -355,7 +356,7 @@ export function treeResources(tree) {
|
|
|
355
356
|
for (const [k, v] of Object.entries(s.fields)) {
|
|
356
357
|
if (!META.has(k))
|
|
357
358
|
out[k] = v;
|
|
358
|
-
else if (v !== undefined
|
|
359
|
+
else if (v !== undefined)
|
|
359
360
|
own[k] = v;
|
|
360
361
|
}
|
|
361
362
|
if (Object.keys(own).length)
|
|
@@ -376,7 +377,11 @@ export function ownFields(r) {
|
|
|
376
377
|
const { id: _id, type: _type, updatedAt: _at, ...rest } = r;
|
|
377
378
|
const own = (r[OWN] ?? {});
|
|
378
379
|
// restored first: a vendor's object leads with its id, and a reader's grep may count on the order
|
|
379
|
-
|
|
380
|
+
const out = { ...(own.id !== undefined ? { id: own.id } : {}), ...(own.type !== undefined ? { type: own.type } : {}), ...rest, ...(own.updatedAt !== undefined ? { updatedAt: own.updatedAt } : {}) };
|
|
381
|
+
// the own record rides along unseen, so an own view taken of an own view is the same view (never one that drops them)
|
|
382
|
+
if (r[OWN] !== undefined)
|
|
383
|
+
Object.defineProperty(out, OWN, { value: own, enumerable: false });
|
|
384
|
+
return out;
|
|
380
385
|
}
|
|
381
386
|
export function dropCheckpoint(service, root) { getActiveWorldStore().remove(checkpointPath(service, root)); }
|
|
382
387
|
/** Preserve each ancestor's observed-then-local overlay, including landed IDs and aliases. */
|
|
@@ -579,7 +584,7 @@ function readTreeMapFull(service, root, opts = {}) {
|
|
|
579
584
|
const facts = opts.until === undefined ? `${parentKeyOf(service, root)}#${statKey(branchLogPath(service, root))}` : null;
|
|
580
585
|
if (facts !== null) {
|
|
581
586
|
const held = getActiveWorldStore().read(checkpointPath(service, root));
|
|
582
|
-
const cut = held === null ? null :
|
|
587
|
+
const cut = held === null ? null : parseExactJson(held);
|
|
583
588
|
if (cut?.facts === facts)
|
|
584
589
|
return new Map(cut.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
|
|
585
590
|
}
|
|
@@ -593,7 +598,7 @@ function readTreeMapFull(service, root, opts = {}) {
|
|
|
593
598
|
const parentDigest = hashFieldValue({ inherited, own });
|
|
594
599
|
const branchDigest = hashFieldValue(branch);
|
|
595
600
|
const raw = opts.until === undefined ? getActiveWorldStore().read(checkpointPath(service, root)) : null;
|
|
596
|
-
const cp = raw === null ? null :
|
|
601
|
+
const cp = raw === null ? null : parseExactJson(raw);
|
|
597
602
|
if (cp?.parentDigest === parentDigest && cp.branchDigest === branchDigest)
|
|
598
603
|
return new Map(cp.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
|
|
599
604
|
const layout = { ...(inherited ? { base: inherited.layout } : {}), parent: own.length, branch: branch.length };
|
package/dist/src/machines.d.ts
CHANGED
|
@@ -4,13 +4,61 @@ export type MachineSpec = {
|
|
|
4
4
|
env?: Record<string, string>;
|
|
5
5
|
ports?: number[];
|
|
6
6
|
command?: string[];
|
|
7
|
+
checks?: Array<{
|
|
8
|
+
port: number;
|
|
9
|
+
type: 'http' | 'tcp';
|
|
10
|
+
path?: string;
|
|
11
|
+
}>;
|
|
12
|
+
resetOnStop?: boolean;
|
|
13
|
+
mounts?: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
path: string;
|
|
16
|
+
}>;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
startTimeoutMs?: number;
|
|
19
|
+
};
|
|
20
|
+
export type GuestImage = {
|
|
21
|
+
context: string;
|
|
22
|
+
dockerfile?: string;
|
|
23
|
+
runtime?: 'node' | 'bun' | 'python' | 'deno';
|
|
24
|
+
command?: string[];
|
|
25
|
+
cwd?: string;
|
|
26
|
+
env?: Record<string, string>;
|
|
27
|
+
prepared?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type LightPoolOptions = {
|
|
30
|
+
images?: Record<string, GuestImage>;
|
|
7
31
|
};
|
|
8
32
|
export type MachineRun = {
|
|
9
33
|
ports: Array<{
|
|
10
34
|
internal: number;
|
|
11
35
|
host: number;
|
|
36
|
+
url?: string;
|
|
12
37
|
}>;
|
|
13
38
|
};
|
|
39
|
+
export type MachineStatus = {
|
|
40
|
+
state: 'started' | 'paused' | 'stopped' | 'exited' | 'failed';
|
|
41
|
+
exitCode?: number;
|
|
42
|
+
reason?: string;
|
|
43
|
+
};
|
|
44
|
+
export type MachineFileStat = {
|
|
45
|
+
type: 'file' | 'directory';
|
|
46
|
+
size: number;
|
|
47
|
+
mode: number;
|
|
48
|
+
mtime: string;
|
|
49
|
+
};
|
|
50
|
+
export type MachineFiles = {
|
|
51
|
+
read(path: string): Promise<Uint8Array>;
|
|
52
|
+
write(path: string, bytes: Uint8Array): Promise<unknown>;
|
|
53
|
+
list(path: string): Promise<Array<{
|
|
54
|
+
name: string;
|
|
55
|
+
type: 'file' | 'directory';
|
|
56
|
+
size: number;
|
|
57
|
+
}>>;
|
|
58
|
+
mkdir(path: string): Promise<unknown>;
|
|
59
|
+
remove(path: string): Promise<unknown>;
|
|
60
|
+
stat(path: string): Promise<MachineFileStat>;
|
|
61
|
+
};
|
|
14
62
|
/** A process started in a machine: its pid, and what it writes and how it ends, as they happen. */
|
|
15
63
|
export type MachineProcess = {
|
|
16
64
|
pid: number;
|
|
@@ -27,24 +75,52 @@ export type ExecSpec = {
|
|
|
27
75
|
env?: Record<string, string>;
|
|
28
76
|
cwd?: string;
|
|
29
77
|
};
|
|
78
|
+
export type MachineHttpReply = {
|
|
79
|
+
port: number;
|
|
80
|
+
method: string;
|
|
81
|
+
path: string;
|
|
82
|
+
requestText?: string;
|
|
83
|
+
status: number;
|
|
84
|
+
headers?: Record<string, string>;
|
|
85
|
+
body?: string;
|
|
86
|
+
};
|
|
30
87
|
export type MachinePool = {
|
|
31
|
-
readonly kind: 'docker' | 'none' | 'fake';
|
|
88
|
+
readonly kind: 'docker' | 'none' | 'fake' | 'light';
|
|
89
|
+
/** Optional capability admission, before vendor resource creation; starts no process. */
|
|
90
|
+
admitRun?(spec: MachineSpec): Promise<void>;
|
|
32
91
|
/** runs the image; a failure (no container runtime, an image that will not start) rejects with the runtime's message */
|
|
33
92
|
run(spec: MachineSpec): Promise<MachineRun>;
|
|
34
93
|
/** starts a process in a running machine; a machine the pool does not run rejects */
|
|
35
94
|
exec(name: string, spec: ExecSpec): Promise<MachineProcess>;
|
|
95
|
+
request(name: string, port: number, request: Request): Promise<Response>;
|
|
96
|
+
/** Undefined means the metadata-only/fake backing has no process lifecycle to observe. */
|
|
97
|
+
status(name: string): Promise<MachineStatus | undefined>;
|
|
98
|
+
ports(name: string): Promise<number[]>;
|
|
99
|
+
files(name: string): Promise<MachineFiles>;
|
|
100
|
+
pause(name: string): Promise<void>;
|
|
101
|
+
resume(name: string): Promise<void>;
|
|
36
102
|
stop(name: string): Promise<void>;
|
|
37
103
|
remove(name: string): Promise<void>;
|
|
38
104
|
/** the fake pool's record of what it was asked */
|
|
39
105
|
readonly asked?: Array<{
|
|
40
|
-
act: 'run' | 'exec' | 'stop' | 'remove';
|
|
106
|
+
act: 'run' | 'exec' | 'request' | 'pause' | 'resume' | 'stop' | 'remove';
|
|
41
107
|
name: string;
|
|
42
108
|
spec?: MachineSpec;
|
|
43
109
|
exec?: ExecSpec;
|
|
110
|
+
port?: number;
|
|
111
|
+
path?: string;
|
|
112
|
+
requestText?: string;
|
|
44
113
|
}>;
|
|
45
114
|
};
|
|
115
|
+
/** A declared compute capability gap, distinct from a failed admitted guest. */
|
|
116
|
+
export declare function isMachineCapabilityGap(error: unknown): boolean;
|
|
46
117
|
export declare const MACHINE_POOL = "_machine_pool";
|
|
47
|
-
export declare const POOL_KINDS: readonly ["docker", "none", "fake"];
|
|
118
|
+
export declare const POOL_KINDS: readonly ["docker", "none", "fake", "light"];
|
|
48
119
|
/** The pool of a kind, for a World (`world`: the mark its containers carry). */
|
|
49
|
-
export declare function machinePool(kind: MachinePool['kind'], world?: string): MachinePool;
|
|
50
|
-
export declare function poolFor(service: string, root: string | undefined, kind: MachinePool['kind']): MachinePool;
|
|
120
|
+
export declare function machinePool(kind: MachinePool['kind'], world?: string, httpReplies?: readonly MachineHttpReply[], light?: LightPoolOptions, root?: string): MachinePool;
|
|
121
|
+
export declare function poolFor(service: string, root: string | undefined, kind: MachinePool['kind'], httpReplies?: readonly MachineHttpReply[], light?: LightPoolOptions): MachinePool;
|
|
122
|
+
/** Re-enrolling a verification pool starts a fresh fake execution record. */
|
|
123
|
+
export declare function resetFakePool(service: string, root: string | undefined): void;
|
|
124
|
+
/** Every machine mutation shares the tree's refusal seam. HTTP/RPC reads must be explicitly
|
|
125
|
+
* classified by the pack; a computed operation may execute arbitrary guest code. */
|
|
126
|
+
export declare function guardedMachinePool(pool: MachinePool, service: string, safeRead: boolean): MachinePool;
|
package/dist/src/machines.js
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
1
9
|
// The World's machine pool (architecture, "Other wires: … machines"): what runs a vendor's customer's image when the
|
|
2
10
|
// vendor is one that runs images (Fly's Machines). It is a platform provider enrolled through the kernel's door
|
|
3
11
|
// `POST /_twin/machine-pool`, recorded in the pack's own tree; a handler reaches it as `ctx.machines`. `docker` runs
|
|
4
12
|
// each machine as a local container with its internal ports published on loopback; `none` runs nothing; `fake`
|
|
5
13
|
// records what it was asked (capability verification). Nothing here runs at module scope, and `node:child_process`
|
|
6
|
-
// is reached only when a
|
|
14
|
+
// is reached only when a compute pool is asked to act. The light pool delegates to the host's Substrate adapter.
|
|
15
|
+
import { refuseReadOnlyWrite } from "./request-scope.js";
|
|
7
16
|
import { nodeBuiltin } from "./serve-http.js";
|
|
17
|
+
/** A declared compute capability gap, distinct from a failed admitted guest. */
|
|
18
|
+
export function isMachineCapabilityGap(error) {
|
|
19
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'WORLD_MACHINE_CAPABILITY_GAP';
|
|
20
|
+
}
|
|
8
21
|
export const MACHINE_POOL = '_machine_pool';
|
|
9
|
-
export const POOL_KINDS = ['docker', 'none', 'fake'];
|
|
22
|
+
export const POOL_KINDS = ['docker', 'none', 'fake', 'light'];
|
|
23
|
+
/** The host owns provider bootstrapping. Browser/kernel imports stay inert, and a missing
|
|
24
|
+
* host provider refuses rather than falling back to metadata-only success. */
|
|
25
|
+
function lightPool(world, root, options) {
|
|
26
|
+
let ready;
|
|
27
|
+
const load = () => ready ??= (async () => {
|
|
28
|
+
const entry = typeof process === 'undefined' ? undefined : process.env.VOLTER_WORLD_GUEST_RUNNER;
|
|
29
|
+
if (!entry)
|
|
30
|
+
throw new Error('light guest runner is unavailable: run this twin inside a World');
|
|
31
|
+
const provider = await import(__rewriteRelativeImportExtension(entry));
|
|
32
|
+
return provider.createLightGuestPool({ world, root, ...options });
|
|
33
|
+
})();
|
|
34
|
+
return {
|
|
35
|
+
kind: 'light',
|
|
36
|
+
admitRun: async (spec) => { await (await load()).admitRun?.(spec); },
|
|
37
|
+
run: async (spec) => (await load()).run(spec),
|
|
38
|
+
exec: async (name, spec) => (await load()).exec(name, spec),
|
|
39
|
+
request: async (name, port, request) => (await load()).request(name, port, request),
|
|
40
|
+
status: async (name) => (await load()).status(name),
|
|
41
|
+
ports: async (name) => (await load()).ports(name),
|
|
42
|
+
files: async (name) => (await load()).files(name),
|
|
43
|
+
pause: async (name) => (await load()).pause(name),
|
|
44
|
+
resume: async (name) => (await load()).resume(name),
|
|
45
|
+
stop: async (name) => (await load()).stop(name),
|
|
46
|
+
remove: async (name) => (await load()).remove(name),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
10
49
|
/** A container's name for a machine: the World's prefix, the World's own mark (a machine id repeats across Worlds and
|
|
11
50
|
* branches, which mint alike), then the vendor's machine name made safe for docker. */
|
|
12
51
|
const containerName = (world, name) => `volter-world-machine-${world}-${name.replace(/[^A-Za-z0-9_.-]/g, '-')}`;
|
|
@@ -21,6 +60,12 @@ function dockerPool(world) {
|
|
|
21
60
|
});
|
|
22
61
|
return {
|
|
23
62
|
kind: 'docker',
|
|
63
|
+
async status(name) {
|
|
64
|
+
const state = JSON.parse(await docker(['inspect', '--format', '{{json .State}}', containerName(world, name)]));
|
|
65
|
+
return state.Running ? { state: 'started' } : { state: 'exited', exitCode: state.ExitCode, ...(state.Error ? { reason: state.Error } : {}) };
|
|
66
|
+
},
|
|
67
|
+
async ports() { throw new Error('port discovery requires the light guest backing'); },
|
|
68
|
+
async files() { throw new Error('file access requires the light guest backing'); },
|
|
24
69
|
async run(spec) {
|
|
25
70
|
const name = containerName(world, spec.name);
|
|
26
71
|
await docker(['rm', '-f', name]).catch(() => undefined);
|
|
@@ -105,47 +150,118 @@ function dockerPool(world) {
|
|
|
105
150
|
clearTimeout(timer);
|
|
106
151
|
}
|
|
107
152
|
},
|
|
153
|
+
async request(name, port, request) {
|
|
154
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
155
|
+
throw new Error('invalid machine port');
|
|
156
|
+
const line = (await docker(['port', containerName(world, name), String(port)])).split('\n')[0] ?? '';
|
|
157
|
+
const host = Number(line.split(':').at(-1));
|
|
158
|
+
if (!Number.isInteger(host) || host < 1 || host > 65535 || !line.startsWith('127.0.0.1:'))
|
|
159
|
+
throw new Error('the machine port is not published on loopback');
|
|
160
|
+
const source = new URL(request.url);
|
|
161
|
+
const headers = new Headers(request.headers);
|
|
162
|
+
for (const key of ['host', 'connection', 'content-length', 'transfer-encoding'])
|
|
163
|
+
headers.delete(key);
|
|
164
|
+
const body = ['GET', 'HEAD'].includes(request.method) ? undefined : await request.clone().arrayBuffer();
|
|
165
|
+
return fetch(`http://127.0.0.1:${host}${source.pathname}${source.search}`, { method: request.method, headers, body, redirect: 'manual', signal: request.signal });
|
|
166
|
+
},
|
|
167
|
+
async pause(name) { await docker(['pause', containerName(world, name)]); },
|
|
168
|
+
async resume(name) { await docker(['unpause', containerName(world, name)]); },
|
|
108
169
|
async stop(name) { await docker(['stop', containerName(world, name)]).catch(() => undefined); },
|
|
109
170
|
async remove(name) { await docker(['rm', '-f', containerName(world, name)]).catch(() => undefined); },
|
|
110
171
|
};
|
|
111
172
|
}
|
|
112
173
|
/** The pool of a kind, for a World (`world`: the mark its containers carry). */
|
|
113
|
-
export function machinePool(kind, world = 'world') {
|
|
174
|
+
export function machinePool(kind, world = 'world', httpReplies = [], light = {}, root) {
|
|
175
|
+
if (kind === 'light')
|
|
176
|
+
return lightPool(world, root, light);
|
|
114
177
|
if (kind === 'docker')
|
|
115
178
|
return dockerPool(world);
|
|
116
179
|
if (kind === 'fake') {
|
|
117
180
|
const asked = [];
|
|
181
|
+
const running = new Map();
|
|
118
182
|
return {
|
|
119
183
|
kind, asked,
|
|
120
|
-
async
|
|
184
|
+
async status() { return undefined; },
|
|
185
|
+
async ports(name) { return running.get(name)?.ports ?? []; },
|
|
186
|
+
async files() { throw new Error('the fake pool has no filesystem'); },
|
|
187
|
+
async run(spec) { running.set(spec.name, { ports: spec.ports ?? [], paused: false }); asked.push({ act: 'run', name: spec.name, spec }); return { ports: (spec.ports ?? []).map((internal) => ({ internal, host: 40_000 + internal })) }; },
|
|
121
188
|
async exec(name, spec) {
|
|
189
|
+
if (!running.has(name) || running.get(name).paused)
|
|
190
|
+
throw new Error('the fake machine is not running');
|
|
122
191
|
asked.push({ act: 'exec', name, exec: spec });
|
|
123
192
|
// a fake process: its pid the count of processes asked, and it ends at once
|
|
124
193
|
const pid = asked.filter((a) => a.act === 'exec').length + 100;
|
|
125
194
|
return { pid, events: (async function* () { yield { exit: 0 }; })() };
|
|
126
195
|
},
|
|
127
|
-
async
|
|
128
|
-
|
|
196
|
+
async request(name, port, request) {
|
|
197
|
+
const machine = running.get(name);
|
|
198
|
+
if (!machine || machine.paused || !machine.ports.includes(port))
|
|
199
|
+
throw new Error('the fake machine port is not running');
|
|
200
|
+
const url = new URL(request.url), path = url.pathname + url.search;
|
|
201
|
+
const requestText = ['GET', 'HEAD'].includes(request.method) ? '' : await request.clone().text();
|
|
202
|
+
asked.push({ act: 'request', name, port, path, requestText });
|
|
203
|
+
const reply = httpReplies.find((r) => r.port === port && r.method === request.method && r.path === path && (r.requestText === undefined || r.requestText === requestText));
|
|
204
|
+
if (!reply)
|
|
205
|
+
throw new Error('no fake HTTP reply matches');
|
|
206
|
+
return new Response(reply.body ?? null, { status: reply.status, headers: reply.headers });
|
|
207
|
+
},
|
|
208
|
+
async pause(name) { if (!running.has(name))
|
|
209
|
+
throw new Error('no fake machine'); running.get(name).paused = true; asked.push({ act: 'pause', name }); },
|
|
210
|
+
async resume(name) { if (!running.has(name))
|
|
211
|
+
throw new Error('no fake machine'); running.get(name).paused = false; asked.push({ act: 'resume', name }); },
|
|
212
|
+
async stop(name) { running.delete(name); asked.push({ act: 'stop', name }); },
|
|
213
|
+
async remove(name) { running.delete(name); asked.push({ act: 'remove', name }); },
|
|
129
214
|
};
|
|
130
215
|
}
|
|
131
216
|
return {
|
|
132
217
|
kind: 'none', async run() { return { ports: [] }; },
|
|
218
|
+
async status() { return undefined; },
|
|
219
|
+
async ports() { throw new Error('no guest ports'); },
|
|
220
|
+
async files() { throw new Error('no guest files'); },
|
|
133
221
|
async exec() { throw new Error('no machine runs in this World: its machine pool is none'); },
|
|
222
|
+
async request() { throw new Error('no machine runs in this World: its machine pool is none'); },
|
|
223
|
+
async pause() { },
|
|
224
|
+
async resume() { },
|
|
134
225
|
async stop() { }, async remove() { },
|
|
135
226
|
};
|
|
136
227
|
}
|
|
137
228
|
/** A vendor's pool in a World, one per World and kind for the life of the process (a fake pool keeps what it was asked). */
|
|
138
229
|
const pools = new Map();
|
|
139
|
-
export function poolFor(service, root, kind) {
|
|
140
|
-
const key = `${service}\0${root ?? ''}\0${kind}`;
|
|
230
|
+
export function poolFor(service, root, kind, httpReplies = [], light = {}) {
|
|
231
|
+
const key = `${service}\0${root ?? ''}\0${kind}${kind === 'light' ? JSON.stringify(light) : ''}`;
|
|
141
232
|
let pool = pools.get(key);
|
|
142
233
|
if (!pool) {
|
|
143
234
|
// the World's mark: a short digest of the vendor and the World's tree, so two Worlds' machines never share a name
|
|
144
235
|
let h = 0;
|
|
145
236
|
for (const ch of `${service}:${root ?? ''}`)
|
|
146
237
|
h = (Math.imul(h, 31) + ch.charCodeAt(0)) >>> 0;
|
|
147
|
-
pool = machinePool(kind, h.toString(16).padStart(8, '0'));
|
|
238
|
+
pool = machinePool(kind, h.toString(16).padStart(8, '0'), httpReplies, light, root);
|
|
148
239
|
pools.set(key, pool);
|
|
149
240
|
}
|
|
150
241
|
return pool;
|
|
151
242
|
}
|
|
243
|
+
/** Re-enrolling a verification pool starts a fresh fake execution record. */
|
|
244
|
+
export function resetFakePool(service, root) { pools.delete(`${service}\0${root ?? ''}\0fake`); }
|
|
245
|
+
/** Every machine mutation shares the tree's refusal seam. HTTP/RPC reads must be explicitly
|
|
246
|
+
* classified by the pack; a computed operation may execute arbitrary guest code. */
|
|
247
|
+
export function guardedMachinePool(pool, service, safeRead) {
|
|
248
|
+
const guard = () => refuseReadOnlyWrite(service);
|
|
249
|
+
return {
|
|
250
|
+
kind: pool.kind, asked: pool.asked,
|
|
251
|
+
...(pool.admitRun ? { admitRun: (spec) => { guard(); return pool.admitRun(spec); } } : {}),
|
|
252
|
+
run: (spec) => { guard(); return pool.run(spec); },
|
|
253
|
+
exec: (name, spec) => { guard(); return pool.exec(name, spec); },
|
|
254
|
+
request: (name, port, request) => { if (!safeRead)
|
|
255
|
+
guard(); return pool.request(name, port, request); },
|
|
256
|
+
status: (name) => pool.status(name),
|
|
257
|
+
ports: (name) => pool.ports(name),
|
|
258
|
+
files: async (name) => {
|
|
259
|
+
const files = await pool.files(name);
|
|
260
|
+
return { ...files, write: (path, bytes) => { guard(); return files.write(path, bytes); }, mkdir: (path) => { guard(); return files.mkdir(path); }, remove: (path) => { guard(); return files.remove(path); } };
|
|
261
|
+
},
|
|
262
|
+
pause: (name) => { guard(); return pool.pause(name); },
|
|
263
|
+
resume: (name) => { guard(); return pool.resume(name); },
|
|
264
|
+
stop: (name) => { guard(); return pool.stop(name); },
|
|
265
|
+
remove: (name) => { guard(); return pool.remove(name); },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
@@ -37,19 +37,14 @@ export type Batch = {
|
|
|
37
37
|
export interface ManagedDatabase {
|
|
38
38
|
/** Whether the World binds a database at all (a World with none refuses every batch). */
|
|
39
39
|
readonly bound: boolean;
|
|
40
|
+
/** The URL the World binds it at, what an application connects to (a credential door's database URL); none when
|
|
41
|
+
* unbound. */
|
|
42
|
+
readonly url?: string;
|
|
40
43
|
/** One transaction as one pipeline and one Sync; the answer is the batch's own statements', in order. */
|
|
41
44
|
batch(batch: Batch): Promise<StatementResult[]>;
|
|
42
45
|
/** A whole SQL script as one simple Query (a vendor's migrations, several statements to a file). */
|
|
43
46
|
script(text: string): Promise<void>;
|
|
44
47
|
}
|
|
45
|
-
/** The transaction-local setting every batch a handler makes carries: the World clock at the call (ctx.occurredAt). */
|
|
46
|
-
export declare const WORLD_CLOCK_SETTING = "volter.world_clock";
|
|
47
|
-
/** The World clock inside the database, for the schemas a vendor lays (its migrations stamp rows with Postgres's
|
|
48
|
-
* `now()`, which reads the wall clock): `volter.now()`, the World clock the batch set or the wall clock where none is
|
|
49
|
-
* set, and every column default in `schemas` that reads `now()` or `CURRENT_TIMESTAMP` (alone or inside an expression,
|
|
50
|
-
* GoTrue's `timezone('utc', now())`) rewritten to read it. Run after the schemas are laid; running it again changes
|
|
51
|
-
* nothing. A trigger or function that calls `now()` itself is the pack's to redefine. */
|
|
52
|
-
export declare function worldClockSql(schemas: string[]): string;
|
|
53
48
|
/** Quote an identifier the way Postgres's quote_ident does for a name that needs it. */
|
|
54
49
|
export declare function quoteIdent(name: string): string;
|
|
55
50
|
/** The part of a Postgres driver the kernel uses: node-postgres's (`pg`) Pool and its clients. */
|
|
@@ -73,12 +68,21 @@ export type DatabaseDriver = {
|
|
|
73
68
|
/** A Postgres driver a host registers in place of node-postgres. */
|
|
74
69
|
export declare function useDatabaseDriver(given: DatabaseDriver): void;
|
|
75
70
|
/** The World's Postgres at `url` (the runtime's binding, never the environment); with `readOnly`, every transaction is
|
|
76
|
-
* READ ONLY whatever the batch asks (a read-only twin or request: Postgres itself refuses a write, 25006)
|
|
77
|
-
* every batch carries the World clock as `volter.world_clock`. */
|
|
71
|
+
* READ ONLY whatever the batch asks (a read-only twin or request: Postgres itself refuses a write, 25006). */
|
|
78
72
|
export declare function managedDatabase(url: string, options?: {
|
|
79
73
|
readOnly?: boolean;
|
|
80
|
-
clock?: string;
|
|
81
74
|
}): ManagedDatabase;
|
|
75
|
+
/** One connection of an application's own to the World's Postgres (a journey's SQL steps: twin-standard
|
|
76
|
+
* walk/journey-kit.ts), its statements sent as its client sends them: a text alone as one simple Query (a migration
|
|
77
|
+
* file, a `BEGIN`), a text with values as one extended-protocol statement, the values bind parameters. A transaction it
|
|
78
|
+
* opens stays open across its statements until it ends it, as on the application's own connection. Each statement's
|
|
79
|
+
* answer is its results in Postgres's text format, untyped. */
|
|
80
|
+
export type DatabaseSession = {
|
|
81
|
+
query(text: string, values?: ReadonlyArray<string | null>): Promise<StatementResult[]>;
|
|
82
|
+
end(): Promise<void>;
|
|
83
|
+
};
|
|
84
|
+
/** The World's Postgres at `url` as one connection held until `end()`. */
|
|
85
|
+
export declare function databaseSession(url: string): Promise<DatabaseSession>;
|
|
82
86
|
/** A handler's database when the World binds none: every use is the refusal naming what to add. */
|
|
83
87
|
export declare function unboundDatabase(service: string): ManagedDatabase;
|
|
84
88
|
/** Close every connection this process holds (a test's teardown; a served twin's connections end with it). */
|