@capgo/cli 5.0.0-alpha.3 → 5.0.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 (59) hide show
  1. package/README.md +197 -37
  2. package/dist/index.js +327 -65172
  3. package/dist/package.json +83 -0
  4. package/package.json +48 -61
  5. package/.eslintignore +0 -4
  6. package/.github/FUNDING.yml +0 -1
  7. package/.github/workflows/build.yml +0 -46
  8. package/.github/workflows/bump_version.yml +0 -56
  9. package/.github/workflows/test.yml +0 -30
  10. package/.prettierignore +0 -6
  11. package/.vscode/launch.json +0 -23
  12. package/.vscode/settings.json +0 -5
  13. package/.vscode/tasks.json +0 -42
  14. package/CHANGELOG.md +0 -2727
  15. package/build.mjs +0 -23
  16. package/bun.lockb +0 -0
  17. package/capacitor.config.ts +0 -33
  18. package/crypto_explained.png +0 -0
  19. package/eslint.config.js +0 -3
  20. package/renovate.json +0 -23
  21. package/src/api/app.ts +0 -75
  22. package/src/api/channels.ts +0 -142
  23. package/src/api/crypto.ts +0 -121
  24. package/src/api/devices_override.ts +0 -41
  25. package/src/api/update.ts +0 -12
  26. package/src/api/versions.ts +0 -98
  27. package/src/app/add.ts +0 -154
  28. package/src/app/debug.ts +0 -214
  29. package/src/app/delete.ts +0 -68
  30. package/src/app/info.ts +0 -87
  31. package/src/app/list.ts +0 -63
  32. package/src/app/set.ts +0 -94
  33. package/src/bundle/check.ts +0 -42
  34. package/src/bundle/cleanup.ts +0 -128
  35. package/src/bundle/compatibility.ts +0 -70
  36. package/src/bundle/decrypt.ts +0 -65
  37. package/src/bundle/delete.ts +0 -53
  38. package/src/bundle/encrypt.ts +0 -69
  39. package/src/bundle/list.ts +0 -43
  40. package/src/bundle/unlink.ts +0 -80
  41. package/src/bundle/upload.ts +0 -434
  42. package/src/bundle/zip.ts +0 -137
  43. package/src/channel/add.ts +0 -73
  44. package/src/channel/currentBundle.ts +0 -73
  45. package/src/channel/delete.ts +0 -51
  46. package/src/channel/list.ts +0 -49
  47. package/src/channel/set.ts +0 -171
  48. package/src/index.ts +0 -285
  49. package/src/init.ts +0 -301
  50. package/src/key.ts +0 -158
  51. package/src/login.ts +0 -66
  52. package/src/types/capacitor__cli.d.ts +0 -6
  53. package/src/types/supabase.types.ts +0 -2065
  54. package/src/utils.ts +0 -719
  55. package/test/chunk_convert.ts +0 -28
  56. package/test/data.ts +0 -18769
  57. package/test/test_headers_rls.ts +0 -24
  58. package/test/test_semver.ts +0 -13
  59. package/tsconfig.json +0 -39
