@zapier/zapier-sdk-cli 0.13.16 → 0.14.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.
@@ -0,0 +1,236 @@
1
+ import packageJsonLib from "package-json";
2
+ import chalk from "chalk";
3
+ import log from "./log";
4
+ import Conf from "conf";
5
+ import { getUpdateCommand } from "./package-manager-detector";
6
+
7
+ interface VersionInfo {
8
+ hasUpdate: boolean;
9
+ latestVersion?: string;
10
+ currentVersion: string;
11
+ isDeprecated: boolean;
12
+ deprecationMessage?: string;
13
+ }
14
+
15
+ interface CachedPackageInfo {
16
+ version: string;
17
+ deprecated?: string | boolean;
18
+ fetched_at: string; // ISO date string
19
+ }
20
+
21
+ interface VersionCache {
22
+ last_reset_timestamp: number; // Unix timestamp in milliseconds
23
+ packages: {
24
+ [packageName: string]: {
25
+ [version: string]: CachedPackageInfo;
26
+ };
27
+ };
28
+ }
29
+
30
+ let config: Conf | null = null;
31
+
32
+ function getConfig(): Conf {
33
+ if (!config) {
34
+ config = new Conf({ projectName: "zapier-sdk-cli" });
35
+ }
36
+ return config;
37
+ }
38
+
39
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
40
+
41
+ const CACHE_RESET_INTERVAL_MS = (() => {
42
+ const { ZAPIER_SDK_UPDATE_CHECK_INTERVAL_MS = `${ONE_DAY_MS}` } = process.env;
43
+ const interval = parseInt(ZAPIER_SDK_UPDATE_CHECK_INTERVAL_MS);
44
+ if (isNaN(interval) || interval < 0) {
45
+ return -1;
46
+ }
47
+ return interval;
48
+ })();
49
+
50
+ function getVersionCache(): VersionCache {
51
+ try {
52
+ const cache = getConfig().get("version_cache") as VersionCache | undefined;
53
+ const now = Date.now();
54
+
55
+ // If no cache, missing timestamp, or it's been more than a day, reset the cache
56
+ if (
57
+ !cache ||
58
+ !cache.last_reset_timestamp ||
59
+ now - cache.last_reset_timestamp >= CACHE_RESET_INTERVAL_MS
60
+ ) {
61
+ const newCache: VersionCache = {
62
+ last_reset_timestamp: now,
63
+ packages: {},
64
+ };
65
+ getConfig().set("version_cache", newCache);
66
+ return newCache;
67
+ }
68
+
69
+ return cache;
70
+ } catch (error) {
71
+ log.debug(`Failed to read version cache: ${error}`);
72
+ return {
73
+ last_reset_timestamp: Date.now(),
74
+ packages: {},
75
+ };
76
+ }
77
+ }
78
+
79
+ function setCachedPackageInfo(
80
+ packageName: string,
81
+ version: string,
82
+ info: CachedPackageInfo,
83
+ ): void {
84
+ try {
85
+ const cache = getVersionCache();
86
+ if (!cache.packages[packageName]) {
87
+ cache.packages[packageName] = {};
88
+ }
89
+ cache.packages[packageName][version] = info;
90
+ getConfig().set("version_cache", cache);
91
+ } catch (error) {
92
+ log.debug(`Failed to cache package info: ${error}`);
93
+ }
94
+ }
95
+
96
+ function getCachedPackageInfo(
97
+ packageName: string,
98
+ version: string,
99
+ ): CachedPackageInfo | undefined {
100
+ try {
101
+ const cache = getVersionCache();
102
+ return cache.packages[packageName]?.[version];
103
+ } catch (error) {
104
+ log.debug(`Failed to get cached package info: ${error}`);
105
+ return undefined;
106
+ }
107
+ }
108
+
109
+ async function fetchCachedPackageInfo(
110
+ packageName: string,
111
+ version?: string,
112
+ ): Promise<CachedPackageInfo> {
113
+ const cacheKey = version || "latest";
114
+
115
+ // Try cache first
116
+ let cachedInfo = getCachedPackageInfo(packageName, cacheKey);
117
+ if (cachedInfo) {
118
+ return cachedInfo;
119
+ }
120
+
121
+ // Not in cache, fetch from npm
122
+ const packageInfo = await packageJsonLib(packageName, {
123
+ version,
124
+ fullMetadata: true,
125
+ });
126
+
127
+ const info: CachedPackageInfo = {
128
+ version: packageInfo.version,
129
+ deprecated: packageInfo.deprecated,
130
+ fetched_at: new Date().toISOString(),
131
+ };
132
+
133
+ // Cache for next time
134
+ setCachedPackageInfo(packageName, cacheKey, info);
135
+
136
+ return info;
137
+ }
138
+
139
+ export async function checkForUpdates({
140
+ packageName,
141
+ currentVersion,
142
+ }: {
143
+ packageName: string;
144
+ currentVersion: string;
145
+ }): Promise<VersionInfo> {
146
+ try {
147
+ // Get latest version info (with caching)
148
+ const latestPackageInfo = await fetchCachedPackageInfo(packageName);
149
+ const latestVersion = latestPackageInfo.version;
150
+ const hasUpdate = currentVersion !== latestVersion;
151
+
152
+ // Check deprecation status of the current version (with caching)
153
+ let currentPackageInfo: CachedPackageInfo;
154
+ try {
155
+ currentPackageInfo = await fetchCachedPackageInfo(
156
+ packageName,
157
+ currentVersion,
158
+ );
159
+ } catch (error) {
160
+ // If we can't fetch the current version info, use the latest version info
161
+ log.debug(`Failed to check deprecation for current version: ${error}`);
162
+ currentPackageInfo = latestPackageInfo;
163
+ }
164
+
165
+ const isDeprecated = Boolean(currentPackageInfo.deprecated);
166
+ const deprecationMessage = isDeprecated
167
+ ? String(currentPackageInfo.deprecated)
168
+ : undefined;
169
+
170
+ return {
171
+ hasUpdate,
172
+ latestVersion,
173
+ currentVersion,
174
+ isDeprecated,
175
+ deprecationMessage,
176
+ };
177
+ } catch (error) {
178
+ // If we can't check for updates (network issues, etc.), fail silently
179
+ log.debug(`Failed to check for updates: ${error}`);
180
+ return {
181
+ hasUpdate: false,
182
+ currentVersion,
183
+ isDeprecated: false,
184
+ };
185
+ }
186
+ }
187
+
188
+ export function displayUpdateNotification(
189
+ versionInfo: VersionInfo,
190
+ packageName: string,
191
+ ): void {
192
+ if (versionInfo.isDeprecated) {
193
+ console.error();
194
+ console.error(
195
+ chalk.red.bold("⚠️ DEPRECATION WARNING") +
196
+ chalk.red(
197
+ ` - ${packageName} v${versionInfo.currentVersion} is deprecated.`,
198
+ ),
199
+ );
200
+ if (versionInfo.deprecationMessage) {
201
+ console.error(chalk.red(` ${versionInfo.deprecationMessage}`));
202
+ }
203
+ console.error(chalk.red(` Please update to the latest version.`));
204
+ console.error();
205
+ }
206
+
207
+ if (versionInfo.hasUpdate) {
208
+ console.log();
209
+ console.log(
210
+ chalk.yellow.bold("📦 Update available!") +
211
+ chalk.yellow(
212
+ ` ${packageName} v${versionInfo.currentVersion} → v${versionInfo.latestVersion}`,
213
+ ),
214
+ );
215
+ console.log(
216
+ chalk.yellow(
217
+ ` Run ${chalk.bold(getUpdateCommand(packageName))} to update.`,
218
+ ),
219
+ );
220
+ console.log();
221
+ }
222
+ }
223
+
224
+ export async function checkAndNotifyUpdates({
225
+ packageName,
226
+ currentVersion,
227
+ }: {
228
+ packageName: string;
229
+ currentVersion: string;
230
+ }): Promise<void> {
231
+ if (CACHE_RESET_INTERVAL_MS < 0) {
232
+ return;
233
+ }
234
+ const versionInfo = await checkForUpdates({ packageName, currentVersion });
235
+ displayUpdateNotification(versionInfo, packageName);
236
+ }