@metaplay/metaplay-auth 1.4.2 → 1.6.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.
package/src/utils.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { spawn } from 'child_process'
2
-
3
- export function stackApiBaseFromOptions (arg: string, options: Record<string, any>): string {
4
- return ''
5
- }
2
+ import { logger } from './logging.js'
3
+ import path from 'path'
4
+ import yaml from 'js-yaml'
5
+ import * as semver from 'semver'
6
6
 
7
7
  /**
8
8
  * Checks if the given string is a fully qualified domain name (FQDN).
@@ -10,7 +10,7 @@ export function stackApiBaseFromOptions (arg: string, options: Record<string, an
10
10
  * @param domain The domain name to check.
11
11
  * @returns true if the domain is a valid FQDN, false otherwise.
12
12
  */
13
- export function isValidFQDN (domain: string): boolean {
13
+ export function isValidFQDN(domain: string): boolean {
14
14
  const fqdnRegex = /^(?=.{1,253}$)(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,})$/
15
15
 
16
16
  return fqdnRegex.test(domain)
@@ -22,7 +22,12 @@ export function isValidFQDN (domain: string): boolean {
22
22
  * @param urlString The HTTP URL string to be split.
23
23
  * @returns An object containing the scheme, hostname, port, and subpaths.
24
24
  */
25
- export function splitUrlComponents (urlString: string): { scheme: string, hostname: string, port: string | null, subpaths: string | null } {
25
+ export function splitUrlComponents(urlString: string): {
26
+ scheme: string
27
+ hostname: string
28
+ port: string | null
29
+ subpaths: string | null
30
+ } {
26
31
  try {
27
32
  const url = new URL(urlString)
28
33
 
@@ -35,51 +40,177 @@ export function splitUrlComponents (urlString: string): { scheme: string, hostna
35
40
  const subpaths = url.pathname.slice(1) ?? null
36
41
 
37
42
  return { scheme, hostname, port, subpaths }
43
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
44
  } catch (error) {
39
45
  throw new Error('Invalid URL')
40
46
  }
41
47
  }
42
48
 
43
- export function getGameserverAdminUrl (uri: string): string {
44
- const parts = uri.split('.')
45
-
46
- const hostname = parts[0]
47
- const domain = uri.substring(hostname.length + 1)
48
-
49
- return `${hostname}-admin.${domain}`
50
- }
51
-
52
- interface CommandResult {
53
- code: number | null
49
+ export interface ExecuteCommandResult {
50
+ exitCode: number | null
54
51
  stdout: any[]
55
52
  stderr: any[]
56
53
  }
57
54
 
58
- export async function executeCommand (command: string, args: string[]): Promise<CommandResult> {
55
+ export interface ExecuteCommandOptions {
56
+ /**
57
+ * Should the stdin/stdout/stderr be inherited from the parent process? If false, the outputs are buffered for reading later.
58
+ */
59
+ inheritStdio?: boolean
60
+ /**
61
+ * Environment variables to pass in. Does not inherit parent process env. Use current process env if not specified.
62
+ */
63
+ env?: NodeJS.ProcessEnv
64
+ }
65
+
66
+ /**
67
+ * Run a child process and return an awaitable Promise.
68
+ *
69
+ * @param command Name of the command/binary to execute.
70
+ * @param args List of arguments to pass to the child process.
71
+ * @returns Awaitable promise with the result of the execution.
72
+ */
73
+ export async function executeCommand(
74
+ command: string,
75
+ args: string[],
76
+ options?: { inheritStdio?: boolean; env?: NodeJS.ProcessEnv }
77
+ ): Promise<ExecuteCommandResult> {
59
78
  return await new Promise((resolve, reject) => {
60
- const childProcess = spawn(command, args) //, { stdio: 'inherit' }) -- this pipes stdout & stderr to our output
61
- let stdout: any[] = []
62
- let stderr: any[] = []
79
+ const childProcess = spawn(command, args, {
80
+ stdio: options?.inheritStdio ? 'inherit' : undefined,
81
+ env: options?.env,
82
+ })
83
+ const stdout: string[] = []
84
+ const stderr: string[] = []
63
85
 
64
- childProcess.stdout?.on('data', (data) => {
65
- stdout += data
86
+ childProcess.stdout?.on('data', (data: string) => {
87
+ stdout.push(data)
66
88
  })
67
89
 
68
- childProcess.stderr?.on('data', (data) => {
69
- stderr += data
90
+ childProcess.stderr?.on('data', (data: string) => {
91
+ stderr.push(data)
70
92
  })
71
93
 
72
94
  // If program runs to completion, resolve promise with success
73
95
  childProcess.on('close', (code) => {
74
- resolve({ code, stdout, stderr })
96
+ resolve({ exitCode: code, stdout, stderr })
75
97
  })
76
98
 
77
99
  childProcess.on('error', (err) => {
78
- reject(
79
- new Error(
80
- `Failed to execute command '${command} ${args.join(' ')}': ${err.message}`
81
- )
82
- )
100
+ reject(new Error(`Failed to execute command '${command} ${args.join(' ')}': ${err.message}`))
83
101
  })
84
102
  })
85
103
  }
104
+
105
+ export function isRunningUnderNpx(): boolean {
106
+ // console.log('process.argv:', process.argv)
107
+ const matchBinary = (binaryPath: string, binaryName: string): boolean => {
108
+ const binary = path.basename(binaryPath, path.extname(binaryPath)) // drop path and extension
109
+ return binary === binaryName
110
+ }
111
+
112
+ // Check if 'npx' is the invoked binary (this check is not sufficient as it can also be 'node')
113
+ if (matchBinary(process.argv[0], 'npx')) {
114
+ return true
115
+ }
116
+
117
+ // Check if the script path contains a known npx temporary directory pattern
118
+ const scriptPath = process.argv[1]
119
+ if (scriptPath?.includes(`${path.sep}_npx${path.sep}`)) {
120
+ return true
121
+ }
122
+
123
+ // Check if the environment variable 'npm_execpath' is exactly 'npx'
124
+ if (process.env.npm_execpath && matchBinary(process.env.npm_execpath, 'npx')) {
125
+ return true
126
+ }
127
+
128
+ // Check if the environment variable '_' ends with 'npx' (specific to npx usage)
129
+ if (process.env._ && matchBinary(process.env._, 'npx')) {
130
+ return true
131
+ }
132
+
133
+ return false
134
+ }
135
+
136
+ /**
137
+ * Join a set of path segments and return result with Unix path separators ('/').
138
+ * @param paths Individual path segments to join
139
+ * @returns Joined path
140
+ */
141
+ export function pathJoin(...paths: string[]): string {
142
+ return path.join(...paths).replaceAll('\\', '/')
143
+ }
144
+
145
+ /**
146
+ * Remove a trailing slash from a URL.
147
+ * @param url URL to remove the trailing slash from.
148
+ * @returns URL without a trailing slash.
149
+ */
150
+ export function removeTrailingSlash(url: string): string {
151
+ return url.replace(/\/+$/, '')
152
+ }
153
+
154
+ /**
155
+ * Entry in the Helm chart repository index.
156
+ */
157
+ interface HelmChartEntry {
158
+ name: string
159
+ version: string
160
+ description?: string
161
+ apiVersion?: string
162
+ appVersion?: string
163
+ urls: string[]
164
+ }
165
+
166
+ /**
167
+ * The index of the Helm chart repository. Contains an entry for each of published chart version.
168
+ */
169
+ interface HelmChartRepoIndex {
170
+ apiVersion: string
171
+ entries: Record<string, HelmChartEntry[]>
172
+ generated: string
173
+ }
174
+
175
+ /**
176
+ * Fetch the available versions of a specific Helm chart from a repository.
177
+ * @param repository URL of the Helm chart repository. No trailing slash allowed.
178
+ * @param chartName Name of the Helm chart.
179
+ * @returns List of available Helm chart versions.
180
+ */
181
+ export async function fetchHelmChartVersions(repository: string, chartName: string): Promise<string[]> {
182
+ // Fetch the index.yaml file from the repository
183
+ logger.debug(`Fetching Helm chart versions from '${repository}'...`)
184
+ const response: Response = await fetch(`${repository}/index.yaml`)
185
+ const indexYaml: string = await response.text()
186
+
187
+ // Parse the YAML index file
188
+ const repoIndex: HelmChartRepoIndex = yaml.load(indexYaml) as HelmChartRepoIndex
189
+
190
+ // Grab all versions >= 0.5.0 -- older are considered legacy
191
+ const chartVersions = repoIndex.entries[chartName]
192
+ .map((entry) => entry.version)
193
+ .filter((version) => semver.gte(version, '0.5.0'))
194
+ return chartVersions
195
+ }
196
+
197
+ /**
198
+ * Resolve the best matching version from a list of versions that satisfy a semver range.
199
+ * @param versions List of versions to resolve from.
200
+ * @param range Semver range to satisfy.
201
+ * @returns The best (latest) matching version or null if no versions satisfy the range.
202
+ */
203
+ export function resolveBestMatchingVersion(versions: string[], range: semver.Range | null): string | null {
204
+ // Filter versions that satisfy the range -- range==null means 'latest' == include all versions
205
+ const satisfyingVersions = range ? versions.filter((version) => semver.satisfies(version, range)) : versions
206
+ logger.debug(`Satisfying versions: ${satisfyingVersions.join(', ')}`)
207
+
208
+ // If no versions satisfy the range, return null
209
+ if (satisfyingVersions.length === 0) {
210
+ return null
211
+ }
212
+
213
+ // Sort the versions to get the highest one that matches the range
214
+ const sortedVersions = satisfyingVersions.sort(semver.rcompare)
215
+ return sortedVersions[0]
216
+ }
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const PACKAGE_VERSION = "1.6.0"