package/build.mjs DELETED
@@ -1,23 +0,0 @@
1
- import * as esbuild from 'esbuild'
2
-
3
- // Replace 'your-external-dependencies-here' with actual externals from your project
4
- const external = [];
5
-
6
- esbuild.build({
7
- entryPoints: ['src/index.ts'],
8
- bundle: true,
9
- platform: 'node',
10
- target: 'node20',
11
- external,
12
- outdir: 'dist',
13
- sourcemap: process.env.NODE_ENV === 'development',
14
- banner: {
15
- js: '#!/usr/bin/env node',
16
- },
17
- define: {
18
- 'process.env.SUPA_DB': '"production"',
19
- },
20
- loader: {
21
- '.ts': 'ts',
22
- },
23
- }).catch(() => process.exit(1));
package/bun.lockb DELETED
Binary file
@@ -1,33 +0,0 @@
1
- /*
2
- * Copyright 2020 EPAM Systems
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
- import type { CapacitorConfig } from '@capacitor/cli'
17
-
18
- const config: CapacitorConfig = {
19
- appId: 'ee.forgr.capacitor_go',
20
- appName: 'Capgo',
21
- webDir: 'dist',
22
- bundledWebRuntime: false,
23
- plugins: {
24
- PushNotifications: {
25
- presentationOptions: ['badge', 'sound', 'alert'],
26
- },
27
- SplashScreen: {
28
- launchAutoHide: false,
29
- },
30
- },
31
- }
32
-
33
- export default config
Binary file
package/eslint.config.js DELETED
@@ -1,3 +0,0 @@
1
- const antfu = require('@antfu/eslint-config').default
2
-
3
- module.exports = antfu()
package/renovate.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "config:base",
5
- "schedule:earlyMondays"
6
- ],
7
- "lockFileMaintenance": {
8
- "enabled": true,
9
- "automerge": true,
10
- "automergeType": "branch",
11
- "platformAutomerge": true
12
- },
13
- "packageRules": [
14
- {
15
- "matchUpdateTypes": [
16
- "minor",
17
- "patch"
18
- ],
19
- "matchCurrentVersion": "!/^0/",
20
- "automerge": true
21
- }
22
- ]
23
- }
package/src/api/app.ts DELETED
@@ -1,75 +0,0 @@
1
- import type { SupabaseClient } from '@supabase/supabase-js'
2
- import * as p from '@clack/prompts'
3
- import { program } from 'commander'
4
- import type { Database } from '../types/supabase.types'
5
- import type { OptionsBase } from '../utils'
6
- import { OrganizationPerm, isAllowedApp, isAllowedAppOrg } from '../utils'
7
-
8
- export async function checkAppExists(supabase: SupabaseClient<Database>, appid: string) {
9
- const { data: app } = await supabase
10
- .rpc('exist_app_v2', { appid })
11
- .single()
12
- return !!app
13
- }
14
-
15
- export async function checkAppExistsAndHasPermissionErr(supabase: SupabaseClient<Database>, apikey: string, appid: string, shouldExist = true) {
16
- const appExist = await checkAppExists(supabase, appid)
17
- const perm = await isAllowedApp(supabase, apikey, appid)
18
-
19
- if (appExist && !shouldExist) {
20
- p.log.error(`App ${appid} already exist`)
21
- program.error('')
22
- }
23
- if (!appExist && shouldExist) {
24
- p.log.error(`App ${appid} does not exist`)
25
- program.error('')
26
- }
27
- if (appExist && !perm) {
28
- p.log.error(`App ${appid} exist and you don't have permission to access it`)
29
- if (appid === 'io.ionic.starter')
30
- p.log.info('Modify your appid in your capacitor.config.json file to something unique, this is a default appid for ionic starter app')
31
-
32
- program.error('')
33
- }
34
- }
35
-
36
- export async function checkAppExistsAndHasPermissionOrgErr(supabase: SupabaseClient<Database>, apikey: string, appid: string, requiredPermission: OrganizationPerm) {
37
- const permissions = await isAllowedAppOrg(supabase, apikey, appid)
38
- if (!permissions.okay) {
39
- switch (permissions.error) {
40
- case 'INVALID_APIKEY': {
41
- p.log.error('Invalid apikey, such apikey does not exists!')
42
- program.error('')
43
- break
44
- }
45
- case 'NO_APP': {
46
- p.log.error(`App ${appid} does not exist`)
47
- program.error('')
48
- break
49
- }
50
- case 'NO_ORG': {
51
- p.log.error('Could not find organization, please contact support to resolve this!')
52
- program.error('')
53
- break
54
- }
55
- }
56
- }
57
-
58
- const remotePermNumber = permissions.data as number
59
- const requiredPermNumber = requiredPermission as number
60
-
61
- if (requiredPermNumber > remotePermNumber) {
62
- p.log.error(`Insuficcent permissions for app ${appid}. Current permission: ${OrganizationPerm[permissions.data]}, required for this action: ${OrganizationPerm[requiredPermission]}.`)
63
- program.error('')
64
- }
65
-
66
- return permissions.data
67
- }
68
-
69
- export interface Options extends OptionsBase {
70
- name?: string
71
- icon?: string
72
- retention?: number
73
- }
74
-
75
- export const newIconPath = 'assets/icon.png'
@@ -1,142 +0,0 @@
1
- import process from 'node:process'
2
- import type { SupabaseClient } from '@supabase/supabase-js'
3
- import { program } from 'commander'
4
- import { Table } from 'console-table-printer'
5
- import * as p from '@clack/prompts'
6
- import type { Database } from '../types/supabase.types'
7
- import { formatError } from '../utils'
8
-
9
- export async function checkVersionNotUsedInChannel(supabase: SupabaseClient<Database>, appid: string, userId: string, versionData: Database['public']['Tables']['app_versions']['Row']) {
10
- const { data: channelFound, error: errorChannel } = await supabase
11
- .from('channels')
12
- .select()
13
- .eq('app_id', appid)
14
- .eq('created_by', userId)
15
- .eq('version', versionData.id)
16
- if (errorChannel) {
17
- p.log.error(`Cannot check Version ${appid}@${versionData.name}`)
18
- program.error('')
19
- }
20
- if (channelFound && channelFound.length > 0) {
21
- p.intro(`❌ Version ${appid}@${versionData.name} is used in ${channelFound.length} channel`)
22
- if (await p.confirm({ message: 'unlink it?' })) {
23
- // loop on all channels and set version to unknown
24
- for (const channel of channelFound) {
25
- const s = p.spinner()
26
- s.start(`Unlinking channel ${channel.name}`)
27
- const { error: errorChannelUpdate } = await supabase
28
- .from('channels')
29
- .update({
30
- version: (await findUnknownVersion(supabase, appid))?.id,
31
- })
32
- .eq('id', channel.id)
33
- if (errorChannelUpdate) {
34
- s.stop(`Cannot update channel ${channel.name} ${formatError(errorChannelUpdate)}`)
35
- process.exit(1)
36
- }
37
- s.stop(`✅ Channel ${channel.name} unlinked`)
38
- }
39
- }
40
- else {
41
- p.log.error(`Unlink it first`)
42
- program.error('')
43
- }
44
- p.outro(`Version unlinked from ${channelFound.length} channel`)
45
- }
46
- }
47
-
48
- export function findUnknownVersion(supabase: SupabaseClient<Database>, appId: string) {
49
- return supabase
50
- .from('app_versions')
51
- .select('id')
52
- .eq('app_id', appId)
53
- .eq('name', 'unknown')
54
- .throwOnError()
55
- .single().then(({ data }) => data)
56
- }
57
-
58
- export function createChannel(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['channels']['Insert']) {
59
- return supabase
60
- .from('channels')
61
- .insert(update)
62
- .select()
63
- .single()
64
- }
65
-
66
- export function delChannel(supabase: SupabaseClient<Database>, name: string, appId: string, userId: string) {
67
- return supabase
68
- .from('channels')
69
- .delete()
70
- .eq('name', name)
71
- .eq('app_id', appId)
72
- .eq('created_by', userId)
73
- .single()
74
- }
75
- interface version {
76
- id: string
77
- name: string
78
- }
79
- export function displayChannels(data: (Database['public']['Tables']['channels']['Row'] & { version?: version, secondVersion?: version })[]) {
80
- const t = new Table({
81
- title: 'Channels',
82
- charLength: { '❌': 2, '✅': 2 },
83
- })
84
-
85
- // add rows with color
86
- data.reverse().forEach((row) => {
87
- t.addRow({
88
- 'Name': row.name,
89
- ...(row.version ? { Version: row.version.name } : undefined),
90
- 'Public': row.public ? '✅' : '❌',
91
- 'iOS': row.ios ? '❌' : '✅',
92
- 'Android': row.android ? '❌' : '✅',
93
- '⬆️ limit': row.disableAutoUpdate,
94
- '⬇️ under native': row.disableAutoUpdateUnderNative ? '❌' : '✅',
95
- 'Self assign': row.allow_device_self_set ? '✅' : '❌',
96
- 'Progressive': row.enable_progressive_deploy ? '✅' : '❌',
97
- ...(row.enable_progressive_deploy && row.secondVersion ? { 'Next version': row.secondVersion.name } : undefined),
98
- ...(row.enable_progressive_deploy && row.secondVersion ? { 'Next %': row.secondaryVersionPercentage } : undefined),
99
- 'AB Testing': row.enableAbTesting ? '✅' : '❌',
100
- ...(row.enableAbTesting && row.secondVersion ? { 'Version B': row.secondVersion } : undefined),
101
- ...(row.enableAbTesting && row.secondVersion ? { 'A/B %': row.secondaryVersionPercentage } : undefined),
102
- 'Emulator': row.allow_emulator ? '✅' : '❌',
103
- 'Dev 📱': row.allow_dev ? '✅' : '❌',
104
- })
105
- })
106
-
107
- p.log.success(t.render())
108
- }
109
-
110
- export async function getActiveChannels(supabase: SupabaseClient<Database>, appid: string) {
111
- const { data, error: vError } = await supabase
112
- .from('channels')
113
- .select(`
114
- id,
115
- name,
116
- public,
117
- allow_emulator,
118
- allow_dev,
119
- ios,
120
- android,
121
- allow_device_self_set,
122
- disableAutoUpdateUnderNative,
123
- disableAutoUpdate,
124
- enable_progressive_deploy,
125
- enableAbTesting,
126
- secondaryVersionPercentage,
127
- secondVersion (id, name),
128
- created_at,
129
- created_by,
130
- app_id,
131
- version (id, name)
132
- `)
133
- .eq('app_id', appid)
134
- // .eq('created_by', userId)
135
- .order('created_at', { ascending: false })
136
-
137
- if (vError) {
138
- p.log.error(`App ${appid} not found in database`)
139
- program.error('')
140
- }
141
- return data
142
- }
package/src/api/crypto.ts DELETED
@@ -1,121 +0,0 @@
1
- import {
2
- constants,
3
- createCipheriv,
4
- createDecipheriv,
5
- generateKeyPairSync,
6
- privateEncrypt,
7
- publicDecrypt,
8
- randomBytes,
9
- } from 'node:crypto'
10
- import { Buffer } from 'node:buffer'
11
-
12
- const algorithm = 'aes-128-cbc'
13
- const formatB64 = 'base64'
14
- const padding = constants.RSA_PKCS1_PADDING
15
-
16
- export function decryptSource(source: Buffer, ivSessionKey: string, key: string): Buffer {
17
- // console.log('decryptKeyType - ', decryptKeyType);
18
- // console.log(key);
19
- // console.log('\nivSessionKey', ivSessionKey)
20
- const [ivB64, sessionb64Encrypted] = ivSessionKey.split(':')
21
- // console.log('\nsessionb64Encrypted', sessionb64Encrypted)
22
- // console.log('\nivB64', ivB64)
23
- const sessionKey = publicDecrypt(
24
- {
25
- key,
26
- padding,
27
- },
28
- Buffer.from(sessionb64Encrypted, formatB64),
29
- )
30
-
31
- // ivB64 to uft-8
32
- const initVector = Buffer.from(ivB64, formatB64)
33
- // console.log('\nSessionB64', sessionB64)
34
-
35
- const decipher = createDecipheriv(algorithm, sessionKey, initVector)
36
- decipher.setAutoPadding(true)
37
- const decryptedData = Buffer.concat([decipher.update(source), decipher.final()])
38
-
39
- return decryptedData
40
- }
41
- export interface Encoded {
42
- ivSessionKey: string
43
- encryptedData: Buffer
44
- }
45
- export function encryptSource(source: Buffer, key: string): Encoded {
46
- // console.log('decryptKeyType - ', decryptKeyType);
47
- // console.log(key);
48
-
49
- // encrypt zip with key
50
- const initVector = randomBytes(16)
51
- const sessionKey = randomBytes(16)
52
- // encrypt session key with public key
53
- // console.log('\nencrypted.key', encrypted.key.toString(CryptoJS.enc.Base64))
54
- const cipher = createCipheriv(algorithm, sessionKey, initVector)
55
- cipher.setAutoPadding(true)
56
- // console.log('\nsessionKey', sessionKey.toString())
57
- // const sessionB64 = sessionKey.toString(formatB64)
58
- // console.log('\nsessionB64', sessionB64)
59
- const ivB64 = initVector.toString(formatB64)
60
- // console.log('\nivB64', ivB64)
61
-
62
- const sessionb64Encrypted = privateEncrypt(
63
- {
64
- key,
65
- padding,
66
- },
67
- sessionKey,
68
- ).toString(formatB64)
69
-
70
- // console.log('\nsessionb64Encrypted', sessionb64Encrypted)
71
- const ivSessionKey = `${ivB64}:${sessionb64Encrypted}`
72
- // console.log('\nivSessionKey', sessionKey)
73
- // encrypted to buffer
74
-
75
- const encryptedData = Buffer.concat([cipher.update(source), cipher.final()])
76
-
77
- return {
78
- encryptedData,
79
- ivSessionKey,
80
- }
81
- }
82
- export interface RSAKeys {
83
- publicKey: string
84
- privateKey: string
85
- }
86
- export function createRSA(): RSAKeys {
87
- const { publicKey, privateKey } = generateKeyPairSync('rsa', {
88
- // The standard secure default length for RSA keys is 2048 bits
89
- modulusLength: 2048,
90
- })
91
-
92
- // Generate RSA key pair
93
- return {
94
- publicKey: publicKey.export({
95
- type: 'pkcs1',
96
- format: 'pem',
97
- }) as string,
98
- privateKey: privateKey.export({
99
- type: 'pkcs1',
100
- format: 'pem',
101
- }) as string,
102
- }
103
- }
104
- // test AES
105
-
106
- // const source = 'Hello world'
107
- // console.log('\nsource', source)
108
- // const { publicKey, privateKey } = createRSA()
109
-
110
- // console.log('\nencryptSource ================================================================')
111
- // // convert source to base64
112
- // const sourceBuff = Buffer.from(source)
113
- // const res = encryptSource(sourceBuff, publicKey)
114
- // console.log('\nencryptedData', res.encryptedData.toString('base64'))
115
- // // console.log('\nres', res)
116
- // console.log('\ndecryptSource ================================================================')
117
- // const decodedSource = decryptSource(res.encryptedData, res.ivSessionKey, privateKey)
118
- // // convert decodedSource from base64 to utf-8
119
- // const decodedSourceString = decodedSource.toString('utf-8')
120
- // console.log('\ndecodedSourceString', decodedSourceString)
121
- // console.log('\n Is match', decodedSourceString === source)
@@ -1,41 +0,0 @@
1
- import process from 'node:process'
2
- import type { SupabaseClient } from '@supabase/supabase-js'
3
- import { program } from 'commander'
4
- import * as p from '@clack/prompts'
5
- import type { Database } from '../types/supabase.types'
6
- import { formatError } from '../utils'
7
-
8
- export async function checkVersionNotUsedInDeviceOverride(supabase: SupabaseClient<Database>, appid: string, versionData: Database['public']['Tables']['app_versions']['Row']) {
9
- const { data: deviceFound, error: errorDevice } = await supabase
10
- .from('devices_override')
11
- .select()
12
- .eq('app_id', appid)
13
- .eq('version', versionData.id)
14
- if (errorDevice) {
15
- p.log.error(`Cannot check Device override ${appid}@${versionData.name}`)
16
- program.error('')
17
- }
18
- if (deviceFound && deviceFound.length > 0) {
19
- p.intro(`❌ Version ${appid}@${versionData.name} is used in ${deviceFound.length} device override`)
20
- if (await p.confirm({ message: 'unlink it?' })) {
21
- // loop on all devices and set version to unknown
22
- for (const device of deviceFound) {
23
- const s = p.spinner()
24
- s.start(`Unlinking device ${device.device_id}`)
25
- const { error: errorDeviceDel } = await supabase
26
- .from('devices_override')
27
- .delete()
28
- .eq('device_id', device.device_id)
29
- if (errorDeviceDel) {
30
- s.stop(`Cannot unlink device ${device.device_id} ${formatError(errorDeviceDel)}`)
31
- process.exit(1)
32
- }
33
- s.stop(`✅ Device ${device.device_id} unlinked`)
34
- }
35
- }
36
- else {
37
- p.log.error(`Unlink it first`)
38
- program.error('')
39
- }
40
- }
41
- }
package/src/api/update.ts DELETED
@@ -1,12 +0,0 @@
1
- import getLatest from 'get-latest-version'
2
- import * as p from '@clack/prompts'
3
- import pack from '../../package.json'
4
-
5
- export async function checkLatest() {
6
- const latest = await getLatest('@capgo/cli')
7
- if (latest !== pack.version) {
8
- p.log.warning(`🚨 You are using @capgo/cli@${pack.version} it's not the latest version.
9
- Please use @capgo/cli@${latest}" or @capgo/cli@latest to keep up to date with the latest features and bug fixes.`,
10
- )
11
- }
12
- }
@@ -1,98 +0,0 @@
1
- import type { SupabaseClient } from '@supabase/supabase-js'
2
- import { program } from 'commander'
3
- import { Table } from 'console-table-printer'
4
- import * as p from '@clack/prompts'
5
- import type { Database } from '../types/supabase.types'
6
-
7
- // import { definitions } from '../types/types_supabase';
8
- import { getHumanDate } from '../utils'
9
- import { checkVersionNotUsedInChannel } from './channels'
10
- import { checkVersionNotUsedInDeviceOverride } from './devices_override'
11
-
12
- export async function deleteAppVersion(supabase: SupabaseClient<Database>, appid: string, userId: string, bundle: string) {
13
- const { error: delAppSpecVersionError } = await supabase
14
- .from('app_versions')
15
- .update({
16
- deleted: true,
17
- })
18
- .eq('app_id', appid)
19
- .eq('deleted', false)
20
- .eq('user_id', userId)
21
- .eq('name', bundle)
22
- if (delAppSpecVersionError) {
23
- p.log.error(`App Version ${appid}@${bundle} not found in database`)
24
- program.error('')
25
- }
26
- }
27
-
28
- export async function deleteSpecificVersion(supabase: SupabaseClient<Database>, appid: string, userId: string, bundle: string) {
29
- const versionData = await getVersionData(supabase, appid, userId, bundle)
30
- await checkVersionNotUsedInChannel(supabase, appid, userId, versionData)
31
- await checkVersionNotUsedInDeviceOverride(supabase, appid, versionData)
32
- // Delete only a specific version in storage
33
- await deleteAppVersion(supabase, appid, userId, bundle)
34
- }
35
-
36
- export function displayBundles(data: (Database['public']['Tables']['app_versions']['Row'] & { keep?: string })[]) {
37
- const t = new Table({
38
- title: 'Bundles',
39
- charLength: { '❌': 2, '✅': 2 },
40
- })
41
-
42
- // add rows with color
43
- data.reverse().forEach((row) => {
44
- t.addRow({
45
- Version: row.name,
46
- Created: getHumanDate(row.created_at),
47
- ...(row.keep != null ? { Keep: row.keep } : {}),
48
- })
49
- })
50
-
51
- p.log.success(t.render())
52
- }
53
-
54
- export async function getActiveAppVersions(supabase: SupabaseClient<Database>, appid: string, userId: string) {
55
- const { data, error: vError } = await supabase
56
- .from('app_versions')
57
- .select()
58
- .eq('app_id', appid)
59
- .eq('user_id', userId)
60
- .eq('deleted', false)
61
- .order('created_at', { ascending: false })
62
-
63
- if (vError) {
64
- p.log.error(`App ${appid} not found in database`)
65
- program.error('')
66
- }
67
- return data
68
- }
69
-
70
- export async function getChannelsVersion(supabase: SupabaseClient<Database>, appid: string) {
71
- // get all channels versionID
72
- const { data: channels, error: channelsError } = await supabase
73
- .from('channels')
74
- .select('version')
75
- .eq('app_id', appid)
76
-
77
- if (channelsError) {
78
- p.log.error(`App ${appid} not found in database`)
79
- program.error('')
80
- }
81
- return channels.map(c => c.version)
82
- }
83
-
84
- export async function getVersionData(supabase: SupabaseClient<Database>, appid: string, userId: string, bundle: string) {
85
- const { data: versionData, error: versionIdError } = await supabase
86
- .from('app_versions')
87
- .select()
88
- .eq('app_id', appid)
89
- .eq('user_id', userId)
90
- .eq('name', bundle)
91
- .eq('deleted', false)
92
- .single()
93
- if (!versionData || versionIdError) {
94
- p.log.error(`App Version ${appid}@${bundle} doesn't exist`)
95
- program.error('')
96
- }
97
- return versionData
98
- }