@quonfig/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +707 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +9 -0
  5. package/bin/run.cmd +3 -0
  6. package/bin/run.js +8 -0
  7. package/dist/api/get-environments.d.ts +10 -0
  8. package/dist/api/get-environments.js +11 -0
  9. package/dist/codegen/code-generators/base-generator.d.ts +11 -0
  10. package/dist/codegen/code-generators/base-generator.js +8 -0
  11. package/dist/codegen/code-generators/base-typescript-generator.d.ts +17 -0
  12. package/dist/codegen/code-generators/base-typescript-generator.js +35 -0
  13. package/dist/codegen/code-generators/node-typescript-generator.d.ts +8 -0
  14. package/dist/codegen/code-generators/node-typescript-generator.js +96 -0
  15. package/dist/codegen/code-generators/react-typescript-generator.d.ts +11 -0
  16. package/dist/codegen/code-generators/react-typescript-generator.js +116 -0
  17. package/dist/codegen/config-downloader.d.ts +7 -0
  18. package/dist/codegen/config-downloader.js +26 -0
  19. package/dist/codegen/json-schema-to-zod.d.ts +3 -0
  20. package/dist/codegen/json-schema-to-zod.js +170 -0
  21. package/dist/codegen/language-mappers/json-to-zod-mapper.d.ts +4 -0
  22. package/dist/codegen/language-mappers/json-to-zod-mapper.js +55 -0
  23. package/dist/codegen/language-mappers/zod-base-mapper.d.ts +35 -0
  24. package/dist/codegen/language-mappers/zod-base-mapper.js +111 -0
  25. package/dist/codegen/language-mappers/zod-to-string-mapper.d.ts +25 -0
  26. package/dist/codegen/language-mappers/zod-to-string-mapper.js +87 -0
  27. package/dist/codegen/language-mappers/zod-to-typescript-mapper.d.ts +35 -0
  28. package/dist/codegen/language-mappers/zod-to-typescript-mapper.js +133 -0
  29. package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.d.ts +36 -0
  30. package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.js +150 -0
  31. package/dist/codegen/mustache-extractor.d.ts +6 -0
  32. package/dist/codegen/mustache-extractor.js +83 -0
  33. package/dist/codegen/schema-evaluator.d.ts +37 -0
  34. package/dist/codegen/schema-evaluator.js +283 -0
  35. package/dist/codegen/schema-extractor.d.ts +24 -0
  36. package/dist/codegen/schema-extractor.js +265 -0
  37. package/dist/codegen/types.d.ts +44 -0
  38. package/dist/codegen/types.js +5 -0
  39. package/dist/codegen/zod-introspection.d.ts +85 -0
  40. package/dist/codegen/zod-introspection.js +205 -0
  41. package/dist/commands/create.d.ts +19 -0
  42. package/dist/commands/create.js +175 -0
  43. package/dist/commands/download.d.ts +13 -0
  44. package/dist/commands/download.js +63 -0
  45. package/dist/commands/generate-new-hex-key.d.ts +7 -0
  46. package/dist/commands/generate-new-hex-key.js +10 -0
  47. package/dist/commands/generate.d.ts +16 -0
  48. package/dist/commands/generate.js +220 -0
  49. package/dist/commands/get.d.ts +13 -0
  50. package/dist/commands/get.js +141 -0
  51. package/dist/commands/info.d.ts +21 -0
  52. package/dist/commands/info.js +464 -0
  53. package/dist/commands/interactive.d.ts +5 -0
  54. package/dist/commands/interactive.js +8 -0
  55. package/dist/commands/list.d.ts +13 -0
  56. package/dist/commands/list.js +61 -0
  57. package/dist/commands/login.d.ts +10 -0
  58. package/dist/commands/login.js +124 -0
  59. package/dist/commands/logout.d.ts +7 -0
  60. package/dist/commands/logout.js +12 -0
  61. package/dist/commands/mcp.d.ts +13 -0
  62. package/dist/commands/mcp.js +145 -0
  63. package/dist/commands/override.d.ts +17 -0
  64. package/dist/commands/override.js +170 -0
  65. package/dist/commands/profile.d.ts +7 -0
  66. package/dist/commands/profile.js +47 -0
  67. package/dist/commands/schema.d.ts +21 -0
  68. package/dist/commands/schema.js +168 -0
  69. package/dist/commands/serve.d.ts +13 -0
  70. package/dist/commands/serve.js +99 -0
  71. package/dist/commands/set-default.d.ts +20 -0
  72. package/dist/commands/set-default.js +273 -0
  73. package/dist/commands/verify.d.ts +12 -0
  74. package/dist/commands/verify.js +48 -0
  75. package/dist/commands/whoami.d.ts +7 -0
  76. package/dist/commands/whoami.js +52 -0
  77. package/dist/commands/workspace.d.ts +7 -0
  78. package/dist/commands/workspace.js +38 -0
  79. package/dist/constants.d.ts +2 -0
  80. package/dist/constants.js +2 -0
  81. package/dist/help.d.ts +4 -0
  82. package/dist/help.js +10 -0
  83. package/dist/index.d.ts +40 -0
  84. package/dist/index.js +112 -0
  85. package/dist/interactive-prompt.d.ts +2 -0
  86. package/dist/interactive-prompt.js +162 -0
  87. package/dist/quonfig.d.ts +15 -0
  88. package/dist/quonfig.js +75 -0
  89. package/dist/result.d.ts +35 -0
  90. package/dist/result.js +9 -0
  91. package/dist/services/mcp.d.ts +15 -0
  92. package/dist/services/mcp.js +59 -0
  93. package/dist/ui/get-confirmation.d.ts +10 -0
  94. package/dist/ui/get-confirmation.js +4 -0
  95. package/dist/ui/get-environment.d.ts +11 -0
  96. package/dist/ui/get-environment.js +42 -0
  97. package/dist/ui/get-key.d.ts +27 -0
  98. package/dist/ui/get-key.js +15 -0
  99. package/dist/ui/get-string.d.ts +5 -0
  100. package/dist/ui/get-string.js +8 -0
  101. package/dist/ui/get-value.d.ts +18 -0
  102. package/dist/ui/get-value.js +46 -0
  103. package/dist/util/autocomplete.d.ts +6 -0
  104. package/dist/util/autocomplete.js +25 -0
  105. package/dist/util/coerce.d.ts +7 -0
  106. package/dist/util/coerce.js +62 -0
  107. package/dist/util/color.d.ts +3 -0
  108. package/dist/util/color.js +4 -0
  109. package/dist/util/config-value-dto.d.ts +16 -0
  110. package/dist/util/config-value-dto.js +82 -0
  111. package/dist/util/context.d.ts +18 -0
  112. package/dist/util/context.js +10 -0
  113. package/dist/util/domain-urls.d.ts +3 -0
  114. package/dist/util/domain-urls.js +18 -0
  115. package/dist/util/encryption.d.ts +20 -0
  116. package/dist/util/encryption.js +153 -0
  117. package/dist/util/file-manager.d.ts +12 -0
  118. package/dist/util/file-manager.js +16 -0
  119. package/dist/util/get-client.d.ts +7 -0
  120. package/dist/util/get-client.js +127 -0
  121. package/dist/util/is-interactive.d.ts +5 -0
  122. package/dist/util/is-interactive.js +7 -0
  123. package/dist/util/json-maybe.d.ts +3 -0
  124. package/dist/util/json-maybe.js +14 -0
  125. package/dist/util/name-arg.d.ts +4 -0
  126. package/dist/util/name-arg.js +3 -0
  127. package/dist/util/oauth-client.d.ts +30 -0
  128. package/dist/util/oauth-client.js +99 -0
  129. package/dist/util/secret-flags.d.ts +13 -0
  130. package/dist/util/secret-flags.js +13 -0
  131. package/dist/util/token-storage.d.ts +25 -0
  132. package/dist/util/token-storage.js +90 -0
  133. package/dist/validations/value.d.ts +4 -0
  134. package/dist/validations/value.js +11 -0
  135. package/dist/verify/standalone.d.ts +14 -0
  136. package/dist/verify/standalone.js +133 -0
  137. package/dist/verify/validate.d.ts +43 -0
  138. package/dist/verify/validate.js +756 -0
  139. package/dist/version.d.ts +2 -0
  140. package/dist/version.js +2 -0
  141. package/oclif.manifest.json +1429 -0
  142. package/package.json +104 -0
