@happyvertical/smrt-config 0.55.2 → 0.55.4

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/dist/index.js CHANGED
@@ -1,262 +1,6 @@
1
+ import { A as getDefaultAIKeyEnvName, B as setExplorer, C as getApplicationRuntimePreset, D as DEFAULT_AI_ENV_PREFIXES, E as AI_PROVIDER_KEY_ENV, F as tryResolveAIProviderConfig, I as withAIAliases, L as getCachedConfig, M as redactBaseUrl, N as resolveAIProviderConfig, O as canonicalizeAIConfig, P as toAIClientOptions, R as getExplorer, S as RuntimeProfileValidationError, T as AIProviderNotConfiguredError, V as setLoadedConfig, _ as parseExportedConfig, a as getModuleConfig, b as getRuntimeConfig, c as mergeAIConfigLayers, d as resolveConfiguredApplicationRuntime, f as resolveEffectiveApplicationRuntime, g as mergeExportedConfig, h as exportConfig, i as getConfig, j as mergeAIConfigObjects, k as describeAIProviderConfig, l as resolveCliDatabaseConfig, m as tryResolveConfiguredAIProvider, n as defineConfig, o as getPackageConfig, p as setConfig, r as getAIConfigBlock, s as getSiteConfig, t as clearCache, u as resolveConfiguredAIProvider, v as sanitizeConfig, w as resolveApplicationRuntime, x as mergeConfigs, y as clearRuntimeConfig, z as setCachedConfig } from "./chunks/shared-B81TOCQq.js";
1
2
  import { cosmiconfig } from "cosmiconfig";
2
3
  import { createJiti } from "jiti";
