@fonoster/common 0.27.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistants/index.d.ts +2 -6
- package/dist/assistants/index.js +2 -6
- package/dist/assistants/sessionAssistantSchema.d.ts +741 -0
- package/dist/assistants/sessionAssistantSchema.js +112 -0
- package/dist/assistants/testCasesSchema.d.ts +4 -4
- package/dist/{voice/Dial.d.ts → calls/index.d.ts} +16 -13
- package/dist/calls/index.js +44 -0
- package/dist/cellsecrets/index.d.ts +91 -0
- package/dist/cellsecrets/index.js +299 -0
- package/dist/config/cli/check.d.ts +6 -0
- package/dist/config/cli/check.js +202 -0
- package/dist/config/cli/envFile.d.ts +23 -0
- package/dist/config/cli/envFile.js +81 -0
- package/dist/config/cli/exposure.d.ts +10 -0
- package/dist/config/cli/exposure.js +93 -0
- package/dist/config/cli/index.d.ts +2 -0
- package/dist/{identity/tokenHasAccessKeyId.js → config/cli/index.js} +7 -8
- package/dist/config/cli/info.d.ts +6 -0
- package/dist/config/cli/info.js +62 -0
- package/dist/config/cli/init.d.ts +9 -0
- package/dist/config/cli/init.js +152 -0
- package/dist/config/cli/main.d.ts +11 -0
- package/dist/config/cli/main.js +155 -0
- package/dist/config/cli/render.d.ts +44 -0
- package/dist/config/cli/render.js +129 -0
- package/dist/config/cli/secrets.d.ts +21 -0
- package/dist/config/cli/secrets.js +117 -0
- package/dist/{utils/findIntegrationsCredentials.d.ts → config/cli/types.d.ts} +6 -5
- package/dist/config/cli/types.js +7 -0
- package/dist/config/configHints.d.ts +39 -0
- package/dist/config/configHints.js +61 -0
- package/dist/{voice → config}/index.d.ts +12 -13
- package/dist/config/index.js +46 -0
- package/dist/config/integrationsSchema.d.ts +32 -0
- package/dist/config/integrationsSchema.js +56 -0
- package/dist/config/interpolateEnv.d.ts +34 -0
- package/dist/config/interpolateEnv.js +70 -0
- package/dist/config/loadServiceConfig.d.ts +33 -0
- package/dist/config/loadServiceConfig.js +121 -0
- package/dist/config/logsSchema.d.ts +76 -0
- package/dist/config/logsSchema.js +49 -0
- package/dist/config/natsConnection.d.ts +53 -0
- package/dist/config/natsConnection.js +58 -0
- package/dist/config/parseConfigFile.d.ts +7 -0
- package/dist/config/parseConfigFile.js +60 -0
- package/dist/config/resolveConfigFile.d.ts +17 -0
- package/dist/config/resolveConfigFile.js +54 -0
- package/dist/config/resolveSecretRefs.d.ts +35 -0
- package/dist/config/resolveSecretRefs.js +129 -0
- package/dist/{assistants/audioFiltersSchema.d.ts → config/strictDeep.d.ts} +9 -12
- package/dist/config/strictDeep.js +58 -0
- package/dist/config/telemetrySchema.d.ts +39 -0
- package/dist/config/telemetrySchema.js +44 -0
- package/dist/config/toJsonSchema.d.ts +30 -0
- package/dist/config/toJsonSchema.js +161 -0
- package/dist/constants.d.ts +1 -4
- package/dist/constants.js +4 -5
- package/dist/envs.d.ts +2 -1
- package/dist/envs.js +13 -11
- package/dist/identity/createAuthInterceptor.d.ts +9 -2
- package/dist/identity/createAuthInterceptor.js +36 -30
- package/dist/identity/hasAccess.d.ts +9 -3
- package/dist/identity/hasAccess.js +32 -8
- package/dist/identity/index.d.ts +1 -0
- package/dist/identity/index.js +1 -0
- package/dist/identity/isValidToken.d.ts +12 -2
- package/dist/identity/isValidToken.js +25 -9
- package/dist/identity/roles.js +22 -6
- package/dist/identity/types.d.ts +6 -1
- package/dist/identity/types.js +1 -0
- package/dist/{assistants/LanguageModelProvider.js → identity/verifyAccessToken.d.ts} +10 -11
- package/dist/identity/verifyAccessToken.js +44 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +14 -2
- package/dist/net/assertPublicDestination.d.ts +52 -0
- package/dist/net/assertPublicDestination.js +342 -0
- package/dist/{voice/PlayDtmf.d.ts → net/index.d.ts} +1 -5
- package/dist/{voice → net}/index.js +1 -13
- package/dist/notifications/createSendEmail.js +3 -1
- package/dist/protos/applications.proto +124 -3
- package/dist/protos/authz.proto +23 -4
- package/dist/protos/capabilities.proto +94 -0
- package/dist/protos/identity.proto +26 -1
- package/dist/protos/providers.proto +135 -0
- package/dist/protos/webhooks.proto +140 -0
- package/dist/{voice/StopSay.d.ts → regions/hub/index.d.ts} +1 -6
- package/dist/regions/hub/index.js +35 -0
- package/dist/regions/hub/natsJwt.d.ts +63 -0
- package/dist/regions/hub/natsJwt.js +195 -0
- package/dist/registry/Registry.d.ts +79 -0
- package/dist/registry/Registry.js +418 -0
- package/dist/registry/cli.d.ts +20 -0
- package/dist/registry/cli.js +424 -0
- package/dist/{assistants/audioFiltersSchema.js → registry/features.d.ts} +13 -14
- package/dist/registry/features.js +36 -0
- package/dist/{assistants/LanguageModelProvider.d.ts → registry/index.d.ts} +6 -8
- package/dist/registry/index.js +40 -0
- package/dist/registry/prisma.d.ts +67 -0
- package/dist/registry/prisma.js +391 -0
- package/dist/registry/stores.d.ts +21 -0
- package/dist/{assistants/eventsHookSchema.js → registry/stores.js} +55 -18
- package/dist/registry/types.d.ts +98 -0
- package/dist/{voice/Say.d.ts → registry/types.js} +4 -10
- package/dist/registry/workspaceKeys.d.ts +41 -0
- package/dist/registry/workspaceKeys.js +64 -0
- package/dist/session/index.d.ts +19 -0
- package/dist/session/index.js +35 -0
- package/dist/session/schema.d.ts +1951 -0
- package/dist/session/schema.js +397 -0
- package/dist/sessions/index.d.ts +93 -0
- package/dist/sessions/index.js +194 -0
- package/dist/telemetry.d.ts +76 -0
- package/dist/telemetry.js +360 -0
- package/dist/types.d.ts +3 -5
- package/dist/utils/{findIntegrationsCredentials.js → WriteBehind.d.ts} +13 -10
- package/dist/utils/WriteBehind.js +64 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/{voice/Play.d.ts → utils/logUnhandledRejections.d.ts} +8 -12
- package/dist/utils/logUnhandledRejections.js +37 -0
- package/dist/validators/calls.d.ts +2 -2
- package/dist/validators/calls.js +5 -2
- package/dist/validators/identity.d.ts +37 -8
- package/dist/validators/identity.js +22 -3
- package/dist/validators/index.d.ts +1 -0
- package/dist/validators/index.js +1 -0
- package/dist/validators/sip.d.ts +40 -0
- package/dist/validators/sip.js +99 -0
- package/dist/validators/sipnet/credentials.d.ts +2 -2
- package/dist/validators/sipnet/domains.js +7 -7
- package/dist/validators/sipnet/trunks.d.ts +14 -14
- package/dist/validators/sipnet/trunks.js +4 -2
- package/dist/webhooks/Dispatcher.d.ts +87 -0
- package/dist/webhooks/Dispatcher.js +225 -0
- package/dist/webhooks/bus.d.ts +49 -0
- package/dist/webhooks/bus.js +170 -0
- package/dist/webhooks/events.d.ts +66 -0
- package/dist/webhooks/events.js +62 -0
- package/dist/webhooks/index.d.ts +23 -0
- package/dist/webhooks/index.js +39 -0
- package/dist/webhooks/prisma.d.ts +55 -0
- package/dist/webhooks/prisma.js +242 -0
- package/dist/webhooks/signing.d.ts +8 -0
- package/dist/webhooks/signing.js +53 -0
- package/package.json +25 -12
- package/schema/session.v1.json +791 -0
- package/dist/assistants/assistantSchema.d.ts +0 -739
- package/dist/assistants/assistantSchema.js +0 -37
- package/dist/assistants/conversationSettingsSchema.d.ts +0 -122
- package/dist/assistants/conversationSettingsSchema.js +0 -133
- package/dist/assistants/eventsHookSchema.d.ts +0 -38
- package/dist/assistants/languageModelConfigSchema.d.ts +0 -206
- package/dist/assistants/languageModelConfigSchema.js +0 -91
- package/dist/identity/tokenHasAccessKeyId.d.ts +0 -2
- package/dist/protos/voice.proto +0 -557
- package/dist/voice/Dial.js +0 -20
- package/dist/voice/Gather.d.ts +0 -37
- package/dist/voice/Gather.js +0 -9
- package/dist/voice/Mute.d.ts +0 -31
- package/dist/voice/Mute.js +0 -9
- package/dist/voice/Play.js +0 -2
- package/dist/voice/PlayDtmf.js +0 -2
- package/dist/voice/PlaybackControl.d.ts +0 -31
- package/dist/voice/PlaybackControl.js +0 -11
- package/dist/voice/Record.d.ts +0 -36
- package/dist/voice/Record.js +0 -7
- package/dist/voice/Say.js +0 -2
- package/dist/voice/StopSay.js +0 -2
- package/dist/voice/Stream.d.ts +0 -53
- package/dist/voice/Stream.js +0 -19
- package/dist/voice/StreamGather.d.ts +0 -34
- package/dist/voice/StreamGather.js +0 -9
- package/dist/voice/Verb.d.ts +0 -27
- package/dist/voice/Verb.js +0 -2
- package/dist/voice/voice.d.ts +0 -171
- package/dist/voice/voice.js +0 -65
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_EXTENSIONS = void 0;
|
|
4
|
+
exports.parseConfigFile = parseConfigFile;
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
7
|
+
* http://github.com/fonoster/fonoster
|
|
8
|
+
*
|
|
9
|
+
* This file is part of Fonoster
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the MIT License (the "License");
|
|
12
|
+
* you may not use this file except in compliance with
|
|
13
|
+
* the License. You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* https://opensource.org/licenses/MIT
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
const path_1 = require("path");
|
|
24
|
+
const jsonc_parser_1 = require("jsonc-parser");
|
|
25
|
+
const yaml_1 = require("yaml");
|
|
26
|
+
const YAML_EXTENSIONS = [".yaml", ".yml"];
|
|
27
|
+
const JSON_EXTENSIONS = [".json", ".jsonc"];
|
|
28
|
+
const CONFIG_EXTENSIONS = [...YAML_EXTENSIONS, ...JSON_EXTENSIONS];
|
|
29
|
+
exports.CONFIG_EXTENSIONS = CONFIG_EXTENSIONS;
|
|
30
|
+
function lineAndColumn(text, offset) {
|
|
31
|
+
const before = text.slice(0, offset);
|
|
32
|
+
const line = before.split("\n").length;
|
|
33
|
+
const column = offset - before.lastIndexOf("\n");
|
|
34
|
+
return { line, column };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses a config file's text. The extension picks the format: `.yaml`/`.yml`
|
|
38
|
+
* as YAML, `.json`/`.jsonc` as JSON with comments and trailing commas.
|
|
39
|
+
*/
|
|
40
|
+
function parseConfigFile(path, text) {
|
|
41
|
+
var _a;
|
|
42
|
+
const extension = (0, path_1.extname)(path).toLowerCase();
|
|
43
|
+
if (YAML_EXTENSIONS.includes(extension)) {
|
|
44
|
+
return (_a = (0, yaml_1.parse)(text)) !== null && _a !== void 0 ? _a : {};
|
|
45
|
+
}
|
|
46
|
+
if (JSON_EXTENSIONS.includes(extension)) {
|
|
47
|
+
const errors = [];
|
|
48
|
+
const value = (0, jsonc_parser_1.parse)(text, errors, {
|
|
49
|
+
allowTrailingComma: true,
|
|
50
|
+
disallowComments: false
|
|
51
|
+
});
|
|
52
|
+
if (errors.length > 0) {
|
|
53
|
+
const first = errors[0];
|
|
54
|
+
const { line, column } = lineAndColumn(text, first.offset);
|
|
55
|
+
throw new Error(`${(0, jsonc_parser_1.printParseErrorCode)(first.error)} at line ${line}, column ${column}`);
|
|
56
|
+
}
|
|
57
|
+
return value !== null && value !== void 0 ? value : {};
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`unsupported extension "${extension}" (use ${CONFIG_EXTENSIONS.join(", ")})`);
|
|
60
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const DEFAULT_SEARCH_DIRS: string[];
|
|
2
|
+
type ResolvedConfigFile = {
|
|
3
|
+
path: string;
|
|
4
|
+
explicit: boolean;
|
|
5
|
+
} | {
|
|
6
|
+
path: undefined;
|
|
7
|
+
explicit: false;
|
|
8
|
+
searched: string[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Finds a service's config file. `--config <path>` wins; otherwise each search
|
|
12
|
+
* directory is tried in order for `<fileBase>` with a supported extension.
|
|
13
|
+
* Two candidates in the same directory is an error: the operator would not
|
|
14
|
+
* know which one is in effect.
|
|
15
|
+
*/
|
|
16
|
+
declare function resolveConfigFile(fileBase: string, argv: string[], searchDirs?: string[]): ResolvedConfigFile;
|
|
17
|
+
export { resolveConfigFile, DEFAULT_SEARCH_DIRS, ResolvedConfigFile };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_SEARCH_DIRS = void 0;
|
|
4
|
+
exports.resolveConfigFile = resolveConfigFile;
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
7
|
+
* http://github.com/fonoster/fonoster
|
|
8
|
+
*
|
|
9
|
+
* This file is part of Fonoster
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the MIT License (the "License");
|
|
12
|
+
* you may not use this file except in compliance with
|
|
13
|
+
* the License. You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* https://opensource.org/licenses/MIT
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
const fs_1 = require("fs");
|
|
24
|
+
const path_1 = require("path");
|
|
25
|
+
const parseConfigFile_1 = require("./parseConfigFile");
|
|
26
|
+
// `/config` is where the bundle mounts each service's file (unify-config-placeholders).
|
|
27
|
+
const DEFAULT_SEARCH_DIRS = ["/config", "/opt/fonoster", "./config"];
|
|
28
|
+
exports.DEFAULT_SEARCH_DIRS = DEFAULT_SEARCH_DIRS;
|
|
29
|
+
/**
|
|
30
|
+
* Finds a service's config file. `--config <path>` wins; otherwise each search
|
|
31
|
+
* directory is tried in order for `<fileBase>` with a supported extension.
|
|
32
|
+
* Two candidates in the same directory is an error: the operator would not
|
|
33
|
+
* know which one is in effect.
|
|
34
|
+
*/
|
|
35
|
+
function resolveConfigFile(fileBase, argv, searchDirs = DEFAULT_SEARCH_DIRS) {
|
|
36
|
+
const flagIndex = argv.indexOf("--config");
|
|
37
|
+
const fromFlag = flagIndex >= 0 ? argv[flagIndex + 1] : undefined;
|
|
38
|
+
if (fromFlag) {
|
|
39
|
+
return { path: (0, path_1.resolve)(process.cwd(), fromFlag), explicit: true };
|
|
40
|
+
}
|
|
41
|
+
const searched = [];
|
|
42
|
+
for (const dir of searchDirs) {
|
|
43
|
+
const candidates = parseConfigFile_1.CONFIG_EXTENSIONS.map((extension) => (0, path_1.resolve)(process.cwd(), (0, path_1.join)(dir, `${fileBase}${extension}`)));
|
|
44
|
+
searched.push(...candidates);
|
|
45
|
+
const found = candidates.filter((candidate) => (0, fs_1.existsSync)(candidate));
|
|
46
|
+
if (found.length > 1) {
|
|
47
|
+
throw new Error(`found more than one ${fileBase} config file (${found.join(", ")}); keep only one`);
|
|
48
|
+
}
|
|
49
|
+
if (found.length === 1) {
|
|
50
|
+
return { path: found[0], explicit: false };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { path: undefined, explicit: false, searched };
|
|
54
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type Env = Record<string, string | undefined>;
|
|
2
|
+
/**
|
|
3
|
+
* `secret://` references in config values (add-platform, decision D8). A string value that is
|
|
4
|
+
* exactly a reference is replaced at startup by the secret it names, so YAML never holds the raw
|
|
5
|
+
* secret:
|
|
6
|
+
*
|
|
7
|
+
* secret://env/NAME the environment variable NAME
|
|
8
|
+
* secret://file/<absolute path> a file's contents (e.g. secret://file/run/secrets/db-url)
|
|
9
|
+
* secret://k8s/<secret>#<key> a Kubernetes Secret mounted as a volume, read from
|
|
10
|
+
* secret://k8s/<ns>/<secret>#<key> <FONOSTER_SECRETS_DIR>/<secret>/<key>
|
|
11
|
+
* (default /var/run/fonoster/secrets; the Helm chart mounts
|
|
12
|
+
* every referenced Secret there). The namespace, when given,
|
|
13
|
+
* must be the pod's (POD_NAMESPACE) if that is known.
|
|
14
|
+
*
|
|
15
|
+
* File contents lose one trailing newline. Errors name the key path and the reference, never a
|
|
16
|
+
* value. Values that only contain `secret://` somewhere else are left alone.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SECRET_PREFIX = "secret://";
|
|
19
|
+
export declare const DEFAULT_SECRETS_DIR = "/var/run/fonoster/secrets";
|
|
20
|
+
export type SecretRefOptions = {
|
|
21
|
+
env?: Env;
|
|
22
|
+
/** Where Kubernetes Secrets are mounted (default FONOSTER_SECRETS_DIR or /var/run/fonoster/secrets). */
|
|
23
|
+
secretsDir?: string;
|
|
24
|
+
/** Reads a file (tests). */
|
|
25
|
+
readFile?: (path: string) => string;
|
|
26
|
+
};
|
|
27
|
+
export declare function isSecretRef(value: unknown): value is string;
|
|
28
|
+
/** Resolves one `secret://` reference. Throws with a message that never contains the secret. */
|
|
29
|
+
export declare function resolveSecretRef(ref: string, options?: SecretRefOptions): string;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a copy of a parsed config with every `secret://` value resolved. Errors name the key
|
|
32
|
+
* path (`providers.deepgram.credential.value: secret://env/DG: environment variable DG is not set`).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSecretRefs<T>(input: T, options?: SecretRefOptions): T;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_SECRETS_DIR = exports.SECRET_PREFIX = void 0;
|
|
4
|
+
exports.isSecretRef = isSecretRef;
|
|
5
|
+
exports.resolveSecretRef = resolveSecretRef;
|
|
6
|
+
exports.resolveSecretRefs = resolveSecretRefs;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
9
|
+
* http://github.com/fonoster/fonoster
|
|
10
|
+
*
|
|
11
|
+
* This file is part of Fonoster
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the MIT License (the "License");
|
|
14
|
+
* you may not use this file except in compliance with
|
|
15
|
+
* the License. You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* https://opensource.org/licenses/MIT
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*/
|
|
25
|
+
const fs_1 = require("fs");
|
|
26
|
+
const path_1 = require("path");
|
|
27
|
+
/**
|
|
28
|
+
* `secret://` references in config values (add-platform, decision D8). A string value that is
|
|
29
|
+
* exactly a reference is replaced at startup by the secret it names, so YAML never holds the raw
|
|
30
|
+
* secret:
|
|
31
|
+
*
|
|
32
|
+
* secret://env/NAME the environment variable NAME
|
|
33
|
+
* secret://file/<absolute path> a file's contents (e.g. secret://file/run/secrets/db-url)
|
|
34
|
+
* secret://k8s/<secret>#<key> a Kubernetes Secret mounted as a volume, read from
|
|
35
|
+
* secret://k8s/<ns>/<secret>#<key> <FONOSTER_SECRETS_DIR>/<secret>/<key>
|
|
36
|
+
* (default /var/run/fonoster/secrets; the Helm chart mounts
|
|
37
|
+
* every referenced Secret there). The namespace, when given,
|
|
38
|
+
* must be the pod's (POD_NAMESPACE) if that is known.
|
|
39
|
+
*
|
|
40
|
+
* File contents lose one trailing newline. Errors name the key path and the reference, never a
|
|
41
|
+
* value. Values that only contain `secret://` somewhere else are left alone.
|
|
42
|
+
*/
|
|
43
|
+
exports.SECRET_PREFIX = "secret://";
|
|
44
|
+
exports.DEFAULT_SECRETS_DIR = "/var/run/fonoster/secrets";
|
|
45
|
+
const NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
46
|
+
const K8S_NAME = /^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$/;
|
|
47
|
+
const K8S_KEY = /^[-._a-zA-Z0-9]+$/;
|
|
48
|
+
function isSecretRef(value) {
|
|
49
|
+
return typeof value === "string" && value.startsWith(exports.SECRET_PREFIX);
|
|
50
|
+
}
|
|
51
|
+
function readSecretFile(ref, path, options) {
|
|
52
|
+
var _a;
|
|
53
|
+
const read = (_a = options.readFile) !== null && _a !== void 0 ? _a : ((p) => (0, fs_1.readFileSync)(p, { encoding: "utf8" }));
|
|
54
|
+
try {
|
|
55
|
+
return read(path).replace(/\r?\n$/, "");
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
const code = e.code;
|
|
59
|
+
throw new Error(`${ref}: cannot read ${path}${code ? ` (${code})` : ""}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Resolves one `secret://` reference. Throws with a message that never contains the secret. */
|
|
63
|
+
function resolveSecretRef(ref, options = {}) {
|
|
64
|
+
var _a, _b, _c;
|
|
65
|
+
const env = (_a = options.env) !== null && _a !== void 0 ? _a : process.env;
|
|
66
|
+
const rest = ref.slice(exports.SECRET_PREFIX.length);
|
|
67
|
+
const slash = rest.indexOf("/");
|
|
68
|
+
const kind = slash < 0 ? rest : rest.slice(0, slash);
|
|
69
|
+
const target = slash < 0 ? "" : rest.slice(slash + 1);
|
|
70
|
+
if (kind === "env") {
|
|
71
|
+
if (!NAME.test(target))
|
|
72
|
+
throw new Error(`invalid variable name in ${ref}`);
|
|
73
|
+
const value = env[target];
|
|
74
|
+
if (value === undefined || value === "")
|
|
75
|
+
throw new Error(`${ref}: environment variable ${target} is not set`);
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
if (kind === "file") {
|
|
79
|
+
if (!target)
|
|
80
|
+
throw new Error(`${ref}: missing file path`);
|
|
81
|
+
const path = (0, path_1.resolve)("/", target);
|
|
82
|
+
return readSecretFile(ref, path, options);
|
|
83
|
+
}
|
|
84
|
+
if (kind === "k8s") {
|
|
85
|
+
const hash = target.indexOf("#");
|
|
86
|
+
if (hash < 0)
|
|
87
|
+
throw new Error(`${ref}: missing #<key>`);
|
|
88
|
+
const parts = target.slice(0, hash).split("/");
|
|
89
|
+
const key = target.slice(hash + 1);
|
|
90
|
+
if (parts.length > 2 || !parts.every((p) => K8S_NAME.test(p)))
|
|
91
|
+
throw new Error(`${ref}: expected secret://k8s/[<namespace>/]<secret>#<key>`);
|
|
92
|
+
if (!K8S_KEY.test(key))
|
|
93
|
+
throw new Error(`${ref}: invalid key`);
|
|
94
|
+
const [namespace, name] = parts.length === 2 ? parts : [undefined, parts[0]];
|
|
95
|
+
const podNamespace = env.POD_NAMESPACE;
|
|
96
|
+
if (namespace && podNamespace && namespace !== podNamespace)
|
|
97
|
+
throw new Error(`${ref}: Secrets of namespace ${namespace} are not mounted here (${podNamespace})`);
|
|
98
|
+
const dir = (_c = (_b = options.secretsDir) !== null && _b !== void 0 ? _b : env.FONOSTER_SECRETS_DIR) !== null && _c !== void 0 ? _c : exports.DEFAULT_SECRETS_DIR;
|
|
99
|
+
const base = (0, path_1.isAbsolute)(dir) ? dir : (0, path_1.resolve)(dir);
|
|
100
|
+
return readSecretFile(ref, (0, path_1.join)(base, name, key), options);
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`unsupported reference ${ref} (use secret://env/NAME, secret://file/<path> or secret://k8s/<secret>#<key>)`);
|
|
103
|
+
}
|
|
104
|
+
function walk(input, options, path) {
|
|
105
|
+
if (isSecretRef(input)) {
|
|
106
|
+
try {
|
|
107
|
+
return resolveSecretRef(input, options);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
throw new Error(`${path || "(root)"}: ${e.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(input))
|
|
114
|
+
return input.map((item, i) => walk(item, options, `${path}[${i}]`));
|
|
115
|
+
if (input !== null && typeof input === "object") {
|
|
116
|
+
return Object.fromEntries(Object.entries(input).map(([k, v]) => [
|
|
117
|
+
k,
|
|
118
|
+
walk(v, options, path ? `${path}.${k}` : k)
|
|
119
|
+
]));
|
|
120
|
+
}
|
|
121
|
+
return input;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns a copy of a parsed config with every `secret://` value resolved. Errors name the key
|
|
125
|
+
* path (`providers.deepgram.credential.value: secret://env/DG: environment variable DG is not set`).
|
|
126
|
+
*/
|
|
127
|
+
function resolveSecretRefs(input, options = {}) {
|
|
128
|
+
return walk(input, options, "");
|
|
129
|
+
}
|
|
@@ -16,15 +16,12 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
name?: string;
|
|
29
|
-
}>, "many">>;
|
|
30
|
-
export { audioFiltersSchema };
|
|
19
|
+
import { ZodTypeAny } from "zod";
|
|
20
|
+
/**
|
|
21
|
+
* Returns a copy of `schema` in which every object rejects unknown keys, so a
|
|
22
|
+
* misspelled config key fails validation instead of being silently dropped.
|
|
23
|
+
* Objects declared with `.passthrough()` (free-form blocks such as provider
|
|
24
|
+
* credentials) are left as they are.
|
|
25
|
+
*/
|
|
26
|
+
declare function strictDeep<T extends ZodTypeAny>(schema: T): T;
|
|
27
|
+
export { strictDeep };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.strictDeep = strictDeep;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const zod_1 = require("zod");
|
|
23
|
+
/**
|
|
24
|
+
* Returns a copy of `schema` in which every object rejects unknown keys, so a
|
|
25
|
+
* misspelled config key fails validation instead of being silently dropped.
|
|
26
|
+
* Objects declared with `.passthrough()` (free-form blocks such as provider
|
|
27
|
+
* credentials) are left as they are.
|
|
28
|
+
*/
|
|
29
|
+
function strictDeep(schema) {
|
|
30
|
+
if (schema instanceof zod_1.ZodObject) {
|
|
31
|
+
const shape = schema.shape;
|
|
32
|
+
const strictShape = Object.fromEntries(Object.entries(shape).map(([key, value]) => [key, strictDeep(value)]));
|
|
33
|
+
const unknownKeys = schema._def.unknownKeys === "passthrough" ? "passthrough" : "strict";
|
|
34
|
+
return new zod_1.ZodObject(Object.assign(Object.assign({}, schema._def), { shape: () => strictShape, unknownKeys }));
|
|
35
|
+
}
|
|
36
|
+
if (schema instanceof zod_1.ZodOptional) {
|
|
37
|
+
return new zod_1.ZodOptional(Object.assign(Object.assign({}, schema._def), { innerType: strictDeep(schema._def.innerType) }));
|
|
38
|
+
}
|
|
39
|
+
if (schema instanceof zod_1.ZodNullable) {
|
|
40
|
+
return new zod_1.ZodNullable(Object.assign(Object.assign({}, schema._def), { innerType: strictDeep(schema._def.innerType) }));
|
|
41
|
+
}
|
|
42
|
+
if (schema instanceof zod_1.ZodDefault) {
|
|
43
|
+
return new zod_1.ZodDefault(Object.assign(Object.assign({}, schema._def), { innerType: strictDeep(schema._def.innerType) }));
|
|
44
|
+
}
|
|
45
|
+
if (schema instanceof zod_1.ZodArray) {
|
|
46
|
+
return new zod_1.ZodArray(Object.assign(Object.assign({}, schema._def), { type: strictDeep(schema._def.type) }));
|
|
47
|
+
}
|
|
48
|
+
if (schema instanceof zod_1.ZodEffects) {
|
|
49
|
+
return new zod_1.ZodEffects(Object.assign(Object.assign({}, schema._def), { schema: strictDeep(schema._def.schema) }));
|
|
50
|
+
}
|
|
51
|
+
if (schema instanceof zod_1.ZodUnion) {
|
|
52
|
+
return new zod_1.ZodUnion(Object.assign(Object.assign({}, schema._def), { options: schema._def.options.map((option) => strictDeep(option)) }));
|
|
53
|
+
}
|
|
54
|
+
if (schema instanceof zod_1.ZodRecord) {
|
|
55
|
+
return new zod_1.ZodRecord(Object.assign(Object.assign({}, schema._def), { valueType: strictDeep(schema._def.valueType) }));
|
|
56
|
+
}
|
|
57
|
+
return schema;
|
|
58
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
/**
|
|
21
|
+
* The `telemetry` section shared by every Fonoster service's config file
|
|
22
|
+
* (unify-config-placeholders): the OTLP/HTTP endpoint telemetry goes to. Empty or absent means
|
|
23
|
+
* off, unless the container environment sets `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
24
|
+
*/
|
|
25
|
+
declare const telemetrySchema: z.ZodOptional<z.ZodObject<{
|
|
26
|
+
/** Usually `"${OTEL_EXPORTER_OTLP_ENDPOINT:-}"`, e.g. http://otel-collector:4318. */
|
|
27
|
+
otlpEndpoint: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
otlpEndpoint?: string;
|
|
30
|
+
}, {
|
|
31
|
+
otlpEndpoint?: string;
|
|
32
|
+
}>>;
|
|
33
|
+
type TelemetryConfig = z.infer<typeof telemetrySchema>;
|
|
34
|
+
/**
|
|
35
|
+
* The environment `initTelemetry` reads, with the config's endpoint (when set) in
|
|
36
|
+
* `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
37
|
+
*/
|
|
38
|
+
declare function telemetryEnv(config: TelemetryConfig, env?: Record<string, string | undefined>): Record<string, string | undefined>;
|
|
39
|
+
export { telemetrySchema, telemetryEnv, TelemetryConfig };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.telemetrySchema = void 0;
|
|
4
|
+
exports.telemetryEnv = telemetryEnv;
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
7
|
+
* http://github.com/fonoster/fonoster
|
|
8
|
+
*
|
|
9
|
+
* This file is part of Fonoster
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the MIT License (the "License");
|
|
12
|
+
* you may not use this file except in compliance with
|
|
13
|
+
* the License. You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* https://opensource.org/licenses/MIT
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
23
|
+
const zod_1 = require("zod");
|
|
24
|
+
/**
|
|
25
|
+
* The `telemetry` section shared by every Fonoster service's config file
|
|
26
|
+
* (unify-config-placeholders): the OTLP/HTTP endpoint telemetry goes to. Empty or absent means
|
|
27
|
+
* off, unless the container environment sets `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
28
|
+
*/
|
|
29
|
+
const telemetrySchema = zod_1.z
|
|
30
|
+
.object({
|
|
31
|
+
/** Usually `"${OTEL_EXPORTER_OTLP_ENDPOINT:-}"`, e.g. http://otel-collector:4318. */
|
|
32
|
+
otlpEndpoint: zod_1.z.string().optional()
|
|
33
|
+
})
|
|
34
|
+
.optional();
|
|
35
|
+
exports.telemetrySchema = telemetrySchema;
|
|
36
|
+
/**
|
|
37
|
+
* The environment `initTelemetry` reads, with the config's endpoint (when set) in
|
|
38
|
+
* `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
39
|
+
*/
|
|
40
|
+
function telemetryEnv(config, env = process.env) {
|
|
41
|
+
var _a;
|
|
42
|
+
const endpoint = (_a = config === null || config === void 0 ? void 0 : config.otlpEndpoint) === null || _a === void 0 ? void 0 : _a.trim();
|
|
43
|
+
return endpoint ? Object.assign(Object.assign({}, env), { OTEL_EXPORTER_OTLP_ENDPOINT: endpoint }) : env;
|
|
44
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { ZodTypeAny } from "zod";
|
|
20
|
+
export type JsonSchema = Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* The JSON Schema (draft 2020-12) of a service config schema. Unknown keys are rejected
|
|
23
|
+
* (`additionalProperties: false`) wherever the service rejects them: every object, as
|
|
24
|
+
* `strictDeep` makes them, except `.passthrough()` blocks.
|
|
25
|
+
*/
|
|
26
|
+
declare function toJsonSchema(schema: ZodTypeAny, options?: {
|
|
27
|
+
id?: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
}): JsonSchema;
|
|
30
|
+
export { toJsonSchema };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toJsonSchema = toJsonSchema;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const zod_1 = require("zod");
|
|
23
|
+
const strictDeep_1 = require("./strictDeep");
|
|
24
|
+
function stringSchema(checks) {
|
|
25
|
+
const out = { type: "string" };
|
|
26
|
+
for (const c of checks) {
|
|
27
|
+
if (c.kind === "min")
|
|
28
|
+
out.minLength = c.value;
|
|
29
|
+
else if (c.kind === "max")
|
|
30
|
+
out.maxLength = c.value;
|
|
31
|
+
else if (c.kind === "length")
|
|
32
|
+
out.minLength = out.maxLength = c.value;
|
|
33
|
+
else if (c.kind === "regex")
|
|
34
|
+
out.pattern = c.regex.source;
|
|
35
|
+
else if (c.kind === "url")
|
|
36
|
+
out.format = "uri";
|
|
37
|
+
else if (c.kind === "email")
|
|
38
|
+
out.format = "email";
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function numberSchema(checks) {
|
|
43
|
+
const out = { type: "number" };
|
|
44
|
+
for (const c of checks) {
|
|
45
|
+
if (c.kind === "int")
|
|
46
|
+
out.type = "integer";
|
|
47
|
+
else if (c.kind === "min")
|
|
48
|
+
out[c.inclusive ? "minimum" : "exclusiveMinimum"] = c.value;
|
|
49
|
+
else if (c.kind === "max")
|
|
50
|
+
out[c.inclusive ? "maximum" : "exclusiveMaximum"] = c.value;
|
|
51
|
+
else if (c.kind === "multipleOf")
|
|
52
|
+
out.multipleOf = c.value;
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/** Whether an object key may be left out of the input (optional, or has a default). */
|
|
57
|
+
function isOptionalInput(schema) {
|
|
58
|
+
const kind = schema._def.typeName;
|
|
59
|
+
if (kind === zod_1.ZodFirstPartyTypeKind.ZodOptional ||
|
|
60
|
+
kind === zod_1.ZodFirstPartyTypeKind.ZodDefault)
|
|
61
|
+
return true;
|
|
62
|
+
if (kind === zod_1.ZodFirstPartyTypeKind.ZodEffects)
|
|
63
|
+
return isOptionalInput(schema._def.schema);
|
|
64
|
+
if (kind === zod_1.ZodFirstPartyTypeKind.ZodNullable)
|
|
65
|
+
return isOptionalInput(schema._def.innerType);
|
|
66
|
+
if (kind === zod_1.ZodFirstPartyTypeKind.ZodAny ||
|
|
67
|
+
kind === zod_1.ZodFirstPartyTypeKind.ZodUnknown)
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
function convert(schema, path) {
|
|
72
|
+
const def = schema._def;
|
|
73
|
+
const description = schema.description
|
|
74
|
+
? { description: schema.description }
|
|
75
|
+
: {};
|
|
76
|
+
switch (def.typeName) {
|
|
77
|
+
case zod_1.ZodFirstPartyTypeKind.ZodString:
|
|
78
|
+
return Object.assign(Object.assign({}, stringSchema(def.checks)), description);
|
|
79
|
+
case zod_1.ZodFirstPartyTypeKind.ZodNumber:
|
|
80
|
+
return Object.assign(Object.assign({}, numberSchema(def.checks)), description);
|
|
81
|
+
case zod_1.ZodFirstPartyTypeKind.ZodBoolean:
|
|
82
|
+
return Object.assign({ type: "boolean" }, description);
|
|
83
|
+
case zod_1.ZodFirstPartyTypeKind.ZodLiteral:
|
|
84
|
+
return Object.assign({ const: def.value }, description);
|
|
85
|
+
case zod_1.ZodFirstPartyTypeKind.ZodEnum:
|
|
86
|
+
return Object.assign({ type: "string", enum: [...def.values] }, description);
|
|
87
|
+
case zod_1.ZodFirstPartyTypeKind.ZodAny:
|
|
88
|
+
case zod_1.ZodFirstPartyTypeKind.ZodUnknown:
|
|
89
|
+
return Object.assign({}, description);
|
|
90
|
+
case zod_1.ZodFirstPartyTypeKind.ZodNull:
|
|
91
|
+
return { type: "null" };
|
|
92
|
+
case zod_1.ZodFirstPartyTypeKind.ZodObject: {
|
|
93
|
+
const shape = def.shape();
|
|
94
|
+
const properties = {};
|
|
95
|
+
const required = [];
|
|
96
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
97
|
+
properties[key] = convert(value, path ? `${path}.${key}` : key);
|
|
98
|
+
if (!isOptionalInput(value))
|
|
99
|
+
required.push(key);
|
|
100
|
+
}
|
|
101
|
+
const catchall = def.catchall;
|
|
102
|
+
const hasCatchall = catchall && catchall._def.typeName !== zod_1.ZodFirstPartyTypeKind.ZodNever;
|
|
103
|
+
return Object.assign(Object.assign(Object.assign({ type: "object", properties }, (required.length ? { required } : {})), { additionalProperties: hasCatchall
|
|
104
|
+
? convert(catchall, `${path}.*`)
|
|
105
|
+
: def.unknownKeys !== "strict" }), description);
|
|
106
|
+
}
|
|
107
|
+
case zod_1.ZodFirstPartyTypeKind.ZodArray: {
|
|
108
|
+
const out = Object.assign({ type: "array", items: convert(def.type, `${path}[]`) }, description);
|
|
109
|
+
if (def.minLength)
|
|
110
|
+
out.minItems = def.minLength.value;
|
|
111
|
+
if (def.maxLength)
|
|
112
|
+
out.maxItems = def.maxLength.value;
|
|
113
|
+
if (def.exactLength)
|
|
114
|
+
out.minItems = out.maxItems = def.exactLength.value;
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
case zod_1.ZodFirstPartyTypeKind.ZodRecord:
|
|
118
|
+
return Object.assign({ type: "object", additionalProperties: convert(def.valueType, `${path}.*`) }, description);
|
|
119
|
+
case zod_1.ZodFirstPartyTypeKind.ZodUnion:
|
|
120
|
+
case zod_1.ZodFirstPartyTypeKind.ZodDiscriminatedUnion: {
|
|
121
|
+
const options = Array.isArray(def.options)
|
|
122
|
+
? def.options
|
|
123
|
+
: [...def.options.values()];
|
|
124
|
+
return Object.assign({ anyOf: options.map((o, i) => convert(o, `${path}|${i}`)) }, description);
|
|
125
|
+
}
|
|
126
|
+
case zod_1.ZodFirstPartyTypeKind.ZodOptional:
|
|
127
|
+
return Object.assign(Object.assign({}, convert(def.innerType, path)), description);
|
|
128
|
+
case zod_1.ZodFirstPartyTypeKind.ZodNullable:
|
|
129
|
+
return Object.assign({ anyOf: [convert(def.innerType, path), { type: "null" }] }, description);
|
|
130
|
+
case zod_1.ZodFirstPartyTypeKind.ZodDefault: {
|
|
131
|
+
const inner = convert(def.innerType, path);
|
|
132
|
+
// Object defaults such as `{}` are filled in by the inner schema; only scalars are shown.
|
|
133
|
+
const value = def.defaultValue();
|
|
134
|
+
return Object.assign(Object.assign(Object.assign({}, inner), (value === null || typeof value !== "object"
|
|
135
|
+
? { default: value }
|
|
136
|
+
: {})), description);
|
|
137
|
+
}
|
|
138
|
+
case zod_1.ZodFirstPartyTypeKind.ZodEffects:
|
|
139
|
+
return Object.assign(Object.assign({}, convert(def.schema, path)), description);
|
|
140
|
+
case zod_1.ZodFirstPartyTypeKind.ZodBranded:
|
|
141
|
+
return convert(def.type, path);
|
|
142
|
+
case zod_1.ZodFirstPartyTypeKind.ZodCatch:
|
|
143
|
+
return convert(def.innerType, path);
|
|
144
|
+
case zod_1.ZodFirstPartyTypeKind.ZodReadonly:
|
|
145
|
+
return convert(def.innerType, path);
|
|
146
|
+
case zod_1.ZodFirstPartyTypeKind.ZodPipeline:
|
|
147
|
+
return convert(def.in, path);
|
|
148
|
+
case zod_1.ZodFirstPartyTypeKind.ZodLazy:
|
|
149
|
+
return convert(def.getter(), path);
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`toJsonSchema: unsupported Zod type ${def.typeName} at ${path || "(root)"}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The JSON Schema (draft 2020-12) of a service config schema. Unknown keys are rejected
|
|
156
|
+
* (`additionalProperties: false`) wherever the service rejects them: every object, as
|
|
157
|
+
* `strictDeep` makes them, except `.passthrough()` blocks.
|
|
158
|
+
*/
|
|
159
|
+
function toJsonSchema(schema, options = {}) {
|
|
160
|
+
return Object.assign(Object.assign(Object.assign({ $schema: "https://json-schema.org/draft/2020-12/schema" }, (options.id ? { $id: options.id } : {})), (options.title ? { title: options.title } : {})), convert((0, strictDeep_1.strictDeep)(schema), ""));
|
|
161
|
+
}
|