livelocale 0.9.0 → 0.10.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/README.md +192 -7
- package/dist/codemod/index.cjs +1692 -593
- package/dist/codemod/index.cjs.map +1 -1
- package/dist/codemod/index.js +1675 -576
- package/dist/codemod/index.js.map +1 -1
- package/dist/server/index.cjs +85 -50
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +85 -50
- package/dist/server/index.js.map +1 -1
- package/dist/studio/assets/index-FFC_lDHv.css +1 -0
- package/dist/studio/assets/index-yI6iF4xN.js +14 -0
- package/dist/studio/index.html +2 -2
- package/dist/vite/index.cjs +25 -24
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +7 -6
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/studio/assets/index-Bvbtkry-.css +0 -1
- package/dist/studio/assets/index-DC_jRsox.js +0 -13
package/dist/server/index.js
CHANGED
|
@@ -1067,7 +1067,7 @@ new file mode 100644
|
|
|
1067
1067
|
}
|
|
1068
1068
|
};
|
|
1069
1069
|
|
|
1070
|
-
// src/server/github-
|
|
1070
|
+
// src/server/github-rest.ts
|
|
1071
1071
|
var GitHubApiError = class extends Error {
|
|
1072
1072
|
constructor(message, status) {
|
|
1073
1073
|
super(message);
|
|
@@ -1076,27 +1076,26 @@ var GitHubApiError = class extends Error {
|
|
|
1076
1076
|
}
|
|
1077
1077
|
status;
|
|
1078
1078
|
};
|
|
1079
|
-
var
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
return "added";
|
|
1088
|
-
case "removed":
|
|
1089
|
-
return "deleted";
|
|
1090
|
-
case "renamed":
|
|
1091
|
-
return "renamed";
|
|
1092
|
-
default:
|
|
1093
|
-
return "modified";
|
|
1094
|
-
}
|
|
1079
|
+
var CHUNK_BYTES = 32768;
|
|
1080
|
+
function encodeBase64Utf8(text) {
|
|
1081
|
+
const bytes = new TextEncoder().encode(text);
|
|
1082
|
+
let binary = "";
|
|
1083
|
+
for (let i = 0; i < bytes.length; i += CHUNK_BYTES) {
|
|
1084
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_BYTES));
|
|
1085
|
+
}
|
|
1086
|
+
return btoa(binary);
|
|
1095
1087
|
}
|
|
1096
|
-
function
|
|
1088
|
+
function decodeBase64Utf8(base64) {
|
|
1089
|
+
const binary = atob(base64.replace(/\s/g, ""));
|
|
1090
|
+
const bytes = new Uint8Array(binary.length);
|
|
1091
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
1092
|
+
return new TextDecoder().decode(bytes);
|
|
1093
|
+
}
|
|
1094
|
+
function encodeRefPath(ref) {
|
|
1095
|
+
return ref.split("/").map(encodeURIComponent).join("/");
|
|
1096
|
+
}
|
|
1097
|
+
function createGitHubRest(config) {
|
|
1097
1098
|
const repo = config.repo;
|
|
1098
|
-
const branch = config.branch;
|
|
1099
|
-
const baseBranch = config.baseBranch ?? "main";
|
|
1100
1099
|
const apiBase = (config.apiBase ?? "https://api.github.com").replace(/\/+$/, "");
|
|
1101
1100
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
1102
1101
|
const owner = repo.split("/")[0] ?? "";
|
|
@@ -1106,17 +1105,18 @@ function createGitHubGateway(config) {
|
|
|
1106
1105
|
if (typeof fetchImpl !== "function") {
|
|
1107
1106
|
throw new Error("[livelocale] The GitHub gateway needs a global fetch (Node 18+).");
|
|
1108
1107
|
}
|
|
1108
|
+
const headers = (hasBody) => ({
|
|
1109
|
+
Authorization: `Bearer ${config.token}`,
|
|
1110
|
+
Accept: "application/vnd.github+json",
|
|
1111
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
1112
|
+
...hasBody ? { "Content-Type": "application/json" } : {}
|
|
1113
|
+
});
|
|
1109
1114
|
async function api(method, path5, options = {}) {
|
|
1110
1115
|
const url = new URL(`${apiBase}/repos/${repo}${path5}`);
|
|
1111
1116
|
for (const [k, v] of Object.entries(options.query ?? {})) url.searchParams.set(k, v);
|
|
1112
1117
|
const response = await fetchImpl(url.toString(), {
|
|
1113
1118
|
method,
|
|
1114
|
-
headers:
|
|
1115
|
-
Authorization: `Bearer ${config.token}`,
|
|
1116
|
-
Accept: "application/vnd.github+json",
|
|
1117
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
1118
|
-
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
1119
|
-
},
|
|
1119
|
+
headers: headers(options.body !== void 0),
|
|
1120
1120
|
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0
|
|
1121
1121
|
});
|
|
1122
1122
|
let data = null;
|
|
@@ -1138,12 +1138,7 @@ function createGitHubGateway(config) {
|
|
|
1138
1138
|
async function probeStatus(method, path5, body) {
|
|
1139
1139
|
const response = await fetchImpl(`${apiBase}/repos/${repo}${path5}`, {
|
|
1140
1140
|
method,
|
|
1141
|
-
headers:
|
|
1142
|
-
Authorization: `Bearer ${config.token}`,
|
|
1143
|
-
Accept: "application/vnd.github+json",
|
|
1144
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
1145
|
-
...body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
1146
|
-
},
|
|
1141
|
+
headers: headers(body !== void 0),
|
|
1147
1142
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
1148
1143
|
});
|
|
1149
1144
|
try {
|
|
@@ -1152,14 +1147,60 @@ function createGitHubGateway(config) {
|
|
|
1152
1147
|
}
|
|
1153
1148
|
return response.status;
|
|
1154
1149
|
}
|
|
1155
|
-
async function refSha(
|
|
1150
|
+
async function refSha(branch) {
|
|
1156
1151
|
const res = await api(
|
|
1157
1152
|
"GET",
|
|
1158
|
-
`/git/ref/heads/${
|
|
1153
|
+
`/git/ref/heads/${encodeRefPath(branch)}`,
|
|
1159
1154
|
{ allow404: true }
|
|
1160
1155
|
);
|
|
1161
1156
|
return res.data?.object?.sha ?? null;
|
|
1162
1157
|
}
|
|
1158
|
+
async function findOpenPullRequest(head, base) {
|
|
1159
|
+
const res = await api("GET", "/pulls", {
|
|
1160
|
+
query: { head: `${owner}:${head}`, base, state: "open" }
|
|
1161
|
+
});
|
|
1162
|
+
const pull = res.data?.[0];
|
|
1163
|
+
return pull ? { number: pull.number, url: pull.html_url } : null;
|
|
1164
|
+
}
|
|
1165
|
+
async function openOrReusePullRequest(input) {
|
|
1166
|
+
const existing = await findOpenPullRequest(input.head, input.base);
|
|
1167
|
+
if (existing) {
|
|
1168
|
+
if (input.updateBodyIfReused) {
|
|
1169
|
+
await api("PATCH", `/pulls/${existing.number}`, { body: { body: input.body } });
|
|
1170
|
+
}
|
|
1171
|
+
return { ...existing, reused: true };
|
|
1172
|
+
}
|
|
1173
|
+
const res = await api("POST", "/pulls", {
|
|
1174
|
+
body: { title: input.title, head: input.head, base: input.base, body: input.body }
|
|
1175
|
+
});
|
|
1176
|
+
return { number: res.data.number, url: res.data.html_url, reused: false };
|
|
1177
|
+
}
|
|
1178
|
+
return { repo, api, probeStatus, refSha, findOpenPullRequest, openOrReusePullRequest };
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/server/github-gateway.ts
|
|
1182
|
+
var MAX_WRITE_ATTEMPTS = 4;
|
|
1183
|
+
var isWriteConflict = (err) => err instanceof GitHubApiError && (err.status === 409 || err.status === 422 && /sha/i.test(err.message));
|
|
1184
|
+
var encodePath = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
1185
|
+
var normalizeDir = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1186
|
+
function mapCompareStatus(status) {
|
|
1187
|
+
switch (status) {
|
|
1188
|
+
case "added":
|
|
1189
|
+
case "copied":
|
|
1190
|
+
return "added";
|
|
1191
|
+
case "removed":
|
|
1192
|
+
return "deleted";
|
|
1193
|
+
case "renamed":
|
|
1194
|
+
return "renamed";
|
|
1195
|
+
default:
|
|
1196
|
+
return "modified";
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
function createGitHubGateway(config) {
|
|
1200
|
+
const rest = createGitHubRest(config);
|
|
1201
|
+
const { repo, api, probeStatus, refSha } = rest;
|
|
1202
|
+
const branch = config.branch;
|
|
1203
|
+
const baseBranch = config.baseBranch ?? "main";
|
|
1163
1204
|
let branchReady = null;
|
|
1164
1205
|
function ensureBranch() {
|
|
1165
1206
|
if (!branchReady) {
|
|
@@ -1186,7 +1227,7 @@ function createGitHubGateway(config) {
|
|
|
1186
1227
|
async function readFile(path5) {
|
|
1187
1228
|
const contents = await getContents(path5);
|
|
1188
1229
|
if (!contents || Array.isArray(contents) || contents.type !== "file") return null;
|
|
1189
|
-
const text =
|
|
1230
|
+
const text = decodeBase64Utf8(contents.content);
|
|
1190
1231
|
return { text, sha: contents.sha };
|
|
1191
1232
|
}
|
|
1192
1233
|
async function putFile(path5, text, message, sha) {
|
|
@@ -1194,7 +1235,7 @@ function createGitHubGateway(config) {
|
|
|
1194
1235
|
await api("PUT", `/contents/${encodePath(path5)}`, {
|
|
1195
1236
|
body: {
|
|
1196
1237
|
message,
|
|
1197
|
-
content:
|
|
1238
|
+
content: encodeBase64Utf8(text),
|
|
1198
1239
|
branch,
|
|
1199
1240
|
...sha ? { sha } : {}
|
|
1200
1241
|
}
|
|
@@ -1399,10 +1440,7 @@ function createGitHubGateway(config) {
|
|
|
1399
1440
|
return (res.data?.files ?? []).filter((f) => f.filename.startsWith(prefix));
|
|
1400
1441
|
}
|
|
1401
1442
|
async function openPullRequestUrl() {
|
|
1402
|
-
|
|
1403
|
-
query: { head: `${owner}:${branch}`, base: baseBranch, state: "open" }
|
|
1404
|
-
});
|
|
1405
|
-
return res.data?.[0]?.html_url ?? null;
|
|
1443
|
+
return (await rest.findOpenPullRequest(branch, baseBranch))?.url ?? null;
|
|
1406
1444
|
}
|
|
1407
1445
|
async function status(basePath) {
|
|
1408
1446
|
const files = await compareFiles(basePath);
|
|
@@ -1433,17 +1471,14 @@ ${f.patch ?? "(binary or too large)"}
|
|
|
1433
1471
|
if (files.length === 0) {
|
|
1434
1472
|
throw new Error(`No changes to propose under ${normalizeDir(basePath)} on ${branch}.`);
|
|
1435
1473
|
}
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
base: baseBranch,
|
|
1443
|
-
body: "Translation changes proposed from the LiveLocale Studio."
|
|
1444
|
-
}
|
|
1474
|
+
const pull = await rest.openOrReusePullRequest({
|
|
1475
|
+
head: branch,
|
|
1476
|
+
base: baseBranch,
|
|
1477
|
+
title: message,
|
|
1478
|
+
body: "Translation changes proposed from the LiveLocale Studio.",
|
|
1479
|
+
updateBodyIfReused: false
|
|
1445
1480
|
});
|
|
1446
|
-
return { hash:
|
|
1481
|
+
return { hash: pull.url };
|
|
1447
1482
|
}
|
|
1448
1483
|
async function discard() {
|
|
1449
1484
|
const base = await refSha(baseBranch);
|