@osdk/integration-testing 0.0.0 → 1.0.0-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
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/CHANGELOG.md +17 -0
- package/bin/postinstall.mjs +15 -0
- package/build/browser/cli-service/CliServiceLauncher.js +220 -0
- package/build/browser/cli-service/CliServiceLauncher.js.map +1 -0
- package/build/browser/cli-service/FoundryCliService.js +168 -0
- package/build/browser/cli-service/FoundryCliService.js.map +1 -0
- package/build/browser/cli-service/OntologyServer.js +32 -0
- package/build/browser/cli-service/OntologyServer.js.map +1 -0
- package/build/browser/cli-service/ServiceDiscoverer.js +100 -0
- package/build/browser/cli-service/ServiceDiscoverer.js.map +1 -0
- package/build/browser/cli-service/StatusServer.js +91 -0
- package/build/browser/cli-service/StatusServer.js.map +1 -0
- package/build/browser/createIntegrationClient.js +67 -0
- package/build/browser/createIntegrationClient.js.map +1 -0
- package/build/browser/createIntegrationServer.js +115 -0
- package/build/browser/createIntegrationServer.js.map +1 -0
- package/build/browser/createSeedClient.js +77 -0
- package/build/browser/createSeedClient.js.map +1 -0
- package/build/browser/generated/cli/OntologySeedingService/setSeed.js +28 -0
- package/build/browser/generated/cli/OntologySeedingService/setSeed.js.map +1 -0
- package/build/browser/generated/cli/OntologySeedingService.js +19 -0
- package/build/browser/generated/cli/OntologySeedingService.js.map +1 -0
- package/build/browser/generated/cli/StatusService/getStatus.js +25 -0
- package/build/browser/generated/cli/StatusService/getStatus.js.map +1 -0
- package/build/browser/generated/cli/StatusService/publishStatus.js +26 -0
- package/build/browser/generated/cli/StatusService/publishStatus.js.map +1 -0
- package/build/browser/generated/cli/StatusService.js +20 -0
- package/build/browser/generated/cli/StatusService.js.map +1 -0
- package/build/browser/generated/cli/__components.js +2 -0
- package/build/browser/generated/cli/__components.js.map +1 -0
- package/build/browser/generated/cli/index.js +20 -0
- package/build/browser/generated/cli/index.js.map +1 -0
- package/build/browser/index.js +27 -0
- package/build/browser/index.js.map +1 -0
- package/build/browser/scripts/download.js +92 -0
- package/build/browser/scripts/download.js.map +1 -0
- package/build/browser/scripts/foundry-host.js +59 -0
- package/build/browser/scripts/foundry-host.js.map +1 -0
- package/build/browser/scripts/foundry-token.js +50 -0
- package/build/browser/scripts/foundry-token.js.map +1 -0
- package/build/browser/scripts/gitRemote.js +39 -0
- package/build/browser/scripts/gitRemote.js.map +1 -0
- package/build/browser/scripts/postinstall.js +38 -0
- package/build/browser/scripts/postinstall.js.map +1 -0
- package/build/browser/test/NoSpawnStatusServer.js +33 -0
- package/build/browser/test/NoSpawnStatusServer.js.map +1 -0
- package/build/browser/test/StubService.js +61 -0
- package/build/browser/test/StubService.js.map +1 -0
- package/build/browser/test/emptyOntologyMetadata.js +32 -0
- package/build/browser/test/emptyOntologyMetadata.js.map +1 -0
- package/build/browser/test/stubStatusServer.js +74 -0
- package/build/browser/test/stubStatusServer.js.map +1 -0
- package/build/browser/test/writeDiscoveryFile.js +39 -0
- package/build/browser/test/writeDiscoveryFile.js.map +1 -0
- package/build/browser/types.js +2 -0
- package/build/browser/types.js.map +1 -0
- package/build/browser/utils/empty-ontology-block.js +61 -0
- package/build/browser/utils/empty-ontology-block.js.map +1 -0
- package/build/browser/utils/foundry-cli.js +46 -0
- package/build/browser/utils/foundry-cli.js.map +1 -0
- package/build/browser/utils/process.js +26 -0
- package/build/browser/utils/process.js.map +1 -0
- package/build/browser/utils/service.js +33 -0
- package/build/browser/utils/service.js.map +1 -0
- package/build/cjs/index.cjs +841 -0
- package/build/cjs/index.cjs.map +1 -0
- package/build/cjs/index.d.cts +118 -0
- package/build/esm/cli-service/CliServiceLauncher.js +220 -0
- package/build/esm/cli-service/CliServiceLauncher.js.map +1 -0
- package/build/esm/cli-service/FoundryCliService.js +168 -0
- package/build/esm/cli-service/FoundryCliService.js.map +1 -0
- package/build/esm/cli-service/OntologyServer.js +32 -0
- package/build/esm/cli-service/OntologyServer.js.map +1 -0
- package/build/esm/cli-service/ServiceDiscoverer.js +100 -0
- package/build/esm/cli-service/ServiceDiscoverer.js.map +1 -0
- package/build/esm/cli-service/StatusServer.js +91 -0
- package/build/esm/cli-service/StatusServer.js.map +1 -0
- package/build/esm/createIntegrationClient.js +67 -0
- package/build/esm/createIntegrationClient.js.map +1 -0
- package/build/esm/createIntegrationServer.js +115 -0
- package/build/esm/createIntegrationServer.js.map +1 -0
- package/build/esm/createSeedClient.js +77 -0
- package/build/esm/createSeedClient.js.map +1 -0
- package/build/esm/generated/cli/OntologySeedingService/setSeed.js +28 -0
- package/build/esm/generated/cli/OntologySeedingService/setSeed.js.map +1 -0
- package/build/esm/generated/cli/OntologySeedingService.js +19 -0
- package/build/esm/generated/cli/OntologySeedingService.js.map +1 -0
- package/build/esm/generated/cli/StatusService/getStatus.js +25 -0
- package/build/esm/generated/cli/StatusService/getStatus.js.map +1 -0
- package/build/esm/generated/cli/StatusService/publishStatus.js +26 -0
- package/build/esm/generated/cli/StatusService/publishStatus.js.map +1 -0
- package/build/esm/generated/cli/StatusService.js +20 -0
- package/build/esm/generated/cli/StatusService.js.map +1 -0
- package/build/esm/generated/cli/__components.js +2 -0
- package/build/esm/generated/cli/__components.js.map +1 -0
- package/build/esm/generated/cli/index.js +20 -0
- package/build/esm/generated/cli/index.js.map +1 -0
- package/build/esm/index.js +27 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/scripts/download.js +92 -0
- package/build/esm/scripts/download.js.map +1 -0
- package/build/esm/scripts/foundry-host.js +59 -0
- package/build/esm/scripts/foundry-host.js.map +1 -0
- package/build/esm/scripts/foundry-token.js +50 -0
- package/build/esm/scripts/foundry-token.js.map +1 -0
- package/build/esm/scripts/gitRemote.js +39 -0
- package/build/esm/scripts/gitRemote.js.map +1 -0
- package/build/esm/scripts/postinstall.js +38 -0
- package/build/esm/scripts/postinstall.js.map +1 -0
- package/build/esm/test/NoSpawnStatusServer.js +33 -0
- package/build/esm/test/NoSpawnStatusServer.js.map +1 -0
- package/build/esm/test/StubService.js +61 -0
- package/build/esm/test/StubService.js.map +1 -0
- package/build/esm/test/emptyOntologyMetadata.js +32 -0
- package/build/esm/test/emptyOntologyMetadata.js.map +1 -0
- package/build/esm/test/stubStatusServer.js +74 -0
- package/build/esm/test/stubStatusServer.js.map +1 -0
- package/build/esm/test/writeDiscoveryFile.js +39 -0
- package/build/esm/test/writeDiscoveryFile.js.map +1 -0
- package/build/esm/types.js +2 -0
- package/build/esm/types.js.map +1 -0
- package/build/esm/utils/empty-ontology-block.js +61 -0
- package/build/esm/utils/empty-ontology-block.js.map +1 -0
- package/build/esm/utils/foundry-cli.js +46 -0
- package/build/esm/utils/foundry-cli.js.map +1 -0
- package/build/esm/utils/process.js +26 -0
- package/build/esm/utils/process.js.map +1 -0
- package/build/esm/utils/service.js +33 -0
- package/build/esm/utils/service.js.map +1 -0
- package/build/types/cli-service/CliServiceLauncher.d.ts +36 -0
- package/build/types/cli-service/CliServiceLauncher.d.ts.map +1 -0
- package/build/types/cli-service/FoundryCliService.d.ts +60 -0
- package/build/types/cli-service/FoundryCliService.d.ts.map +1 -0
- package/build/types/cli-service/OntologyServer.d.ts +10 -0
- package/build/types/cli-service/OntologyServer.d.ts.map +1 -0
- package/build/types/cli-service/ServiceDiscoverer.d.ts +22 -0
- package/build/types/cli-service/ServiceDiscoverer.d.ts.map +1 -0
- package/build/types/cli-service/StatusServer.d.ts +17 -0
- package/build/types/cli-service/StatusServer.d.ts.map +1 -0
- package/build/types/createIntegrationClient.d.ts +2 -0
- package/build/types/createIntegrationClient.d.ts.map +1 -0
- package/build/types/createIntegrationServer.d.ts +2 -0
- package/build/types/createIntegrationServer.d.ts.map +1 -0
- package/build/types/createSeedClient.d.ts +9 -0
- package/build/types/createSeedClient.d.ts.map +1 -0
- package/build/types/generated/cli/OntologySeedingService/setSeed.d.ts +9 -0
- package/build/types/generated/cli/OntologySeedingService/setSeed.d.ts.map +1 -0
- package/build/types/generated/cli/OntologySeedingService.d.ts +1 -0
- package/build/types/generated/cli/OntologySeedingService.d.ts.map +1 -0
- package/build/types/generated/cli/StatusService/getStatus.d.ts +6 -0
- package/build/types/generated/cli/StatusService/getStatus.d.ts.map +1 -0
- package/build/types/generated/cli/StatusService/publishStatus.d.ts +7 -0
- package/build/types/generated/cli/StatusService/publishStatus.d.ts.map +1 -0
- package/build/types/generated/cli/StatusService.d.ts +2 -0
- package/build/types/generated/cli/StatusService.d.ts.map +1 -0
- package/build/types/generated/cli/__components.d.ts +189 -0
- package/build/types/generated/cli/__components.d.ts.map +1 -0
- package/build/types/generated/cli/index.d.ts +3 -0
- package/build/types/generated/cli/index.d.ts.map +1 -0
- package/build/types/index.d.ts +10 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/scripts/download.d.ts +6 -0
- package/build/types/scripts/download.d.ts.map +1 -0
- package/build/types/scripts/foundry-host.d.ts +7 -0
- package/build/types/scripts/foundry-host.d.ts.map +1 -0
- package/build/types/scripts/foundry-token.d.ts +5 -0
- package/build/types/scripts/foundry-token.d.ts.map +1 -0
- package/build/types/scripts/gitRemote.d.ts +9 -0
- package/build/types/scripts/gitRemote.d.ts.map +1 -0
- package/build/types/scripts/postinstall.d.ts +1 -0
- package/build/types/scripts/postinstall.d.ts.map +1 -0
- package/build/types/test/NoSpawnStatusServer.d.ts +11 -0
- package/build/types/test/NoSpawnStatusServer.d.ts.map +1 -0
- package/build/types/test/StubService.d.ts +23 -0
- package/build/types/test/StubService.d.ts.map +1 -0
- package/build/types/test/emptyOntologyMetadata.d.ts +3 -0
- package/build/types/test/emptyOntologyMetadata.d.ts.map +1 -0
- package/build/types/test/stubStatusServer.d.ts +14 -0
- package/build/types/test/stubStatusServer.d.ts.map +1 -0
- package/build/types/test/writeDiscoveryFile.d.ts +8 -0
- package/build/types/test/writeDiscoveryFile.d.ts.map +1 -0
- package/build/types/types.d.ts +104 -0
- package/build/types/types.d.ts.map +1 -0
- package/build/types/utils/empty-ontology-block.d.ts +4 -0
- package/build/types/utils/empty-ontology-block.d.ts.map +1 -0
- package/build/types/utils/foundry-cli.d.ts +13 -0
- package/build/types/utils/foundry-cli.d.ts.map +1 -0
- package/build/types/utils/process.d.ts +1 -0
- package/build/types/utils/process.d.ts.map +1 -0
- package/build/types/utils/service.d.ts +4 -0
- package/build/types/utils/service.d.ts.map +1 -0
- package/package.json +92 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @osdk/integration-testing
|
|
2
|
+
|
|
3
|
+
## 1.0.0-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cec01e5: Introduce the `@osdk/integration-testing` package for Ontology SDK integration-testing helpers, and add `SeedClient.set` for replacing the seeded state rather than adding to it.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [7b85fa7]
|
|
12
|
+
- Updated dependencies [342c492]
|
|
13
|
+
- Updated dependencies [cec01e5]
|
|
14
|
+
- @osdk/generator-converters.preview@0.34.0-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
|
|
15
|
+
- @osdk/api@2.55.1-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
|
|
16
|
+
- @osdk/seed-helpers@0.26.0-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
|
|
17
|
+
- @osdk/client@2.55.1-main-cec01e57a04a4058ea12c83f98ab14fff4e769a1
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
3
|
+
|
|
4
|
+
const main = async () => {
|
|
5
|
+
let postinstall;
|
|
6
|
+
try {
|
|
7
|
+
const exported = await import("../build/esm/scripts/postinstall.js");
|
|
8
|
+
postinstall = exported.postinstall;
|
|
9
|
+
} catch {
|
|
10
|
+
// no-op
|
|
11
|
+
}
|
|
12
|
+
await postinstall?.();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
main();
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { setTimeout } from "node:timers/promises";
|
|
19
|
+
import invariant from "tiny-invariant";
|
|
20
|
+
import { DEFAULT_READY_TIMEOUT_MS } from "./FoundryCliService.js";
|
|
21
|
+
import { ServiceDiscoverer } from "./ServiceDiscoverer.js";
|
|
22
|
+
import { StatusServer } from "./StatusServer.js";
|
|
23
|
+
const HEALTH_POLL_INTERVAL_MS = 200;
|
|
24
|
+
/**
|
|
25
|
+
* Brings a set of Foundry services. Includes a status server by default.
|
|
26
|
+
*/
|
|
27
|
+
export class CliServiceLauncher {
|
|
28
|
+
#discoverer;
|
|
29
|
+
#statusServer;
|
|
30
|
+
#services = new Map();
|
|
31
|
+
|
|
32
|
+
/** Memoized in-flight starts, so a shared dependency is started once. */
|
|
33
|
+
#starts = new Map();
|
|
34
|
+
#started = false;
|
|
35
|
+
constructor(config = {}) {
|
|
36
|
+
const projectDir = config.projectDir ?? process.cwd();
|
|
37
|
+
this.#discoverer = new ServiceDiscoverer({
|
|
38
|
+
basePath: path.resolve(projectDir, ".palantir")
|
|
39
|
+
});
|
|
40
|
+
this.#statusServer = config.statusServer ?? new StatusServer({
|
|
41
|
+
projectPath: projectDir,
|
|
42
|
+
foundryCliPath: config.foundryCliPath
|
|
43
|
+
});
|
|
44
|
+
this.register(this.#statusServer);
|
|
45
|
+
}
|
|
46
|
+
get services() {
|
|
47
|
+
return [...this.#services.values()];
|
|
48
|
+
}
|
|
49
|
+
get(name) {
|
|
50
|
+
return this.#services.get(name);
|
|
51
|
+
}
|
|
52
|
+
register(service) {
|
|
53
|
+
const existing = this.#services.get(service.name);
|
|
54
|
+
if (existing === service) {
|
|
55
|
+
return service;
|
|
56
|
+
}
|
|
57
|
+
!(existing === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, `A different ${service.name} service is already registered`) : invariant(false) : void 0;
|
|
58
|
+
this.#services.set(service.name, service);
|
|
59
|
+
service.attach({
|
|
60
|
+
discoverer: this.#discoverer,
|
|
61
|
+
statusServer: this.#statusServer
|
|
62
|
+
});
|
|
63
|
+
for (const dependency of service.dependencies) {
|
|
64
|
+
this.register(dependency);
|
|
65
|
+
}
|
|
66
|
+
return service;
|
|
67
|
+
}
|
|
68
|
+
async start() {
|
|
69
|
+
await this.#ensureDiscovering();
|
|
70
|
+
this.#assertAcyclic();
|
|
71
|
+
const results = await Promise.allSettled([...this.#services.values()].map(service => this.#start(service)));
|
|
72
|
+
const failed = results.find(result => result.status === "rejected");
|
|
73
|
+
if (failed !== undefined) {
|
|
74
|
+
await this.stop();
|
|
75
|
+
throw failed.reason;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async startService(service) {
|
|
79
|
+
this.register(service);
|
|
80
|
+
await this.#ensureDiscovering();
|
|
81
|
+
this.#assertAcyclic();
|
|
82
|
+
await this.#start(service);
|
|
83
|
+
}
|
|
84
|
+
async #ensureDiscovering() {
|
|
85
|
+
if (!this.#started) {
|
|
86
|
+
await this.#discoverer.start();
|
|
87
|
+
this.#started = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async #start(service) {
|
|
91
|
+
const runningPromise = this.#starts.get(service.name);
|
|
92
|
+
if (typeof runningPromise !== "undefined") {
|
|
93
|
+
return runningPromise;
|
|
94
|
+
}
|
|
95
|
+
const started = this.#startImmediate(service);
|
|
96
|
+
this.#starts.set(service.name, started);
|
|
97
|
+
try {
|
|
98
|
+
await started;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
this.#starts.delete(service.name);
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async #startImmediate(service) {
|
|
105
|
+
await Promise.all(this.#prerequisites(service).map(dependency => this.#start(dependency)));
|
|
106
|
+
await service.start();
|
|
107
|
+
await this.waitUntilReady(service);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Everything that must be ready before `service` may start: its declared
|
|
112
|
+
* dependencies, plus the status server.
|
|
113
|
+
*/
|
|
114
|
+
#prerequisites(service) {
|
|
115
|
+
return service.name === this.#statusServer.name ? service.dependencies : [this.#statusServer, ...service.dependencies];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Poll a service's health until it is ready, reaches a terminal state, or
|
|
120
|
+
* exceeds its timeout.
|
|
121
|
+
*/
|
|
122
|
+
async waitUntilReady(service) {
|
|
123
|
+
const deadline = Date.now() + DEFAULT_READY_TIMEOUT_MS;
|
|
124
|
+
let health = await service.checkHealth();
|
|
125
|
+
while (!health.ready) {
|
|
126
|
+
const current = health;
|
|
127
|
+
const notReady = () => `${service.name} is not ready (${current.state})${formatError(service, current)}`;
|
|
128
|
+
!!current.terminal ? process.env.NODE_ENV !== "production" ? invariant(false, notReady) : invariant(false) : void 0;
|
|
129
|
+
!(service.getExitInfo() === undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, notReady) : invariant(false) : void 0;
|
|
130
|
+
!(Date.now() < deadline) ? process.env.NODE_ENV !== "production" ? invariant(false, () => `${service.name} is not ready (${current.state}) within ` + `${DEFAULT_READY_TIMEOUT_MS}ms${formatError(service, current)}`) : invariant(false) : void 0;
|
|
131
|
+
await setTimeout(HEALTH_POLL_INTERVAL_MS);
|
|
132
|
+
await this.#discoverer.refresh();
|
|
133
|
+
health = await service.checkHealth();
|
|
134
|
+
}
|
|
135
|
+
return health;
|
|
136
|
+
}
|
|
137
|
+
async checkHealth() {
|
|
138
|
+
await this.#discoverer.refresh();
|
|
139
|
+
return Promise.all([...this.#services.values()].map(service => service.checkHealth()));
|
|
140
|
+
}
|
|
141
|
+
async checkDependencies(service) {
|
|
142
|
+
await this.#discoverer.refresh();
|
|
143
|
+
return Promise.all(service.dependencies.map(dependency => dependency.checkHealth()));
|
|
144
|
+
}
|
|
145
|
+
async dependenciesReady(service) {
|
|
146
|
+
const healths = await this.checkDependencies(service);
|
|
147
|
+
return healths.every(({
|
|
148
|
+
ready
|
|
149
|
+
}) => ready);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Stop every service, dependents before their dependencies, waiting for each
|
|
154
|
+
* process to be gone before the next is signalled.
|
|
155
|
+
*/
|
|
156
|
+
async stop() {
|
|
157
|
+
for (const service of this.#startupOrder().reverse()) {
|
|
158
|
+
await service.stop();
|
|
159
|
+
}
|
|
160
|
+
this.#starts.clear();
|
|
161
|
+
this.#discoverer.stop();
|
|
162
|
+
this.#started = false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Registered services in a valid startup order: every service appears after
|
|
167
|
+
* everything it depends on.
|
|
168
|
+
*/
|
|
169
|
+
#startupOrder() {
|
|
170
|
+
const ordered = [];
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
const visit = service => {
|
|
173
|
+
if (seen.has(service.name)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
seen.add(service.name);
|
|
177
|
+
for (const dependency of this.#prerequisites(service)) {
|
|
178
|
+
visit(dependency);
|
|
179
|
+
}
|
|
180
|
+
ordered.push(service);
|
|
181
|
+
};
|
|
182
|
+
for (const service of this.#services.values()) {
|
|
183
|
+
visit(service);
|
|
184
|
+
}
|
|
185
|
+
return ordered;
|
|
186
|
+
}
|
|
187
|
+
#assertAcyclic() {
|
|
188
|
+
const settled = new Set();
|
|
189
|
+
const visit = (service, walkedPath) => {
|
|
190
|
+
if (settled.has(service.name)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const cycleStart = walkedPath.indexOf(service.name);
|
|
194
|
+
!(cycleStart === -1) ? process.env.NODE_ENV !== "production" ? invariant(false, () => `Dependency cycle between services: ${[...walkedPath.slice(cycleStart), service.name].join(" -> ")}`) : invariant(false) : void 0;
|
|
195
|
+
for (const dependency of this.#prerequisites(service)) {
|
|
196
|
+
visit(dependency, [...walkedPath, service.name]);
|
|
197
|
+
}
|
|
198
|
+
settled.add(service.name);
|
|
199
|
+
};
|
|
200
|
+
for (const service of this.#services.values()) {
|
|
201
|
+
visit(service, []);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const formatError = (service, health) => {
|
|
206
|
+
const parts = [];
|
|
207
|
+
if (health.message != null && health.message.length > 0) {
|
|
208
|
+
parts.push(health.message);
|
|
209
|
+
}
|
|
210
|
+
const exit = service.getExitInfo();
|
|
211
|
+
if (exit !== undefined) {
|
|
212
|
+
parts.push(`the process exited (${exit})`);
|
|
213
|
+
}
|
|
214
|
+
const stderr = service.getCapturedStderr();
|
|
215
|
+
if (stderr.length > 0) {
|
|
216
|
+
parts.push(`stderr:\n${stderr}`);
|
|
217
|
+
}
|
|
218
|
+
return parts.length === 0 ? "" : `: ${parts.join("; ")}`;
|
|
219
|
+
};
|
|
220
|
+
//# sourceMappingURL=CliServiceLauncher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CliServiceLauncher.js","names":["path","setTimeout","invariant","DEFAULT_READY_TIMEOUT_MS","ServiceDiscoverer","StatusServer","HEALTH_POLL_INTERVAL_MS","CliServiceLauncher","discoverer","statusServer","services","Map","starts","started","constructor","config","projectDir","process","cwd","basePath","resolve","projectPath","foundryCliPath","register","values","get","name","service","existing","undefined","env","NODE_ENV","set","attach","dependency","dependencies","start","ensureDiscovering","assertAcyclic","results","Promise","allSettled","map","failed","find","result","status","stop","reason","startService","#ensureDiscovering","#start","runningPromise","startImmediate","err","delete","#startImmediate","all","prerequisites","waitUntilReady","#prerequisites","deadline","Date","now","health","checkHealth","ready","current","notReady","state","formatError","terminal","getExitInfo","refresh","checkDependencies","dependenciesReady","healths","every","startupOrder","reverse","clear","#startupOrder","ordered","seen","Set","visit","has","add","push","#assertAcyclic","settled","walkedPath","cycleStart","indexOf","slice","join","parts","message","length","exit","stderr","getCapturedStderr"],"sources":["CliServiceLauncher.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path from \"node:path\";\nimport { setTimeout } from \"node:timers/promises\";\n\nimport invariant from \"tiny-invariant\";\n\nimport type { ServiceName } from \"../generated/cli/index.js\";\nimport {\n DEFAULT_READY_TIMEOUT_MS,\n type FoundryCliService,\n type ServiceHealth,\n} from \"./FoundryCliService.js\";\nimport { ServiceDiscoverer } from \"./ServiceDiscoverer.js\";\nimport { StatusServer } from \"./StatusServer.js\";\n\nconst HEALTH_POLL_INTERVAL_MS = 200;\n\nexport interface CliServiceLauncherConfig {\n /** Directory the services run in; `.palantir/` hangs off it. */\n projectDir?: string;\n /** Path to the `foundry` binary. */\n foundryCliPath?: string;\n /** Status server to use. One is created if not supplied. */\n statusServer?: StatusServer;\n}\n\n/**\n * Brings a set of Foundry services. Includes a status server by default.\n */\nexport class CliServiceLauncher {\n #discoverer: ServiceDiscoverer;\n #statusServer: StatusServer;\n #services = new Map<ServiceName, FoundryCliService>();\n\n /** Memoized in-flight starts, so a shared dependency is started once. */\n #starts = new Map<ServiceName, Promise<void>>();\n #started = false;\n\n constructor(config: CliServiceLauncherConfig = {}) {\n const projectDir = config.projectDir ?? process.cwd();\n this.#discoverer = new ServiceDiscoverer({\n basePath: path.resolve(projectDir, \".palantir\"),\n });\n this.#statusServer =\n config.statusServer ??\n new StatusServer({\n projectPath: projectDir,\n foundryCliPath: config.foundryCliPath,\n });\n this.register(this.#statusServer);\n }\n\n get services(): readonly FoundryCliService[] {\n return [...this.#services.values()];\n }\n\n get(name: ServiceName): FoundryCliService | undefined {\n return this.#services.get(name);\n }\n\n register<T extends FoundryCliService>(service: T): T {\n const existing = this.#services.get(service.name);\n if (existing === service) {\n return service;\n }\n invariant(\n existing === undefined,\n `A different ${service.name} service is already registered`,\n );\n this.#services.set(service.name, service);\n service.attach({\n discoverer: this.#discoverer,\n statusServer: this.#statusServer,\n });\n for (const dependency of service.dependencies) {\n this.register(dependency);\n }\n return service;\n }\n\n async start(): Promise<void> {\n await this.#ensureDiscovering();\n this.#assertAcyclic();\n const results = await Promise.allSettled(\n [...this.#services.values()].map((service) => this.#start(service)),\n );\n const failed = results.find((result) => result.status === \"rejected\");\n if (failed !== undefined) {\n await this.stop();\n throw failed.reason;\n }\n }\n\n async startService(service: FoundryCliService): Promise<void> {\n this.register(service);\n await this.#ensureDiscovering();\n this.#assertAcyclic();\n await this.#start(service);\n }\n\n async #ensureDiscovering(): Promise<void> {\n if (!this.#started) {\n await this.#discoverer.start();\n this.#started = true;\n }\n }\n\n async #start(service: FoundryCliService): Promise<void> {\n const runningPromise = this.#starts.get(service.name);\n if (typeof runningPromise !== \"undefined\") {\n return runningPromise;\n }\n const started = this.#startImmediate(service);\n this.#starts.set(service.name, started);\n try {\n await started;\n } catch (err) {\n this.#starts.delete(service.name);\n throw err;\n }\n }\n\n async #startImmediate(service: FoundryCliService): Promise<void> {\n await Promise.all(\n this.#prerequisites(service).map((dependency) => this.#start(dependency)),\n );\n await service.start();\n await this.waitUntilReady(service);\n }\n\n /**\n * Everything that must be ready before `service` may start: its declared\n * dependencies, plus the status server.\n */\n #prerequisites(service: FoundryCliService): readonly FoundryCliService[] {\n return service.name === this.#statusServer.name\n ? service.dependencies\n : [this.#statusServer, ...service.dependencies];\n }\n\n /**\n * Poll a service's health until it is ready, reaches a terminal state, or\n * exceeds its timeout.\n */\n async waitUntilReady(service: FoundryCliService): Promise<ServiceHealth> {\n const deadline = Date.now() + DEFAULT_READY_TIMEOUT_MS;\n let health = await service.checkHealth();\n while (!health.ready) {\n const current = health;\n const notReady = (): string =>\n `${service.name} is not ready (${current.state})${formatError(service, current)}`;\n invariant(!current.terminal, notReady);\n invariant(service.getExitInfo() === undefined, notReady);\n invariant(\n Date.now() < deadline,\n () =>\n `${service.name} is not ready (${current.state}) within ` +\n `${DEFAULT_READY_TIMEOUT_MS}ms${formatError(service, current)}`,\n );\n await setTimeout(HEALTH_POLL_INTERVAL_MS);\n await this.#discoverer.refresh();\n health = await service.checkHealth();\n }\n return health;\n }\n\n async checkHealth(): Promise<ServiceHealth[]> {\n await this.#discoverer.refresh();\n return Promise.all(\n [...this.#services.values()].map((service) => service.checkHealth()),\n );\n }\n\n async checkDependencies(\n service: FoundryCliService,\n ): Promise<ServiceHealth[]> {\n await this.#discoverer.refresh();\n return Promise.all(\n service.dependencies.map((dependency) => dependency.checkHealth()),\n );\n }\n\n async dependenciesReady(service: FoundryCliService): Promise<boolean> {\n const healths = await this.checkDependencies(service);\n return healths.every(({ ready }) => ready);\n }\n\n /**\n * Stop every service, dependents before their dependencies, waiting for each\n * process to be gone before the next is signalled.\n */\n async stop(): Promise<void> {\n for (const service of this.#startupOrder().reverse()) {\n await service.stop();\n }\n this.#starts.clear();\n this.#discoverer.stop();\n this.#started = false;\n }\n\n /**\n * Registered services in a valid startup order: every service appears after\n * everything it depends on.\n */\n #startupOrder(): FoundryCliService[] {\n const ordered: FoundryCliService[] = [];\n const seen = new Set<ServiceName>();\n const visit = (service: FoundryCliService): void => {\n if (seen.has(service.name)) {\n return;\n }\n seen.add(service.name);\n for (const dependency of this.#prerequisites(service)) {\n visit(dependency);\n }\n ordered.push(service);\n };\n for (const service of this.#services.values()) {\n visit(service);\n }\n return ordered;\n }\n\n #assertAcyclic(): void {\n const settled = new Set<ServiceName>();\n const visit = (\n service: FoundryCliService,\n walkedPath: ServiceName[],\n ): void => {\n if (settled.has(service.name)) {\n return;\n }\n const cycleStart = walkedPath.indexOf(service.name);\n invariant(\n cycleStart === -1,\n () =>\n `Dependency cycle between services: ${[\n ...walkedPath.slice(cycleStart),\n service.name,\n ].join(\" -> \")}`,\n );\n for (const dependency of this.#prerequisites(service)) {\n visit(dependency, [...walkedPath, service.name]);\n }\n settled.add(service.name);\n };\n for (const service of this.#services.values()) {\n visit(service, []);\n }\n }\n}\n\nconst formatError = (\n service: FoundryCliService,\n health: ServiceHealth,\n): string => {\n const parts: string[] = [];\n if (health.message != null && health.message.length > 0) {\n parts.push(health.message);\n }\n const exit = service.getExitInfo();\n if (exit !== undefined) {\n parts.push(`the process exited (${exit})`);\n }\n const stderr = service.getCapturedStderr();\n if (stderr.length > 0) {\n parts.push(`stderr:\\n${stderr}`);\n }\n return parts.length === 0 ? \"\" : `: ${parts.join(\"; \")}`;\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { spawn } from "node:child_process";
|
|
18
|
+
import { once } from "node:events";
|
|
19
|
+
import invariant from "tiny-invariant";
|
|
20
|
+
const MAX_CAPTURED_STDERR_CHUNKS = 32;
|
|
21
|
+
export const DEFAULT_READY_TIMEOUT_MS = 30_000;
|
|
22
|
+
export const DEFAULT_STOP_GRACE_MS = 5_000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The shared collaborators a service needs to answer questions about itself.
|
|
26
|
+
* Injected by the launcher via {@link FoundryCliService.attach} so services do
|
|
27
|
+
* not have to construct — or know how to reach — either one.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const TERMINAL_STATES = new Set(["FAILED", "STOPPED"]);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One Foundry CLI service subprocess.
|
|
34
|
+
*
|
|
35
|
+
* Subclasses supply the `foundry` subcommand to run and, where the default is
|
|
36
|
+
* wrong, how to tell that the service is up.
|
|
37
|
+
*
|
|
38
|
+
* Dependencies are declared but not enforced here: a service names what it needs
|
|
39
|
+
* and {@link CliServiceLauncher} is what orders and awaits them.
|
|
40
|
+
*/
|
|
41
|
+
export class FoundryCliService {
|
|
42
|
+
#foundryCliPath;
|
|
43
|
+
#projectPath;
|
|
44
|
+
#child;
|
|
45
|
+
#context;
|
|
46
|
+
#stderr = [];
|
|
47
|
+
#exit;
|
|
48
|
+
constructor(name, config) {
|
|
49
|
+
this.name = name;
|
|
50
|
+
this.dependencies = config.dependencies ?? [];
|
|
51
|
+
this.#foundryCliPath = config.foundryCliPath ?? "foundry";
|
|
52
|
+
this.#projectPath = config.projectPath;
|
|
53
|
+
}
|
|
54
|
+
getProjectPath() {
|
|
55
|
+
return this.#projectPath;
|
|
56
|
+
}
|
|
57
|
+
getCaCertPath() {
|
|
58
|
+
return this.#context?.discoverer.get(this.name)?.caCertPath ?? undefined;
|
|
59
|
+
}
|
|
60
|
+
getUrl() {
|
|
61
|
+
return this.#context?.discoverer.get(this.name)?.url;
|
|
62
|
+
}
|
|
63
|
+
getExitInfo() {
|
|
64
|
+
if (this.#exit === undefined) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const {
|
|
68
|
+
code,
|
|
69
|
+
signal
|
|
70
|
+
} = this.#exit;
|
|
71
|
+
return signal ?? `code ${code}`;
|
|
72
|
+
}
|
|
73
|
+
getCapturedStderr() {
|
|
74
|
+
return this.#stderr.join("").trim();
|
|
75
|
+
}
|
|
76
|
+
getContext() {
|
|
77
|
+
const context = this.#context;
|
|
78
|
+
!(context !== undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, `${this.name} is not registered with a CliServiceLauncher, so it has ` + `no discoverer or status server to consult`) : invariant(false) : void 0;
|
|
79
|
+
return context;
|
|
80
|
+
}
|
|
81
|
+
attach(context) {
|
|
82
|
+
this.#context = context;
|
|
83
|
+
}
|
|
84
|
+
async start() {
|
|
85
|
+
if (this.#child !== undefined) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await this.getContext().discoverer.refresh();
|
|
89
|
+
!!(await this.checkHealth()).ready ? process.env.NODE_ENV !== "production" ? invariant(false, `${this.name} is already running; stop it before starting a new one`) : invariant(false) : void 0;
|
|
90
|
+
await this.#spawn();
|
|
91
|
+
}
|
|
92
|
+
async #spawn() {
|
|
93
|
+
this.#exit = undefined;
|
|
94
|
+
this.#stderr = [];
|
|
95
|
+
const child = spawn(this.#foundryCliPath, [...this.getArgs()], {
|
|
96
|
+
cwd: this.#projectPath
|
|
97
|
+
});
|
|
98
|
+
this.#child = child;
|
|
99
|
+
child.stderr.setEncoding("utf-8");
|
|
100
|
+
child.stderr.on("data", chunk => {
|
|
101
|
+
if (this.#stderr.push(chunk) > MAX_CAPTURED_STDERR_CHUNKS) {
|
|
102
|
+
this.#stderr.shift();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
child.stdout.resume(); // flush, since we don't read from stdout
|
|
106
|
+
child.on("exit", (code, signal) => {
|
|
107
|
+
this.#exit = {
|
|
108
|
+
code,
|
|
109
|
+
signal
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
await new Promise((resolve, reject) => {
|
|
113
|
+
child.once("spawn", resolve);
|
|
114
|
+
child.once("error", error => reject(new Error(`Failed to spawn ${this.name} via ${this.#foundryCliPath}`, {
|
|
115
|
+
cause: error
|
|
116
|
+
})));
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async checkHealth() {
|
|
120
|
+
const {
|
|
121
|
+
discoverer,
|
|
122
|
+
statusServer
|
|
123
|
+
} = this.getContext();
|
|
124
|
+
const discovery = discoverer.get(this.name);
|
|
125
|
+
if (typeof discovery === "undefined") {
|
|
126
|
+
return {
|
|
127
|
+
service: this.name,
|
|
128
|
+
state: "UNDISCOVERED",
|
|
129
|
+
ready: false,
|
|
130
|
+
terminal: false
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const status = await statusServer.getServiceStatus(this.name);
|
|
134
|
+
const state = status?.status ?? "DISCOVERED";
|
|
135
|
+
return {
|
|
136
|
+
service: this.name,
|
|
137
|
+
state,
|
|
138
|
+
ready: state === "READY",
|
|
139
|
+
terminal: TERMINAL_STATES.has(state),
|
|
140
|
+
url: discovery.url,
|
|
141
|
+
...(status?.message != null ? {
|
|
142
|
+
message: status.message
|
|
143
|
+
} : {})
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Signal the service and wait for the process to actually be gone.
|
|
149
|
+
*/
|
|
150
|
+
async stop() {
|
|
151
|
+
const child = this.#child;
|
|
152
|
+
this.#child = undefined;
|
|
153
|
+
if (child === undefined || child.exitCode != null || child.signalCode != null) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const exited = once(child, "exit");
|
|
157
|
+
child.kill("SIGINT");
|
|
158
|
+
const escalation = setTimeout(() => {
|
|
159
|
+
child.kill("SIGKILL");
|
|
160
|
+
}, DEFAULT_STOP_GRACE_MS);
|
|
161
|
+
try {
|
|
162
|
+
await exited;
|
|
163
|
+
} finally {
|
|
164
|
+
clearTimeout(escalation);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=FoundryCliService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FoundryCliService.js","names":["spawn","once","invariant","MAX_CAPTURED_STDERR_CHUNKS","DEFAULT_READY_TIMEOUT_MS","DEFAULT_STOP_GRACE_MS","TERMINAL_STATES","Set","FoundryCliService","foundryCliPath","projectPath","child","context","stderr","exit","constructor","name","config","dependencies","getProjectPath","getCaCertPath","discoverer","get","caCertPath","undefined","getUrl","url","getExitInfo","code","signal","getCapturedStderr","join","trim","getContext","process","env","NODE_ENV","attach","start","refresh","checkHealth","ready","#spawn","getArgs","cwd","setEncoding","on","chunk","push","shift","stdout","resume","Promise","resolve","reject","error","Error","cause","statusServer","discovery","service","state","terminal","status","getServiceStatus","has","message","stop","exitCode","signalCode","exited","kill","escalation","setTimeout","clearTimeout"],"sources":["FoundryCliService.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { once } from \"node:events\";\n\nimport invariant from \"tiny-invariant\";\n\nimport type { ServiceLifecycle, ServiceName } from \"../generated/cli/index.js\";\nimport type { ServiceDiscoverer } from \"./ServiceDiscoverer.js\";\nimport type { StatusServer } from \"./StatusServer.js\";\n\nconst MAX_CAPTURED_STDERR_CHUNKS = 32;\n\nexport const DEFAULT_READY_TIMEOUT_MS = 30_000;\n\nexport const DEFAULT_STOP_GRACE_MS = 5_000;\n\nexport type ServiceState = ServiceLifecycle | \"UNDISCOVERED\" | \"DISCOVERED\";\n\nexport interface ServiceHealth {\n service: ServiceName;\n state: ServiceState;\n ready: boolean;\n terminal: boolean; // Whether `state` is terminal, so waiting for readiness is unnecessary.\n url?: string;\n message?: string;\n}\n\n/**\n * The shared collaborators a service needs to answer questions about itself.\n * Injected by the launcher via {@link FoundryCliService.attach} so services do\n * not have to construct — or know how to reach — either one.\n */\nexport interface ServiceContext {\n discoverer: ServiceDiscoverer;\n statusServer: StatusServer;\n}\n\nexport interface FoundryServiceConfig {\n /** Path to the `foundry` binary. */\n foundryCliPath?: string;\n /** Working directory the CLI runs in; the `.palantir/` dir hangs off it. */\n projectPath: string;\n /** Services that must be ready before this one may be started. */\n dependencies?: readonly FoundryCliService[];\n}\n\nconst TERMINAL_STATES: ReadonlySet<ServiceState> = new Set<ServiceState>([\n \"FAILED\",\n \"STOPPED\",\n]);\n\n/**\n * One Foundry CLI service subprocess.\n *\n * Subclasses supply the `foundry` subcommand to run and, where the default is\n * wrong, how to tell that the service is up.\n *\n * Dependencies are declared but not enforced here: a service names what it needs\n * and {@link CliServiceLauncher} is what orders and awaits them.\n */\nexport abstract class FoundryCliService {\n readonly name: ServiceName;\n readonly dependencies: readonly FoundryCliService[];\n\n #foundryCliPath: string;\n #projectPath: string;\n #child: ChildProcessWithoutNullStreams | undefined;\n #context: ServiceContext | undefined;\n #stderr: string[] = [];\n #exit: { code: number | null; signal: NodeJS.Signals | null } | undefined;\n\n protected constructor(name: ServiceName, config: FoundryServiceConfig) {\n this.name = name;\n this.dependencies = config.dependencies ?? [];\n this.#foundryCliPath = config.foundryCliPath ?? \"foundry\";\n this.#projectPath = config.projectPath;\n }\n\n protected abstract getArgs(): readonly string[];\n\n getProjectPath(): string {\n return this.#projectPath;\n }\n\n getCaCertPath(): string | undefined {\n return this.#context?.discoverer.get(this.name)?.caCertPath ?? undefined;\n }\n\n getUrl(): string | undefined {\n return this.#context?.discoverer.get(this.name)?.url;\n }\n\n getExitInfo(): string | undefined {\n if (this.#exit === undefined) {\n return undefined;\n }\n const { code, signal } = this.#exit;\n return signal ?? `code ${code}`;\n }\n\n getCapturedStderr(): string {\n return this.#stderr.join(\"\").trim();\n }\n\n protected getContext(): ServiceContext {\n const context = this.#context;\n invariant(\n context !== undefined,\n `${this.name} is not registered with a CliServiceLauncher, so it has ` +\n `no discoverer or status server to consult`,\n );\n return context;\n }\n\n attach(context: ServiceContext): void {\n this.#context = context;\n }\n\n async start(): Promise<void> {\n if (this.#child !== undefined) {\n return;\n }\n await this.getContext().discoverer.refresh();\n invariant(\n !(await this.checkHealth()).ready,\n `${this.name} is already running; stop it before starting a new one`,\n );\n await this.#spawn();\n }\n\n async #spawn(): Promise<void> {\n this.#exit = undefined;\n this.#stderr = [];\n const child = spawn(this.#foundryCliPath, [...this.getArgs()], {\n cwd: this.#projectPath,\n });\n this.#child = child;\n child.stderr.setEncoding(\"utf-8\");\n child.stderr.on(\"data\", (chunk: string) => {\n if (this.#stderr.push(chunk) > MAX_CAPTURED_STDERR_CHUNKS) {\n this.#stderr.shift();\n }\n });\n child.stdout.resume(); // flush, since we don't read from stdout\n child.on(\"exit\", (code, signal) => {\n this.#exit = { code, signal };\n });\n await new Promise<void>((resolve, reject) => {\n child.once(\"spawn\", resolve);\n child.once(\"error\", (error) =>\n reject(\n new Error(\n `Failed to spawn ${this.name} via ${this.#foundryCliPath}`,\n {\n cause: error,\n },\n ),\n ),\n );\n });\n }\n\n async checkHealth(): Promise<ServiceHealth> {\n const { discoverer, statusServer } = this.getContext();\n const discovery = discoverer.get(this.name);\n if (typeof discovery === \"undefined\") {\n return {\n service: this.name,\n state: \"UNDISCOVERED\",\n ready: false,\n terminal: false,\n };\n }\n const status = await statusServer.getServiceStatus(this.name);\n const state: ServiceState = status?.status ?? \"DISCOVERED\";\n return {\n service: this.name,\n state,\n ready: state === \"READY\",\n terminal: TERMINAL_STATES.has(state),\n url: discovery.url,\n ...(status?.message != null ? { message: status.message } : {}),\n };\n }\n\n /**\n * Signal the service and wait for the process to actually be gone.\n */\n async stop(): Promise<void> {\n const child = this.#child;\n this.#child = undefined;\n if (\n child === undefined ||\n child.exitCode != null ||\n child.signalCode != null\n ) {\n return;\n }\n const exited = once(child, \"exit\");\n child.kill(\"SIGINT\");\n const escalation = setTimeout(() => {\n child.kill(\"SIGKILL\");\n }, DEFAULT_STOP_GRACE_MS);\n try {\n await exited;\n } finally {\n clearTimeout(escalation);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import path, { isAbsolute, resolve } from "node:path";
|
|
18
|
+
import { FoundryCliService } from "./FoundryCliService.js";
|
|
19
|
+
export class OntologyServer extends FoundryCliService {
|
|
20
|
+
#metadataPath;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
super("ONTOLOGY", config);
|
|
23
|
+
this.#metadataPath = isAbsolute(config.metadataPath) ? config.metadataPath : resolve(config.projectPath, config.metadataPath);
|
|
24
|
+
}
|
|
25
|
+
getMetadataPath() {
|
|
26
|
+
return this.#metadataPath;
|
|
27
|
+
}
|
|
28
|
+
getArgs() {
|
|
29
|
+
return ["start", "ontology", "--skip-build", "--metadata", this.#metadataPath, "--discovery-path", path.resolve(this.getProjectPath(), ".palantir")];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=OntologyServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OntologyServer.js","names":["path","isAbsolute","resolve","FoundryCliService","OntologyServer","metadataPath","constructor","config","projectPath","getMetadataPath","getArgs","getProjectPath"],"sources":["OntologyServer.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport path, { isAbsolute, resolve } from \"node:path\";\n\nimport {\n FoundryCliService,\n type FoundryServiceConfig,\n} from \"./FoundryCliService.js\";\n\nexport interface OntologyServerConfig extends FoundryServiceConfig {\n metadataPath: string;\n}\n\nexport class OntologyServer extends FoundryCliService {\n #metadataPath: string;\n\n constructor(config: OntologyServerConfig) {\n super(\"ONTOLOGY\", config);\n this.#metadataPath = isAbsolute(config.metadataPath)\n ? config.metadataPath\n : resolve(config.projectPath, config.metadataPath);\n }\n\n getMetadataPath(): string {\n return this.#metadataPath;\n }\n\n protected override getArgs(): readonly string[] {\n return [\n \"start\",\n \"ontology\",\n \"--skip-build\",\n \"--metadata\",\n this.#metadataPath,\n \"--discovery-path\",\n path.resolve(this.getProjectPath(), \".palantir\"),\n ];\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,IAAI,IAAIC,UAAU,EAAEC,OAAO,QAAQ,WAAW;AAErD,SACEC,iBAAiB,QAEZ,wBAAwB;AAM/B,OAAO,MAAMC,cAAc,SAASD,iBAAiB,CAAC;EACpD,CAACE,YAAY;EAEbC,WAAWA,CAACC,MAA4B,EAAE;IACxC,KAAK,CAAC,UAAU,EAAEA,MAAM,CAAC;IACzB,IAAI,CAAC,CAACF,YAAY,GAAGJ,UAAU,CAACM,MAAM,CAACF,YAAY,CAAC,GAChDE,MAAM,CAACF,YAAY,GACnBH,OAAO,CAACK,MAAM,CAACC,WAAW,EAAED,MAAM,CAACF,YAAY,CAAC;EACtD;EAEAI,eAAeA,CAAA,EAAW;IACxB,OAAO,IAAI,CAAC,CAACJ,YAAY;EAC3B;EAEmBK,OAAOA,CAAA,EAAsB;IAC9C,OAAO,CACL,OAAO,EACP,UAAU,EACV,cAAc,EACd,YAAY,EACZ,IAAI,CAAC,CAACL,YAAY,EAClB,kBAAkB,EAClBL,IAAI,CAACE,OAAO,CAAC,IAAI,CAACS,cAAc,CAAC,CAAC,EAAE,WAAW,CAAC,CACjD;EACH;AACF","ignoreList":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as fs from "node:fs/promises";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import invariant from "tiny-invariant";
|
|
20
|
+
import { isProcessAlive } from "../utils/process.js";
|
|
21
|
+
import { serviceNameFromFileName } from "../utils/service.js";
|
|
22
|
+
/**
|
|
23
|
+
* Reads the discovery files that running Foundry services publish into
|
|
24
|
+
* `basePath`.
|
|
25
|
+
*/
|
|
26
|
+
export class ServiceDiscoverer {
|
|
27
|
+
#basePath;
|
|
28
|
+
#discovered = new Map();
|
|
29
|
+
constructor(args) {
|
|
30
|
+
this.#basePath = args.basePath;
|
|
31
|
+
}
|
|
32
|
+
get discoveryDir() {
|
|
33
|
+
return this.#basePath;
|
|
34
|
+
}
|
|
35
|
+
async start() {
|
|
36
|
+
await fs.mkdir(this.#basePath, {
|
|
37
|
+
recursive: true
|
|
38
|
+
});
|
|
39
|
+
await this.refresh();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Re-read every discovery file in the directory.
|
|
44
|
+
*/
|
|
45
|
+
async refresh() {
|
|
46
|
+
const fileNames = await fs.readdir(this.#basePath);
|
|
47
|
+
const discoveries = await Promise.all(fileNames.map(fileName => this.#read(fileName)));
|
|
48
|
+
return discoveries.filter(discovery => discovery !== undefined);
|
|
49
|
+
}
|
|
50
|
+
get(name) {
|
|
51
|
+
const discovery = this.#discovered.get(name);
|
|
52
|
+
if (typeof discovery === "undefined") {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (!isProcessAlive(discovery.pid)) {
|
|
56
|
+
this.#discovered.delete(name); // Stale: the publisher died without cleaning up after itself.
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return discovery;
|
|
60
|
+
}
|
|
61
|
+
isDiscovered(name) {
|
|
62
|
+
return this.get(name) !== undefined;
|
|
63
|
+
}
|
|
64
|
+
all() {
|
|
65
|
+
const live = new Map();
|
|
66
|
+
for (const name of this.#discovered.keys()) {
|
|
67
|
+
const discovery = this.get(name);
|
|
68
|
+
if (discovery !== undefined) {
|
|
69
|
+
live.set(name, discovery);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return live;
|
|
73
|
+
}
|
|
74
|
+
stop() {
|
|
75
|
+
this.#discovered.clear();
|
|
76
|
+
}
|
|
77
|
+
async #read(fileName) {
|
|
78
|
+
const serviceName = serviceNameFromFileName(fileName);
|
|
79
|
+
if (typeof serviceName === "undefined") {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
let contents;
|
|
83
|
+
try {
|
|
84
|
+
contents = await fs.readFile(join(this.#basePath, fileName), "utf-8");
|
|
85
|
+
} catch {
|
|
86
|
+
this.#discovered.delete(serviceName);
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(contents);
|
|
92
|
+
} catch {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
!parsed.url ? process.env.NODE_ENV !== "production" ? invariant(false, `Service URL not found`) : invariant(false) : void 0;
|
|
96
|
+
this.#discovered.set(serviceName, parsed);
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=ServiceDiscoverer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServiceDiscoverer.js","names":["fs","join","invariant","isProcessAlive","serviceNameFromFileName","ServiceDiscoverer","basePath","discovered","Map","constructor","args","discoveryDir","start","mkdir","recursive","refresh","fileNames","readdir","discoveries","Promise","all","map","fileName","read","filter","discovery","undefined","get","name","pid","delete","isDiscovered","live","keys","set","stop","clear","#read","serviceName","contents","readFile","parsed","JSON","parse","url","process","env","NODE_ENV"],"sources":["ServiceDiscoverer.ts"],"sourcesContent":["/*\n * Copyright 2026 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as fs from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport invariant from \"tiny-invariant\";\n\nimport type {\n ComponentDiscovery,\n ServiceName,\n} from \"../generated/cli/index.js\";\nimport { isProcessAlive } from \"../utils/process.js\";\nimport { serviceNameFromFileName } from \"../utils/service.js\";\n\nexport type ServiceDiscovererConfig = {\n basePath: string;\n};\n\n/**\n * Reads the discovery files that running Foundry services publish into\n * `basePath`.\n */\nexport class ServiceDiscoverer {\n #basePath: string;\n #discovered = new Map<ServiceName, ComponentDiscovery>();\n\n constructor(args: ServiceDiscovererConfig) {\n this.#basePath = args.basePath;\n }\n\n get discoveryDir(): string {\n return this.#basePath;\n }\n\n async start(): Promise<void> {\n await fs.mkdir(this.#basePath, { recursive: true });\n await this.refresh();\n }\n\n /**\n * Re-read every discovery file in the directory.\n */\n async refresh(): Promise<ComponentDiscovery[]> {\n const fileNames = await fs.readdir(this.#basePath);\n const discoveries = await Promise.all(\n fileNames.map((fileName) => this.#read(fileName)),\n );\n return discoveries.filter(\n (discovery): discovery is ComponentDiscovery => discovery !== undefined,\n );\n }\n\n get(name: ServiceName): ComponentDiscovery | undefined {\n const discovery = this.#discovered.get(name);\n if (typeof discovery === \"undefined\") {\n return undefined;\n }\n if (!isProcessAlive(discovery.pid)) {\n this.#discovered.delete(name); // Stale: the publisher died without cleaning up after itself.\n return undefined;\n }\n return discovery;\n }\n\n isDiscovered(name: ServiceName): boolean {\n return this.get(name) !== undefined;\n }\n\n all(): Map<ServiceName, ComponentDiscovery> {\n const live = new Map<ServiceName, ComponentDiscovery>();\n for (const name of this.#discovered.keys()) {\n const discovery = this.get(name);\n if (discovery !== undefined) {\n live.set(name, discovery);\n }\n }\n return live;\n }\n\n stop(): void {\n this.#discovered.clear();\n }\n\n async #read(fileName: string): Promise<ComponentDiscovery | undefined> {\n const serviceName = serviceNameFromFileName(fileName);\n if (typeof serviceName === \"undefined\") {\n return undefined;\n }\n let contents: string;\n try {\n contents = await fs.readFile(join(this.#basePath, fileName), \"utf-8\");\n } catch {\n this.#discovered.delete(serviceName);\n return undefined;\n }\n let parsed: ComponentDiscovery;\n try {\n parsed = JSON.parse(contents) as ComponentDiscovery;\n } catch {\n return undefined;\n }\n invariant(parsed.url, `Service URL not found`);\n this.#discovered.set(serviceName, parsed);\n return parsed;\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,KAAKA,EAAE,MAAM,kBAAkB;AACtC,SAASC,IAAI,QAAQ,WAAW;AAEhC,OAAOC,SAAS,MAAM,gBAAgB;AAMtC,SAASC,cAAc,QAAQ,qBAAqB;AACpD,SAASC,uBAAuB,QAAQ,qBAAqB;AAM7D;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,CAAC;EAC7B,CAACC,QAAQ;EACT,CAACC,UAAU,GAAG,IAAIC,GAAG,CAAkC,CAAC;EAExDC,WAAWA,CAACC,IAA6B,EAAE;IACzC,IAAI,CAAC,CAACJ,QAAQ,GAAGI,IAAI,CAACJ,QAAQ;EAChC;EAEA,IAAIK,YAAYA,CAAA,EAAW;IACzB,OAAO,IAAI,CAAC,CAACL,QAAQ;EACvB;EAEA,MAAMM,KAAKA,CAAA,EAAkB;IAC3B,MAAMZ,EAAE,CAACa,KAAK,CAAC,IAAI,CAAC,CAACP,QAAQ,EAAE;MAAEQ,SAAS,EAAE;IAAK,CAAC,CAAC;IACnD,MAAM,IAAI,CAACC,OAAO,CAAC,CAAC;EACtB;;EAEA;AACF;AACA;EACE,MAAMA,OAAOA,CAAA,EAAkC;IAC7C,MAAMC,SAAS,GAAG,MAAMhB,EAAE,CAACiB,OAAO,CAAC,IAAI,CAAC,CAACX,QAAQ,CAAC;IAClD,MAAMY,WAAW,GAAG,MAAMC,OAAO,CAACC,GAAG,CACnCJ,SAAS,CAACK,GAAG,CAAEC,QAAQ,IAAK,IAAI,CAAC,CAACC,IAAI,CAACD,QAAQ,CAAC,CAClD,CAAC;IACD,OAAOJ,WAAW,CAACM,MAAM,CACtBC,SAAS,IAAsCA,SAAS,KAAKC,SAChE,CAAC;EACH;EAEAC,GAAGA,CAACC,IAAiB,EAAkC;IACrD,MAAMH,SAAS,GAAG,IAAI,CAAC,CAAClB,UAAU,CAACoB,GAAG,CAACC,IAAI,CAAC;IAC5C,IAAI,OAAOH,SAAS,KAAK,WAAW,EAAE;MACpC,OAAOC,SAAS;IAClB;IACA,IAAI,CAACvB,cAAc,CAACsB,SAAS,CAACI,GAAG,CAAC,EAAE;MAClC,IAAI,CAAC,CAACtB,UAAU,CAACuB,MAAM,CAACF,IAAI,CAAC,CAAC,CAAC;MAC/B,OAAOF,SAAS;IAClB;IACA,OAAOD,SAAS;EAClB;EAEAM,YAAYA,CAACH,IAAiB,EAAW;IACvC,OAAO,IAAI,CAACD,GAAG,CAACC,IAAI,CAAC,KAAKF,SAAS;EACrC;EAEAN,GAAGA,CAAA,EAAyC;IAC1C,MAAMY,IAAI,GAAG,IAAIxB,GAAG,CAAkC,CAAC;IACvD,KAAK,MAAMoB,IAAI,IAAI,IAAI,CAAC,CAACrB,UAAU,CAAC0B,IAAI,CAAC,CAAC,EAAE;MAC1C,MAAMR,SAAS,GAAG,IAAI,CAACE,GAAG,CAACC,IAAI,CAAC;MAChC,IAAIH,SAAS,KAAKC,SAAS,EAAE;QAC3BM,IAAI,CAACE,GAAG,CAACN,IAAI,EAAEH,SAAS,CAAC;MAC3B;IACF;IACA,OAAOO,IAAI;EACb;EAEAG,IAAIA,CAAA,EAAS;IACX,IAAI,CAAC,CAAC5B,UAAU,CAAC6B,KAAK,CAAC,CAAC;EAC1B;EAEA,MAAM,CAACb,IAAIc,CAACf,QAAgB,EAA2C;IACrE,MAAMgB,WAAW,GAAGlC,uBAAuB,CAACkB,QAAQ,CAAC;IACrD,IAAI,OAAOgB,WAAW,KAAK,WAAW,EAAE;MACtC,OAAOZ,SAAS;IAClB;IACA,IAAIa,QAAgB;IACpB,IAAI;MACFA,QAAQ,GAAG,MAAMvC,EAAE,CAACwC,QAAQ,CAACvC,IAAI,CAAC,IAAI,CAAC,CAACK,QAAQ,EAAEgB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACvE,CAAC,CAAC,MAAM;MACN,IAAI,CAAC,CAACf,UAAU,CAACuB,MAAM,CAACQ,WAAW,CAAC;MACpC,OAAOZ,SAAS;IAClB;IACA,IAAIe,MAA0B;IAC9B,IAAI;MACFA,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACJ,QAAQ,CAAuB;IACrD,CAAC,CAAC,MAAM;MACN,OAAOb,SAAS;IAClB;IACA,CAAUe,MAAM,CAACG,GAAG,GAAAC,OAAA,CAAAC,GAAA,CAAAC,QAAA,oBAApB7C,SAAS,QAAa,uBAAuB,IAA7CA,SAAS;IACT,IAAI,CAAC,CAACK,UAAU,CAAC2B,GAAG,CAACI,WAAW,EAAEG,MAAM,CAAC;IACzC,OAAOA,MAAM;EACf;AACF","ignoreList":[]}
|