@semiont/core 0.3.3 → 0.3.5
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/README.md +14 -0
- package/dist/config/node-config-loader.d.ts +56 -0
- package/dist/config/node-config-loader.js +300 -0
- package/dist/config/node-config-loader.js.map +1 -0
- package/dist/config.types-Cas714K0.d.ts +561 -0
- package/dist/index.d.ts +73 -787
- package/dist/index.js +206 -1807
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -15,6 +15,20 @@ Core types and domain logic for the Semiont semantic knowledge platform. This pa
|
|
|
15
15
|
- ✅ **Backend** (`apps/backend`) - Server implementation, imports types from core
|
|
16
16
|
- ✅ **Packages** - Other monorepo packages that need OpenAPI types or EventBus
|
|
17
17
|
- ✅ **Internal Utilities** - Type generation, validation, domain logic
|
|
18
|
+
- ✅ **Frontend / Browser** - Types and pure utilities (main barrel is browser-safe)
|
|
19
|
+
|
|
20
|
+
## Who Should Use `@semiont/core/node` Instead
|
|
21
|
+
|
|
22
|
+
Node.js-specific exports live in the `/node` subpath:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { SemiontProject, loadEnvironmentConfig } from '@semiont/core/node';
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- **`SemiontProject`** — represents a project on the filesystem; resolves XDG directories, reads/writes files. Not usable in a browser.
|
|
29
|
+
- **`loadEnvironmentConfig`** — loads `~/.semiontconfig` + `.semiont/config` using `fs`/`os`/`path`. Not usable in a browser.
|
|
30
|
+
|
|
31
|
+
**Rule**: If your code runs in a browser or edge runtime, use `@semiont/core`. If it runs in Node.js and needs filesystem access, use `@semiont/core/node`.
|
|
18
32
|
|
|
19
33
|
## Who Should Use `@semiont/api-client` Instead
|
|
20
34
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { E as EnvironmentConfig } from '../config.types-Cas714K0.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Semiont project rooted at a given directory.
|
|
5
|
+
*
|
|
6
|
+
* Computes all paths — durable and ephemeral — once at construction time.
|
|
7
|
+
* XDG environment variables are read here and nowhere else.
|
|
8
|
+
*
|
|
9
|
+
* Durable paths (inside the project root, committed or repo-local):
|
|
10
|
+
* eventsDir — .semiont/events/ (system of record, committed)
|
|
11
|
+
* dataDir — projectRoot/ (representations store root; representations/ is committed)
|
|
12
|
+
*
|
|
13
|
+
* Ephemeral paths (outside the project root, never committed):
|
|
14
|
+
* configDir — $XDG_CONFIG_HOME/semiont/{name}/ (generated config for managed processes)
|
|
15
|
+
* dataHome — $XDG_DATA_HOME/semiont/{name}/ (persistent user data, e.g. database files)
|
|
16
|
+
* stateDir — $XDG_STATE_HOME/semiont/{name}/
|
|
17
|
+
* projectionsDir — stateDir/projections/
|
|
18
|
+
* jobsDir — stateDir/jobs/
|
|
19
|
+
* runtimeDir — $XDG_RUNTIME_DIR/semiont/{name}/ (or $TMPDIR fallback)
|
|
20
|
+
*/
|
|
21
|
+
declare class SemiontProject {
|
|
22
|
+
readonly root: string;
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly eventsDir: string;
|
|
25
|
+
readonly dataDir: string;
|
|
26
|
+
readonly configDir: string;
|
|
27
|
+
readonly dataHome: string;
|
|
28
|
+
readonly stateDir: string;
|
|
29
|
+
readonly projectionsDir: string;
|
|
30
|
+
readonly jobsDir: string;
|
|
31
|
+
readonly runtimeDir: string;
|
|
32
|
+
constructor(projectRoot: string, name?: string);
|
|
33
|
+
/**
|
|
34
|
+
* Delete all ephemeral state for this project (stateDir + runtimeDir).
|
|
35
|
+
* Does not touch eventsDir or dataDir.
|
|
36
|
+
*/
|
|
37
|
+
destroy(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Read the project name from .semiont/config [project] name = "..."
|
|
40
|
+
* Falls back to the directory basename if the config is absent or has no name.
|
|
41
|
+
*/
|
|
42
|
+
private static readName;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load semiont environment config for a Node.js process.
|
|
47
|
+
*
|
|
48
|
+
* Reads ~/.semiontconfig (global) merged with .semiont/config (project-local),
|
|
49
|
+
* then selects the given environment overlay.
|
|
50
|
+
*
|
|
51
|
+
* This is the canonical config loader for any Node.js process. SEMIONT_ENV
|
|
52
|
+
* should be read once at the process entry point and passed as `environment`.
|
|
53
|
+
*/
|
|
54
|
+
declare function loadEnvironmentConfig(projectRoot: string, environment: string): EnvironmentConfig;
|
|
55
|
+
|
|
56
|
+
export { SemiontProject, loadEnvironmentConfig };
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { parse } from 'smol-toml';
|
|
5
|
+
|
|
6
|
+
// src/config/node-config-loader.ts
|
|
7
|
+
function deepMerge(base, override) {
|
|
8
|
+
const result = { ...base };
|
|
9
|
+
for (const key of Object.keys(override)) {
|
|
10
|
+
const b = base[key];
|
|
11
|
+
const o = override[key];
|
|
12
|
+
if (o !== void 0 && o !== null && typeof o === "object" && !Array.isArray(o) && b !== void 0 && b !== null && typeof b === "object" && !Array.isArray(b)) {
|
|
13
|
+
result[key] = deepMerge(b, o);
|
|
14
|
+
} else if (o !== void 0) {
|
|
15
|
+
result[key] = o;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
function resolveEnvVars(obj, env) {
|
|
21
|
+
if (typeof obj === "string") {
|
|
22
|
+
return obj.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
23
|
+
if (env[varName] === void 0) {
|
|
24
|
+
throw new Error(`Environment variable ${varName} is not set (referenced in config as ${match})`);
|
|
25
|
+
}
|
|
26
|
+
return env[varName];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(obj)) {
|
|
30
|
+
return obj.map((item) => resolveEnvVars(item, env));
|
|
31
|
+
}
|
|
32
|
+
if (obj !== null && typeof obj === "object") {
|
|
33
|
+
const resolved = {};
|
|
34
|
+
for (const key in obj) {
|
|
35
|
+
resolved[key] = resolveEnvVars(obj[key], env);
|
|
36
|
+
}
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
function requirePlatform(value, serviceName) {
|
|
42
|
+
if (!value) {
|
|
43
|
+
throw new Error(`platform is required for service '${serviceName}' \u2014 add 'platform = "posix"|"container"|"external"' to its config section`);
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
function loadTomlConfig(projectRoot, environment, globalConfigPath, reader, env) {
|
|
48
|
+
const projectConfigContent = reader.readIfExists(`${projectRoot}/.semiont/config`);
|
|
49
|
+
let projectName = "semiont-project";
|
|
50
|
+
let projectSite;
|
|
51
|
+
let projectEnvSection = {};
|
|
52
|
+
if (projectConfigContent) {
|
|
53
|
+
const projectConfig = parse(projectConfigContent);
|
|
54
|
+
projectName = projectConfig.project?.name ?? projectName;
|
|
55
|
+
projectSite = projectConfig.site;
|
|
56
|
+
projectEnvSection = projectConfig.environments?.[environment] ?? {};
|
|
57
|
+
}
|
|
58
|
+
const globalContent = reader.readIfExists(globalConfigPath);
|
|
59
|
+
const raw = globalContent ? parse(globalContent) : {};
|
|
60
|
+
const userEnvSection = raw.environments?.[environment] ?? {};
|
|
61
|
+
const envSection = deepMerge(
|
|
62
|
+
projectEnvSection,
|
|
63
|
+
userEnvSection
|
|
64
|
+
);
|
|
65
|
+
const resolved = resolveEnvVars(envSection, env);
|
|
66
|
+
const flatInference = resolved.inference;
|
|
67
|
+
const makeMeaningSection = resolved["make-meaning"];
|
|
68
|
+
const workersSection = resolved.workers ?? {};
|
|
69
|
+
const actorsSection = resolved.actors ?? {};
|
|
70
|
+
const defaultWorkerInference = workersSection["default"]?.inference;
|
|
71
|
+
const defaultMakeMeaningInference = makeMeaningSection?.default?.inference;
|
|
72
|
+
function mergeWithFlatInference(specific) {
|
|
73
|
+
if (!flatInference) return specific;
|
|
74
|
+
return {
|
|
75
|
+
apiKey: flatInference.apiKey,
|
|
76
|
+
maxTokens: flatInference.maxTokens,
|
|
77
|
+
endpoint: flatInference.endpoint,
|
|
78
|
+
baseURL: flatInference.baseURL,
|
|
79
|
+
...specific
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function resolveActorInference(fromMakeMeaning, fromActors) {
|
|
83
|
+
const base = fromMakeMeaning ?? fromActors ?? defaultMakeMeaningInference;
|
|
84
|
+
if (!base) return void 0;
|
|
85
|
+
return mergeWithFlatInference(base);
|
|
86
|
+
}
|
|
87
|
+
const actors = {};
|
|
88
|
+
const gathererInference = resolveActorInference(
|
|
89
|
+
makeMeaningSection?.actors?.gatherer?.inference,
|
|
90
|
+
actorsSection["gatherer"]?.inference
|
|
91
|
+
);
|
|
92
|
+
if (gathererInference) actors.gatherer = gathererInference;
|
|
93
|
+
const matcherInference = resolveActorInference(
|
|
94
|
+
makeMeaningSection?.actors?.matcher?.inference,
|
|
95
|
+
actorsSection["matcher"]?.inference
|
|
96
|
+
);
|
|
97
|
+
if (matcherInference) actors.matcher = matcherInference;
|
|
98
|
+
const workers = {};
|
|
99
|
+
const workerTypes = ["reference-annotation", "highlight-annotation", "assessment-annotation", "comment-annotation", "tag-annotation", "generation"];
|
|
100
|
+
if (defaultWorkerInference) {
|
|
101
|
+
workers.default = mergeWithFlatInference(defaultWorkerInference);
|
|
102
|
+
}
|
|
103
|
+
for (const wt of workerTypes) {
|
|
104
|
+
const specific = workersSection[wt]?.inference;
|
|
105
|
+
if (specific) {
|
|
106
|
+
workers[wt] = mergeWithFlatInference(specific);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const backend = resolved.backend;
|
|
110
|
+
const site = resolved.site ?? projectSite;
|
|
111
|
+
const inferenceSection = resolved.inference;
|
|
112
|
+
let inferenceProviders;
|
|
113
|
+
if (inferenceSection) {
|
|
114
|
+
inferenceProviders = {};
|
|
115
|
+
if (inferenceSection.type === "anthropic") {
|
|
116
|
+
inferenceProviders.anthropic = {
|
|
117
|
+
platform: requirePlatform(inferenceSection.platform, "inference"),
|
|
118
|
+
endpoint: inferenceSection.endpoint ?? "https://api.anthropic.com",
|
|
119
|
+
apiKey: inferenceSection.apiKey ?? ""
|
|
120
|
+
};
|
|
121
|
+
} else if (inferenceSection.type === "ollama") {
|
|
122
|
+
inferenceProviders.ollama = {
|
|
123
|
+
platform: { type: requirePlatform(inferenceSection.platform, "inference") },
|
|
124
|
+
baseURL: inferenceSection.baseURL,
|
|
125
|
+
port: inferenceSection.baseURL ? void 0 : 11434
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const topLevelWorkers = {};
|
|
130
|
+
for (const [name, w] of Object.entries(workersSection)) {
|
|
131
|
+
if (w.inference) {
|
|
132
|
+
topLevelWorkers[name] = { inference: { type: w.inference.type, model: w.inference.model } };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const topLevelActors = {};
|
|
136
|
+
for (const [name, a] of Object.entries(actorsSection)) {
|
|
137
|
+
if (a.inference) {
|
|
138
|
+
topLevelActors[name] = { inference: { type: a.inference.type, model: a.inference.model } };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (makeMeaningSection?.actors?.gatherer?.inference) {
|
|
142
|
+
topLevelActors["gatherer"] = { inference: { type: makeMeaningSection.actors.gatherer.inference.type, model: makeMeaningSection.actors.gatherer.inference.model } };
|
|
143
|
+
}
|
|
144
|
+
if (makeMeaningSection?.actors?.matcher?.inference) {
|
|
145
|
+
topLevelActors["matcher"] = { inference: { type: makeMeaningSection.actors.matcher.inference.type, model: makeMeaningSection.actors.matcher.inference.model } };
|
|
146
|
+
}
|
|
147
|
+
const frontend = resolved.frontend;
|
|
148
|
+
const proxy = resolved.proxy;
|
|
149
|
+
const config = {
|
|
150
|
+
services: {
|
|
151
|
+
backend: backend ? {
|
|
152
|
+
platform: { type: requirePlatform(backend.platform, "backend") },
|
|
153
|
+
port: backend.port ?? 4e3,
|
|
154
|
+
publicURL: backend.publicURL ?? `http://localhost:${backend.port ?? 4e3}`,
|
|
155
|
+
corsOrigin: backend.corsOrigin ?? backend.frontendURL ?? "http://localhost:3000"
|
|
156
|
+
} : void 0,
|
|
157
|
+
frontend: frontend ? {
|
|
158
|
+
platform: { type: requirePlatform(frontend.platform, "frontend") },
|
|
159
|
+
port: frontend.port ?? 3e3,
|
|
160
|
+
siteName: site?.siteName ?? "Semiont",
|
|
161
|
+
publicURL: frontend.publicURL
|
|
162
|
+
} : void 0,
|
|
163
|
+
proxy: proxy ? {
|
|
164
|
+
platform: { type: requirePlatform(proxy.platform, "proxy") },
|
|
165
|
+
type: "envoy",
|
|
166
|
+
port: proxy.port ?? 8080,
|
|
167
|
+
adminPort: proxy.adminPort ?? 9901,
|
|
168
|
+
backendPort: backend?.port ?? 4e3,
|
|
169
|
+
frontendPort: frontend?.port ?? 3e3
|
|
170
|
+
} : void 0,
|
|
171
|
+
graph: resolved.graph ? {
|
|
172
|
+
...resolved.graph,
|
|
173
|
+
platform: { type: requirePlatform(resolved.graph.platform, "graph") },
|
|
174
|
+
type: resolved.graph.type ?? "neo4j"
|
|
175
|
+
} : makeMeaningSection?.graph,
|
|
176
|
+
database: resolved.database ? {
|
|
177
|
+
platform: { type: requirePlatform(resolved.database.platform, "database") },
|
|
178
|
+
type: "postgres",
|
|
179
|
+
image: resolved.database.image,
|
|
180
|
+
host: resolved.database.host ?? "localhost",
|
|
181
|
+
port: resolved.database.port ?? 5432,
|
|
182
|
+
name: resolved.database.name,
|
|
183
|
+
user: resolved.database.user,
|
|
184
|
+
password: resolved.database.password
|
|
185
|
+
} : void 0
|
|
186
|
+
},
|
|
187
|
+
...inferenceProviders ? { inference: inferenceProviders } : {},
|
|
188
|
+
...Object.keys(topLevelWorkers).length > 0 ? { workers: topLevelWorkers } : {},
|
|
189
|
+
...Object.keys(topLevelActors).length > 0 ? { actors: topLevelActors } : {},
|
|
190
|
+
site: site ? {
|
|
191
|
+
domain: site.domain ?? "localhost",
|
|
192
|
+
siteName: site.siteName,
|
|
193
|
+
adminEmail: site.adminEmail,
|
|
194
|
+
oauthAllowedDomains: site.oauthAllowedDomains
|
|
195
|
+
} : void 0,
|
|
196
|
+
logLevel: resolved.logLevel,
|
|
197
|
+
_metadata: {
|
|
198
|
+
environment,
|
|
199
|
+
projectRoot,
|
|
200
|
+
projectName,
|
|
201
|
+
...Object.keys(actors).length > 0 ? { actors } : {},
|
|
202
|
+
...Object.keys(workers).length > 0 ? { workers } : {}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
return config;
|
|
206
|
+
}
|
|
207
|
+
function createTomlConfigLoader(reader, globalConfigPath, env) {
|
|
208
|
+
return (projectRoot, environment) => {
|
|
209
|
+
return loadTomlConfig(projectRoot, environment, globalConfigPath, reader, env);
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
var SemiontProject = class _SemiontProject {
|
|
213
|
+
root;
|
|
214
|
+
name;
|
|
215
|
+
// Durable
|
|
216
|
+
eventsDir;
|
|
217
|
+
dataDir;
|
|
218
|
+
// Ephemeral — config (generated config files for managed processes)
|
|
219
|
+
configDir;
|
|
220
|
+
// Ephemeral — data (persistent user data managed by semiont)
|
|
221
|
+
dataHome;
|
|
222
|
+
// Ephemeral — state
|
|
223
|
+
stateDir;
|
|
224
|
+
projectionsDir;
|
|
225
|
+
jobsDir;
|
|
226
|
+
// Ephemeral — runtime
|
|
227
|
+
runtimeDir;
|
|
228
|
+
constructor(projectRoot, name) {
|
|
229
|
+
this.root = projectRoot;
|
|
230
|
+
if (name !== void 0) {
|
|
231
|
+
const configPath = path.join(projectRoot, ".semiont", "config");
|
|
232
|
+
if (!fs.existsSync(configPath)) {
|
|
233
|
+
fs.mkdirSync(path.join(projectRoot, ".semiont"), { recursive: true });
|
|
234
|
+
fs.writeFileSync(configPath, `[project]
|
|
235
|
+
name = "${name}"
|
|
236
|
+
`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
this.name = _SemiontProject.readName(projectRoot);
|
|
240
|
+
this.eventsDir = path.join(projectRoot, ".semiont", "events");
|
|
241
|
+
this.dataDir = projectRoot;
|
|
242
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
243
|
+
this.configDir = path.join(xdgConfig, "semiont", this.name);
|
|
244
|
+
const xdgData = process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share");
|
|
245
|
+
this.dataHome = path.join(xdgData, "semiont", this.name);
|
|
246
|
+
const xdgState = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state");
|
|
247
|
+
this.stateDir = path.join(xdgState, "semiont", this.name);
|
|
248
|
+
this.projectionsDir = path.join(this.stateDir, "projections");
|
|
249
|
+
this.jobsDir = path.join(this.stateDir, "jobs");
|
|
250
|
+
const xdgRuntime = process.env.XDG_RUNTIME_DIR;
|
|
251
|
+
const runtimeBase = xdgRuntime ?? process.env.TMPDIR ?? "/tmp";
|
|
252
|
+
this.runtimeDir = path.join(runtimeBase, "semiont", this.name);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Delete all ephemeral state for this project (stateDir + runtimeDir).
|
|
256
|
+
* Does not touch eventsDir or dataDir.
|
|
257
|
+
*/
|
|
258
|
+
async destroy() {
|
|
259
|
+
await Promise.all([
|
|
260
|
+
fs.promises.rm(this.configDir, { recursive: true, force: true }),
|
|
261
|
+
fs.promises.rm(this.stateDir, { recursive: true, force: true }),
|
|
262
|
+
fs.promises.rm(this.runtimeDir, { recursive: true, force: true })
|
|
263
|
+
]);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Read the project name from .semiont/config [project] name = "..."
|
|
267
|
+
* Falls back to the directory basename if the config is absent or has no name.
|
|
268
|
+
*/
|
|
269
|
+
static readName(projectRoot) {
|
|
270
|
+
const configPath = path.join(projectRoot, ".semiont", "config");
|
|
271
|
+
if (fs.existsSync(configPath)) {
|
|
272
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
273
|
+
for (const line of content.split("\n")) {
|
|
274
|
+
const trimmed = line.trim();
|
|
275
|
+
if (trimmed.startsWith("name") && trimmed.includes("=")) {
|
|
276
|
+
const [, ...rest] = trimmed.split("=");
|
|
277
|
+
return rest.join("=").trim().replace(/^"(.*)"$/, "$1");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return path.basename(projectRoot);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// src/config/node-config-loader.ts
|
|
286
|
+
var nodeTomlFileReader = {
|
|
287
|
+
readIfExists: (filePath) => fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : null
|
|
288
|
+
};
|
|
289
|
+
function loadEnvironmentConfig(projectRoot, environment) {
|
|
290
|
+
const globalConfigPath = path.join(os.homedir(), ".semiontconfig");
|
|
291
|
+
return createTomlConfigLoader(
|
|
292
|
+
nodeTomlFileReader,
|
|
293
|
+
globalConfigPath,
|
|
294
|
+
process.env
|
|
295
|
+
)(projectRoot, environment);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export { SemiontProject, loadEnvironmentConfig };
|
|
299
|
+
//# sourceMappingURL=node-config-loader.js.map
|
|
300
|
+
//# sourceMappingURL=node-config-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/config/toml-loader.ts","../../src/project.ts","../../src/config/node-config-loader.ts"],"names":["parseToml","fs2","path2","os2"],"mappings":";;;;;;AA0BA,SAAS,SAAA,CAA6C,MAAS,QAAA,EAAyB;AACtF,EAAA,MAAM,MAAA,GAAS,EAAE,GAAG,IAAA,EAAK;AACzB,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,MAAM,CAAA,GAAI,KAAK,GAAG,CAAA;AAClB,IAAA,MAAM,CAAA,GAAI,SAAS,GAAG,CAAA;AACtB,IAAA,IAAI,CAAA,KAAM,UAAa,CAAA,KAAM,IAAA,IAAQ,OAAO,CAAA,KAAM,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAC,KAC1E,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,IAAA,IAAQ,OAAO,CAAA,KAAM,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG;AAC/E,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,SAAA,CAAU,CAAA,EAA8B,CAA4B,CAAA;AAAA,IACpF,CAAA,MAAA,IAAW,MAAM,MAAA,EAAW;AAC1B,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,cAAA,CAAe,KAAc,GAAA,EAAkD;AACtF,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,gBAAA,EAAkB,CAAC,OAAO,OAAA,KAAY;AACvD,MAAA,IAAI,GAAA,CAAI,OAAO,CAAA,KAAM,MAAA,EAAW;AAC9B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qBAAA,EAAwB,OAAO,CAAA,qCAAA,EAAwC,KAAK,CAAA,CAAA,CAAG,CAAA;AAAA,MACjG;AACA,MAAA,OAAO,IAAI,OAAO,CAAA;AAAA,IACpB,CAAC,CAAA;AAAA,EACH;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACtB,IAAA,OAAO,IAAI,GAAA,CAAI,CAAA,IAAA,KAAQ,cAAA,CAAe,IAAA,EAAM,GAAG,CAAC,CAAA;AAAA,EAClD;AACA,EAAA,IAAI,GAAA,KAAQ,IAAA,IAAQ,OAAO,GAAA,KAAQ,QAAA,EAAU;AAC3C,IAAA,MAAM,WAAoC,EAAC;AAC3C,IAAA,KAAA,MAAW,OAAO,GAAA,EAAgC;AAChD,MAAA,QAAA,CAAS,GAAG,CAAA,GAAI,cAAA,CAAgB,GAAA,CAAgC,GAAG,GAAG,GAAG,CAAA;AAAA,IAC3E;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA;AACT;AAwHA,SAAS,eAAA,CAAgB,OAA2B,WAAA,EAAmC;AACrF,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,WAAW,CAAA,8EAAA,CAA2E,CAAA;AAAA,EAC7I;AACA,EAAA,OAAO,KAAA;AACT;AAaO,SAAS,cAAA,CACd,WAAA,EACA,WAAA,EACA,gBAAA,EACA,QACA,GAAA,EACmB;AAEnB,EAAA,MAAM,oBAAA,GAAuB,MAAA,CAAO,YAAA,CAAa,CAAA,EAAG,WAAW,CAAA,gBAAA,CAAkB,CAAA;AACjF,EAAA,IAAI,WAAA,GAAc,iBAAA;AAClB,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI,oBAAwC,EAAC;AAC7C,EAAA,IAAI,oBAAA,EAAsB;AACxB,IAAA,MAAM,aAAA,GAAgBA,MAAU,oBAAoB,CAAA;AAKpD,IAAA,WAAA,GAAc,aAAA,CAAc,SAAS,IAAA,IAAQ,WAAA;AAC7C,IAAA,WAAA,GAAc,aAAA,CAAc,IAAA;AAC5B,IAAA,iBAAA,GAAoB,aAAA,CAAc,YAAA,GAAe,WAAW,CAAA,IAAK,EAAC;AAAA,EACpE;AAGA,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,YAAA,CAAa,gBAAgB,CAAA;AAC1D,EAAA,MAAM,GAAA,GAAM,aAAA,GAAiBA,KAAA,CAAU,aAAa,IAA2B,EAAC;AAGhF,EAAA,MAAM,cAAA,GAAqC,GAAA,CAAI,YAAA,GAAe,WAAW,KAAK,EAAC;AAC/E,EAAA,MAAM,UAAA,GAAiC,SAAA;AAAA,IACrC,iBAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,MAAM,QAAA,GAAW,cAAA,CAAe,UAAA,EAAY,GAAG,CAAA;AAM/C,EAAA,MAAM,gBAAgB,QAAA,CAAS,SAAA;AAC/B,EAAA,MAAM,kBAAA,GAAqB,SAAS,cAAc,CAAA;AAClD,EAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,OAAA,IAAW,EAAC;AAC5C,EAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,MAAA,IAAU,EAAC;AAC1C,EAAA,MAAM,sBAAA,GAAyB,cAAA,CAAe,SAAS,CAAA,EAAG,SAAA;AAC1D,EAAA,MAAM,2BAAA,GAA8B,oBAAoB,OAAA,EAAS,SAAA;AAEjE,EAAA,SAAS,uBAAuB,QAAA,EAA4C;AAC1E,IAAA,IAAI,CAAC,eAAe,OAAO,QAAA;AAC3B,IAAA,OAAO;AAAA,MACL,QAAQ,aAAA,CAAc,MAAA;AAAA,MACtB,WAAW,aAAA,CAAc,SAAA;AAAA,MACzB,UAAU,aAAA,CAAc,QAAA;AAAA,MACxB,SAAS,aAAA,CAAc,OAAA;AAAA,MACvB,GAAG;AAAA,KACL;AAAA,EACF;AAEA,EAAA,SAAS,qBAAA,CAAsB,iBAAmC,UAAA,EAA2D;AAC3H,IAAA,MAAM,IAAA,GAAO,mBAAmB,UAAA,IAAc,2BAAA;AAC9C,IAAA,IAAI,CAAC,MAAM,OAAO,MAAA;AAClB,IAAA,OAAO,uBAAuB,IAAI,CAAA;AAAA,EACpC;AAEA,EAAA,MAAM,SAA+B,EAAC;AACtC,EAAA,MAAM,iBAAA,GAAoB,qBAAA;AAAA,IACxB,kBAAA,EAAoB,QAAQ,QAAA,EAAU,SAAA;AAAA,IACtC,aAAA,CAAc,UAAU,CAAA,EAAG;AAAA,GAC7B;AACA,EAAA,IAAI,iBAAA,SAA0B,QAAA,GAAW,iBAAA;AAEzC,EAAA,MAAM,gBAAA,GAAmB,qBAAA;AAAA,IACvB,kBAAA,EAAoB,QAAQ,OAAA,EAAS,SAAA;AAAA,IACrC,aAAA,CAAc,SAAS,CAAA,EAAG;AAAA,GAC5B;AACA,EAAA,IAAI,gBAAA,SAAyB,OAAA,GAAU,gBAAA;AAEvC,EAAA,MAAM,UAAiC,EAAC;AACxC,EAAA,MAAM,cAAc,CAAC,sBAAA,EAAwB,wBAAwB,uBAAA,EAAyB,oBAAA,EAAsB,kBAAkB,YAAY,CAAA;AAClJ,EAAA,IAAI,sBAAA,EAAwB;AAC1B,IAAA,OAAA,CAAQ,OAAA,GAAU,uBAAuB,sBAAsB,CAAA;AAAA,EACjE;AACA,EAAA,KAAA,MAAW,MAAM,WAAA,EAAa;AAC5B,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,EAAE,CAAA,EAAG,SAAA;AACrC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAC,OAAA,CAA4C,EAAE,CAAA,GAAI,sBAAA,CAAuB,QAAQ,CAAA;AAAA,IACpF;AAAA,EACF;AAGA,EAAA,MAAM,UAAU,QAAA,CAAS,OAAA;AACzB,EAAA,MAAM,IAAA,GAAO,SAAS,IAAA,IAAQ,WAAA;AAC9B,EAAA,MAAM,mBAAmB,QAAA,CAAS,SAAA;AAIlC,EAAA,IAAI,kBAAA;AACJ,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,kBAAA,GAAqB,EAAC;AACtB,IAAA,IAAI,gBAAA,CAAiB,SAAS,WAAA,EAAa;AACzC,MAAA,kBAAA,CAAmB,SAAA,GAAY;AAAA,QAC7B,QAAA,EAAU,eAAA,CAAgB,gBAAA,CAAiB,QAAA,EAAU,WAAW,CAAA;AAAA,QAChE,QAAA,EAAU,iBAAiB,QAAA,IAAY,2BAAA;AAAA,QACvC,MAAA,EAAQ,iBAAiB,MAAA,IAAU;AAAA,OACrC;AAAA,IACF,CAAA,MAAA,IAAW,gBAAA,CAAiB,IAAA,KAAS,QAAA,EAAU;AAC7C,MAAA,kBAAA,CAAmB,MAAA,GAAS;AAAA,QAC1B,UAAU,EAAE,IAAA,EAAM,gBAAgB,gBAAA,CAAiB,QAAA,EAAU,WAAW,CAAA,EAAE;AAAA,QAC1E,SAAS,gBAAA,CAAiB,OAAA;AAAA,QAC1B,IAAA,EAAM,gBAAA,CAAiB,OAAA,GAAU,MAAA,GAAY;AAAA,OAC/C;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,kBAAgD,EAAC;AACvD,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,cAAc,CAAA,EAAG;AACtD,IAAA,IAAI,EAAE,SAAA,EAAW;AACf,MAAA,eAAA,CAAgB,IAAI,CAAA,GAAI,EAAE,SAAA,EAAW,EAAE,IAAA,EAAM,CAAA,CAAE,SAAA,CAAU,IAAA,EAAM,KAAA,EAAO,CAAA,CAAE,SAAA,CAAU,OAAM,EAAE;AAAA,IAC5F;AAAA,EACF;AACA,EAAA,MAAM,iBAA8C,EAAC;AACrD,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,aAAa,CAAA,EAAG;AACrD,IAAA,IAAI,EAAE,SAAA,EAAW;AACf,MAAA,cAAA,CAAe,IAAI,CAAA,GAAI,EAAE,SAAA,EAAW,EAAE,IAAA,EAAM,CAAA,CAAE,SAAA,CAAU,IAAA,EAAM,KAAA,EAAO,CAAA,CAAE,SAAA,CAAU,OAAM,EAAE;AAAA,IAC3F;AAAA,EACF;AAEA,EAAA,IAAI,kBAAA,EAAoB,MAAA,EAAQ,QAAA,EAAU,SAAA,EAAW;AACnD,IAAA,cAAA,CAAe,UAAU,CAAA,GAAI,EAAE,SAAA,EAAW,EAAE,MAAM,kBAAA,CAAmB,MAAA,CAAO,QAAA,CAAS,SAAA,CAAU,MAAM,KAAA,EAAO,kBAAA,CAAmB,OAAO,QAAA,CAAS,SAAA,CAAU,OAAM,EAAE;AAAA,EACnK;AACA,EAAA,IAAI,kBAAA,EAAoB,MAAA,EAAQ,OAAA,EAAS,SAAA,EAAW;AAClD,IAAA,cAAA,CAAe,SAAS,CAAA,GAAI,EAAE,SAAA,EAAW,EAAE,MAAM,kBAAA,CAAmB,MAAA,CAAO,OAAA,CAAQ,SAAA,CAAU,MAAM,KAAA,EAAO,kBAAA,CAAmB,OAAO,OAAA,CAAQ,SAAA,CAAU,OAAM,EAAE;AAAA,EAChK;AAEA,EAAA,MAAM,WAAW,QAAA,CAAS,QAAA;AAC1B,EAAA,MAAM,QAAQ,QAAA,CAAS,KAAA;AAEvB,EAAA,MAAM,MAAA,GAA4B;AAAA,IAChC,QAAA,EAAU;AAAA,MACR,SAAS,OAAA,GAAU;AAAA,QACjB,UAAU,EAAE,IAAA,EAAM,gBAAgB,OAAA,CAAQ,QAAA,EAAU,SAAS,CAAA,EAAE;AAAA,QAC/D,IAAA,EAAM,QAAQ,IAAA,IAAQ,GAAA;AAAA,QACtB,WAAW,OAAA,CAAQ,SAAA,IAAa,CAAA,iBAAA,EAAoB,OAAA,CAAQ,QAAQ,GAAI,CAAA,CAAA;AAAA,QACxE,UAAA,EAAY,OAAA,CAAQ,UAAA,IAAc,OAAA,CAAQ,WAAA,IAAe;AAAA,OAC3D,GAAI,MAAA;AAAA,MACJ,UAAU,QAAA,GAAW;AAAA,QACnB,UAAU,EAAE,IAAA,EAAM,gBAAgB,QAAA,CAAS,QAAA,EAAU,UAAU,CAAA,EAAE;AAAA,QACjE,IAAA,EAAM,SAAS,IAAA,IAAQ,GAAA;AAAA,QACvB,QAAA,EAAU,MAAM,QAAA,IAAY,SAAA;AAAA,QAC5B,WAAW,QAAA,CAAS;AAAA,OACtB,GAAI,MAAA;AAAA,MACJ,OAAO,KAAA,GAAQ;AAAA,QACb,UAAU,EAAE,IAAA,EAAM,gBAAgB,KAAA,CAAM,QAAA,EAAU,OAAO,CAAA,EAAE;AAAA,QAC3D,IAAA,EAAM,OAAA;AAAA,QACN,IAAA,EAAM,MAAM,IAAA,IAAQ,IAAA;AAAA,QACpB,SAAA,EAAW,MAAM,SAAA,IAAa,IAAA;AAAA,QAC9B,WAAA,EAAa,SAAS,IAAA,IAAQ,GAAA;AAAA,QAC9B,YAAA,EAAc,UAAU,IAAA,IAAQ;AAAA,OAClC,GAAI,MAAA;AAAA,MACJ,KAAA,EAAO,SAAS,KAAA,GAAQ;AAAA,QACtB,GAAG,QAAA,CAAS,KAAA;AAAA,QACZ,QAAA,EAAU,EAAE,IAAA,EAAM,eAAA,CAAgB,SAAS,KAAA,CAAM,QAAA,EAAgC,OAAO,CAAA,EAAE;AAAA,QAC1F,IAAA,EAAO,QAAA,CAAS,KAAA,CAAM,IAAA,IAAQ;AAAA,UACe,kBAAA,EAAoB,KAAA;AAAA,MACnE,QAAA,EAAU,SAAS,QAAA,GAAW;AAAA,QAC5B,QAAA,EAAU,EAAE,IAAA,EAAM,eAAA,CAAgB,SAAS,QAAA,CAAS,QAAA,EAAU,UAAU,CAAA,EAAE;AAAA,QAC1E,IAAA,EAAM,UAAA;AAAA,QACN,KAAA,EAAO,SAAS,QAAA,CAAS,KAAA;AAAA,QACzB,IAAA,EAAM,QAAA,CAAS,QAAA,CAAS,IAAA,IAAQ,WAAA;AAAA,QAChC,IAAA,EAAM,QAAA,CAAS,QAAA,CAAS,IAAA,IAAQ,IAAA;AAAA,QAChC,IAAA,EAAM,SAAS,QAAA,CAAS,IAAA;AAAA,QACxB,IAAA,EAAM,SAAS,QAAA,CAAS,IAAA;AAAA,QACxB,QAAA,EAAU,SAAS,QAAA,CAAS;AAAA,OAC9B,GAAiD;AAAA,KACnD;AAAA,IACA,GAAI,kBAAA,GAAqB,EAAE,SAAA,EAAW,kBAAA,KAAuB,EAAC;AAAA,IAC9D,GAAI,MAAA,CAAO,IAAA,CAAK,eAAe,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,OAAA,EAAS,eAAA,EAAgB,GAAI,EAAC;AAAA,IAC9E,GAAI,MAAA,CAAO,IAAA,CAAK,cAAc,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,EAAE,MAAA,EAAQ,cAAA,EAAe,GAAI,EAAC;AAAA,IAC3E,MAAM,IAAA,GAAO;AAAA,MACX,MAAA,EAAQ,KAAK,MAAA,IAAU,WAAA;AAAA,MACvB,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,qBAAqB,IAAA,CAAK;AAAA,KAC5B,GAAI,MAAA;AAAA,IACJ,UAAU,QAAA,CAAS,QAAA;AAAA,IACnB,SAAA,EAAW;AAAA,MACT,WAAA;AAAA,MACA,WAAA;AAAA,MACA,WAAA;AAAA,MACA,GAAI,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,SAAS,CAAA,GAAI,EAAE,MAAA,EAAO,GAAI,EAAC;AAAA,MACnD,GAAI,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,SAAS,CAAA,GAAI,EAAE,OAAA,EAAQ,GAAI;AAAC;AACvD,GACF;AAEA,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,sBAAA,CACd,MAAA,EACA,gBAAA,EACA,GAAA,EACA;AACA,EAAA,OAAO,CAAC,aAAqB,WAAA,KAA2C;AACtE,IAAA,OAAO,cAAA,CAAe,WAAA,EAAa,WAAA,EAAa,gBAAA,EAAkB,QAAQ,GAAG,CAAA;AAAA,EAC/E,CAAA;AACF;ACrYO,IAAM,cAAA,GAAN,MAAM,eAAA,CAAe;AAAA,EACjB,IAAA;AAAA,EACA,IAAA;AAAA;AAAA,EAGA,SAAA;AAAA,EACA,OAAA;AAAA;AAAA,EAGA,SAAA;AAAA;AAAA,EAGA,QAAA;AAAA;AAAA,EAGA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,OAAA;AAAA;AAAA,EAGA,UAAA;AAAA,EAET,WAAA,CAAY,aAAqB,IAAA,EAAe;AAC9C,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AACZ,IAAA,IAAI,SAAS,MAAA,EAAW;AACtB,MAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,WAAA,EAAa,UAAA,EAAY,QAAQ,CAAA;AAC9D,MAAA,IAAI,CAAI,EAAA,CAAA,UAAA,CAAW,UAAU,CAAA,EAAG;AAC9B,QAAG,EAAA,CAAA,SAAA,CAAe,UAAK,WAAA,EAAa,UAAU,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACpE,QAAG,iBAAc,UAAA,EAAY,CAAA;AAAA,QAAA,EAAsB,IAAI,CAAA;AAAA,CAAK,CAAA;AAAA,MAC9D;AAAA,IACF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA,CAAe,QAAA,CAAS,WAAW,CAAA;AAE/C,IAAA,IAAA,CAAK,SAAA,GAAiB,IAAA,CAAA,IAAA,CAAK,WAAA,EAAa,UAAA,EAAY,QAAQ,CAAA;AAC5D,IAAA,IAAA,CAAK,OAAA,GAAU,WAAA;AAEf,IAAA,MAAM,YAAY,OAAA,CAAQ,GAAA,CAAI,mBAAwB,IAAA,CAAA,IAAA,CAAQ,EAAA,CAAA,OAAA,IAAW,SAAS,CAAA;AAClF,IAAA,IAAA,CAAK,SAAA,GAAiB,IAAA,CAAA,IAAA,CAAK,SAAA,EAAW,SAAA,EAAW,KAAK,IAAI,CAAA;AAE1D,IAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,aAAA,IAAsB,UAAQ,EAAA,CAAA,OAAA,EAAQ,EAAG,UAAU,OAAO,CAAA;AACtF,IAAA,IAAA,CAAK,QAAA,GAAgB,IAAA,CAAA,IAAA,CAAK,OAAA,EAAS,SAAA,EAAW,KAAK,IAAI,CAAA;AAEvD,IAAA,MAAM,QAAA,GAAW,QAAQ,GAAA,CAAI,cAAA,IAAuB,UAAQ,EAAA,CAAA,OAAA,EAAQ,EAAG,UAAU,OAAO,CAAA;AACxF,IAAA,IAAA,CAAK,QAAA,GAAgB,IAAA,CAAA,IAAA,CAAK,QAAA,EAAU,SAAA,EAAW,KAAK,IAAI,CAAA;AACxD,IAAA,IAAA,CAAK,cAAA,GAAsB,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,aAAa,CAAA;AAC5D,IAAA,IAAA,CAAK,OAAA,GAAe,IAAA,CAAA,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AAE9C,IAAA,MAAM,UAAA,GAAa,QAAQ,GAAA,CAAI,eAAA;AAC/B,IAAA,MAAM,WAAA,GAAc,UAAA,IAAc,OAAA,CAAQ,GAAA,CAAI,MAAA,IAAU,MAAA;AACxD,IAAA,IAAA,CAAK,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,WAAA,EAAa,SAAA,EAAW,KAAK,IAAI,CAAA;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAA,GAAyB;AAC7B,IAAA,MAAM,QAAQ,GAAA,CAAI;AAAA,MACb,EAAA,CAAA,QAAA,CAAS,GAAG,IAAA,CAAK,SAAA,EAAW,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,MAC5D,EAAA,CAAA,QAAA,CAAS,GAAG,IAAA,CAAK,QAAA,EAAU,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,MAC3D,EAAA,CAAA,QAAA,CAAS,GAAG,IAAA,CAAK,UAAA,EAAY,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM;AAAA,KACjE,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAe,SAAS,WAAA,EAA6B;AACnD,IAAA,MAAM,UAAA,GAAkB,IAAA,CAAA,IAAA,CAAK,WAAA,EAAa,UAAA,EAAY,QAAQ,CAAA;AAC9D,IAAA,IAAO,EAAA,CAAA,UAAA,CAAW,UAAU,CAAA,EAAG;AAC7B,MAAA,MAAM,OAAA,GAAa,EAAA,CAAA,YAAA,CAAa,UAAA,EAAY,OAAO,CAAA;AACnD,MAAA,KAAA,MAAW,IAAA,IAAQ,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA,EAAG;AACtC,QAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAC1B,QAAA,IAAI,QAAQ,UAAA,CAAW,MAAM,KAAK,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AACvD,UAAA,MAAM,GAAG,GAAG,IAAI,CAAA,GAAI,OAAA,CAAQ,MAAM,GAAG,CAAA;AACrC,UAAA,OAAO,IAAA,CAAK,KAAK,GAAG,CAAA,CAAE,MAAK,CAAE,OAAA,CAAQ,YAAY,IAAI,CAAA;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAY,cAAS,WAAW,CAAA;AAAA,EAClC;AACF;;;AChGA,IAAM,kBAAA,GAAqB;AAAA,EACzB,YAAA,EAAc,CAAC,QAAA,KACVC,EAAA,CAAA,UAAA,CAAW,QAAQ,CAAA,GAAOA,EAAA,CAAA,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA,GAAI;AACnE,CAAA;AAWO,SAAS,qBAAA,CACd,aACA,WAAA,EACmB;AACnB,EAAA,MAAM,gBAAA,GAAwBC,IAAA,CAAA,IAAA,CAAQC,EAAA,CAAA,OAAA,EAAQ,EAAG,gBAAgB,CAAA;AACjE,EAAA,OAAO,sBAAA;AAAA,IACL,kBAAA;AAAA,IACA,gBAAA;AAAA,IACA,OAAA,CAAQ;AAAA,GACV,CAAE,aAAa,WAAW,CAAA;AAC5B","file":"node-config-loader.js","sourcesContent":["/**\n * TOML Config Loader\n *\n * Reads ~/.semiontconfig (TOML) and .semiont/config (TOML) and produces\n * an EnvironmentConfig for the requested environment.\n *\n * File format: see TOML-XDG-CONFIG.md\n *\n * Loading sequence:\n * 1. Read .semiont/config → projectName, site, environments.<env>.* (project base)\n * 2. Read ~/.semiontconfig → defaults, environments.<env>.* (user overrides)\n * 3. Deep-merge: project base ← user overrides (user wins on conflicts)\n * Any environment name is valid (local, staging, production, custom, ...)\n * 4. Resolve ${VAR} references from process.env\n * 5. Apply inheritance: workers.<name> → workers.default → error\n * 6. Map to EnvironmentConfig shape\n */\n\nimport { parse as parseToml } from 'smol-toml';\nimport type { EnvironmentConfig, OllamaProviderConfig, AnthropicProviderConfig } from './config.types';\nimport type { PlatformType } from './config.types';\n\n/**\n * Deep merge two plain objects. Arrays and primitives in `override` replace those in `base`.\n * Nested objects are merged recursively. `override` takes precedence on conflicts.\n */\nfunction deepMerge<T extends Record<string, unknown>>(base: T, override: Partial<T>): T {\n const result = { ...base } as Record<string, unknown>;\n for (const key of Object.keys(override)) {\n const b = base[key];\n const o = override[key];\n if (o !== undefined && o !== null && typeof o === 'object' && !Array.isArray(o) &&\n b !== undefined && b !== null && typeof b === 'object' && !Array.isArray(b)) {\n result[key] = deepMerge(b as Record<string, unknown>, o as Record<string, unknown>);\n } else if (o !== undefined) {\n result[key] = o;\n }\n }\n return result as T;\n}\n\nfunction resolveEnvVars(obj: unknown, env: Record<string, string | undefined>): unknown {\n if (typeof obj === 'string') {\n return obj.replace(/\\$\\{([^}]+)\\}/g, (match, varName) => {\n if (env[varName] === undefined) {\n throw new Error(`Environment variable ${varName} is not set (referenced in config as ${match})`);\n }\n return env[varName] as string;\n });\n }\n if (Array.isArray(obj)) {\n return obj.map(item => resolveEnvVars(item, env));\n }\n if (obj !== null && typeof obj === 'object') {\n const resolved: Record<string, unknown> = {};\n for (const key in obj as Record<string, unknown>) {\n resolved[key] = resolveEnvVars((obj as Record<string, unknown>)[key], env);\n }\n return resolved;\n }\n return obj;\n}\n\n// ── Inference config types (mirrored from packages/make-meaning/src/config.ts) ─\n// Kept here to avoid a circular dependency: core cannot import make-meaning.\n\nexport interface InferenceConfig {\n type: 'anthropic' | 'ollama';\n model: string;\n maxTokens?: number;\n apiKey?: string;\n endpoint?: string;\n baseURL?: string;\n}\n\nexport interface ActorInferenceConfig {\n gatherer?: InferenceConfig;\n matcher?: InferenceConfig;\n}\n\nexport interface WorkerInferenceConfig {\n default?: InferenceConfig;\n 'reference-annotation'?: InferenceConfig;\n 'highlight-annotation'?: InferenceConfig;\n 'assessment-annotation'?: InferenceConfig;\n 'comment-annotation'?: InferenceConfig;\n 'tag-annotation'?: InferenceConfig;\n 'generation'?: InferenceConfig;\n}\n\n// ── Types for ~/.semiontconfig ────────────────────────────────────────────────\n\ninterface SemiontConfigFile {\n user?: {\n name?: string;\n email?: string;\n };\n defaults?: {\n environment?: string;\n platform?: string;\n };\n environments?: Record<string, EnvironmentSection>;\n}\n\ninterface GraphSection {\n platform?: string;\n type?: string;\n name?: string;\n uri?: string;\n username?: string;\n password?: string;\n database?: string;\n [key: string]: unknown;\n}\n\ninterface InferenceFlatSection {\n type?: 'anthropic' | 'ollama';\n platform?: string;\n model?: string;\n maxTokens?: number;\n apiKey?: string;\n endpoint?: string;\n baseURL?: string;\n}\n\ninterface EnvironmentSection {\n backend?: {\n platform?: string;\n port?: number;\n publicURL?: string;\n frontendURL?: string;\n corsOrigin?: string;\n };\n frontend?: {\n platform?: string;\n port?: number;\n publicURL?: string;\n };\n proxy?: {\n platform?: string;\n port?: number;\n adminPort?: number;\n publicURL?: string;\n };\n site?: {\n domain?: string;\n siteName?: string;\n adminEmail?: string;\n oauthAllowedDomains?: string[];\n enableLocalAuth?: boolean;\n };\n database?: {\n platform?: string;\n image?: string;\n host?: string;\n port?: number;\n name?: string;\n user?: string;\n password?: string;\n };\n graph?: GraphSection;\n inference?: InferenceFlatSection;\n 'make-meaning'?: {\n graph?: Record<string, unknown>;\n actors?: {\n gatherer?: { inference?: InferenceConfig };\n matcher?: { inference?: InferenceConfig };\n };\n default?: { inference?: InferenceConfig };\n };\n workers?: Record<string, { inference?: InferenceConfig }>;\n actors?: Record<string, { inference?: InferenceConfig }>;\n logLevel?: 'error' | 'warn' | 'info' | 'http' | 'debug';\n}\n\n// ── File reader abstraction (same pattern as createConfigLoader) ──────────────\n\nexport type TomlFileReader = {\n readIfExists: (path: string) => string | null;\n};\n\nfunction requirePlatform(value: string | undefined, serviceName: string): PlatformType {\n if (!value) {\n throw new Error(`platform is required for service '${serviceName}' — add 'platform = \"posix\"|\"container\"|\"external\"' to its config section`);\n }\n return value as PlatformType;\n}\n\n// ── Main loader function ──────────────────────────────────────────────────────\n\n/**\n * Parse ~/.semiontconfig and .semiont/config and return EnvironmentConfig.\n *\n * @param projectRoot - Path to the project root (contains .semiont/config)\n * @param environment - Environment name (e.g. 'local', 'production')\n * @param globalConfigPath - Path to ~/.semiontconfig (caller resolves ~ expansion)\n * @param reader - File reader abstraction\n * @param env - Environment variables for ${VAR} resolution\n */\nexport function loadTomlConfig(\n projectRoot: string,\n environment: string,\n globalConfigPath: string,\n reader: TomlFileReader,\n env: Record<string, string | undefined>\n): EnvironmentConfig {\n // 1. Read project config from .semiont/config\n const projectConfigContent = reader.readIfExists(`${projectRoot}/.semiont/config`);\n let projectName = 'semiont-project';\n let projectSite: EnvironmentSection['site'] | undefined;\n let projectEnvSection: EnvironmentSection = {};\n if (projectConfigContent) {\n const projectConfig = parseToml(projectConfigContent) as {\n project?: { name?: string };\n site?: EnvironmentSection['site'];\n environments?: Record<string, EnvironmentSection>;\n };\n projectName = projectConfig.project?.name ?? projectName;\n projectSite = projectConfig.site;\n projectEnvSection = projectConfig.environments?.[environment] ?? {};\n }\n\n // 2. Read global config (optional — missing config yields empty environments)\n const globalContent = reader.readIfExists(globalConfigPath);\n const raw = globalContent ? (parseToml(globalContent) as SemiontConfigFile) : ({} as SemiontConfigFile);\n\n // 3. Deep-merge: project base + user overrides (user wins on conflicts)\n const userEnvSection: EnvironmentSection = raw.environments?.[environment] ?? {};\n const envSection: EnvironmentSection = deepMerge(\n projectEnvSection as Record<string, unknown>,\n userEnvSection as Record<string, unknown>\n ) as EnvironmentSection;\n\n // 4. Resolve ${VAR} references\n const resolved = resolveEnvVars(envSection, env) as EnvironmentSection;\n\n // 5. Build make-meaning actor/worker inference config with inheritance\n // The flat [inference] section provides defaults (apiKey, maxTokens, endpoint/baseURL).\n // Actor/worker sections only need to specify type and model; missing fields fall back\n // to the flat inference section.\n const flatInference = resolved.inference;\n const makeMeaningSection = resolved['make-meaning'];\n const workersSection = resolved.workers ?? {};\n const actorsSection = resolved.actors ?? {};\n const defaultWorkerInference = workersSection['default']?.inference;\n const defaultMakeMeaningInference = makeMeaningSection?.default?.inference;\n\n function mergeWithFlatInference(specific: InferenceConfig): InferenceConfig {\n if (!flatInference) return specific;\n return {\n apiKey: flatInference.apiKey,\n maxTokens: flatInference.maxTokens,\n endpoint: flatInference.endpoint,\n baseURL: flatInference.baseURL,\n ...specific,\n };\n }\n\n function resolveActorInference(fromMakeMeaning?: InferenceConfig, fromActors?: InferenceConfig): InferenceConfig | undefined {\n const base = fromMakeMeaning ?? fromActors ?? defaultMakeMeaningInference;\n if (!base) return undefined;\n return mergeWithFlatInference(base);\n }\n\n const actors: ActorInferenceConfig = {};\n const gathererInference = resolveActorInference(\n makeMeaningSection?.actors?.gatherer?.inference,\n actorsSection['gatherer']?.inference\n );\n if (gathererInference) actors.gatherer = gathererInference;\n\n const matcherInference = resolveActorInference(\n makeMeaningSection?.actors?.matcher?.inference,\n actorsSection['matcher']?.inference\n );\n if (matcherInference) actors.matcher = matcherInference;\n\n const workers: WorkerInferenceConfig = {};\n const workerTypes = ['reference-annotation', 'highlight-annotation', 'assessment-annotation', 'comment-annotation', 'tag-annotation', 'generation'] as const;\n if (defaultWorkerInference) {\n workers.default = mergeWithFlatInference(defaultWorkerInference);\n }\n for (const wt of workerTypes) {\n const specific = workersSection[wt]?.inference;\n if (specific) {\n (workers as Record<string, InferenceConfig>)[wt] = mergeWithFlatInference(specific);\n }\n }\n\n // 6. Map to EnvironmentConfig\n const backend = resolved.backend;\n const site = resolved.site ?? projectSite;\n const inferenceSection = resolved.inference;\n\n // Build inference providers config\n // Supports flat format: [environments.local.inference] with type = \"anthropic\"|\"ollama\"\n let inferenceProviders: EnvironmentConfig['inference'] | undefined;\n if (inferenceSection) {\n inferenceProviders = {};\n if (inferenceSection.type === 'anthropic') {\n inferenceProviders.anthropic = {\n platform: requirePlatform(inferenceSection.platform, 'inference'),\n endpoint: inferenceSection.endpoint ?? 'https://api.anthropic.com',\n apiKey: inferenceSection.apiKey ?? '',\n } as AnthropicProviderConfig;\n } else if (inferenceSection.type === 'ollama') {\n inferenceProviders.ollama = {\n platform: { type: requirePlatform(inferenceSection.platform, 'inference') },\n baseURL: inferenceSection.baseURL,\n port: inferenceSection.baseURL ? undefined : 11434,\n } as OllamaProviderConfig;\n }\n }\n\n // Build top-level workers/actors maps for EnvironmentConfig\n const topLevelWorkers: EnvironmentConfig['workers'] = {};\n for (const [name, w] of Object.entries(workersSection)) {\n if (w.inference) {\n topLevelWorkers[name] = { inference: { type: w.inference.type, model: w.inference.model } };\n }\n }\n const topLevelActors: EnvironmentConfig['actors'] = {};\n for (const [name, a] of Object.entries(actorsSection)) {\n if (a.inference) {\n topLevelActors[name] = { inference: { type: a.inference.type, model: a.inference.model } };\n }\n }\n // Also include make-meaning actors\n if (makeMeaningSection?.actors?.gatherer?.inference) {\n topLevelActors['gatherer'] = { inference: { type: makeMeaningSection.actors.gatherer.inference.type, model: makeMeaningSection.actors.gatherer.inference.model } };\n }\n if (makeMeaningSection?.actors?.matcher?.inference) {\n topLevelActors['matcher'] = { inference: { type: makeMeaningSection.actors.matcher.inference.type, model: makeMeaningSection.actors.matcher.inference.model } };\n }\n\n const frontend = resolved.frontend;\n const proxy = resolved.proxy;\n\n const config: EnvironmentConfig = {\n services: {\n backend: backend ? {\n platform: { type: requirePlatform(backend.platform, 'backend') },\n port: backend.port ?? 4000,\n publicURL: backend.publicURL ?? `http://localhost:${backend.port ?? 4000}`,\n corsOrigin: backend.corsOrigin ?? backend.frontendURL ?? 'http://localhost:3000',\n } : undefined,\n frontend: frontend ? {\n platform: { type: requirePlatform(frontend.platform, 'frontend') },\n port: frontend.port ?? 3000,\n siteName: site?.siteName ?? 'Semiont',\n publicURL: frontend.publicURL,\n } : undefined,\n proxy: proxy ? {\n platform: { type: requirePlatform(proxy.platform, 'proxy') },\n type: 'envoy',\n port: proxy.port ?? 8080,\n adminPort: proxy.adminPort ?? 9901,\n backendPort: backend?.port ?? 4000,\n frontendPort: frontend?.port ?? 3000,\n } : undefined,\n graph: resolved.graph ? {\n ...resolved.graph,\n platform: { type: requirePlatform(resolved.graph.platform as string | undefined, 'graph') },\n type: (resolved.graph.type ?? 'neo4j') as import('./config.types').GraphDatabaseType,\n } as EnvironmentConfig['services']['graph'] : (makeMeaningSection?.graph as EnvironmentConfig['services']['graph']),\n database: resolved.database ? {\n platform: { type: requirePlatform(resolved.database.platform, 'database') },\n type: 'postgres',\n image: resolved.database.image,\n host: resolved.database.host ?? 'localhost',\n port: resolved.database.port ?? 5432,\n name: resolved.database.name,\n user: resolved.database.user,\n password: resolved.database.password,\n } as EnvironmentConfig['services']['database'] : undefined,\n },\n ...(inferenceProviders ? { inference: inferenceProviders } : {}),\n ...(Object.keys(topLevelWorkers).length > 0 ? { workers: topLevelWorkers } : {}),\n ...(Object.keys(topLevelActors).length > 0 ? { actors: topLevelActors } : {}),\n site: site ? {\n domain: site.domain ?? 'localhost',\n siteName: site.siteName,\n adminEmail: site.adminEmail,\n oauthAllowedDomains: site.oauthAllowedDomains as [string, ...string[]] | undefined,\n } : undefined,\n logLevel: resolved.logLevel,\n _metadata: {\n environment,\n projectRoot,\n projectName,\n ...(Object.keys(actors).length > 0 ? { actors } : {}),\n ...(Object.keys(workers).length > 0 ? { workers } : {}),\n },\n };\n\n return config;\n}\n\n/**\n * Create a TOML config loader backed by a file reader.\n * Drop-in replacement for createConfigLoader that reads TOML instead of JSON.\n * The caller must resolve globalConfigPath (e.g. expand '~' using process.env.HOME).\n */\nexport function createTomlConfigLoader(\n reader: TomlFileReader,\n globalConfigPath: string,\n env: Record<string, string | undefined>\n) {\n return (projectRoot: string, environment: string): EnvironmentConfig => {\n return loadTomlConfig(projectRoot, environment, globalConfigPath, reader, env);\n };\n}\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Represents a Semiont project rooted at a given directory.\n *\n * Computes all paths — durable and ephemeral — once at construction time.\n * XDG environment variables are read here and nowhere else.\n *\n * Durable paths (inside the project root, committed or repo-local):\n * eventsDir — .semiont/events/ (system of record, committed)\n * dataDir — projectRoot/ (representations store root; representations/ is committed)\n *\n * Ephemeral paths (outside the project root, never committed):\n * configDir — $XDG_CONFIG_HOME/semiont/{name}/ (generated config for managed processes)\n * dataHome — $XDG_DATA_HOME/semiont/{name}/ (persistent user data, e.g. database files)\n * stateDir — $XDG_STATE_HOME/semiont/{name}/\n * projectionsDir — stateDir/projections/\n * jobsDir — stateDir/jobs/\n * runtimeDir — $XDG_RUNTIME_DIR/semiont/{name}/ (or $TMPDIR fallback)\n */\nexport class SemiontProject {\n readonly root: string;\n readonly name: string;\n\n // Durable\n readonly eventsDir: string;\n readonly dataDir: string;\n\n // Ephemeral — config (generated config files for managed processes)\n readonly configDir: string;\n\n // Ephemeral — data (persistent user data managed by semiont)\n readonly dataHome: string;\n\n // Ephemeral — state\n readonly stateDir: string;\n readonly projectionsDir: string;\n readonly jobsDir: string;\n\n // Ephemeral — runtime\n readonly runtimeDir: string;\n\n constructor(projectRoot: string, name?: string) {\n this.root = projectRoot;\n if (name !== undefined) {\n const configPath = path.join(projectRoot, '.semiont', 'config');\n if (!fs.existsSync(configPath)) {\n fs.mkdirSync(path.join(projectRoot, '.semiont'), { recursive: true });\n fs.writeFileSync(configPath, `[project]\\nname = \"${name}\"\\n`);\n }\n }\n this.name = SemiontProject.readName(projectRoot);\n\n this.eventsDir = path.join(projectRoot, '.semiont', 'events');\n this.dataDir = projectRoot;\n\n const xdgConfig = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');\n this.configDir = path.join(xdgConfig, 'semiont', this.name);\n\n const xdgData = process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');\n this.dataHome = path.join(xdgData, 'semiont', this.name);\n\n const xdgState = process.env.XDG_STATE_HOME || path.join(os.homedir(), '.local', 'state');\n this.stateDir = path.join(xdgState, 'semiont', this.name);\n this.projectionsDir = path.join(this.stateDir, 'projections');\n this.jobsDir = path.join(this.stateDir, 'jobs');\n\n const xdgRuntime = process.env.XDG_RUNTIME_DIR;\n const runtimeBase = xdgRuntime ?? process.env.TMPDIR ?? '/tmp';\n this.runtimeDir = path.join(runtimeBase, 'semiont', this.name);\n }\n\n /**\n * Delete all ephemeral state for this project (stateDir + runtimeDir).\n * Does not touch eventsDir or dataDir.\n */\n async destroy(): Promise<void> {\n await Promise.all([\n fs.promises.rm(this.configDir, { recursive: true, force: true }),\n fs.promises.rm(this.stateDir, { recursive: true, force: true }),\n fs.promises.rm(this.runtimeDir, { recursive: true, force: true }),\n ]);\n }\n\n /**\n * Read the project name from .semiont/config [project] name = \"...\"\n * Falls back to the directory basename if the config is absent or has no name.\n */\n private static readName(projectRoot: string): string {\n const configPath = path.join(projectRoot, '.semiont', 'config');\n if (fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.startsWith('name') && trimmed.includes('=')) {\n const [, ...rest] = trimmed.split('=');\n return rest.join('=').trim().replace(/^\"(.*)\"$/, '$1');\n }\n }\n }\n return path.basename(projectRoot);\n }\n}\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { createTomlConfigLoader } from './toml-loader.js';\nimport type { EnvironmentConfig } from './config.types.js';\n\nexport { SemiontProject } from '../project.js';\n\nconst nodeTomlFileReader = {\n readIfExists: (filePath: string): string | null =>\n fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : null,\n};\n\n/**\n * Load semiont environment config for a Node.js process.\n *\n * Reads ~/.semiontconfig (global) merged with .semiont/config (project-local),\n * then selects the given environment overlay.\n *\n * This is the canonical config loader for any Node.js process. SEMIONT_ENV\n * should be read once at the process entry point and passed as `environment`.\n */\nexport function loadEnvironmentConfig(\n projectRoot: string,\n environment: string\n): EnvironmentConfig {\n const globalConfigPath = path.join(os.homedir(), '.semiontconfig');\n return createTomlConfigLoader(\n nodeTomlFileReader,\n globalConfigPath,\n process.env\n )(projectRoot, environment);\n}\n"]}
|