3
- //#region src/ai.ts
4
- var AI_PROVIDER_KEY_ENV = Object.freeze({
5
- openai: "OPENAI_API_KEY",
6
- anthropic: "ANTHROPIC_API_KEY",
7
- gemini: "GEMINI_API_KEY"
8
- });
9
- var AUTO_DETECT_ORDER = [
10
- "openai",
11
- "anthropic",
12
- "gemini"
13
- ];
14
- var DEFAULT_AI_ENV_PREFIXES = Object.freeze(["SMRT_AI", "HAVE_AI"]);
15
- function getDefaultAIKeyEnvName(provider) {
16
- const normalized = nonEmpty(provider)?.toLowerCase();
17
- return normalized && Object.hasOwn(AI_PROVIDER_KEY_ENV, normalized) ? AI_PROVIDER_KEY_ENV[normalized] : void 0;
18
- }
19
- var AIProviderNotConfiguredError = class extends Error {
20
- code = "SMRT_AI_NOT_CONFIGURED";
21
- constructor(message) {
22
- super(message);
23
- this.name = "AIProviderNotConfiguredError";
24
- }
25
- };
26
- function nonEmpty(value) {
27
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
28
- }
29
- function redactBaseUrl(value) {
30
- if (!value) return void 0;
31
- try {
32
- const url = new URL(value);
33
- return `${url.protocol}//${url.host}`;
34
- } catch {
35
- return "[unparseable-url]";
36
- }
37
- }
38
- function tryResolveAIProviderConfig(options = {}) {
39
- const env = options.env ?? process.env;
40
- const read = (name) => nonEmpty(env[name]);
41
- const prefixes = options.prefixes ?? DEFAULT_AI_ENV_PREFIXES;
42
- const explicit = options.explicit ?? {};
43
- const block = options.config ?? {};
44
- const fromEnv = (suffix) => prefixes.map((prefix) => ({
45
- source: `env:${prefix}_${suffix}`,
46
- value: read(`${prefix}_${suffix}`)
47
- }));
48
- const explicitCand = (value) => ({
49
- source: "explicit",
50
- value: nonEmpty(value)
51
- });
52
- const configCand = (value) => ({
53
- source: "config",
54
- value: nonEmpty(value)
55
- });
56
- const layered = (exp, cfg, envs) => options.envOverridesConfig ? [
57
- ...exp,
58
- ...envs,
59
- ...cfg
60
- ] : [
61
- ...exp,
62
- ...cfg,
63
- ...envs
64
- ];
65
- const first = (candidates) => {
66
- for (const c of candidates) if (c.value) return {
67
- value: c.value,
68
- source: c.source
69
- };
70
- };
71
- const sources = {};
72
- const result = {};
73
- const set = (field, hit) => {
74
- if (hit) {
75
- result[field] = hit.value;
76
- sources[field] = hit.source;
77
- }
78
- };
79
- set("provider", first(layered([explicitCand(nonEmpty(explicit.provider) ?? nonEmpty(explicit.type))], [configCand(block.provider)], fromEnv("PROVIDER"))));
80
- set("model", first(layered([explicitCand(nonEmpty(explicit.model) ?? nonEmpty(explicit.defaultModel))], [configCand(block.model)], fromEnv("MODEL"))));
81
- const autoDetected = result.provider || options.autoDetect === false ? void 0 : AUTO_DETECT_ORDER.find((p) => read(AI_PROVIDER_KEY_ENV[p]));
82
- if (autoDetected) {
83
- result.provider = autoDetected;
84
- sources.provider = "auto-detect";
85
- }
86
- const selected = result.provider?.toLowerCase();
87
- const configKeyEnv = nonEmpty(block.apiKeyEnv);
88
- const explicitGroup = {
89
- source: "explicit",
90
- provider: nonEmpty(explicit.provider) ?? nonEmpty(explicit.type),
91
- apiKey: nonEmpty(explicit.apiKey),
92
- baseUrl: nonEmpty(explicit.baseUrl)
93
- };
94
- const configGroup = {
95
- source: "config",
96
- provider: nonEmpty(block.provider),
97
- apiKey: nonEmpty(block.apiKey) ?? (configKeyEnv ? read(configKeyEnv) : void 0),
98
- baseUrl: nonEmpty(block.baseUrl)
99
- };
100
- const envGroups = prefixes.map((prefix) => ({
101
- source: `env:${prefix}`,
102
- provider: read(`${prefix}_PROVIDER`),
103
- apiKey: read(`${prefix}_API_KEY`),
104
- baseUrl: read(`${prefix}_BASE_URL`)
105
- }));
106
- const groups = options.envOverridesConfig ? [
107
- explicitGroup,
108
- ...envGroups,
109
- configGroup
110
- ] : [
111
- explicitGroup,
112
- configGroup,
113
- ...envGroups
114
- ];
115
- const usable = (g) => !g.provider || g.provider.toLowerCase() === selected;
116
- const sourceName = (g, suffix) => g.source.startsWith("env:") ? `${g.source}_${suffix}` : g.source;
117
- for (const g of groups) {
118
- if (!usable(g)) continue;
119
- if (g.baseUrl && !result.baseUrl) {
120
- result.baseUrl = g.baseUrl;
121
- sources.baseUrl = sourceName(g, "BASE_URL");
122
- }
123
- if (g.apiKey && !result.apiKey) {
124
- result.apiKey = g.apiKey;
125
- sources.apiKey = sourceName(g, "API_KEY");
126
- }
127
- }
128
- const providerKeyEnv = getDefaultAIKeyEnvName(result.provider);
129
- if (!result.apiKey && providerKeyEnv && options.providerKeyEnvFallback !== false && read(providerKeyEnv)) {
130
- result.apiKey = read(providerKeyEnv);
131
- sources.apiKey = "provider-key-env";
132
- }
133
- if (!result.provider && (options.requireProvider ?? true)) return;
134
- if (!result.provider && !result.apiKey) return;
135
- const resolved = {
136
- ...result,
137
- sources,
138
- toJSON: () => describeAIProviderConfig(resolved)
139
- };
140
- Object.defineProperty(resolved, "toJSON", { enumerable: false });
141
- return resolved;
142
- }
143
- function resolveAIProviderConfig(options = {}) {
144
- const resolved = tryResolveAIProviderConfig(options);
145
- if (resolved) return resolved;
146
- const envNames = (options.prefixes ?? DEFAULT_AI_ENV_PREFIXES).map((p) => `${p}_PROVIDER`).join(", ");
147
- const keyNames = Object.values(AI_PROVIDER_KEY_ENV).join(", ");
148
- throw new AIProviderNotConfiguredError(`No AI provider is configured. Set the \`ai\` block in smrt.config.ts (provider, model, apiKeyEnv), or set ${envNames}` + (options.autoDetect === false ? "." : `, or one of ${keyNames} to auto-detect the provider.`));
149
- }
150
- function describeAIProviderConfig(resolved) {
151
- return {
152
- provider: resolved.provider,
153
- model: resolved.model,
154
- baseUrl: redactBaseUrl(resolved.baseUrl),
155
- apiKey: resolved.apiKey ? "[redacted]" : void 0,
156
- sources: { ...resolved.sources }
157
- };
158
- }
159
- function toAIClientOptions(resolved) {
160
- const out = {};
161
- if (resolved.provider) {
162
- out.type = resolved.provider;
163
- out.provider = resolved.provider;
164
- }
165
- if (resolved.apiKey) out.apiKey = resolved.apiKey;
166
- if (resolved.baseUrl) out.baseUrl = resolved.baseUrl;
167
- if (resolved.model) out.defaultModel = resolved.model;
168
- return out;
169
- }
170
- var OWNED_AI_FIELDS = [
171
- "apiKey",
172
- "apiKeyEnv",
173
- "baseUrl",
174
- "model"
175
- ];
176
- function canonicalizeAIConfig(input) {
177
- if (!input || typeof input !== "object") return {};
178
- const out = {};
179
- for (const [key, value] of Object.entries(input)) {
180
- if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
181
- if (typeof value === "string" && value.trim().length === 0) continue;
182
- out[key] = value;
183
- }
184
- const provider = nonEmpty(out.provider) ?? nonEmpty(out.type);
185
- const model = nonEmpty(out.model) ?? nonEmpty(out.defaultModel);
186
- delete out.type;
187
- delete out.defaultModel;
188
- if (provider) out.provider = provider;
189
- else delete out.provider;
190
- if (model) out.model = model;
191
- else delete out.model;
192
- return out;
193
- }
194
- function mergeAIConfigObjects(lower, higher) {
195
- const low = canonicalizeAIConfig(lower);
196
- const high = canonicalizeAIConfig(higher);
197
- const lowName = nonEmpty(low.provider)?.toLowerCase();
198
- const highName = nonEmpty(high.provider)?.toLowerCase();
199
- if (lowName && highName && lowName !== highName) {
200
- delete low.provider;
201
- for (const f of OWNED_AI_FIELDS) delete low[f];
202
- }
203
- return {
204
- ...low,
205
- ...high
206
- };
207
- }
208
- function withAIAliases(config) {
209
- const out = { ...config };
210
- const provider = nonEmpty(out.provider) ?? nonEmpty(out.type);
211
- const model = nonEmpty(out.model) ?? nonEmpty(out.defaultModel);
212
- for (const k of ["provider", "type"]) if (provider) out[k] = provider;
213
- else delete out[k];
214
- for (const k of ["model", "defaultModel"]) if (model) out[k] = model;
215
- else delete out[k];
216
- return out;
217
- }
218
- //#endregion
219
- //#region src/database-environment.ts
220
- var POSTGRES_URL = /^postgres(?:ql)?:\/\//i;
221
- function environmentType(value) {
222
- const normalized = value?.trim().toLowerCase();
223
- if (!normalized) return null;
224
- if (normalized === "sqlite") return "sqlite";
225
- if (normalized === "postgres" || normalized === "postgresql") return "postgres";
226
- throw new TypeError("DATABASE_TYPE must be sqlite or postgres.");
227
- }
228
- function resolveCliDatabase(declared, env) {
229
- if (typeof declared?.url === "string" && declared.url !== "") return {
230
- source: "config",
231
- database: typeof declared.type === "string" ? {
232
- type: declared.type,
233
- url: declared.url
234
- } : { url: declared.url }
235
- };
236
- const url = env.DATABASE_URL?.trim();
237
- if (!url) return {
238
- source: "none",
239
- database: null
240
- };
241
- let type;
242
- try {
243
- type = declared?.type === "sqlite" || declared?.type === "postgres" ? declared.type : environmentType(env.DATABASE_TYPE) ?? (POSTGRES_URL.test(url) ? "postgres" : "sqlite");
244
- } catch (error) {
245
- return {
246
- source: "invalid-environment",
247
- database: null,
248
- error: error.message
249
- };
250
- }
251
- return {
252
- source: "environment",
253
- database: {
254
- type,
255
- url
256
- }
257
- };
258
- }
259
- //#endregion
260
4
  //#region src/loader.ts
