@capgo/cli 7.18.22 → 7.19.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 (101) hide show
  1. package/dist/index.js +236 -349
  2. package/dist/src/bundle/partial.d.ts.map +1 -1
  3. package/dist/src/bundle/upload.d.ts.map +1 -1
  4. package/dist/src/bundle/zip.d.ts.map +1 -1
  5. package/dist/src/init.d.ts.map +1 -1
  6. package/dist/src/sdk.js +191 -191
  7. package/dist/src/utils.d.ts +3 -11
  8. package/dist/src/utils.d.ts.map +1 -1
  9. package/package.json +2 -4
  10. package/dist/package.json +0 -88
  11. package/dist/src/api/app.js +0 -45
  12. package/dist/src/api/app.js.map +0 -1
  13. package/dist/src/api/channels.js +0 -167
  14. package/dist/src/api/channels.js.map +0 -1
  15. package/dist/src/api/cryptoV2.js +0 -102
  16. package/dist/src/api/cryptoV2.js.map +0 -1
  17. package/dist/src/api/update.js +0 -13
  18. package/dist/src/api/update.js.map +0 -1
  19. package/dist/src/api/versions.js +0 -92
  20. package/dist/src/api/versions.js.map +0 -1
  21. package/dist/src/app/add.js +0 -150
  22. package/dist/src/app/add.js.map +0 -1
  23. package/dist/src/app/debug.js +0 -222
  24. package/dist/src/app/debug.js.map +0 -1
  25. package/dist/src/app/delete.js +0 -89
  26. package/dist/src/app/delete.js.map +0 -1
  27. package/dist/src/app/info.js +0 -84
  28. package/dist/src/app/info.js.map +0 -1
  29. package/dist/src/app/list.js +0 -48
  30. package/dist/src/app/list.js.map +0 -1
  31. package/dist/src/app/set.js +0 -96
  32. package/dist/src/app/set.js.map +0 -1
  33. package/dist/src/app/setting.js +0 -50
  34. package/dist/src/app/setting.js.map +0 -1
  35. package/dist/src/bundle/check.js +0 -30
  36. package/dist/src/bundle/check.js.map +0 -1
  37. package/dist/src/bundle/cleanup.js +0 -105
  38. package/dist/src/bundle/cleanup.js.map +0 -1
  39. package/dist/src/bundle/compatibility.js +0 -62
  40. package/dist/src/bundle/compatibility.js.map +0 -1
  41. package/dist/src/bundle/decryptV2.js +0 -76
  42. package/dist/src/bundle/decryptV2.js.map +0 -1
  43. package/dist/src/bundle/delete.js +0 -40
  44. package/dist/src/bundle/delete.js.map +0 -1
  45. package/dist/src/bundle/encryptV2.js +0 -108
  46. package/dist/src/bundle/encryptV2.js.map +0 -1
  47. package/dist/src/bundle/list.js +0 -36
  48. package/dist/src/bundle/list.js.map +0 -1
  49. package/dist/src/bundle/partial.js +0 -272
  50. package/dist/src/bundle/partial.js.map +0 -1
  51. package/dist/src/bundle/unlink.js +0 -70
  52. package/dist/src/bundle/unlink.js.map +0 -1
  53. package/dist/src/bundle/upload.js +0 -930
  54. package/dist/src/bundle/upload.js.map +0 -1
  55. package/dist/src/bundle/upload_interface.js +0 -2
  56. package/dist/src/bundle/upload_interface.js.map +0 -1
  57. package/dist/src/bundle/zip.js +0 -151
  58. package/dist/src/bundle/zip.js.map +0 -1
  59. package/dist/src/channel/add.js +0 -68
  60. package/dist/src/channel/add.js.map +0 -1
  61. package/dist/src/channel/currentBundle.js +0 -54
  62. package/dist/src/channel/currentBundle.js.map +0 -1
  63. package/dist/src/channel/delete.js +0 -77
  64. package/dist/src/channel/delete.js.map +0 -1
  65. package/dist/src/channel/list.js +0 -45
  66. package/dist/src/channel/list.js.map +0 -1
  67. package/dist/src/channel/set.js +0 -220
  68. package/dist/src/channel/set.js.map +0 -1
  69. package/dist/src/checksum.js +0 -46
  70. package/dist/src/checksum.js.map +0 -1
  71. package/dist/src/config/index.js +0 -31
  72. package/dist/src/config/index.js.map +0 -1
  73. package/dist/src/docs.js +0 -280
  74. package/dist/src/docs.js.map +0 -1
  75. package/dist/src/index.js +0 -522
  76. package/dist/src/index.js.map +0 -1
  77. package/dist/src/init.js +0 -797
  78. package/dist/src/init.js.map +0 -1
  79. package/dist/src/keyV2.js +0 -163
  80. package/dist/src/keyV2.js.map +0 -1
  81. package/dist/src/login.js +0 -51
  82. package/dist/src/login.js.map +0 -1
  83. package/dist/src/organisation/add.js +0 -82
  84. package/dist/src/organisation/add.js.map +0 -1
  85. package/dist/src/organisation/delete.js +0 -91
  86. package/dist/src/organisation/delete.js.map +0 -1
  87. package/dist/src/organisation/index.js +0 -5
  88. package/dist/src/organisation/index.js.map +0 -1
  89. package/dist/src/organisation/list.js +0 -60
  90. package/dist/src/organisation/list.js.map +0 -1
  91. package/dist/src/organisation/set.js +0 -95
  92. package/dist/src/organisation/set.js.map +0 -1
  93. package/dist/src/sdk.js.map +0 -1
  94. package/dist/src/types/supabase.types.js +0 -86
  95. package/dist/src/types/supabase.types.js.map +0 -1
  96. package/dist/src/user/account.js +0 -31
  97. package/dist/src/user/account.js.map +0 -1
  98. package/dist/src/utils/latest-version.js +0 -26
  99. package/dist/src/utils/latest-version.js.map +0 -1
  100. package/dist/src/utils.js +0 -1215
  101. package/dist/src/utils.js.map +0 -1
