@elaraai/e3-cloud-files 0.0.0-stage → 1.0.92
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/LICENSE.md +54 -0
- package/README.md +286 -2
- package/dist/src/authoring.d.ts +114 -0
- package/dist/src/authoring.d.ts.map +1 -0
- package/dist/src/authoring.js +178 -0
- package/dist/src/authoring.js.map +1 -0
- package/dist/src/check/index.d.ts +15 -0
- package/dist/src/check/index.d.ts.map +1 -0
- package/dist/src/check/index.js +15 -0
- package/dist/src/check/index.js.map +1 -0
- package/dist/src/check.d.ts +66 -0
- package/dist/src/check.d.ts.map +1 -0
- package/dist/src/check.js +140 -0
- package/dist/src/check.js.map +1 -0
- package/dist/src/index.d.ts +27 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +27 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/paths.d.ts +70 -0
- package/dist/src/paths.d.ts.map +1 -0
- package/dist/src/paths.js +205 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/s3/index.d.ts +44 -0
- package/dist/src/s3/index.d.ts.map +1 -0
- package/dist/src/s3/index.js +138 -0
- package/dist/src/s3/index.js.map +1 -0
- package/dist/src/storage/index.d.ts +16 -0
- package/dist/src/storage/index.d.ts.map +1 -0
- package/dist/src/storage/index.js +120 -0
- package/dist/src/storage/index.js.map +1 -0
- package/dist/src/sync/engine.d.ts +191 -0
- package/dist/src/sync/engine.d.ts.map +1 -0
- package/dist/src/sync/engine.js +481 -0
- package/dist/src/sync/engine.js.map +1 -0
- package/dist/src/sync/index.d.ts +15 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +15 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/memory.d.ts +75 -0
- package/dist/src/sync/memory.d.ts.map +1 -0
- package/dist/src/sync/memory.js +155 -0
- package/dist/src/sync/memory.js.map +1 -0
- package/dist/test/decode.examples.d.ts +33 -0
- package/dist/test/decode.examples.d.ts.map +1 -0
- package/dist/test/decode.examples.js +103 -0
- package/dist/test/decode.examples.js.map +1 -0
- package/dist/test/orders.examples.d.ts +88 -0
- package/dist/test/orders.examples.d.ts.map +1 -0
- package/dist/test/orders.examples.js +71 -0
- package/dist/test/orders.examples.js.map +1 -0
- package/package.json +76 -3
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The sync: what a source's files fill, brought up to date.
|
|
7
|
+
*
|
|
8
|
+
* One algorithm for a local repository and a deployed workspace alike: what
|
|
9
|
+
* reads a bucket, what writes a workspace's inputs and what remembers the
|
|
10
|
+
* files each input took are given to it ({@link FilesReader},
|
|
11
|
+
* {@link FilesWriter}, {@link FilesLedger}), so both behave the same.
|
|
12
|
+
*
|
|
13
|
+
* For each mapping: list the source, compare each file with what the input
|
|
14
|
+
* took last (its ETag and size), read what is new or changed, write the input,
|
|
15
|
+
* and remember what it took and the value it wrote. A file the same as before
|
|
16
|
+
* is never read again, so a sync with nothing new reads nothing; an input
|
|
17
|
+
* whose files are as they were is not written. A file over its shape's limit
|
|
18
|
+
* is refused, named in the report, and never truncated; so is a file its
|
|
19
|
+
* reader cannot read (archived, changed since it was listed, or closed to the
|
|
20
|
+
* reader's access: {@link FilesUnreadableError}), the sync going on with the
|
|
21
|
+
* rest, and the next sync trying it again. An input is remembered only once
|
|
22
|
+
* it is written, so a sync that stops part way takes the rest next time.
|
|
23
|
+
*
|
|
24
|
+
* A folder's new and changed files are taken in batches of at most 256 MiB
|
|
25
|
+
* ({@link FOLDER_BATCH_BYTES}): each batch is read, written and remembered
|
|
26
|
+
* before the next is read, so a sync holds one batch of files, however large
|
|
27
|
+
* the folder. A sync can stop between inputs and between a folder's batches
|
|
28
|
+
* when asked ({@link syncFilesRound}, a cloud round handing over), and the
|
|
29
|
+
* next takes the rest.
|
|
30
|
+
*
|
|
31
|
+
* What an input took is trusted only while the input still holds the value
|
|
32
|
+
* the sync wrote, and its mapping and its source's binding are those it was
|
|
33
|
+
* taken under ({@link LedgerState}'s `under`): a deploy that resets the
|
|
34
|
+
* workspace's inputs, anyone setting one by hand, a changed declaration or a
|
|
35
|
+
* source bound elsewhere, and the next sync takes all of its files again, even
|
|
36
|
+
* when a sync under the old declarations wrote its ledger after the change.
|
|
37
|
+
*
|
|
38
|
+
* Mappings that share a source and a prefix list it once a round; a glob that
|
|
39
|
+
* matches only the prefix's own folder lists that folder alone.
|
|
40
|
+
*
|
|
41
|
+
* @packageDocumentation
|
|
42
|
+
*/
|
|
43
|
+
import { StringType, StructType, none, printFor, some, variant } from '@elaraai/east';
|
|
44
|
+
import { FILES_FILE_MAX_BYTES, FILES_FOLDER_ENTRY_MAX_BYTES, FilesShapeType, filesProblemText, } from '@elaraai/e3-cloud-types';
|
|
45
|
+
import { globMatcher, matchesTopFolderOnly } from '../paths.js';
|
|
46
|
+
/**
|
|
47
|
+
* A file a reader cannot read, which a sync refuses and names in its report,
|
|
48
|
+
* going on with the rest; the next sync tries it again. A reader throws it
|
|
49
|
+
* for what is the file's own, never for its source as a whole: a file
|
|
50
|
+
* archived, changed or gone since it was listed, or closed to the reader's
|
|
51
|
+
* access. Any other error ends the round, which is tried again.
|
|
52
|
+
*/
|
|
53
|
+
export class FilesUnreadableError extends Error {
|
|
54
|
+
key;
|
|
55
|
+
reason;
|
|
56
|
+
/**
|
|
57
|
+
* @param key - The file's key, relative to the binding
|
|
58
|
+
* @param reason - Why, completing "'<key>' …"
|
|
59
|
+
*/
|
|
60
|
+
constructor(key, reason) {
|
|
61
|
+
super(`${key} ${reason}`);
|
|
62
|
+
this.key = key;
|
|
63
|
+
this.reason = reason;
|
|
64
|
+
this.name = 'FilesUnreadableError';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** What an input took before its first sync: nothing. */
|
|
68
|
+
export const NO_LEDGER = { entries: new Map(), value: null, under: null };
|
|
69
|
+
/**
|
|
70
|
+
* What an input took, as a store keeps it (e3-cloud-types' `FilesLedgerType`).
|
|
71
|
+
*
|
|
72
|
+
* @param state - What it took
|
|
73
|
+
* @returns Its East value
|
|
74
|
+
*/
|
|
75
|
+
export function ledgerValueOf(state) {
|
|
76
|
+
return {
|
|
77
|
+
entries: new Map([...state.entries].map(([key, e]) => [key, { etag: e.etag, size: BigInt(e.size) }])),
|
|
78
|
+
value: state.value === null ? none : some(state.value),
|
|
79
|
+
under: state.under === null ? none : some(state.under),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* What an input took, from what a store keeps.
|
|
84
|
+
*
|
|
85
|
+
* @param value - Its East value
|
|
86
|
+
* @returns What it took
|
|
87
|
+
*/
|
|
88
|
+
export function ledgerStateOf(value) {
|
|
89
|
+
return {
|
|
90
|
+
entries: new Map([...value.entries].map(([key, e]) => [key, { etag: e.etag, size: Number(e.size) }])),
|
|
91
|
+
value: value.value.type === 'some' ? value.value.value : null,
|
|
92
|
+
under: value.under.type === 'some' ? value.under.value : null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const printUnder = printFor(StructType({ url: StringType, shape: FilesShapeType }));
|
|
96
|
+
/**
|
|
97
|
+
* What an input's files are taken under: its mapping's shape and where its
|
|
98
|
+
* source is bound, as one text. A ledger taken under another is not trusted:
|
|
99
|
+
* its keys may name other files, and its paths another folder's.
|
|
100
|
+
*
|
|
101
|
+
* @param mapping - The mapping
|
|
102
|
+
* @param binding - Its source's binding
|
|
103
|
+
* @returns The text
|
|
104
|
+
*/
|
|
105
|
+
export function takenUnder(mapping, binding) {
|
|
106
|
+
return printUnder({ url: binding.url, shape: mapping.shape });
|
|
107
|
+
}
|
|
108
|
+
/** The most bytes of a folder's files a sync holds before it writes them, when not told: 256 MiB. */
|
|
109
|
+
export const FOLDER_BATCH_BYTES = 256 * 1024 * 1024;
|
|
110
|
+
/** The most files of a folder a sync writes at once. */
|
|
111
|
+
const FOLDER_BATCH_FILES = 4096;
|
|
112
|
+
/** Thrown when a sync cannot start, naming every problem. */
|
|
113
|
+
export class FilesSyncError extends Error {
|
|
114
|
+
problems;
|
|
115
|
+
constructor(problems) {
|
|
116
|
+
super(`The files cannot be synced:\n${problems.map((p) => ` - ${filesProblemText(p)}`).join('\n')}`);
|
|
117
|
+
this.name = 'FilesSyncError';
|
|
118
|
+
this.problems = problems;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Whether a sync should stop here: asked to, once it has written something. */
|
|
122
|
+
function stopping(sync) {
|
|
123
|
+
if (!sync.yielded && sync.progressed && sync.options.shouldYield?.() === true)
|
|
124
|
+
sync.yielded = true;
|
|
125
|
+
return sync.yielded;
|
|
126
|
+
}
|
|
127
|
+
/** A tally of nothing. */
|
|
128
|
+
function emptyTally() {
|
|
129
|
+
return { added: 0n, changed: 0n, removed: 0n, unchanged: 0n, refused: [], bytes: 0n, updated: false };
|
|
130
|
+
}
|
|
131
|
+
/** A mapping's report, from its tally. */
|
|
132
|
+
function reportOf(mapping, tally) {
|
|
133
|
+
return { input: mapping.input, ...tally };
|
|
134
|
+
}
|
|
135
|
+
/** A key's path relative to a prefix. */
|
|
136
|
+
function relative(prefix, key) {
|
|
137
|
+
return key.slice(prefix.length);
|
|
138
|
+
}
|
|
139
|
+
/** Whether two ledger entries name the same bytes. */
|
|
140
|
+
function same(a, b) {
|
|
141
|
+
return a !== undefined && b !== undefined && a.etag === b.etag && a.size === b.size;
|
|
142
|
+
}
|
|
143
|
+
/** A file's ledger entry. */
|
|
144
|
+
function entryOf(object) {
|
|
145
|
+
return { etag: object.etag, size: object.size };
|
|
146
|
+
}
|
|
147
|
+
/** A mapping's binding; checked to exist before a sync starts. */
|
|
148
|
+
function bindingOf(mapping, options) {
|
|
149
|
+
return options.bindings.find((b) => b.source === mapping.source);
|
|
150
|
+
}
|
|
151
|
+
/** A mapping's reader: one per bound source, for the sync. */
|
|
152
|
+
function readerOf(sync, mapping) {
|
|
153
|
+
let reader = sync.readers.get(mapping.source);
|
|
154
|
+
if (reader === undefined)
|
|
155
|
+
sync.readers.set(mapping.source, reader = sync.options.reader(bindingOf(mapping, sync.options)));
|
|
156
|
+
return reader;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* What an input took last, trusted only while it holds the value the sync
|
|
160
|
+
* wrote and was taken under the mapping and binding as they are.
|
|
161
|
+
*/
|
|
162
|
+
async function before(sync, mapping) {
|
|
163
|
+
const [state, current] = await Promise.all([sync.options.ledger.read(mapping.input), sync.options.writer.current(mapping.input)]);
|
|
164
|
+
const under = takenUnder(mapping, bindingOf(mapping, sync.options));
|
|
165
|
+
if (state.value !== null && state.value === current && state.under === under)
|
|
166
|
+
return { entries: state.entries, trusted: true, stale: false, under };
|
|
167
|
+
return { entries: new Map(), trusted: false, stale: current !== null || state.value !== null, under };
|
|
168
|
+
}
|
|
169
|
+
/** A listing's key among a round's: its source, its prefix and its depth. */
|
|
170
|
+
function listingKey(source, prefix, topFolderOnly) {
|
|
171
|
+
return `${source}\u0000${prefix}\u0000${topFolderOnly ? 'top' : 'all'}`;
|
|
172
|
+
}
|
|
173
|
+
/** The files a mapping's prefix holds, as its source lists them: once a round, for every mapping that lists it. */
|
|
174
|
+
async function listed(sync, mapping, prefix, topFolderOnly) {
|
|
175
|
+
const key = listingKey(mapping.source, prefix, topFolderOnly);
|
|
176
|
+
const shared = sync.shared.get(key);
|
|
177
|
+
const listing = () => readerOf(sync, mapping).list(prefix, { topFolderOnly });
|
|
178
|
+
if (shared === undefined)
|
|
179
|
+
return listing();
|
|
180
|
+
if (shared.files === undefined) {
|
|
181
|
+
const files = [];
|
|
182
|
+
for await (const object of listing())
|
|
183
|
+
files.push(object);
|
|
184
|
+
shared.files = files;
|
|
185
|
+
}
|
|
186
|
+
const { files } = shared;
|
|
187
|
+
shared.remaining -= 1;
|
|
188
|
+
if (shared.remaining === 0)
|
|
189
|
+
sync.shared.delete(key);
|
|
190
|
+
return files;
|
|
191
|
+
}
|
|
192
|
+
/** A folder's files to read, in batches of at most `maxBytes` (a file larger alone in its own). */
|
|
193
|
+
function batched(files, maxBytes) {
|
|
194
|
+
const batches = [];
|
|
195
|
+
let batch = [];
|
|
196
|
+
let bytes = 0;
|
|
197
|
+
for (const file of files) {
|
|
198
|
+
if (batch.length > 0 && (bytes + file.size > maxBytes || batch.length >= FOLDER_BATCH_FILES)) {
|
|
199
|
+
batches.push(batch);
|
|
200
|
+
batch = [];
|
|
201
|
+
bytes = 0;
|
|
202
|
+
}
|
|
203
|
+
batch.push(file);
|
|
204
|
+
bytes += file.size;
|
|
205
|
+
}
|
|
206
|
+
if (batch.length > 0)
|
|
207
|
+
batches.push(batch);
|
|
208
|
+
return batches;
|
|
209
|
+
}
|
|
210
|
+
/** The files a selection holds: under its prefix, matching its glob, keyed by their path relative to the prefix. */
|
|
211
|
+
async function selection(sync, mapping, prefix, include) {
|
|
212
|
+
const matches = globMatcher(include);
|
|
213
|
+
const files = new Map();
|
|
214
|
+
for await (const object of await listed(sync, mapping, prefix, matchesTopFolderOnly(include))) {
|
|
215
|
+
const path = relative(prefix, object.key);
|
|
216
|
+
// A key ending with / is a folder's placeholder, not a file
|
|
217
|
+
if (path === '' || object.key.endsWith('/'))
|
|
218
|
+
continue;
|
|
219
|
+
if (matches(path))
|
|
220
|
+
files.set(path, object);
|
|
221
|
+
}
|
|
222
|
+
return files;
|
|
223
|
+
}
|
|
224
|
+
/** Syncs a `file` or `latest` mapping to the one file it selects, or none. */
|
|
225
|
+
async function syncOne(sync, mapping, chosen, missing) {
|
|
226
|
+
const { options } = sync;
|
|
227
|
+
const report = emptyTally();
|
|
228
|
+
if (chosen === null) {
|
|
229
|
+
report.refused.push(missing);
|
|
230
|
+
return reportOf(mapping, report);
|
|
231
|
+
}
|
|
232
|
+
const prior = await before(sync, mapping);
|
|
233
|
+
if (same(prior.entries.get(chosen.key), entryOf(chosen))) {
|
|
234
|
+
report.unchanged = 1n;
|
|
235
|
+
return reportOf(mapping, report);
|
|
236
|
+
}
|
|
237
|
+
if (chosen.size > FILES_FILE_MAX_BYTES) {
|
|
238
|
+
report.refused.push({ key: chosen.key, reason: `is ${chosen.size} bytes, over the ${FILES_FILE_MAX_BYTES} a file input takes` });
|
|
239
|
+
return reportOf(mapping, report);
|
|
240
|
+
}
|
|
241
|
+
const taken = () => {
|
|
242
|
+
if (!prior.trusted && !prior.stale)
|
|
243
|
+
report.added = 1n;
|
|
244
|
+
else
|
|
245
|
+
report.changed = 1n;
|
|
246
|
+
report.bytes = BigInt(chosen.size);
|
|
247
|
+
report.updated = true;
|
|
248
|
+
};
|
|
249
|
+
if (options.dryRun === true) {
|
|
250
|
+
taken();
|
|
251
|
+
return reportOf(mapping, report);
|
|
252
|
+
}
|
|
253
|
+
let bytes;
|
|
254
|
+
try {
|
|
255
|
+
bytes = await readerOf(sync, mapping).read(chosen);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
if (!(err instanceof FilesUnreadableError))
|
|
259
|
+
throw err;
|
|
260
|
+
// The input keeps what it holds, and the next sync tries the file again
|
|
261
|
+
report.refused.push({ key: chosen.key, reason: err.reason });
|
|
262
|
+
return reportOf(mapping, report);
|
|
263
|
+
}
|
|
264
|
+
const value = await options.writer.setFile(mapping.input, bytes);
|
|
265
|
+
await options.ledger.write(mapping.input, { entries: new Map([[chosen.key, entryOf(chosen)]]), value, under: prior.under });
|
|
266
|
+
taken();
|
|
267
|
+
sync.progressed = true;
|
|
268
|
+
return reportOf(mapping, report);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Syncs a `folder` mapping: what is new or changed read and written, what is
|
|
272
|
+
* gone taken out. The files to read are taken in batches, each written and
|
|
273
|
+
* remembered before the next is read; the round may stop between batches.
|
|
274
|
+
* What is gone goes with the first batch; an input the sync does not trust is
|
|
275
|
+
* made again, from the first batch alone, then added to. A file its reader
|
|
276
|
+
* cannot read is refused: what the input held of it stays, and so does what
|
|
277
|
+
* the ledger remembers of it, so the next sync tries it again.
|
|
278
|
+
*/
|
|
279
|
+
async function syncFolder(sync, mapping, prefix, files) {
|
|
280
|
+
const { options } = sync;
|
|
281
|
+
const report = emptyTally();
|
|
282
|
+
const prior = await before(sync, mapping);
|
|
283
|
+
const binding = bindingOf(mapping, options);
|
|
284
|
+
// On a local repository, `latest` keeps only the greatest keys
|
|
285
|
+
let kept = files;
|
|
286
|
+
if (binding.latest.type === 'some') {
|
|
287
|
+
const keys = [...files.keys()].sort();
|
|
288
|
+
kept = new Map(keys.slice(Math.max(0, keys.length - Number(binding.latest.value))).map((path) => [path, files.get(path)]));
|
|
289
|
+
}
|
|
290
|
+
// The plan: what is read, what stays as it is, what goes
|
|
291
|
+
const toRead = [];
|
|
292
|
+
const staying = new Set();
|
|
293
|
+
for (const object of kept.values()) {
|
|
294
|
+
const previous = prior.entries.get(object.key);
|
|
295
|
+
if (same(previous, entryOf(object))) {
|
|
296
|
+
report.unchanged += 1n;
|
|
297
|
+
staying.add(object.key);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (object.size > FILES_FOLDER_ENTRY_MAX_BYTES) {
|
|
301
|
+
report.refused.push({ key: object.key, reason: `is ${object.size} bytes, over the ${FILES_FOLDER_ENTRY_MAX_BYTES} a folder's file may be` });
|
|
302
|
+
// What the input held of it, if anything, stays
|
|
303
|
+
if (previous !== undefined)
|
|
304
|
+
staying.add(object.key);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
toRead.push(object);
|
|
308
|
+
}
|
|
309
|
+
const reading = new Set(toRead.map((object) => object.key));
|
|
310
|
+
const gone = [...prior.entries.keys()].filter((key) => !staying.has(key) && !reading.has(key));
|
|
311
|
+
// An input the sync does not trust is made again from the files alone, even when there are none
|
|
312
|
+
const fresh = !prior.trusted;
|
|
313
|
+
if (toRead.length === 0 && gone.length === 0 && !fresh)
|
|
314
|
+
return reportOf(mapping, report);
|
|
315
|
+
if (options.dryRun === true) {
|
|
316
|
+
for (const object of toRead) {
|
|
317
|
+
if (prior.entries.has(object.key))
|
|
318
|
+
report.changed += 1n;
|
|
319
|
+
else
|
|
320
|
+
report.added += 1n;
|
|
321
|
+
report.bytes += BigInt(object.size);
|
|
322
|
+
}
|
|
323
|
+
report.removed = BigInt(gone.length);
|
|
324
|
+
report.updated = true;
|
|
325
|
+
return reportOf(mapping, report);
|
|
326
|
+
}
|
|
327
|
+
const reader = readerOf(sync, mapping);
|
|
328
|
+
const entries = new Map(prior.entries);
|
|
329
|
+
const batches = batched(toRead, options.batchBytes ?? FOLDER_BATCH_BYTES);
|
|
330
|
+
let first = true;
|
|
331
|
+
for (const batch of batches.length === 0 ? [[]] : batches) {
|
|
332
|
+
if (!first && stopping(sync))
|
|
333
|
+
break;
|
|
334
|
+
const set = new Map();
|
|
335
|
+
const read = [];
|
|
336
|
+
for (const object of batch) {
|
|
337
|
+
try {
|
|
338
|
+
set.set(relative(prefix, object.key), await reader.read(object));
|
|
339
|
+
read.push(object);
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
if (!(err instanceof FilesUnreadableError))
|
|
343
|
+
throw err;
|
|
344
|
+
report.refused.push({ key: object.key, reason: err.reason });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const remove = first ? new Set(gone.map((key) => relative(prefix, key))) : new Set();
|
|
348
|
+
const makes = first && fresh;
|
|
349
|
+
// A batch whose files were all refused changes nothing: no write
|
|
350
|
+
if (set.size > 0 || remove.size > 0 || makes) {
|
|
351
|
+
const value = await options.writer.updateFolder(mapping.input, { set, remove, fresh: makes });
|
|
352
|
+
if (first) {
|
|
353
|
+
for (const key of gone)
|
|
354
|
+
entries.delete(key);
|
|
355
|
+
report.removed = BigInt(gone.length);
|
|
356
|
+
}
|
|
357
|
+
for (const object of read) {
|
|
358
|
+
if (prior.entries.has(object.key))
|
|
359
|
+
report.changed += 1n;
|
|
360
|
+
else
|
|
361
|
+
report.added += 1n;
|
|
362
|
+
report.bytes += BigInt(object.size);
|
|
363
|
+
entries.set(object.key, entryOf(object));
|
|
364
|
+
}
|
|
365
|
+
report.updated = true;
|
|
366
|
+
await options.ledger.write(mapping.input, { entries: new Map(entries), value, under: prior.under });
|
|
367
|
+
sync.progressed = true;
|
|
368
|
+
}
|
|
369
|
+
first = false;
|
|
370
|
+
}
|
|
371
|
+
return reportOf(mapping, report);
|
|
372
|
+
}
|
|
373
|
+
/** Syncs a `listing` mapping: written whole whenever any file is new, changed or gone. */
|
|
374
|
+
async function syncListing(sync, mapping, files) {
|
|
375
|
+
const { options } = sync;
|
|
376
|
+
const report = emptyTally();
|
|
377
|
+
const prior = await before(sync, mapping);
|
|
378
|
+
const after = new Map();
|
|
379
|
+
const rows = new Map();
|
|
380
|
+
for (const [path, object] of files) {
|
|
381
|
+
const previous = prior.entries.get(object.key);
|
|
382
|
+
if (previous === undefined)
|
|
383
|
+
report.added += 1n;
|
|
384
|
+
else if (same(previous, entryOf(object)))
|
|
385
|
+
report.unchanged += 1n;
|
|
386
|
+
else
|
|
387
|
+
report.changed += 1n;
|
|
388
|
+
after.set(object.key, entryOf(object));
|
|
389
|
+
rows.set(path, { size: BigInt(object.size), modified: object.modified, etag: object.etag });
|
|
390
|
+
}
|
|
391
|
+
for (const key of prior.entries.keys())
|
|
392
|
+
if (!after.has(key))
|
|
393
|
+
report.removed += 1n;
|
|
394
|
+
report.updated = report.added > 0n || report.changed > 0n || report.removed > 0n || !prior.trusted;
|
|
395
|
+
if (options.dryRun === true || !report.updated)
|
|
396
|
+
return reportOf(mapping, report);
|
|
397
|
+
const value = await options.writer.setListing(mapping.input, rows);
|
|
398
|
+
await options.ledger.write(mapping.input, { entries: after, value, under: prior.under });
|
|
399
|
+
sync.progressed = true;
|
|
400
|
+
return reportOf(mapping, report);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Brings each mapping's input up to date with its source's files, stopping
|
|
404
|
+
* when asked between inputs or between a folder's batches: one round of a
|
|
405
|
+
* sync that hands over, as the platform's do. What it stopped before, the
|
|
406
|
+
* next round takes.
|
|
407
|
+
*
|
|
408
|
+
* @param options - The declarations, the bindings, what reads, writes and remembers, and when to stop
|
|
409
|
+
* @returns What it did, per input it reached, and whether it stopped before the end
|
|
410
|
+
* @throws {FilesSyncError} When a mapping's source is not bound, before
|
|
411
|
+
* anything is read
|
|
412
|
+
* @throws {Error} When an input named is not one a declaration fills
|
|
413
|
+
*/
|
|
414
|
+
export async function syncFilesRound(options) {
|
|
415
|
+
const now = options.now ?? (() => new Date());
|
|
416
|
+
const started = now();
|
|
417
|
+
const declared = new Set(options.manifest.mappings.map((m) => m.input));
|
|
418
|
+
const unknown = (options.inputs ?? []).filter((input) => !declared.has(input));
|
|
419
|
+
if (unknown.length > 0) {
|
|
420
|
+
throw new Error(`No declaration fills ${unknown.map((i) => `'${i}'`).join(', ')}: the inputs files fill are ${[...declared].join(', ') || 'none'}`);
|
|
421
|
+
}
|
|
422
|
+
const wanted = options.inputs === undefined ? null : new Set(options.inputs);
|
|
423
|
+
const mappings = options.manifest.mappings.filter((m) => wanted === null || wanted.has(m.input));
|
|
424
|
+
const bound = new Set(options.bindings.map((b) => b.source));
|
|
425
|
+
const problems = [...new Set(mappings.map((m) => m.source))]
|
|
426
|
+
.filter((source) => !bound.has(source))
|
|
427
|
+
.map((source) => variant('source_unbound', { source }));
|
|
428
|
+
if (problems.length > 0)
|
|
429
|
+
throw new FilesSyncError(problems);
|
|
430
|
+
// The listings more than one mapping reads, each listed once
|
|
431
|
+
const listings = new Map();
|
|
432
|
+
for (const { source, shape } of mappings) {
|
|
433
|
+
if (shape.type === 'file')
|
|
434
|
+
continue;
|
|
435
|
+
const key = listingKey(source, shape.value.prefix, matchesTopFolderOnly(shape.value.include.type === 'some' ? shape.value.include.value : null));
|
|
436
|
+
listings.set(key, (listings.get(key) ?? 0) + 1);
|
|
437
|
+
}
|
|
438
|
+
const shared = new Map([...listings].filter(([, uses]) => uses > 1).map(([key, uses]) => [key, { remaining: uses }]));
|
|
439
|
+
const sync = { options, readers: new Map(), shared, progressed: false, yielded: false };
|
|
440
|
+
const reports = [];
|
|
441
|
+
for (const mapping of mappings) {
|
|
442
|
+
if (stopping(sync))
|
|
443
|
+
break;
|
|
444
|
+
const shape = mapping.shape;
|
|
445
|
+
if (shape.type === 'file') {
|
|
446
|
+
const missing = { key: shape.value.key, reason: 'no such file' };
|
|
447
|
+
reports.push(await syncOne(sync, mapping, await readerOf(sync, mapping).stat(shape.value.key), missing));
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const { prefix } = shape.value;
|
|
451
|
+
const files = await selection(sync, mapping, prefix, shape.value.include.type === 'some' ? shape.value.include.value : null);
|
|
452
|
+
switch (shape.type) {
|
|
453
|
+
case 'latest': {
|
|
454
|
+
const greatest = [...files.keys()].sort().at(-1);
|
|
455
|
+
const missing = { key: prefix, reason: 'no file under it matches' };
|
|
456
|
+
reports.push(await syncOne(sync, mapping, greatest === undefined ? null : files.get(greatest), missing));
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case 'folder':
|
|
460
|
+
reports.push(await syncFolder(sync, mapping, prefix, files));
|
|
461
|
+
break;
|
|
462
|
+
case 'listing':
|
|
463
|
+
reports.push(await syncListing(sync, mapping, files));
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return { report: { started, finished: now(), dryRun: options.dryRun === true, mappings: reports }, yielded: sync.yielded };
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Brings each mapping's input up to date with its source's files, to the end.
|
|
471
|
+
*
|
|
472
|
+
* @param options - The declarations, the bindings, and what reads, writes and remembers
|
|
473
|
+
* @returns What it did, per mapping
|
|
474
|
+
* @throws {FilesSyncError} When a mapping's source is not bound, before
|
|
475
|
+
* anything is read
|
|
476
|
+
* @throws {Error} When an input named is not one a declaration fills
|
|
477
|
+
*/
|
|
478
|
+
export async function syncFiles(options) {
|
|
479
|
+
return (await syncFilesRound(options)).report;
|
|
480
|
+
}
|
|
481
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/sync/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `@elaraai/e3-cloud-files/sync`: the sync, with what reads a bucket, writes
|
|
7
|
+
* a workspace and remembers what each input took given to it, so a local
|
|
8
|
+
* repository and a deployed workspace sync the same way; and those three in
|
|
9
|
+
* memory, for tests.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export { syncFiles, syncFilesRound, FilesSyncError, FilesUnreadableError, FOLDER_BATCH_BYTES, NO_LEDGER, ledgerStateOf, ledgerValueOf, takenUnder, type SyncOptions, type SyncRoundOptions, type FilesSyncRound, type FilesListOptions, type FilesObject, type FilesReader, type FilesWriter, type FolderChanges, type FilesLedger, type LedgerEntry, type LedgerState, } from './engine.js';
|
|
14
|
+
export { MemoryBucket, MemoryWorkspace, MemoryLedger } from './memory.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EACL,SAAS,EACT,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,aAAa,EACb,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `@elaraai/e3-cloud-files/sync`: the sync, with what reads a bucket, writes
|
|
7
|
+
* a workspace and remembers what each input took given to it, so a local
|
|
8
|
+
* repository and a deployed workspace sync the same way; and those three in
|
|
9
|
+
* memory, for tests.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export { syncFiles, syncFilesRound, FilesSyncError, FilesUnreadableError, FOLDER_BATCH_BYTES, NO_LEDGER, ledgerStateOf, ledgerValueOf, takenUnder, } from './engine.js';
|
|
14
|
+
export { MemoryBucket, MemoryWorkspace, MemoryLedger } from './memory.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AAEH,OAAO,EACL,SAAS,EACT,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,aAAa,EACb,UAAU,GAYX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Licensed under BSL 1.1. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The sync's ports in memory: a bucket, a workspace's inputs and a ledger,
|
|
7
|
+
* for unit tests and for the portable suite every implementation runs.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import type { FilesListingRow } from '@elaraai/e3-cloud-types';
|
|
12
|
+
import { type FilesLedger, type FilesReader, type FilesWriter, type FolderChanges, type LedgerState } from './engine.js';
|
|
13
|
+
/** A bucket in memory: files by key, each with its time and an ETag that changes with its bytes. */
|
|
14
|
+
export declare class MemoryBucket {
|
|
15
|
+
#private;
|
|
16
|
+
/** How many times a file's bytes were read, for tests that hold a sync to reading only what changed */
|
|
17
|
+
reads: number;
|
|
18
|
+
/** How many listings were asked for, for tests that hold a sync to listing a prefix once */
|
|
19
|
+
listings: number;
|
|
20
|
+
/**
|
|
21
|
+
* Writes a file, as an upload would.
|
|
22
|
+
*
|
|
23
|
+
* @param key - Its key, from the bucket's root
|
|
24
|
+
* @param bytes - Its bytes, or text as UTF-8
|
|
25
|
+
* @param modified - When it was written
|
|
26
|
+
*/
|
|
27
|
+
put(key: string, bytes: Uint8Array | string, modified?: Date): void;
|
|
28
|
+
/** Deletes a file. */
|
|
29
|
+
delete(key: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Makes a file unreadable, as an archived file, or one encrypted with a key
|
|
32
|
+
* the reader may not use, is: listed, but refused when read.
|
|
33
|
+
*
|
|
34
|
+
* @param key - Its key, from the bucket's root
|
|
35
|
+
* @param reason - Why, as the reader says it; `null` makes it readable again
|
|
36
|
+
*/
|
|
37
|
+
refuse(key: string, reason: string | null): void;
|
|
38
|
+
/**
|
|
39
|
+
* A reader of the files under a prefix, as a binding to `s3://bucket/<prefix>` reads them.
|
|
40
|
+
* A file put after the reader is made is listed by it, as a bucket would list it.
|
|
41
|
+
*
|
|
42
|
+
* @param prefix - The binding's prefix: `''`, or ending with `/`
|
|
43
|
+
* @returns The reader, whose keys are relative to the prefix
|
|
44
|
+
*/
|
|
45
|
+
reader(prefix?: string): FilesReader;
|
|
46
|
+
}
|
|
47
|
+
/** A workspace's inputs in memory: what a sync wrote to each, and a hash of each value. */
|
|
48
|
+
export declare class MemoryWorkspace implements FilesWriter {
|
|
49
|
+
#private;
|
|
50
|
+
/** Each `file` or `latest` input's bytes */
|
|
51
|
+
readonly files: Map<string, Uint8Array<ArrayBufferLike>>;
|
|
52
|
+
/** Each `folder` input's entries */
|
|
53
|
+
readonly folders: Map<string, Map<string, Uint8Array<ArrayBufferLike>>>;
|
|
54
|
+
/** Each `listing` input's rows */
|
|
55
|
+
readonly listings: Map<string, ReadonlyMap<string, {
|
|
56
|
+
readonly size: bigint;
|
|
57
|
+
readonly modified: Date;
|
|
58
|
+
readonly etag: string;
|
|
59
|
+
}>>;
|
|
60
|
+
/** How many times each input was written */
|
|
61
|
+
readonly writes: Map<string, number>;
|
|
62
|
+
/** Empties an input, as a deploy that resets the workspace's inputs does. */
|
|
63
|
+
reset(input: string): void;
|
|
64
|
+
current(input: string): Promise<string | null>;
|
|
65
|
+
setFile(input: string, bytes: Uint8Array): Promise<string>;
|
|
66
|
+
updateFolder(input: string, changes: FolderChanges): Promise<string>;
|
|
67
|
+
setListing(input: string, rows: ReadonlyMap<string, FilesListingRow>): Promise<string>;
|
|
68
|
+
}
|
|
69
|
+
/** A ledger in memory. */
|
|
70
|
+
export declare class MemoryLedger implements FilesLedger {
|
|
71
|
+
#private;
|
|
72
|
+
read(input: string): Promise<LedgerState>;
|
|
73
|
+
write(input: string, state: LedgerState): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/sync/memory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAmC,KAAK,WAAW,EAAoB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAqB5K,oGAAoG;AACpG,qBAAa,YAAY;;IAKvB,uGAAuG;IACvG,KAAK,SAAK;IACV,4FAA4F;IAC5F,QAAQ,SAAK;IAEb;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,QAAQ,GAAE,IAAiB,GAAG,IAAI;IAM/E,sBAAsB;IACtB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;;;;;OAMG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKhD;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,SAAK,GAAG,WAAW;CA8BjC;AAED,2FAA2F;AAC3F,qBAAa,eAAgB,YAAW,WAAW;;IACjD,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,2CAAiC;IAC/C,oCAAoC;IACpC,QAAQ,CAAC,OAAO,wDAA8C;IAC9D,kCAAkC;IAClC,QAAQ,CAAC,QAAQ;;;;QAA2D;IAC5E,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,sBAA6B;IAa5C,6EAA6E;IAC7E,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO1B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAK1D,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAQpE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;CAIvF;AAED,0BAA0B;AAC1B,qBAAa,YAAa,YAAW,WAAW;;IAG9C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIzC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAIxD"}
|