@sanity/cli-core 0.0.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/SanityCommand.d.ts +56 -0
  4. package/dist/SanityCommand.js +72 -0
  5. package/dist/SanityCommand.js.map +1 -0
  6. package/dist/config/__tests__/cliToken.test.js +74 -0
  7. package/dist/config/__tests__/cliToken.test.js.map +1 -0
  8. package/dist/config/__tests__/cliUserConfig.test.js +131 -0
  9. package/dist/config/__tests__/cliUserConfig.test.js.map +1 -0
  10. package/dist/config/__tests__/findProjectRoot.test.js +159 -0
  11. package/dist/config/__tests__/findProjectRoot.test.js.map +1 -0
  12. package/dist/config/cli/getCliConfig.d.ts +16 -0
  13. package/dist/config/cli/getCliConfig.js +67 -0
  14. package/dist/config/cli/getCliConfig.js.map +1 -0
  15. package/dist/config/cli/getCliConfig.worker.d.ts +1 -0
  16. package/dist/config/cli/getCliConfig.worker.js +14 -0
  17. package/dist/config/cli/getCliConfig.worker.js.map +1 -0
  18. package/dist/config/cli/schemas.d.ts +204 -0
  19. package/dist/config/cli/schemas.js +77 -0
  20. package/dist/config/cli/schemas.js.map +1 -0
  21. package/dist/config/cli/types.d.ts +13 -0
  22. package/dist/config/cli/types.js +3 -0
  23. package/dist/config/cli/types.js.map +1 -0
  24. package/dist/config/findProjectRoot.d.ts +14 -0
  25. package/dist/config/findProjectRoot.js +56 -0
  26. package/dist/config/findProjectRoot.js.map +1 -0
  27. package/dist/config/studio/getStudioConfig.d.ts +14 -0
  28. package/dist/config/studio/getStudioConfig.js +16 -0
  29. package/dist/config/studio/getStudioConfig.js.map +1 -0
  30. package/dist/config/studio/readStudioConfig.d.ts +190 -0
  31. package/dist/config/studio/readStudioConfig.js +45 -0
  32. package/dist/config/studio/readStudioConfig.js.map +1 -0
  33. package/dist/config/studio/readStudioConfig.worker.d.ts +1 -0
  34. package/dist/config/studio/readStudioConfig.worker.js +64 -0
  35. package/dist/config/studio/readStudioConfig.worker.js.map +1 -0
  36. package/dist/config/util/findAppConfigPath.d.ts +8 -0
  37. package/dist/config/util/findAppConfigPath.js +22 -0
  38. package/dist/config/util/findAppConfigPath.js.map +1 -0
  39. package/dist/config/util/findConfigsPaths.d.ts +16 -0
  40. package/dist/config/util/findConfigsPaths.js +21 -0
  41. package/dist/config/util/findConfigsPaths.js.map +1 -0
  42. package/dist/config/util/findStudioConfigPath.d.ts +9 -0
  43. package/dist/config/util/findStudioConfigPath.js +31 -0
  44. package/dist/config/util/findStudioConfigPath.js.map +1 -0
  45. package/dist/config/util/isSanityV2StudioRoot.d.ts +8 -0
  46. package/dist/config/util/isSanityV2StudioRoot.js +19 -0
  47. package/dist/config/util/isSanityV2StudioRoot.js.map +1 -0
  48. package/dist/config/util/recursivelyResolveProjectRoot.d.ts +27 -0
  49. package/dist/config/util/recursivelyResolveProjectRoot.js +28 -0
  50. package/dist/config/util/recursivelyResolveProjectRoot.js.map +1 -0
  51. package/dist/debug.d.ts +15 -0
  52. package/dist/debug.js +15 -0
  53. package/dist/debug.js.map +1 -0
  54. package/dist/index.d.ts +28 -0
  55. package/dist/index.js +27 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/loaders/studio/stubs.d.ts +254 -0
  58. package/dist/loaders/studio/stubs.js +257 -0
  59. package/dist/loaders/studio/stubs.js.map +1 -0
  60. package/dist/loaders/studio/studioWorkerLoader.worker.d.ts +1 -0
  61. package/dist/loaders/studio/studioWorkerLoader.worker.js +117 -0
  62. package/dist/loaders/studio/studioWorkerLoader.worker.js.map +1 -0
  63. package/dist/loaders/studio/studioWorkerTask.d.ts +40 -0
  64. package/dist/loaders/studio/studioWorkerTask.js +69 -0
  65. package/dist/loaders/studio/studioWorkerTask.js.map +1 -0
  66. package/dist/loaders/tsx/tsxWorkerLoader.worker.d.ts +1 -0
  67. package/dist/loaders/tsx/tsxWorkerLoader.worker.js +12 -0
  68. package/dist/loaders/tsx/tsxWorkerLoader.worker.js.map +1 -0
  69. package/dist/loaders/tsx/tsxWorkerTask.d.ts +28 -0
  70. package/dist/loaders/tsx/tsxWorkerTask.js +61 -0
  71. package/dist/loaders/tsx/tsxWorkerTask.js.map +1 -0
  72. package/dist/services/apiClient.d.ts +39 -0
  73. package/dist/services/apiClient.js +88 -0
  74. package/dist/services/apiClient.js.map +1 -0
  75. package/dist/services/cliUserConfig.d.ts +57 -0
  76. package/dist/services/cliUserConfig.js +103 -0
  77. package/dist/services/cliUserConfig.js.map +1 -0
  78. package/dist/services/getCliToken.d.ts +7 -0
  79. package/dist/services/getCliToken.js +21 -0
  80. package/dist/services/getCliToken.js.map +1 -0
  81. package/dist/types.d.ts +7 -0
  82. package/dist/types.js +3 -0
  83. package/dist/types.js.map +1 -0
  84. package/dist/util/NotFoundError.d.ts +20 -0
  85. package/dist/util/NotFoundError.js +27 -0
  86. package/dist/util/NotFoundError.js.map +1 -0
  87. package/dist/util/__tests__/createExpiringConfig.test.js +309 -0
  88. package/dist/util/__tests__/createExpiringConfig.test.js.map +1 -0
  89. package/dist/util/createExpiringConfig.d.ts +32 -0
  90. package/dist/util/createExpiringConfig.js +35 -0
  91. package/dist/util/createExpiringConfig.js.map +1 -0
  92. package/dist/util/fileExists.d.ts +9 -0
  93. package/dist/util/fileExists.js +13 -0
  94. package/dist/util/fileExists.js.map +1 -0
  95. package/dist/util/generateHelpUrl.d.ts +8 -0
  96. package/dist/util/generateHelpUrl.js +11 -0
  97. package/dist/util/generateHelpUrl.js.map +1 -0
  98. package/dist/util/getSanityEnvVar.d.ts +19 -0
  99. package/dist/util/getSanityEnvVar.js +24 -0
  100. package/dist/util/getSanityEnvVar.js.map +1 -0
  101. package/dist/util/getSanityUrl.d.ts +5 -0
  102. package/dist/util/getSanityUrl.js +8 -0
  103. package/dist/util/getSanityUrl.js.map +1 -0
  104. package/dist/util/getUserConfig.d.ts +2 -0
  105. package/dist/util/getUserConfig.js +15 -0
  106. package/dist/util/getUserConfig.js.map +1 -0
  107. package/dist/util/isCi.d.ts +1 -0
  108. package/dist/util/isCi.js +7 -0
  109. package/dist/util/isCi.js.map +1 -0
  110. package/dist/util/isHttpError.d.ts +29 -0
  111. package/dist/util/isHttpError.js +18 -0
  112. package/dist/util/isHttpError.js.map +1 -0
  113. package/dist/util/isInteractive.d.ts +1 -0
  114. package/dist/util/isInteractive.js +5 -0
  115. package/dist/util/isInteractive.js.map +1 -0
  116. package/dist/util/isRecord.d.ts +8 -0
  117. package/dist/util/isRecord.js +11 -0
  118. package/dist/util/isRecord.js.map +1 -0
  119. package/dist/util/isTrueish.d.ts +1 -0
  120. package/dist/util/isTrueish.js +10 -0
  121. package/dist/util/isTrueish.js.map +1 -0
  122. package/dist/util/readJsonFile.d.ts +8 -0
  123. package/dist/util/readJsonFile.js +26 -0
  124. package/dist/util/readJsonFile.js.map +1 -0
  125. package/dist/util/safeStructuredClone.d.ts +8 -0
  126. package/dist/util/safeStructuredClone.js +40 -0
  127. package/dist/util/safeStructuredClone.js.map +1 -0
  128. package/dist/util/writeJsonFile.d.ts +9 -0
  129. package/dist/util/writeJsonFile.js +19 -0
  130. package/dist/util/writeJsonFile.js.map +1 -0
  131. package/dist/ux/colorizeJson.d.ts +1 -0
  132. package/dist/ux/colorizeJson.js +32 -0
  133. package/dist/ux/colorizeJson.js.map +1 -0
  134. package/dist/ux/formatObject.d.ts +1 -0
  135. package/dist/ux/formatObject.js +9 -0
  136. package/dist/ux/formatObject.js.map +1 -0
  137. package/dist/ux/logSymbols.d.ts +1 -0
  138. package/dist/ux/logSymbols.js +3 -0
  139. package/dist/ux/logSymbols.js.map +1 -0
  140. package/dist/ux/printKeyValue.d.ts +1 -0
  141. package/dist/ux/printKeyValue.js +16 -0
  142. package/dist/ux/printKeyValue.js.map +1 -0
  143. package/dist/ux/spinner.d.ts +1 -0
  144. package/dist/ux/spinner.js +3 -0
  145. package/dist/ux/spinner.js.map +1 -0
  146. package/dist/ux/timer.d.ts +12 -0
  147. package/dist/ux/timer.js +29 -0
  148. package/dist/ux/timer.js.map +1 -0
  149. package/package.json +81 -0
