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