@uipath/vertical-solutions-tool 1.204.0 → 1.205.0-preview.258
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 +80 -0
- package/dist/THIRD-PARTY-NOTICES.md +1617 -369
- package/dist/createSdk-2f9x47em.js +12 -0
- package/dist/deploy-jobs-3k87fkwz.js +23 -0
- package/dist/deploy-service-qaykt7er.js +1779 -0
- package/dist/deployment-search-arbm2z06.js +2649 -0
- package/dist/embedded-file-reader-r66242ey.js +114 -0
- package/dist/first-party-service-srxaftkn.js +27 -0
- package/dist/index-7a3eqtxv.js +3174 -0
- package/dist/index-8s9dxpj4.js +18812 -0
- package/dist/index-fyc0k5dd.js +51405 -0
- package/dist/index-h43sj8at.js +23075 -0
- package/dist/index-kmp9fmhb.js +25 -0
- package/dist/index-ngs0a2he.js +15 -0
- package/dist/index.js +14 -3
- package/dist/index.umd.min-fqefd9kn.js +7814 -0
- package/dist/job-commands-azgbrbdb.js +23 -0
- package/dist/job-output-t43hzj1f.js +36 -0
- package/dist/prompts-dtgt183n.js +260 -0
- package/dist/resume-service-mqfnnzb5.js +344 -0
- package/dist/seed-service-2d1f7zaz.js +2014 -0
- package/dist/seed-templates-sqqqratr.js +14 -0
- package/dist/service-errors-jzbq3z7p.js +22 -0
- package/dist/solution-auth-zq6gyxsv.js +11 -0
- package/dist/solution-deployments-m44fyb88.js +33 -0
- package/dist/templates-85yrztpk.js +15 -0
- package/dist/tool-342sz3hm.js +439 -0
- package/dist/tool-38ad77wd.js +1472 -0
- package/dist/tool-4z1dy1rn.js +260 -0
- package/dist/tool-53w8skv5.js +262 -0
- package/dist/tool-67587985.js +554 -0
- package/dist/tool-87e2ps7f.js +13 -0
- package/dist/tool-cag2z2d6.js +30 -0
- package/dist/tool-e4qgwf9h.js +2281 -0
- package/dist/tool-gdtpsdn7.js +342 -0
- package/dist/tool-gvmshnq9.js +555 -0
- package/dist/tool-h4f967yd.js +73 -0
- package/dist/tool-hdxtgckw.js +249 -0
- package/dist/tool-jjxbj28g.js +17067 -0
- package/dist/tool-keqjsxdx.js +28 -0
- package/dist/tool-maw0g02p.js +24 -0
- package/dist/tool-mn1wkbhw.js +113 -0
- package/dist/tool-n35nrs32.js +4949 -0
- package/dist/tool-p589v2r5.js +114 -0
- package/dist/tool-pz9qm3dv.js +6723 -0
- package/dist/tool-r1ewa0tf.js +5645 -0
- package/dist/tool-r2epw08s.js +164 -0
- package/dist/tool-rr9fensp.js +12 -0
- package/dist/tool-s04z9t80.js +19 -0
- package/dist/tool-s9qbxa2g.js +638 -0
- package/dist/tool-tjhwq0ez.js +261 -0
- package/dist/tool-v15vxc4x.js +183 -0
- package/dist/tool-vf2mgth0.js +14286 -0
- package/dist/tool-vp7rjr2q.js +22083 -0
- package/dist/tool-xd2ejt3h.js +103 -0
- package/dist/tool-yx29vb63.js +672 -0
- package/dist/tool.js +12 -2
- package/dist/watch-job-wh6m7xzq.js +31 -0
- package/package.json +3 -3
- package/dist/tool-xymb3dsq.js +0 -45085
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Unzip,
|
|
3
|
+
UnzipInflate,
|
|
4
|
+
UnzipPassThrough
|
|
5
|
+
} from "./tool-67587985.js";
|
|
6
|
+
import {
|
|
7
|
+
SEED_ROOT_DIR,
|
|
8
|
+
acquireSeedWorkspaceLock,
|
|
9
|
+
errorCode,
|
|
10
|
+
insideSeedRoot,
|
|
11
|
+
isCaseInsensitiveDisk,
|
|
12
|
+
isDanglingLink,
|
|
13
|
+
isInsideWorkspace,
|
|
14
|
+
isMissingOrNotADirectory,
|
|
15
|
+
isPreservedWorkspaceFile,
|
|
16
|
+
namesSeedRoot,
|
|
17
|
+
reservedOnThisPlatform,
|
|
18
|
+
seedRoot,
|
|
19
|
+
tempPathFor,
|
|
20
|
+
workspaceRoot
|
|
21
|
+
} from "./tool-s9qbxa2g.js";
|
|
22
|
+
import {
|
|
23
|
+
catchError
|
|
24
|
+
} from "./tool-pz9qm3dv.js";
|
|
25
|
+
import {
|
|
26
|
+
getFileSystem
|
|
27
|
+
} from "./tool-hdxtgckw.js";
|
|
28
|
+
|
|
29
|
+
// src/services/seed/template-apply.ts
|
|
30
|
+
import { randomUUID } from "node:crypto";
|
|
31
|
+
|
|
32
|
+
// src/services/seed/folder-links.ts
|
|
33
|
+
async function isFolderLink(root, relativeDir) {
|
|
34
|
+
return await folderLinkState(root, relativeDir) !== "own";
|
|
35
|
+
}
|
|
36
|
+
async function folderLinkState(root, relativeDir) {
|
|
37
|
+
const fs = getFileSystem();
|
|
38
|
+
const absolute = fs.path.join(root.lexical, relativeDir);
|
|
39
|
+
if (await isDanglingLink(absolute))
|
|
40
|
+
return "link";
|
|
41
|
+
const real = await fs.realpath(absolute);
|
|
42
|
+
const resolved = real.normalize("NFC");
|
|
43
|
+
const expected = fs.path.join(root.real, relativeDir).normalize("NFC");
|
|
44
|
+
if (resolved === expected)
|
|
45
|
+
return "own";
|
|
46
|
+
if (resolved.toLowerCase() !== expected.toLowerCase())
|
|
47
|
+
return "link";
|
|
48
|
+
const folds = await isCaseInsensitiveDisk(root);
|
|
49
|
+
if (folds === true)
|
|
50
|
+
return "own";
|
|
51
|
+
return folds === false ? "link" : "unknown";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/services/seed/template-apply.ts
|
|
55
|
+
function linkEscapeError(relativePath) {
|
|
56
|
+
return new Error(`The seed workspace path ${relativePath} is a link that leads outside ` + "the workspace; refusing to write through it. Remove the link and " + "refresh again.");
|
|
57
|
+
}
|
|
58
|
+
function folderLinkError(relativePath, folder) {
|
|
59
|
+
return new Error(`The seed workspace path ${relativePath} goes through ${folder}, which ` + "is a folder link; refusing to write through it. Replace the link " + "with a real folder and refresh again.");
|
|
60
|
+
}
|
|
61
|
+
function fileLinkError(relativePath) {
|
|
62
|
+
return new Error(`The seed workspace path ${relativePath} is a link; refusing to ` + "replace it. Replace the link with a real file and refresh again.");
|
|
63
|
+
}
|
|
64
|
+
async function mkdirInsideWorkspace(root, relativePath, staging) {
|
|
65
|
+
const fs = getFileSystem();
|
|
66
|
+
const created = staging.createdDirs;
|
|
67
|
+
const segments = relativePath.split("/").slice(0, -1);
|
|
68
|
+
for (let depth = 1;depth <= segments.length; depth += 1) {
|
|
69
|
+
const relativeDir = segments.slice(0, depth).join("/");
|
|
70
|
+
const current = fs.path.join(root.lexical, relativeDir);
|
|
71
|
+
if (await isDisplacedByFolder(current) && await isInsideWorkspace(root, current) && !await isFolderLink(root, relativeDir)) {
|
|
72
|
+
const backup = `${current}.${randomUUID()}.uip-old`;
|
|
73
|
+
await step(fs.rename(current, backup), relativeDir);
|
|
74
|
+
staging.displaced.push({ target: current, backup });
|
|
75
|
+
}
|
|
76
|
+
if (!await fs.exists(current)) {
|
|
77
|
+
if (await isDanglingLink(current)) {
|
|
78
|
+
throw folderLinkError(relativePath, relativeDir);
|
|
79
|
+
}
|
|
80
|
+
await fs.mkdir(current);
|
|
81
|
+
created.push(current);
|
|
82
|
+
if (await isDanglingLink(current)) {
|
|
83
|
+
throw folderLinkError(relativePath, relativeDir);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!await isInsideWorkspace(root, current)) {
|
|
87
|
+
throw linkEscapeError(relativePath);
|
|
88
|
+
}
|
|
89
|
+
if (await isFolderLink(root, relativeDir)) {
|
|
90
|
+
throw folderLinkError(relativePath, relativeDir);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function isDisplacedByFolder(current) {
|
|
95
|
+
const fs = getFileSystem();
|
|
96
|
+
const [statError, stats] = await catchError(fs.stat(current));
|
|
97
|
+
if (statError || !stats)
|
|
98
|
+
return false;
|
|
99
|
+
return !stats.isDirectory();
|
|
100
|
+
}
|
|
101
|
+
async function checkedTarget(root, relativePath) {
|
|
102
|
+
const fs = getFileSystem();
|
|
103
|
+
const target = fs.path.join(root.lexical, relativePath);
|
|
104
|
+
if (!await isInsideWorkspace(root, target)) {
|
|
105
|
+
throw linkEscapeError(relativePath);
|
|
106
|
+
}
|
|
107
|
+
if (await fs.exists(target) && await isFolderLink(root, relativePath)) {
|
|
108
|
+
throw fileLinkError(relativePath);
|
|
109
|
+
}
|
|
110
|
+
return target;
|
|
111
|
+
}
|
|
112
|
+
async function applyEntries(root, entries) {
|
|
113
|
+
const staging = {
|
|
114
|
+
entries: [],
|
|
115
|
+
createdDirs: [],
|
|
116
|
+
displaced: []
|
|
117
|
+
};
|
|
118
|
+
const [applyError] = await catchError(stageAndSwap(root, entries, staging));
|
|
119
|
+
if (!applyError)
|
|
120
|
+
return;
|
|
121
|
+
const leftovers = await rollBack(staging);
|
|
122
|
+
const undone = leftovers.length === 0 ? " Nothing in the seed workspace was changed." : ` Could not put everything back: ${leftovers.join("; ")}.`;
|
|
123
|
+
throw new Error(`${applyError.message}${undone}`);
|
|
124
|
+
}
|
|
125
|
+
async function stageAndSwap(root, entries, staging) {
|
|
126
|
+
const fs = getFileSystem();
|
|
127
|
+
for (const [relativePath, bytes] of entries) {
|
|
128
|
+
await mkdirInsideWorkspace(root, relativePath, staging);
|
|
129
|
+
const target = await checkedTarget(root, relativePath);
|
|
130
|
+
const entry = {
|
|
131
|
+
relativePath,
|
|
132
|
+
target,
|
|
133
|
+
temp: tempPathFor(target),
|
|
134
|
+
swapped: false
|
|
135
|
+
};
|
|
136
|
+
staging.entries.push(entry);
|
|
137
|
+
await step(fs.writeFile(entry.temp, bytes), relativePath);
|
|
138
|
+
}
|
|
139
|
+
for (const entry of staging.entries)
|
|
140
|
+
await moveAside(entry);
|
|
141
|
+
for (const entry of staging.entries) {
|
|
142
|
+
await step(fs.rename(entry.temp, entry.target), entry.relativePath);
|
|
143
|
+
entry.swapped = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function moveAside(entry) {
|
|
147
|
+
const fs = getFileSystem();
|
|
148
|
+
const [statError, stats] = await catchError(fs.stat(entry.target));
|
|
149
|
+
if (statError)
|
|
150
|
+
throw writeFailure(entry.relativePath, statError);
|
|
151
|
+
const backup = `${entry.target}.${randomUUID()}.uip-old`;
|
|
152
|
+
if (!stats) {
|
|
153
|
+
const [listError] = await catchError(fs.readdir(fs.path.dirname(entry.target)));
|
|
154
|
+
if (!listError) {
|
|
155
|
+
if (!await isDanglingLink(entry.target))
|
|
156
|
+
return;
|
|
157
|
+
} else {
|
|
158
|
+
const [renameError] = await catchError(fs.rename(entry.target, backup));
|
|
159
|
+
if (renameError) {
|
|
160
|
+
if (isMissingOrNotADirectory(renameError))
|
|
161
|
+
return;
|
|
162
|
+
throw writeFailure(entry.relativePath, renameError);
|
|
163
|
+
}
|
|
164
|
+
entry.backup = backup;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
await step(fs.rename(entry.target, backup), entry.relativePath);
|
|
169
|
+
entry.backup = backup;
|
|
170
|
+
}
|
|
171
|
+
async function step(operation, relativePath) {
|
|
172
|
+
const [error] = await catchError(operation);
|
|
173
|
+
if (error)
|
|
174
|
+
throw writeFailure(relativePath, error);
|
|
175
|
+
}
|
|
176
|
+
function writeFailure(relativePath, error) {
|
|
177
|
+
return new Error(`Could not write ${relativePath}: ${error.message}. Close anything ` + "holding it open and refresh again.");
|
|
178
|
+
}
|
|
179
|
+
async function rollBack(staging) {
|
|
180
|
+
const fs = getFileSystem();
|
|
181
|
+
const leftovers = [];
|
|
182
|
+
for (const entry of [...staging.entries].reverse()) {
|
|
183
|
+
const [error] = await catchError(undoEntry(entry));
|
|
184
|
+
if (error)
|
|
185
|
+
leftovers.push(await leftoverNote(entry, error));
|
|
186
|
+
}
|
|
187
|
+
for (const dir of [...staging.createdDirs].reverse()) {
|
|
188
|
+
await catchError(fs.rm(dir));
|
|
189
|
+
}
|
|
190
|
+
for (const moved of [...staging.displaced].reverse()) {
|
|
191
|
+
const [error] = await catchError(fs.rename(moved.backup, moved.target));
|
|
192
|
+
if (error) {
|
|
193
|
+
leftovers.push(`${moved.target} (${error.message}), its previous copy is at ${moved.backup}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return leftovers;
|
|
197
|
+
}
|
|
198
|
+
async function undoEntry(entry) {
|
|
199
|
+
const fs = getFileSystem();
|
|
200
|
+
if (entry.backup && entry.swapped) {
|
|
201
|
+
await restoreOverSwapped(entry.backup, entry.target);
|
|
202
|
+
} else if (entry.backup) {
|
|
203
|
+
await fs.rename(entry.backup, entry.target);
|
|
204
|
+
} else if (entry.swapped) {
|
|
205
|
+
await fs.rm(entry.target);
|
|
206
|
+
}
|
|
207
|
+
if (!entry.swapped)
|
|
208
|
+
await fs.rm(entry.temp);
|
|
209
|
+
}
|
|
210
|
+
async function restoreOverSwapped(backup, target) {
|
|
211
|
+
const fs = getFileSystem();
|
|
212
|
+
const [error] = await catchError(fs.rename(backup, target));
|
|
213
|
+
if (!error)
|
|
214
|
+
return;
|
|
215
|
+
if (errorCode(error) !== "ENOTDIR")
|
|
216
|
+
throw error;
|
|
217
|
+
await fs.rm(target);
|
|
218
|
+
await fs.rename(backup, target);
|
|
219
|
+
}
|
|
220
|
+
async function leftoverNote(entry, error) {
|
|
221
|
+
const note = `${entry.relativePath} (${error.message})`;
|
|
222
|
+
const backup = entry.backup;
|
|
223
|
+
if (!backup)
|
|
224
|
+
return note;
|
|
225
|
+
const [probeError, kept] = await catchError(async () => {
|
|
226
|
+
if (await getFileSystem().exists(backup))
|
|
227
|
+
return true;
|
|
228
|
+
return isDanglingLink(backup);
|
|
229
|
+
});
|
|
230
|
+
return probeError || kept ? `${note}, its previous copy is at ${backup}` : note;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/services/seed/path-folding.ts
|
|
234
|
+
function exactPath(path) {
|
|
235
|
+
return path.normalize("NFC");
|
|
236
|
+
}
|
|
237
|
+
function foldPath(path) {
|
|
238
|
+
return path.split("/").map((segment) => segment.replace(/[. ]+$/, "").normalize("NFC").toLowerCase()).join("/");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/services/seed/path-segments.ts
|
|
242
|
+
var CONTROL_BYTES = /[\u0000-\u001F\u007F-\u009F]/;
|
|
243
|
+
var CONTROL_BYTES_ALL = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
244
|
+
function isUnsafeSegment(segment) {
|
|
245
|
+
return isDotSegment(segment) || segmentNameProblem(segment) !== undefined;
|
|
246
|
+
}
|
|
247
|
+
function withoutControlBytes(text) {
|
|
248
|
+
return text.replace(CONTROL_BYTES_ALL, "");
|
|
249
|
+
}
|
|
250
|
+
function isDotSegment(segment) {
|
|
251
|
+
return segment.replace(/[. ]+$/, "") === "";
|
|
252
|
+
}
|
|
253
|
+
function segmentNameProblem(segment) {
|
|
254
|
+
if (CONTROL_BYTES.test(segment)) {
|
|
255
|
+
return `"${withoutControlBytes(segment)}" contains a control character`;
|
|
256
|
+
}
|
|
257
|
+
if (segment.includes(":"))
|
|
258
|
+
return `"${segment}" contains ":"`;
|
|
259
|
+
if (reservedOnThisPlatform(segment)) {
|
|
260
|
+
return `Windows reserves "${segment}" as a device name`;
|
|
261
|
+
}
|
|
262
|
+
if (/[. ]$/.test(segment))
|
|
263
|
+
return `"${segment}" ends in a dot or a space`;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/services/seed/template-archive.ts
|
|
268
|
+
var MAX_ENTRIES = 2000;
|
|
269
|
+
var MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
270
|
+
var ARCHIVE_LIMITS = {
|
|
271
|
+
maxEntries: MAX_ENTRIES,
|
|
272
|
+
maxTotalBytes: MAX_TOTAL_BYTES
|
|
273
|
+
};
|
|
274
|
+
function extractSeedEntries(zip, limits = ARCHIVE_LIMITS) {
|
|
275
|
+
const entries = new Map;
|
|
276
|
+
const directories = [];
|
|
277
|
+
const skipped = [];
|
|
278
|
+
const warnings = [];
|
|
279
|
+
if (zip.length === 0) {
|
|
280
|
+
return { entries, directories, skipped, warnings };
|
|
281
|
+
}
|
|
282
|
+
const claimed = new Map;
|
|
283
|
+
const claimedFolders = new Map;
|
|
284
|
+
const inflated = inflateWithinLimits(zip, limits);
|
|
285
|
+
for (const name of inflated.directories) {
|
|
286
|
+
if (unsafeDirectoryEntry(name)) {
|
|
287
|
+
skipped.push(name);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const relative = toWorkspacePath(name.replace(/[\\/]+$/, ""));
|
|
291
|
+
if (relative === undefined)
|
|
292
|
+
continue;
|
|
293
|
+
claimFolders(claimedFolders, relative.split("/"), name);
|
|
294
|
+
directories.push(relative);
|
|
295
|
+
}
|
|
296
|
+
for (const [name, bytes] of inflated.files) {
|
|
297
|
+
const relative = toWorkspacePath(name);
|
|
298
|
+
if (relative === undefined) {
|
|
299
|
+
skipped.push(name);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
claimPath(claimed, relative, name);
|
|
303
|
+
claimFolders(claimedFolders, relative.split("/").slice(0, -1), name);
|
|
304
|
+
entries.set(relative, bytes);
|
|
305
|
+
}
|
|
306
|
+
const asFolders = new Set([
|
|
307
|
+
...directories.map(foldPath),
|
|
308
|
+
...claimedFolders.keys()
|
|
309
|
+
]);
|
|
310
|
+
for (const relative of entries.keys()) {
|
|
311
|
+
if (asFolders.has(foldPath(relative))) {
|
|
312
|
+
skipped.push(`${SEED_ROOT_DIR}/${relative}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (skipped.length > 0) {
|
|
316
|
+
warnings.push(`Ignored ${skipped.length} archive ${skipped.length === 1 ? "entry" : "entries"} outside 'seeding/': ${quoteEntryNames(skipped)}`);
|
|
317
|
+
}
|
|
318
|
+
return { entries, directories, skipped, warnings };
|
|
319
|
+
}
|
|
320
|
+
function quoteEntryNames(names) {
|
|
321
|
+
return names.slice(0, 5).map(quoteEntryName).join(", ");
|
|
322
|
+
}
|
|
323
|
+
function quoteEntryName(name) {
|
|
324
|
+
return JSON.stringify(name).replace(/[\u007F-\u009F]/g, (byte) => {
|
|
325
|
+
const code = byte.codePointAt(0) ?? 0;
|
|
326
|
+
return `\\u${code.toString(16).padStart(4, "0")}`;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
function claimFolders(claimed, segments, name) {
|
|
330
|
+
for (let depth = 1;depth <= segments.length; depth += 1) {
|
|
331
|
+
claimFolder(claimed, segments.slice(0, depth).join("/"), name);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function claimFolder(claimed, relative, name) {
|
|
335
|
+
const folded = foldPath(relative);
|
|
336
|
+
const earlier = claimed.get(folded);
|
|
337
|
+
if (earlier === undefined) {
|
|
338
|
+
claimed.set(folded, relative);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (earlier === relative)
|
|
342
|
+
return;
|
|
343
|
+
throw new Error("The seed-template archive spells one folder two ways " + `(${quoteEntryName(`${SEED_ROOT_DIR}/${earlier}`)} and ` + `${quoteEntryName(`${SEED_ROOT_DIR}/${relative}`)}, in ` + `${quoteEntryName(name)}); on a disk that does not tell them ` + "apart they would merge into one folder, so the archive is " + "refused and the seed workspace is left as it is.");
|
|
344
|
+
}
|
|
345
|
+
function claimPath(claimed, relative, name) {
|
|
346
|
+
const folded = foldPath(relative);
|
|
347
|
+
const earlier = claimed.get(folded);
|
|
348
|
+
if (earlier !== undefined) {
|
|
349
|
+
throw new Error(`The seed-template archive has two entries for the same file ` + `('${earlier}' and '${name}'); refusing it and leaving the ` + "seed workspace as it is.");
|
|
350
|
+
}
|
|
351
|
+
claimed.set(folded, name);
|
|
352
|
+
}
|
|
353
|
+
function inflateWithinLimits(zip, limits) {
|
|
354
|
+
const files = [];
|
|
355
|
+
const directories = [];
|
|
356
|
+
let entryCount = 0;
|
|
357
|
+
let totalBytes = 0;
|
|
358
|
+
const unzipper = new Unzip((file) => {
|
|
359
|
+
entryCount += 1;
|
|
360
|
+
if (entryCount > limits.maxEntries) {
|
|
361
|
+
throw new Error(`The seed-template archive holds more than ${limits.maxEntries} entries, ` + `over the ${limits.maxEntries} limit; refusing to extract it.`);
|
|
362
|
+
}
|
|
363
|
+
if (/[\\/]$/.test(file.name)) {
|
|
364
|
+
directories.push(file.name);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const chunks = [];
|
|
368
|
+
let size = 0;
|
|
369
|
+
file.ondata = (error, chunk, final) => {
|
|
370
|
+
if (error)
|
|
371
|
+
throw error;
|
|
372
|
+
totalBytes += chunk.length;
|
|
373
|
+
if (totalBytes > limits.maxTotalBytes) {
|
|
374
|
+
throw new Error(`The seed-template archive expands past the ${describeBytes(limits.maxTotalBytes)} limit; refusing to extract it.`);
|
|
375
|
+
}
|
|
376
|
+
chunks.push(chunk);
|
|
377
|
+
size += chunk.length;
|
|
378
|
+
if (final)
|
|
379
|
+
files.push([file.name, concat(chunks, size)]);
|
|
380
|
+
};
|
|
381
|
+
file.start();
|
|
382
|
+
});
|
|
383
|
+
unzipper.register(UnzipInflate);
|
|
384
|
+
unzipper.register(UnzipPassThrough);
|
|
385
|
+
unzipper.push(zip, true);
|
|
386
|
+
return { files, directories };
|
|
387
|
+
}
|
|
388
|
+
function unsafeDirectoryEntry(name) {
|
|
389
|
+
const trimmed = name.replace(/[\\/]+$/, "");
|
|
390
|
+
const normalized = trimmed.replaceAll("\\", "/");
|
|
391
|
+
if (normalized === SEED_ROOT_DIR)
|
|
392
|
+
return false;
|
|
393
|
+
return toWorkspacePath(trimmed) === undefined;
|
|
394
|
+
}
|
|
395
|
+
function concat(chunks, size) {
|
|
396
|
+
if (chunks.length === 1)
|
|
397
|
+
return chunks[0];
|
|
398
|
+
const out = new Uint8Array(size);
|
|
399
|
+
let offset = 0;
|
|
400
|
+
for (const chunk of chunks) {
|
|
401
|
+
out.set(chunk, offset);
|
|
402
|
+
offset += chunk.length;
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
function describeBytes(bytes) {
|
|
407
|
+
const mb = bytes / (1024 * 1024);
|
|
408
|
+
return mb >= 1 ? `${Math.round(mb)} MB` : `${bytes} byte`;
|
|
409
|
+
}
|
|
410
|
+
function toWorkspacePath(name) {
|
|
411
|
+
const normalized = name.replaceAll("\\", "/");
|
|
412
|
+
if (normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const segments = normalized.split("/").filter((segment) => segment !== "");
|
|
416
|
+
if (segments.length < 2)
|
|
417
|
+
return;
|
|
418
|
+
if (segments[0] !== SEED_ROOT_DIR)
|
|
419
|
+
return;
|
|
420
|
+
if (segments.some(isUnsafeSegment))
|
|
421
|
+
return;
|
|
422
|
+
const relative = segments.slice(1);
|
|
423
|
+
if (relative.some(isPreservedWorkspaceFile))
|
|
424
|
+
return;
|
|
425
|
+
return relative.join("/");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/services/seed/template-prune.ts
|
|
429
|
+
async function removeStaleFiles(root, keptPaths, keptDirs = []) {
|
|
430
|
+
const warnings = [];
|
|
431
|
+
const [probeError, folds] = await catchError(isCaseInsensitiveDisk(root));
|
|
432
|
+
if (probeError || folds === undefined) {
|
|
433
|
+
warnings.push(`Could not tell whether the disk folds case (${probeError?.message ?? "the probe was inconclusive"}), so any file whose name differs only in case from one the ` + "archive brought was kept. Remove it by hand if it is stale, " + "or it will still be seeded.");
|
|
434
|
+
}
|
|
435
|
+
const kept = {
|
|
436
|
+
files: new Set,
|
|
437
|
+
dirs: new Set,
|
|
438
|
+
filesExact: new Set,
|
|
439
|
+
dirsExact: new Set,
|
|
440
|
+
exact: new Set,
|
|
441
|
+
keepAll: new Set,
|
|
442
|
+
keepAllExact: new Set,
|
|
443
|
+
foldsCase: folds !== false
|
|
444
|
+
};
|
|
445
|
+
for (const path of keptPaths) {
|
|
446
|
+
const segments = path.split("/");
|
|
447
|
+
for (let depth = 1;depth <= segments.length; depth += 1) {
|
|
448
|
+
const prefix = segments.slice(0, depth).join("/");
|
|
449
|
+
kept.exact.add(exactPath(prefix));
|
|
450
|
+
const file = depth === segments.length;
|
|
451
|
+
(file ? kept.files : kept.dirs).add(foldPath(prefix));
|
|
452
|
+
(file ? kept.filesExact : kept.dirsExact).add(exactPath(prefix));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const path of keptDirs) {
|
|
456
|
+
const segments = path.split("/");
|
|
457
|
+
for (let depth = 1;depth <= segments.length; depth += 1) {
|
|
458
|
+
const prefix = segments.slice(0, depth).join("/");
|
|
459
|
+
kept.exact.add(exactPath(prefix));
|
|
460
|
+
kept.dirs.add(foldPath(prefix));
|
|
461
|
+
kept.dirsExact.add(exactPath(prefix));
|
|
462
|
+
}
|
|
463
|
+
if (holdsArchivedFile(path, kept))
|
|
464
|
+
continue;
|
|
465
|
+
kept.keepAll.add(foldPath(path));
|
|
466
|
+
kept.keepAllExact.add(exactPath(path));
|
|
467
|
+
}
|
|
468
|
+
await pruneDir(root, "", kept, warnings);
|
|
469
|
+
return warnings;
|
|
470
|
+
}
|
|
471
|
+
function holdsArchivedFile(dir, kept) {
|
|
472
|
+
const under = (paths, prefix) => {
|
|
473
|
+
const wanted = `${exactPath(prefix)}/`;
|
|
474
|
+
for (const path of paths) {
|
|
475
|
+
if (exactPath(path).startsWith(wanted))
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
return false;
|
|
479
|
+
};
|
|
480
|
+
if (under(kept.filesExact, dir))
|
|
481
|
+
return true;
|
|
482
|
+
return kept.foldsCase && under(kept.files, foldPath(dir));
|
|
483
|
+
}
|
|
484
|
+
function exactlyListed(relatives, kept) {
|
|
485
|
+
return new Set(relatives.filter((relative) => kept.exact.has(exactPath(relative))).map(foldPath));
|
|
486
|
+
}
|
|
487
|
+
async function pruneDir(root, relativeDir, kept, warnings) {
|
|
488
|
+
const fs = getFileSystem();
|
|
489
|
+
if (relativeDir !== "" && !await isRealFolder(root, relativeDir, warnings)) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const [listError, entries] = await catchError(fs.readdir(fs.path.join(root.lexical, relativeDir)));
|
|
493
|
+
if (listError) {
|
|
494
|
+
warnings.push(`Could not list ${relativeDir || "the workspace"} to remove stale ` + `files: ${listError.message}. Check it by hand, or a stale CSV ` + "or attachment there will still be seeded.");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (!entries)
|
|
498
|
+
return;
|
|
499
|
+
const relatives = entries.filter((entry) => !isPreservedWorkspaceFile(entry)).map((entry) => relativeDir ? `${relativeDir}/${entry}` : entry);
|
|
500
|
+
const exact = exactlyListed(relatives, kept);
|
|
501
|
+
for (const relative of relatives) {
|
|
502
|
+
const folded = foldPath(relative);
|
|
503
|
+
const shadowed = exact.has(folded) && !kept.exact.has(exactPath(relative));
|
|
504
|
+
const matches = (exactSet, foldedSet) => exactSet.has(exactPath(relative)) || kept.foldsCase && foldedSet.has(folded);
|
|
505
|
+
if (!shadowed && matches(kept.keepAllExact, kept.keepAll)) {
|
|
506
|
+
if (await isDeclaredFolder(root, relative, warnings))
|
|
507
|
+
continue;
|
|
508
|
+
await removeStale(root, relative, warnings);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (!shadowed && matches(kept.filesExact, kept.files))
|
|
512
|
+
continue;
|
|
513
|
+
if (!shadowed && matches(kept.dirsExact, kept.dirs)) {
|
|
514
|
+
await pruneDir(root, relative, kept, warnings);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
await removeStale(root, relative, warnings);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function isDeclaredFolder(root, relative, warnings) {
|
|
521
|
+
const fs = getFileSystem();
|
|
522
|
+
const [statError, stats] = await catchError(fs.stat(fs.path.join(root.lexical, relative)));
|
|
523
|
+
if (statError || !stats) {
|
|
524
|
+
warnings.push(`Kept ${relative}: could not check whether it is still a folder (${statError?.message ?? "the check was inconclusive"}). Remove it by hand if it is stale, or it will still be seeded.`);
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
if (!stats.isDirectory())
|
|
528
|
+
return false;
|
|
529
|
+
const [linkError, state] = await catchError(folderLinkState(root, relative));
|
|
530
|
+
if (linkError || state === "unknown") {
|
|
531
|
+
warnings.push(`Kept ${relative}: could not tell whether it is a link (${linkError?.message ?? "the disk would not say whether it folds case"}). Remove it by hand if it is stale, or it will still be seeded.`);
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
return state === "own";
|
|
535
|
+
}
|
|
536
|
+
async function removeStale(root, relative, warnings) {
|
|
537
|
+
if (!await staysInside(root, relative, warnings))
|
|
538
|
+
return;
|
|
539
|
+
const [linkError, state] = await catchError(folderLinkState(root, relative));
|
|
540
|
+
if (linkError || state === "unknown") {
|
|
541
|
+
warnings.push(`Left ${relative} alone: could not tell whether it is a link (${linkError?.message ?? "the disk would not say whether it folds case"}). Remove it by hand if it is stale, or it will still be seeded.`);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const linked = state === "link";
|
|
545
|
+
if (!linked && await holdsPreservedFile(root, relative)) {
|
|
546
|
+
await removeStaleFolder(root, relative, warnings);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
await remove(root, relative, warnings);
|
|
550
|
+
}
|
|
551
|
+
async function remove(root, relative, warnings) {
|
|
552
|
+
const fs = getFileSystem();
|
|
553
|
+
const [rmError] = await catchError(fs.rm(fs.path.join(root.lexical, relative)));
|
|
554
|
+
if (rmError) {
|
|
555
|
+
warnings.push(`Could not remove the stale ${relative}: ${rmError.message}. ` + "Delete it by hand, or it will still be seeded.");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function holdsPreservedFile(root, relative) {
|
|
559
|
+
const fs = getFileSystem();
|
|
560
|
+
const [linkError, linked] = await catchError(isFolderLink(root, relative));
|
|
561
|
+
if (linkError)
|
|
562
|
+
return true;
|
|
563
|
+
if (linked)
|
|
564
|
+
return false;
|
|
565
|
+
const [listError, entries] = await catchError(fs.readdir(fs.path.join(root.lexical, relative)));
|
|
566
|
+
if (listError)
|
|
567
|
+
return !isMissingOrNotADirectory(listError);
|
|
568
|
+
if (!entries)
|
|
569
|
+
return false;
|
|
570
|
+
for (const entry of entries) {
|
|
571
|
+
if (isPreservedWorkspaceFile(entry))
|
|
572
|
+
return true;
|
|
573
|
+
if (await holdsPreservedFile(root, `${relative}/${entry}`))
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
async function removeStaleFolder(root, relative, warnings) {
|
|
579
|
+
const fs = getFileSystem();
|
|
580
|
+
const dir = fs.path.join(root.lexical, relative);
|
|
581
|
+
const [linkError, linked] = await catchError(isFolderLink(root, relative));
|
|
582
|
+
if (linkError || linked) {
|
|
583
|
+
warnings.push(`Left ${relative} alone: ${linkError ? `could not check whether it is a link (${linkError.message})` : "it is a link, not the folder this prune found"}. Remove it by hand if it is stale, or it will still be seeded.`);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const [listError, entries] = await catchError(fs.readdir(dir));
|
|
587
|
+
if (listError || !entries) {
|
|
588
|
+
warnings.push(`Could not list the stale ${relative}: ${listError?.message ?? "no entries"}. Delete it by hand, or it will still be seeded.`);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
for (const entry of entries) {
|
|
592
|
+
if (isPreservedWorkspaceFile(entry))
|
|
593
|
+
continue;
|
|
594
|
+
await removeStale(root, `${relative}/${entry}`, warnings);
|
|
595
|
+
}
|
|
596
|
+
const [, left] = await catchError(fs.readdir(dir));
|
|
597
|
+
if (left && left.length === 0) {
|
|
598
|
+
await remove(root, relative, warnings);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
warnings.push(`Left the stale ${relative} in place: it still holds files this prune ` + "never deletes. Remove it by hand if you do not need them.");
|
|
602
|
+
}
|
|
603
|
+
async function staysInside(root, relative, warnings) {
|
|
604
|
+
const path = getFileSystem().path.join(root.lexical, relative);
|
|
605
|
+
const [checkError, inside] = await catchError(isInsideWorkspace(root, path));
|
|
606
|
+
if (!checkError && inside)
|
|
607
|
+
return true;
|
|
608
|
+
warnings.push(`Left ${relative} alone: ${checkError ? `could not check where it leads (${checkError.message})` : "it is a link that leads outside the workspace"}. Remove it by hand if it is stale, or it will still be seeded.`);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
async function isRealFolder(root, relativeDir, warnings) {
|
|
612
|
+
if (!await staysInside(root, relativeDir, warnings))
|
|
613
|
+
return false;
|
|
614
|
+
const [checkError, linked] = await catchError(isFolderLink(root, relativeDir));
|
|
615
|
+
if (!checkError && !linked)
|
|
616
|
+
return true;
|
|
617
|
+
warnings.push(`Left ${relativeDir} alone: it is a folder link, so its files were not ` + "checked. Remove it by hand if it is stale, or it will still be seeded.");
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/services/seed/templates.ts
|
|
622
|
+
async function refreshSeedTemplates(options) {
|
|
623
|
+
const fs = getFileSystem();
|
|
624
|
+
const dir = options.dir;
|
|
625
|
+
const extracted = extractSeedEntries(options.zip);
|
|
626
|
+
if (extracted.skipped.length > 0) {
|
|
627
|
+
throw new Error(`The seed-template archive has ${extracted.skipped.length} unsafe or ` + `out-of-scope ${extracted.skipped.length === 1 ? "entry" : "entries"} ` + `(${quoteEntryNames(extracted.skipped)}); refusing it and ` + "leaving the seed workspace as it is.");
|
|
628
|
+
}
|
|
629
|
+
await confineDefaultWorkspace(dir);
|
|
630
|
+
await fs.mkdir(dir);
|
|
631
|
+
await confineDefaultWorkspace(dir);
|
|
632
|
+
const written = [...extracted.entries.keys()];
|
|
633
|
+
const apply = async () => {
|
|
634
|
+
await confineDefaultWorkspace(dir);
|
|
635
|
+
const root = await workspaceRoot(dir);
|
|
636
|
+
await applyEntries(root, extracted.entries);
|
|
637
|
+
return removeStaleFiles(root, written, extracted.directories);
|
|
638
|
+
};
|
|
639
|
+
const removalWarnings = options.lockHeld ? await apply() : await withWorkspaceLock(dir, apply);
|
|
640
|
+
return {
|
|
641
|
+
dir,
|
|
642
|
+
written,
|
|
643
|
+
warnings: [...extracted.warnings, ...removalWarnings]
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function confineDefaultWorkspace(dir) {
|
|
647
|
+
const fs = getFileSystem();
|
|
648
|
+
const rootPath = fs.path.resolve(seedRoot());
|
|
649
|
+
const target = fs.path.resolve(fs.env.cwd(), dir);
|
|
650
|
+
if (target !== rootPath && !await namesSeedRoot(target))
|
|
651
|
+
return;
|
|
652
|
+
const checked = await insideSeedRoot(target);
|
|
653
|
+
if (checked.ok)
|
|
654
|
+
return;
|
|
655
|
+
throw new Error(`${checked.message} Refusing to write the seed templates into it. ` + checked.instructions);
|
|
656
|
+
}
|
|
657
|
+
async function withWorkspaceLock(dir, work) {
|
|
658
|
+
const fs = getFileSystem();
|
|
659
|
+
const [lockError, release] = await catchError(acquireSeedWorkspaceLock(dir, fs));
|
|
660
|
+
if (lockError || !release) {
|
|
661
|
+
throw new Error(`Could not lock the seed workspace ${dir} for the refresh: ` + `${lockError?.message ?? "no lock"}. Another deploy or seed may ` + "be using it; wait for it to finish and refresh again.");
|
|
662
|
+
}
|
|
663
|
+
try {
|
|
664
|
+
return await work();
|
|
665
|
+
} finally {
|
|
666
|
+
await catchError(release());
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export { isUnsafeSegment, withoutControlBytes, isDotSegment, segmentNameProblem, refreshSeedTemplates };
|
|
671
|
+
|
|
672
|
+
//# debugId=0E3DA92558B4E64564756E2164756E21
|
package/dist/tool.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
metadata,
|
|
3
3
|
registerCommands
|
|
4
|
-
} from "./tool-
|
|
4
|
+
} from "./tool-n35nrs32.js";
|
|
5
|
+
import"./tool-jjxbj28g.js";
|
|
6
|
+
import"./tool-r1ewa0tf.js";
|
|
7
|
+
import"./tool-38ad77wd.js";
|
|
8
|
+
import"./tool-cag2z2d6.js";
|
|
9
|
+
import"./tool-h4f967yd.js";
|
|
10
|
+
import"./tool-rr9fensp.js";
|
|
11
|
+
import"./tool-s04z9t80.js";
|
|
12
|
+
import"./tool-vf2mgth0.js";
|
|
13
|
+
import"./tool-pz9qm3dv.js";
|
|
14
|
+
import"./tool-hdxtgckw.js";
|
|
5
15
|
import"./tool-mpvp5rqs.js";
|
|
6
16
|
import"./tool-1yh8g550.js";
|
|
7
17
|
export {
|
|
@@ -9,4 +19,4 @@ export {
|
|
|
9
19
|
registerCommands
|
|
10
20
|
};
|
|
11
21
|
|
|
12
|
-
//# debugId=
|
|
22
|
+
//# debugId=4E0A5B04996A3AAE64756E2164756E21
|