261
5
  var MODULE_NAME = "smrt";
262
6
  var jitiInstance;
@@ -267,18 +11,6 @@ function getJiti() {
267
11
  var typeScriptLoader = async (filepath) => {
268
12
  return getJiti().import(filepath, { default: true });
269
13
  };
270
- function getCachedConfig() {
271
- return globalThis.__smrtLoaderCachedConfig ?? null;
272
- }
273
- function setCachedConfig(config) {
274
- globalThis.__smrtLoaderCachedConfig = config;
275
- }
276
- function getExplorer() {
277
- return globalThis.__smrtLoaderExplorer ?? null;
278
- }
279
- function setExplorer(exp) {
280
- globalThis.__smrtLoaderExplorer = exp;
281
- }
282
14
  function isFileNotFound(error) {
283
15
  if (!error || typeof error !== "object") return false;
284
16
  if (error.code === "ENOENT") return true;
@@ -324,800 +56,13 @@ async function loadConfig$1(options = {}) {
324
56
  if (cache) setCachedConfig(config);
325
57
  return config;
326
58
  }
327
- function clearConfigCache() {
328
- setCachedConfig(null);
329
- const explorer = getExplorer();
330
- if (explorer) {
331
- explorer.clearCaches();
332
- setExplorer(null);
333
- }
334
- }
335
- //#endregion
336
- //#region src/runtime-profile.ts
337
- function isApplicationRuntimeProfile(value) {
338
- return value === "local" || value === "self-hosted" || value === "cloud";
339
- }
340
- function invalidProfileIssue() {
341
- return {
342
- code: "invalid_config",
343
- path: "profile",
344
- message: "must be local, self-hosted, or cloud.",
345
- recovery: "Select one documented runtime profile."
346
- };
347
- }
348
- var RuntimeProfileValidationError = class extends Error {
349
- issues;
350
- constructor(issues) {
351
- super(`Invalid application runtime profile:
352
- ${issues.map((issue) => `- ${issue.path}: ${issue.message} Recovery: ${issue.recovery}`).join("\n")}`);
353
- this.name = "RuntimeProfileValidationError";
354
- this.issues = Object.freeze(issues.map((issue) => Object.freeze({ ...issue })));
355
- }
356
- };
357
- var PROFILE_DEFAULTS = {
358
- local: {
359
- database: {
360
- engine: "sqlite",
361
- connectionOwnership: "user"
362
- },
363
- authentication: {
364
- provider: "owner-bootstrap",
365
- ownerBootstrap: "single-use"
366
- },
367
- tenancy: {
368
- mode: "default-tenant",
369
- context: "defaulted",
370
- isolation: "application"
371
- },
372
- assets: {
373
- provider: "local-files",
374
- ownership: "user"
375
- },
376
- secrets: {
377
- provider: "local-file",
378
- ownership: "user"
379
- },
380
- jobs: { topology: "embedded" },
381
- network: {
382
- exposure: "loopback",
383
- tls: false
384
- },
385
- portability: {
386
- backup: "file-snapshot",
387
- logicalExport: true,
388
- logicalImport: true
389
- }
390
- },
391
- "self-hosted": {
392
- database: {
393
- engine: "postgres",
394
- connectionOwnership: "operator"
395
- },
396
- authentication: {
397
- provider: "oidc",
398
- ownerBootstrap: "disabled"
399
- },
400
- tenancy: {
401
- mode: "single-tenant",
402
- context: "defaulted",
403
- isolation: "application"
404
- },
405
- assets: {
406
- provider: "s3-compatible",
407
- ownership: "operator"
408
- },
409
- secrets: {
410
- provider: "environment",
411
- ownership: "operator"
412
- },
413
- jobs: { topology: "external" },
414
- network: {
415
- exposure: "public",
416
- tls: true
417
- },
418
- portability: {
419
- backup: "operator",
420
- logicalExport: true,
421
- logicalImport: true
422
- }
423
- },
424
- cloud: {
425
- database: {
426
- engine: "postgres",
427
- connectionOwnership: "managed"
428
- },
429
- authentication: {
430
- provider: "hosted-identity",
431
- ownerBootstrap: "disabled"
432
- },
433
- tenancy: {
434
- mode: "multi-tenant",
435
- context: "required",
436
- isolation: "database-rls"
437
- },
438
- assets: {
439
- provider: "managed-object-storage",
440
- ownership: "managed"
441
- },
442
- secrets: {
443
- provider: "managed",
444
- ownership: "managed"
445
- },
446
- jobs: { topology: "scalable" },
447
- network: {
448
- exposure: "public",
449
- tls: true
450
- },
451
- portability: {
452
- backup: "managed",
453
- logicalExport: true,
454
- logicalImport: true
455
- }
456
- }
457
- };
458
- var INVARIANTS = {
459
- domainModels: "identical",
460
- generatedRest: "identical",
461
- generatedCli: "identical",
462
- generatedMcp: "identical",
463
- generatedWebMcp: "identical",
464
- mcpExposurePolicy: "identical",
465
- webMcpExposurePolicy: "identical",
466
- actionEffects: "identical",
467
- approvalPolicy: "identical",
468
- authorizationRecords: "required",
469
- jobInvocation: "identical",
470
- portability: "logical-export-import"
471
- };
472
- var PROVIDER_FIELDS = {
473
- database: ["engine", "connectionOwnership"],
474
- authentication: ["provider", "ownerBootstrap"],
475
- tenancy: [
476
- "mode",
477
- "context",
478
- "isolation"
479
- ],
480
- assets: ["provider", "ownership"],
481
- secrets: ["provider", "ownership"],
482
- jobs: ["topology"],
483
- network: ["exposure", "tls"]
484
- };
485
- var PROVIDER_VALUE_DOMAINS = {
486
- database: {
487
- engine: ["sqlite", "postgres"],
488
- connectionOwnership: [
489
- "user",
490
- "operator",
491
- "managed"
492
- ]
493
- },
494
- authentication: {
495
- provider: [
496
- "owner-bootstrap",
497
- "oidc",
498
- "magic-link",
499
- "hosted-identity"
500
- ],
501
- ownerBootstrap: ["single-use", "disabled"]
502
- },
503
- tenancy: {
504
- mode: [
505
- "default-tenant",
506
- "single-tenant",
507
- "multi-tenant"
508
- ],
509
- context: ["defaulted", "required"],
510
- isolation: ["application", "database-rls"]
511
- },
512
- assets: {
513
- provider: [
514
- "local-files",
515
- "s3-compatible",
516
- "managed-object-storage"
517
- ],
518
- ownership: [
519
- "user",
520
- "operator",
521
- "managed"
522
- ]
523
- },
524
- secrets: {
525
- provider: [
526
- "local-file",
527
- "environment",
528
- "external",
529
- "managed"
530
- ],
531
- ownership: [
532
- "user",
533
- "operator",
534
- "managed"
535
- ]
536
- },
537
- jobs: { topology: [
538
- "inline",
539
- "embedded",
540
- "external",
541
- "scalable"
542
- ] },
543
- network: {
544
- exposure: ["loopback", "public"],
545
- tls: [false, true]
546
- }
547
- };
548
- function isPlainRecord(value) {
549
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
550
- const prototype = Object.getPrototypeOf(value);
551
- return prototype === Object.prototype || prototype === null;
552
- }
553
- function cloneDefaults(profile) {
554
- const defaults = PROFILE_DEFAULTS[profile];
555
- return {
556
- database: { ...defaults.database },
557
- authentication: { ...defaults.authentication },
558
- tenancy: { ...defaults.tenancy },
559
- assets: { ...defaults.assets },
560
- secrets: { ...defaults.secrets },
561
- jobs: { ...defaults.jobs },
562
- network: { ...defaults.network },
563
- portability: { ...defaults.portability }
564
- };
565
- }
566
- function rejectUnknownFields(input) {
567
- const issues = [];
568
- for (const key of Object.keys(input).sort()) if (key !== "profile" && key !== "providers") issues.push({
569
- code: "unsupported_field",
570
- path: key,
571
- message: "is not part of the runtime-profile contract.",
572
- recovery: "Move provider selection under providers or remove this field."
573
- });
574
- if (Object.hasOwn(input, "profile") && !isApplicationRuntimeProfile(input.profile)) issues.push(invalidProfileIssue());
575
- const providers = Object.hasOwn(input, "providers") ? input.providers : void 0;
576
- if (providers === void 0) return issues;
577
- if (!isPlainRecord(providers)) {
578
- issues.push({
579
- code: "invalid_config",
580
- path: "providers",
581
- message: "must be an object with Object.prototype or null.",
582
- recovery: "Use providers: { database: { ... } } or omit providers."
583
- });
584
- return issues;
585
- }
586
- for (const providerName of Object.keys(providers).sort()) {
587
- if (!Object.hasOwn(PROVIDER_FIELDS, providerName)) {
588
- issues.push({
589
- code: "unsupported_field",
590
- path: `providers.${providerName}`,
591
- message: "is not an overridable infrastructure provider.",
592
- recovery: "Use database, authentication, tenancy, assets, secrets, jobs, or network."
593
- });
594
- continue;
595
- }
596
- const value = providers[providerName];
597
- if (!isPlainRecord(value)) {
598
- issues.push({
599
- code: "invalid_config",
600
- path: `providers.${providerName}`,
601
- message: "must be an object with Object.prototype or null.",
602
- recovery: `Provide a partial ${providerName} provider object or omit it.`
603
- });
604
- continue;
605
- }
606
- const allowed = PROVIDER_FIELDS[providerName];
607
- const valueDomains = PROVIDER_VALUE_DOMAINS[providerName];
608
- for (const field of Object.keys(value).sort()) {
609
- if (!allowed.includes(field)) {
610
- issues.push({
611
- code: "unsupported_field",
612
- path: `providers.${providerName}.${field}`,
613
- message: "is not a supported provider override.",
614
- recovery: "Remove secret values and application-policy fields; configure only documented provider selectors."
615
- });
616
- continue;
617
- }
618
- const suppliedValue = Object.hasOwn(value, field) ? value[field] : void 0;
619
- const allowedValues = valueDomains[field];
620
- if (suppliedValue !== void 0 && !allowedValues.includes(suppliedValue)) issues.push({
621
- code: "invalid_config",
622
- path: `providers.${providerName}.${field}`,
623
- message: `must be one of ${allowedValues.map(String).join(", ")}.`,
624
- recovery: "Choose a documented provider selector value; supplied values are omitted from diagnostics."
625
- });
626
- }
627
- }
628
- return issues;
629
- }
630
- function validateApplicationRuntimeConfigShape(config) {
631
- if (!isPlainRecord(config)) return [{
632
- code: "invalid_config",
633
- path: "runtime",
634
- message: "must be an object with Object.prototype or null.",
635
- recovery: "Provide runtime: { profile: \"local\" | \"self-hosted\" | \"cloud\" }."
636
- }];
637
- return rejectUnknownFields(config);
638
- }
639
- function applyOverrides(providers, input) {
640
- const overrideRoot = Object.hasOwn(input, "providers") && isPlainRecord(input.providers) ? input.providers : {};
641
- const report = [];
642
- for (const providerName of Object.keys(PROVIDER_FIELDS)) {
643
- if (!Object.hasOwn(overrideRoot, providerName)) continue;
644
- const supplied = overrideRoot[providerName];
645
- if (!isPlainRecord(supplied)) continue;
646
- const target = providers[providerName];
647
- for (const field of PROVIDER_FIELDS[providerName]) {
648
- if (!Object.hasOwn(supplied, field)) continue;
649
- const value = supplied[field];
650
- if (value === void 0) continue;
651
- const previous = target[field];
652
- target[field] = value;
653
- if (value !== previous) report.push({
654
- path: `providers.${providerName}.${field}`,
655
- from: previous,
656
- to: value
657
- });
658
- }
659
- }
660
- return report;
661
- }
662
- function incompatible(issues, path, expected, recovery) {
663
- issues.push({
664
- code: "incompatible_provider",
665
- path,
666
- message: `is incompatible; ${expected}.`,
667
- recovery
668
- });
669
- }
670
- function requireAllowed(issues, path, actual, allowed, profile) {
671
- if (!allowed.includes(actual)) incompatible(issues, path, `${profile} requires one of ${allowed.map(String).join(", ")}`, `Choose a supported ${profile} provider or select a different profile.`);
672
- }
673
- function validateComposition(profile, providers) {
674
- const issues = [];
675
- const allow = (path, actual, values) => requireAllowed(issues, path, actual, values, profile);
676
- if (profile === "local") {
677
- allow("providers.database.engine", providers.database.engine, ["sqlite"]);
678
- allow("providers.database.connectionOwnership", providers.database.connectionOwnership, ["user"]);
679
- allow("providers.authentication.provider", providers.authentication.provider, ["owner-bootstrap"]);
680
- allow("providers.authentication.ownerBootstrap", providers.authentication.ownerBootstrap, ["single-use"]);
681
- allow("providers.tenancy.mode", providers.tenancy.mode, ["default-tenant"]);
682
- allow("providers.tenancy.context", providers.tenancy.context, ["defaulted"]);
683
- allow("providers.tenancy.isolation", providers.tenancy.isolation, ["application"]);
684
- allow("providers.assets.provider", providers.assets.provider, ["local-files"]);
685
- allow("providers.assets.ownership", providers.assets.ownership, ["user"]);
686
- allow("providers.secrets.provider", providers.secrets.provider, ["local-file"]);
687
- allow("providers.secrets.ownership", providers.secrets.ownership, ["user"]);
688
- allow("providers.jobs.topology", providers.jobs.topology, ["inline", "embedded"]);
689
- allow("providers.network.exposure", providers.network.exposure, ["loopback"]);
690
- allow("providers.network.tls", providers.network.tls, [false, true]);
691
- } else if (profile === "self-hosted") {
692
- allow("providers.database.engine", providers.database.engine, ["postgres"]);
693
- allow("providers.database.connectionOwnership", providers.database.connectionOwnership, ["operator"]);
694
- allow("providers.authentication.provider", providers.authentication.provider, ["oidc", "magic-link"]);
695
- allow("providers.authentication.ownerBootstrap", providers.authentication.ownerBootstrap, ["disabled"]);
696
- allow("providers.tenancy.mode", providers.tenancy.mode, ["single-tenant", "multi-tenant"]);
697
- allow("providers.tenancy.context", providers.tenancy.context, providers.tenancy.mode === "multi-tenant" ? ["required"] : ["defaulted"]);
698
- allow("providers.tenancy.isolation", providers.tenancy.isolation, ["application", "database-rls"]);
699
- allow("providers.assets.provider", providers.assets.provider, ["local-files", "s3-compatible"]);
700
- allow("providers.assets.ownership", providers.assets.ownership, ["operator"]);
701
- allow("providers.secrets.provider", providers.secrets.provider, [
702
- "environment",
703
- "local-file",
704
- "external"
705
- ]);
706
- allow("providers.secrets.ownership", providers.secrets.ownership, ["operator"]);
707
- allow("providers.jobs.topology", providers.jobs.topology, ["external"]);
708
- allow("providers.network.exposure", providers.network.exposure, ["public"]);
709
- allow("providers.network.tls", providers.network.tls, [true]);
710
- } else {
711
- allow("providers.database.engine", providers.database.engine, ["postgres"]);
712
- allow("providers.database.connectionOwnership", providers.database.connectionOwnership, ["managed"]);
713
- allow("providers.authentication.provider", providers.authentication.provider, ["hosted-identity"]);
714
- allow("providers.authentication.ownerBootstrap", providers.authentication.ownerBootstrap, ["disabled"]);
715
- allow("providers.tenancy.mode", providers.tenancy.mode, ["multi-tenant"]);
716
- allow("providers.tenancy.context", providers.tenancy.context, ["required"]);
717
- allow("providers.tenancy.isolation", providers.tenancy.isolation, ["application", "database-rls"]);
718
- allow("providers.assets.provider", providers.assets.provider, ["managed-object-storage", "s3-compatible"]);
719
- allow("providers.assets.ownership", providers.assets.ownership, ["managed"]);
720
- allow("providers.secrets.provider", providers.secrets.provider, ["managed", "external"]);
721
- allow("providers.secrets.ownership", providers.secrets.ownership, ["managed"]);
722
- allow("providers.jobs.topology", providers.jobs.topology, ["scalable"]);
723
- allow("providers.network.exposure", providers.network.exposure, ["public"]);
724
- allow("providers.network.tls", providers.network.tls, [true]);
725
- }
726
- return issues;
727
- }
728
- function deriveCapabilities(profile, providers) {
729
- return {
730
- ownerBootstrap: providers.authentication.ownerBootstrap === "single-use",
731
- publicAuthentication: providers.authentication.provider !== "owner-bootstrap",
732
- defaultTenant: providers.tenancy.context === "defaulted",
733
- multiTenant: providers.tenancy.mode === "multi-tenant",
734
- localAssets: providers.assets.provider === "local-files",
735
- externalWorkers: providers.jobs.topology === "external" || providers.jobs.topology === "scalable",
736
- backup: true,
737
- logicalExport: true,
738
- logicalImport: true,
739
- diagnostics: true,
740
- orchestrationHealth: profile !== "local"
741
- };
742
- }
743
- function deepFreeze(value) {
744
- if (value && typeof value === "object" && !Object.isFrozen(value)) {
745
- Object.freeze(value);
746
- for (const child of Object.values(value)) deepFreeze(child);
747
- }
748
- return value;
749
- }
750
- function resolveApplicationRuntime(config) {
751
- if (!isPlainRecord(config)) throw new RuntimeProfileValidationError([...validateApplicationRuntimeConfigShape(config)]);
752
- const unknownIssues = validateApplicationRuntimeConfigShape(config);
753
- const profile = Object.hasOwn(config, "profile") ? config.profile : void 0;
754
- if (!isApplicationRuntimeProfile(profile)) throw new RuntimeProfileValidationError([...unknownIssues, ...Object.hasOwn(config, "profile") ? [] : [invalidProfileIssue()]]);
755
- const providers = cloneDefaults(profile);
756
- const overrides = applyOverrides(providers, config);
757
- const issues = [...unknownIssues, ...validateComposition(profile, providers)];
758
- if (issues.length > 0) throw new RuntimeProfileValidationError(issues);
759
- return deepFreeze({
760
- schemaVersion: 1,
761
- profile,
762
- providers,
763
- capabilities: deriveCapabilities(profile, providers),
764
- invariants: { ...INVARIANTS },
765
- diagnostics: {
766
- secretValuesIncluded: false,
767
- overrides,
768
- unsafeOverrides: []
769
- }
770
- });
771
- }
772
- function getApplicationRuntimePreset(profile) {
773
- if (!isApplicationRuntimeProfile(profile)) throw new RuntimeProfileValidationError([invalidProfileIssue()]);
774
- return deepFreeze(cloneDefaults(profile));
775
- }
776
- //#endregion
777
- //#region src/merge.ts
778
- globalThis.__smrtRuntimeConfig ??= {};
779
- var UNSAFE_KEYS = /* @__PURE__ */ new Set([
780
- "__proto__",
781
- "constructor",
782
- "prototype"
783
- ]);
784
- function defineOwn(target, key, value) {
785
- Object.defineProperty(target, key, {
786
- configurable: true,
787
- enumerable: true,
788
- value,
789
- writable: true
790
- });
791
- }
792
- function isPlainObject(value) {
793
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
794
- const proto = Object.getPrototypeOf(value);
795
- return proto === null || proto === Object.prototype;
796
- }
797
- function deepClone(value) {
798
- if (Array.isArray(value)) return value.map((item) => deepClone(item));
799
- if (isPlainObject(value)) {
800
- const out = {};
801
- for (const key of Object.keys(value)) {
802
- if (UNSAFE_KEYS.has(key)) continue;
803
- defineOwn(out, key, deepClone(value[key]));
804
- }
805
- return out;
806
- }
807
- return value;
808
- }
809
- function deepMerge(target, source) {
810
- const result = {};
811
- const tgt = target;
812
- const src = source;
813
- for (const key of Object.keys(tgt)) {
814
- if (UNSAFE_KEYS.has(key) || Object.hasOwn(src, key)) continue;
815
- defineOwn(result, key, deepClone(tgt[key]));
816
- }
817
- for (const key of Object.keys(src)) {
818
- if (UNSAFE_KEYS.has(key)) continue;
819
- const sourceValue = src[key];
820
- const targetValue = Object.hasOwn(tgt, key) ? tgt[key] : void 0;
821
- if (isPlainObject(sourceValue) && isPlainObject(targetValue)) defineOwn(result, key, deepMerge(targetValue, sourceValue));
822
- else if (sourceValue !== void 0 && sourceValue !== null) defineOwn(result, key, deepClone(sourceValue));
823
- else if (targetValue !== void 0) defineOwn(result, key, deepClone(targetValue));
824
- }
825
- return result;
826
- }
827
- function setConfig$1(config) {
828
- const incomingRuntime = Object.hasOwn(config, "runtime") ? config.runtime : void 0;
829
- if (incomingRuntime !== void 0 && (incomingRuntime === null || typeof incomingRuntime !== "object" || Array.isArray(incomingRuntime))) throw new RuntimeProfileValidationError([...validateApplicationRuntimeConfigShape(incomingRuntime)]);
830
- if (incomingRuntime !== null && (typeof incomingRuntime === "object" || typeof incomingRuntime === "function") && Object.hasOwn(incomingRuntime, "profile")) {
831
- const profileIssues = validateApplicationRuntimeConfigShape(incomingRuntime).filter((issue) => issue.path === "runtime" || issue.path === "profile");
832
- if (profileIssues.length > 0) throw new RuntimeProfileValidationError([...profileIssues]);
833
- }
834
- let current = globalThis.__smrtRuntimeConfig || {};
835
- const currentRuntime = Object.hasOwn(current, "runtime") && isPlainObject(current.runtime) ? current.runtime : void 0;
836
- const incomingRuntimeMap = Object.hasOwn(config, "runtime") && isPlainObject(config.runtime) ? config.runtime : void 0;
837
- if (currentRuntime !== void 0 && incomingRuntimeMap !== void 0 && Object.hasOwn(currentRuntime, "profile") && Object.hasOwn(incomingRuntimeMap, "profile") && currentRuntime.profile !== incomingRuntimeMap.profile) {
838
- current = deepClone(current);
839
- const resetRuntime = current.runtime;
840
- delete resetRuntime.providers;
841
- }
842
- const merged = deepMerge(current, config);
843
- const aiOf = (c) => isPlainObject(c) && isPlainObject(c.ai) ? c.ai : void 0;
844
- const pkgAiOf = (c) => isPlainObject(c) && isPlainObject(c.packages) && isPlainObject(c.packages.ai) ? c.packages.ai : void 0;
845
- if (aiOf(config)) merged.ai = mergeAIConfigObjects(aiOf(current) ?? {}, aiOf(config));
846
- if (pkgAiOf(config)) merged.packages.ai = mergeAIConfigObjects(pkgAiOf(current) ?? {}, pkgAiOf(config));
847
- globalThis.__smrtRuntimeConfig = merged;
848
- }
849
- function getRuntimeConfig() {
850
- return globalThis.__smrtRuntimeConfig || {};
851
- }
852
- function clearRuntimeConfig() {
853
- globalThis.__smrtRuntimeConfig = {};
854
- }
855
- function mergeConfigs(defaults, fileConfig, runtime) {
856
- let result = { ...defaults };
857
- result = deepMerge(result, fileConfig);
858
- result = deepMerge(result, runtime);
859
- return result;
860
- }
861
- //#endregion
862
- //#region src/export.ts
863
- var SECRET_PATTERNS = [
864
- /secret/i,
865
- /password/i,
866
- /passwd/i,
867
- /(^|[_-])pass([_-]|$)/i,
868
- /(^|[_-])pwd([_-]|$)/i,
869
- /[a-z0-9]Pass([_-]|$)/,
870
- /[a-z0-9]Pwd([_-]|$)/,
871
- /^(pass|pwd)$/i,
872
- /token/i,
873
- /credential/i,
874
- /private/i,
875
- /oauth/i,
876
- /authorization/i,
877
- /(^|[_-])auth([_-]|$)/i,
878
- /cookie/i,
879
- /salt/i,
880
- /passphrase/i,
881
- /\bcert\b/i,
882
- /certificate/i,
883
- /api[_-]?key/i,
884
- /access[_-]?key/i,
885
- /secret[_-]?key/i,
886
- /signing[_-]?key/i,
887
- /encryption[_-]?key/i,
888
- /private[_-]?key/i,
889
- /public[_-]?key/i,
890
- /[a-z0-9]Key([_-]|Id|$)/,
891
- /(^|[_-])key([_-]|id$|$)/i,
892
- /connection[_-]?string/i,
893
- /conn[_-]?str/i,
894
- /(^|[_-])db[_-]?url/i,
895
- /database[_-]?url/i,
896
- /datasource[_-]?url/i
897
- ];
898
- function isSecretKey(key) {
899
- return SECRET_PATTERNS.some((pattern) => pattern.test(key));
900
- }
901
- var CREDENTIAL_URL_RE = /([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]+@/gi;
902
- var VALUE_SECRET_PATTERNS = [
903
- {
904
- re: /-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z ]+ )?PRIVATE KEY-----/g,
905
- replace: "***REDACTED PRIVATE KEY***"
906
- },
907
- {
908
- re: /\bsk-(?:proj-|ant-)?[A-Za-z0-9_-]{16,}/g,
909
- replace: "***"
910
- },
911
- {
912
- re: /\b[sr]k_(?:live|test)_[A-Za-z0-9]{16,}/g,
913
- replace: "***"
914
- },
915
- {
916
- re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g,
917
- replace: "***"
918
- },
919
- {
920
- re: /\bgithub_pat_[A-Za-z0-9_]{22,}/g,
921
- replace: "***"
922
- },
923
- {
924
- re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
925
- replace: "***"
926
- },
927
- {
928
- re: /\bAIza[A-Za-z0-9_-]{35}/g,
929
- replace: "***"
930
- },
931
- {
932
- re: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,
933
- replace: "***"
934
- },
935
- {
936
- re: /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/gi,
937
- replace: "Bearer ***"
938
- }
939
- ];
940
- function redactValueSecrets(value) {
941
- let out = value.replace(CREDENTIAL_URL_RE, "$1***@");
942
- for (const { re, replace } of VALUE_SECRET_PATTERNS) out = out.replace(re, replace);
943
- return out;
944
- }
945
- function sanitizeConfig(config) {
946
- if (config === null || config === void 0) return config;
947
- if (Array.isArray(config)) return config.map((item) => sanitizeConfig(item));
948
- if (typeof config === "object") {
949
- const result = {};
950
- for (const [key, value] of Object.entries(config)) {
951
- if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
952
- if (isSecretKey(key)) continue;
953
- result[key] = sanitizeConfig(value);
954
- }
955
- return result;
956
- }
957
- if (typeof config === "string") return redactValueSecrets(config);
958
- return config;
959
- }
960
- function exportConfig(config, options = {}) {
961
- const { includeSecrets = false, format = "json", indent = 2 } = options;
962
- const exportData = includeSecrets ? config : sanitizeConfig(config);
963
- const jsonString = JSON.stringify(exportData, null, indent);
964
- if (format === "js") return `export default ${jsonString};
965
- `;
966
- return jsonString;
967
- }
968
- function parseExportedConfig(content) {
969
- const trimmed = content.trim();
970
- if (trimmed.startsWith("export default")) {
971
- const jsonPart = trimmed.replace(/^export default\s*/, "").replace(/;\s*$/, "");
972
- return JSON.parse(jsonPart);
973
- }
974
- return JSON.parse(content);
975
- }
976
- function mergeExportedConfig(baseConfig, exportedConfig) {
977
- const result = { ...baseConfig };
978
- for (const [key, value] of Object.entries(exportedConfig)) {
979
- if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
980
- if (key === "ai" && value !== null && typeof value === "object" && !Array.isArray(value) && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key])) result[key] = mergeAIConfigObjects(result[key], value);
981
- else if (value !== null && typeof value === "object" && !Array.isArray(value) && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key])) result[key] = mergeExportedConfig(result[key], value);
982
- else result[key] = value;
983
- }
984
- return result;
985
- }
986
59
  //#endregion