@@ -0,0 +1,103 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join as joinPath } from 'node:path';
4
+ import { z } from 'zod';
5
+ import { debug } from '../debug.js';
6
+ import { readJsonFile } from '../util/readJsonFile.js';
7
+ import { writeJsonFile } from '../util/writeJsonFile.js';
8
+ const cliUserConfigSchema = {
9
+ authToken: z.string().optional(),
10
+ telemetryConsent: z.object({
11
+ updatedAt: z.number().optional(),
12
+ value: z.object({
13
+ status: z.enum([
14
+ 'undetermined',
15
+ 'unset',
16
+ 'granted',
17
+ 'denied'
18
+ ]),
19
+ type: z.string()
20
+ }).passthrough()
21
+ }).optional()
22
+ };
23
+ /**
24
+ * Set the config value for the given property.
25
+ * Validates that the passed value adheres to the defined CLI config schema.
26
+ *
27
+ * @param prop - The property to set the value for
28
+ * @param value - The value to set
29
+ * @internal
30
+ */ export async function setConfig(prop, value) {
31
+ const config = await readConfig();
32
+ const valueSchema = cliUserConfigSchema[prop];
33
+ if (!valueSchema) {
34
+ throw new Error(`No schema defined for config property "${prop}"`);
35
+ }
36
+ const { error, success } = valueSchema.safeParse(value);
37
+ if (!success) {
38
+ const message = error.issues.map(({ message, path })=>`[${path.join('.')}] ${message}`).join('\n');
39
+ throw new Error(`Invalid value for config property "${prop}": ${message}`);
40
+ }
41
+ const configPath = getCliUserConfigPath();
42
+ await mkdir(dirname(configPath), {
43
+ recursive: true
44
+ });
45
+ await writeJsonFile(configPath, {
46
+ ...config,
47
+ [prop]: value
48
+ }, {
49
+ pretty: true
50
+ });
51
+ }
52
+ /**
53
+ * Get the config value for the given property
54
+ *
55
+ * @param prop - The property to get the value for
56
+ * @returns The value of the given property
57
+ * @internal
58
+ */ export async function getConfig(prop) {
59
+ const config = await readConfig();
60
+ const valueSchema = cliUserConfigSchema[prop];
61
+ if (!valueSchema) {
62
+ throw new Error(`No schema defined for config property "${prop}"`);
63
+ }
64
+ const { error, success } = valueSchema.safeParse(config[prop]);
65
+ if (!success) {
66
+ const message = error.issues.map(({ message, path })=>`[${path.join('.')}] ${message}`).join('\n');
67
+ throw new Error(`Invalid value for config property "${prop}": ${message}`);
68
+ }
69
+ return config[prop];
70
+ }
71
+ /**
72
+ * Read the whole configuration from file system. If the file does not exist or could
73
+ * not be loaded, an empty configuration object is returned.
74
+ *
75
+ * @returns The whole CLI configuration.
76
+ * @internal
77
+ */ async function readConfig() {
78
+ const defaultConfig = {};
79
+ try {
80
+ const config = await readJsonFile(getCliUserConfigPath());
81
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
82
+ throw new Error('Invalid config file - expected an object');
83
+ }
84
+ return config;
85
+ } catch (err) {
86
+ debug('Failed to read CLI config file: %s', err instanceof Error ? err.message : `${err}`);
87
+ return defaultConfig;
88
+ }
89
+ }
90
+ /**
91
+ * Get the file system location for the CLI user configuration file.
92
+ * Takes into account the active environment (staging vs production).
93
+ * The file is located in the user's home directory under the `.config` directory.
94
+ *
95
+ * @returns The path to the CLI configuration file.
96
+ * @internal
97
+ */ function getCliUserConfigPath() {
98
+ const sanityEnvSuffix = process.env.SANITY_INTERNAL_ENV === 'staging' ? '-staging' : '';
99
+ const cliConfigPath = process.env.SANITY_CLI_CONFIG_PATH || joinPath(homedir(), '.config', `sanity${sanityEnvSuffix}`, 'config.json');
100
+ return cliConfigPath;
101
+ }
102
+
103
+ //# sourceMappingURL=cliUserConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/cliUserConfig.ts"],"sourcesContent":["import {mkdir} from 'node:fs/promises'\nimport {homedir} from 'node:os'\nimport {dirname, join as joinPath} from 'node:path'\n\nimport {z} from 'zod'\n\nimport {debug} from '../debug.js'\nimport {readJsonFile} from '../util/readJsonFile.js'\nimport {writeJsonFile} from '../util/writeJsonFile.js'\n\nconst cliUserConfigSchema = {\n authToken: z.string().optional(),\n telemetryConsent: z\n .object({\n updatedAt: z.number().optional(),\n value: z\n .object({\n status: z.enum(['undetermined', 'unset', 'granted', 'denied']),\n type: z.string(),\n })\n .passthrough(),\n })\n .optional(),\n}\n\n/**\n * The CLI user configuration schema.\n *\n * @internal\n */\ntype CliUserConfig = z.infer<z.ZodObject<typeof cliUserConfigSchema>>\n\n/**\n * Set the config value for the given property.\n * Validates that the passed value adheres to the defined CLI config schema.\n *\n * @param prop - The property to set the value for\n * @param value - The value to set\n * @internal\n */\nexport async function setConfig<P extends keyof CliUserConfig>(prop: P, value: CliUserConfig[P]) {\n const config = await readConfig()\n const valueSchema = cliUserConfigSchema[prop]\n if (!valueSchema) {\n throw new Error(`No schema defined for config property \"${prop}\"`)\n }\n\n const {error, success} = valueSchema.safeParse(value)\n if (!success) {\n const message = error.issues\n .map(({message, path}) => `[${path.join('.')}] ${message}`)\n .join('\\n')\n\n throw new Error(`Invalid value for config property \"${prop}\": ${message}`)\n }\n\n const configPath = getCliUserConfigPath()\n await mkdir(dirname(configPath), {recursive: true})\n await writeJsonFile(configPath, {...config, [prop]: value}, {pretty: true})\n}\n\n/**\n * Get the config value for the given property\n *\n * @param prop - The property to get the value for\n * @returns The value of the given property\n * @internal\n */\nexport async function getConfig<P extends keyof CliUserConfig>(prop: P): Promise<CliUserConfig[P]> {\n const config = await readConfig()\n const valueSchema = cliUserConfigSchema[prop]\n if (!valueSchema) {\n throw new Error(`No schema defined for config property \"${prop}\"`)\n }\n\n const {error, success} = valueSchema.safeParse(config[prop])\n if (!success) {\n const message = error.issues\n .map(({message, path}) => `[${path.join('.')}] ${message}`)\n .join('\\n')\n\n throw new Error(`Invalid value for config property \"${prop}\": ${message}`)\n }\n\n return config[prop]\n}\n\n/**\n * Read the whole configuration from file system. If the file does not exist or could\n * not be loaded, an empty configuration object is returned.\n *\n * @returns The whole CLI configuration.\n * @internal\n */\nasync function readConfig(): Promise<CliUserConfig> {\n const defaultConfig: CliUserConfig = {}\n try {\n const config = await readJsonFile(getCliUserConfigPath())\n if (!config || typeof config !== 'object' || Array.isArray(config)) {\n throw new Error('Invalid config file - expected an object')\n }\n return config\n } catch (err: unknown) {\n debug('Failed to read CLI config file: %s', err instanceof Error ? err.message : `${err}`)\n return defaultConfig\n }\n}\n\n/**\n * Get the file system location for the CLI user configuration file.\n * Takes into account the active environment (staging vs production).\n * The file is located in the user's home directory under the `.config` directory.\n *\n * @returns The path to the CLI configuration file.\n * @internal\n */\nfunction getCliUserConfigPath() {\n const sanityEnvSuffix = process.env.SANITY_INTERNAL_ENV === 'staging' ? '-staging' : ''\n const cliConfigPath =\n process.env.SANITY_CLI_CONFIG_PATH ||\n joinPath(homedir(), '.config', `sanity${sanityEnvSuffix}`, 'config.json')\n\n return cliConfigPath\n}\n"],"names":["mkdir","homedir","dirname","join","joinPath","z","debug","readJsonFile","writeJsonFile","cliUserConfigSchema","authToken","string","optional","telemetryConsent","object","updatedAt","number","value","status","enum","type","passthrough","setConfig","prop","config","readConfig","valueSchema","Error","error","success","safeParse","message","issues","map","path","configPath","getCliUserConfigPath","recursive","pretty","getConfig","defaultConfig","Array","isArray","err","sanityEnvSuffix","process","env","SANITY_INTERNAL_ENV","cliConfigPath","SANITY_CLI_CONFIG_PATH"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get the CLI authentication token from the environment or the config file
3
+ *
4
+ * @returns A promise that resolves to a CLI token, or undefined if no token is found
5
+ * @internal
6
+ */
7
+ export declare function getCliToken(): Promise<string | undefined>;
@@ -0,0 +1,21 @@
1
+ import { getConfig } from './cliUserConfig.js';
2
+ let cachedToken;
3
+ /**
4
+ * Get the CLI authentication token from the environment or the config file
5
+ *
6
+ * @returns A promise that resolves to a CLI token, or undefined if no token is found
7
+ * @internal
8
+ */ export async function getCliToken() {
9
+ if (cachedToken !== undefined) {
10
+ return cachedToken;
11
+ }
12
+ const token = process.env.SANITY_AUTH_TOKEN;
13
+ if (token) {
14
+ cachedToken = token.trim();
15
+ return cachedToken;
16
+ }
17
+ cachedToken = await getConfig('authToken');
18
+ return cachedToken;
19
+ }
20
+
21
+ //# sourceMappingURL=getCliToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/getCliToken.ts"],"sourcesContent":["import {getConfig} from './cliUserConfig.js'\n\nlet cachedToken: string | undefined\n\n/**\n * Get the CLI authentication token from the environment or the config file\n *\n * @returns A promise that resolves to a CLI token, or undefined if no token is found\n * @internal\n */\nexport async function getCliToken(): Promise<string | undefined> {\n if (cachedToken !== undefined) {\n return cachedToken\n }\n\n const token = process.env.SANITY_AUTH_TOKEN\n if (token) {\n cachedToken = token.trim()\n return cachedToken\n }\n\n cachedToken = await getConfig('authToken')\n return cachedToken\n}\n"],"names":["getConfig","cachedToken","getCliToken","undefined","token","process","env","SANITY_AUTH_TOKEN","trim"],"mappings":"AAAA,SAAQA,SAAS,QAAO,qBAAoB;AAE5C,IAAIC;AAEJ;;;;;CAKC,GACD,OAAO,eAAeC;IACpB,IAAID,gBAAgBE,WAAW;QAC7B,OAAOF;IACT;IAEA,MAAMG,QAAQC,QAAQC,GAAG,CAACC,iBAAiB;IAC3C,IAAIH,OAAO;QACTH,cAAcG,MAAMI,IAAI;QACxB,OAAOP;IACT;IAEAA,cAAc,MAAMD,UAAU;IAC9B,OAAOC;AACT"}
@@ -0,0 +1,7 @@
1
+ import { type Command } from '@oclif/core';
2
+ export interface Output {
3
+ error: Command['error'];
4
+ log: Command['log'];
5
+ warn: Command['warn'];
6
+ }
7
+ export type RequireProps<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import {type Command} from '@oclif/core'\n\nexport interface Output {\n error: Command['error']\n log: Command['log']\n warn: Command['warn']\n}\n\nexport type RequireProps<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>\n"],"names":[],"mappings":"AAQA,WAAkF"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Error thrown when a file or directory is not found
3
+ *
4
+ * `code` is always `ENOENT` to mirror Node.js behavior when a file is not found
5
+ *
6
+ * @internal
7
+ */
8
+ export declare class NotFoundError extends Error {
9
+ code: string;
10
+ path?: string;
11
+ constructor(message: string, path?: string);
12
+ }
13
+ /**
14
+ * Returns whether or not the given error is a `NotFoundError`
15
+ *
16
+ * @param err - The error to check
17
+ * @returns `true` if the error is a `NotFoundError`, `false` otherwise
18
+ * @internal
19
+ */
20
+ export declare function isNotFoundError(err: unknown): err is NotFoundError;
@@ -0,0 +1,27 @@
1
+ import { isRecord } from './isRecord.js';
2
+ /**
3
+ * Error thrown when a file or directory is not found
4
+ *
5
+ * `code` is always `ENOENT` to mirror Node.js behavior when a file is not found
6
+ *
7
+ * @internal
8
+ */ export class NotFoundError extends Error {
9
+ code = 'ENOENT';
10
+ path;
11
+ constructor(message, path){
12
+ super(message);
13
+ this.path = path;
14
+ this.name = 'NotFoundError';
15
+ }
16
+ }
17
+ /**
18
+ * Returns whether or not the given error is a `NotFoundError`
19
+ *
20
+ * @param err - The error to check
21
+ * @returns `true` if the error is a `NotFoundError`, `false` otherwise
22
+ * @internal
23
+ */ export function isNotFoundError(err) {
24
+ return isRecord(err) && 'name' in err && err.name === 'NotFoundError' && 'code' in err && err.code === 'ENOENT' && 'message' in err && typeof err.message === 'string';
25
+ }
26
+
27
+ //# sourceMappingURL=NotFoundError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/NotFoundError.ts"],"sourcesContent":["import {isRecord} from './isRecord.js'\n\n/**\n * Error thrown when a file or directory is not found\n *\n * `code` is always `ENOENT` to mirror Node.js behavior when a file is not found\n *\n * @internal\n */\nexport class NotFoundError extends Error {\n code = 'ENOENT'\n path?: string\n\n constructor(message: string, path?: string) {\n super(message)\n this.path = path\n this.name = 'NotFoundError'\n }\n}\n\n/**\n * Returns whether or not the given error is a `NotFoundError`\n *\n * @param err - The error to check\n * @returns `true` if the error is a `NotFoundError`, `false` otherwise\n * @internal\n */\nexport function isNotFoundError(err: unknown): err is NotFoundError {\n return (\n isRecord(err) &&\n 'name' in err &&\n err.name === 'NotFoundError' &&\n 'code' in err &&\n err.code === 'ENOENT' &&\n 'message' in err &&\n typeof err.message === 'string'\n )\n}\n"],"names":["isRecord","NotFoundError","Error","code","path","message","name","isNotFoundError","err"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,gBAAe;AAEtC;;;;;;CAMC,GACD,OAAO,MAAMC,sBAAsBC;IACjCC,OAAO,SAAQ;IACfC,KAAa;IAEb,YAAYC,OAAe,EAAED,IAAa,CAAE;QAC1C,KAAK,CAACC;QACN,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAI,CAACE,IAAI,GAAG;IACd;AACF;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,gBAAgBC,GAAY;IAC1C,OACER,SAASQ,QACT,UAAUA,OACVA,IAAIF,IAAI,KAAK,mBACb,UAAUE,OACVA,IAAIL,IAAI,KAAK,YACb,aAAaK,OACb,OAAOA,IAAIH,OAAO,KAAK;AAE3B"}
@@ -0,0 +1,309 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+ import { createExpiringConfig } from '../createExpiringConfig.js';
3
+ describe('createExpiringConfig', ()=>{
4
+ let mockStore;
5
+ let fetchValue;
6
+ let onCacheHit;
7
+ let onFetch;
8
+ let onRevalidate;
9
+ beforeEach(()=>{
10
+ // Mock ConfigStore
11
+ mockStore = {
12
+ delete: vi.fn(),
13
+ get: vi.fn(),
14
+ set: vi.fn()
15
+ };
16
+ // Reset all mocks
17
+ fetchValue = vi.fn();
18
+ onCacheHit = vi.fn();
19
+ onFetch = vi.fn();
20
+ onRevalidate = vi.fn();
21
+ });
22
+ test('returns fetched value when cache is empty', async ()=>{
23
+ const testValue = 'test-value';
24
+ const config = createExpiringConfig({
25
+ fetchValue: fetchValue.mockResolvedValue(testValue),
26
+ key: 'test-key',
27
+ onCacheHit,
28
+ onFetch,
29
+ onRevalidate,
30
+ store: mockStore,
31
+ ttl: 5000
32
+ });
33
+ // Mock empty cache
34
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
35
+ const result = await config.get();
36
+ expect(result).toBe(testValue);
37
+ expect(fetchValue).toHaveBeenCalledOnce();
38
+ expect(onFetch).toHaveBeenCalledOnce();
39
+ expect(onCacheHit).not.toHaveBeenCalled();
40
+ expect(onRevalidate).not.toHaveBeenCalled();
41
+ expect(mockStore.set).toHaveBeenCalledWith('test-key', {
42
+ updatedAt: expect.any(Number),
43
+ value: testValue
44
+ });
45
+ });
46
+ test('returns cached value when it has not expired', async ()=>{
47
+ const cachedValue = 'cached-value';
48
+ const ttl = 5000;
49
+ const updatedAt = Date.now() - 1000 // 1 second ago (not expired)
50
+ ;
51
+ const config = createExpiringConfig({
52
+ fetchValue,
53
+ key: 'test-key',
54
+ onCacheHit,
55
+ onFetch,
56
+ onRevalidate,
57
+ store: mockStore,
58
+ ttl
59
+ });
60
+ // Mock cached value that hasn't expired
61
+ vi.mocked(mockStore.get).mockReturnValue({
62
+ updatedAt,
63
+ value: cachedValue
64
+ });
65
+ const result = await config.get();
66
+ expect(result).toBe(cachedValue);
67
+ expect(fetchValue).not.toHaveBeenCalled();
68
+ expect(onCacheHit).toHaveBeenCalledOnce();
69
+ expect(onFetch).not.toHaveBeenCalled();
70
+ expect(onRevalidate).not.toHaveBeenCalled();
71
+ expect(mockStore.set).not.toHaveBeenCalled();
72
+ });
73
+ test('fetches new value when cached value has expired', async ()=>{
74
+ const newValue = 'new-value';
75
+ const ttl = 1000;
76
+ const updatedAt = Date.now() - 2000 // 2 seconds ago (expired)
77
+ ;
78
+ const config = createExpiringConfig({
79
+ fetchValue: fetchValue.mockResolvedValue(newValue),
80
+ key: 'test-key',
81
+ onCacheHit,
82
+ onFetch,
83
+ onRevalidate,
84
+ store: mockStore,
85
+ ttl
86
+ });
87
+ // Mock expired cached value
88
+ vi.mocked(mockStore.get).mockReturnValue({
89
+ updatedAt,
90
+ value: 'old-value'
91
+ });
92
+ const result = await config.get();
93
+ expect(result).toBe(newValue);
94
+ expect(fetchValue).toHaveBeenCalledOnce();
95
+ expect(onRevalidate).toHaveBeenCalledOnce();
96
+ expect(onFetch).toHaveBeenCalledOnce();
97
+ expect(onCacheHit).not.toHaveBeenCalled();
98
+ expect(mockStore.set).toHaveBeenCalledWith('test-key', {
99
+ updatedAt: expect.any(Number),
100
+ value: newValue
101
+ });
102
+ });
103
+ test('deletes cached value from store', ()=>{
104
+ const config = createExpiringConfig({
105
+ fetchValue,
106
+ key: 'test-key',
107
+ store: mockStore,
108
+ ttl: 5000
109
+ });
110
+ config.delete();
111
+ expect(mockStore.delete).toHaveBeenCalledWith('test-key');
112
+ });
113
+ test('handles concurrent get() calls correctly', async ()=>{
114
+ const testValue = 'test-value';
115
+ let resolvePromise;
116
+ const delayedFetch = new Promise((resolve)=>{
117
+ resolvePromise = resolve;
118
+ });
119
+ const config = createExpiringConfig({
120
+ fetchValue: fetchValue.mockReturnValue(delayedFetch),
121
+ key: 'test-key',
122
+ onFetch,
123
+ store: mockStore,
124
+ ttl: 5000
125
+ });
126
+ // Mock empty cache
127
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
128
+ // Start multiple concurrent get() calls
129
+ const promise1 = config.get();
130
+ const promise2 = config.get();
131
+ const promise3 = config.get();
132
+ // Resolve the fetch
133
+ resolvePromise(testValue);
134
+ const [result1, result2, result3] = await Promise.all([
135
+ promise1,
136
+ promise2,
137
+ promise3
138
+ ]);
139
+ expect(result1).toBe(testValue);
140
+ expect(result2).toBe(testValue);
141
+ expect(result3).toBe(testValue);
142
+ expect(fetchValue).toHaveBeenCalledOnce(); // Only one fetch should happen
143
+ expect(onFetch).toHaveBeenCalledOnce();
144
+ });
145
+ test('handles synchronous fetchValue function', async ()=>{
146
+ const testValue = 'sync-value';
147
+ const syncFetchValue = vi.fn().mockReturnValue(testValue);
148
+ const config = createExpiringConfig({
149
+ fetchValue: syncFetchValue,
150
+ key: 'test-key',
151
+ store: mockStore,
152
+ ttl: 5000
153
+ });
154
+ // Mock empty cache
155
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
156
+ const result = await config.get();
157
+ expect(result).toBe(testValue);
158
+ expect(syncFetchValue).toHaveBeenCalledOnce();
159
+ });
160
+ test('handles fetchValue throwing an error', async ()=>{
161
+ const error = new Error('Fetch failed');
162
+ const config = createExpiringConfig({
163
+ fetchValue: fetchValue.mockRejectedValue(error),
164
+ key: 'test-key',
165
+ store: mockStore,
166
+ ttl: 5000
167
+ });
168
+ // Mock empty cache
169
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
170
+ await expect(config.get()).rejects.toThrow('Fetch failed');
171
+ expect(fetchValue).toHaveBeenCalledOnce();
172
+ expect(mockStore.set).not.toHaveBeenCalled();
173
+ });
174
+ test('handles different data types as cached values', async ()=>{
175
+ const objectValue = {
176
+ key: 'value',
177
+ number: 42
178
+ };
179
+ const config = createExpiringConfig({
180
+ fetchValue: fetchValue.mockResolvedValue(objectValue),
181
+ key: 'test-key',
182
+ store: mockStore,
183
+ ttl: 5000
184
+ });
185
+ // Mock empty cache
186
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
187
+ const result = await config.get();
188
+ expect(result).toEqual(objectValue);
189
+ expect(mockStore.set).toHaveBeenCalledWith('test-key', {
190
+ updatedAt: expect.any(Number),
191
+ value: objectValue
192
+ });
193
+ });
194
+ test('works with TTL of 0 (immediate expiration)', async ()=>{
195
+ const testValue = 'test-value';
196
+ const config = createExpiringConfig({
197
+ fetchValue: fetchValue.mockResolvedValue(testValue),
198
+ key: 'test-key',
199
+ onRevalidate,
200
+ store: mockStore,
201
+ ttl: 0
202
+ });
203
+ // Mock cached value that would be immediately expired
204
+ // Use a timestamp from 1ms ago to ensure it's > ttl (0)
205
+ vi.mocked(mockStore.get).mockReturnValue({
206
+ updatedAt: Date.now() - 1,
207
+ value: 'old-value'
208
+ });
209
+ const result = await config.get();
210
+ expect(result).toBe(testValue);
211
+ expect(fetchValue).toHaveBeenCalledOnce();
212
+ expect(onRevalidate).toHaveBeenCalledOnce();
213
+ });
214
+ test('works without optional callback functions', async ()=>{
215
+ const testValue = 'test-value';
216
+ const config = createExpiringConfig({
217
+ fetchValue: fetchValue.mockResolvedValue(testValue),
218
+ key: 'test-key',
219
+ store: mockStore,
220
+ ttl: 5000
221
+ });
222
+ // Mock empty cache
223
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
224
+ const result = await config.get();
225
+ expect(result).toBe(testValue);
226
+ expect(fetchValue).toHaveBeenCalledOnce();
227
+ });
228
+ test('handles cached value without updatedAt timestamp', async ()=>{
229
+ const newValue = 'new-value';
230
+ const config = createExpiringConfig({
231
+ fetchValue: fetchValue.mockResolvedValue(newValue),
232
+ key: 'test-key',
233
+ onFetch,
234
+ store: mockStore,
235
+ ttl: 5000
236
+ });
237
+ // Mock cached value without updatedAt (invalid cache entry)
238
+ vi.mocked(mockStore.get).mockReturnValue({
239
+ value: 'old-value'
240
+ });
241
+ const result = await config.get();
242
+ expect(result).toBe(newValue);
243
+ expect(fetchValue).toHaveBeenCalledOnce();
244
+ expect(onFetch).toHaveBeenCalledOnce();
245
+ });
246
+ test('handles cached value without value property', async ()=>{
247
+ const newValue = 'new-value';
248
+ const config = createExpiringConfig({
249
+ fetchValue: fetchValue.mockResolvedValue(newValue),
250
+ key: 'test-key',
251
+ onFetch,
252
+ store: mockStore,
253
+ ttl: 5000
254
+ });
255
+ // Mock cached entry without value property
256
+ vi.mocked(mockStore.get).mockReturnValue({
257
+ updatedAt: Date.now()
258
+ });
259
+ const result = await config.get();
260
+ expect(result).toBe(newValue);
261
+ expect(fetchValue).toHaveBeenCalledOnce();
262
+ expect(onFetch).toHaveBeenCalledOnce();
263
+ });
264
+ test('stores timestamp correctly when caching new values', async ()=>{
265
+ const testValue = 'test-value';
266
+ const config = createExpiringConfig({
267
+ fetchValue: fetchValue.mockResolvedValue(testValue),
268
+ key: 'test-key',
269
+ store: mockStore,
270
+ ttl: 5000
271
+ });
272
+ // Mock empty cache
273
+ vi.mocked(mockStore.get).mockReturnValue(undefined);
274
+ await config.get();
275
+ expect(mockStore.set).toHaveBeenCalledWith('test-key', {
276
+ updatedAt: expect.any(Number),
277
+ value: testValue
278
+ });
279
+ });
280
+ test('subsequent requests after cache is populated use cached value', async ()=>{
281
+ const testValue = 'test-value';
282
+ const config = createExpiringConfig({
283
+ fetchValue: fetchValue.mockResolvedValue(testValue),
284
+ key: 'test-key',
285
+ onCacheHit,
286
+ onFetch,
287
+ store: mockStore,
288
+ ttl: 5000
289
+ });
290
+ // Mock empty cache for first request
291
+ vi.mocked(mockStore.get).mockReturnValueOnce(undefined);
292
+ // First request should fetch
293
+ const result1 = await config.get();
294
+ // Mock cache populated for subsequent request
295
+ vi.mocked(mockStore.get).mockReturnValueOnce({
296
+ updatedAt: Date.now(),
297
+ value: testValue
298
+ });
299
+ // Second request should hit cache
300
+ const result2 = await config.get();
301
+ expect(result1).toBe(testValue);
302
+ expect(result2).toBe(testValue);
303
+ expect(fetchValue).toHaveBeenCalledOnce();
304
+ expect(onFetch).toHaveBeenCalledOnce();
305
+ expect(onCacheHit).toHaveBeenCalledOnce();
306
+ });
307
+ });
308
+
309
+ //# sourceMappingURL=createExpiringConfig.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/util/__tests__/createExpiringConfig.test.ts"],"sourcesContent":["import type ConfigStore from 'configstore'\n\nimport {beforeEach, describe, expect, test, vi} from 'vitest'\n\nimport {createExpiringConfig} from '../createExpiringConfig.js'\n\ndescribe('createExpiringConfig', () => {\n let mockStore: ConfigStore\n let fetchValue: ReturnType<typeof vi.fn>\n let onCacheHit: ReturnType<typeof vi.fn>\n let onFetch: ReturnType<typeof vi.fn>\n let onRevalidate: ReturnType<typeof vi.fn>\n\n beforeEach(() => {\n // Mock ConfigStore\n mockStore = {\n delete: vi.fn(),\n get: vi.fn(),\n set: vi.fn(),\n } as unknown as ConfigStore\n\n // Reset all mocks\n fetchValue = vi.fn()\n onCacheHit = vi.fn()\n onFetch = vi.fn()\n onRevalidate = vi.fn()\n })\n\n test('returns fetched value when cache is empty', async () => {\n const testValue = 'test-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(testValue),\n key: 'test-key',\n onCacheHit,\n onFetch,\n onRevalidate,\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n const result = await config.get()\n\n expect(result).toBe(testValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onFetch).toHaveBeenCalledOnce()\n expect(onCacheHit).not.toHaveBeenCalled()\n expect(onRevalidate).not.toHaveBeenCalled()\n expect(mockStore.set).toHaveBeenCalledWith('test-key', {\n updatedAt: expect.any(Number),\n value: testValue,\n })\n })\n\n test('returns cached value when it has not expired', async () => {\n const cachedValue = 'cached-value'\n const ttl = 5000\n const updatedAt = Date.now() - 1000 // 1 second ago (not expired)\n\n const config = createExpiringConfig({\n fetchValue,\n key: 'test-key',\n onCacheHit,\n onFetch,\n onRevalidate,\n store: mockStore,\n ttl,\n })\n\n // Mock cached value that hasn't expired\n vi.mocked(mockStore.get).mockReturnValue({\n updatedAt,\n value: cachedValue,\n })\n\n const result = await config.get()\n\n expect(result).toBe(cachedValue)\n expect(fetchValue).not.toHaveBeenCalled()\n expect(onCacheHit).toHaveBeenCalledOnce()\n expect(onFetch).not.toHaveBeenCalled()\n expect(onRevalidate).not.toHaveBeenCalled()\n expect(mockStore.set).not.toHaveBeenCalled()\n })\n\n test('fetches new value when cached value has expired', async () => {\n const newValue = 'new-value'\n const ttl = 1000\n const updatedAt = Date.now() - 2000 // 2 seconds ago (expired)\n\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(newValue),\n key: 'test-key',\n onCacheHit,\n onFetch,\n onRevalidate,\n store: mockStore,\n ttl,\n })\n\n // Mock expired cached value\n vi.mocked(mockStore.get).mockReturnValue({\n updatedAt,\n value: 'old-value',\n })\n\n const result = await config.get()\n\n expect(result).toBe(newValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onRevalidate).toHaveBeenCalledOnce()\n expect(onFetch).toHaveBeenCalledOnce()\n expect(onCacheHit).not.toHaveBeenCalled()\n expect(mockStore.set).toHaveBeenCalledWith('test-key', {\n updatedAt: expect.any(Number),\n value: newValue,\n })\n })\n\n test('deletes cached value from store', () => {\n const config = createExpiringConfig({\n fetchValue,\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n config.delete()\n\n expect(mockStore.delete).toHaveBeenCalledWith('test-key')\n })\n\n test('handles concurrent get() calls correctly', async () => {\n const testValue = 'test-value'\n let resolvePromise: (value: string) => void\n const delayedFetch = new Promise<string>((resolve) => {\n resolvePromise = resolve\n })\n\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockReturnValue(delayedFetch),\n key: 'test-key',\n onFetch,\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n // Start multiple concurrent get() calls\n const promise1 = config.get()\n const promise2 = config.get()\n const promise3 = config.get()\n\n // Resolve the fetch\n resolvePromise!(testValue)\n\n const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3])\n\n expect(result1).toBe(testValue)\n expect(result2).toBe(testValue)\n expect(result3).toBe(testValue)\n expect(fetchValue).toHaveBeenCalledOnce() // Only one fetch should happen\n expect(onFetch).toHaveBeenCalledOnce()\n })\n\n test('handles synchronous fetchValue function', async () => {\n const testValue = 'sync-value'\n const syncFetchValue = vi.fn().mockReturnValue(testValue)\n\n const config = createExpiringConfig({\n fetchValue: syncFetchValue,\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n const result = await config.get()\n\n expect(result).toBe(testValue)\n expect(syncFetchValue).toHaveBeenCalledOnce()\n })\n\n test('handles fetchValue throwing an error', async () => {\n const error = new Error('Fetch failed')\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockRejectedValue(error),\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n await expect(config.get()).rejects.toThrow('Fetch failed')\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(mockStore.set).not.toHaveBeenCalled()\n })\n\n test('handles different data types as cached values', async () => {\n const objectValue = {key: 'value', number: 42}\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(objectValue),\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n const result = await config.get()\n\n expect(result).toEqual(objectValue)\n expect(mockStore.set).toHaveBeenCalledWith('test-key', {\n updatedAt: expect.any(Number),\n value: objectValue,\n })\n })\n\n test('works with TTL of 0 (immediate expiration)', async () => {\n const testValue = 'test-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(testValue),\n key: 'test-key',\n onRevalidate,\n store: mockStore,\n ttl: 0,\n })\n\n // Mock cached value that would be immediately expired\n // Use a timestamp from 1ms ago to ensure it's > ttl (0)\n vi.mocked(mockStore.get).mockReturnValue({\n updatedAt: Date.now() - 1,\n value: 'old-value',\n })\n\n const result = await config.get()\n\n expect(result).toBe(testValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onRevalidate).toHaveBeenCalledOnce()\n })\n\n test('works without optional callback functions', async () => {\n const testValue = 'test-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(testValue),\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n const result = await config.get()\n\n expect(result).toBe(testValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n })\n\n test('handles cached value without updatedAt timestamp', async () => {\n const newValue = 'new-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(newValue),\n key: 'test-key',\n onFetch,\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock cached value without updatedAt (invalid cache entry)\n vi.mocked(mockStore.get).mockReturnValue({\n value: 'old-value',\n // updatedAt is missing\n })\n\n const result = await config.get()\n\n expect(result).toBe(newValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onFetch).toHaveBeenCalledOnce()\n })\n\n test('handles cached value without value property', async () => {\n const newValue = 'new-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(newValue),\n key: 'test-key',\n onFetch,\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock cached entry without value property\n vi.mocked(mockStore.get).mockReturnValue({\n updatedAt: Date.now(),\n // value is missing\n })\n\n const result = await config.get()\n\n expect(result).toBe(newValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onFetch).toHaveBeenCalledOnce()\n })\n\n test('stores timestamp correctly when caching new values', async () => {\n const testValue = 'test-value'\n\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(testValue),\n key: 'test-key',\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache\n vi.mocked(mockStore.get).mockReturnValue(undefined)\n\n await config.get()\n\n expect(mockStore.set).toHaveBeenCalledWith('test-key', {\n updatedAt: expect.any(Number),\n value: testValue,\n })\n })\n\n test('subsequent requests after cache is populated use cached value', async () => {\n const testValue = 'test-value'\n const config = createExpiringConfig({\n fetchValue: fetchValue.mockResolvedValue(testValue),\n key: 'test-key',\n onCacheHit,\n onFetch,\n store: mockStore,\n ttl: 5000,\n })\n\n // Mock empty cache for first request\n vi.mocked(mockStore.get).mockReturnValueOnce(undefined)\n\n // First request should fetch\n const result1 = await config.get()\n\n // Mock cache populated for subsequent request\n vi.mocked(mockStore.get).mockReturnValueOnce({\n updatedAt: Date.now(),\n value: testValue,\n })\n\n // Second request should hit cache\n const result2 = await config.get()\n\n expect(result1).toBe(testValue)\n expect(result2).toBe(testValue)\n expect(fetchValue).toHaveBeenCalledOnce()\n expect(onFetch).toHaveBeenCalledOnce()\n expect(onCacheHit).toHaveBeenCalledOnce()\n })\n})\n"],"names":["beforeEach","describe","expect","test","vi","createExpiringConfig","mockStore","fetchValue","onCacheHit","onFetch","onRevalidate","delete","fn","get","set","testValue","config","mockResolvedValue","key","store","ttl","mocked","mockReturnValue","undefined","result","toBe","toHaveBeenCalledOnce","not","toHaveBeenCalled","toHaveBeenCalledWith","updatedAt","any","Number","value","cachedValue","Date","now","newValue","resolvePromise","delayedFetch","Promise","resolve","promise1","promise2","promise3","result1","result2","result3","all","syncFetchValue","error","Error","mockRejectedValue","rejects","toThrow","objectValue","number","toEqual","mockReturnValueOnce"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type ConfigStore from 'configstore';
2
+ export interface ExpiringConfigOptions<Type> {
3
+ /** Fetch value */
4
+ fetchValue: () => Promise<Type> | Type;
5
+ /** Config key */
6
+ key: string;
7
+ /** Config store */
8
+ store: ConfigStore;
9
+ /** TTL (milliseconds) */
10
+ ttl: number;
11
+ /** Subscribe to cache hit event */
12
+ onCacheHit?: () => void;
13
+ /** Subscribe to fetch event */
14
+ onFetch?: () => void;
15
+ /** Subscribe to revalidate event */
16
+ onRevalidate?: () => void;
17
+ }
18
+ export interface ExpiringConfigApi<Type> {
19
+ /**
20
+ * Delete the cached value.
21
+ */
22
+ delete: () => void;
23
+ /**
24
+ * Attempt to get the cached value. If there is no cached value, or the cached value has expired,
25
+ * fetch, cache, and return the value.
26
+ */
27
+ get: () => Promise<Type>;
28
+ }
29
+ /**
30
+ * Create a config in the provided config store that expires after the provided TTL.
31
+ */
32
+ export declare function createExpiringConfig<Type>({ fetchValue, key, onCacheHit, onFetch, onRevalidate, store, ttl, }: ExpiringConfigOptions<Type>): ExpiringConfigApi<Type>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Create a config in the provided config store that expires after the provided TTL.
3
+ */ export function createExpiringConfig({ fetchValue, key, onCacheHit = ()=>null, onFetch = ()=>null, onRevalidate = ()=>null, store, ttl }) {
4
+ let currentFetch = null;
5
+ return {
6
+ delete () {
7
+ store.delete(key);
8
+ },
9
+ async get () {
10
+ const { updatedAt, value } = store.get(key) ?? {};
11
+ if (value && updatedAt) {
12
+ const hasExpired = Date.now() - updatedAt > ttl;
13
+ if (!hasExpired) {
14
+ onCacheHit();
15
+ return value;
16
+ }
17
+ onRevalidate();
18
+ }
19
+ if (currentFetch) {
20
+ return currentFetch;
21
+ }
22
+ onFetch();
23
+ currentFetch = Promise.resolve(fetchValue());
24
+ const nextValue = await currentFetch;
25
+ currentFetch = null;
26
+ store.set(key, {
27
+ updatedAt: Date.now(),
28
+ value: nextValue
29
+ });
30
+ return nextValue;
31
+ }
32
+ };
33
+ }
34
+
35
+ //# sourceMappingURL=createExpiringConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/createExpiringConfig.ts"],"sourcesContent":["import type ConfigStore from 'configstore'\n\nexport interface ExpiringConfigOptions<Type> {\n /** Fetch value */\n fetchValue: () => Promise<Type> | Type\n /** Config key */\n key: string\n /** Config store */\n store: ConfigStore\n /** TTL (milliseconds) */\n ttl: number\n\n /** Subscribe to cache hit event */\n onCacheHit?: () => void\n /** Subscribe to fetch event */\n onFetch?: () => void\n /** Subscribe to revalidate event */\n onRevalidate?: () => void\n}\n\nexport interface ExpiringConfigApi<Type> {\n /**\n * Delete the cached value.\n */\n delete: () => void\n /**\n * Attempt to get the cached value. If there is no cached value, or the cached value has expired,\n * fetch, cache, and return the value.\n */\n get: () => Promise<Type>\n}\n\n/**\n * Create a config in the provided config store that expires after the provided TTL.\n */\nexport function createExpiringConfig<Type>({\n fetchValue,\n key,\n onCacheHit = () => null,\n onFetch = () => null,\n onRevalidate = () => null,\n store,\n ttl,\n}: ExpiringConfigOptions<Type>): ExpiringConfigApi<Type> {\n let currentFetch: Promise<Type> | null = null\n return {\n delete() {\n store.delete(key)\n },\n async get() {\n const {updatedAt, value} = store.get(key) ?? {}\n\n if (value && updatedAt) {\n const hasExpired = Date.now() - updatedAt > ttl\n\n if (!hasExpired) {\n onCacheHit()\n return value\n }\n\n onRevalidate()\n }\n\n if (currentFetch) {\n return currentFetch\n }\n onFetch()\n\n currentFetch = Promise.resolve(fetchValue())\n const nextValue = await currentFetch\n currentFetch = null\n\n store.set(key, {\n updatedAt: Date.now(),\n value: nextValue,\n })\n\n return nextValue\n },\n }\n}\n"],"names":["createExpiringConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","currentFetch","delete","get","updatedAt","value","hasExpired","Date","now","Promise","resolve","nextValue","set"],"mappings":"AAgCA;;CAEC,GACD,OAAO,SAASA,qBAA2B,EACzCC,UAAU,EACVC,GAAG,EACHC,aAAa,IAAM,IAAI,EACvBC,UAAU,IAAM,IAAI,EACpBC,eAAe,IAAM,IAAI,EACzBC,KAAK,EACLC,GAAG,EACyB;IAC5B,IAAIC,eAAqC;IACzC,OAAO;QACLC;YACEH,MAAMG,MAAM,CAACP;QACf;QACA,MAAMQ;YACJ,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAC,GAAGN,MAAMI,GAAG,CAACR,QAAQ,CAAC;YAE9C,IAAIU,SAASD,WAAW;gBACtB,MAAME,aAAaC,KAAKC,GAAG,KAAKJ,YAAYJ;gBAE5C,IAAI,CAACM,YAAY;oBACfV;oBACA,OAAOS;gBACT;gBAEAP;YACF;YAEA,IAAIG,cAAc;gBAChB,OAAOA;YACT;YACAJ;YAEAI,eAAeQ,QAAQC,OAAO,CAAChB;YAC/B,MAAMiB,YAAY,MAAMV;YACxBA,eAAe;YAEfF,MAAMa,GAAG,CAACjB,KAAK;gBACbS,WAAWG,KAAKC,GAAG;gBACnBH,OAAOM;YACT;YAEA,OAAOA;QACT;IACF;AACF"}