@osdk/integration-testing 0.0.0 → 0.1.0

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.
Files changed (192) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin/postinstall.mjs +15 -0
  3. package/build/browser/cli-service/CliServiceLauncher.js +220 -0
  4. package/build/browser/cli-service/CliServiceLauncher.js.map +1 -0
  5. package/build/browser/cli-service/FoundryCliService.js +168 -0
  6. package/build/browser/cli-service/FoundryCliService.js.map +1 -0
  7. package/build/browser/cli-service/OntologyServer.js +32 -0
  8. package/build/browser/cli-service/OntologyServer.js.map +1 -0
  9. package/build/browser/cli-service/ServiceDiscoverer.js +100 -0
  10. package/build/browser/cli-service/ServiceDiscoverer.js.map +1 -0
  11. package/build/browser/cli-service/StatusServer.js +91 -0
  12. package/build/browser/cli-service/StatusServer.js.map +1 -0
  13. package/build/browser/createIntegrationClient.js +67 -0
  14. package/build/browser/createIntegrationClient.js.map +1 -0
  15. package/build/browser/createIntegrationServer.js +115 -0
  16. package/build/browser/createIntegrationServer.js.map +1 -0
  17. package/build/browser/createSeedClient.js +77 -0
  18. package/build/browser/createSeedClient.js.map +1 -0
  19. package/build/browser/generated/cli/OntologySeedingService/setSeed.js +28 -0
  20. package/build/browser/generated/cli/OntologySeedingService/setSeed.js.map +1 -0
  21. package/build/browser/generated/cli/OntologySeedingService.js +19 -0
  22. package/build/browser/generated/cli/OntologySeedingService.js.map +1 -0
  23. package/build/browser/generated/cli/StatusService/getStatus.js +25 -0
  24. package/build/browser/generated/cli/StatusService/getStatus.js.map +1 -0
  25. package/build/browser/generated/cli/StatusService/publishStatus.js +26 -0
  26. package/build/browser/generated/cli/StatusService/publishStatus.js.map +1 -0
  27. package/build/browser/generated/cli/StatusService.js +20 -0
  28. package/build/browser/generated/cli/StatusService.js.map +1 -0
  29. package/build/browser/generated/cli/__components.js +2 -0
  30. package/build/browser/generated/cli/__components.js.map +1 -0
  31. package/build/browser/generated/cli/index.js +20 -0
  32. package/build/browser/generated/cli/index.js.map +1 -0
  33. package/build/browser/index.js +27 -0
  34. package/build/browser/index.js.map +1 -0
  35. package/build/browser/scripts/download.js +92 -0
  36. package/build/browser/scripts/download.js.map +1 -0
  37. package/build/browser/scripts/foundry-host.js +59 -0
  38. package/build/browser/scripts/foundry-host.js.map +1 -0
  39. package/build/browser/scripts/foundry-token.js +50 -0
  40. package/build/browser/scripts/foundry-token.js.map +1 -0
  41. package/build/browser/scripts/gitRemote.js +39 -0
  42. package/build/browser/scripts/gitRemote.js.map +1 -0
  43. package/build/browser/scripts/postinstall.js +38 -0
  44. package/build/browser/scripts/postinstall.js.map +1 -0
  45. package/build/browser/test/NoSpawnStatusServer.js +33 -0
  46. package/build/browser/test/NoSpawnStatusServer.js.map +1 -0
  47. package/build/browser/test/StubService.js +61 -0
  48. package/build/browser/test/StubService.js.map +1 -0
  49. package/build/browser/test/emptyOntologyMetadata.js +32 -0
  50. package/build/browser/test/emptyOntologyMetadata.js.map +1 -0
  51. package/build/browser/test/stubStatusServer.js +74 -0
  52. package/build/browser/test/stubStatusServer.js.map +1 -0
  53. package/build/browser/test/writeDiscoveryFile.js +39 -0
  54. package/build/browser/test/writeDiscoveryFile.js.map +1 -0
  55. package/build/browser/types.js +2 -0
  56. package/build/browser/types.js.map +1 -0
  57. package/build/browser/utils/empty-ontology-block.js +61 -0
  58. package/build/browser/utils/empty-ontology-block.js.map +1 -0
  59. package/build/browser/utils/foundry-cli.js +46 -0
  60. package/build/browser/utils/foundry-cli.js.map +1 -0
  61. package/build/browser/utils/process.js +26 -0
  62. package/build/browser/utils/process.js.map +1 -0
  63. package/build/browser/utils/service.js +33 -0
  64. package/build/browser/utils/service.js.map +1 -0
  65. package/build/cjs/index.cjs +841 -0
  66. package/build/cjs/index.cjs.map +1 -0
  67. package/build/cjs/index.d.cts +118 -0
  68. package/build/esm/cli-service/CliServiceLauncher.js +220 -0
  69. package/build/esm/cli-service/CliServiceLauncher.js.map +1 -0
  70. package/build/esm/cli-service/FoundryCliService.js +168 -0
  71. package/build/esm/cli-service/FoundryCliService.js.map +1 -0
  72. package/build/esm/cli-service/OntologyServer.js +32 -0
  73. package/build/esm/cli-service/OntologyServer.js.map +1 -0
  74. package/build/esm/cli-service/ServiceDiscoverer.js +100 -0
  75. package/build/esm/cli-service/ServiceDiscoverer.js.map +1 -0
  76. package/build/esm/cli-service/StatusServer.js +91 -0
  77. package/build/esm/cli-service/StatusServer.js.map +1 -0
  78. package/build/esm/createIntegrationClient.js +67 -0
  79. package/build/esm/createIntegrationClient.js.map +1 -0
  80. package/build/esm/createIntegrationServer.js +115 -0
  81. package/build/esm/createIntegrationServer.js.map +1 -0
  82. package/build/esm/createSeedClient.js +77 -0
  83. package/build/esm/createSeedClient.js.map +1 -0
  84. package/build/esm/generated/cli/OntologySeedingService/setSeed.js +28 -0
  85. package/build/esm/generated/cli/OntologySeedingService/setSeed.js.map +1 -0
  86. package/build/esm/generated/cli/OntologySeedingService.js +19 -0
  87. package/build/esm/generated/cli/OntologySeedingService.js.map +1 -0
  88. package/build/esm/generated/cli/StatusService/getStatus.js +25 -0
  89. package/build/esm/generated/cli/StatusService/getStatus.js.map +1 -0
  90. package/build/esm/generated/cli/StatusService/publishStatus.js +26 -0
  91. package/build/esm/generated/cli/StatusService/publishStatus.js.map +1 -0
  92. package/build/esm/generated/cli/StatusService.js +20 -0
  93. package/build/esm/generated/cli/StatusService.js.map +1 -0
  94. package/build/esm/generated/cli/__components.js +2 -0
  95. package/build/esm/generated/cli/__components.js.map +1 -0
  96. package/build/esm/generated/cli/index.js +20 -0
  97. package/build/esm/generated/cli/index.js.map +1 -0
  98. package/build/esm/index.js +27 -0
  99. package/build/esm/index.js.map +1 -0
  100. package/build/esm/scripts/download.js +92 -0
  101. package/build/esm/scripts/download.js.map +1 -0
  102. package/build/esm/scripts/foundry-host.js +59 -0
  103. package/build/esm/scripts/foundry-host.js.map +1 -0
  104. package/build/esm/scripts/foundry-token.js +50 -0
  105. package/build/esm/scripts/foundry-token.js.map +1 -0
  106. package/build/esm/scripts/gitRemote.js +39 -0
  107. package/build/esm/scripts/gitRemote.js.map +1 -0
  108. package/build/esm/scripts/postinstall.js +38 -0
  109. package/build/esm/scripts/postinstall.js.map +1 -0
  110. package/build/esm/test/NoSpawnStatusServer.js +33 -0
  111. package/build/esm/test/NoSpawnStatusServer.js.map +1 -0
  112. package/build/esm/test/StubService.js +61 -0
  113. package/build/esm/test/StubService.js.map +1 -0
  114. package/build/esm/test/emptyOntologyMetadata.js +32 -0
  115. package/build/esm/test/emptyOntologyMetadata.js.map +1 -0
  116. package/build/esm/test/stubStatusServer.js +74 -0
  117. package/build/esm/test/stubStatusServer.js.map +1 -0
  118. package/build/esm/test/writeDiscoveryFile.js +39 -0
  119. package/build/esm/test/writeDiscoveryFile.js.map +1 -0
  120. package/build/esm/types.js +2 -0
  121. package/build/esm/types.js.map +1 -0
  122. package/build/esm/utils/empty-ontology-block.js +61 -0
  123. package/build/esm/utils/empty-ontology-block.js.map +1 -0
  124. package/build/esm/utils/foundry-cli.js +46 -0
  125. package/build/esm/utils/foundry-cli.js.map +1 -0
  126. package/build/esm/utils/process.js +26 -0
  127. package/build/esm/utils/process.js.map +1 -0
  128. package/build/esm/utils/service.js +33 -0
  129. package/build/esm/utils/service.js.map +1 -0
  130. package/build/types/cli-service/CliServiceLauncher.d.ts +36 -0
  131. package/build/types/cli-service/CliServiceLauncher.d.ts.map +1 -0
  132. package/build/types/cli-service/FoundryCliService.d.ts +60 -0
  133. package/build/types/cli-service/FoundryCliService.d.ts.map +1 -0
  134. package/build/types/cli-service/OntologyServer.d.ts +10 -0
  135. package/build/types/cli-service/OntologyServer.d.ts.map +1 -0
  136. package/build/types/cli-service/ServiceDiscoverer.d.ts +22 -0
  137. package/build/types/cli-service/ServiceDiscoverer.d.ts.map +1 -0
  138. package/build/types/cli-service/StatusServer.d.ts +17 -0
  139. package/build/types/cli-service/StatusServer.d.ts.map +1 -0
  140. package/build/types/createIntegrationClient.d.ts +2 -0
  141. package/build/types/createIntegrationClient.d.ts.map +1 -0
  142. package/build/types/createIntegrationServer.d.ts +2 -0
  143. package/build/types/createIntegrationServer.d.ts.map +1 -0
  144. package/build/types/createSeedClient.d.ts +9 -0
  145. package/build/types/createSeedClient.d.ts.map +1 -0
  146. package/build/types/generated/cli/OntologySeedingService/setSeed.d.ts +9 -0
  147. package/build/types/generated/cli/OntologySeedingService/setSeed.d.ts.map +1 -0
  148. package/build/types/generated/cli/OntologySeedingService.d.ts +1 -0
  149. package/build/types/generated/cli/OntologySeedingService.d.ts.map +1 -0
  150. package/build/types/generated/cli/StatusService/getStatus.d.ts +6 -0
  151. package/build/types/generated/cli/StatusService/getStatus.d.ts.map +1 -0
  152. package/build/types/generated/cli/StatusService/publishStatus.d.ts +7 -0
  153. package/build/types/generated/cli/StatusService/publishStatus.d.ts.map +1 -0
  154. package/build/types/generated/cli/StatusService.d.ts +2 -0
  155. package/build/types/generated/cli/StatusService.d.ts.map +1 -0
  156. package/build/types/generated/cli/__components.d.ts +189 -0
  157. package/build/types/generated/cli/__components.d.ts.map +1 -0
  158. package/build/types/generated/cli/index.d.ts +3 -0
  159. package/build/types/generated/cli/index.d.ts.map +1 -0
  160. package/build/types/index.d.ts +10 -0
  161. package/build/types/index.d.ts.map +1 -0
  162. package/build/types/scripts/download.d.ts +6 -0
  163. package/build/types/scripts/download.d.ts.map +1 -0
  164. package/build/types/scripts/foundry-host.d.ts +7 -0
  165. package/build/types/scripts/foundry-host.d.ts.map +1 -0
  166. package/build/types/scripts/foundry-token.d.ts +5 -0
  167. package/build/types/scripts/foundry-token.d.ts.map +1 -0
  168. package/build/types/scripts/gitRemote.d.ts +9 -0
  169. package/build/types/scripts/gitRemote.d.ts.map +1 -0
  170. package/build/types/scripts/postinstall.d.ts +1 -0
  171. package/build/types/scripts/postinstall.d.ts.map +1 -0
  172. package/build/types/test/NoSpawnStatusServer.d.ts +11 -0
  173. package/build/types/test/NoSpawnStatusServer.d.ts.map +1 -0
  174. package/build/types/test/StubService.d.ts +23 -0
  175. package/build/types/test/StubService.d.ts.map +1 -0
  176. package/build/types/test/emptyOntologyMetadata.d.ts +3 -0
  177. package/build/types/test/emptyOntologyMetadata.d.ts.map +1 -0
  178. package/build/types/test/stubStatusServer.d.ts +14 -0
  179. package/build/types/test/stubStatusServer.d.ts.map +1 -0
  180. package/build/types/test/writeDiscoveryFile.d.ts +8 -0
  181. package/build/types/test/writeDiscoveryFile.d.ts.map +1 -0
  182. package/build/types/types.d.ts +104 -0
  183. package/build/types/types.d.ts.map +1 -0
  184. package/build/types/utils/empty-ontology-block.d.ts +4 -0
  185. package/build/types/utils/empty-ontology-block.d.ts.map +1 -0
  186. package/build/types/utils/foundry-cli.d.ts +13 -0
  187. package/build/types/utils/foundry-cli.d.ts.map +1 -0
  188. package/build/types/utils/process.d.ts +1 -0
  189. package/build/types/utils/process.d.ts.map +1 -0
  190. package/build/types/utils/service.d.ts +4 -0
  191. package/build/types/utils/service.d.ts.map +1 -0
  192. package/package.json +93 -1