package/dist/src/utils.js DELETED
@@ -1,1215 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
- import { homedir, platform as osPlatform } from 'node:os';
3
- import path, { dirname, join, relative, resolve, sep } from 'node:path';
4
- import { cwd, env } from 'node:process';
5
- import { findMonorepoRoot, findNXMonorepoRoot, isMonorepo, isNXMonorepo } from '@capacitor/cli/dist/util/monorepotools';
6
- import { findInstallCommand, findPackageManagerRunner, findPackageManagerType } from '@capgo/find-package-manager';
7
- import { confirm as confirmC, isCancel, log, select, spinner as spinnerC } from '@clack/prompts';
8
- import { createClient, FunctionsHttpError } from '@supabase/supabase-js';
9
- import AdmZip from 'adm-zip';
10
- // Native fetch is available in Node.js >= 18
11
- import prettyjson from 'prettyjson';
12
- import cleanVersion from 'semver/functions/clean';
13
- import validVersion from 'semver/functions/valid';
14
- import subset from 'semver/ranges/subset';
15
- import * as tus from 'tus-js-client';
16
- import { checksum as getChecksum } from './checksum';
17
- import { loadConfig, writeConfig } from './config';
18
- export const baseKey = '.capgo_key';
19
- export const baseKeyV2 = '.capgo_key_v2';
20
- export const baseKeyPub = `${baseKey}.pub`;
21
- export const baseKeyPubV2 = `${baseKeyV2}.pub`;
22
- export const defaultHost = 'https://capgo.app';
23
- export const defaultFileHost = 'https://files.capgo.app';
24
- export const defaultApiHost = 'https://api.capgo.app';
25
- export const defaultHostWeb = 'https://console.capgo.app';
26
- export const UPLOAD_TIMEOUT = 120000;
27
- export const ALERT_UPLOAD_SIZE_BYTES = 1024 * 1024 * 20; // 20MB
28
- export const MAX_UPLOAD_LENGTH_BYTES = 1024 * 1024 * 1024; // 1GB
29
- export const MAX_CHUNK_SIZE_BYTES = 1024 * 1024 * 99; // 99MB
30
- export const PACKNAME = 'package.json';
31
- export const regexSemver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-z-][0-9a-z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-z-][0-9a-z-]*))*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i;
32
- export const formatError = (error) => error ? `\n${prettyjson.render(error)}` : '';
33
- export function wait(ms) {
34
- return new Promise((resolve) => {
35
- setTimeout(resolve, ms);
36
- });
37
- }
38
- export function projectIsMonorepo(dir) {
39
- return isMonorepo(dir) || isNXMonorepo(dir);
40
- }
41
- export function findRoot(dir) {
42
- if (isMonorepo(dir)) {
43
- return findMonorepoRoot(dir);
44
- }
45
- else if (isNXMonorepo(dir)) {
46
- return findNXMonorepoRoot(dir);
47
- }
48
- return dir;
49
- }
50
- // do not expose this function this prevent missuses
51
- function readPackageJson(f = findRoot(cwd()), file = undefined) {
52
- const fileSplit = file?.split(',')[0];
53
- if (fileSplit) {
54
- if (!existsSync(fileSplit)) {
55
- const message = `Package.json at ${fileSplit} does not exist`;
56
- log.error(message);
57
- throw new Error(message);
58
- }
59
- }
60
- const packageJson = readFileSync(fileSplit ?? join(f, PACKNAME));
61
- return JSON.parse(packageJson);
62
- }
63
- export function getPackageScripts(f = findRoot(cwd()), file = undefined) {
64
- const packageJson = readPackageJson(f, file);
65
- return packageJson.scripts;
66
- }
67
- export function getBundleVersion(f = findRoot(cwd()), file = undefined) {
68
- const packageJson = readPackageJson(f, file);
69
- return packageJson.version ?? '';
70
- }
71
- function returnVersion(version) {
72
- const tmpVersion = version.replace('^', '').replace('~', '');
73
- if (validVersion(tmpVersion)) {
74
- return cleanVersion(tmpVersion) ?? tmpVersion;
75
- }
76
- return tmpVersion;
77
- }
78
- export async function getAllPackagesDependencies(f = findRoot(cwd()), file = undefined) {
79
- // if file contain , split by comma and return the array
80
- let files = file?.split(',');
81
- files ??= [join(f, PACKNAME)];
82
- if (files) {
83
- for (const file of files) {
84
- if (!existsSync(file)) {
85
- const message = `Package.json at ${file} does not exist`;
86
- log.error(message);
87
- throw new Error(message);
88
- }
89
- }
90
- }
91
- const dependencies = new Map();
92
- for (const file of files) {
93
- const packageJson = readFileSync(file);
94
- const pkg = JSON.parse(packageJson);
95
- for (const dependency in pkg.dependencies) {
96
- dependencies.set(dependency, returnVersion(pkg.dependencies[dependency]));
97
- }
98
- for (const dependency in pkg.devDependencies) {
99
- dependencies.set(dependency, returnVersion(pkg.devDependencies[dependency]));
100
- }
101
- }
102
- return dependencies;
103
- }
104
- export async function getConfig() {
105
- try {
106
- const extConfig = await loadConfig();
107
- if (!extConfig) {
108
- const message = 'No capacitor config file found, run `cap init` first';
109
- log.error(message);
110
- throw new Error(message);
111
- }
112
- return extConfig;
113
- }
114
- catch (err) {
115
- const message = `No capacitor config file found, run \`cap init\` first ${formatError(err)}`;
116
- log.error(message);
117
- throw new Error(message);
118
- }
119
- }
120
- export async function updateConfigbyKey(key, newConfig) {
121
- const extConfig = await getConfig();
122
- if (extConfig?.config) {
123
- extConfig.config.plugins ??= {};
124
- extConfig.config.plugins.extConfig ??= {};
125
- extConfig.config.plugins[key] ??= {};
126
- extConfig.config.plugins[key] = {
127
- ...extConfig.config.plugins[key],
128
- ...newConfig,
129
- };
130
- // console.log('extConfig', extConfig)
131
- await writeConfig(key, extConfig);
132
- }
133
- return extConfig;
134
- }
135
- export async function updateConfigUpdater(newConfig) {
136
- return updateConfigbyKey('CapacitorUpdater', newConfig);
137
- }
138
- export async function getLocalConfig() {
139
- try {
140
- const extConfig = await getConfig();
141
- const capConfig = {
142
- host: (extConfig?.config?.plugins?.CapacitorUpdater?.localHost || defaultHost),
143
- hostWeb: (extConfig?.config?.plugins?.CapacitorUpdater?.localWebHost || defaultHostWeb),
144
- hostFilesApi: (extConfig?.config?.plugins?.CapacitorUpdater?.localApiFiles || defaultFileHost),
145
- hostApi: (extConfig?.config?.plugins?.CapacitorUpdater?.localApi || defaultApiHost),
146
- };
147
- if (extConfig?.config?.plugins?.CapacitorUpdater?.localSupa && extConfig?.config?.plugins?.CapacitorUpdater?.localSupaAnon) {
148
- log.info('Using custom supabase instance from capacitor.config.json');
149
- capConfig.supaKey = extConfig?.config?.plugins?.CapacitorUpdater?.localSupaAnon;
150
- capConfig.supaHost = extConfig?.config?.plugins?.CapacitorUpdater?.localSupa;
151
- }
152
- return capConfig;
153
- }
154
- catch {
155
- return {
156
- host: defaultHost,
157
- hostWeb: defaultHostWeb,
158
- hostFilesApi: defaultFileHost,
159
- hostApi: defaultApiHost,
160
- };
161
- }
162
- }
163
- // eslint-disable-next-line regexp/no-unused-capturing-group
164
- const nativeFileRegex = /([A-Za-z0-9]+)\.(java|swift|kt|scala)$/;
165
- export async function getRemoteConfig() {
166
- // call host + /api/get_config and parse the result as json using fetch
167
- const localConfig = await getLocalConfig();
168
- try {
169
- const response = await fetch(`${localConfig.hostApi}/private/config`);
170
- if (!response.ok) {
171
- throw new Error(`HTTP error! status: ${response.status}`);
172
- }
173
- const data = await response.json();
174
- return { ...data, ...localConfig };
175
- }
176
- catch {
177
- log.info(`Local config ${formatError(localConfig)}`);
178
- return localConfig;
179
- }
180
- }
181
- export async function getRemoteFileConfig() {
182
- const localConfig = await getLocalConfig();
183
- // call host + /api/get_config and parse the result as json using fetch
184
- try {
185
- const response = await fetch(`${localConfig.hostFilesApi}/files/config`);
186
- if (!response.ok) {
187
- throw new Error(`HTTP error! status: ${response.status}`);
188
- }
189
- return await response.json();
190
- }
191
- catch {
192
- return {
193
- partialUpload: false,
194
- TUSUpload: false,
195
- partialUploadForced: false,
196
- TUSUploadForced: false,
197
- maxUploadLength: MAX_UPLOAD_LENGTH_BYTES,
198
- maxChunkSize: MAX_CHUNK_SIZE_BYTES,
199
- alertUploadSize: ALERT_UPLOAD_SIZE_BYTES,
200
- };
201
- }
202
- }
203
- export async function createSupabaseClient(apikey, supaHost, supaKey) {
204
- const config = await getRemoteConfig();
205
- if (supaHost && supaKey) {
206
- log.info('Using custom supabase instance from provided options');
207
- config.supaHost = supaHost;
208
- config.supaKey = supaKey;
209
- }
210
- if (!config.supaHost || !config.supaKey) {
211
- log.error('Cannot connect to server please try again later');
212
- throw new Error('Cannot connect to server please try again later');
213
- }
214
- return createClient(config.supaHost, config.supaKey, {
215
- auth: {
216
- persistSession: false,
217
- },
218
- global: {
219
- headers: {
220
- capgkey: apikey,
221
- },
222
- },
223
- });
224
- }
225
- export async function checkKey(supabase, apikey, keymode) {
226
- const { data: apiAccess } = await supabase
227
- .rpc('is_allowed_capgkey', { apikey, keymode })
228
- .single();
229
- if (!apiAccess) {
230
- log.error(`Invalid API key or insufficient permissions.`);
231
- // create a string from keymode array with comma and space and "or" for the last one
232
- const keymodeStr = keymode.map((k, i) => {
233
- if (keymode.length === 1)
234
- return `"${k}"`;
235
- if (i === keymode.length - 1)
236
- return `or "${k}"`;
237
- return `"${k}", `;
238
- }).join('');
239
- const message = `Your key should be: ${keymodeStr} mode.`;
240
- log.error(message);
241
- throw new Error('Invalid API key or insufficient permissions.');
242
- }
243
- }
244
- export async function isPayingOrg(supabase, orgId) {
245
- const { data } = await supabase
246
- .rpc('is_paying_org', { orgid: orgId })
247
- .single();
248
- return data || false;
249
- }
250
- export async function isTrialOrg(supabase, orgId) {
251
- const { data } = await supabase
252
- .rpc('is_trial_org', { orgid: orgId })
253
- .single();
254
- return data || 0;
255
- }
256
- export async function isAllowedActionOrg(supabase, orgId) {
257
- const { data } = await supabase
258
- .rpc('is_allowed_action_org', { orgid: orgId })
259
- .single();
260
- return !!data;
261
- }
262
- export async function isAllowedActionAppIdApiKey(supabase, appId, apikey) {
263
- const { data } = await supabase
264
- .rpc('is_allowed_action', { apikey, appid: appId })
265
- .single();
266
- return !!data;
267
- }
268
- export async function isAllowedApp(supabase, apikey, appId) {
269
- const { data } = await supabase
270
- .rpc('is_app_owner', { apikey, appid: appId })
271
- .single();
272
- return !!data;
273
- }
274
- export var OrganizationPerm;
275
- (function (OrganizationPerm) {
276
- OrganizationPerm[OrganizationPerm["none"] = 0] = "none";
277
- OrganizationPerm[OrganizationPerm["read"] = 1] = "read";
278
- OrganizationPerm[OrganizationPerm["upload"] = 2] = "upload";
279
- OrganizationPerm[OrganizationPerm["write"] = 3] = "write";
280
- OrganizationPerm[OrganizationPerm["admin"] = 4] = "admin";
281
- OrganizationPerm[OrganizationPerm["super_admin"] = 5] = "super_admin";
282
- })(OrganizationPerm || (OrganizationPerm = {}));
283
- export const hasOrganizationPerm = (perm, required) => perm >= required;
284
- export async function isAllowedAppOrg(supabase, apikey, appId) {
285
- const { data, error } = await supabase
286
- .rpc('get_org_perm_for_apikey', { apikey, app_id: appId })
287
- .single();
288
- if (error) {
289
- log.error('Cannot get permissions for organization!');
290
- console.error(error);
291
- throw new Error('Cannot get permissions for organization');
292
- }
293
- const ok = data.includes('perm');
294
- if (ok) {
295
- let perm = null;
296
- switch (data) {
297
- case 'perm_none': {
298
- perm = OrganizationPerm.none;
299
- break;
300
- }
301
- case 'perm_read': {
302
- perm = OrganizationPerm.read;
303
- break;
304
- }
305
- case 'perm_upload': {
306
- perm = OrganizationPerm.upload;
307
- break;
308
- }
309
- case 'perm_write': {
310
- perm = OrganizationPerm.write;
311
- break;
312
- }
313
- case 'perm_admin': {
314
- perm = OrganizationPerm.admin;
315
- break;
316
- }
317
- case 'perm_owner': {
318
- perm = OrganizationPerm.super_admin;
319
- break;
320
- }
321
- default: {
322
- if (data.includes('invite')) {
323
- log.info('Please accept/deny the organization invitation before trying to access the app');
324
- throw new Error('Organization invitation pending');
325
- }
326
- log.error(`Invalid output when fetching organization permission. Response: ${data}`);
327
- throw new Error(`Invalid output when fetching organization permission. Response: ${data}`);
328
- }
329
- }
330
- return {
331
- okay: true,
332
- data: perm,
333
- };
334
- }
335
- // This means that something went wrong here
336
- let functionError = null;
337
- switch (data) {
338
- case 'INVALID_APIKEY': {
339
- functionError = 'INVALID_APIKEY';
340
- break;
341
- }
342
- case 'NO_APP': {
343
- functionError = 'NO_APP';
344
- break;
345
- }
346
- case 'NO_ORG': {
347
- functionError = 'NO_ORG';
348
- break;
349
- }
350
- default: {
351
- log.error(`Invalid error when fetching organization permission. Response: ${data}`);
352
- throw new Error(`Invalid error when fetching organization permission. Response: ${data}`);
353
- }
354
- }
355
- return {
356
- okay: false,
357
- error: functionError,
358
- };
359
- }
360
- export async function checkRemoteCliMessages(supabase, orgId, cliVersion) {
361
- const { data: messages, error } = await supabase.rpc('get_organization_cli_warnings', { orgid: orgId, cli_version: cliVersion });
362
- if (error) {
363
- log.error(`Cannot get cli warnings: ${formatError(error)}`);
364
- return;
365
- }
366
- if (messages.length > 0) {
367
- log.warn(`Found ${messages.length} cli warnings for your organization.`);
368
- let fatalError = null;
369
- for (const message of messages) {
370
- if (typeof message !== 'object' || typeof message.message !== 'string' || typeof message.fatal !== 'boolean') {
371
- log.error(`Invalid cli warning: ${message}`);
372
- continue;
373
- }
374
- const msg = message;
375
- if (msg.fatal) {
376
- log.error(`${msg.message.replaceAll('\\n', '\n')}`);
377
- fatalError = new Error(msg.message);
378
- }
379
- else {
380
- log.warn(`${msg.message.replaceAll('\\n', '\n')}`);
381
- }
382
- }
383
- if (fatalError) {
384
- log.error('Please fix the warnings and try again.');
385
- throw fatalError;
386
- }
387
- log.info('End of cli warnings.');
388
- }
389
- }
390
- export async function checkPlanValid(supabase, orgId, apikey, appId, warning = true) {
391
- const config = await getRemoteConfig();
392
- // isAllowedActionAppIdApiKey was updated in the orgs_v3 migration to work with the new system
393
- const validPlan = await (appId ? isAllowedActionAppIdApiKey(supabase, appId, apikey) : isAllowedActionOrg(supabase, orgId));
394
- if (!validPlan) {
395
- log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
396
- wait(100);
397
- import('open')
398
- .then((module) => {
399
- module.default(`${config.hostWeb}/dashboard/settings/plans`);
400
- });
401
- wait(500);
402
- throw new Error('Plan upgrade required');
403
- }
404
- const [trialDays, ispaying] = await Promise.all([
405
- isTrialOrg(supabase, orgId),
406
- isPayingOrg(supabase, orgId),
407
- ]);
408
- if (trialDays > 0 && warning && !ispaying)
409
- log.warn(`WARNING !!\nTrial expires in ${trialDays} days, upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
410
- }
411
- export async function checkPlanValidUpload(supabase, orgId, apikey, appId, warning = true) {
412
- const config = await getRemoteConfig();
413
- // isAllowedActionAppIdApiKey was updated in the orgs_v3 migration to work with the new system
414
- const { data: validPlan } = await supabase.rpc('is_allowed_action_org_action', { orgid: orgId, actions: ['storage'] });
415
- if (!validPlan) {
416
- log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
417
- wait(100);
418
- import('open')
419
- .then((module) => {
420
- module.default(`${config.hostWeb}/dashboard/settings/plans`);
421
- });
422
- wait(500);
423
- throw new Error('Plan upgrade required for upload');
424
- }
425
- const [trialDays, ispaying] = await Promise.all([
426
- isTrialOrg(supabase, orgId),
427
- isPayingOrg(supabase, orgId),
428
- ]);
429
- if (trialDays > 0 && warning && !ispaying)
430
- log.warn(`WARNING !!\nTrial expires in ${trialDays} days, upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
431
- }
432
- export function findSavedKey(quiet = false) {
433
- const envKey = env.CAPGO_TOKEN?.trim();
434
- if (envKey) {
435
- if (!quiet)
436
- log.info('Use CAPGO_TOKEN environment variable');
437
- return envKey;
438
- }
439
- // search for key in home dir
440
- const userHomeDir = homedir();
441
- let key;
442
- let keyPath = `${userHomeDir}/.capgo`;
443
- if (existsSync(keyPath)) {
444
- if (!quiet)
445
- log.info(`Use global API key ${keyPath}`);
446
- key = readFileSync(keyPath, 'utf8').trim();
447
- }
448
- keyPath = `.capgo`;
449
- if (!key && existsSync(keyPath)) {
450
- if (!quiet)
451
- log.info(`Use local API key ${keyPath}`);
452
- key = readFileSync(keyPath, 'utf8').trim();
453
- }
454
- if (!key) {
455
- const message = `Cannot find API key in local folder or global, please login first with ${getPMAndCommand().runner} @capacitor/cli login`;
456
- log.error(message);
457
- throw new Error(message);
458
- }
459
- return key;
460
- }
461
- async function* getFiles(dir) {
462
- const dirents = await readdirSync(dir, { withFileTypes: true });
463
- for (const dirent of dirents) {
464
- const res = resolve(dir, dirent.name);
465
- if (dirent.isDirectory()
466
- && !dirent.name.startsWith('.')
467
- && !dirent.name.startsWith('node_modules')
468
- && !dirent.name.startsWith('dist')) {
469
- yield* getFiles(res);
470
- }
471
- else {
472
- yield res;
473
- }
474
- }
475
- }
476
- export function getContentType(filename) {
477
- const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp)$/i;
478
- const match = filename.match(imageExtensions);
479
- if (match) {
480
- const ext = match[1].toLowerCase();
481
- switch (ext) {
482
- case 'jpg':
483
- case 'jpeg':
484
- return 'image/jpeg';
485
- case 'png':
486
- return 'image/png';
487
- case 'webp':
488
- return 'image/webp';
489
- }
490
- }
491
- return null;
492
- }
493
- export async function findProjectType() {
494
- // for nuxtjs check if nuxt.config.js exists
495
- // for nextjs check if next.config.js exists
496
- // for angular check if angular.json exists
497
- // for sveltekit check if svelte.config.js exists or svelte is in package.json dependancies
498
- // for vue check if vue.config.js exists or vue is in package.json dependancies
499
- // for react check if package.json exists and react is in dependencies
500
- const pwd = cwd();
501
- let isTypeScript = false;
502
- // Check for TypeScript configuration file
503
- const tsConfigPath = resolve(pwd, 'tsconfig.json');
504
- if (existsSync(tsConfigPath)) {
505
- isTypeScript = true;
506
- }
507
- for await (const f of getFiles(pwd)) {
508
- // find number of folder in path after pwd
509
- if (f.includes('angular.json')) {
510
- log.info('Found angular project');
511
- return isTypeScript ? 'angular-ts' : 'angular-js';
512
- }
513
- if (f.includes('nuxt.config.js') || f.includes('nuxt.config.ts')) {
514
- log.info('Found nuxtjs project');
515
- return isTypeScript ? 'nuxtjs-ts' : 'nuxtjs-js';
516
- }
517
- if (f.includes('next.config.js') || f.includes('next.config.mjs')) {
518
- log.info('Found nextjs project');
519
- return isTypeScript ? 'nextjs-ts' : 'nextjs-js';
520
- }
521
- if (f.includes('svelte.config.js')) {
522
- log.info('Found sveltekit project');
523
- return isTypeScript ? 'sveltekit-ts' : 'sveltekit-js';
524
- }
525
- if (f.includes('rolluconfig.js')) {
526
- log.info('Found svelte project');
527
- return isTypeScript ? 'svelte-ts' : 'svelte-js';
528
- }
529
- if (f.includes('vue.config.js')) {
530
- log.info('Found vue project');
531
- return isTypeScript ? 'vue-ts' : 'vue-js';
532
- }
533
- if (f.includes(PACKNAME)) {
534
- const folder = dirname(f);
535
- const dependencies = await getAllPackagesDependencies(folder);
536
- if (dependencies) {
537
- if (dependencies.get('react')) {
538
- log.info('Found react project test');
539
- return isTypeScript ? 'react-ts' : 'react-js';
540
- }
541
- if (dependencies.get('vue')) {
542
- log.info('Found vue project');
543
- return isTypeScript ? 'vue-ts' : 'vue-js';
544
- }
545
- }
546
- }
547
- }
548
- return 'unknown';
549
- }
550
- export function findMainFileForProjectType(projectType, isTypeScript) {
551
- if (projectType === 'angular-js' || projectType === 'angular-ts') {
552
- return isTypeScript ? 'src/main.ts' : 'src/main.js';
553
- }
554
- if (projectType === 'nextjs-js' || projectType === 'nextjs-ts') {
555
- return isTypeScript ? 'src/app/layout.tsx' : 'src/app/layout.js';
556
- }
557
- if (projectType === 'svelte-js' || projectType === 'svelte-ts') {
558
- return isTypeScript ? 'src/main.ts' : 'src/main.js';
559
- }
560
- if (projectType === 'vue-js' || projectType === 'vue-ts') {
561
- return isTypeScript ? 'src/main.ts' : 'src/main.js';
562
- }
563
- if (projectType === 'react-js' || projectType === 'react-ts') {
564
- return isTypeScript ? 'src/index.tsx' : 'src/index.js';
565
- }
566
- return null;
567
- }
568
- // create a function to find the right command to build the project in static mode depending on the project type
569
- export async function findBuildCommandForProjectType(projectType) {
570
- if (projectType === 'angular') {
571
- log.info('Angular project detected');
572
- return 'build';
573
- }
574
- if (projectType === 'nuxtjs') {
575
- log.info('Nuxtjs project detected');
576
- return 'generate';
577
- }
578
- if (projectType === 'nextjs') {
579
- log.info('Nextjs project detected');
580
- log.warn('Please make sure you have configured static export in your next.config.js: https://nextjs.org/docs/pages/building-your-application/deploying/static-exports');
581
- log.warn('Please make sure you have the output: \'export\' and distDir: \'dist\' in your next.config.js');
582
- const doContinue = await confirmC({ message: 'Do you want to continue?' });
583
- if (!doContinue) {
584
- const message = 'Build command selection aborted by user';
585
- log.error(message);
586
- throw new Error(message);
587
- }
588
- return 'build';
589
- }
590
- if (projectType === 'sveltekit') {
591
- log.info('Sveltekit project detected');
592
- log.warn('Please make sure you have the adapter-static installed: https://kit.svelte.dev/docs/adapter-static');
593
- log.warn('Please make sure you have the pages: \'dist\' and assets: \'dest\', in your svelte.config.js adaptater');
594
- const doContinue = await confirmC({ message: 'Do you want to continue?' });
595
- if (!doContinue) {
596
- const message = 'Build command selection aborted by user';
597
- log.error(message);
598
- throw new Error(message);
599
- }
600
- return 'build';
601
- }
602
- return 'build';
603
- }
604
- export async function findMainFile() {
605
- // eslint-disable-next-line regexp/no-unused-capturing-group
606
- const mainRegex = /(main|index)\.(ts|tsx|js|jsx)$/;
607
- // search for main.ts or main.js in local dir and subdirs
608
- let mainFile = '';
609
- const pwd = cwd();
610
- const pwdL = pwd.split('/').length;
611
- for await (const f of getFiles(pwd)) {
612
- // find number of folder in path after pwd
613
- const folders = f.split('/').length - pwdL;
614
- if (folders <= 2 && mainRegex.test(f)) {
615
- mainFile = f;
616
- log.info(`Found main file here ${f}`);
617
- break;
618
- }
619
- }
620
- return mainFile;
621
- }
622
- export async function updateOrCreateVersion(supabase, update) {
623
- return supabase.from('app_versions')
624
- .upsert(update, { onConflict: 'name,app_id' })
625
- .eq('app_id', update.app_id)
626
- .eq('name', update.name);
627
- }
628
- export async function uploadUrl(supabase, appId, name) {
629
- const data = {
630
- app_id: appId,
631
- name,
632
- version: 0,
633
- };
634
- try {
635
- const pathUploadLink = 'files/upload_link';
636
- const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) });
637
- if (res.error) {
638
- // Handle error case
639
- if (res.error instanceof FunctionsHttpError) {
640
- const errorBody = await res.error.context.json();
641
- log.error(`Upload URL error: ${errorBody.status || JSON.stringify(errorBody)}`);
642
- }
643
- else {
644
- log.error(`Cannot get upload url: ${res.error.message}`);
645
- }
646
- return '';
647
- }
648
- return res.data.url;
649
- }
650
- catch (error) {
651
- log.error(`Cannot get upload url ${formatError(error)}`);
652
- }
653
- return '';
654
- }
655
- async function* walkDirectory(dir) {
656
- const entries = readdirSync(dir, { withFileTypes: true });
657
- for (const entry of entries) {
658
- const fullPath = join(dir, entry.name);
659
- if (entry.isDirectory()) {
660
- yield* walkDirectory(fullPath);
661
- }
662
- else {
663
- yield fullPath;
664
- }
665
- }
666
- }
667
- export async function generateManifest(path) {
668
- const allFiles = [];
669
- const ignoredFiles = ['.DS_Store', '.git', '.gitignore', 'node_modules', 'package-lock.json', 'tsconfig.json', 'tsconfig.app.json', 'tsconfig.spec.json', 'tsconfig.app.json', 'tsconfig.spec.json', 'tsconfig.app.json', 'tsconfig.spec.json'];
670
- for await (const file of walkDirectory(path)) {
671
- if (ignoredFiles.some(ignoredFile => file.includes(ignoredFile))) {
672
- log.info(`Ignoring file ${file}, please ensure you have only required files in your dist folder`);
673
- continue;
674
- }
675
- const buffer = readFileSync(file);
676
- // ignore files with size 0
677
- if (buffer.length === 0) {
678
- log.info(`Ignoring empty file ${file}, please ensure you have only required files in your dist folder`);
679
- continue;
680
- }
681
- const hash = await getChecksum(buffer, 'sha256');
682
- let filePath = relative(path, file);
683
- if (filePath.startsWith('/'))
684
- filePath = filePath.substring(1);
685
- allFiles.push({ file: filePath, hash });
686
- }
687
- return allFiles;
688
- }
689
- export async function zipFile(filePath) {
690
- if (osPlatform() === 'win32') {
691
- return zipFileWindows(filePath);
692
- }
693
- else {
694
- return zipFileUnix(filePath);
695
- }
696
- }
697
- export function zipFileUnix(filePath) {
698
- const zip = new AdmZip();
699
- zip.addLocalFolder(filePath);
700
- return zip.toBuffer();
701
- }
702
- export async function zipFileWindows(filePath) {
703
- log.info('Zipping file windows mode');
704
- const zip = new AdmZip();
705
- const addToZip = (folderPath, zipPath) => {
706
- const items = readdirSync(folderPath);
707
- for (const item of items) {
708
- const itemPath = join(folderPath, item);
709
- const stats = statSync(itemPath);
710
- if (stats.isFile()) {
711
- const fileContent = readFileSync(itemPath);
712
- zip.addFile(join(zipPath, item).split(sep).join('/'), fileContent);
713
- }
714
- else if (stats.isDirectory()) {
715
- addToZip(itemPath, join(zipPath, item));
716
- }
717
- }
718
- };
719
- addToZip(filePath, '');
720
- return zip.toBuffer();
721
- }
722
- export async function uploadTUS(apikey, data, orgId, appId, name, spinner, localConfig, chunkSize) {
723
- return new Promise((resolve, reject) => {
724
- sendEvent(apikey, {
725
- channel: 'app',
726
- event: 'App TUS upload',
727
- icon: '⏫',
728
- user_id: orgId,
729
- tags: {
730
- 'app-id': appId,
731
- },
732
- notify: false,
733
- });
734
- const upload = new tus.Upload(data, {
735
- endpoint: `${localConfig.hostFilesApi}/files/upload/attachments/`,
736
- // parallelUploads: multipart,
737
- chunkSize,
738
- metadataForPartialUploads: {
739
- filename: `orgs/${orgId}/apps/${appId}/${name}.zip`,
740
- filetype: 'application/gzip',
741
- },
742
- metadata: {
743
- filename: `orgs/${orgId}/apps/${appId}/${name}.zip`,
744
- filetype: 'application/zip',
745
- },
746
- headers: {
747
- Authorization: apikey,
748
- },
749
- // Callback for errors which cannot be fixed using retries
750
- onError(error) {
751
- log.error(`Error uploading bundle: ${error.message}`);
752
- if (error instanceof tus.DetailedError) {
753
- const body = error.originalResponse?.getBody();
754
- const jsonBody = JSON.parse(body || '{"error": "unknown error"}');
755
- reject(jsonBody.status || jsonBody.error || jsonBody.message || 'unknown error');
756
- }
757
- else {
758
- reject(error.message || error.toString() || 'unknown error');
759
- }
760
- },
761
- // Callback for reporting upload progress
762
- onProgress(bytesUploaded, bytesTotal) {
763
- const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
764
- spinner.message(`Uploaded ${percentage}%`);
765
- },
766
- // Callback for once the upload is completed
767
- async onSuccess() {
768
- await sendEvent(apikey, {
769
- channel: 'app',
770
- event: 'App TUS done',
771
- icon: '⏫',
772
- user_id: orgId,
773
- tags: {
774
- 'app-id': appId,
775
- },
776
- notify: false,
777
- }).catch();
778
- resolve(true);
779
- },
780
- });
781
- // Start the upload
782
- upload.start();
783
- });
784
- }
785
- export async function deletedFailedVersion(supabase, appId, name) {
786
- const data = {
787
- app_id: appId,
788
- name,
789
- };
790
- try {
791
- const pathFailed = 'private/delete_failed_version';
792
- const res = await supabase.functions.invoke(pathFailed, { body: JSON.stringify(data), method: 'DELETE' });
793
- if (res.error) {
794
- if (res.error instanceof FunctionsHttpError) {
795
- const errorBody = await res.error.context.json();
796
- log.error(`Cannot delete failed version: ${errorBody.status || JSON.stringify(errorBody)}`);
797
- }
798
- else {
799
- log.error(`Cannot delete failed version: ${res.error.message}`);
800
- }
801
- return;
802
- }
803
- return res.data?.status;
804
- }
805
- catch (error) {
806
- if (error instanceof FunctionsHttpError) {
807
- const errorBody = await error.context.json();
808
- log.error(`Cannot delete failed version: ${errorBody.message || JSON.stringify(errorBody)}`);
809
- }
810
- else {
811
- log.error(`Cannot delete failed version: ${formatError(error)}`);
812
- }
813
- }
814
- }
815
- export async function updateOrCreateChannel(supabase, update) {
816
- // console.log('updateOrCreateChannel', update)
817
- if (!update.app_id || !update.name || !update.created_by) {
818
- log.error('missing app_id, name, or created_by');
819
- return Promise.reject(new Error('missing app_id, name, or created_by'));
820
- }
821
- const { data, error } = await supabase
822
- .from('channels')
823
- .select()
824
- .eq('app_id', update.app_id)
825
- .eq('name', update.name)
826
- .single();
827
- if (data && !error) {
828
- return supabase
829
- .from('channels')
830
- .update(update)
831
- .eq('app_id', update.app_id)
832
- .eq('name', update.name)
833
- .select()
834
- .single();
835
- }
836
- return supabase
837
- .from('channels')
838
- .insert(update)
839
- .select()
840
- .single();
841
- }
842
- export async function sendEvent(capgkey, payload, verbose) {
843
- try {
844
- if (verbose) {
845
- log.info(`Get remove config: for ${payload.event}`);
846
- }
847
- const config = await getRemoteConfig();
848
- if (verbose) {
849
- log.info(`Sending LogSnag event: ${JSON.stringify(payload)}`);
850
- }
851
- const controller = new AbortController();
852
- const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 seconds timeout
853
- try {
854
- const fetchResponse = await fetch(`${config.hostApi}/private/events`, {
855
- method: 'POST',
856
- body: JSON.stringify(payload),
857
- headers: {
858
- 'Content-Type': 'application/json',
859
- 'capgkey': capgkey,
860
- },
861
- signal: controller.signal,
862
- });
863
- clearTimeout(timeoutId);
864
- if (!fetchResponse.ok) {
865
- throw new Error(`HTTP error! status: ${fetchResponse.status}`);
866
- }
867
- const response = await fetchResponse.json();
868
- if (response.error) {
869
- log.error(`Failed to send LogSnag event: ${response.error}`);
870
- }
871
- }
872
- finally {
873
- clearTimeout(timeoutId);
874
- }
875
- }
876
- catch (error) {
877
- if (verbose) {
878
- log.error('Failed to send Stats event details:');
879
- log.error(formatError(error));
880
- }
881
- }
882
- }
883
- export async function getOrganization(supabase, roles) {
884
- const { error: orgError, data: allOrganizations } = await supabase
885
- .rpc('get_orgs_v6');
886
- if (orgError) {
887
- log.error('Cannot get the list of organizations - exiting');
888
- log.error(`Error ${JSON.stringify(orgError)}`);
889
- throw new Error('Cannot get the list of organizations');
890
- }
891
- const adminOrgs = allOrganizations.filter(org => !!roles.find(role => role === org.role));
892
- if (allOrganizations.length === 0) {
893
- log.error('Could not get organization please create an organization first');
894
- throw new Error('No organizations available');
895
- }
896
- if (adminOrgs.length === 0) {
897
- log.error(`Could not find organization with roles: ${roles.join(' or ')} please create an organization or ask the admin to add you to the organization with this roles`);
898
- throw new Error('Could not find organization with required roles');
899
- }
900
- const organizationUidRaw = (adminOrgs.length > 1)
901
- ? await select({
902
- message: 'Please pick the organization that you want to insert to',
903
- options: adminOrgs.map((org) => {
904
- return { value: org.gid, label: org.name };
905
- }),
906
- })
907
- : adminOrgs[0].gid;
908
- if (isCancel(organizationUidRaw)) {
909
- log.error('Canceled organization selection, exiting');
910
- throw new Error('Organization selection cancelled');
911
- }
912
- const organizationUid = organizationUidRaw;
913
- const organization = allOrganizations.find(org => org.gid === organizationUid);
914
- log.info(`Using the organization "${organization.name}" as the app owner`);
915
- return organization;
916
- }
917
- export async function verifyUser(supabase, apikey, keymod = ['all']) {
918
- await checkKey(supabase, apikey, keymod);
919
- const { data: dataUser, error: userIdError } = await supabase
920
- .rpc('get_user_id', { apikey })
921
- .single();
922
- const userId = (dataUser || '').toString();
923
- if (!userId || userIdError) {
924
- log.error(`Cannot auth user with apikey`);
925
- throw new Error('Cannot authenticate user with provided API key');
926
- }
927
- return userId;
928
- }
929
- export async function getOrganizationId(supabase, appId) {
930
- const { data, error } = await supabase.from('apps')
931
- .select('owner_org')
932
- .eq('app_id', appId)
933
- .single();
934
- if (!data || error) {
935
- log.error(`Cannot get organization id for app id ${appId}`);
936
- formatError(error);
937
- throw new Error(`Cannot get organization id for app id ${appId}`);
938
- }
939
- return data.owner_org;
940
- }
941
- export async function requireUpdateMetadata(supabase, channel, appId) {
942
- const { data, error } = await supabase
943
- .from('channels')
944
- .select('disable_auto_update')
945
- .eq('name', channel)
946
- .eq('app_id', appId)
947
- .limit(1);
948
- if (error) {
949
- log.error(`Cannot check if disableAutoUpdate is required ${formatError(error)}`);
950
- throw new Error('Cannot check if disableAutoUpdate is required');
951
- }
952
- // Channel does not exist and the default is never 'version_number'
953
- if (data.length === 0)
954
- return false;
955
- const { disable_auto_update } = (data[0]);
956
- return disable_auto_update === 'version_number';
957
- }
958
- export function getHumanDate(createdA) {
959
- const date = new Date(createdA || '');
960
- return date.toLocaleString();
961
- }
962
- let pmFetched = false;
963
- let pm = 'npm';
964
- let pmCommand = 'install';
965
- let pmRunner = 'npx';
966
- export function getPMAndCommand() {
967
- if (pmFetched)
968
- return { pm, command: pmCommand, installCommand: `${pm} ${pmCommand}`, runner: pmRunner };
969
- const dir = findRoot(cwd());
970
- pm = findPackageManagerType(dir, 'npm');
971
- pmCommand = findInstallCommand(pm);
972
- pmFetched = true;
973
- pmRunner = findPackageManagerRunner(dir);
974
- return { pm, command: pmCommand, installCommand: `${pm} ${pmCommand}`, runner: pmRunner };
975
- }
976
- function readDirRecursively(dir) {
977
- const entries = readdirSync(dir, { withFileTypes: true });
978
- const files = entries.flatMap((entry) => {
979
- const fullPath = join(dir, entry.name);
980
- if (entry.isDirectory()) {
981
- return readDirRecursively(fullPath);
982
- }
983
- else {
984
- // Use relative path to avoid issues with long paths on Windows
985
- return fullPath.split(`node_modules${sep}`)[1] || fullPath;
986
- }
987
- });
988
- return files;
989
- }
990
- export async function getLocalDepenencies(packageJsonPath, nodeModulesString) {
991
- const nodeModules = nodeModulesString ? nodeModulesString.split(',') : [];
992
- let dependencies;
993
- try {
994
- dependencies = await getAllPackagesDependencies('', packageJsonPath);
995
- }
996
- catch (err) {
997
- log.error('Invalid package.json, JSON parsing failed');
998
- console.error('json parse error: ', err);
999
- throw err instanceof Error ? err : new Error('Invalid package.json');
1000
- }
1001
- const firstPackageJson = packageJsonPath?.split(',')[0];
1002
- const dir = !firstPackageJson ? findRoot(cwd()) : path.resolve(firstPackageJson).replace(PACKNAME, '');
1003
- if (!dependencies) {
1004
- log.error('Missing dependencies section in package.json');
1005
- throw new Error('Missing dependencies section in package.json');
1006
- }
1007
- for (const [key, value] of Object.entries(dependencies)) {
1008
- if (typeof value !== 'string') {
1009
- log.error(`Invalid dependency ${key}: ${value}, expected string, got ${typeof value}`);
1010
- throw new Error(`Invalid dependency ${key}: expected string version`);
1011
- }
1012
- }
1013
- const nodeModulesPaths = nodeModules.length === 0
1014
- ? [join(cwd(), 'node_modules')]
1015
- : nodeModules;
1016
- const anyValidPath = nodeModulesPaths.some(path => existsSync(path));
1017
- if (!anyValidPath) {
1018
- const pm = findPackageManagerType(dir, 'npm');
1019
- const installCmd = findInstallCommand(pm);
1020
- log.error(`Missing node_modules folder at ${nodeModulesPaths.join(', ')}, please run ${pm} ${installCmd}`);
1021
- throw new Error('Missing node_modules folder');
1022
- }
1023
- let anyInvalid = false;
1024
- const dependenciesObject = await Promise.all(Array.from(dependencies.entries())
1025
- .map(async ([key, value]) => {
1026
- let dependencyFound = false;
1027
- let hasNativeFiles = false;
1028
- for (const modulePath of nodeModulesPaths) {
1029
- const dependencyFolderPath = join(modulePath, key);
1030
- if (existsSync(dependencyFolderPath)) {
1031
- dependencyFound = true;
1032
- try {
1033
- const files = readDirRecursively(dependencyFolderPath);
1034
- if (files.some(fileName => nativeFileRegex.test(fileName))) {
1035
- hasNativeFiles = true;
1036
- break;
1037
- }
1038
- }
1039
- catch (error) {
1040
- log.error(`Error reading node_modules files for ${key} package in ${modulePath}`);
1041
- console.error(error);
1042
- throw error instanceof Error ? error : new Error(`Error reading node_modules files for ${key}`);
1043
- }
1044
- }
1045
- }
1046
- if (!dependencyFound) {
1047
- anyInvalid = true;
1048
- const pm = findPackageManagerType(dir, 'npm');
1049
- const installCmd = findInstallCommand(pm);
1050
- log.error(`Missing dependency ${key}, please run ${pm} ${installCmd}`);
1051
- return { name: key, version: value };
1052
- }
1053
- return {
1054
- name: key,
1055
- version: value,
1056
- native: hasNativeFiles,
1057
- };
1058
- })).catch(() => []);
1059
- if (anyInvalid || dependenciesObject.find(a => a.native === undefined)) {
1060
- log.error('Missing dependencies or invalid dependencies');
1061
- log.error('If you use monorepo, workspace or any special package manager you can use the --package-json [path,] and --node-modules [path,] options to make the command work properly');
1062
- throw new Error('Missing dependencies or invalid dependencies');
1063
- }
1064
- return dependenciesObject;
1065
- }
1066
- export async function getRemoteChecksums(supabase, appId, channel) {
1067
- const { data, error } = await supabase
1068
- .from('channels')
1069
- .select(`version(checksum)`)
1070
- .eq('name', channel)
1071
- .eq('app_id', appId)
1072
- .single();
1073
- const channelData = data;
1074
- if (error
1075
- || channelData === null
1076
- || !channelData.version
1077
- || !channelData.version.checksum) {
1078
- return null;
1079
- }
1080
- return channelData.version.checksum;
1081
- }
1082
- export function convertNativePackages(nativePackages) {
1083
- if (!nativePackages) {
1084
- log.error(`Error parsing native packages, perhaps the metadata does not exist in Capgo?`);
1085
- throw new Error('Error parsing native packages');
1086
- }
1087
- // Check types
1088
- for (const data of nativePackages) {
1089
- if (typeof data !== 'object') {
1090
- log.error(`Invalid remote native package data: ${data}, expected object, got ${typeof data}`);
1091
- throw new Error('Invalid remote native package data');
1092
- }
1093
- const { name, version } = data;
1094
- if (!name || typeof name !== 'string') {
1095
- log.error(`Invalid remote native package name: ${name}, expected string, got ${typeof name}`);
1096
- throw new Error('Invalid remote native package name');
1097
- }
1098
- if (!version || typeof version !== 'string') {
1099
- log.error(`Invalid remote native package version: ${version}, expected string, got ${typeof version}`);
1100
- throw new Error('Invalid remote native package version');
1101
- }
1102
- }
1103
- const mappedRemoteNativePackages = new Map((nativePackages)
1104
- .map(a => [a.name, a]));
1105
- return mappedRemoteNativePackages;
1106
- }
1107
- export async function getRemoteDepenencies(supabase, appId, channel) {
1108
- const { data: remoteNativePackages, error } = await supabase
1109
- .from('channels')
1110
- .select(`version (
1111
- native_packages
1112
- )`)
1113
- .eq('name', channel)
1114
- .eq('app_id', appId)
1115
- .single();
1116
- if (error) {
1117
- log.error(`Error fetching native packages: ${error.message}`);
1118
- throw new Error(`Error fetching native packages: ${error.message}`);
1119
- }
1120
- return convertNativePackages(remoteNativePackages.version.native_packages ?? []);
1121
- }
1122
- export async function checkChecksum(supabase, appId, channel, currentChecksum) {
1123
- const s = spinnerC();
1124
- s.start(`Checking bundle checksum compatibility with channel ${channel}`);
1125
- const remoteChecksum = await getRemoteChecksums(supabase, appId, channel);
1126
- if (!remoteChecksum) {
1127
- s.stop(`No checksum found for channel ${channel}, the bundle will be uploaded`);
1128
- return;
1129
- }
1130
- if (remoteChecksum && remoteChecksum === currentChecksum) {
1131
- // cannot upload the same bundle
1132
- log.error(`Cannot upload the same bundle content.\nCurrent bundle checksum matches remote bundle for channel ${channel}\nDid you builded your app before uploading?\nPS: You can ignore this check with "--ignore-checksum-check"`);
1133
- throw new Error('Cannot upload the same bundle content');
1134
- }
1135
- s.stop(`Checksum compatible with ${channel} channel`);
1136
- }
1137
- export function getAppId(appId, config) {
1138
- const finalAppId = appId || config?.plugins?.CapacitorUpdater?.appId || config?.appId;
1139
- return finalAppId;
1140
- }
1141
- export function isCompatible(pkg) {
1142
- // Only check compatibility if there's a local version
1143
- // If there's a local version but no remote version, or versions don't match, it's incompatible
1144
- if (!pkg.localVersion)
1145
- return true; // If no local version, it's compatible (remote-only package)
1146
- if (!pkg.remoteVersion)
1147
- return false; // If local version but no remote version, it's incompatible
1148
- try {
1149
- return subset(pkg.localVersion, pkg.remoteVersion);
1150
- }
1151
- catch {
1152
- return false; // If version comparison fails, consider it incompatible
1153
- }
1154
- }
1155
- export async function checkCompatibility(supabase, appId, channel, packageJsonPath, nodeModules) {
1156
- const dependenciesObject = await getLocalDepenencies(packageJsonPath, nodeModules);
1157
- const mappedRemoteNativePackages = await getRemoteDepenencies(supabase, appId, channel);
1158
- const finalDepenencies = dependenciesObject
1159
- .filter(a => !!a.native)
1160
- .map((local) => {
1161
- const remotePackage = mappedRemoteNativePackages.get(local.name);
1162
- if (remotePackage) {
1163
- return {
1164
- name: local.name,
1165
- localVersion: local.version,
1166
- remoteVersion: remotePackage.version,
1167
- };
1168
- }
1169
- return {
1170
- name: local.name,
1171
- localVersion: local.version,
1172
- remoteVersion: undefined,
1173
- };
1174
- });
1175
- // Only include remote packages that are not in local for informational purposes
1176
- // These won't affect compatibility
1177
- const removeNotInLocal = [...mappedRemoteNativePackages]
1178
- .filter(([remoteName]) => dependenciesObject.find(a => a.name === remoteName) === undefined)
1179
- .map(([name, version]) => ({ name, localVersion: undefined, remoteVersion: version.version }));
1180
- finalDepenencies.push(...removeNotInLocal);
1181
- return {
1182
- finalCompatibility: finalDepenencies,
1183
- localDependencies: dependenciesObject,
1184
- };
1185
- }
1186
- export async function checkCompatibilityNativePackages(supabase, appId, channel, nativePackages) {
1187
- const mappedRemoteNativePackages = await getRemoteDepenencies(supabase, appId, channel);
1188
- const finalDepenencies = nativePackages
1189
- .map((local) => {
1190
- const remotePackage = mappedRemoteNativePackages.get(local.name);
1191
- if (remotePackage) {
1192
- return {
1193
- name: local.name,
1194
- localVersion: local.version,
1195
- remoteVersion: remotePackage.version,
1196
- };
1197
- }
1198
- return {
1199
- name: local.name,
1200
- localVersion: local.version,
1201
- remoteVersion: undefined,
1202
- };
1203
- });
1204
- // Only include remote packages that are not in local for informational purposes
1205
- // These won't affect compatibility
1206
- const removeNotInLocal = [...mappedRemoteNativePackages]
1207
- .filter(([remoteName]) => nativePackages.find(a => a.name === remoteName) === undefined)
1208
- .map(([name, version]) => ({ name, localVersion: undefined, remoteVersion: version.version }));
1209
- finalDepenencies.push(...removeNotInLocal);
1210
- return {
1211
- finalCompatibility: finalDepenencies,
1212
- localDependencies: nativePackages,
1213
- };
1214
- }
1215
- //# sourceMappingURL=utils.js.map