@cedarjs/uploads 8.0.0-canary.3429 → 8.0.0-canary.3433
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 +7 -6
- package/dist/cleanupStaleUploads.d.ts +3 -4
- package/dist/cleanupStaleUploads.d.ts.map +1 -1
- package/dist/cleanupStaleUploads.js +2 -8
- package/dist/deleteFile.d.ts +15 -16
- package/dist/deleteFile.d.ts.map +1 -1
- package/dist/deleteFile.js +10 -49
- package/dist/derivedFiles.d.ts +189 -0
- package/dist/derivedFiles.d.ts.map +1 -0
- package/dist/derivedFiles.js +376 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/types.d.ts +56 -75
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/variants.d.ts +0 -212
- package/dist/variants.d.ts.map +0 -1
- package/dist/variants.js +0 -500
package/dist/variants.js
DELETED
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
|
-
import mime from "mime-types";
|
|
4
|
-
import { UploadError } from "./errors.js";
|
|
5
|
-
import { generateStorageKey } from "./keys.js";
|
|
6
|
-
import { DB_MAX_FILE_SIZE } from "./profiles.js";
|
|
7
|
-
import { toDataUri } from "./serialize.js";
|
|
8
|
-
import { resolveTarget } from "./targets.js";
|
|
9
|
-
const ONE_DAY = 24 * 60 * 60 * 1e3;
|
|
10
|
-
const TEN_MINUTES = 10 * 60 * 1e3;
|
|
11
|
-
const MIME_TYPE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i;
|
|
12
|
-
const VARIANT_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
13
|
-
function idOf(source) {
|
|
14
|
-
return typeof source === "string" ? source : source.id;
|
|
15
|
-
}
|
|
16
|
-
function isPrismaError(e, code) {
|
|
17
|
-
return typeof e === "object" && e !== null && "code" in e && e.code === code;
|
|
18
|
-
}
|
|
19
|
-
function defaultFilename(sourceFilename, name, mimeType) {
|
|
20
|
-
const base = sourceFilename.replace(/\.[^./\\]*$/, "") || "file";
|
|
21
|
-
const ext = mimeType ? mime.extension(mimeType.split(";")[0].trim()) : false;
|
|
22
|
-
return ext ? `${base}-${name}.${ext}` : `${base}-${name}`;
|
|
23
|
-
}
|
|
24
|
-
async function readBytes(db, targets, upload) {
|
|
25
|
-
const target = resolveTarget(targets, upload.target);
|
|
26
|
-
if (target.providerType === "db") {
|
|
27
|
-
const row = await db.upload.findUnique({ where: { id: upload.id } });
|
|
28
|
-
if (!row?.data) {
|
|
29
|
-
throw new UploadError(
|
|
30
|
-
"NOT_FOUND",
|
|
31
|
-
`Upload '${upload.id}' has no inline data.`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
return Buffer.from(row.data);
|
|
35
|
-
}
|
|
36
|
-
if (!upload.storageKey) {
|
|
37
|
-
throw new UploadError(
|
|
38
|
-
"NOT_FOUND",
|
|
39
|
-
`Upload '${upload.id}' has no stored file.`
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
return Buffer.from(await target.read(upload.storageKey));
|
|
43
|
-
}
|
|
44
|
-
async function deleteVariantRow(db, targets, row) {
|
|
45
|
-
let current = row;
|
|
46
|
-
while (current) {
|
|
47
|
-
if (current.storageKey) {
|
|
48
|
-
await resolveTarget(targets, current.target).delete(current.storageKey);
|
|
49
|
-
}
|
|
50
|
-
const { count } = await db.upload.deleteMany({
|
|
51
|
-
where: {
|
|
52
|
-
id: current.id,
|
|
53
|
-
status: current.status,
|
|
54
|
-
tokenId: current.tokenId,
|
|
55
|
-
storageKey: current.storageKey
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
if (count === 1) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
current = await db.upload.findUnique({
|
|
62
|
-
where: { id: current.id },
|
|
63
|
-
omit: { data: true }
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async function deleteVariants(source, { db, targets }) {
|
|
68
|
-
const rows = await db.upload.findMany({
|
|
69
|
-
where: { sourceId: idOf(source) },
|
|
70
|
-
omit: { data: true }
|
|
71
|
-
});
|
|
72
|
-
for (const row of rows) {
|
|
73
|
-
await deleteVariantRow(db, targets, row);
|
|
74
|
-
}
|
|
75
|
-
return rows.length;
|
|
76
|
-
}
|
|
77
|
-
function defineUploadVariants(options) {
|
|
78
|
-
const {
|
|
79
|
-
db,
|
|
80
|
-
targets,
|
|
81
|
-
target: defaultTarget,
|
|
82
|
-
retryFailedAfter = ONE_DAY,
|
|
83
|
-
claimTimeout = TEN_MINUTES,
|
|
84
|
-
pollInterval = 250,
|
|
85
|
-
onError = (error, { sourceId, variant }) => console.error(
|
|
86
|
-
`[cedar uploads] could not make variant '${variant}' of ${sourceId}:`,
|
|
87
|
-
error
|
|
88
|
-
)
|
|
89
|
-
} = options;
|
|
90
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
91
|
-
for (const [name, value] of Object.entries(options.variants)) {
|
|
92
|
-
if (!VARIANT_NAME.test(name)) {
|
|
93
|
-
throw new UploadError(
|
|
94
|
-
"CONFIGURATION",
|
|
95
|
-
`Invalid variant name '${name}'. Use letters, digits, '.', '_' and '-', starting with a letter or digit.`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
const definition = typeof value === "function" ? { generate: value } : value;
|
|
99
|
-
const target = definition.target ?? defaultTarget;
|
|
100
|
-
if (!Object.hasOwn(targets, target.name) || targets[target.name] !== target) {
|
|
101
|
-
throw new UploadError(
|
|
102
|
-
"CONFIGURATION",
|
|
103
|
-
`Variant '${name}' uses a storage target that isn't in \`targets\`.`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
definitions.set(name, { generate: definition.generate, target });
|
|
107
|
-
}
|
|
108
|
-
const names = [...definitions.keys()];
|
|
109
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
110
|
-
const waitingFor = /* @__PURE__ */ new Map();
|
|
111
|
-
const keyOf = (sourceId, name) => `${sourceId}\0${name}`;
|
|
112
|
-
const isWaitingFor = (from, to) => {
|
|
113
|
-
const seen = /* @__PURE__ */ new Set();
|
|
114
|
-
const stack = [from];
|
|
115
|
-
for (let key = stack.pop(); key !== void 0; key = stack.pop()) {
|
|
116
|
-
if (key === to) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
if (!seen.has(key)) {
|
|
120
|
-
seen.add(key);
|
|
121
|
-
stack.push(...waitingFor.get(key) ?? []);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return false;
|
|
125
|
-
};
|
|
126
|
-
const obtainFor = async (waiter, sourceId, name, chain) => {
|
|
127
|
-
const key = keyOf(sourceId, name);
|
|
128
|
-
if (!chain.includes(name) && inFlight.has(key) && isWaitingFor(key, waiter)) {
|
|
129
|
-
throw new UploadError(
|
|
130
|
-
"CONFIGURATION",
|
|
131
|
-
`Variant '${name}' and the variant reading it depend on each other.`
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
const waits = waitingFor.get(waiter) ?? [];
|
|
135
|
-
waitingFor.set(waiter, [...waits, key]);
|
|
136
|
-
try {
|
|
137
|
-
return await obtain(sourceId, name, chain);
|
|
138
|
-
} finally {
|
|
139
|
-
const rest = waitingFor.get(waiter) ?? [];
|
|
140
|
-
rest.splice(rest.indexOf(key), 1);
|
|
141
|
-
if (rest.length === 0) {
|
|
142
|
-
waitingFor.delete(waiter);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const definitionOf = (name) => {
|
|
147
|
-
const definition = definitions.get(name);
|
|
148
|
-
if (!definition) {
|
|
149
|
-
throw new UploadError(
|
|
150
|
-
"CONFIGURATION",
|
|
151
|
-
`Unknown upload variant '${name}'. Configured variants: ${names.join(", ") || "(none)"}.`
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
return definition;
|
|
155
|
-
};
|
|
156
|
-
const loadSource = async (sourceId) => {
|
|
157
|
-
const source = await db.upload.findUnique({
|
|
158
|
-
where: { id: sourceId },
|
|
159
|
-
omit: { data: true }
|
|
160
|
-
});
|
|
161
|
-
if (source?.status !== "completed") {
|
|
162
|
-
throw new UploadError(
|
|
163
|
-
"NOT_FOUND",
|
|
164
|
-
`Upload '${sourceId}' does not exist or is not completed, so it cannot have variants.`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
if (source.sourceId) {
|
|
168
|
-
throw new UploadError(
|
|
169
|
-
"NOT_SUPPORTED",
|
|
170
|
-
`Upload '${sourceId}' is itself a variant. Variants are made from original uploads only; use \`acquireVariantData()\` in a generator to make one variant from another.`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
return source;
|
|
174
|
-
};
|
|
175
|
-
const findRow = (sourceId, name) => db.upload.findFirst({
|
|
176
|
-
where: { sourceId, variant: name },
|
|
177
|
-
omit: { data: true }
|
|
178
|
-
});
|
|
179
|
-
const make = async (source, rowId, claim, name, chain) => {
|
|
180
|
-
const definition = definitionOf(name);
|
|
181
|
-
const target = definition.target;
|
|
182
|
-
const ours = { id: rowId, status: "pending", tokenId: claim };
|
|
183
|
-
const fail = async (error) => {
|
|
184
|
-
if (error !== void 0) {
|
|
185
|
-
onError(error, { sourceId: source.id, variant: name });
|
|
186
|
-
}
|
|
187
|
-
await db.upload.updateMany({ where: ours, data: { status: "failed" } });
|
|
188
|
-
return null;
|
|
189
|
-
};
|
|
190
|
-
let file;
|
|
191
|
-
try {
|
|
192
|
-
file = await definition.generate({
|
|
193
|
-
source,
|
|
194
|
-
variant: name,
|
|
195
|
-
readSource: () => readBytes(db, targets, source),
|
|
196
|
-
acquireVariantData: async (other) => {
|
|
197
|
-
const made = await obtainFor(
|
|
198
|
-
keyOf(source.id, name),
|
|
199
|
-
source.id,
|
|
200
|
-
other,
|
|
201
|
-
[...chain, name]
|
|
202
|
-
);
|
|
203
|
-
return made ? readBytes(db, targets, made) : null;
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
} catch (e) {
|
|
207
|
-
return fail(e);
|
|
208
|
-
}
|
|
209
|
-
if (!file) {
|
|
210
|
-
return fail();
|
|
211
|
-
}
|
|
212
|
-
if (!MIME_TYPE.test(file.mimeType)) {
|
|
213
|
-
return fail(
|
|
214
|
-
new UploadError(
|
|
215
|
-
"MIME_TYPE_NOT_ALLOWED",
|
|
216
|
-
`Variant '${name}' returned '${file.mimeType}', which is not a valid MIME type.`
|
|
217
|
-
)
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
const isDbTarget = target.providerType === "db";
|
|
221
|
-
if (isDbTarget && file.data.byteLength > DB_MAX_FILE_SIZE) {
|
|
222
|
-
return fail(
|
|
223
|
-
new UploadError(
|
|
224
|
-
"FILE_TOO_LARGE",
|
|
225
|
-
`Variant '${name}' is ${file.data.byteLength} bytes, which exceeds the ${DB_MAX_FILE_SIZE} byte limit for target '${target.name}'.`
|
|
226
|
-
)
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const storageKey = isDbTarget ? null : generateStorageKey(file.mimeType);
|
|
230
|
-
if (storageKey) {
|
|
231
|
-
try {
|
|
232
|
-
await target.write(storageKey, file.data, {
|
|
233
|
-
contentType: file.mimeType
|
|
234
|
-
});
|
|
235
|
-
} catch (e) {
|
|
236
|
-
await target.delete(storageKey).catch(() => void 0);
|
|
237
|
-
await db.upload.deleteMany({ where: ours });
|
|
238
|
-
throw e;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
const { count } = await db.upload.updateMany({
|
|
242
|
-
where: ours,
|
|
243
|
-
data: {
|
|
244
|
-
status: "completed",
|
|
245
|
-
// A retry or a takeover writes to the variant's current target,
|
|
246
|
-
// which is not necessarily the one the row was created for
|
|
247
|
-
target: target.name,
|
|
248
|
-
storageKey,
|
|
249
|
-
size: BigInt(file.data.byteLength),
|
|
250
|
-
mimeType: file.mimeType,
|
|
251
|
-
filename: file.filename ?? defaultFilename(source.filename, name, file.mimeType),
|
|
252
|
-
// Copying gives Prisma the plain-ArrayBuffer-backed array its
|
|
253
|
-
// `Bytes` input requires
|
|
254
|
-
data: isDbTarget ? new Uint8Array(file.data) : null,
|
|
255
|
-
tokenId: null
|
|
256
|
-
}
|
|
257
|
-
}).catch(async (e) => {
|
|
258
|
-
if (storageKey) {
|
|
259
|
-
await target.delete(storageKey).catch(() => void 0);
|
|
260
|
-
}
|
|
261
|
-
await db.upload.deleteMany({ where: ours }).catch(() => void 0);
|
|
262
|
-
throw e;
|
|
263
|
-
});
|
|
264
|
-
if (count !== 1) {
|
|
265
|
-
if (storageKey) {
|
|
266
|
-
await target.delete(storageKey).catch(() => void 0);
|
|
267
|
-
}
|
|
268
|
-
const current = await findRow(source.id, name);
|
|
269
|
-
return current?.status === "completed" ? current : null;
|
|
270
|
-
}
|
|
271
|
-
return db.upload.findUnique({ where: { id: rowId }, omit: { data: true } });
|
|
272
|
-
};
|
|
273
|
-
const obtainUncached = async (sourceId, name, chain) => {
|
|
274
|
-
const definition = definitionOf(name);
|
|
275
|
-
const source = await loadSource(sourceId);
|
|
276
|
-
for (; ; ) {
|
|
277
|
-
const row = await findRow(sourceId, name);
|
|
278
|
-
const now = Date.now();
|
|
279
|
-
const claim = randomUUID();
|
|
280
|
-
if (!row) {
|
|
281
|
-
let created;
|
|
282
|
-
try {
|
|
283
|
-
created = await db.upload.create({
|
|
284
|
-
data: {
|
|
285
|
-
sourceId,
|
|
286
|
-
variant: name,
|
|
287
|
-
target: definition.target.name,
|
|
288
|
-
status: "pending",
|
|
289
|
-
filename: defaultFilename(source.filename, name),
|
|
290
|
-
mimeType: "application/octet-stream",
|
|
291
|
-
size: 0n,
|
|
292
|
-
tokenId: claim,
|
|
293
|
-
userId: source.userId,
|
|
294
|
-
organizationId: source.organizationId
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
} catch (e) {
|
|
298
|
-
if (isPrismaError(e, "P2002")) {
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
if (isPrismaError(e, "P2003")) {
|
|
302
|
-
throw new UploadError(
|
|
303
|
-
"NOT_FOUND",
|
|
304
|
-
`Upload '${sourceId}' was deleted while its variant '${name}' was being made.`,
|
|
305
|
-
e
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
throw e;
|
|
309
|
-
}
|
|
310
|
-
return make(source, created.id, claim, name, chain);
|
|
311
|
-
}
|
|
312
|
-
if (row.status === "completed") {
|
|
313
|
-
return row;
|
|
314
|
-
}
|
|
315
|
-
if (row.status === "failed") {
|
|
316
|
-
const cutoff2 = new Date(now - retryFailedAfter);
|
|
317
|
-
if (row.updatedAt >= cutoff2) {
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
const { count } = await db.upload.updateMany({
|
|
321
|
-
where: { id: row.id, status: "failed", updatedAt: { lt: cutoff2 } },
|
|
322
|
-
data: { status: "pending", tokenId: claim }
|
|
323
|
-
});
|
|
324
|
-
if (count === 1) {
|
|
325
|
-
return make(source, row.id, claim, name, chain);
|
|
326
|
-
}
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
const cutoff = new Date(now - claimTimeout);
|
|
330
|
-
if (row.updatedAt < cutoff) {
|
|
331
|
-
const { count } = await db.upload.updateMany({
|
|
332
|
-
where: {
|
|
333
|
-
id: row.id,
|
|
334
|
-
status: "pending",
|
|
335
|
-
tokenId: row.tokenId,
|
|
336
|
-
updatedAt: { lt: cutoff }
|
|
337
|
-
},
|
|
338
|
-
data: { tokenId: claim }
|
|
339
|
-
});
|
|
340
|
-
if (count === 1) {
|
|
341
|
-
return make(source, row.id, claim, name, chain);
|
|
342
|
-
}
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
await sleep(pollInterval);
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
const obtain = (sourceId, name, chain) => {
|
|
349
|
-
if (chain.includes(name)) {
|
|
350
|
-
return Promise.reject(
|
|
351
|
-
new UploadError(
|
|
352
|
-
"CONFIGURATION",
|
|
353
|
-
`Variant '${name}' depends on itself: ${[...chain, name].join(" -> ")}.`
|
|
354
|
-
)
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
const key = keyOf(sourceId, name);
|
|
358
|
-
const running = inFlight.get(key);
|
|
359
|
-
if (running) {
|
|
360
|
-
return running;
|
|
361
|
-
}
|
|
362
|
-
const attempt = obtainUncached(sourceId, name, chain);
|
|
363
|
-
const forget = () => {
|
|
364
|
-
if (inFlight.get(key) === attempt) {
|
|
365
|
-
inFlight.delete(key);
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
inFlight.set(key, attempt);
|
|
369
|
-
attempt.then(forget, forget);
|
|
370
|
-
return attempt;
|
|
371
|
-
};
|
|
372
|
-
const acquireVariant = (source, name) => obtain(idOf(source), name, []);
|
|
373
|
-
const findVariant = async (source, name) => {
|
|
374
|
-
definitionOf(name);
|
|
375
|
-
return db.upload.findFirst({
|
|
376
|
-
where: { sourceId: idOf(source), variant: name, status: "completed" },
|
|
377
|
-
omit: { data: true }
|
|
378
|
-
});
|
|
379
|
-
};
|
|
380
|
-
const findVariants = async (sourceIds, name) => {
|
|
381
|
-
definitionOf(name);
|
|
382
|
-
const rows = sourceIds.length ? await db.upload.findMany({
|
|
383
|
-
where: {
|
|
384
|
-
sourceId: { in: sourceIds },
|
|
385
|
-
variant: name,
|
|
386
|
-
status: "completed"
|
|
387
|
-
},
|
|
388
|
-
omit: { data: true }
|
|
389
|
-
}) : [];
|
|
390
|
-
const bySource = /* @__PURE__ */ new Map();
|
|
391
|
-
for (const row of rows) {
|
|
392
|
-
if (row.sourceId) {
|
|
393
|
-
bySource.set(row.sourceId, row);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return bySource;
|
|
397
|
-
};
|
|
398
|
-
const acquireVariantData = async (source, name) => {
|
|
399
|
-
const upload = await acquireVariant(source, name);
|
|
400
|
-
if (!upload) {
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
return { upload, data: await readBytes(db, targets, upload) };
|
|
404
|
-
};
|
|
405
|
-
const acquireVariantUrl = async (source, name, urlOptions = {}) => {
|
|
406
|
-
const upload = await acquireVariant(source, name);
|
|
407
|
-
if (!upload) {
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
const target = resolveTarget(targets, upload.target);
|
|
411
|
-
if (target.providerType === "db") {
|
|
412
|
-
return toDataUri(upload.mimeType, await readBytes(db, targets, upload));
|
|
413
|
-
}
|
|
414
|
-
if (!upload.storageKey) {
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
return target.getSignedReadUrl(upload.storageKey, urlOptions);
|
|
418
|
-
};
|
|
419
|
-
const regenerateVariants = async (name, {
|
|
420
|
-
remake = true,
|
|
421
|
-
batchSize = 100
|
|
422
|
-
} = {}) => {
|
|
423
|
-
definitionOf(name);
|
|
424
|
-
const result = {
|
|
425
|
-
deleted: 0,
|
|
426
|
-
made: 0,
|
|
427
|
-
failed: 0,
|
|
428
|
-
errors: 0
|
|
429
|
-
};
|
|
430
|
-
const newest = await db.upload.findFirst({
|
|
431
|
-
where: { variant: name },
|
|
432
|
-
omit: { data: true },
|
|
433
|
-
orderBy: { createdAt: "desc" }
|
|
434
|
-
});
|
|
435
|
-
if (!newest) {
|
|
436
|
-
return result;
|
|
437
|
-
}
|
|
438
|
-
const cutoff = newest.createdAt;
|
|
439
|
-
const exclude = [];
|
|
440
|
-
for (; ; ) {
|
|
441
|
-
const rows = await db.upload.findMany({
|
|
442
|
-
where: {
|
|
443
|
-
variant: name,
|
|
444
|
-
createdAt: { lte: cutoff },
|
|
445
|
-
...exclude.length > 0 && { id: { notIn: exclude } }
|
|
446
|
-
},
|
|
447
|
-
omit: { data: true },
|
|
448
|
-
orderBy: { createdAt: "asc" },
|
|
449
|
-
take: batchSize
|
|
450
|
-
});
|
|
451
|
-
if (rows.length === 0) {
|
|
452
|
-
return result;
|
|
453
|
-
}
|
|
454
|
-
for (const row of rows) {
|
|
455
|
-
const sourceId = row.sourceId ?? "";
|
|
456
|
-
try {
|
|
457
|
-
await deleteVariantRow(db, targets, row);
|
|
458
|
-
result.deleted += 1;
|
|
459
|
-
} catch (e) {
|
|
460
|
-
result.errors += 1;
|
|
461
|
-
exclude.push(row.id);
|
|
462
|
-
onError(e, { sourceId, variant: name });
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
if (remake) {
|
|
466
|
-
await inFlight.get(keyOf(sourceId, name))?.catch(() => null);
|
|
467
|
-
const made = await acquireVariant(sourceId, name).catch(
|
|
468
|
-
(e) => {
|
|
469
|
-
onError(e, { sourceId, variant: name });
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
);
|
|
473
|
-
if (made) {
|
|
474
|
-
result.made += 1;
|
|
475
|
-
} else {
|
|
476
|
-
result.failed += 1;
|
|
477
|
-
}
|
|
478
|
-
const current = await findRow(sourceId, name);
|
|
479
|
-
if (current && current.createdAt <= cutoff) {
|
|
480
|
-
exclude.push(current.id);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
return {
|
|
487
|
-
names,
|
|
488
|
-
acquireVariant,
|
|
489
|
-
findVariant,
|
|
490
|
-
findVariants,
|
|
491
|
-
acquireVariantData,
|
|
492
|
-
acquireVariantUrl,
|
|
493
|
-
regenerateVariants,
|
|
494
|
-
deleteVariants: (source) => deleteVariants(source, { db, targets })
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
export {
|
|
498
|
-
defineUploadVariants,
|
|
499
|
-
deleteVariants
|
|
500
|
-
};
|