shipthis 0.1.35 → 0.1.37
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/README.md +231 -80
- package/assets/markdown/agreement-update.md.ejs +12 -0
- package/assets/markdown/confirm-delete-android-keystore.md.ejs +22 -0
- package/assets/markdown/confirm-delete-android-serviceaccountkey.md.ejs +24 -0
- package/assets/markdown/confirm-delete-apple-credential.md.ejs +29 -0
- package/dist/{AppleBundleIdDetails-N_j1wv6f.js → AppleBundleIdDetails-KMzyex1H.js} +3 -5
- package/dist/{Command-DN1j3tjt.js → Command-CHh8RDXK.js} +1 -2
- package/dist/{CommandGame-D2NqytWc.js → CommandGame-Rvmsa7b0.js} +2 -2
- package/dist/{Create-BRdv5jXQ.js → Create-DInZ_pw-.js} +1 -4
- package/dist/{GameStatus-joyHPy0f.js → GameStatus-D7ID10tS.js} +1 -4
- package/dist/{Import-BNMHsJoM.js → Import-BmO8EYKj.js} +2 -5
- package/dist/{JobLogTail-D35FO5v-.js → JobLogTail-CYdceTKL.js} +2 -5
- package/dist/{JobProgress-D-9KESaA.js → JobProgress-DBCYbWrO.js} +16 -18
- package/dist/{JobStatusTable-DIJ_h-gi.js → JobStatusTable-DhnhY1_i.js} +2 -4
- package/dist/{ProjectCredentialsTable-BMKgv99h.js → ProjectCredentialsTable-BDYVYqAq.js} +5 -3
- package/dist/{UserCredentialsTable-CUrTDzTK.js → UserCredentialsTable-cDPvjF-F.js} +4 -4
- package/dist/{baseAppleCommand-Cs9a52e5.js → baseAppleCommand-CQ5dOna7.js} +1 -1
- package/dist/{baseGameAndroidCommand-BCB1SMCf.js → baseGameAndroidCommand-D4ryZS6A.js} +1 -2
- package/dist/commands/apiKey/create.js +3 -6
- package/dist/commands/apiKey/list.js +3 -6
- package/dist/commands/apiKey/revoke.js +3 -6
- package/dist/commands/apple/apiKey/create.js +7 -10
- package/dist/commands/apple/apiKey/delete.js +118 -0
- package/dist/commands/apple/apiKey/export.js +6 -9
- package/dist/commands/apple/apiKey/import.js +6 -9
- package/dist/commands/apple/apiKey/status.js +4 -7
- package/dist/commands/apple/certificate/create.js +7 -10
- package/dist/commands/apple/certificate/delete.js +127 -0
- package/dist/commands/apple/certificate/export.js +6 -9
- package/dist/commands/apple/certificate/import.js +6 -9
- package/dist/commands/apple/certificate/status.js +4 -7
- package/dist/commands/apple/login.js +23 -4
- package/dist/commands/apple/status.js +4 -7
- package/dist/commands/dashboard.js +12 -1
- package/dist/commands/game/android/apiKey/connect.js +8 -11
- package/dist/commands/game/android/apiKey/create.js +10 -13
- package/dist/commands/game/android/apiKey/delete.js +100 -0
- package/dist/commands/game/android/apiKey/export.js +6 -9
- package/dist/commands/game/android/apiKey/import.js +6 -9
- package/dist/commands/game/android/apiKey/invite.js +14 -4
- package/dist/commands/game/android/apiKey/policy.js +3 -6
- package/dist/commands/game/android/apiKey/status.js +6 -9
- package/dist/commands/game/android/keyStore/create.js +7 -10
- package/dist/commands/game/android/keyStore/delete.js +100 -0
- package/dist/commands/game/android/keyStore/export.js +5 -8
- package/dist/commands/game/android/keyStore/import.js +8 -11
- package/dist/commands/game/android/keyStore/status.js +7 -10
- package/dist/commands/game/android/status.js +13 -1
- package/dist/commands/game/build/download.js +3 -6
- package/dist/commands/game/build/list.js +4 -7
- package/dist/commands/game/create.js +13 -2
- package/dist/commands/game/details.js +4 -7
- package/dist/commands/game/export.js +13 -1
- package/dist/commands/game/ios/app/addTester.js +4 -7
- package/dist/commands/game/ios/app/create.js +3 -6
- package/dist/commands/game/ios/app/status.js +6 -9
- package/dist/commands/game/ios/app/sync.js +4 -7
- package/dist/commands/game/ios/profile/create.js +6 -9
- package/dist/commands/game/ios/profile/delete.js +123 -0
- package/dist/commands/game/ios/profile/export.js +5 -8
- package/dist/commands/game/ios/profile/import.js +5 -8
- package/dist/commands/game/ios/profile/status.js +8 -51
- package/dist/commands/game/ios/status.js +10 -13
- package/dist/commands/game/ios/wizard.js +2 -6
- package/dist/commands/game/job/list.js +3 -6
- package/dist/commands/game/job/status.js +8 -11
- package/dist/commands/game/list.js +3 -6
- package/dist/commands/game/ship.js +17 -14
- package/dist/commands/game/status.js +7 -10
- package/dist/commands/game/wizard.js +14 -17
- package/dist/commands/internal/fastlane.js +13 -1
- package/dist/commands/internal/readme.js +13 -4
- package/dist/commands/login.js +27 -4
- package/dist/commands/status.js +6 -8
- package/dist/{export-DFCZKNQk.js → export-DujIwhJw.js} +1 -1
- package/dist/{import-BpGyif-m.js → import-DGCqCAcC.js} +1 -1
- package/dist/{index-DlE_SPt3.js → index-9LxGafAo.js} +3 -5
- package/dist/{index-BTXEUd8W.js → index-BPh_qt7t.js} +4 -6
- package/dist/index-CNsmEDXi.js +48 -0
- package/dist/index-CmuXyPed.js +1905 -0
- package/dist/{upload-D19OQsbn.js → upload-D4x4yCia.js} +1 -1
- package/dist/{useAppleApp-BZc_cNa-.js → useAppleApp-taaewPSf.js} +1 -1
- package/dist/{useAppleBundleId-DvMXAvWD.js → useAppleBundleId-BeGViMe7.js} +1 -1
- package/dist/useAppleProfiles-1TtO0aO6.js +63 -0
- package/dist/{useGoogleStatus-Cx_QIsXa.js → useGoogleStatus-CSsxEvX7.js} +1 -2
- package/dist/{useProjectCredentials-DxdwJCfU.js → useProjectCredentials-Cm50WMZU.js} +1 -3
- package/dist/{useWebSocket-cM5yOcDv.js → useWebSocket-BVfn36be.js} +1 -1
- package/docs/apple/apiKey/delete.md +25 -0
- package/docs/apple/apiKey.md +26 -0
- package/docs/apple/certificate/delete.md +25 -0
- package/docs/apple/certificate.md +26 -0
- package/docs/assets/st.png +0 -0
- package/docs/game/android/apiKey/delete.md +25 -0
- package/docs/game/android/apiKey.md +26 -0
- package/docs/game/android/keyStore/delete.md +25 -0
- package/docs/game/android/keyStore.md +26 -0
- package/docs/game/ios/profile/delete.md +26 -0
- package/docs/game/ios/profile.md +27 -0
- package/docs/game/ship.md +4 -4
- package/docs/login.md +6 -2
- package/docs/status.md +1 -1
- package/package.json +13 -2
- package/dist/ejs-DirFZbza.js +0 -716
- package/dist/index-BwnzoldS.js +0 -784
- package/dist/index-CJWMt1s-.js +0 -153
- package/dist/index-hoHfGrjg.js +0 -221
- package/dist/index-izrACZbC.js +0 -24
- package/dist/useAndroidServiceAccountTestResult-CwKeW0ED.js +0 -50
|
@@ -0,0 +1,1905 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import fs__default from 'node:fs';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import CryptoJS from 'crypto-js';
|
|
5
|
+
import { v4 } from 'uuid';
|
|
6
|
+
import { DateTime } from 'luxon';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import * as expo from '@expo/apple-utils/build/index.js';
|
|
11
|
+
import crypto from 'node:crypto';
|
|
12
|
+
import { promises } from 'node:readline';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import readlineSync from 'readline-sync';
|
|
15
|
+
import 'isomorphic-git';
|
|
16
|
+
import merge from 'deepmerge';
|
|
17
|
+
import { parse } from 'ini';
|
|
18
|
+
import { QueryClient, useQuery } from '@tanstack/react-query';
|
|
19
|
+
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
20
|
+
import 'fast-glob';
|
|
21
|
+
import 'yazl';
|
|
22
|
+
import 'socket.io-client';
|
|
23
|
+
import 'fullscreen-ink';
|
|
24
|
+
import { useStdin, useInput, Text, Box } from 'ink';
|
|
25
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
26
|
+
import Spinner from 'ink-spinner';
|
|
27
|
+
import 'string-length';
|
|
28
|
+
import 'strip-ansi';
|
|
29
|
+
import open from 'open';
|
|
30
|
+
import '@inkjs/ui';
|
|
31
|
+
import require$$0 from 'fs';
|
|
32
|
+
import require$$1 from 'path';
|
|
33
|
+
import { setOptions, parse as parse$1 } from 'marked';
|
|
34
|
+
import TerminalRenderer from 'marked-terminal';
|
|
35
|
+
import 'qrcode';
|
|
36
|
+
|
|
37
|
+
const cacheKeys = {
|
|
38
|
+
androidKeyTestResult: (props) => ["androidKeyTestResult", ...Object.values(props)],
|
|
39
|
+
androidSetupStatus: (props) => ["androidSetupStatus", ...Object.values(props)],
|
|
40
|
+
builds: (props) => ["builds", ...Object.values(props)],
|
|
41
|
+
googleStatus: () => ["googleStatus"],
|
|
42
|
+
job: (props) => ["job", ...Object.values(props)],
|
|
43
|
+
jobLogs: (props) => ["jobLogs", ...Object.values(props)],
|
|
44
|
+
jobs: (props) => ["jobs", ...Object.values(props)],
|
|
45
|
+
projectCredentials: (props) => ["projectCredentials", ...Object.values(props)],
|
|
46
|
+
userCredentials: (props) => ["userCredentials", ...Object.values(props)]
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const AUTH_ENV_VAR_NAME = "SHIPTHIS_TOKEN";
|
|
50
|
+
const DOMAIN_ENV_VAR_NAME = "SHIPTHIS_DOMAIN";
|
|
51
|
+
const DEFAULT_SHIPPED_FILES_GLOBS = ["**/*"];
|
|
52
|
+
const DEFAULT_IGNORED_FILES_GLOBS = [
|
|
53
|
+
".git",
|
|
54
|
+
".gitignore",
|
|
55
|
+
"shipthis.json",
|
|
56
|
+
"shipthis-*.zip",
|
|
57
|
+
".godot/**",
|
|
58
|
+
".nomedia",
|
|
59
|
+
".import/**",
|
|
60
|
+
"export.cfg",
|
|
61
|
+
"export_credentials.cfg",
|
|
62
|
+
"*.translation",
|
|
63
|
+
".mono/**",
|
|
64
|
+
"data_*/**",
|
|
65
|
+
"mono_crash.*.json"
|
|
66
|
+
];
|
|
67
|
+
const PRIMARY_DOMAIN = "shipth.is";
|
|
68
|
+
function getUrlsForDomain(domain) {
|
|
69
|
+
const isPublic = domain.includes(PRIMARY_DOMAIN);
|
|
70
|
+
const apiDomain = (isPublic ? `api.` : "") + domain;
|
|
71
|
+
const wsDomain = (isPublic ? `ws.` : "") + domain;
|
|
72
|
+
return {
|
|
73
|
+
api: `https://${apiDomain}/api/1.0.0`,
|
|
74
|
+
web: `https://${domain}/`,
|
|
75
|
+
ws: `wss://${wsDomain}`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const DOMAIN = process.env[DOMAIN_ENV_VAR_NAME] || PRIMARY_DOMAIN;
|
|
79
|
+
const BACKEND_URLS = getUrlsForDomain(DOMAIN);
|
|
80
|
+
const API_URL = BACKEND_URLS.api;
|
|
81
|
+
const WS_URL = BACKEND_URLS.ws;
|
|
82
|
+
const WEB_URL = BACKEND_URLS.web;
|
|
83
|
+
|
|
84
|
+
const DEFAULT_LOCALE = "en-US";
|
|
85
|
+
function castObjectDates(apiObject, keys = ["createdAt", "updatedAt"]) {
|
|
86
|
+
if (!apiObject) return apiObject;
|
|
87
|
+
const datesOnly = Object.keys(apiObject).filter((k) => keys.includes(k)).reduce((a, c) => {
|
|
88
|
+
if (!apiObject[c]) return a;
|
|
89
|
+
a[c] = DateTime.fromISO(apiObject[c]);
|
|
90
|
+
return a;
|
|
91
|
+
}, {});
|
|
92
|
+
return {
|
|
93
|
+
...apiObject,
|
|
94
|
+
...datesOnly
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function castArrayObjectDates(apiArray, keys = ["createdAt", "updatedAt"]) {
|
|
98
|
+
return apiArray.map((apiObject) => castObjectDates(apiObject, keys));
|
|
99
|
+
}
|
|
100
|
+
function castJobDates(jobObject) {
|
|
101
|
+
if (jobObject.build) return castObjectDates({ ...jobObject, build: castObjectDates(jobObject.build) });
|
|
102
|
+
return castObjectDates(jobObject);
|
|
103
|
+
}
|
|
104
|
+
function getDateLocale() {
|
|
105
|
+
const fallback = Intl.DateTimeFormat().resolvedOptions().locale.replaceAll("_", "-") || DEFAULT_LOCALE;
|
|
106
|
+
try {
|
|
107
|
+
const { env } = process;
|
|
108
|
+
const fullLocale = env.LC_TIME || env.LANG || env.LANGUAGE || env.LC_ALL || env.LC_MESSAGES;
|
|
109
|
+
const shortLocale = fullLocale?.split(".")[0].replaceAll("_", "-");
|
|
110
|
+
const finalLocal = shortLocale || fallback;
|
|
111
|
+
const _ = DateTime.now().toLocaleString(DateTime.DATE_SHORT, { locale: finalLocal });
|
|
112
|
+
return finalLocal;
|
|
113
|
+
} catch {
|
|
114
|
+
return fallback;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function getShortDate(inputDate) {
|
|
118
|
+
const locale = getDateLocale();
|
|
119
|
+
return inputDate.toLocaleString(DateTime.DATE_SHORT, { locale });
|
|
120
|
+
}
|
|
121
|
+
function getShortDateTime(inputDate, extraFormatOpts = {}) {
|
|
122
|
+
const locale = getDateLocale();
|
|
123
|
+
const formatOpts = { ...DateTime.DATETIME_SHORT, ...extraFormatOpts };
|
|
124
|
+
return inputDate.toLocaleString(formatOpts, { locale });
|
|
125
|
+
}
|
|
126
|
+
function getShortTime(inputDate, extraFormatOpts = { fractionalSecondDigits: 3 }) {
|
|
127
|
+
const locale = getDateLocale();
|
|
128
|
+
const formatOpts = { ...DateTime.TIME_24_WITH_SECONDS, ...extraFormatOpts };
|
|
129
|
+
return inputDate.toLocaleString(formatOpts, { locale });
|
|
130
|
+
}
|
|
131
|
+
function getShortTimeDelta(start, end) {
|
|
132
|
+
return end.diff(start).rescale().set({ milliseconds: 0 }).shiftTo("minutes", "seconds").toHuman({
|
|
133
|
+
listStyle: "short",
|
|
134
|
+
unitDisplay: "short"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let currentAuthToken;
|
|
139
|
+
function setAuthToken(token) {
|
|
140
|
+
currentAuthToken = token;
|
|
141
|
+
}
|
|
142
|
+
function getAuthToken() {
|
|
143
|
+
return currentAuthToken;
|
|
144
|
+
}
|
|
145
|
+
function getAuthedHeaders() {
|
|
146
|
+
return {
|
|
147
|
+
Authorization: `Bearer ${currentAuthToken}`
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function createProject(props) {
|
|
151
|
+
const headers = getAuthedHeaders();
|
|
152
|
+
const opt = { headers };
|
|
153
|
+
const { data } = await axios.post(`${API_URL}/projects`, props, opt);
|
|
154
|
+
return castObjectDates(data);
|
|
155
|
+
}
|
|
156
|
+
async function getProject(projectId) {
|
|
157
|
+
const headers = getAuthedHeaders();
|
|
158
|
+
const opt = { headers };
|
|
159
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}`, opt);
|
|
160
|
+
return castObjectDates(data);
|
|
161
|
+
}
|
|
162
|
+
async function getProjects(params) {
|
|
163
|
+
const headers = getAuthedHeaders();
|
|
164
|
+
const opt = { headers, params };
|
|
165
|
+
const { data: rawData } = await axios.get(`${API_URL}/projects`, opt);
|
|
166
|
+
const data = castArrayObjectDates(rawData.data);
|
|
167
|
+
return {
|
|
168
|
+
data,
|
|
169
|
+
pageCount: rawData.pageCount
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function updateProject(projectId, edits) {
|
|
173
|
+
const headers = getAuthedHeaders();
|
|
174
|
+
const opt = { headers };
|
|
175
|
+
const { data } = await axios.put(`${API_URL}/projects/${projectId}`, edits, opt);
|
|
176
|
+
return castObjectDates(data);
|
|
177
|
+
}
|
|
178
|
+
async function getProjectPlatformProgress(projectId, platform) {
|
|
179
|
+
const headers = getAuthedHeaders();
|
|
180
|
+
const opt = { headers };
|
|
181
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/${platform}/progress`, opt);
|
|
182
|
+
return data;
|
|
183
|
+
}
|
|
184
|
+
async function getNewUploadTicket(projectId) {
|
|
185
|
+
const headers = getAuthedHeaders();
|
|
186
|
+
const opt = { headers };
|
|
187
|
+
const { data } = await axios.post(`${API_URL}/upload/${projectId}/url`, {}, opt);
|
|
188
|
+
return data;
|
|
189
|
+
}
|
|
190
|
+
async function startJobsFromUpload(uploadTicketId, startOptions) {
|
|
191
|
+
const headers = getAuthedHeaders();
|
|
192
|
+
const opt = { headers };
|
|
193
|
+
const { data } = await axios.post(`${API_URL}/upload/start/${uploadTicketId}`, startOptions, opt);
|
|
194
|
+
return castArrayObjectDates(data);
|
|
195
|
+
}
|
|
196
|
+
async function getProjectJobs(projectId, params) {
|
|
197
|
+
const headers = getAuthedHeaders();
|
|
198
|
+
const opt = { headers, params };
|
|
199
|
+
const { data: rawData } = await axios.get(`${API_URL}/projects/${projectId}/jobs`, opt);
|
|
200
|
+
const data = castArrayObjectDates(rawData.data);
|
|
201
|
+
return {
|
|
202
|
+
data,
|
|
203
|
+
pageCount: rawData.pageCount
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function getJob(jobId, projectId) {
|
|
207
|
+
const headers = getAuthedHeaders();
|
|
208
|
+
const opt = { headers };
|
|
209
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/jobs/${jobId}`, opt);
|
|
210
|
+
return castJobDates(data);
|
|
211
|
+
}
|
|
212
|
+
async function getSingleUseUrl(destination) {
|
|
213
|
+
const headers = await getAuthedHeaders();
|
|
214
|
+
const { data } = await axios.post(`${API_URL}/me/otp`, {}, { headers });
|
|
215
|
+
const queryString = Object.entries({ ...data, destination }).map(([key, value]) => `${key}=${encodeURIComponent(`${value}`)}`).join("&");
|
|
216
|
+
const url = `${WEB_URL}exchange/?${queryString}`;
|
|
217
|
+
return url;
|
|
218
|
+
}
|
|
219
|
+
async function getShortAuthRequiredUrl(destination) {
|
|
220
|
+
const { email } = await getSelf();
|
|
221
|
+
const key = v4();
|
|
222
|
+
const salt = "Na (s) + 1/2 Cl\u2082 (g) \u2192 NaCl (s)";
|
|
223
|
+
const fullKey = `${key}${salt}`;
|
|
224
|
+
const token = CryptoJS.AES.encrypt(email, fullKey).toString();
|
|
225
|
+
const params = {
|
|
226
|
+
destination,
|
|
227
|
+
key,
|
|
228
|
+
token
|
|
229
|
+
};
|
|
230
|
+
const queryString = Object.entries(params).map(([key2, value]) => `${key2}=${encodeURIComponent(`${value}`)}`).join("&");
|
|
231
|
+
const url = `${WEB_URL}login/?${queryString}`;
|
|
232
|
+
const headers = await getAuthedHeaders();
|
|
233
|
+
const { data } = await axios.post(
|
|
234
|
+
`${API_URL}/me/shorten`,
|
|
235
|
+
{
|
|
236
|
+
url
|
|
237
|
+
},
|
|
238
|
+
{ headers }
|
|
239
|
+
);
|
|
240
|
+
return data.url;
|
|
241
|
+
}
|
|
242
|
+
async function getBuild(projectId, buildId) {
|
|
243
|
+
const headers = getAuthedHeaders();
|
|
244
|
+
const opt = { headers };
|
|
245
|
+
const { data } = await axios.get(`${API_URL}/projects/${projectId}/builds/${buildId}`, opt);
|
|
246
|
+
return castObjectDates(data);
|
|
247
|
+
}
|
|
248
|
+
async function getSelf() {
|
|
249
|
+
const headers = getAuthedHeaders();
|
|
250
|
+
const opt = { headers };
|
|
251
|
+
const { data } = await axios.get(`${API_URL}/me`, opt);
|
|
252
|
+
return castObjectDates(data);
|
|
253
|
+
}
|
|
254
|
+
async function getTerms() {
|
|
255
|
+
const headers = getAuthedHeaders();
|
|
256
|
+
const opt = { headers };
|
|
257
|
+
const { data } = await axios.get(`${API_URL}/me/terms`, opt);
|
|
258
|
+
return {
|
|
259
|
+
// Any agreements which have changed since the user last accepted terms
|
|
260
|
+
changes: castArrayObjectDates(data.changes),
|
|
261
|
+
// Current versions of any agreements
|
|
262
|
+
current: castArrayObjectDates(data.current)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
async function acceptTerms() {
|
|
266
|
+
const headers = getAuthedHeaders();
|
|
267
|
+
const opt = { headers };
|
|
268
|
+
const { data } = await axios.post(`${API_URL}/me/terms`, {}, opt);
|
|
269
|
+
return castObjectDates(data);
|
|
270
|
+
}
|
|
271
|
+
async function getGoogleAuthUrl(projectId) {
|
|
272
|
+
const headers = getAuthedHeaders();
|
|
273
|
+
const opt = { headers };
|
|
274
|
+
const web = encodeURIComponent(new URL("/google/redirect/", WEB_URL).href);
|
|
275
|
+
const url = `${API_URL}/projects/${projectId}/credentials/android/key/connect`;
|
|
276
|
+
const { data } = await axios.get(`${url}?redirectUri=${web}`, opt);
|
|
277
|
+
const response = data;
|
|
278
|
+
return await getShortAuthRequiredUrl(response.url);
|
|
279
|
+
}
|
|
280
|
+
async function disconnectGoogle() {
|
|
281
|
+
const headers = getAuthedHeaders();
|
|
282
|
+
const opt = { headers };
|
|
283
|
+
await axios.delete(`${API_URL}/me/google/connect`, opt);
|
|
284
|
+
}
|
|
285
|
+
async function getGoogleStatus() {
|
|
286
|
+
const headers = getAuthedHeaders();
|
|
287
|
+
const opt = { headers };
|
|
288
|
+
const { data } = await axios.get(`${API_URL}/me/google/status`, opt);
|
|
289
|
+
return castObjectDates(data, ["orgCreatedAt"]);
|
|
290
|
+
}
|
|
291
|
+
async function enforcePolicy() {
|
|
292
|
+
const headers = getAuthedHeaders();
|
|
293
|
+
const opt = { headers };
|
|
294
|
+
const { data } = await axios.post(`${API_URL}/me/google/policy`, null, opt);
|
|
295
|
+
return castObjectDates(data, ["orgCreatedAt"]);
|
|
296
|
+
}
|
|
297
|
+
async function revokePolicy() {
|
|
298
|
+
const headers = getAuthedHeaders();
|
|
299
|
+
const opt = { headers };
|
|
300
|
+
const { data } = await axios.delete(`${API_URL}/me/google/policy`, opt);
|
|
301
|
+
return castObjectDates(data, ["orgCreatedAt"]);
|
|
302
|
+
}
|
|
303
|
+
async function inviteServiceAccount(projectId, developerId) {
|
|
304
|
+
try {
|
|
305
|
+
const headers = getAuthedHeaders();
|
|
306
|
+
const { data } = await axios.post(
|
|
307
|
+
`${API_URL}/projects/${projectId}/credentials/android/key/invite/`,
|
|
308
|
+
{ developerId },
|
|
309
|
+
{
|
|
310
|
+
headers
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
return data;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error("inviteServiceAccount Error", error);
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function downloadBuildById(projectId, buildId, fileName) {
|
|
320
|
+
const build = await getBuild(projectId, buildId);
|
|
321
|
+
const { url } = build;
|
|
322
|
+
const writer = fs.createWriteStream(fileName);
|
|
323
|
+
const response = await axios({
|
|
324
|
+
method: "GET",
|
|
325
|
+
responseType: "stream",
|
|
326
|
+
url
|
|
327
|
+
});
|
|
328
|
+
response.data.pipe(writer);
|
|
329
|
+
return new Promise((resolve, reject) => {
|
|
330
|
+
writer.on("finish", resolve);
|
|
331
|
+
writer.on("error", reject);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const APIKEYS_DATE_FIELDS = ["createdAt", "updatedAt", "expiresAt", "lastUsedAt", "revokedAt"];
|
|
335
|
+
async function getAPIKeys(params) {
|
|
336
|
+
const headers = getAuthedHeaders();
|
|
337
|
+
const opt = { headers, params };
|
|
338
|
+
const { data: rawData } = await axios.get(`${API_URL}/me/keys`, opt);
|
|
339
|
+
const data = castArrayObjectDates(rawData.data, APIKEYS_DATE_FIELDS);
|
|
340
|
+
return {
|
|
341
|
+
data,
|
|
342
|
+
pageCount: rawData.pageCount
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function createAPIKey(createProps) {
|
|
346
|
+
const headers = getAuthedHeaders();
|
|
347
|
+
const opt = { headers };
|
|
348
|
+
const { data } = await axios.post(`${API_URL}/me/keys`, createProps, opt);
|
|
349
|
+
return castObjectDates(data, APIKEYS_DATE_FIELDS);
|
|
350
|
+
}
|
|
351
|
+
async function revokeAPIKey(apiKeyId) {
|
|
352
|
+
const headers = getAuthedHeaders();
|
|
353
|
+
const opt = { headers };
|
|
354
|
+
await axios.delete(`${API_URL}/me/keys/${apiKeyId}`, opt);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const defaultExport = expo.default;
|
|
358
|
+
const {
|
|
359
|
+
ApiKey,
|
|
360
|
+
ApiKeyType,
|
|
361
|
+
App,
|
|
362
|
+
Auth,
|
|
363
|
+
BetaGroup,
|
|
364
|
+
BundleId,
|
|
365
|
+
CapabilityType,
|
|
366
|
+
CapabilityTypeOption,
|
|
367
|
+
Certificate,
|
|
368
|
+
CertificateType,
|
|
369
|
+
Profile,
|
|
370
|
+
ProfileType,
|
|
371
|
+
Session,
|
|
372
|
+
UserRole
|
|
373
|
+
} = defaultExport;
|
|
374
|
+
|
|
375
|
+
var Platform = /* @__PURE__ */ ((Platform2) => {
|
|
376
|
+
Platform2["ANDROID"] = "ANDROID";
|
|
377
|
+
Platform2["IOS"] = "IOS";
|
|
378
|
+
return Platform2;
|
|
379
|
+
})(Platform || {});
|
|
380
|
+
var GameEngine = /* @__PURE__ */ ((GameEngine2) => {
|
|
381
|
+
GameEngine2["GODOT"] = "godot";
|
|
382
|
+
return GameEngine2;
|
|
383
|
+
})(GameEngine || {});
|
|
384
|
+
var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
|
|
385
|
+
JobStatus2["COMPLETED"] = "COMPLETED";
|
|
386
|
+
JobStatus2["FAILED"] = "FAILED";
|
|
387
|
+
JobStatus2["PENDING"] = "PENDING";
|
|
388
|
+
JobStatus2["PROCESSING"] = "PROCESSING";
|
|
389
|
+
return JobStatus2;
|
|
390
|
+
})(JobStatus || {});
|
|
391
|
+
var JobStage = /* @__PURE__ */ ((JobStage2) => {
|
|
392
|
+
JobStage2["BUILD"] = "BUILD";
|
|
393
|
+
JobStage2["CONFIGURE"] = "CONFIGURE";
|
|
394
|
+
JobStage2["EXPORT"] = "EXPORT";
|
|
395
|
+
JobStage2["PUBLISH"] = "PUBLISH";
|
|
396
|
+
JobStage2["SETUP"] = "SETUP";
|
|
397
|
+
return JobStage2;
|
|
398
|
+
})(JobStage || {});
|
|
399
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
400
|
+
LogLevel2["ERROR"] = "ERROR";
|
|
401
|
+
LogLevel2["INFO"] = "INFO";
|
|
402
|
+
LogLevel2["WARN"] = "WARN";
|
|
403
|
+
return LogLevel2;
|
|
404
|
+
})(LogLevel || {});
|
|
405
|
+
var CredentialsType = /* @__PURE__ */ ((CredentialsType2) => {
|
|
406
|
+
CredentialsType2["CERTIFICATE"] = "CERTIFICATE";
|
|
407
|
+
CredentialsType2["KEY"] = "KEY";
|
|
408
|
+
return CredentialsType2;
|
|
409
|
+
})(CredentialsType || {});
|
|
410
|
+
var BuildType = /* @__PURE__ */ ((BuildType2) => {
|
|
411
|
+
BuildType2["AAB"] = "AAB";
|
|
412
|
+
BuildType2["APK"] = "APK";
|
|
413
|
+
BuildType2["IPA"] = "IPA";
|
|
414
|
+
return BuildType2;
|
|
415
|
+
})(BuildType || {});
|
|
416
|
+
|
|
417
|
+
function isCWDGodotGame() {
|
|
418
|
+
const cwd = process.cwd();
|
|
419
|
+
const godotProject = path.join(cwd, "project.godot");
|
|
420
|
+
return fs__default.existsSync(godotProject);
|
|
421
|
+
}
|
|
422
|
+
const GODOT_CAPABILITIES = [
|
|
423
|
+
// TODO: how about capabilities from godot extensions
|
|
424
|
+
{ key: "capabilities/access_wifi", name: "Access WiFi", type: CapabilityType.ACCESS_WIFI },
|
|
425
|
+
{ key: "capabilities/push_notifications", name: "Push Notifications", type: CapabilityType.PUSH_NOTIFICATIONS }
|
|
426
|
+
];
|
|
427
|
+
function getGodotProjectCapabilities(platform) {
|
|
428
|
+
const exportPresets = getGodotExportPresets(platform);
|
|
429
|
+
const { options } = exportPresets;
|
|
430
|
+
const capabilities = [];
|
|
431
|
+
for (const capability of GODOT_CAPABILITIES) {
|
|
432
|
+
if (!(capability.key in options)) continue;
|
|
433
|
+
if (`${options[capability.key]}`.toLocaleLowerCase() === "true") capabilities.push(capability.type);
|
|
434
|
+
}
|
|
435
|
+
return capabilities;
|
|
436
|
+
}
|
|
437
|
+
function getGodotProjectConfig() {
|
|
438
|
+
const cwd = process.cwd();
|
|
439
|
+
const projectGodotPath = path.join(cwd, "project.godot");
|
|
440
|
+
const projectGodotContent = fs__default.readFileSync(projectGodotPath, "utf8");
|
|
441
|
+
return parse(projectGodotContent);
|
|
442
|
+
}
|
|
443
|
+
function getGodotProjectName() {
|
|
444
|
+
try {
|
|
445
|
+
const projectGodotConfig = getGodotProjectConfig();
|
|
446
|
+
return projectGodotConfig.application["config/name"];
|
|
447
|
+
} catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function getGodotAppleBundleIdentifier() {
|
|
452
|
+
try {
|
|
453
|
+
const preset = getGodotExportPresets(Platform.IOS);
|
|
454
|
+
return preset.options["application/bundle_identifier"];
|
|
455
|
+
} catch (error) {
|
|
456
|
+
console.log(error);
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
function getGodotAndroidPackageName() {
|
|
461
|
+
try {
|
|
462
|
+
const preset = getGodotExportPresets(Platform.ANDROID);
|
|
463
|
+
return preset.options["package/unique_name"];
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.log(error);
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function getGodotVersion() {
|
|
470
|
+
const projectGodotConfig = getGodotProjectConfig();
|
|
471
|
+
if ("config/features" in projectGodotConfig.application) {
|
|
472
|
+
const features = projectGodotConfig.application["config/features"];
|
|
473
|
+
const match = features.match(/"(\d+\.\d+)"/);
|
|
474
|
+
if (!match) throw new Error("Couldn't find Godot version in project.godot");
|
|
475
|
+
return match[1];
|
|
476
|
+
}
|
|
477
|
+
return "3.6";
|
|
478
|
+
}
|
|
479
|
+
function getGodotExportPresets(platform) {
|
|
480
|
+
const { warn } = console;
|
|
481
|
+
let presetConfig = platform === Platform.IOS ? getBaseExportPresets_iOS() : getBaseExportPresets_Android();
|
|
482
|
+
const cwd = process.cwd();
|
|
483
|
+
const filename = "export_presets.cfg";
|
|
484
|
+
const exportPresetsPath = path.join(cwd, filename);
|
|
485
|
+
const isFound = fs__default.existsSync(exportPresetsPath);
|
|
486
|
+
if (isFound) {
|
|
487
|
+
const exportPresetsContent = fs__default.readFileSync(exportPresetsPath, "utf8");
|
|
488
|
+
const exportPresetsIni = parse(exportPresetsContent);
|
|
489
|
+
const presetIndexes = Object.keys(exportPresetsIni.preset || {});
|
|
490
|
+
const presetIndex = presetIndexes.find((index) => {
|
|
491
|
+
const current = exportPresetsIni.preset[index];
|
|
492
|
+
return `${current.name}`.toUpperCase() === platform;
|
|
493
|
+
});
|
|
494
|
+
if (presetIndex) {
|
|
495
|
+
presetConfig = merge(presetConfig, exportPresetsIni.preset[presetIndex]);
|
|
496
|
+
} else {
|
|
497
|
+
warn(`Preset ${platform} not found in ${filename} - will use defaults`);
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
warn(`${filename} not found at ${exportPresetsPath}`);
|
|
501
|
+
}
|
|
502
|
+
return presetConfig;
|
|
503
|
+
}
|
|
504
|
+
function getBaseExportPresets_iOS() {
|
|
505
|
+
return {
|
|
506
|
+
custom_features: "",
|
|
507
|
+
dedicated_server: false,
|
|
508
|
+
encrypt_directory: false,
|
|
509
|
+
encrypt_pck: false,
|
|
510
|
+
encryption_exclude_filters: "",
|
|
511
|
+
encryption_include_filters: "",
|
|
512
|
+
exclude_filter: "",
|
|
513
|
+
export_filter: "all_resources",
|
|
514
|
+
export_path: "output",
|
|
515
|
+
include_filter: "",
|
|
516
|
+
name: "iOS",
|
|
517
|
+
options: {
|
|
518
|
+
"application/export_project_only": true,
|
|
519
|
+
"application/icon_interpolation": "4",
|
|
520
|
+
"application/launch_screens_interpolation": "4",
|
|
521
|
+
"application/short_version": "1.0.0",
|
|
522
|
+
// default version number
|
|
523
|
+
"application/signature": "",
|
|
524
|
+
"architectures/arm64": true,
|
|
525
|
+
"capabilities/access_wifi": false,
|
|
526
|
+
"capabilities/push_notifications": false,
|
|
527
|
+
"custom_template/debug": "",
|
|
528
|
+
"custom_template/release": "",
|
|
529
|
+
"icons/app_store_1024x1024": "",
|
|
530
|
+
"icons/ipad_76x76": "",
|
|
531
|
+
"icons/ipad_152x152": "",
|
|
532
|
+
"icons/ipad_167x167": "",
|
|
533
|
+
"icons/iphone_120x120": "",
|
|
534
|
+
"icons/iphone_180x180": "",
|
|
535
|
+
"icons/notification_40x40": "",
|
|
536
|
+
"icons/notification_60x60": "",
|
|
537
|
+
"icons/settings_58x58": "",
|
|
538
|
+
"icons/settings_87x87": "",
|
|
539
|
+
"icons/spotlight_40x40": "",
|
|
540
|
+
"icons/spotlight_80x80": "",
|
|
541
|
+
"landscape_launch_screens/ipad_1024x768": "",
|
|
542
|
+
"landscape_launch_screens/ipad_2048x1536": "",
|
|
543
|
+
"landscape_launch_screens/iphone_2208x1242": "",
|
|
544
|
+
"landscape_launch_screens/iphone_2436x1125": "",
|
|
545
|
+
"portrait_launch_screens/ipad_768x1024": "",
|
|
546
|
+
"portrait_launch_screens/ipad_1536x2048": "",
|
|
547
|
+
"portrait_launch_screens/iphone_640x960": "",
|
|
548
|
+
"portrait_launch_screens/iphone_640x1136": "",
|
|
549
|
+
"portrait_launch_screens/iphone_750x1334": "",
|
|
550
|
+
"portrait_launch_screens/iphone_1125x2436": "",
|
|
551
|
+
"portrait_launch_screens/iphone_1242x2208": "",
|
|
552
|
+
"privacy/camera_usage_description": "",
|
|
553
|
+
"privacy/camera_usage_description_localized": "{}",
|
|
554
|
+
"privacy/microphone_usage_description": "",
|
|
555
|
+
"privacy/microphone_usage_description_localized": "{}",
|
|
556
|
+
"privacy/photolibrary_usage_description": "",
|
|
557
|
+
"privacy/photolibrary_usage_description_localized": "{}",
|
|
558
|
+
"storyboard/custom_bg_color": "Color(0, 0, 0, 1)",
|
|
559
|
+
"storyboard/custom_image@2x": "",
|
|
560
|
+
"storyboard/custom_image@3x": "",
|
|
561
|
+
"storyboard/image_scale_mode": "0",
|
|
562
|
+
"storyboard/use_custom_bg_color": false,
|
|
563
|
+
"storyboard/use_launch_screen_storyboard": true,
|
|
564
|
+
"user_data/accessible_from_files_app": false,
|
|
565
|
+
"user_data/accessible_from_itunes_sharing": false
|
|
566
|
+
},
|
|
567
|
+
platform: "iOS",
|
|
568
|
+
runnable: true
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function getBaseExportPresets_Android() {
|
|
572
|
+
return {
|
|
573
|
+
name: "Android",
|
|
574
|
+
// TODO
|
|
575
|
+
options: {
|
|
576
|
+
// TODO
|
|
577
|
+
},
|
|
578
|
+
platform: "Android"
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const queryClient = new QueryClient({
|
|
583
|
+
defaultOptions: {
|
|
584
|
+
queries: {
|
|
585
|
+
staleTime: 50
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
var KeyTestStatus = /* @__PURE__ */ ((KeyTestStatus2) => {
|
|
591
|
+
KeyTestStatus2["ERROR"] = "error";
|
|
592
|
+
KeyTestStatus2["SUCCESS"] = "success";
|
|
593
|
+
return KeyTestStatus2;
|
|
594
|
+
})(KeyTestStatus || {});
|
|
595
|
+
var KeyTestError = /* @__PURE__ */ ((KeyTestError2) => {
|
|
596
|
+
KeyTestError2["APP_NOT_FOUND"] = "app_not_found";
|
|
597
|
+
KeyTestError2["NO_PACKAGE_NAME"] = "no_package_name";
|
|
598
|
+
KeyTestError2["NO_SERVICE_ACCOUNT_KEY"] = "no_service_account_key";
|
|
599
|
+
KeyTestError2["NOT_INVITED"] = "not_invited";
|
|
600
|
+
return KeyTestError2;
|
|
601
|
+
})(KeyTestError || {});
|
|
602
|
+
const KeyTestErrorMessage = {
|
|
603
|
+
["app_not_found" /* APP_NOT_FOUND */]: "Application not found in Google Play Console",
|
|
604
|
+
["no_package_name" /* NO_PACKAGE_NAME */]: "Android Package Name has not been set",
|
|
605
|
+
["no_service_account_key" /* NO_SERVICE_ACCOUNT_KEY */]: "Service Account API Key not found in your account",
|
|
606
|
+
["not_invited" /* NOT_INVITED */]: "Service Account has not been invited to Google Play"
|
|
607
|
+
};
|
|
608
|
+
function niceError(keyError) {
|
|
609
|
+
return keyError ? KeyTestErrorMessage[keyError] : void 0;
|
|
610
|
+
}
|
|
611
|
+
const fetchKeyTestResult = async ({ projectId }, config) => {
|
|
612
|
+
if (!projectId) throw new Error("projectId is required");
|
|
613
|
+
const url = `${API_URL}/projects/${projectId}/credentials/android/key/test`;
|
|
614
|
+
const headers = getAuthedHeaders();
|
|
615
|
+
const { data } = await axios.post(url, {}, { headers, ...config });
|
|
616
|
+
return data;
|
|
617
|
+
};
|
|
618
|
+
const useAndroidServiceAccountTestResult = (props) => useQuery({
|
|
619
|
+
queryFn: () => fetchKeyTestResult(props),
|
|
620
|
+
queryKey: cacheKeys.androidKeyTestResult(props)
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
async function queryBuilds({ projectId, ...pageAndSortParams }) {
|
|
624
|
+
try {
|
|
625
|
+
const headers = getAuthedHeaders();
|
|
626
|
+
const url = `${API_URL}/projects/${projectId}/builds`;
|
|
627
|
+
const response = await axios.get(url, { headers, params: pageAndSortParams });
|
|
628
|
+
return {
|
|
629
|
+
...response.data,
|
|
630
|
+
data: castArrayObjectDates(response.data.data)
|
|
631
|
+
};
|
|
632
|
+
} catch (error) {
|
|
633
|
+
console.warn("queryBuilds Error", error);
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function getBuildSummary(build) {
|
|
638
|
+
const ext = build.buildType || (build.platform === Platform.IOS ? "IPA" : "AAB");
|
|
639
|
+
const filename = `game.${ext.toLowerCase()}`;
|
|
640
|
+
const details = getJobDetailsSummary(build.jobDetails);
|
|
641
|
+
const summary = {};
|
|
642
|
+
summary.id = getShortUUID(build.id);
|
|
643
|
+
summary.version = details.version;
|
|
644
|
+
summary.gitInfo = details.gitInfo;
|
|
645
|
+
summary.platform = getPlatformName(build.platform);
|
|
646
|
+
summary.jobId = getShortUUID(build.jobId);
|
|
647
|
+
summary.createdAt = getShortDateTime(build.createdAt);
|
|
648
|
+
summary.cmd = `shipthis game build download ${getShortUUID(build.id)} ${filename}`;
|
|
649
|
+
return summary;
|
|
650
|
+
}
|
|
651
|
+
const useBuilds = (props) => {
|
|
652
|
+
const queryResult = useQuery({
|
|
653
|
+
queryFn: async () => queryBuilds(props),
|
|
654
|
+
queryKey: cacheKeys.builds(props)
|
|
655
|
+
});
|
|
656
|
+
return queryResult;
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
function getJobDetailsSummary(jobDetails) {
|
|
660
|
+
const semanticVersion = jobDetails?.semanticVersion || "N/A";
|
|
661
|
+
const buildNumber = jobDetails?.buildNumber || "N/A";
|
|
662
|
+
const gitCommit = jobDetails?.gitCommitHash ? getShortUUID(jobDetails.gitCommitHash) : "";
|
|
663
|
+
const gitBranch = jobDetails?.gitBranch || "";
|
|
664
|
+
const details = {};
|
|
665
|
+
details.version = `${semanticVersion} (${buildNumber})`;
|
|
666
|
+
details.gitInfo = gitCommit ? `${gitCommit} (${gitBranch})` : "";
|
|
667
|
+
return details;
|
|
668
|
+
}
|
|
669
|
+
function getJobSummary(job, timeNow) {
|
|
670
|
+
const inProgress = ![JobStatus.COMPLETED, JobStatus.FAILED].includes(job.status);
|
|
671
|
+
const details = getJobDetailsSummary(job.details);
|
|
672
|
+
const summary = {};
|
|
673
|
+
summary.id = getShortUUID(job.id);
|
|
674
|
+
summary.version = details.version;
|
|
675
|
+
summary.gitInfo = details.gitInfo;
|
|
676
|
+
summary.platform = getPlatformName(job.type);
|
|
677
|
+
summary.status = job.status;
|
|
678
|
+
summary.createdAt = getShortDateTime(job.createdAt);
|
|
679
|
+
summary.runtime = getShortTimeDelta(job.createdAt, inProgress ? timeNow : job.updatedAt);
|
|
680
|
+
return summary;
|
|
681
|
+
}
|
|
682
|
+
const useJob = (props) => useQuery({
|
|
683
|
+
queryFn: () => getJob(props.jobId, props.projectId),
|
|
684
|
+
queryKey: cacheKeys.job(props)
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
const useSafeInput = (handler, options = { isActive: true }) => {
|
|
688
|
+
const { isRawModeSupported } = useStdin();
|
|
689
|
+
const isActive = isRawModeSupported === true && options.isActive !== false;
|
|
690
|
+
useInput(
|
|
691
|
+
(input, key) => {
|
|
692
|
+
const lowerInput = input.toLowerCase();
|
|
693
|
+
return handler(lowerInput, key);
|
|
694
|
+
},
|
|
695
|
+
{ isActive }
|
|
696
|
+
);
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
function getShortUUID(originalUuid) {
|
|
700
|
+
return originalUuid.slice(0, 8);
|
|
701
|
+
}
|
|
702
|
+
function getStageColor(stage) {
|
|
703
|
+
switch (stage) {
|
|
704
|
+
case JobStage.SETUP: {
|
|
705
|
+
return "#FFB3B3";
|
|
706
|
+
}
|
|
707
|
+
// pastel red
|
|
708
|
+
case JobStage.CONFIGURE: {
|
|
709
|
+
return "#FFD9B3";
|
|
710
|
+
}
|
|
711
|
+
// pastel orange
|
|
712
|
+
case JobStage.EXPORT: {
|
|
713
|
+
return "#FFFACD";
|
|
714
|
+
}
|
|
715
|
+
// pastel yellow
|
|
716
|
+
case JobStage.BUILD: {
|
|
717
|
+
return "#B3FFB3";
|
|
718
|
+
}
|
|
719
|
+
// pastel green
|
|
720
|
+
case JobStage.PUBLISH: {
|
|
721
|
+
return "#B3D9FF";
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function getMessageColor(level) {
|
|
726
|
+
switch (level) {
|
|
727
|
+
case LogLevel.INFO: {
|
|
728
|
+
return "white";
|
|
729
|
+
}
|
|
730
|
+
case LogLevel.WARN: {
|
|
731
|
+
return "yellow";
|
|
732
|
+
}
|
|
733
|
+
case LogLevel.ERROR: {
|
|
734
|
+
return "red";
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function getJobStatusColor(status) {
|
|
739
|
+
switch (status) {
|
|
740
|
+
case JobStatus.PENDING: {
|
|
741
|
+
return "yellow";
|
|
742
|
+
}
|
|
743
|
+
case JobStatus.PROCESSING: {
|
|
744
|
+
return "blue";
|
|
745
|
+
}
|
|
746
|
+
case JobStatus.COMPLETED: {
|
|
747
|
+
return "green";
|
|
748
|
+
}
|
|
749
|
+
case JobStatus.FAILED: {
|
|
750
|
+
return "red";
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async function getFileHash(filename) {
|
|
755
|
+
return new Promise((resolve, reject) => {
|
|
756
|
+
const hash = crypto.createHash("sha256");
|
|
757
|
+
const rs = fs__default.createReadStream(filename);
|
|
758
|
+
rs.on("error", reject);
|
|
759
|
+
rs.on("data", (chunk) => hash.update(chunk));
|
|
760
|
+
rs.on("end", () => resolve(hash.digest("hex")));
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
function isValidSemVer(versionString) {
|
|
764
|
+
const [semVer, major, minor, patch, prerelease, buildmetadata] = versionString.match(
|
|
765
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][\dA-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][\dA-Za-z-]*))*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$/
|
|
766
|
+
) ?? [];
|
|
767
|
+
return Boolean(semVer);
|
|
768
|
+
}
|
|
769
|
+
function makeHumanReadable(rawObject) {
|
|
770
|
+
const getLabel = (key) => {
|
|
771
|
+
const words = key.split(/(?=[A-Z])/);
|
|
772
|
+
return words.map((word) => word[0].toUpperCase() + word.slice(1)).join(" ").replaceAll(" ", " ");
|
|
773
|
+
};
|
|
774
|
+
const withLabels = Object.entries(rawObject).reduce((acc, [key, value]) => {
|
|
775
|
+
acc[getLabel(key)] = value;
|
|
776
|
+
return acc;
|
|
777
|
+
}, {});
|
|
778
|
+
return withLabels;
|
|
779
|
+
}
|
|
780
|
+
function getPlatformName(platform) {
|
|
781
|
+
switch (platform) {
|
|
782
|
+
case Platform.IOS: {
|
|
783
|
+
return "iOS";
|
|
784
|
+
}
|
|
785
|
+
case Platform.ANDROID: {
|
|
786
|
+
return "Android";
|
|
787
|
+
}
|
|
788
|
+
default: {
|
|
789
|
+
throw new Error(`Unknown platform: ${platform}`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
async function getMaskedInput(message) {
|
|
794
|
+
const password = readlineSync.question(message, {
|
|
795
|
+
hideEchoBack: true
|
|
796
|
+
// This will hide the input as the user types
|
|
797
|
+
});
|
|
798
|
+
return password;
|
|
799
|
+
}
|
|
800
|
+
async function getInput(message) {
|
|
801
|
+
const rl = promises.createInterface({
|
|
802
|
+
input: process.stdin,
|
|
803
|
+
output: process.stdout
|
|
804
|
+
});
|
|
805
|
+
const answer = await rl.question(message);
|
|
806
|
+
rl.close();
|
|
807
|
+
return answer;
|
|
808
|
+
}
|
|
809
|
+
function generatePackageName(gameName) {
|
|
810
|
+
let normalizedGameName = gameName.trim().toLowerCase();
|
|
811
|
+
normalizedGameName = normalizedGameName.replaceAll(/[\s_\-]+/g, ".");
|
|
812
|
+
normalizedGameName = normalizedGameName.replaceAll(/[^\d.a-z]/g, "");
|
|
813
|
+
normalizedGameName = normalizedGameName.replaceAll(/\.+/g, ".");
|
|
814
|
+
normalizedGameName = normalizedGameName.replace(/^\./, "").replace(/\.$/, "");
|
|
815
|
+
if (/^\d/.test(normalizedGameName)) {
|
|
816
|
+
normalizedGameName = "app." + normalizedGameName;
|
|
817
|
+
}
|
|
818
|
+
const prefix = "com.";
|
|
819
|
+
if (normalizedGameName === "") {
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
return prefix + normalizedGameName;
|
|
823
|
+
}
|
|
824
|
+
function scriptDir(importMeta) {
|
|
825
|
+
const filename = fileURLToPath(importMeta.url);
|
|
826
|
+
const dirname = path.dirname(filename);
|
|
827
|
+
return dirname;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
class BaseCommand extends Command {
|
|
831
|
+
// define flags that can be inherited by any command that extends BaseCommand
|
|
832
|
+
static baseFlags = {};
|
|
833
|
+
// add the --json flag
|
|
834
|
+
static enableJsonFlag = false;
|
|
835
|
+
args;
|
|
836
|
+
flags;
|
|
837
|
+
async catch(err) {
|
|
838
|
+
return super.catch(err);
|
|
839
|
+
}
|
|
840
|
+
// Used in baseGameCommand and the other commands that need to ensure that the CWD is a Godot project
|
|
841
|
+
ensureWeAreInAProjectDir() {
|
|
842
|
+
if (!isCWDGodotGame()) {
|
|
843
|
+
this.error("No Godot project detected. Please run this from a godot project directory.", { exit: 1 });
|
|
844
|
+
}
|
|
845
|
+
if (!this.hasProjectConfig()) {
|
|
846
|
+
this.error(
|
|
847
|
+
'No ShipThis config found. Please run `shipthis game create --name "Space Invaders"` to create a game.',
|
|
848
|
+
{ exit: 1 }
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
ensureWeHaveAppleCookies() {
|
|
853
|
+
if (!this.hasAuthConfigFile()) {
|
|
854
|
+
this.error("You must be authenticated with Apple in to use this command. Please run shipthis apple login", {
|
|
855
|
+
exit: 1
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
async finally(_) {
|
|
860
|
+
return super.finally(_);
|
|
861
|
+
}
|
|
862
|
+
// Used in the apple commands to get the cookies from the auth file
|
|
863
|
+
async getAppleCookies() {
|
|
864
|
+
const authConfig = await this.getAuthConfig();
|
|
865
|
+
if (!authConfig.appleCookies) return null;
|
|
866
|
+
return authConfig.appleCookies;
|
|
867
|
+
}
|
|
868
|
+
// Returns the current auth config - prefers to use the env var
|
|
869
|
+
async getAuthConfig() {
|
|
870
|
+
const envVarValue = process.env[AUTH_ENV_VAR_NAME];
|
|
871
|
+
if (!envVarValue) return await this.getAuthConfigFromFile();
|
|
872
|
+
setAuthToken(envVarValue);
|
|
873
|
+
const self = await getSelf();
|
|
874
|
+
const selfWithJwt = {
|
|
875
|
+
...self,
|
|
876
|
+
jwt: envVarValue
|
|
877
|
+
};
|
|
878
|
+
return {
|
|
879
|
+
shipThisUser: selfWithJwt
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
// Loads the auth config from the file system
|
|
883
|
+
async getAuthConfigFromFile() {
|
|
884
|
+
const baseConfig = {};
|
|
885
|
+
const configPath = this.getAuthConfigPath();
|
|
886
|
+
if (!fs__default.existsSync(configPath)) return baseConfig;
|
|
887
|
+
const raw = await fs__default.promises.readFile(configPath, "utf8");
|
|
888
|
+
const parsed = JSON.parse(raw);
|
|
889
|
+
if (parsed.shipThisUser) {
|
|
890
|
+
setAuthToken(parsed.shipThisUser.jwt);
|
|
891
|
+
}
|
|
892
|
+
return {
|
|
893
|
+
...baseConfig,
|
|
894
|
+
...parsed
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
// This is used to expose the flags to the Android Wizard
|
|
898
|
+
getDetailsFlagsValues() {
|
|
899
|
+
const keys = Object.keys(DetailsFlags);
|
|
900
|
+
const values = {};
|
|
901
|
+
for (const key of keys) {
|
|
902
|
+
if (this.flags[key]) values[key] = this.flags[key];
|
|
903
|
+
}
|
|
904
|
+
return values;
|
|
905
|
+
}
|
|
906
|
+
// Exposing it to the react components using the CommandContext
|
|
907
|
+
getFlags() {
|
|
908
|
+
return this.flags;
|
|
909
|
+
}
|
|
910
|
+
getGameId() {
|
|
911
|
+
const { flags } = this;
|
|
912
|
+
if (flags.gameId) return flags.gameId;
|
|
913
|
+
const { project } = this.getProjectConfigSafe();
|
|
914
|
+
if (!project) return null;
|
|
915
|
+
return project.id;
|
|
916
|
+
}
|
|
917
|
+
async getProjectConfig() {
|
|
918
|
+
if (!this.hasProjectConfig()) throw new Error("No project config found");
|
|
919
|
+
return this.getProjectConfigSafe();
|
|
920
|
+
}
|
|
921
|
+
getProjectConfigSafe() {
|
|
922
|
+
if (!this.hasProjectConfig()) return {};
|
|
923
|
+
const configPath = this.getProjectConfigPath();
|
|
924
|
+
const raw = fs__default.readFileSync(configPath, "utf8");
|
|
925
|
+
return JSON.parse(raw);
|
|
926
|
+
}
|
|
927
|
+
hasAuthConfigFile() {
|
|
928
|
+
const configPath = this.getAuthConfigPath();
|
|
929
|
+
return fs__default.existsSync(configPath);
|
|
930
|
+
}
|
|
931
|
+
hasProjectConfig() {
|
|
932
|
+
const configPath = this.getProjectConfigPath();
|
|
933
|
+
return fs__default.existsSync(configPath);
|
|
934
|
+
}
|
|
935
|
+
// Tests the apple cookies
|
|
936
|
+
async hasValidAppleAuthState() {
|
|
937
|
+
try {
|
|
938
|
+
await this.refreshAppleAuthState();
|
|
939
|
+
return true;
|
|
940
|
+
} catch {
|
|
941
|
+
return false;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
async init() {
|
|
945
|
+
process.on("SIGINT", () => process.exit(0));
|
|
946
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
947
|
+
await super.init();
|
|
948
|
+
const { args, flags } = await this.parse({
|
|
949
|
+
args: this.ctor.args,
|
|
950
|
+
baseFlags: super.ctor.baseFlags,
|
|
951
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
952
|
+
flags: this.ctor.flags,
|
|
953
|
+
strict: this.ctor.strict
|
|
954
|
+
});
|
|
955
|
+
this.flags = flags;
|
|
956
|
+
this.args = args;
|
|
957
|
+
await this.getAuthConfig();
|
|
958
|
+
}
|
|
959
|
+
async isAuthenticated() {
|
|
960
|
+
const authConfig = await this.getAuthConfig();
|
|
961
|
+
return Boolean(authConfig.shipThisUser?.jwt);
|
|
962
|
+
}
|
|
963
|
+
async refreshAppleAuthState() {
|
|
964
|
+
const cookies = await this.getAppleCookies();
|
|
965
|
+
const rerunMessage = `Please run ${chalk.bold("shipthis apple login")} to authenticate with Apple.`;
|
|
966
|
+
if (!cookies) throw new Error(`No Apple cookies found. ${rerunMessage}`);
|
|
967
|
+
const authState = await Auth.loginWithCookiesAsync(
|
|
968
|
+
{
|
|
969
|
+
cookies
|
|
970
|
+
},
|
|
971
|
+
{}
|
|
972
|
+
);
|
|
973
|
+
if (!authState) throw new Error(`Failed to refresh Apple auth state. ${rerunMessage}`);
|
|
974
|
+
return authState;
|
|
975
|
+
}
|
|
976
|
+
// Pass undefined to logout
|
|
977
|
+
async setAppleCookies(cookies) {
|
|
978
|
+
const authConfig = await this.getAuthConfig();
|
|
979
|
+
await this.setAuthConfig({ ...authConfig, appleCookies: cookies });
|
|
980
|
+
}
|
|
981
|
+
// This is called after login to persist the JWT and user details
|
|
982
|
+
async setAuthConfig(config) {
|
|
983
|
+
const configPath = this.getAuthConfigPath();
|
|
984
|
+
await fs__default.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
985
|
+
}
|
|
986
|
+
async setProjectConfig(config) {
|
|
987
|
+
const configPath = this.getProjectConfigPath();
|
|
988
|
+
await fs__default.promises.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
989
|
+
}
|
|
990
|
+
async updateProjectConfig(update) {
|
|
991
|
+
const config = await this.getProjectConfig();
|
|
992
|
+
await this.setProjectConfig({ ...config, ...update });
|
|
993
|
+
}
|
|
994
|
+
// Returns the values of the flags in DetailsFlags
|
|
995
|
+
getAuthConfigPath() {
|
|
996
|
+
return path.join(this.config.home, ".shipthis.auth.json");
|
|
997
|
+
}
|
|
998
|
+
getProjectConfigPath() {
|
|
999
|
+
return path.join(process.cwd(), "shipthis.json");
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function getDefaultExportFromCjs (x) {
|
|
1004
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
var ejs$1 = {};
|
|
1008
|
+
|
|
1009
|
+
var utils = {};
|
|
1010
|
+
|
|
1011
|
+
var hasRequiredUtils;
|
|
1012
|
+
|
|
1013
|
+
function requireUtils () {
|
|
1014
|
+
if (hasRequiredUtils) return utils;
|
|
1015
|
+
hasRequiredUtils = 1;
|
|
1016
|
+
(function (exports) {
|
|
1017
|
+
var regExpChars = /[|\\{}()[\]^$+*?.]/g;
|
|
1018
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
1019
|
+
var hasOwn = function(obj, key) {
|
|
1020
|
+
return hasOwnProperty.apply(obj, [key]);
|
|
1021
|
+
};
|
|
1022
|
+
exports.escapeRegExpChars = function(string) {
|
|
1023
|
+
if (!string) {
|
|
1024
|
+
return "";
|
|
1025
|
+
}
|
|
1026
|
+
return String(string).replace(regExpChars, "\\$&");
|
|
1027
|
+
};
|
|
1028
|
+
var _ENCODE_HTML_RULES = {
|
|
1029
|
+
"&": "&",
|
|
1030
|
+
"<": "<",
|
|
1031
|
+
">": ">",
|
|
1032
|
+
'"': """,
|
|
1033
|
+
"'": "'"
|
|
1034
|
+
};
|
|
1035
|
+
var _MATCH_HTML = /[&<>'"]/g;
|
|
1036
|
+
function encode_char(c) {
|
|
1037
|
+
return _ENCODE_HTML_RULES[c] || c;
|
|
1038
|
+
}
|
|
1039
|
+
var escapeFuncStr = `var _ENCODE_HTML_RULES = {
|
|
1040
|
+
"&": "&"
|
|
1041
|
+
, "<": "<"
|
|
1042
|
+
, ">": ">"
|
|
1043
|
+
, '"': """
|
|
1044
|
+
, "'": "'"
|
|
1045
|
+
}
|
|
1046
|
+
, _MATCH_HTML = /[&<>'"]/g;
|
|
1047
|
+
function encode_char(c) {
|
|
1048
|
+
return _ENCODE_HTML_RULES[c] || c;
|
|
1049
|
+
};
|
|
1050
|
+
`;
|
|
1051
|
+
exports.escapeXML = function(markup) {
|
|
1052
|
+
return markup == void 0 ? "" : String(markup).replace(_MATCH_HTML, encode_char);
|
|
1053
|
+
};
|
|
1054
|
+
function escapeXMLToString() {
|
|
1055
|
+
return Function.prototype.toString.call(this) + ";\n" + escapeFuncStr;
|
|
1056
|
+
}
|
|
1057
|
+
try {
|
|
1058
|
+
if (typeof Object.defineProperty === "function") {
|
|
1059
|
+
Object.defineProperty(exports.escapeXML, "toString", { value: escapeXMLToString });
|
|
1060
|
+
} else {
|
|
1061
|
+
exports.escapeXML.toString = escapeXMLToString;
|
|
1062
|
+
}
|
|
1063
|
+
} catch (err) {
|
|
1064
|
+
console.warn("Unable to set escapeXML.toString (is the Function prototype frozen?)");
|
|
1065
|
+
}
|
|
1066
|
+
exports.shallowCopy = function(to, from) {
|
|
1067
|
+
from = from || {};
|
|
1068
|
+
if (to !== null && to !== void 0) {
|
|
1069
|
+
for (var p in from) {
|
|
1070
|
+
if (!hasOwn(from, p)) {
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
if (p === "__proto__" || p === "constructor") {
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
to[p] = from[p];
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return to;
|
|
1080
|
+
};
|
|
1081
|
+
exports.shallowCopyFromList = function(to, from, list) {
|
|
1082
|
+
list = list || [];
|
|
1083
|
+
from = from || {};
|
|
1084
|
+
if (to !== null && to !== void 0) {
|
|
1085
|
+
for (var i = 0; i < list.length; i++) {
|
|
1086
|
+
var p = list[i];
|
|
1087
|
+
if (typeof from[p] != "undefined") {
|
|
1088
|
+
if (!hasOwn(from, p)) {
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
if (p === "__proto__" || p === "constructor") {
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
to[p] = from[p];
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
return to;
|
|
1099
|
+
};
|
|
1100
|
+
exports.cache = {
|
|
1101
|
+
_data: {},
|
|
1102
|
+
set: function(key, val) {
|
|
1103
|
+
this._data[key] = val;
|
|
1104
|
+
},
|
|
1105
|
+
get: function(key) {
|
|
1106
|
+
return this._data[key];
|
|
1107
|
+
},
|
|
1108
|
+
remove: function(key) {
|
|
1109
|
+
delete this._data[key];
|
|
1110
|
+
},
|
|
1111
|
+
reset: function() {
|
|
1112
|
+
this._data = {};
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
exports.hyphenToCamel = function(str) {
|
|
1116
|
+
return str.replace(/-[a-z]/g, function(match) {
|
|
1117
|
+
return match[1].toUpperCase();
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1120
|
+
exports.createNullProtoObjWherePossible = function() {
|
|
1121
|
+
if (typeof Object.create == "function") {
|
|
1122
|
+
return function() {
|
|
1123
|
+
return /* @__PURE__ */ Object.create(null);
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (!({ __proto__: null } instanceof Object)) {
|
|
1127
|
+
return function() {
|
|
1128
|
+
return { __proto__: null };
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
return function() {
|
|
1132
|
+
return {};
|
|
1133
|
+
};
|
|
1134
|
+
}();
|
|
1135
|
+
exports.hasOwnOnlyObject = function(obj) {
|
|
1136
|
+
var o = exports.createNullProtoObjWherePossible();
|
|
1137
|
+
for (var p in obj) {
|
|
1138
|
+
if (hasOwn(obj, p)) {
|
|
1139
|
+
o[p] = obj[p];
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
return o;
|
|
1143
|
+
};
|
|
1144
|
+
} (utils));
|
|
1145
|
+
return utils;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
var version = "3.1.10";
|
|
1149
|
+
var require$$3 = {
|
|
1150
|
+
version: version};
|
|
1151
|
+
|
|
1152
|
+
var hasRequiredEjs;
|
|
1153
|
+
|
|
1154
|
+
function requireEjs () {
|
|
1155
|
+
if (hasRequiredEjs) return ejs$1;
|
|
1156
|
+
hasRequiredEjs = 1;
|
|
1157
|
+
(function (exports) {
|
|
1158
|
+
/**
|
|
1159
|
+
* @file Embedded JavaScript templating engine. {@link http://ejs.co}
|
|
1160
|
+
* @author Matthew Eernisse <mde@fleegix.org>
|
|
1161
|
+
* @author Tiancheng "Timothy" Gu <timothygu99@gmail.com>
|
|
1162
|
+
* @project EJS
|
|
1163
|
+
* @license {@link http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0}
|
|
1164
|
+
*/
|
|
1165
|
+
var fs = require$$0;
|
|
1166
|
+
var path = require$$1;
|
|
1167
|
+
var utils = requireUtils();
|
|
1168
|
+
var scopeOptionWarned = false;
|
|
1169
|
+
var _VERSION_STRING = require$$3.version;
|
|
1170
|
+
var _DEFAULT_OPEN_DELIMITER = "<";
|
|
1171
|
+
var _DEFAULT_CLOSE_DELIMITER = ">";
|
|
1172
|
+
var _DEFAULT_DELIMITER = "%";
|
|
1173
|
+
var _DEFAULT_LOCALS_NAME = "locals";
|
|
1174
|
+
var _NAME = "ejs";
|
|
1175
|
+
var _REGEX_STRING = "(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)";
|
|
1176
|
+
var _OPTS_PASSABLE_WITH_DATA = [
|
|
1177
|
+
"delimiter",
|
|
1178
|
+
"scope",
|
|
1179
|
+
"context",
|
|
1180
|
+
"debug",
|
|
1181
|
+
"compileDebug",
|
|
1182
|
+
"client",
|
|
1183
|
+
"_with",
|
|
1184
|
+
"rmWhitespace",
|
|
1185
|
+
"strict",
|
|
1186
|
+
"filename",
|
|
1187
|
+
"async"
|
|
1188
|
+
];
|
|
1189
|
+
var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat("cache");
|
|
1190
|
+
var _BOM = /^\uFEFF/;
|
|
1191
|
+
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
|
1192
|
+
exports.cache = utils.cache;
|
|
1193
|
+
exports.fileLoader = fs.readFileSync;
|
|
1194
|
+
exports.localsName = _DEFAULT_LOCALS_NAME;
|
|
1195
|
+
exports.promiseImpl = new Function("return this;")().Promise;
|
|
1196
|
+
exports.resolveInclude = function(name, filename, isDir) {
|
|
1197
|
+
var dirname = path.dirname;
|
|
1198
|
+
var extname = path.extname;
|
|
1199
|
+
var resolve = path.resolve;
|
|
1200
|
+
var includePath = resolve(isDir ? filename : dirname(filename), name);
|
|
1201
|
+
var ext = extname(name);
|
|
1202
|
+
if (!ext) {
|
|
1203
|
+
includePath += ".ejs";
|
|
1204
|
+
}
|
|
1205
|
+
return includePath;
|
|
1206
|
+
};
|
|
1207
|
+
function resolvePaths(name, paths) {
|
|
1208
|
+
var filePath;
|
|
1209
|
+
if (paths.some(function(v) {
|
|
1210
|
+
filePath = exports.resolveInclude(name, v, true);
|
|
1211
|
+
return fs.existsSync(filePath);
|
|
1212
|
+
})) {
|
|
1213
|
+
return filePath;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function getIncludePath(path2, options) {
|
|
1217
|
+
var includePath;
|
|
1218
|
+
var filePath;
|
|
1219
|
+
var views = options.views;
|
|
1220
|
+
var match = /^[A-Za-z]+:\\|^\//.exec(path2);
|
|
1221
|
+
if (match && match.length) {
|
|
1222
|
+
path2 = path2.replace(/^\/*/, "");
|
|
1223
|
+
if (Array.isArray(options.root)) {
|
|
1224
|
+
includePath = resolvePaths(path2, options.root);
|
|
1225
|
+
} else {
|
|
1226
|
+
includePath = exports.resolveInclude(path2, options.root || "/", true);
|
|
1227
|
+
}
|
|
1228
|
+
} else {
|
|
1229
|
+
if (options.filename) {
|
|
1230
|
+
filePath = exports.resolveInclude(path2, options.filename);
|
|
1231
|
+
if (fs.existsSync(filePath)) {
|
|
1232
|
+
includePath = filePath;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (!includePath && Array.isArray(views)) {
|
|
1236
|
+
includePath = resolvePaths(path2, views);
|
|
1237
|
+
}
|
|
1238
|
+
if (!includePath && typeof options.includer !== "function") {
|
|
1239
|
+
throw new Error('Could not find the include file "' + options.escapeFunction(path2) + '"');
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return includePath;
|
|
1243
|
+
}
|
|
1244
|
+
function handleCache(options, template) {
|
|
1245
|
+
var func;
|
|
1246
|
+
var filename = options.filename;
|
|
1247
|
+
var hasTemplate = arguments.length > 1;
|
|
1248
|
+
if (options.cache) {
|
|
1249
|
+
if (!filename) {
|
|
1250
|
+
throw new Error("cache option requires a filename");
|
|
1251
|
+
}
|
|
1252
|
+
func = exports.cache.get(filename);
|
|
1253
|
+
if (func) {
|
|
1254
|
+
return func;
|
|
1255
|
+
}
|
|
1256
|
+
if (!hasTemplate) {
|
|
1257
|
+
template = fileLoader(filename).toString().replace(_BOM, "");
|
|
1258
|
+
}
|
|
1259
|
+
} else if (!hasTemplate) {
|
|
1260
|
+
if (!filename) {
|
|
1261
|
+
throw new Error("Internal EJS error: no file name or template provided");
|
|
1262
|
+
}
|
|
1263
|
+
template = fileLoader(filename).toString().replace(_BOM, "");
|
|
1264
|
+
}
|
|
1265
|
+
func = exports.compile(template, options);
|
|
1266
|
+
if (options.cache) {
|
|
1267
|
+
exports.cache.set(filename, func);
|
|
1268
|
+
}
|
|
1269
|
+
return func;
|
|
1270
|
+
}
|
|
1271
|
+
function tryHandleCache(options, data, cb) {
|
|
1272
|
+
var result;
|
|
1273
|
+
if (!cb) {
|
|
1274
|
+
if (typeof exports.promiseImpl == "function") {
|
|
1275
|
+
return new exports.promiseImpl(function(resolve, reject) {
|
|
1276
|
+
try {
|
|
1277
|
+
result = handleCache(options)(data);
|
|
1278
|
+
resolve(result);
|
|
1279
|
+
} catch (err) {
|
|
1280
|
+
reject(err);
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
} else {
|
|
1284
|
+
throw new Error("Please provide a callback function");
|
|
1285
|
+
}
|
|
1286
|
+
} else {
|
|
1287
|
+
try {
|
|
1288
|
+
result = handleCache(options)(data);
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
return cb(err);
|
|
1291
|
+
}
|
|
1292
|
+
cb(null, result);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function fileLoader(filePath) {
|
|
1296
|
+
return exports.fileLoader(filePath);
|
|
1297
|
+
}
|
|
1298
|
+
function includeFile(path2, options) {
|
|
1299
|
+
var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
|
|
1300
|
+
opts.filename = getIncludePath(path2, opts);
|
|
1301
|
+
if (typeof options.includer === "function") {
|
|
1302
|
+
var includerResult = options.includer(path2, opts.filename);
|
|
1303
|
+
if (includerResult) {
|
|
1304
|
+
if (includerResult.filename) {
|
|
1305
|
+
opts.filename = includerResult.filename;
|
|
1306
|
+
}
|
|
1307
|
+
if (includerResult.template) {
|
|
1308
|
+
return handleCache(opts, includerResult.template);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return handleCache(opts);
|
|
1313
|
+
}
|
|
1314
|
+
function rethrow(err, str, flnm, lineno, esc) {
|
|
1315
|
+
var lines = str.split("\n");
|
|
1316
|
+
var start = Math.max(lineno - 3, 0);
|
|
1317
|
+
var end = Math.min(lines.length, lineno + 3);
|
|
1318
|
+
var filename = esc(flnm);
|
|
1319
|
+
var context = lines.slice(start, end).map(function(line, i) {
|
|
1320
|
+
var curr = i + start + 1;
|
|
1321
|
+
return (curr == lineno ? " >> " : " ") + curr + "| " + line;
|
|
1322
|
+
}).join("\n");
|
|
1323
|
+
err.path = filename;
|
|
1324
|
+
err.message = (filename || "ejs") + ":" + lineno + "\n" + context + "\n\n" + err.message;
|
|
1325
|
+
throw err;
|
|
1326
|
+
}
|
|
1327
|
+
function stripSemi(str) {
|
|
1328
|
+
return str.replace(/;(\s*$)/, "$1");
|
|
1329
|
+
}
|
|
1330
|
+
exports.compile = function compile(template, opts) {
|
|
1331
|
+
var templ;
|
|
1332
|
+
if (opts && opts.scope) {
|
|
1333
|
+
if (!scopeOptionWarned) {
|
|
1334
|
+
console.warn("`scope` option is deprecated and will be removed in EJS 3");
|
|
1335
|
+
scopeOptionWarned = true;
|
|
1336
|
+
}
|
|
1337
|
+
if (!opts.context) {
|
|
1338
|
+
opts.context = opts.scope;
|
|
1339
|
+
}
|
|
1340
|
+
delete opts.scope;
|
|
1341
|
+
}
|
|
1342
|
+
templ = new Template(template, opts);
|
|
1343
|
+
return templ.compile();
|
|
1344
|
+
};
|
|
1345
|
+
exports.render = function(template, d, o) {
|
|
1346
|
+
var data = d || utils.createNullProtoObjWherePossible();
|
|
1347
|
+
var opts = o || utils.createNullProtoObjWherePossible();
|
|
1348
|
+
if (arguments.length == 2) {
|
|
1349
|
+
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA);
|
|
1350
|
+
}
|
|
1351
|
+
return handleCache(opts, template)(data);
|
|
1352
|
+
};
|
|
1353
|
+
exports.renderFile = function() {
|
|
1354
|
+
var args = Array.prototype.slice.call(arguments);
|
|
1355
|
+
var filename = args.shift();
|
|
1356
|
+
var cb;
|
|
1357
|
+
var opts = { filename };
|
|
1358
|
+
var data;
|
|
1359
|
+
var viewOpts;
|
|
1360
|
+
if (typeof arguments[arguments.length - 1] == "function") {
|
|
1361
|
+
cb = args.pop();
|
|
1362
|
+
}
|
|
1363
|
+
if (args.length) {
|
|
1364
|
+
data = args.shift();
|
|
1365
|
+
if (args.length) {
|
|
1366
|
+
utils.shallowCopy(opts, args.pop());
|
|
1367
|
+
} else {
|
|
1368
|
+
if (data.settings) {
|
|
1369
|
+
if (data.settings.views) {
|
|
1370
|
+
opts.views = data.settings.views;
|
|
1371
|
+
}
|
|
1372
|
+
if (data.settings["view cache"]) {
|
|
1373
|
+
opts.cache = true;
|
|
1374
|
+
}
|
|
1375
|
+
viewOpts = data.settings["view options"];
|
|
1376
|
+
if (viewOpts) {
|
|
1377
|
+
utils.shallowCopy(opts, viewOpts);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
utils.shallowCopyFromList(opts, data, _OPTS_PASSABLE_WITH_DATA_EXPRESS);
|
|
1381
|
+
}
|
|
1382
|
+
opts.filename = filename;
|
|
1383
|
+
} else {
|
|
1384
|
+
data = utils.createNullProtoObjWherePossible();
|
|
1385
|
+
}
|
|
1386
|
+
return tryHandleCache(opts, data, cb);
|
|
1387
|
+
};
|
|
1388
|
+
exports.Template = Template;
|
|
1389
|
+
exports.clearCache = function() {
|
|
1390
|
+
exports.cache.reset();
|
|
1391
|
+
};
|
|
1392
|
+
function Template(text, optsParam) {
|
|
1393
|
+
var opts = utils.hasOwnOnlyObject(optsParam);
|
|
1394
|
+
var options = utils.createNullProtoObjWherePossible();
|
|
1395
|
+
this.templateText = text;
|
|
1396
|
+
this.mode = null;
|
|
1397
|
+
this.truncate = false;
|
|
1398
|
+
this.currentLine = 1;
|
|
1399
|
+
this.source = "";
|
|
1400
|
+
options.client = opts.client || false;
|
|
1401
|
+
options.escapeFunction = opts.escape || opts.escapeFunction || utils.escapeXML;
|
|
1402
|
+
options.compileDebug = opts.compileDebug !== false;
|
|
1403
|
+
options.debug = !!opts.debug;
|
|
1404
|
+
options.filename = opts.filename;
|
|
1405
|
+
options.openDelimiter = opts.openDelimiter || exports.openDelimiter || _DEFAULT_OPEN_DELIMITER;
|
|
1406
|
+
options.closeDelimiter = opts.closeDelimiter || exports.closeDelimiter || _DEFAULT_CLOSE_DELIMITER;
|
|
1407
|
+
options.delimiter = opts.delimiter || exports.delimiter || _DEFAULT_DELIMITER;
|
|
1408
|
+
options.strict = opts.strict || false;
|
|
1409
|
+
options.context = opts.context;
|
|
1410
|
+
options.cache = opts.cache || false;
|
|
1411
|
+
options.rmWhitespace = opts.rmWhitespace;
|
|
1412
|
+
options.root = opts.root;
|
|
1413
|
+
options.includer = opts.includer;
|
|
1414
|
+
options.outputFunctionName = opts.outputFunctionName;
|
|
1415
|
+
options.localsName = opts.localsName || exports.localsName || _DEFAULT_LOCALS_NAME;
|
|
1416
|
+
options.views = opts.views;
|
|
1417
|
+
options.async = opts.async;
|
|
1418
|
+
options.destructuredLocals = opts.destructuredLocals;
|
|
1419
|
+
options.legacyInclude = typeof opts.legacyInclude != "undefined" ? !!opts.legacyInclude : true;
|
|
1420
|
+
if (options.strict) {
|
|
1421
|
+
options._with = false;
|
|
1422
|
+
} else {
|
|
1423
|
+
options._with = typeof opts._with != "undefined" ? opts._with : true;
|
|
1424
|
+
}
|
|
1425
|
+
this.opts = options;
|
|
1426
|
+
this.regex = this.createRegex();
|
|
1427
|
+
}
|
|
1428
|
+
Template.modes = {
|
|
1429
|
+
EVAL: "eval",
|
|
1430
|
+
ESCAPED: "escaped",
|
|
1431
|
+
RAW: "raw",
|
|
1432
|
+
COMMENT: "comment",
|
|
1433
|
+
LITERAL: "literal"
|
|
1434
|
+
};
|
|
1435
|
+
Template.prototype = {
|
|
1436
|
+
createRegex: function() {
|
|
1437
|
+
var str = _REGEX_STRING;
|
|
1438
|
+
var delim = utils.escapeRegExpChars(this.opts.delimiter);
|
|
1439
|
+
var open = utils.escapeRegExpChars(this.opts.openDelimiter);
|
|
1440
|
+
var close = utils.escapeRegExpChars(this.opts.closeDelimiter);
|
|
1441
|
+
str = str.replace(/%/g, delim).replace(/</g, open).replace(/>/g, close);
|
|
1442
|
+
return new RegExp(str);
|
|
1443
|
+
},
|
|
1444
|
+
compile: function() {
|
|
1445
|
+
var src;
|
|
1446
|
+
var fn;
|
|
1447
|
+
var opts = this.opts;
|
|
1448
|
+
var prepended = "";
|
|
1449
|
+
var appended = "";
|
|
1450
|
+
var escapeFn = opts.escapeFunction;
|
|
1451
|
+
var ctor;
|
|
1452
|
+
var sanitizedFilename = opts.filename ? JSON.stringify(opts.filename) : "undefined";
|
|
1453
|
+
if (!this.source) {
|
|
1454
|
+
this.generateSource();
|
|
1455
|
+
prepended += ' var __output = "";\n function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
|
|
1456
|
+
if (opts.outputFunctionName) {
|
|
1457
|
+
if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {
|
|
1458
|
+
throw new Error("outputFunctionName is not a valid JS identifier.");
|
|
1459
|
+
}
|
|
1460
|
+
prepended += " var " + opts.outputFunctionName + " = __append;\n";
|
|
1461
|
+
}
|
|
1462
|
+
if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {
|
|
1463
|
+
throw new Error("localsName is not a valid JS identifier.");
|
|
1464
|
+
}
|
|
1465
|
+
if (opts.destructuredLocals && opts.destructuredLocals.length) {
|
|
1466
|
+
var destructuring = " var __locals = (" + opts.localsName + " || {}),\n";
|
|
1467
|
+
for (var i = 0; i < opts.destructuredLocals.length; i++) {
|
|
1468
|
+
var name = opts.destructuredLocals[i];
|
|
1469
|
+
if (!_JS_IDENTIFIER.test(name)) {
|
|
1470
|
+
throw new Error("destructuredLocals[" + i + "] is not a valid JS identifier.");
|
|
1471
|
+
}
|
|
1472
|
+
if (i > 0) {
|
|
1473
|
+
destructuring += ",\n ";
|
|
1474
|
+
}
|
|
1475
|
+
destructuring += name + " = __locals." + name;
|
|
1476
|
+
}
|
|
1477
|
+
prepended += destructuring + ";\n";
|
|
1478
|
+
}
|
|
1479
|
+
if (opts._with !== false) {
|
|
1480
|
+
prepended += " with (" + opts.localsName + " || {}) {\n";
|
|
1481
|
+
appended += " }\n";
|
|
1482
|
+
}
|
|
1483
|
+
appended += " return __output;\n";
|
|
1484
|
+
this.source = prepended + this.source + appended;
|
|
1485
|
+
}
|
|
1486
|
+
if (opts.compileDebug) {
|
|
1487
|
+
src = "var __line = 1\n , __lines = " + JSON.stringify(this.templateText) + "\n , __filename = " + sanitizedFilename + ";\ntry {\n" + this.source + "} catch (e) {\n rethrow(e, __lines, __filename, __line, escapeFn);\n}\n";
|
|
1488
|
+
} else {
|
|
1489
|
+
src = this.source;
|
|
1490
|
+
}
|
|
1491
|
+
if (opts.client) {
|
|
1492
|
+
src = "escapeFn = escapeFn || " + escapeFn.toString() + ";\n" + src;
|
|
1493
|
+
if (opts.compileDebug) {
|
|
1494
|
+
src = "rethrow = rethrow || " + rethrow.toString() + ";\n" + src;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (opts.strict) {
|
|
1498
|
+
src = '"use strict";\n' + src;
|
|
1499
|
+
}
|
|
1500
|
+
if (opts.debug) {
|
|
1501
|
+
console.log(src);
|
|
1502
|
+
}
|
|
1503
|
+
if (opts.compileDebug && opts.filename) {
|
|
1504
|
+
src = src + "\n//# sourceURL=" + sanitizedFilename + "\n";
|
|
1505
|
+
}
|
|
1506
|
+
try {
|
|
1507
|
+
if (opts.async) {
|
|
1508
|
+
try {
|
|
1509
|
+
ctor = new Function("return (async function(){}).constructor;")();
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
if (e instanceof SyntaxError) {
|
|
1512
|
+
throw new Error("This environment does not support async/await");
|
|
1513
|
+
} else {
|
|
1514
|
+
throw e;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
} else {
|
|
1518
|
+
ctor = Function;
|
|
1519
|
+
}
|
|
1520
|
+
fn = new ctor(opts.localsName + ", escapeFn, include, rethrow", src);
|
|
1521
|
+
} catch (e) {
|
|
1522
|
+
if (e instanceof SyntaxError) {
|
|
1523
|
+
if (opts.filename) {
|
|
1524
|
+
e.message += " in " + opts.filename;
|
|
1525
|
+
}
|
|
1526
|
+
e.message += " while compiling ejs\n\n";
|
|
1527
|
+
e.message += "If the above error is not helpful, you may want to try EJS-Lint:\n";
|
|
1528
|
+
e.message += "https://github.com/RyanZim/EJS-Lint";
|
|
1529
|
+
if (!opts.async) {
|
|
1530
|
+
e.message += "\n";
|
|
1531
|
+
e.message += "Or, if you meant to create an async function, pass `async: true` as an option.";
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
throw e;
|
|
1535
|
+
}
|
|
1536
|
+
var returnedFn = opts.client ? fn : function anonymous(data) {
|
|
1537
|
+
var include = function(path2, includeData) {
|
|
1538
|
+
var d = utils.shallowCopy(utils.createNullProtoObjWherePossible(), data);
|
|
1539
|
+
if (includeData) {
|
|
1540
|
+
d = utils.shallowCopy(d, includeData);
|
|
1541
|
+
}
|
|
1542
|
+
return includeFile(path2, opts)(d);
|
|
1543
|
+
};
|
|
1544
|
+
return fn.apply(
|
|
1545
|
+
opts.context,
|
|
1546
|
+
[data || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]
|
|
1547
|
+
);
|
|
1548
|
+
};
|
|
1549
|
+
if (opts.filename && typeof Object.defineProperty === "function") {
|
|
1550
|
+
var filename = opts.filename;
|
|
1551
|
+
var basename = path.basename(filename, path.extname(filename));
|
|
1552
|
+
try {
|
|
1553
|
+
Object.defineProperty(returnedFn, "name", {
|
|
1554
|
+
value: basename,
|
|
1555
|
+
writable: false,
|
|
1556
|
+
enumerable: false,
|
|
1557
|
+
configurable: true
|
|
1558
|
+
});
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return returnedFn;
|
|
1563
|
+
},
|
|
1564
|
+
generateSource: function() {
|
|
1565
|
+
var opts = this.opts;
|
|
1566
|
+
if (opts.rmWhitespace) {
|
|
1567
|
+
this.templateText = this.templateText.replace(/[\r\n]+/g, "\n").replace(/^\s+|\s+$/gm, "");
|
|
1568
|
+
}
|
|
1569
|
+
this.templateText = this.templateText.replace(/[ \t]*<%_/gm, "<%_").replace(/_%>[ \t]*/gm, "_%>");
|
|
1570
|
+
var self = this;
|
|
1571
|
+
var matches = this.parseTemplateText();
|
|
1572
|
+
var d = this.opts.delimiter;
|
|
1573
|
+
var o = this.opts.openDelimiter;
|
|
1574
|
+
var c = this.opts.closeDelimiter;
|
|
1575
|
+
if (matches && matches.length) {
|
|
1576
|
+
matches.forEach(function(line, index) {
|
|
1577
|
+
var closing;
|
|
1578
|
+
if (line.indexOf(o + d) === 0 && line.indexOf(o + d + d) !== 0) {
|
|
1579
|
+
closing = matches[index + 2];
|
|
1580
|
+
if (!(closing == d + c || closing == "-" + d + c || closing == "_" + d + c)) {
|
|
1581
|
+
throw new Error('Could not find matching close tag for "' + line + '".');
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
self.scanLine(line);
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
},
|
|
1588
|
+
parseTemplateText: function() {
|
|
1589
|
+
var str = this.templateText;
|
|
1590
|
+
var pat = this.regex;
|
|
1591
|
+
var result = pat.exec(str);
|
|
1592
|
+
var arr = [];
|
|
1593
|
+
var firstPos;
|
|
1594
|
+
while (result) {
|
|
1595
|
+
firstPos = result.index;
|
|
1596
|
+
if (firstPos !== 0) {
|
|
1597
|
+
arr.push(str.substring(0, firstPos));
|
|
1598
|
+
str = str.slice(firstPos);
|
|
1599
|
+
}
|
|
1600
|
+
arr.push(result[0]);
|
|
1601
|
+
str = str.slice(result[0].length);
|
|
1602
|
+
result = pat.exec(str);
|
|
1603
|
+
}
|
|
1604
|
+
if (str) {
|
|
1605
|
+
arr.push(str);
|
|
1606
|
+
}
|
|
1607
|
+
return arr;
|
|
1608
|
+
},
|
|
1609
|
+
_addOutput: function(line) {
|
|
1610
|
+
if (this.truncate) {
|
|
1611
|
+
line = line.replace(/^(?:\r\n|\r|\n)/, "");
|
|
1612
|
+
this.truncate = false;
|
|
1613
|
+
}
|
|
1614
|
+
if (!line) {
|
|
1615
|
+
return line;
|
|
1616
|
+
}
|
|
1617
|
+
line = line.replace(/\\/g, "\\\\");
|
|
1618
|
+
line = line.replace(/\n/g, "\\n");
|
|
1619
|
+
line = line.replace(/\r/g, "\\r");
|
|
1620
|
+
line = line.replace(/"/g, '\\"');
|
|
1621
|
+
this.source += ' ; __append("' + line + '")\n';
|
|
1622
|
+
},
|
|
1623
|
+
scanLine: function(line) {
|
|
1624
|
+
var self = this;
|
|
1625
|
+
var d = this.opts.delimiter;
|
|
1626
|
+
var o = this.opts.openDelimiter;
|
|
1627
|
+
var c = this.opts.closeDelimiter;
|
|
1628
|
+
var newLineCount = 0;
|
|
1629
|
+
newLineCount = line.split("\n").length - 1;
|
|
1630
|
+
switch (line) {
|
|
1631
|
+
case o + d:
|
|
1632
|
+
case o + d + "_":
|
|
1633
|
+
this.mode = Template.modes.EVAL;
|
|
1634
|
+
break;
|
|
1635
|
+
case o + d + "=":
|
|
1636
|
+
this.mode = Template.modes.ESCAPED;
|
|
1637
|
+
break;
|
|
1638
|
+
case o + d + "-":
|
|
1639
|
+
this.mode = Template.modes.RAW;
|
|
1640
|
+
break;
|
|
1641
|
+
case o + d + "#":
|
|
1642
|
+
this.mode = Template.modes.COMMENT;
|
|
1643
|
+
break;
|
|
1644
|
+
case o + d + d:
|
|
1645
|
+
this.mode = Template.modes.LITERAL;
|
|
1646
|
+
this.source += ' ; __append("' + line.replace(o + d + d, o + d) + '")\n';
|
|
1647
|
+
break;
|
|
1648
|
+
case d + d + c:
|
|
1649
|
+
this.mode = Template.modes.LITERAL;
|
|
1650
|
+
this.source += ' ; __append("' + line.replace(d + d + c, d + c) + '")\n';
|
|
1651
|
+
break;
|
|
1652
|
+
case d + c:
|
|
1653
|
+
case "-" + d + c:
|
|
1654
|
+
case "_" + d + c:
|
|
1655
|
+
if (this.mode == Template.modes.LITERAL) {
|
|
1656
|
+
this._addOutput(line);
|
|
1657
|
+
}
|
|
1658
|
+
this.mode = null;
|
|
1659
|
+
this.truncate = line.indexOf("-") === 0 || line.indexOf("_") === 0;
|
|
1660
|
+
break;
|
|
1661
|
+
default:
|
|
1662
|
+
if (this.mode) {
|
|
1663
|
+
switch (this.mode) {
|
|
1664
|
+
case Template.modes.EVAL:
|
|
1665
|
+
case Template.modes.ESCAPED:
|
|
1666
|
+
case Template.modes.RAW:
|
|
1667
|
+
if (line.lastIndexOf("//") > line.lastIndexOf("\n")) {
|
|
1668
|
+
line += "\n";
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
switch (this.mode) {
|
|
1672
|
+
// Just executing code
|
|
1673
|
+
case Template.modes.EVAL:
|
|
1674
|
+
this.source += " ; " + line + "\n";
|
|
1675
|
+
break;
|
|
1676
|
+
// Exec, esc, and output
|
|
1677
|
+
case Template.modes.ESCAPED:
|
|
1678
|
+
this.source += " ; __append(escapeFn(" + stripSemi(line) + "))\n";
|
|
1679
|
+
break;
|
|
1680
|
+
// Exec and output
|
|
1681
|
+
case Template.modes.RAW:
|
|
1682
|
+
this.source += " ; __append(" + stripSemi(line) + ")\n";
|
|
1683
|
+
break;
|
|
1684
|
+
case Template.modes.COMMENT:
|
|
1685
|
+
break;
|
|
1686
|
+
// Literal <%% mode, append as raw output
|
|
1687
|
+
case Template.modes.LITERAL:
|
|
1688
|
+
this._addOutput(line);
|
|
1689
|
+
break;
|
|
1690
|
+
}
|
|
1691
|
+
} else {
|
|
1692
|
+
this._addOutput(line);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (self.opts.compileDebug && newLineCount) {
|
|
1696
|
+
this.currentLine += newLineCount;
|
|
1697
|
+
this.source += " ; __line = " + this.currentLine + "\n";
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
exports.escapeXML = utils.escapeXML;
|
|
1702
|
+
exports.__express = exports.renderFile;
|
|
1703
|
+
exports.VERSION = _VERSION_STRING;
|
|
1704
|
+
exports.name = _NAME;
|
|
1705
|
+
if (typeof window != "undefined") {
|
|
1706
|
+
window.ejs = exports;
|
|
1707
|
+
}
|
|
1708
|
+
} (ejs$1));
|
|
1709
|
+
return ejs$1;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
var ejsExports = requireEjs();
|
|
1713
|
+
var ejs = /*@__PURE__*/getDefaultExportFromCjs(ejsExports);
|
|
1714
|
+
|
|
1715
|
+
const cleanHyperlinks = (input) => (
|
|
1716
|
+
// When we run in a <ScrollArea> the links break
|
|
1717
|
+
// Remove OSC 8 hyperlink wrappers but preserve the styled content inside
|
|
1718
|
+
input.replaceAll(/\u001B]8;;[^\u0007]*\u0007/g, "").replaceAll("\x1B]8;;\x07", "")
|
|
1719
|
+
);
|
|
1720
|
+
const getRenderedMarkdown = ({ filename, templateVars, ...options }) => {
|
|
1721
|
+
setOptions({
|
|
1722
|
+
renderer: new TerminalRenderer({
|
|
1723
|
+
...options
|
|
1724
|
+
})
|
|
1725
|
+
});
|
|
1726
|
+
const entrypointPath = fs__default.realpathSync(process.argv[1]);
|
|
1727
|
+
const root = path.dirname(entrypointPath);
|
|
1728
|
+
const mdPath = path.join(root, "..", "assets", "markdown", filename);
|
|
1729
|
+
const mdTemplate = fs__default.readFileSync(mdPath, "utf8").trim();
|
|
1730
|
+
const markdown = ejs.render(mdTemplate, templateVars ?? {}, {
|
|
1731
|
+
filename: mdPath
|
|
1732
|
+
});
|
|
1733
|
+
const rendered = parse$1(markdown).trim();
|
|
1734
|
+
const cleaned = cleanHyperlinks(rendered);
|
|
1735
|
+
return cleaned;
|
|
1736
|
+
};
|
|
1737
|
+
const Markdown = ({ filename, templateVars, ...options }) => {
|
|
1738
|
+
const [text, setText] = useState("");
|
|
1739
|
+
useEffect(() => {
|
|
1740
|
+
const cleaned = getRenderedMarkdown({ filename, templateVars, ...options });
|
|
1741
|
+
setText(cleaned);
|
|
1742
|
+
}, [filename, templateVars, options]);
|
|
1743
|
+
return /* @__PURE__ */ jsx(Text, { children: text });
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
const CommandContext = React.createContext({
|
|
1747
|
+
command: null,
|
|
1748
|
+
setCommand(command) {
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
const CommandProvider = (props) => {
|
|
1752
|
+
const [command, setCommand] = useState(props.command || null);
|
|
1753
|
+
return /* @__PURE__ */ jsx(CommandContext.Provider, { value: { command, setCommand }, children: props.children });
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
const GameContext = React.createContext({
|
|
1757
|
+
game: null,
|
|
1758
|
+
gameId: null,
|
|
1759
|
+
setGameId(gameId) {
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
const GameProvider = ({ children }) => {
|
|
1763
|
+
const { command } = React.useContext(CommandContext);
|
|
1764
|
+
const [gameId, setGameId] = useState(command?.getGameId() || null);
|
|
1765
|
+
const [game, setGame] = useState(null);
|
|
1766
|
+
const handleGameIdChange = async () => {
|
|
1767
|
+
if (!gameId) {
|
|
1768
|
+
setGame(null);
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
const game2 = await getProject(gameId);
|
|
1772
|
+
setGame(game2);
|
|
1773
|
+
};
|
|
1774
|
+
useEffect(() => {
|
|
1775
|
+
handleGameIdChange();
|
|
1776
|
+
}, [gameId]);
|
|
1777
|
+
return /* @__PURE__ */ jsx(GameContext.Provider, { value: { game, gameId, setGameId }, children });
|
|
1778
|
+
};
|
|
1779
|
+
|
|
1780
|
+
scriptDir(import.meta);
|
|
1781
|
+
const getIsAppFound = (result) => {
|
|
1782
|
+
const isFound = result?.status === KeyTestStatus.SUCCESS || result?.status === KeyTestStatus.ERROR && result?.error === KeyTestError.NOT_INVITED;
|
|
1783
|
+
return isFound;
|
|
1784
|
+
};
|
|
1785
|
+
const CreateGooglePlayGame = (props) => {
|
|
1786
|
+
const { gameId } = useContext(GameContext);
|
|
1787
|
+
return /* @__PURE__ */ jsx(Fragment, { children: gameId && /* @__PURE__ */ jsx(Create, { gameId, ...props }) });
|
|
1788
|
+
};
|
|
1789
|
+
const Create = ({ gameId, onComplete, onError, ...boxProps }) => {
|
|
1790
|
+
const { data: result, isFetching } = useAndroidServiceAccountTestResult({ projectId: gameId });
|
|
1791
|
+
const { data: builds } = useBuilds({ pageNumber: 0, projectId: gameId });
|
|
1792
|
+
const previousIsFound = useRef(false);
|
|
1793
|
+
useEffect(() => {
|
|
1794
|
+
const isFound = getIsAppFound(result);
|
|
1795
|
+
if (previousIsFound.current === false && isFound) {
|
|
1796
|
+
onComplete();
|
|
1797
|
+
}
|
|
1798
|
+
previousIsFound.current = isFound;
|
|
1799
|
+
}, [result]);
|
|
1800
|
+
useSafeInput(async (input) => {
|
|
1801
|
+
if (!gameId) return;
|
|
1802
|
+
switch (input) {
|
|
1803
|
+
case "r": {
|
|
1804
|
+
queryClient.invalidateQueries({
|
|
1805
|
+
queryKey: cacheKeys.androidKeyTestResult({ projectId: gameId })
|
|
1806
|
+
});
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1809
|
+
case "d": {
|
|
1810
|
+
const dashUrl = await getShortAuthRequiredUrl(`/games/${getShortUUID(gameId)}/builds`);
|
|
1811
|
+
await open(dashUrl);
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
const initialBuild = builds?.data.find(
|
|
1817
|
+
(build) => build.platform === Platform.ANDROID && build.buildType === BuildType.AAB
|
|
1818
|
+
);
|
|
1819
|
+
const downloadCmd = initialBuild ? `${getBuildSummary(initialBuild).cmd}` : "Initial AAB build not found!";
|
|
1820
|
+
const templateVars = {
|
|
1821
|
+
dashboardURL: new URL(`/games/${getShortUUID(gameId)}/builds`, WEB_URL).toString(),
|
|
1822
|
+
downloadCmd
|
|
1823
|
+
};
|
|
1824
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 1, ...boxProps, children: [
|
|
1825
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1826
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: isFetching ? "Checking..." : "ShipThis has not detected your game in Google Play. Press R to test again." }),
|
|
1827
|
+
isFetching && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsx(Markdown, { filename: "create-google-play-game.md.ejs", templateVars })
|
|
1830
|
+
] }) });
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
class BaseAuthenticatedCommand extends BaseCommand {
|
|
1834
|
+
static flags = {};
|
|
1835
|
+
async init() {
|
|
1836
|
+
await super.init();
|
|
1837
|
+
if (!this.isAuthenticated()) {
|
|
1838
|
+
this.error("No auth config found. Please run `shipthis login` to authenticate.", { exit: 1 });
|
|
1839
|
+
}
|
|
1840
|
+
const self = await getSelf();
|
|
1841
|
+
const accepted = Boolean(self.details?.hasAcceptedTerms);
|
|
1842
|
+
if (!accepted) {
|
|
1843
|
+
this.error("You must accept the agreements first. Please run `shipthis login --acceptAgreements` to do this.", {
|
|
1844
|
+
exit: 1
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
const terms = await getTerms();
|
|
1848
|
+
if (terms.changes.length > 0) {
|
|
1849
|
+
const warningMD = getRenderedMarkdown({
|
|
1850
|
+
filename: "agreement-update.md.ejs",
|
|
1851
|
+
templateVars: {
|
|
1852
|
+
changes: terms.changes.map((a) => {
|
|
1853
|
+
return {
|
|
1854
|
+
...a,
|
|
1855
|
+
url: new URL(a.path, WEB_URL).toString()
|
|
1856
|
+
};
|
|
1857
|
+
})
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
console.log(warningMD);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
class BaseGameCommand extends BaseAuthenticatedCommand {
|
|
1866
|
+
static flags = {
|
|
1867
|
+
...BaseAuthenticatedCommand.flags,
|
|
1868
|
+
gameId: Flags.string({ char: "g", description: "The ID of the game" })
|
|
1869
|
+
};
|
|
1870
|
+
async getGame() {
|
|
1871
|
+
try {
|
|
1872
|
+
const gameId = await this.getGameId();
|
|
1873
|
+
if (!gameId) this.error("No game ID found.");
|
|
1874
|
+
return await getProject(gameId);
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
if (error?.response?.status === 404) {
|
|
1877
|
+
this.error("Game not found - please check you have access");
|
|
1878
|
+
} else throw error;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
async updateGame(update) {
|
|
1882
|
+
const project = await this.getGame();
|
|
1883
|
+
const projectUpdate = {
|
|
1884
|
+
...project,
|
|
1885
|
+
...update
|
|
1886
|
+
};
|
|
1887
|
+
const updatedProject = await updateProject(project.id, projectUpdate);
|
|
1888
|
+
await this.updateProjectConfig({ project: updatedProject });
|
|
1889
|
+
return updatedProject;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
const DetailsFlags = {
|
|
1894
|
+
androidPackageName: Flags.string({ char: "a", description: "Set the Android package name" }),
|
|
1895
|
+
buildNumber: Flags.integer({ char: "b", description: "Set the build number" }),
|
|
1896
|
+
gameEngine: Flags.string({ char: "e", description: "Set the game engine" }),
|
|
1897
|
+
gameEngineVersion: Flags.string({ char: "v", description: "Set the game engine version" }),
|
|
1898
|
+
gcpProjectId: Flags.string({ char: "g", description: "Set the GCP project ID" }),
|
|
1899
|
+
gcpServiceAccountId: Flags.string({ char: "c", description: "Set the GCP service account ID" }),
|
|
1900
|
+
iosBundleId: Flags.string({ char: "i", description: "Set the iOS bundle ID" }),
|
|
1901
|
+
name: Flags.string({ char: "n", description: "The name of the game" }),
|
|
1902
|
+
semanticVersion: Flags.string({ char: "s", description: "Set the semantic version" })
|
|
1903
|
+
};
|
|
1904
|
+
|
|
1905
|
+
export { getAuthToken as $, ApiKey as A, BaseAuthenticatedCommand as B, CredentialsType as C, DetailsFlags as D, DEFAULT_IGNORED_FILES_GLOBS as E, getGodotProjectName as F, GODOT_CAPABILITIES as G, getProjectJobs as H, getJobSummary as I, getJobStatusColor as J, getJob as K, JobStatus as L, getStageColor as M, makeHumanReadable as N, getProject as O, Platform as P, getProjectPlatformProgress as Q, GameContext as R, CommandContext as S, isValidSemVer as T, UserRole as U, castArrayObjectDates as V, WEB_URL as W, queryClient as X, CommandProvider as Y, GameProvider as Z, WS_URL as _, ApiKeyType as a, cacheKeys as a0, downloadBuildById as a1, getGoogleStatus as a2, getGodotAndroidPackageName as a3, enforcePolicy as a4, revokePolicy as a5, fetchKeyTestResult as a6, niceError as a7, KeyTestStatus as a8, KeyTestError as a9, getMessageColor as aA, getShortTime as aB, updateProject as aC, queryBuilds as aD, CreateGooglePlayGame as aE, inviteServiceAccount as aa, disconnectGoogle as ab, BaseCommand as ac, ejs as ad, getAPIKeys as ae, createAPIKey as af, revokeAPIKey as ag, getSingleUseUrl as ah, acceptTerms as ai, setAuthToken as aj, Auth as ak, getMaskedInput as al, getNewUploadTicket as am, getFileHash as an, startJobsFromUpload as ao, getPlatformName as ap, useBuilds as aq, getBuildSummary as ar, LogLevel as as, useSafeInput as at, Markdown as au, getShortAuthRequiredUrl as av, castObjectDates as aw, getGoogleAuthUrl as ax, useJob as ay, castJobDates as az, getRenderedMarkdown as b, getInput as c, Certificate as d, getProjects as e, getShortDate as f, getShortUUID as g, BaseGameCommand as h, generatePackageName as i, getGodotAppleBundleIdentifier as j, BundleId as k, App as l, CapabilityTypeOption as m, BetaGroup as n, isCWDGodotGame as o, CertificateType as p, Profile as q, ProfileType as r, API_URL as s, getAuthedHeaders as t, getGodotProjectCapabilities as u, CapabilityType as v, GameEngine as w, getGodotVersion as x, createProject as y, DEFAULT_SHIPPED_FILES_GLOBS as z };
|