987
60
  //#region src/index.ts
988
- globalThis.__smrtConfigCache ??= null;
989
- function getLoadedConfig() {
990
- return globalThis.__smrtConfigCache ?? null;
991
- }
992
- function setLoadedConfig(config) {
993
- globalThis.__smrtConfigCache = config;
994
- }
995
61
  async function loadConfig(options) {
996
62
  const config = await loadConfig$1(options);
997
63
  setLoadedConfig(config);
998
64
  return config;
999
65
  }
1000
- function getConfig() {
1001
- return getLoadedConfig();
1002
- }
1003
- function resolveConfiguredApplicationRuntime() {
1004
- return resolveRuntimeLayers(getLoadedConfig());
1005
- }
1006
- function resolveEffectiveApplicationRuntime(config) {
1007
- if (!declaresRuntime(config ?? null) && !declaresRuntime(getRuntimeConfig())) return resolveApplicationRuntime({ profile: "local" });
1008
- return resolveRuntimeLayers(config ?? null);
1009
- }
1010
- function declaresRuntime(layer) {
1011
- return layer !== null && Object.hasOwn(layer, "runtime") && layer.runtime !== void 0;
1012
- }
1013
- function resolveRuntimeLayers(loadedConfig) {
1014
- const runtimeConfig = getRuntimeConfig();
1015
- const fileRuntime = loadedConfig && Object.hasOwn(loadedConfig, "runtime") ? loadedConfig.runtime : void 0;
1016
- const runtimeOverride = Object.hasOwn(runtimeConfig, "runtime") ? runtimeConfig.runtime : void 0;
1017
- for (const layer of [fileRuntime, runtimeOverride]) {
1018
- if (layer === void 0) continue;
1019
- const shapeIssues = validateApplicationRuntimeConfigShape(layer);
1020
- if (shapeIssues.length > 0) throw new RuntimeProfileValidationError([...shapeIssues]);
1021
- }
1022
- const fileHasProfile = fileRuntime !== void 0 && Object.hasOwn(fileRuntime, "profile");
1023
- const profileChanged = runtimeOverride !== void 0 && Object.hasOwn(runtimeOverride, "profile") && runtimeOverride.profile !== (fileHasProfile ? fileRuntime.profile : void 0);
1024
- if (profileChanged && fileHasProfile) resolveApplicationRuntime(fileRuntime);
1025
- const fileRuntimeWithoutProviders = { ...fileRuntime ?? {} };
1026
- delete fileRuntimeWithoutProviders.providers;
1027
- return resolveApplicationRuntime(mergeConfigs({}, profileChanged ? fileRuntimeWithoutProviders : fileRuntime ?? {}, runtimeOverride ?? {}));
1028
- }
1029
- function getSiteConfig() {
1030
- return (getLoadedConfig() || {}).site || null;
1031
- }
1032
- function getModuleConfig(moduleName, defaults) {
1033
- const fileConfig = getLoadedConfig() || {};
1034
- const runtime = getRuntimeConfig();
1035
- const globalConfig = fileConfig.smrt || {};
1036
- const moduleConfig = fileConfig.modules?.[moduleName] || {};
1037
- const runtimeModuleConfig = runtime.modules?.[moduleName] || {};
1038
- return mergeConfigs(mergeConfigs(mergeConfigs(defaults || {}, globalConfig, {}), moduleConfig, {}), runtimeModuleConfig, {});
1039
- }
1040
- function resolveCliDatabaseConfig(env = process.env) {
1041
- const declared = getPackageConfig("cli").database;
1042
- return resolveCliDatabase(declared, env);
1043
- }
1044
- function getPackageConfig(packageName, defaults) {
1045
- const fileConfig = getLoadedConfig() || {};
1046
- const runtime = getRuntimeConfig();
1047
- const globalConfig = fileConfig.smrt || {};
1048
- const packageConfig = fileConfig.packages?.[packageName] || {};
1049
- const runtimePackageConfig = runtime.packages?.[packageName] || {};
1050
- return mergeConfigs(mergeConfigs(mergeConfigs(defaults || {}, globalConfig, {}), packageConfig, {}), runtimePackageConfig, {});
1051
- }
1052
- function setConfig(config) {
1053
- setConfig$1(config);
1054
- }
1055
- function clearCache() {
1056
- setLoadedConfig(null);
1057
- clearConfigCache();
1058
- clearRuntimeConfig();
1059
- }
1060
- function defineConfig(config) {
1061
- return config;
1062
- }
1063
- function getAIConfigBlock() {
1064
- const file = getLoadedConfig();
1065
- const runtime = getRuntimeConfig();
1066
- return mergeAIConfigLayers([
1067
- file?.packages?.ai,
1068
- file?.ai,
1069
- runtime.packages?.ai,
1070
- runtime.ai
1071
- ]);
1072
- }
1073
- function mergeAIConfigLayers(layers) {
1074
- const FIELDS = [
1075
- "model",
1076
- "baseUrl",
1077
- "apiKeyEnv",
1078
- "apiKey"
1079
- ];
1080
- let provider;
1081
- const values = {};
1082
- const owners = {};
1083
- const text = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
1084
- for (const layer of layers) {
1085
- if (!layer || typeof layer !== "object") continue;
1086
- const rec = layer;
1087
- const named = text(rec.provider) ?? text(rec.type);
1088
- if (named && provider && named.toLowerCase() !== provider.toLowerCase()) for (const f of FIELDS) {
1089
- const owner = owners[f];
1090
- if (owner && owner.toLowerCase() !== named.toLowerCase()) {
1091
- delete values[f];
1092
- delete owners[f];
1093
- }
1094
- }
1095
- if (named) provider = named;
1096
- for (const f of FIELDS) {
1097
- const v = f === "model" ? text(rec.model) ?? text(rec.defaultModel) : text(rec[f]);
1098
- if (v) {
1099
- values[f] = v;
1100
- owners[f] = provider;
1101
- }
1102
- }
1103
- }
1104
- if (!provider && FIELDS.every((f) => !values[f])) return null;
1105
- const merged = { ...values };
1106
- if (provider) merged.provider = provider;
1107
- return merged;
1108
- }
1109
- function resolveConfiguredAIProvider(options = {}) {
1110
- return resolveAIProviderConfig({
1111
- ...options,
1112
- config: options.config === void 0 ? getAIConfigBlock() : options.config
1113
- });
1114
- }
1115
- function tryResolveConfiguredAIProvider(options = {}) {
1116
- return tryResolveAIProviderConfig({
1117
- ...options,
1118
- config: options.config === void 0 ? getAIConfigBlock() : options.config
1119
- });
1120
- }
1121
66
  //#endregion
1122
67
  export { AIProviderNotConfiguredError, AI_PROVIDER_KEY_ENV, DEFAULT_AI_ENV_PREFIXES, RuntimeProfileValidationError, canonicalizeAIConfig, clearCache, clearRuntimeConfig, defineConfig, describeAIProviderConfig, exportConfig, getAIConfigBlock, getApplicationRuntimePreset, getConfig, getDefaultAIKeyEnvName, getModuleConfig, getPackageConfig, getRuntimeConfig, getSiteConfig, loadConfig, mergeAIConfigLayers, mergeAIConfigObjects, mergeConfigs, mergeExportedConfig, parseExportedConfig, redactBaseUrl, resolveAIProviderConfig, resolveApplicationRuntime, resolveCliDatabaseConfig, resolveConfiguredAIProvider, resolveConfiguredApplicationRuntime, resolveEffectiveApplicationRuntime, sanitizeConfig, setConfig, toAIClientOptions, tryResolveAIProviderConfig, tryResolveConfiguredAIProvider, withAIAliases };
1123
68