@@ -0,0 +1,841 @@
1
+ 'use strict';
2
+
3
+ var fs3 = require('fs/promises');
4
+ var client = require('@osdk/client');
5
+ var unitTesting = require('@osdk/unit-testing');
6
+ var undici = require('undici');
7
+ var path4 = require('path');
8
+ var generatorConverters_preview = require('@osdk/generator-converters.preview');
9
+ var invariant4 = require('tiny-invariant');
10
+ var promises = require('timers/promises');
11
+ var child_process = require('child_process');
12
+ var events = require('events');
13
+ var conjureLite = require('conjure-lite');
14
+ var seedHelpers = require('@osdk/seed-helpers');
15
+ var util = require('util');
16
+ var semver = require('semver');
17
+
18
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
+
20
+ function _interopNamespace(e) {
21
+ if (e && e.__esModule) return e;
22
+ var n = Object.create(null);
23
+ if (e) {
24
+ Object.keys(e).forEach(function (k) {
25
+ if (k !== 'default') {
26
+ var d = Object.getOwnPropertyDescriptor(e, k);
27
+ Object.defineProperty(n, k, d.get ? d : {
28
+ enumerable: true,
29
+ get: function () { return e[k]; }
30
+ });
31
+ }
32
+ });
33
+ }
34
+ n.default = e;
35
+ return Object.freeze(n);
36
+ }
37
+
38
+ var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
39
+ var path4__default = /*#__PURE__*/_interopDefault(path4);
40
+ var invariant4__default = /*#__PURE__*/_interopDefault(invariant4);
41
+
42
+ var __defProp = Object.defineProperty;
43
+ var __export = (target, all) => {
44
+ for (var name in all)
45
+ __defProp(target, name, { get: all[name], enumerable: true });
46
+ };
47
+ async function createIntegrationClient(config) {
48
+ const {
49
+ baseUrl,
50
+ metadata,
51
+ caCertPath
52
+ } = config;
53
+ const agent = new undici.Agent({
54
+ connect: caCertPath ? {
55
+ ca: await fs3__namespace.default.readFile(caCertPath)
56
+ } : {
57
+ rejectUnauthorized: false
58
+ }
59
+ });
60
+ const fetchWithCert = (input, init) => undici.fetch(input, {
61
+ ...init,
62
+ dispatcher: agent
63
+ });
64
+ const realClient = client.createClient(baseUrl, metadata.ontology.rid, () => Promise.resolve("integration-client-token"), {}, fetchWithCert);
65
+ const mockClient = unitTesting.createMockClient();
66
+ const clientInternal = (def) => {
67
+ switch (def.type) {
68
+ case "query":
69
+ return mockClient(def);
70
+ case "action":
71
+ return realClient(def);
72
+ case "interface":
73
+ return realClient(def);
74
+ case "object":
75
+ return realClient(def);
76
+ case "experiment":
77
+ default:
78
+ return realClient(def);
79
+ }
80
+ };
81
+ const client$1 = Object.defineProperties(clientInternal, {
82
+ ...Object.getOwnPropertyDescriptors(realClient),
83
+ whenQuery: {
84
+ value: mockClient.whenQuery.bind(void 0)
85
+ },
86
+ clearStubs: {
87
+ value: mockClient.clearStubs.bind(void 0)
88
+ }
89
+ });
90
+ return client$1;
91
+ }
92
+ var MAX_CAPTURED_STDERR_CHUNKS = 32;
93
+ var DEFAULT_READY_TIMEOUT_MS = 3e4;
94
+ var DEFAULT_STOP_GRACE_MS = 5e3;
95
+ var TERMINAL_STATES = /* @__PURE__ */ new Set(["FAILED", "STOPPED"]);
96
+ var FoundryCliService = class {
97
+ #foundryCliPath;
98
+ #projectPath;
99
+ #child;
100
+ #context;
101
+ #stderr = [];
102
+ #exit;
103
+ constructor(name, config) {
104
+ this.name = name;
105
+ this.dependencies = config.dependencies ?? [];
106
+ this.#foundryCliPath = config.foundryCliPath ?? "foundry";
107
+ this.#projectPath = config.projectPath;
108
+ }
109
+ getProjectPath() {
110
+ return this.#projectPath;
111
+ }
112
+ getCaCertPath() {
113
+ return this.#context?.discoverer.get(this.name)?.caCertPath ?? void 0;
114
+ }
115
+ getUrl() {
116
+ return this.#context?.discoverer.get(this.name)?.url;
117
+ }
118
+ getExitInfo() {
119
+ if (this.#exit === void 0) {
120
+ return void 0;
121
+ }
122
+ const {
123
+ code,
124
+ signal
125
+ } = this.#exit;
126
+ return signal ?? `code ${code}`;
127
+ }
128
+ getCapturedStderr() {
129
+ return this.#stderr.join("").trim();
130
+ }
131
+ getContext() {
132
+ const context = this.#context;
133
+ !(context !== void 0) ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, `${this.name} is not registered with a CliServiceLauncher, so it has no discoverer or status server to consult`) : invariant4__default.default(false) : void 0;
134
+ return context;
135
+ }
136
+ attach(context) {
137
+ this.#context = context;
138
+ }
139
+ async start() {
140
+ if (this.#child !== void 0) {
141
+ return;
142
+ }
143
+ await this.getContext().discoverer.refresh();
144
+ !!(await this.checkHealth()).ready ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, `${this.name} is already running; stop it before starting a new one`) : invariant4__default.default(false) : void 0;
145
+ await this.#spawn();
146
+ }
147
+ async #spawn() {
148
+ this.#exit = void 0;
149
+ this.#stderr = [];
150
+ const child = child_process.spawn(this.#foundryCliPath, [...this.getArgs()], {
151
+ cwd: this.#projectPath
152
+ });
153
+ this.#child = child;
154
+ child.stderr.setEncoding("utf-8");
155
+ child.stderr.on("data", (chunk) => {
156
+ if (this.#stderr.push(chunk) > MAX_CAPTURED_STDERR_CHUNKS) {
157
+ this.#stderr.shift();
158
+ }
159
+ });
160
+ child.stdout.resume();
161
+ child.on("exit", (code, signal) => {
162
+ this.#exit = {
163
+ code,
164
+ signal
165
+ };
166
+ });
167
+ await new Promise((resolve2, reject) => {
168
+ child.once("spawn", resolve2);
169
+ child.once("error", (error) => reject(new Error(`Failed to spawn ${this.name} via ${this.#foundryCliPath}`, {
170
+ cause: error
171
+ })));
172
+ });
173
+ }
174
+ async checkHealth() {
175
+ const {
176
+ discoverer,
177
+ statusServer
178
+ } = this.getContext();
179
+ const discovery = discoverer.get(this.name);
180
+ if (typeof discovery === "undefined") {
181
+ return {
182
+ service: this.name,
183
+ state: "UNDISCOVERED",
184
+ ready: false,
185
+ terminal: false
186
+ };
187
+ }
188
+ const status = await statusServer.getServiceStatus(this.name);
189
+ const state = status?.status ?? "DISCOVERED";
190
+ return {
191
+ service: this.name,
192
+ state,
193
+ ready: state === "READY",
194
+ terminal: TERMINAL_STATES.has(state),
195
+ url: discovery.url,
196
+ ...status?.message != null ? {
197
+ message: status.message
198
+ } : {}
199
+ };
200
+ }
201
+ /**
202
+ * Signal the service and wait for the process to actually be gone.
203
+ */
204
+ async stop() {
205
+ const child = this.#child;
206
+ this.#child = void 0;
207
+ if (child === void 0 || child.exitCode != null || child.signalCode != null) {
208
+ return;
209
+ }
210
+ const exited = events.once(child, "exit");
211
+ child.kill("SIGINT");
212
+ const escalation = setTimeout(() => {
213
+ child.kill("SIGKILL");
214
+ }, DEFAULT_STOP_GRACE_MS);
215
+ try {
216
+ await exited;
217
+ } finally {
218
+ clearTimeout(escalation);
219
+ }
220
+ }
221
+ };
222
+
223
+ // src/utils/process.ts
224
+ var isProcessAlive = (pid) => {
225
+ try {
226
+ process.kill(pid, 0);
227
+ return true;
228
+ } catch (err) {
229
+ return err.code === "EPERM";
230
+ }
231
+ };
232
+
233
+ // src/utils/service.ts
234
+ var SERVICE_FILE_NAMES = {
235
+ ONTOLOGY: "ontology",
236
+ TYPESCRIPT_FUNCTIONS: "typescript-functions",
237
+ PYTHON_FUNCTIONS: "python-functions",
238
+ APP: "app",
239
+ STATUS_SERVER: "status-server",
240
+ PLATFORM_API_PROXY: "platform-api-proxy"
241
+ };
242
+ var SERVICE_NAMES_BY_FILE_NAME = new Map(Object.entries(SERVICE_FILE_NAMES).map(([name, stem]) => [stem, name]));
243
+ var serviceNameFromFileName = (fileName) => {
244
+ const name = fileName.match(/^\.?(?<name>.+)-discovery\.json$/u)?.groups?.name;
245
+ return name === void 0 ? void 0 : SERVICE_NAMES_BY_FILE_NAME.get(name);
246
+ };
247
+
248
+ // src/cli-service/ServiceDiscoverer.ts
249
+ var ServiceDiscoverer = class {
250
+ #basePath;
251
+ #discovered = /* @__PURE__ */ new Map();
252
+ constructor(args) {
253
+ this.#basePath = args.basePath;
254
+ }
255
+ get discoveryDir() {
256
+ return this.#basePath;
257
+ }
258
+ async start() {
259
+ await fs3__namespace.mkdir(this.#basePath, {
260
+ recursive: true
261
+ });
262
+ await this.refresh();
263
+ }
264
+ /**
265
+ * Re-read every discovery file in the directory.
266
+ */
267
+ async refresh() {
268
+ const fileNames = await fs3__namespace.readdir(this.#basePath);
269
+ const discoveries = await Promise.all(fileNames.map((fileName) => this.#read(fileName)));
270
+ return discoveries.filter((discovery) => discovery !== void 0);
271
+ }
272
+ get(name) {
273
+ const discovery = this.#discovered.get(name);
274
+ if (typeof discovery === "undefined") {
275
+ return void 0;
276
+ }
277
+ if (!isProcessAlive(discovery.pid)) {
278
+ this.#discovered.delete(name);
279
+ return void 0;
280
+ }
281
+ return discovery;
282
+ }
283
+ isDiscovered(name) {
284
+ return this.get(name) !== void 0;
285
+ }
286
+ all() {
287
+ const live = /* @__PURE__ */ new Map();
288
+ for (const name of this.#discovered.keys()) {
289
+ const discovery = this.get(name);
290
+ if (discovery !== void 0) {
291
+ live.set(name, discovery);
292
+ }
293
+ }
294
+ return live;
295
+ }
296
+ stop() {
297
+ this.#discovered.clear();
298
+ }
299
+ async #read(fileName) {
300
+ const serviceName = serviceNameFromFileName(fileName);
301
+ if (typeof serviceName === "undefined") {
302
+ return void 0;
303
+ }
304
+ let contents;
305
+ try {
306
+ contents = await fs3__namespace.readFile(path4.join(this.#basePath, fileName), "utf-8");
307
+ } catch {
308
+ this.#discovered.delete(serviceName);
309
+ return void 0;
310
+ }
311
+ let parsed;
312
+ try {
313
+ parsed = JSON.parse(contents);
314
+ } catch {
315
+ return void 0;
316
+ }
317
+ !parsed.url ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, `Service URL not found`) : invariant4__default.default(false) : void 0;
318
+ this.#discovered.set(serviceName, parsed);
319
+ return parsed;
320
+ }
321
+ };
322
+
323
+ // src/generated/cli/OntologySeedingService.ts
324
+ var OntologySeedingService_exports = {};
325
+ __export(OntologySeedingService_exports, {
326
+ setSeed: () => setSeed
327
+ });
328
+ async function setSeed(ctx, seed) {
329
+ return conjureLite.conjureFetch(ctx, `/seed`, "PUT", seed);
330
+ }
331
+
332
+ // src/generated/cli/StatusService.ts
333
+ var StatusService_exports = {};
334
+ __export(StatusService_exports, {
335
+ getStatus: () => getStatus,
336
+ publishStatus: () => publishStatus
337
+ });
338
+ async function publishStatus(ctx, status) {
339
+ return conjureLite.conjureFetch(ctx, `/status`, "POST", status);
340
+ }
341
+ async function getStatus(ctx) {
342
+ return conjureLite.conjureFetch(ctx, `/status`, "GET");
343
+ }
344
+
345
+ // src/cli-service/StatusServer.ts
346
+ var HEALTH_REQUEST_TIMEOUT_MS = 2e3;
347
+ var StatusServer = class extends FoundryCliService {
348
+ /** Shared by callers that ask while a fetch is already outstanding. */
349
+ #inFlight;
350
+ constructor(config) {
351
+ super("STATUS_SERVER", config);
352
+ }
353
+ getArgs() {
354
+ return ["start", "status-server", "--discovery-path", path4__default.default.resolve(this.getProjectPath(), ".palantir")];
355
+ }
356
+ async checkHealth() {
357
+ const discovery = this.getContext().discoverer.get("STATUS_SERVER");
358
+ if (discovery === void 0) {
359
+ return {
360
+ service: "STATUS_SERVER",
361
+ state: "UNDISCOVERED",
362
+ ready: false,
363
+ terminal: false
364
+ };
365
+ }
366
+ const healthy = await this.#isHealthy(discovery.url);
367
+ return {
368
+ service: "STATUS_SERVER",
369
+ state: healthy ? "READY" : "DISCOVERED",
370
+ ready: healthy,
371
+ terminal: false,
372
+ url: discovery.url
373
+ };
374
+ }
375
+ async #isHealthy(baseUrl) {
376
+ try {
377
+ const response = await fetch(new URL("/api/health", baseUrl), {
378
+ signal: AbortSignal.timeout(HEALTH_REQUEST_TIMEOUT_MS)
379
+ });
380
+ if (!response.ok) {
381
+ return false;
382
+ }
383
+ const body = await response.json();
384
+ return body.status === "healthy";
385
+ } catch {
386
+ return false;
387
+ }
388
+ }
389
+ /**
390
+ * The lifecycle snapshot for every service.
391
+ */
392
+ async getServiceStatuses() {
393
+ const baseUrl = this.getContext().discoverer.get("STATUS_SERVER")?.url;
394
+ !baseUrl ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, "Cannot get service status because the status server is not discovered yet") : invariant4__default.default(false) : void 0;
395
+ this.#inFlight ??= StatusService_exports.getStatus({
396
+ baseUrl,
397
+ servicePath: ""
398
+ }).catch(() => void 0).finally(() => {
399
+ this.#inFlight = void 0;
400
+ });
401
+ const result = await this.#inFlight;
402
+ return result ?? [];
403
+ }
404
+ async getServiceStatus(name) {
405
+ const statuses = await this.getServiceStatuses();
406
+ return statuses.find(({
407
+ service
408
+ }) => service === name);
409
+ }
410
+ };
411
+
412
+ // src/cli-service/CliServiceLauncher.ts
413
+ var HEALTH_POLL_INTERVAL_MS = 200;
414
+ var CliServiceLauncher = class {
415
+ #discoverer;
416
+ #statusServer;
417
+ #services = /* @__PURE__ */ new Map();
418
+ /** Memoized in-flight starts, so a shared dependency is started once. */
419
+ #starts = /* @__PURE__ */ new Map();
420
+ #started = false;
421
+ constructor(config = {}) {
422
+ const projectDir = config.projectDir ?? process.cwd();
423
+ this.#discoverer = new ServiceDiscoverer({
424
+ basePath: path4__default.default.resolve(projectDir, ".palantir")
425
+ });
426
+ this.#statusServer = config.statusServer ?? new StatusServer({
427
+ projectPath: projectDir,
428
+ foundryCliPath: config.foundryCliPath
429
+ });
430
+ this.register(this.#statusServer);
431
+ }
432
+ get services() {
433
+ return [...this.#services.values()];
434
+ }
435
+ get(name) {
436
+ return this.#services.get(name);
437
+ }
438
+ register(service) {
439
+ const existing = this.#services.get(service.name);
440
+ if (existing === service) {
441
+ return service;
442
+ }
443
+ !(existing === void 0) ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, `A different ${service.name} service is already registered`) : invariant4__default.default(false) : void 0;
444
+ this.#services.set(service.name, service);
445
+ service.attach({
446
+ discoverer: this.#discoverer,
447
+ statusServer: this.#statusServer
448
+ });
449
+ for (const dependency of service.dependencies) {
450
+ this.register(dependency);
451
+ }
452
+ return service;
453
+ }
454
+ async start() {
455
+ await this.#ensureDiscovering();
456
+ this.#assertAcyclic();
457
+ const results = await Promise.allSettled([...this.#services.values()].map((service) => this.#start(service)));
458
+ const failed = results.find((result) => result.status === "rejected");
459
+ if (failed !== void 0) {
460
+ await this.stop();
461
+ throw failed.reason;
462
+ }
463
+ }
464
+ async startService(service) {
465
+ this.register(service);
466
+ await this.#ensureDiscovering();
467
+ this.#assertAcyclic();
468
+ await this.#start(service);
469
+ }
470
+ async #ensureDiscovering() {
471
+ if (!this.#started) {
472
+ await this.#discoverer.start();
473
+ this.#started = true;
474
+ }
475
+ }
476
+ async #start(service) {
477
+ const runningPromise = this.#starts.get(service.name);
478
+ if (typeof runningPromise !== "undefined") {
479
+ return runningPromise;
480
+ }
481
+ const started = this.#startImmediate(service);
482
+ this.#starts.set(service.name, started);
483
+ try {
484
+ await started;
485
+ } catch (err) {
486
+ this.#starts.delete(service.name);
487
+ throw err;
488
+ }
489
+ }
490
+ async #startImmediate(service) {
491
+ await Promise.all(this.#prerequisites(service).map((dependency) => this.#start(dependency)));
492
+ await service.start();
493
+ await this.waitUntilReady(service);
494
+ }
495
+ /**
496
+ * Everything that must be ready before `service` may start: its declared
497
+ * dependencies, plus the status server.
498
+ */
499
+ #prerequisites(service) {
500
+ return service.name === this.#statusServer.name ? service.dependencies : [this.#statusServer, ...service.dependencies];
501
+ }
502
+ /**
503
+ * Poll a service's health until it is ready, reaches a terminal state, or
504
+ * exceeds its timeout.
505
+ */
506
+ async waitUntilReady(service) {
507
+ const deadline = Date.now() + DEFAULT_READY_TIMEOUT_MS;
508
+ let health = await service.checkHealth();
509
+ while (!health.ready) {
510
+ const current = health;
511
+ const notReady = () => `${service.name} is not ready (${current.state})${formatError(service, current)}`;
512
+ !!current.terminal ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, notReady) : invariant4__default.default(false) : void 0;
513
+ !(service.getExitInfo() === void 0) ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, notReady) : invariant4__default.default(false) : void 0;
514
+ !(Date.now() < deadline) ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, () => `${service.name} is not ready (${current.state}) within ${DEFAULT_READY_TIMEOUT_MS}ms${formatError(service, current)}`) : invariant4__default.default(false) : void 0;
515
+ await promises.setTimeout(HEALTH_POLL_INTERVAL_MS);
516
+ await this.#discoverer.refresh();
517
+ health = await service.checkHealth();
518
+ }
519
+ return health;
520
+ }
521
+ async checkHealth() {
522
+ await this.#discoverer.refresh();
523
+ return Promise.all([...this.#services.values()].map((service) => service.checkHealth()));
524
+ }
525
+ async checkDependencies(service) {
526
+ await this.#discoverer.refresh();
527
+ return Promise.all(service.dependencies.map((dependency) => dependency.checkHealth()));
528
+ }
529
+ async dependenciesReady(service) {
530
+ const healths = await this.checkDependencies(service);
531
+ return healths.every(({
532
+ ready
533
+ }) => ready);
534
+ }
535
+ /**
536
+ * Stop every service, dependents before their dependencies, waiting for each
537
+ * process to be gone before the next is signalled.
538
+ */
539
+ async stop() {
540
+ for (const service of this.#startupOrder().reverse()) {
541
+ await service.stop();
542
+ }
543
+ this.#starts.clear();
544
+ this.#discoverer.stop();
545
+ this.#started = false;
546
+ }
547
+ /**
548
+ * Registered services in a valid startup order: every service appears after
549
+ * everything it depends on.
550
+ */
551
+ #startupOrder() {
552
+ const ordered = [];
553
+ const seen = /* @__PURE__ */ new Set();
554
+ const visit = (service) => {
555
+ if (seen.has(service.name)) {
556
+ return;
557
+ }
558
+ seen.add(service.name);
559
+ for (const dependency of this.#prerequisites(service)) {
560
+ visit(dependency);
561
+ }
562
+ ordered.push(service);
563
+ };
564
+ for (const service of this.#services.values()) {
565
+ visit(service);
566
+ }
567
+ return ordered;
568
+ }
569
+ #assertAcyclic() {
570
+ const settled = /* @__PURE__ */ new Set();
571
+ const visit = (service, walkedPath) => {
572
+ if (settled.has(service.name)) {
573
+ return;
574
+ }
575
+ const cycleStart = walkedPath.indexOf(service.name);
576
+ !(cycleStart === -1) ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, () => `Dependency cycle between services: ${[...walkedPath.slice(cycleStart), service.name].join(" -> ")}`) : invariant4__default.default(false) : void 0;
577
+ for (const dependency of this.#prerequisites(service)) {
578
+ visit(dependency, [...walkedPath, service.name]);
579
+ }
580
+ settled.add(service.name);
581
+ };
582
+ for (const service of this.#services.values()) {
583
+ visit(service, []);
584
+ }
585
+ }
586
+ };
587
+ var formatError = (service, health) => {
588
+ const parts = [];
589
+ if (health.message != null && health.message.length > 0) {
590
+ parts.push(health.message);
591
+ }
592
+ const exit = service.getExitInfo();
593
+ if (exit !== void 0) {
594
+ parts.push(`the process exited (${exit})`);
595
+ }
596
+ const stderr = service.getCapturedStderr();
597
+ if (stderr.length > 0) {
598
+ parts.push(`stderr:
599
+ ${stderr}`);
600
+ }
601
+ return parts.length === 0 ? "" : `: ${parts.join("; ")}`;
602
+ };
603
+ var OntologyServer = class extends FoundryCliService {
604
+ #metadataPath;
605
+ constructor(config) {
606
+ super("ONTOLOGY", config);
607
+ this.#metadataPath = path4.isAbsolute(config.metadataPath) ? config.metadataPath : path4.resolve(config.projectPath, config.metadataPath);
608
+ }
609
+ getMetadataPath() {
610
+ return this.#metadataPath;
611
+ }
612
+ getArgs() {
613
+ return ["start", "ontology", "--skip-build", "--metadata", this.#metadataPath, "--discovery-path", path4__default.default.resolve(this.getProjectPath(), ".palantir")];
614
+ }
615
+ };
616
+ function createSeedClient(config) {
617
+ const {
618
+ baseUrl,
619
+ metadata,
620
+ fetchFn
621
+ } = config;
622
+ const builder = new seedHelpers.SeedBuilder(metadata);
623
+ const seedClientFunction = async (seed) => {
624
+ let result;
625
+ if (typeof seed === "function") {
626
+ result = seed(builder);
627
+ } else {
628
+ builder.addAll(seed);
629
+ }
630
+ await applySeed(builder.build());
631
+ return result;
632
+ };
633
+ const applySeed = async (seed) => {
634
+ const res = await OntologySeedingService_exports.setSeed({
635
+ baseUrl,
636
+ servicePath: "/api",
637
+ fetchFn
638
+ }, seed);
639
+ return res;
640
+ };
641
+ const seedClientUtils = {
642
+ ref: (o, pk) => builder.ref(o, pk),
643
+ addAll: async (output) => {
644
+ builder.addAll(output);
645
+ await applySeed(builder.build());
646
+ },
647
+ set: async (output) => {
648
+ builder.set(output);
649
+ await applySeed(builder.build());
650
+ },
651
+ create: async (o, props) => {
652
+ const ref = builder.create(o, props);
653
+ await applySeed(builder.build());
654
+ return ref;
655
+ },
656
+ update: async (ref, props) => {
657
+ const returnedRef = builder.update(ref, props);
658
+ await applySeed(builder.build());
659
+ return returnedRef;
660
+ },
661
+ delete: async (ref) => {
662
+ builder.delete(ref);
663
+ await applySeed(builder.build());
664
+ },
665
+ link: async (source, apiName, target) => {
666
+ builder.link(source, apiName, target);
667
+ await applySeed(builder.build());
668
+ },
669
+ unlink: async (source, apiName, target) => {
670
+ builder.unlink(source, apiName, target);
671
+ await applySeed(builder.build());
672
+ }
673
+ };
674
+ return Object.defineProperties(seedClientFunction, Object.getOwnPropertyDescriptors(seedClientUtils));
675
+ }
676
+
677
+ // src/utils/empty-ontology-block.ts
678
+ var EMPTY_ONTOLOGY_BLOCK_DATA = {
679
+ objectTypes: {},
680
+ sharedPropertyTypes: {},
681
+ interfaceTypes: {},
682
+ linkTypes: {},
683
+ actionTypes: {},
684
+ blockPermissionInformation: {
685
+ actionTypes: {},
686
+ objectTypes: {},
687
+ linkTypes: {},
688
+ interfaceTypes: {},
689
+ sharedPropertyTypes: {}
690
+ },
691
+ blockOutputCompassLocations: {},
692
+ knownIdentifiers: {
693
+ actionParameterIds: {},
694
+ actionParameters: {},
695
+ actionTypes: {},
696
+ datasourceColumns: {},
697
+ datasources: {},
698
+ filesDatasources: {},
699
+ functions: {},
700
+ geotimeSeriesSyncs: {},
701
+ groupIds: {},
702
+ interfaceActionTypeConstraints: {},
703
+ interfaceLinkTypes: {},
704
+ interfaceParameterConstraints: {},
705
+ interfacePropertyTypes: {},
706
+ interfaceTypes: {},
707
+ linkTypeIds: {},
708
+ linkTypes: {},
709
+ markings: {},
710
+ objectTypeIds: {},
711
+ objectTypes: {},
712
+ propertyTypeIds: {},
713
+ propertyTypes: {},
714
+ sharedPropertyTypes: {},
715
+ timeSeriesSyncs: {},
716
+ valueTypes: {},
717
+ webhooks: {},
718
+ workshopModules: {}
719
+ },
720
+ ruleSets: {}
721
+ };
722
+
723
+ // src/createIntegrationServer.ts
724
+ var transformMetadata = (metadata) => {
725
+ const transformed = generatorConverters_preview.PreviewOntologyIrConverter.getPreviewFullMetadataFromBlockData(EMPTY_ONTOLOGY_BLOCK_DATA, metadata);
726
+ return {
727
+ ...transformed,
728
+ ontology: metadata.ontology
729
+ };
730
+ };
731
+ var writeMetadata = async (args) => {
732
+ const {
733
+ metadataPath,
734
+ previewMetadata
735
+ } = args;
736
+ const stringified = JSON.stringify(previewMetadata);
737
+ await fs3__namespace.default.writeFile(metadataPath, stringified);
738
+ };
739
+ async function createIntegrationServer(config) {
740
+ const {
741
+ metadata,
742
+ projectPath = process.cwd(),
743
+ foundryCliPath
744
+ } = config;
745
+ const testPath = await fs3__namespace.default.mkdtemp(path4__default.default.join(projectPath, ".test-run-"));
746
+ const metadataPath = path4__default.default.resolve(testPath, "ontology-metadata.json");
747
+ const previewMetadata = transformMetadata(metadata);
748
+ const serviceLauncher = new CliServiceLauncher({
749
+ projectDir: testPath,
750
+ foundryCliPath
751
+ });
752
+ await writeMetadata({
753
+ previewMetadata,
754
+ metadataPath
755
+ });
756
+ const ontology = serviceLauncher.register(new OntologyServer({
757
+ projectPath: testPath,
758
+ metadataPath,
759
+ foundryCliPath
760
+ }));
761
+ let client;
762
+ let seedClient;
763
+ return {
764
+ getOntologyUrl: () => ontology.getUrl(),
765
+ start: async () => {
766
+ await serviceLauncher.start();
767
+ },
768
+ stop: async () => {
769
+ await serviceLauncher.stop();
770
+ await fs3__namespace.default.rm(testPath, {
771
+ recursive: true,
772
+ force: true
773
+ });
774
+ },
775
+ getSeedClient: async () => {
776
+ if (seedClient !== void 0) return seedClient;
777
+ const baseUrl = ontology.getUrl();
778
+ !baseUrl ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, "Ontology server is not ready") : invariant4__default.default(false) : void 0;
779
+ const caCertPath = ontology.getCaCertPath();
780
+ !caCertPath ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, "Ontology server is not ready") : invariant4__default.default(false) : void 0;
781
+ const agent = new undici.Agent({
782
+ connect: caCertPath ? {
783
+ ca: await fs3__namespace.default.readFile(caCertPath)
784
+ } : {
785
+ rejectUnauthorized: false
786
+ }
787
+ });
788
+ const fetchWithCert = (input, init) => undici.fetch(input, {
789
+ ...init,
790
+ dispatcher: agent
791
+ });
792
+ return seedClient = createSeedClient({
793
+ baseUrl,
794
+ metadata,
795
+ fetchFn: fetchWithCert
796
+ });
797
+ },
798
+ getClient: async () => {
799
+ if (client !== void 0) return client;
800
+ const baseUrl = ontology.getUrl();
801
+ !baseUrl ? process.env.NODE_ENV !== "production" ? invariant4__default.default(false, "Ontology server is not ready") : invariant4__default.default(false) : void 0;
802
+ return client = await createIntegrationClient({
803
+ baseUrl,
804
+ metadata,
805
+ caCertPath: ontology.getCaCertPath()
806
+ });
807
+ }
808
+ };
809
+ }
810
+ var MIN_FOUNDRY_CLI_VERSION = "0.224.0";
811
+ var FOUNDRY_VERSION_PATTERN = /cli (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)/u;
812
+ var parseFoundryVersion = (stdout) => stdout.trim().match(FOUNDRY_VERSION_PATTERN)?.[1];
813
+ var versionIsAtMinimum = (version, min) => semver.valid(version) != null && semver.valid(min) != null && semver.gte(version, min);
814
+ var execAsync = util.promisify(child_process.exec);
815
+ var checkFoundryCliVersion = () => {
816
+ return execAsync("which foundry").then(() => execAsync("foundry --version")).then(({
817
+ stdout
818
+ }) => {
819
+ const version = parseFoundryVersion(stdout);
820
+ if (!version) return {
821
+ type: "version-error"
822
+ };
823
+ const isMin = versionIsAtMinimum(version, MIN_FOUNDRY_CLI_VERSION);
824
+ if (!isMin) return {
825
+ type: "version-incompatible",
826
+ version
827
+ };
828
+ return {
829
+ type: "installed",
830
+ version
831
+ };
832
+ }).catch(() => ({
833
+ type: "not-found"
834
+ }));
835
+ };
836
+
837
+ exports.checkFoundryCliVersion = checkFoundryCliVersion;
838
+ exports.createIntegrationClient = createIntegrationClient;
839
+ exports.createIntegrationServer = createIntegrationServer;
840
+ //# sourceMappingURL=index.cjs.map
841
+ //# sourceMappingURL=index.cjs.map