@@ -0,0 +1,11 @@
1
+ import type { Environment } from '../api/get-environments.js';
2
+ import { APICommand } from '../index.js';
3
+ import { JsonObj } from '../result.js';
4
+ declare const getEnvironment: ({ allowDefaultEnvironment, command, flags, message, providedEnvironment, }: {
5
+ allowDefaultEnvironment?: boolean;
6
+ command: APICommand;
7
+ flags: JsonObj;
8
+ message: string;
9
+ providedEnvironment: string | undefined;
10
+ }) => Promise<Environment | undefined>;
11
+ export default getEnvironment;
@@ -0,0 +1,42 @@
1
+ import { getEnvironments } from '../api/get-environments.js';
2
+ import autocomplete from '../util/autocomplete.js';
3
+ import isInteractive from '../util/is-interactive.js';
4
+ const defaultEnvironmentName = '[Default]';
5
+ const getEnvironment = async ({ allowDefaultEnvironment = false, command, flags, message, providedEnvironment, }) => {
6
+ if (!providedEnvironment && !isInteractive(flags)) {
7
+ command.err("'environment' is required when interactive mode isn't available.");
8
+ }
9
+ const environments = await getEnvironments(command);
10
+ command.verboseLog({ environments });
11
+ if (providedEnvironment?.toLowerCase() === defaultEnvironmentName.toLowerCase()) {
12
+ return {
13
+ id: '',
14
+ name: defaultEnvironmentName,
15
+ };
16
+ }
17
+ if (providedEnvironment) {
18
+ const matchingEnvironment = environments.find((environment) => environment.name.toLowerCase() === providedEnvironment.toLowerCase());
19
+ if (!matchingEnvironment) {
20
+ command.err(`Environment \`${providedEnvironment}\` not found. Valid environments: ${environments
21
+ .map((environment) => environment.name)
22
+ .join(', ')}`);
23
+ }
24
+ return matchingEnvironment;
25
+ }
26
+ const environmentNames = environments.map((environment) => environment.name);
27
+ if (allowDefaultEnvironment) {
28
+ environmentNames.unshift(defaultEnvironmentName);
29
+ }
30
+ const selectedEnvironment = await autocomplete({
31
+ message,
32
+ source: environmentNames,
33
+ });
34
+ if (selectedEnvironment === defaultEnvironmentName) {
35
+ return {
36
+ id: '',
37
+ name: defaultEnvironmentName,
38
+ };
39
+ }
40
+ return environments.find((environment) => environment.name === selectedEnvironment);
41
+ };
42
+ export default getEnvironment;
@@ -0,0 +1,27 @@
1
+ import { Quonfig } from '@quonfig/node';
2
+ type Flags = {
3
+ ['sdk-key']?: string;
4
+ interactive?: boolean;
5
+ };
6
+ export type CommandLike = {
7
+ err: (input: Error | string, options?: {
8
+ code?: string | undefined;
9
+ exit: false;
10
+ }) => void;
11
+ error: (input: Error | string, options: {
12
+ code?: string | undefined;
13
+ exit: false;
14
+ }) => void;
15
+ };
16
+ declare const getKey: ({ args, command, flags, message, }: {
17
+ args: {
18
+ name?: string;
19
+ };
20
+ command: CommandLike;
21
+ flags: Flags;
22
+ message: string;
23
+ }) => Promise<{
24
+ key: null | string | undefined;
25
+ quonfig: Quonfig | undefined;
26
+ }>;
27
+ export default getKey;
@@ -0,0 +1,15 @@
1
+ import { initQuonfig } from '../quonfig.js';
2
+ import autocomplete from '../util/autocomplete.js';
3
+ import isInteractive from '../util/is-interactive.js';
4
+ const getKey = async ({ args, command, flags, message, }) => {
5
+ if (!args.name && !isInteractive(flags)) {
6
+ command.err("'name' argument is required when interactive mode isn't available.");
7
+ }
8
+ const quonfig = await initQuonfig(command, flags);
9
+ let key = args.name;
10
+ if (!key) {
11
+ key = await autocomplete({ message, source: () => quonfig.keys() });
12
+ }
13
+ return { key, quonfig };
14
+ };
15
+ export default getKey;
@@ -0,0 +1,5 @@
1
+ declare const getString: ({ allowBlank, message }: {
2
+ allowBlank: boolean;
3
+ message: string;
4
+ }) => Promise<string>;
5
+ export default getString;
@@ -0,0 +1,8 @@
1
+ import { ux } from '@oclif/core';
2
+ const getString = async ({ allowBlank, message }) => {
3
+ const options = {
4
+ required: !allowBlank,
5
+ };
6
+ return ux.prompt(message, options);
7
+ };
8
+ export default getString;
@@ -0,0 +1,18 @@
1
+ import { Quonfig } from '@quonfig/node';
2
+ import { Result } from '../result.js';
3
+ interface Environment {
4
+ id: string;
5
+ name: string;
6
+ }
7
+ declare const getValue: ({ allowBlank, desiredValue, environment, flags, key, message, quonfig, }: {
8
+ allowBlank?: boolean;
9
+ desiredValue: string | undefined;
10
+ environment?: Environment;
11
+ flags: {
12
+ interactive: boolean;
13
+ };
14
+ key?: string;
15
+ message: string;
16
+ quonfig: Quonfig;
17
+ }) => Promise<Result<string>>;
18
+ export default getValue;
@@ -0,0 +1,46 @@
1
+ import { defaultValueFor } from '../quonfig.js';
2
+ import { failure, noop, success } from '../result.js';
3
+ import validateValue from '../validations/value.js';
4
+ import getString from './get-string.js';
5
+ const getValue = async ({ allowBlank = true, desiredValue, environment, flags, key, message, quonfig, }) => {
6
+ if (desiredValue === undefined && !flags.interactive) {
7
+ return failure(`No value provided for ${key}`);
8
+ }
9
+ if (!key) {
10
+ const value = desiredValue ?? (await promptForValue({ allowBlank, message }));
11
+ if (value === undefined || value === null) {
12
+ return noop();
13
+ }
14
+ return success(value);
15
+ }
16
+ const currentDefault = environment ? defaultValueFor(environment.id, key) : undefined;
17
+ const config = quonfig.rawConfig(key);
18
+ if (!config) {
19
+ return failure(`Could not find config named ${key}`);
20
+ }
21
+ const selectedValue = desiredValue ?? (await promptForValue({ allowBlank, config, currentDefault, message }));
22
+ if (selectedValue === undefined || selectedValue === null) {
23
+ return noop();
24
+ }
25
+ if (selectedValue === currentDefault?.value?.toString()) {
26
+ return noop(`The default is already \`${selectedValue}\``);
27
+ }
28
+ return validateValue(quonfig, key, selectedValue);
29
+ };
30
+ const valueToString = (v) => {
31
+ if (!v)
32
+ return '';
33
+ if (v.value === undefined || v.value === null)
34
+ return '';
35
+ return String(v.value);
36
+ };
37
+ const promptForValue = async ({ allowBlank, config, currentDefault, message, }) => {
38
+ // In the new quonfig format, allowable values aren't stored at the config level.
39
+ // Just prompt for a string input.
40
+ if (!currentDefault) {
41
+ return getString({ allowBlank, message });
42
+ }
43
+ const autoCompleteMessage = `The current default is \`${valueToString(currentDefault)}\`. Enter your new default.`;
44
+ return getString({ allowBlank, message: autoCompleteMessage });
45
+ };
46
+ export default getValue;
@@ -0,0 +1,6 @@
1
+ type Args = {
2
+ message: string;
3
+ source: (() => string[]) | string[];
4
+ };
5
+ declare const autocomplete: ({ message, source }: Args) => Promise<string | null>;
6
+ export default autocomplete;
@@ -0,0 +1,25 @@
1
+ // eslint-disable-next-line import/default
2
+ import fuzzy from 'fuzzy';
3
+ import internalAutocomplete from 'inquirer-autocomplete-standalone';
4
+ import { green } from '../util/color.js';
5
+ const options = {
6
+ post: '\u001B[0m',
7
+ pre: '\u001B[31m',
8
+ };
9
+ const unformat = (str) => str.replaceAll(options.pre, '').replaceAll(options.post, '');
10
+ const autocomplete = async ({ message, source }) => {
11
+ try {
12
+ const result = await internalAutocomplete({
13
+ message: green(message),
14
+ async source(input) {
15
+ const list = typeof source === 'function' ? source() : source;
16
+ // eslint-disable-next-line import/no-named-as-default-member
17
+ return fuzzy.filter(input ?? '', list, options).map((el) => ({ value: el.string }));
18
+ },
19
+ });
20
+ return unformat(result);
21
+ }
22
+ catch { }
23
+ return null;
24
+ };
25
+ export default autocomplete;
@@ -0,0 +1,7 @@
1
+ import type { ConfigValue } from '@quonfig/node';
2
+ import { ConfigValueType } from '@quonfig/node';
3
+ type ConfigValueWithConfigValueType = [ConfigValue, ConfigValueType];
4
+ export declare const TYPE_MAPPING: Record<string, ConfigValueType>;
5
+ export declare const coerceIntoType: (type: string, value: string) => ConfigValueWithConfigValueType | undefined;
6
+ export declare const coerceBool: (value: string) => boolean;
7
+ export {};
@@ -0,0 +1,62 @@
1
+ import { ConfigValueType } from '@quonfig/node';
2
+ const TRUE_VALUES = new Set(['true', '1', 't']);
3
+ const BOOLEAN_VALUES = new Set([...TRUE_VALUES, 'false', '0', 'f']);
4
+ export const TYPE_MAPPING = {
5
+ bool: ConfigValueType.Bool,
6
+ boolean: ConfigValueType.Bool,
7
+ double: ConfigValueType.Double,
8
+ int: ConfigValueType.Int,
9
+ string: ConfigValueType.String,
10
+ 'string-list': ConfigValueType.StringList,
11
+ stringList: ConfigValueType.StringList,
12
+ };
13
+ export const coerceIntoType = (type, value) => {
14
+ switch (type) {
15
+ case 'string': {
16
+ return [{ string: value }, TYPE_MAPPING[type]];
17
+ }
18
+ case 'int': {
19
+ try {
20
+ const int = BigInt(value);
21
+ return [{ int }, TYPE_MAPPING[type]];
22
+ }
23
+ catch {
24
+ throw new TypeError(`Invalid default value for int: ${value}`);
25
+ }
26
+ }
27
+ case 'double': {
28
+ const double = Number.parseFloat(value);
29
+ if (Number.isNaN(double)) {
30
+ throw new TypeError(`Invalid default value for double: ${value}`);
31
+ }
32
+ return [{ double }, TYPE_MAPPING[type]];
33
+ }
34
+ case 'bool':
35
+ case 'boolean': {
36
+ return [{ bool: coerceBool(value) }, TYPE_MAPPING[type]];
37
+ }
38
+ case 'stringList':
39
+ case 'string-list': {
40
+ return [{ stringList: { values: value.split(/\s*,\s*/) } }, TYPE_MAPPING[type]];
41
+ }
42
+ case 'json': {
43
+ try {
44
+ // ensure the value is valid JSON
45
+ JSON.parse(value);
46
+ return [{ json: { json: value } }, ConfigValueType.Json];
47
+ }
48
+ catch {
49
+ throw new TypeError(`Invalid default value for JSON: ${value}`);
50
+ }
51
+ }
52
+ default: {
53
+ return undefined;
54
+ }
55
+ }
56
+ };
57
+ export const coerceBool = (value) => {
58
+ if (!BOOLEAN_VALUES.has(value.toLowerCase())) {
59
+ throw new TypeError(`Invalid default value for boolean: ${value}`);
60
+ }
61
+ return TRUE_VALUES.has(value.toLowerCase());
62
+ };
@@ -0,0 +1,3 @@
1
+ export declare const red: (message: string) => string;
2
+ export declare const green: (message: string) => string;
3
+ export declare const checkmark: string;
@@ -0,0 +1,4 @@
1
+ import chalk from 'chalk';
2
+ export const red = (message) => chalk.red(message);
3
+ export const green = (message) => chalk.green(message);
4
+ export const checkmark = chalk.green('✔');
@@ -0,0 +1,16 @@
1
+ import type { ConfigValue } from '@quonfig/node';
2
+ import { ConfigValueType } from '@quonfig/node';
3
+ /**
4
+ * Maps a ConfigValue to the DTO format expected by the API.
5
+ * Used by both create and set-default commands.
6
+ * @param configValue - The config value to convert
7
+ * @param valueType - The type of the config value
8
+ * @returns The DTO representation of the config value
9
+ */
10
+ export declare function mapConfigValueToDto(configValue: ConfigValue, valueType: ConfigValueType): Record<string, unknown>;
11
+ /**
12
+ * Converts a ConfigValueType enum to its string representation for the API.
13
+ * @param valueType - The config value type enum
14
+ * @returns The string representation of the value type
15
+ */
16
+ export declare function mapValueTypeToString(valueType: ConfigValueType): string;
@@ -0,0 +1,82 @@
1
+ import { ConfigValueType } from '@quonfig/node';
2
+ /**
3
+ * Maps a ConfigValue to the DTO format expected by the API.
4
+ * Used by both create and set-default commands.
5
+ * @param configValue - The config value to convert
6
+ * @param valueType - The type of the config value
7
+ * @returns The DTO representation of the config value
8
+ */
9
+ export function mapConfigValueToDto(configValue, valueType) {
10
+ const dto = {
11
+ type: mapValueTypeToString(valueType),
12
+ };
13
+ // Handle provided (env-var) values
14
+ if (configValue.provided) {
15
+ return {
16
+ ...dto,
17
+ provided: {
18
+ source: configValue.provided.source,
19
+ lookup: configValue.provided.lookup,
20
+ },
21
+ };
22
+ }
23
+ // Extract the actual value based on type
24
+ let value;
25
+ if (configValue.bool !== undefined) {
26
+ value = configValue.bool;
27
+ }
28
+ else if (configValue.string !== undefined) {
29
+ value = configValue.string;
30
+ }
31
+ else if (configValue.int !== undefined) {
32
+ value = configValue.int;
33
+ }
34
+ else if (configValue.double !== undefined) {
35
+ value = configValue.double;
36
+ }
37
+ else if (configValue.stringList !== undefined) {
38
+ value = configValue.stringList.values;
39
+ }
40
+ else if (configValue.json !== undefined) {
41
+ value = configValue.json;
42
+ }
43
+ else if (configValue.duration !== undefined) {
44
+ value = configValue.duration;
45
+ }
46
+ else if (configValue.intRange !== undefined) {
47
+ value = configValue.intRange;
48
+ }
49
+ else if (configValue.value !== undefined) {
50
+ // Handle encrypted/confidential values where value is directly set
51
+ value = configValue.value;
52
+ }
53
+ dto.value = value;
54
+ if (configValue.confidential) {
55
+ dto.confidential = true;
56
+ }
57
+ if (configValue.decryptWith) {
58
+ dto.decryptWith = configValue.decryptWith;
59
+ }
60
+ return dto;
61
+ }
62
+ /**
63
+ * Converts a ConfigValueType enum to its string representation for the API.
64
+ * @param valueType - The config value type enum
65
+ * @returns The string representation of the value type
66
+ */
67
+ export function mapValueTypeToString(valueType) {
68
+ const mapping = {
69
+ [ConfigValueType.Bool]: 'bool',
70
+ [ConfigValueType.String]: 'string',
71
+ [ConfigValueType.Int]: 'int',
72
+ [ConfigValueType.Double]: 'double',
73
+ [ConfigValueType.StringList]: 'string_list',
74
+ [ConfigValueType.Json]: 'json',
75
+ [ConfigValueType.LimitDefinition]: 'limit_definition',
76
+ [ConfigValueType.Duration]: 'duration',
77
+ [ConfigValueType.IntRange]: 'int_range',
78
+ [ConfigValueType.Bytes]: 'bytes',
79
+ [ConfigValueType.LogLevel]: 'log_level',
80
+ };
81
+ return mapping[valueType] || 'string';
82
+ }
@@ -0,0 +1,18 @@
1
+ interface JavascriptClientContext {
2
+ type: string;
3
+ values: {
4
+ [key: string]: {
5
+ string: string;
6
+ };
7
+ };
8
+ }
9
+ interface JavascriptClientContextInput {
10
+ contexts: JavascriptClientContext[];
11
+ }
12
+ interface ContextObj {
13
+ [key: string]: {
14
+ [key: string]: string;
15
+ };
16
+ }
17
+ export declare const javaScriptClientFormattedContextToContext: (input: JavascriptClientContextInput) => ContextObj;
18
+ export {};
@@ -0,0 +1,10 @@
1
+ export const javaScriptClientFormattedContextToContext = (input) => {
2
+ const result = {};
3
+ for (const context of input.contexts) {
4
+ result[context.type] = {};
5
+ for (const [key, value] of Object.entries(context.values)) {
6
+ result[context.type][key] = value.string;
7
+ }
8
+ }
9
+ return result;
10
+ };
@@ -0,0 +1,3 @@
1
+ export declare const getDomain: () => string;
2
+ export declare const getApiUrl: (domain?: string) => string;
3
+ export declare const getAppUrl: (domain?: string) => string;
@@ -0,0 +1,18 @@
1
+ const DEFAULT_DOMAIN = 'quonfig.com';
2
+ export const getDomain = () => process.env.QUONFIG_DOMAIN || DEFAULT_DOMAIN;
3
+ export const getApiUrl = (domain) => {
4
+ // Allow full override for local development
5
+ if (process.env.QUONFIG_API_BASE_URL_OVERRIDE) {
6
+ return process.env.QUONFIG_API_BASE_URL_OVERRIDE;
7
+ }
8
+ const actualDomain = domain || getDomain();
9
+ return `https://app.${actualDomain}`;
10
+ };
11
+ export const getAppUrl = (domain) => {
12
+ // Allow explicit override for app URL
13
+ if (process.env.QUONFIG_APP_BASE_URL_OVERRIDE) {
14
+ return process.env.QUONFIG_APP_BASE_URL_OVERRIDE;
15
+ }
16
+ const actualDomain = domain || getDomain();
17
+ return `https://app.${actualDomain}`;
18
+ };
@@ -0,0 +1,20 @@
1
+ import type { APICommand } from '../index.js';
2
+ import type { Secret } from './secret-flags.js';
3
+ import { Result } from '../result.js';
4
+ /**
5
+ * Decrypts an encrypted config value
6
+ * Based on @quonfig/node/src/encryption.ts
7
+ * @param {string} encryptedString - The encrypted string in format: encryptedData--IV--authTag
8
+ * @param {string} keyStringHex - The encryption key as a 64-character hex string
9
+ * @returns {string} The decrypted plaintext value
10
+ */
11
+ export declare function decrypt(encryptedString: string, keyStringHex: string): string;
12
+ /**
13
+ * Creates an encrypted ConfigValue using the new v1 API endpoints
14
+ * @param command - The APICommand instance for API calls and logging
15
+ * @param value - The plaintext value to encrypt
16
+ * @param secret - Secret configuration containing key name
17
+ * @param environmentId - Environment ID to fetch encryption key for (empty string for default)
18
+ * @returns Result<ConfigValue> with encrypted value or error
19
+ */
20
+ export declare function makeConfidentialValue(command: APICommand, value: string, secret: Secret, environmentId: string): Promise<Result<any>>;
@@ -0,0 +1,153 @@
1
+ import { encryption } from '@quonfig/node';
2
+ import forge from 'node-forge';
3
+ import { failure, success } from '../result.js';
4
+ const SEPARATOR = '--';
5
+ const KEY_LENGTH = 32; // 32 bytes for AES-256
6
+ const TAG_LENGTH = 16; // 16 bytes for GCM tag
7
+ /**
8
+ * Decrypts an encrypted config value
9
+ * Based on @quonfig/node/src/encryption.ts
10
+ * @param {string} encryptedString - The encrypted string in format: encryptedData--IV--authTag
11
+ * @param {string} keyStringHex - The encryption key as a 64-character hex string
12
+ * @returns {string} The decrypted plaintext value
13
+ */
14
+ export function decrypt(encryptedString, keyStringHex) {
15
+ if (keyStringHex.length !== KEY_LENGTH * 2) {
16
+ throw new Error(`Invalid key length. Key must be a ${KEY_LENGTH * 2}-character hex string.`);
17
+ }
18
+ const parts = encryptedString.split(SEPARATOR);
19
+ if (parts.length !== 3) {
20
+ throw new Error('Invalid encrypted string. Must contain encrypted data, IV, and auth tag.');
21
+ }
22
+ const encryptedDataPart = parts[0];
23
+ const ivPart = parts[1];
24
+ const authTagPart = parts[2];
25
+ if (encryptedDataPart === undefined || ivPart === undefined || authTagPart === undefined) {
26
+ throw new Error('Invalid encrypted string. All parts must be defined.');
27
+ }
28
+ if (encryptedDataPart === '') {
29
+ return '';
30
+ }
31
+ // Convert hex strings to bytes
32
+ const keyBytes = forge.util.hexToBytes(keyStringHex);
33
+ const encryptedBytes = forge.util.hexToBytes(encryptedDataPart);
34
+ const ivBytes = forge.util.hexToBytes(ivPart);
35
+ const tagBytes = forge.util.hexToBytes(authTagPart);
36
+ // Create decipher
37
+ const decipher = forge.cipher.createDecipher('AES-GCM', keyBytes);
38
+ decipher.start({
39
+ iv: ivBytes,
40
+ tag: forge.util.createBuffer(tagBytes),
41
+ tagLength: TAG_LENGTH * 8, // tagLength is in bits
42
+ });
43
+ // Decrypt the data
44
+ decipher.update(forge.util.createBuffer(encryptedBytes));
45
+ const success = decipher.finish();
46
+ if (!success) {
47
+ throw new Error('Authentication tag verification failed');
48
+ }
49
+ return decipher.output.toString();
50
+ }
51
+ /**
52
+ * Creates an encrypted ConfigValue using the new v1 API endpoints
53
+ * @param command - The APICommand instance for API calls and logging
54
+ * @param value - The plaintext value to encrypt
55
+ * @param secret - Secret configuration containing key name
56
+ * @param environmentId - Environment ID to fetch encryption key for (empty string for default)
57
+ * @returns Result<ConfigValue> with encrypted value or error
58
+ */
59
+ export async function makeConfidentialValue(command, value, secret, environmentId) {
60
+ // First check if the encryption key exists in metadata
61
+ const metadataRequest = await command.apiClient.post('/api/v1/metadata/list', { workspaceId: command.workspaceId });
62
+ if (!metadataRequest.ok) {
63
+ return failure(`Failed to check if encryption key exists: ${metadataRequest.status}`, {
64
+ phase: 'finding-secret',
65
+ });
66
+ }
67
+ const metadata = metadataRequest.json;
68
+ const keyExists = metadata.configs.some((config) => config.key === secret.keyName);
69
+ if (!keyExists) {
70
+ return failure(`Failed to create secret: encryption key '${secret.keyName}' does not exist. Please create it first or use --secret-key-name to specify a different key.`, {
71
+ phase: 'finding-secret',
72
+ });
73
+ }
74
+ // Fetch the encryption key config
75
+ const configRequest = await command.apiClient.post('/api/v1/metadata/getByKey', { workspaceId: command.workspaceId, key: secret.keyName });
76
+ if (!configRequest.ok) {
77
+ const message = configRequest.status === 404
78
+ ? `Failed to create secret: ${secret.keyName} not found`
79
+ : `Failed to fetch encryption key config ${secret.keyName}: ${configRequest.status}`;
80
+ return failure(message, {
81
+ phase: 'finding-secret',
82
+ });
83
+ }
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ const keyConfig = configRequest.json;
86
+ // Find the encryption key for this environment (or default)
87
+ let secretKey;
88
+ let envVar;
89
+ // Check environment-specific config first
90
+ if (keyConfig.environments && environmentId) {
91
+ /* eslint-disable @typescript-eslint/no-explicit-any */
92
+ const envConfig = keyConfig.environments.find((env) => String(env.id) === String(environmentId));
93
+ /* eslint-enable @typescript-eslint/no-explicit-any */
94
+ const ruleValue = envConfig?.rules?.[0]?.value;
95
+ // Check if it's a provided value (env var)
96
+ if (ruleValue?.type === 'provided' && ruleValue?.value?.lookup) {
97
+ envVar = ruleValue.value.lookup;
98
+ }
99
+ // Check for old format: provided.lookup
100
+ else if (ruleValue?.provided?.lookup) {
101
+ envVar = ruleValue.provided.lookup;
102
+ }
103
+ // Otherwise it's a literal value
104
+ else if (ruleValue?.value && typeof ruleValue.value === 'string') {
105
+ secretKey = ruleValue.value;
106
+ }
107
+ }
108
+ // Fall back to default config
109
+ if (!secretKey && !envVar) {
110
+ const defaultValue = keyConfig.default?.rules?.[0]?.value;
111
+ // Check if it's a provided value (env var)
112
+ if (defaultValue?.type === 'provided' && defaultValue?.value?.lookup) {
113
+ envVar = defaultValue.value.lookup;
114
+ }
115
+ // Check for old format: provided.lookup
116
+ else if (defaultValue?.provided?.lookup) {
117
+ envVar = defaultValue.provided.lookup;
118
+ }
119
+ // Otherwise it's a literal value
120
+ else if (defaultValue?.value && typeof defaultValue.value === 'string') {
121
+ secretKey = defaultValue.value;
122
+ }
123
+ }
124
+ // If we have an env var, resolve it
125
+ if (envVar && !secretKey) {
126
+ secretKey = process.env[envVar];
127
+ command.log(`Encrypting with key from env var: ${envVar}`);
128
+ if (typeof secretKey !== 'string') {
129
+ return failure(`Failed to create secret: env var ${envVar} is not present`, {
130
+ phase: 'finding-secret',
131
+ });
132
+ }
133
+ }
134
+ // If we have a literal value, use it
135
+ if (secretKey) {
136
+ command.verboseLog(`Using literal value from ${secret.keyName} to encrypt secret`);
137
+ }
138
+ if (!secretKey) {
139
+ return failure(`Failed to create secret: ${secret.keyName} does not have a value configured for environment ${environmentId || 'default'} or default env`, {
140
+ phase: 'finding-secret',
141
+ });
142
+ }
143
+ if (secretKey.length !== 64) {
144
+ return failure(`Secret key is too short. ${secret.keyName} must be 64 characters.`, {
145
+ phase: 'finding-secret',
146
+ });
147
+ }
148
+ return success({
149
+ confidential: true,
150
+ decryptWith: secret.keyName,
151
+ value: encryption.encrypt(value, secretKey),
152
+ });
153
+ }
@@ -0,0 +1,12 @@
1
+ interface CreateFileManagerArgs {
2
+ outputDirectory: string;
3
+ verboseLog: (category: string | unknown, message?: unknown) => void;
4
+ }
5
+ interface FileManager {
6
+ writeFile: (args: {
7
+ data: string;
8
+ filename: string;
9
+ }) => Promise<void>;
10
+ }
11
+ export declare function createFileManager({ outputDirectory, verboseLog }: CreateFileManagerArgs): FileManager;
12
+ export {};
@@ -0,0 +1,16 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ export function createFileManager({ outputDirectory, verboseLog }) {
4
+ return {
5
+ async writeFile({ data, filename }) {
6
+ // Ensure the directory exists
7
+ verboseLog(`Creating directory: ${outputDirectory}`);
8
+ await fs.promises.mkdir(outputDirectory, { recursive: true });
9
+ // Write the generated code to the file
10
+ const outputFile = path.join(outputDirectory, filename);
11
+ verboseLog(`Writing file: ${outputFile}`);
12
+ await fs.promises.writeFile(outputFile, data);
13
+ verboseLog(`Wrote file at ${outputFile}`);
14
+ },
15
+ };
16
+ }