@nimbus-sh/platform 0.8.2 → 0.14.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/dist/composition.d.ts +3 -2
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +34 -19
- package/dist/crc32.d.ts +5 -5
- package/dist/crc32.d.ts.map +1 -1
- package/dist/crc32.js +8 -12
- package/dist/diag-counters.d.ts +18 -10
- package/dist/diag-counters.d.ts.map +1 -1
- package/dist/diag-counters.js +7 -20
- package/dist/heap-estimate.d.ts +5 -20
- package/dist/heap-estimate.d.ts.map +1 -1
- package/dist/heap-estimate.js +6 -33
- package/dist/heavy-alloc-coord.d.ts +131 -11
- package/dist/heavy-alloc-coord.d.ts.map +1 -1
- package/dist/heavy-alloc-coord.js +241 -22
- package/dist/hex.d.ts +20 -0
- package/dist/hex.d.ts.map +1 -0
- package/dist/hex.js +29 -0
- package/dist/install-phase.d.ts +1 -15
- package/dist/install-phase.d.ts.map +1 -1
- package/dist/lexical-path.d.ts +11 -0
- package/dist/lexical-path.d.ts.map +1 -0
- package/dist/lexical-path.js +19 -0
- package/dist/limits.d.ts +4 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +25 -0
- package/dist/module-source.d.ts.map +1 -1
- package/dist/module-source.js +2 -1
- package/dist/oom-classify.d.ts +4 -0
- package/dist/oom-classify.d.ts.map +1 -1
- package/dist/oom-classify.js +6 -0
- package/dist/realm-builtin.d.ts +2 -0
- package/dist/realm-builtin.d.ts.map +1 -0
- package/dist/realm-builtin.js +11 -0
- package/dist/utf8.d.ts +21 -1
- package/dist/utf8.d.ts.map +1 -1
- package/dist/utf8.js +73 -1
- package/dist/w7-frame.d.ts +92 -17
- package/dist/w7-frame.d.ts.map +1 -1
- package/dist/w7-frame.js +87 -35
- package/dist/wave-writer.d.ts +11 -6
- package/dist/wave-writer.d.ts.map +1 -1
- package/dist/wave-writer.js +28 -29
- package/dist/weighted-credit-pool.d.ts +43 -0
- package/dist/weighted-credit-pool.d.ts.map +1 -1
- package/dist/weighted-credit-pool.js +250 -46
- package/dist/worker-code.d.ts +48 -0
- package/dist/worker-code.d.ts.map +1 -0
- package/dist/worker-code.js +10 -0
- package/package.json +2 -2
- package/src/composition.ts +41 -21
- package/src/crc32.ts +9 -12
- package/src/diag-counters.ts +23 -22
- package/src/heap-estimate.ts +11 -40
- package/src/heavy-alloc-coord.ts +275 -20
- package/src/hex.ts +29 -0
- package/src/install-phase.ts +1 -15
- package/src/lexical-path.ts +17 -0
- package/src/limits.ts +29 -0
- package/src/module-source.ts +3 -1
- package/src/oom-classify.ts +8 -0
- package/src/realm-builtin.ts +22 -0
- package/src/utf8.ts +75 -1
- package/src/w7-frame.ts +152 -53
- package/src/wave-writer.ts +28 -27
- package/src/weighted-credit-pool.ts +256 -51
- package/src/worker-code.ts +38 -0
package/dist/utf8.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* utf8.ts — the UTF-8 length of a string,
|
|
2
|
+
* utf8.ts — the UTF-8 length of a string, and the order of two strings by
|
|
3
|
+
* their UTF-8 bytes, without encoding them.
|
|
3
4
|
*
|
|
4
5
|
* Bounds on names and payloads are in bytes, and the hot ones (every path a
|
|
5
6
|
* W7 wave or a listing page carries) were measured by encoding the string
|
|
@@ -7,7 +8,20 @@
|
|
|
7
8
|
* TextEncoder would write: one to four per code point, and three for a
|
|
8
9
|
* lone surrogate, which it writes as U+FFFD.
|
|
9
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* From this many UTF-16 units, a string is counted natively (longUtf8Length),
|
|
13
|
+
* into a scratch buffer of SCRATCH_BYTES. The resident store's count on the
|
|
14
|
+
* launch's captures (worker vfs/resident/cell-cost.ts) keeps the same policy,
|
|
15
|
+
* and tests/unit/utf8-length.mjs holds both to TextEncoder.
|
|
16
|
+
*/
|
|
17
|
+
export const UTF8_LONG_UNITS = 256;
|
|
18
|
+
export const UTF8_SCRATCH_BYTES = 256 * 1024;
|
|
19
|
+
const encoder = new TextEncoder();
|
|
20
|
+
/** Where longUtf8Length's encodes land: written, never read. */
|
|
21
|
+
let scratch;
|
|
10
22
|
export function utf8Length(text) {
|
|
23
|
+
if (text.length >= UTF8_LONG_UNITS)
|
|
24
|
+
return longUtf8Length(text);
|
|
11
25
|
let bytes = text.length;
|
|
12
26
|
for (let index = 0; index < text.length; index++) {
|
|
13
27
|
const unit = text.charCodeAt(index);
|
|
@@ -33,3 +47,61 @@ export function utf8Length(text) {
|
|
|
33
47
|
}
|
|
34
48
|
return bytes;
|
|
35
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* A long string's UTF-8 length at the engine's speed: encoded a slice at a
|
|
52
|
+
* time into one reused buffer, only the count kept. encodeInto never splits
|
|
53
|
+
* a surrogate pair, so each slice starts on a code point.
|
|
54
|
+
*/
|
|
55
|
+
function longUtf8Length(text) {
|
|
56
|
+
scratch ??= new Uint8Array(UTF8_SCRATCH_BYTES);
|
|
57
|
+
let bytes = 0;
|
|
58
|
+
for (let read = 0; read < text.length;) {
|
|
59
|
+
const done = encoder.encodeInto(read === 0 ? text : text.slice(read), scratch);
|
|
60
|
+
read += done.read;
|
|
61
|
+
bytes += done.written;
|
|
62
|
+
}
|
|
63
|
+
return bytes;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The code point at `index` of `text`: a pair is one past U+FFFF, and a lone
|
|
67
|
+
* surrogate is its own (as WTF-8 writes it, between U+D7FF and U+E000).
|
|
68
|
+
*/
|
|
69
|
+
function codePointAt(text, index) {
|
|
70
|
+
const unit = text.charCodeAt(index);
|
|
71
|
+
if (unit < 0xd800 || unit > 0xdbff || index + 1 >= text.length)
|
|
72
|
+
return unit;
|
|
73
|
+
const next = text.charCodeAt(index + 1);
|
|
74
|
+
return next >= 0xdc00 && next <= 0xdfff ? 0x10000 + ((unit - 0xd800) << 10) + (next - 0xdc00) : unit;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `a` against `b` (negative, 0, positive) as their UTF-8 bytes order them,
|
|
78
|
+
* as SQLite's `ORDER BY` of a text column and git's index do: code point
|
|
79
|
+
* order. A string UTF-8 cannot write (a lone surrogate) is ordered as WTF-8
|
|
80
|
+
* writes it, its surrogate a code point of its own: the order is total, as a
|
|
81
|
+
* listing's cursor needs, and decided at the first code point that differs,
|
|
82
|
+
* so a directory's entries follow it together. Units equal in both are
|
|
83
|
+
* passed as they are; two that differ, neither a surrogate, are their own
|
|
84
|
+
* code points; the rest, rare in a path, is surrogatesAt's.
|
|
85
|
+
*/
|
|
86
|
+
export function compareUtf8(a, b) {
|
|
87
|
+
const n = Math.min(a.length, b.length);
|
|
88
|
+
for (let i = 0; i < n; i++) {
|
|
89
|
+
const x = a.charCodeAt(i);
|
|
90
|
+
const y = b.charCodeAt(i);
|
|
91
|
+
if (x === y)
|
|
92
|
+
continue;
|
|
93
|
+
if ((x < 0xd800 || x > 0xdfff) && (y < 0xd800 || y > 0xdfff))
|
|
94
|
+
return x - y;
|
|
95
|
+
return surrogatesAt(a, b, i);
|
|
96
|
+
}
|
|
97
|
+
return a.length - b.length;
|
|
98
|
+
}
|
|
99
|
+
/** compareUtf8 of `a` and `b`, equal before `i`, where they differ at `i` and one unit there is a surrogate. */
|
|
100
|
+
function surrogatesAt(a, b, i) {
|
|
101
|
+
// A low half after a high one the two share: the code points begin at the high one.
|
|
102
|
+
const before = i > 0 ? a.charCodeAt(i - 1) : 0;
|
|
103
|
+
const x = a.charCodeAt(i);
|
|
104
|
+
const y = b.charCodeAt(i);
|
|
105
|
+
const at = before >= 0xd800 && before <= 0xdbff && ((x >= 0xdc00 && x <= 0xdfff) || (y >= 0xdc00 && y <= 0xdfff)) ? i - 1 : i;
|
|
106
|
+
return codePointAt(a, at) - codePointAt(b, at);
|
|
107
|
+
}
|
package/dist/w7-frame.d.ts
CHANGED
|
@@ -65,15 +65,29 @@ export interface BatchWritePayload {
|
|
|
65
65
|
*/
|
|
66
66
|
ops?: W7Op[];
|
|
67
67
|
}
|
|
68
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* One attribute change (setattr): the mode, the owner, or the times; what
|
|
70
|
+
* chmod, chown and utimes each make. An owner side that is null keeps what
|
|
71
|
+
* the file has when the change lands (chown's -1).
|
|
72
|
+
*/
|
|
73
|
+
/**
|
|
74
|
+
* A time utimes and futimes set, as utimensat(2) takes it: ms since the
|
|
75
|
+
* epoch (explicit: only the owner may set it), 'now' (UTIME_NOW: the
|
|
76
|
+
* clock of the filesystem that commits the change, as it commits it) or
|
|
77
|
+
* 'omit' (UTIME_OMIT: left as it is then). 'now' and 'omit' need the owner or write
|
|
78
|
+
* permission. Carried as it is to the filesystem that commits the change,
|
|
79
|
+
* which alone resolves it (posix-access.ts, timeAt) and judges it
|
|
80
|
+
* (utimesRefusal, explicitTimes).
|
|
81
|
+
*/
|
|
82
|
+
export type FsTime = number | 'now' | 'omit';
|
|
69
83
|
export type W7Attrs = {
|
|
70
84
|
mode: number;
|
|
71
85
|
} | {
|
|
72
|
-
uid: number;
|
|
73
|
-
gid: number;
|
|
86
|
+
uid: number | null;
|
|
87
|
+
gid: number | null;
|
|
74
88
|
} | {
|
|
75
|
-
atime:
|
|
76
|
-
mtime:
|
|
89
|
+
atime: FsTime;
|
|
90
|
+
mtime: FsTime;
|
|
77
91
|
};
|
|
78
92
|
/**
|
|
79
93
|
* A process's filesystem call as a record (v4), applied by the session's own
|
|
@@ -84,6 +98,32 @@ export type W7Attrs = {
|
|
|
84
98
|
* nothing makes a missing parent. The program-order counterpart of the
|
|
85
99
|
* syscall, where `file` and `directory` are a checkout's upserts.
|
|
86
100
|
*/
|
|
101
|
+
/** What a W7 open asks of its file, as open(2)'s O_ACCMODE: to read it, to write it, or both. */
|
|
102
|
+
export type W7OpenAccess = 'read' | 'write' | 'readwrite';
|
|
103
|
+
/** The rights an open's access grants its description: what a call through it may do. */
|
|
104
|
+
export declare function openAccessRights(access: W7OpenAccess): {
|
|
105
|
+
read: boolean;
|
|
106
|
+
write: boolean;
|
|
107
|
+
};
|
|
108
|
+
/** The permission bits an open asks of the file it reaches (4 read, 2 write): its access's, and write under `truncate`, as open(2)'s. */
|
|
109
|
+
export declare function openAccessMask(call: {
|
|
110
|
+
access: W7OpenAccess;
|
|
111
|
+
truncate?: true;
|
|
112
|
+
}): number;
|
|
113
|
+
/**
|
|
114
|
+
* What a process decided a call on from its own view of the name: the file
|
|
115
|
+
* it expected there (its number on the session's filesystem), none (null),
|
|
116
|
+
* or the file its open of description `made` reached, for a file that open
|
|
117
|
+
* made and the process has no number of yet (it keeps that description
|
|
118
|
+
* open until it has). Where the name leads elsewhere now (a stale view: a
|
|
119
|
+
* peer replaced the name, or a link there leads to another file; or that
|
|
120
|
+
* open was refused), the session refuses the call before any effect:
|
|
121
|
+
* ENOENT where it expected a file, EEXIST where it expected none, EXDEV
|
|
122
|
+
* where the name leads to a mount.
|
|
123
|
+
*/
|
|
124
|
+
export type W7Expected = number | null | {
|
|
125
|
+
readonly made: string;
|
|
126
|
+
};
|
|
87
127
|
export type W7Call =
|
|
88
128
|
/**
|
|
89
129
|
* `ino`, on a call that makes a name (writeFile, appendFile, mkdir,
|
|
@@ -153,15 +193,29 @@ export type W7Call =
|
|
|
153
193
|
} | {
|
|
154
194
|
call: 'unlink';
|
|
155
195
|
path: string;
|
|
196
|
+
expect?: W7Expected;
|
|
156
197
|
} | {
|
|
157
198
|
call: 'rmdir';
|
|
158
199
|
path: string;
|
|
200
|
+
expect?: W7Expected;
|
|
159
201
|
} | {
|
|
160
202
|
call: 'symlink';
|
|
161
203
|
path: string;
|
|
162
204
|
target: string;
|
|
163
205
|
ino?: number;
|
|
164
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* copyfile as the session copies a file: the file \`from\` names (a link
|
|
209
|
+
* followed) to \`path\` (a link there followed, a file there replaced),
|
|
210
|
+
* by reference within one filesystem and by its bytes across, no byte of
|
|
211
|
+
* it in the wave. The copy's mode is a copy's there: a new file the
|
|
212
|
+
* source's less the umask, a file there its own.
|
|
213
|
+
*/
|
|
214
|
+
| {
|
|
215
|
+
call: 'copyFile';
|
|
216
|
+
path: string;
|
|
217
|
+
from: string;
|
|
218
|
+
}
|
|
165
219
|
/**
|
|
166
220
|
* rm(1) as fs.rm makes it: a file or link unlinked, a directory with all
|
|
167
221
|
* it holds when `recursive` (else EISDIR's refusal, as unlink's), and a
|
|
@@ -177,8 +231,8 @@ export type W7Call =
|
|
|
177
231
|
| {
|
|
178
232
|
call: 'lchown';
|
|
179
233
|
path: string;
|
|
180
|
-
uid: number;
|
|
181
|
-
gid: number;
|
|
234
|
+
uid: number | null;
|
|
235
|
+
gid: number | null;
|
|
182
236
|
}
|
|
183
237
|
/** utimes of the link itself (lutimes). */
|
|
184
238
|
| {
|
|
@@ -188,29 +242,36 @@ export type W7Call =
|
|
|
188
242
|
mtime: number;
|
|
189
243
|
}
|
|
190
244
|
/**
|
|
191
|
-
* open(2) of a file
|
|
192
|
-
* session's own open decides it: a name made
|
|
193
|
-
* `umask`) or refused (EEXIST when `exclusive`,
|
|
194
|
-
* EISDIR, EACCES for any access asked that the
|
|
245
|
+
* open(2) of a file with the access `access` asks, as O_ACCMODE does
|
|
246
|
+
* (W7OpenAccess), as the session's own open decides it: a name made
|
|
247
|
+
* (`create`, `mode` less `umask`) or refused (EEXIST when `exclusive`,
|
|
248
|
+
* ENOENT without `create`, EISDIR, EACCES for any access asked that the
|
|
249
|
+
* file's mode refuses, write too under `truncate`: openAccessMask),
|
|
195
250
|
* emptied when `truncate`, a link at the name followed unless `nofollow`
|
|
196
251
|
* (ELOOP). Its answer is the file's stat. With `description` (an id the
|
|
197
252
|
* process chose), the session keeps the open description under it, its
|
|
198
|
-
* access
|
|
199
|
-
* name it, and its `close` ends it. `ino`: the number
|
|
200
|
-
* given (a delegation's, as a mkdir's).
|
|
253
|
+
* rights the access's (openAccessRights); the description's write, append
|
|
254
|
+
* and ftruncate calls name it, and its `close` ends it. `ino`: the number
|
|
255
|
+
* a name it makes is given (a delegation's, as a mkdir's). `fchmod`: the
|
|
256
|
+
* file it opened then given exactly this mode (fchmod(2), as libuv's
|
|
257
|
+
* copyfile narrows its destination before a byte reaches it), part of the
|
|
258
|
+
* open: refused (EPERM: another's file), the open is refused and keeps no
|
|
259
|
+
* description, so a write through it is EBADF.
|
|
201
260
|
*/
|
|
202
261
|
| {
|
|
203
262
|
call: 'open';
|
|
204
263
|
path: string;
|
|
205
264
|
mode: number;
|
|
265
|
+
access: W7OpenAccess;
|
|
206
266
|
ino?: number;
|
|
207
267
|
umask?: number;
|
|
208
|
-
read?: true;
|
|
209
268
|
create?: true;
|
|
210
269
|
truncate?: true;
|
|
211
270
|
exclusive?: true;
|
|
212
271
|
nofollow?: true;
|
|
213
272
|
description?: string;
|
|
273
|
+
fchmod?: number;
|
|
274
|
+
expect?: W7Expected;
|
|
214
275
|
}
|
|
215
276
|
/** close(2) of the session's open description `description` (an `open` call's). */
|
|
216
277
|
| {
|
|
@@ -241,18 +302,26 @@ export type W7Op = {
|
|
|
241
302
|
type: 'file';
|
|
242
303
|
inode: BatchInodeEntry;
|
|
243
304
|
source: AsyncIterable<Uint8Array>;
|
|
244
|
-
}
|
|
305
|
+
}
|
|
306
|
+
/** `expect`, `expectTo`: the files the process expected at `from` and at `to` (W7Expected). */
|
|
307
|
+
| {
|
|
245
308
|
type: 'rename';
|
|
246
309
|
from: string;
|
|
247
310
|
to: string;
|
|
311
|
+
expect?: W7Expected;
|
|
312
|
+
expectTo?: W7Expected;
|
|
248
313
|
} | {
|
|
249
314
|
type: 'truncate';
|
|
250
315
|
path: string;
|
|
251
316
|
size: number;
|
|
252
|
-
}
|
|
317
|
+
}
|
|
318
|
+
/** `description`: through the session's open description of that id (fchmod, fchown, futimens): its file, whatever names it now. */
|
|
319
|
+
| {
|
|
253
320
|
type: 'setattr';
|
|
254
321
|
path: string;
|
|
255
322
|
attrs: W7Attrs;
|
|
323
|
+
description?: string;
|
|
324
|
+
expect?: W7Expected;
|
|
256
325
|
} | {
|
|
257
326
|
type: 'call';
|
|
258
327
|
call: W7Call;
|
|
@@ -335,6 +404,8 @@ export type W7DecodedRecord = {
|
|
|
335
404
|
type: 'rename';
|
|
336
405
|
from: string;
|
|
337
406
|
to: string;
|
|
407
|
+
expect?: W7Expected;
|
|
408
|
+
expectTo?: W7Expected;
|
|
338
409
|
} | {
|
|
339
410
|
type: 'truncate';
|
|
340
411
|
path: string;
|
|
@@ -343,6 +414,8 @@ export type W7DecodedRecord = {
|
|
|
343
414
|
type: 'setattr';
|
|
344
415
|
path: string;
|
|
345
416
|
attrs: W7Attrs;
|
|
417
|
+
description?: string;
|
|
418
|
+
expect?: W7Expected;
|
|
346
419
|
} | {
|
|
347
420
|
type: 'call';
|
|
348
421
|
call: W7PathCall;
|
|
@@ -375,5 +448,7 @@ export declare function encodeWriteBatch(payload: BatchWritePayload): Promise<Ui
|
|
|
375
448
|
* and before its payload bytes are read or copied.
|
|
376
449
|
*/
|
|
377
450
|
export declare function decodeWriteBatchStream(stream: ReadableStream<Uint8Array>, options?: W7DecodeOptions): Promise<W7DecodedStream>;
|
|
451
|
+
/** `value` as an FsTime (a finite number of milliseconds, 'now' or 'omit'); else an error naming `label`. */
|
|
452
|
+
export declare function parseFsTime(value: unknown, label: string): FsTime;
|
|
378
453
|
export {};
|
|
379
454
|
//# sourceMappingURL=w7-frame.d.ts.map
|
package/dist/w7-frame.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"w7-frame.d.ts","sourceRoot":"","sources":["../src/w7-frame.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"w7-frame.d.ts","sourceRoot":"","sources":["../src/w7-frame.ts"],"names":[],"mappings":"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"}
|
package/dist/w7-frame.js
CHANGED
|
@@ -15,6 +15,19 @@
|
|
|
15
15
|
import { crc32 } from './crc32.js';
|
|
16
16
|
import { CHUNK_SIZE } from './limits.js';
|
|
17
17
|
import { utf8Length } from './utf8.js';
|
|
18
|
+
import { rootClampedSegments } from './lexical-path.js';
|
|
19
|
+
function isOpenAccess(value) {
|
|
20
|
+
return value === 'read' || value === 'write' || value === 'readwrite';
|
|
21
|
+
}
|
|
22
|
+
/** The rights an open's access grants its description: what a call through it may do. */
|
|
23
|
+
export function openAccessRights(access) {
|
|
24
|
+
return { read: access !== 'write', write: access !== 'read' };
|
|
25
|
+
}
|
|
26
|
+
/** The permission bits an open asks of the file it reaches (4 read, 2 write): its access's, and write under `truncate`, as open(2)'s. */
|
|
27
|
+
export function openAccessMask(call) {
|
|
28
|
+
const rights = openAccessRights(call.access);
|
|
29
|
+
return (rights.read ? 4 : 0) | (rights.write || call.truncate === true ? 2 : 0);
|
|
30
|
+
}
|
|
18
31
|
export const W7_MAGIC = new Uint8Array([0x4e, 0x57, 0x37, 0x04]);
|
|
19
32
|
/** v3's magic: decoded, never encoded, for the release that rolls v4 out. Delete with v3 decoding. */
|
|
20
33
|
const W7_MAGIC_V3 = new Uint8Array([0x4e, 0x57, 0x37, 0x03]);
|
|
@@ -372,7 +385,7 @@ v3) {
|
|
|
372
385
|
throw new Error(`w7-frame: unknown record tag ${envelope.tag}`);
|
|
373
386
|
summary.pathCount++;
|
|
374
387
|
summary.opCount++;
|
|
375
|
-
const value = parseObject(payload, 'call', ['call', 'path'], ['mode', 'target', 'ino', 'size', 'existing', 'recursive', 'force', 'uid', 'gid', 'atime', 'mtime', 'umask', '
|
|
388
|
+
const value = parseObject(payload, 'call', ['call', 'path'], ['mode', 'target', 'from', 'ino', 'size', 'existing', 'recursive', 'force', 'uid', 'gid', 'atime', 'mtime', 'umask', 'access', 'create', 'truncate', 'exclusive', 'nofollow', 'description', 'fchmod', 'expect']);
|
|
376
389
|
yield { type: 'call', call: parsePathCall(value, (path, label) => ownedPaths.claim(canonicalPath(path, label))) };
|
|
377
390
|
break;
|
|
378
391
|
}
|
|
@@ -384,20 +397,23 @@ v3) {
|
|
|
384
397
|
summary.pathCount++;
|
|
385
398
|
summary.opCount++;
|
|
386
399
|
if (envelope.tag === RecordTag.Rename) {
|
|
387
|
-
const value = parseObject(payload, 'rename', ['from', 'to']);
|
|
400
|
+
const value = parseObject(payload, 'rename', ['from', 'to'], ['expect', 'expectTo']);
|
|
388
401
|
const from = ownedPaths.claim(canonicalPath(value.from, 'rename source'));
|
|
389
402
|
const to = ownedPaths.claim(canonicalPath(value.to, 'rename target'));
|
|
390
|
-
yield { type: 'rename', from, to };
|
|
403
|
+
yield { type: 'rename', from, to, ...expectations(value, 'rename') };
|
|
391
404
|
}
|
|
392
405
|
else if (envelope.tag === RecordTag.Truncate) {
|
|
393
406
|
const value = parseObject(payload, 'truncate', ['path', 'size']);
|
|
394
407
|
yield { type: 'truncate', path: ownedPaths.claim(canonicalPath(value.path, 'truncate path')), size: safeInteger(value.size, 'truncate size') };
|
|
395
408
|
}
|
|
396
409
|
else {
|
|
397
|
-
const value = parseObject(payload, 'setattr', ['path'], ['mode', 'uid', 'gid', 'atime', 'mtime']);
|
|
410
|
+
const value = parseObject(payload, 'setattr', ['path'], ['mode', 'uid', 'gid', 'atime', 'mtime', 'description', 'expect']);
|
|
398
411
|
const path = ownedPaths.claim(canonicalPath(value.path, 'setattr path'));
|
|
399
|
-
const { path: _path, ...attrs } = value;
|
|
400
|
-
yield {
|
|
412
|
+
const { path: _path, description, expect: _expect, ...attrs } = value;
|
|
413
|
+
yield {
|
|
414
|
+
type: 'setattr', path, attrs: parseAttrs(attrs, 'setattr'),
|
|
415
|
+
...(description === undefined ? {} : { description: descriptionId(description, 'setattr description') }), ...expectations(value, 'setattr'),
|
|
416
|
+
};
|
|
401
417
|
}
|
|
402
418
|
break;
|
|
403
419
|
}
|
|
@@ -470,7 +486,7 @@ async function* encodeRecords(batchId, ops) {
|
|
|
470
486
|
case 'rename':
|
|
471
487
|
state.summary.pathCount++;
|
|
472
488
|
state.summary.opCount++;
|
|
473
|
-
yield encodeMetadataRecord(RecordTag.Rename, { from: op.from, to: op.to }, state);
|
|
489
|
+
yield encodeMetadataRecord(RecordTag.Rename, { from: op.from, to: op.to, ...expectations(op, 'rename') }, state);
|
|
474
490
|
break;
|
|
475
491
|
case 'truncate':
|
|
476
492
|
state.summary.pathCount++;
|
|
@@ -480,7 +496,7 @@ async function* encodeRecords(batchId, ops) {
|
|
|
480
496
|
case 'setattr':
|
|
481
497
|
state.summary.pathCount++;
|
|
482
498
|
state.summary.opCount++;
|
|
483
|
-
yield encodeMetadataRecord(RecordTag.SetAttr, { path: op.path, ...op.attrs }, state);
|
|
499
|
+
yield encodeMetadataRecord(RecordTag.SetAttr, { path: op.path, ...op.attrs, ...(op.description === undefined ? {} : { description: op.description }), ...expectations(op, 'setattr') }, state);
|
|
484
500
|
break;
|
|
485
501
|
case 'call':
|
|
486
502
|
state.summary.pathCount++;
|
|
@@ -513,8 +529,8 @@ async function* encodeFile(file, state) {
|
|
|
513
529
|
let fileCheck = 0;
|
|
514
530
|
let chunkId = 0;
|
|
515
531
|
const pieces = file.source === null ? givenChunks(file.chunks) : fixedChunks(file.inode, file.source);
|
|
532
|
+
const contentBytes = TEXT_ENCODER.encode(file.contentId);
|
|
516
533
|
for await (const data of pieces) {
|
|
517
|
-
const contentBytes = new TextEncoder().encode(file.contentId);
|
|
518
534
|
const prefix = new Uint8Array(4 + contentBytes.length + 8);
|
|
519
535
|
writeU32LE(prefix, 0, contentBytes.length);
|
|
520
536
|
prefix.set(contentBytes, 4);
|
|
@@ -573,16 +589,20 @@ async function* fixedChunks(inode, source) {
|
|
|
573
589
|
}
|
|
574
590
|
}
|
|
575
591
|
function encodeMetadataRecord(tag, value, state) {
|
|
576
|
-
const payload =
|
|
592
|
+
const payload = TEXT_ENCODER.encode(JSON.stringify(value));
|
|
577
593
|
if (payload.byteLength > MAX_METADATA_BYTES) {
|
|
578
594
|
throw new Error(`w7-frame: metadata record exceeds ${MAX_METADATA_BYTES} bytes`);
|
|
579
595
|
}
|
|
580
|
-
|
|
596
|
+
// The header and the payload in one buffer, checked as one (crc32 composes).
|
|
597
|
+
const record = new Uint8Array(5 + payload.byteLength);
|
|
598
|
+
record[0] = tag;
|
|
599
|
+
writeU32LE(record, 1, payload.byteLength);
|
|
600
|
+
record.set(payload, 5);
|
|
581
601
|
if (state && tag !== RecordTag.BatchEnd) {
|
|
582
|
-
state.batchCheck = updateRecordCheck(state.batchCheck,
|
|
602
|
+
state.batchCheck = updateRecordCheck(state.batchCheck, record);
|
|
583
603
|
state.summary.recordCount++;
|
|
584
604
|
}
|
|
585
|
-
return [
|
|
605
|
+
return [record];
|
|
586
606
|
}
|
|
587
607
|
/**
|
|
588
608
|
* The payload's operations in the order they are encoded: its `ops` as
|
|
@@ -685,11 +705,16 @@ function prepareOps(payload, batchId) {
|
|
|
685
705
|
kind: 'rename',
|
|
686
706
|
from: ownedPaths.claim(canonicalPath(op.from, 'rename source')),
|
|
687
707
|
to: ownedPaths.claim(canonicalPath(op.to, 'rename target')),
|
|
708
|
+
...expectations(op, 'rename'),
|
|
688
709
|
};
|
|
689
710
|
case 'truncate':
|
|
690
711
|
return { kind: 'truncate', path: ownedPaths.claim(canonicalPath(op.path, 'truncate path')), size: safeInteger(op.size, 'truncate size') };
|
|
691
712
|
case 'setattr':
|
|
692
|
-
return {
|
|
713
|
+
return {
|
|
714
|
+
kind: 'setattr', path: ownedPaths.claim(canonicalPath(op.path, 'setattr path')), attrs: parseAttrs(op.attrs, 'setattr'),
|
|
715
|
+
...(op.description === undefined ? {} : { description: descriptionId(op.description, 'setattr description') }),
|
|
716
|
+
...expectations(op, 'setattr'),
|
|
717
|
+
};
|
|
693
718
|
case 'call': {
|
|
694
719
|
const call = op.call;
|
|
695
720
|
if ('data' in call) {
|
|
@@ -728,11 +753,19 @@ function parseAttrs(value, label) {
|
|
|
728
753
|
if (keys === 'mode')
|
|
729
754
|
return { mode: u32(value.mode, `${label} mode`) };
|
|
730
755
|
if (keys === 'gid,uid')
|
|
731
|
-
return { uid:
|
|
756
|
+
return { uid: ownerId(value.uid, `${label} uid`), gid: ownerId(value.gid, `${label} gid`) };
|
|
732
757
|
if (keys === 'atime,mtime')
|
|
733
|
-
return { atime:
|
|
758
|
+
return { atime: parseFsTime(value.atime, `w7-frame: ${label} atime`), mtime: parseFsTime(value.mtime, `w7-frame: ${label} mtime`) };
|
|
734
759
|
throw new Error(`w7-frame: ${label} changes the mode, the owner or the times, one of them: got ${keys || 'nothing'}`);
|
|
735
760
|
}
|
|
761
|
+
/** `value` as an FsTime (a finite number of milliseconds, 'now' or 'omit'); else an error naming `label`. */
|
|
762
|
+
export function parseFsTime(value, label) {
|
|
763
|
+
if (value === 'now' || value === 'omit')
|
|
764
|
+
return value;
|
|
765
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
766
|
+
return value;
|
|
767
|
+
throw new Error(`${label} must be a time: milliseconds, 'now' or 'omit'`);
|
|
768
|
+
}
|
|
736
769
|
function parseDelete(bytes) {
|
|
737
770
|
const value = parseObject(bytes, 'delete', ['path']);
|
|
738
771
|
return { path: canonicalPath(value.path, 'delete path') };
|
|
@@ -805,9 +838,9 @@ function parsePathCall(value, path) {
|
|
|
805
838
|
}
|
|
806
839
|
case 'unlink':
|
|
807
840
|
case 'rmdir':
|
|
808
|
-
if (keys !== 'call,path')
|
|
841
|
+
if (keys !== 'call,path' && keys !== 'call,expect,path')
|
|
809
842
|
break;
|
|
810
|
-
return { call: value.call, path: path(value.path, `${value.call} path`) };
|
|
843
|
+
return { call: value.call, path: path(value.path, `${value.call} path`), ...expectations(value, value.call) };
|
|
811
844
|
case 'symlink':
|
|
812
845
|
if (keys !== 'call,path,target' && keys !== 'call,ino,path,target')
|
|
813
846
|
break;
|
|
@@ -815,6 +848,10 @@ function parsePathCall(value, path) {
|
|
|
815
848
|
call: 'symlink', path: path(value.path, 'symlink path'), target: boundedString(value.target, 'symlink target', MAX_PATH_BYTES),
|
|
816
849
|
...(value.ino === undefined ? {} : { ino: inodeNumber(value.ino, 'symlink ino') }),
|
|
817
850
|
};
|
|
851
|
+
case 'copyFile':
|
|
852
|
+
if (keys !== 'call,from,path')
|
|
853
|
+
break;
|
|
854
|
+
return { call: 'copyFile', path: path(value.path, 'copyFile path'), from: path(value.from, 'copyFile from') };
|
|
818
855
|
case 'ftruncate':
|
|
819
856
|
if (keys.replace(',description', '').replace(',ino', '') !== 'call,path,size')
|
|
820
857
|
break;
|
|
@@ -843,28 +880,32 @@ function parsePathCall(value, path) {
|
|
|
843
880
|
case 'lchown':
|
|
844
881
|
if (keys !== 'call,gid,path,uid')
|
|
845
882
|
break;
|
|
846
|
-
return { call: 'lchown', path: path(value.path, 'lchown path'), uid:
|
|
883
|
+
return { call: 'lchown', path: path(value.path, 'lchown path'), uid: ownerId(value.uid, 'lchown uid'), gid: ownerId(value.gid, 'lchown gid') };
|
|
847
884
|
case 'open': {
|
|
848
|
-
const flags = ['create', 'exclusive', 'nofollow', '
|
|
885
|
+
const flags = ['create', 'exclusive', 'nofollow', 'truncate'];
|
|
849
886
|
let required = keys;
|
|
850
|
-
for (const flag of ['create', 'description', 'exclusive', '
|
|
887
|
+
for (const flag of ['create', 'description', 'exclusive', 'expect', 'fchmod', 'ino', 'nofollow', 'truncate', 'umask'])
|
|
851
888
|
required = required.replace(`,${flag}`, '');
|
|
852
|
-
if (required !== 'call,mode,path')
|
|
889
|
+
if (required !== 'access,call,mode,path')
|
|
853
890
|
break;
|
|
854
891
|
for (const flag of flags) {
|
|
855
892
|
if (value[flag] !== undefined && value[flag] !== true)
|
|
856
893
|
throw new Error(`w7-frame: open ${flag} is true or absent`);
|
|
857
894
|
}
|
|
895
|
+
const access = value.access;
|
|
896
|
+
if (!isOpenAccess(access))
|
|
897
|
+
throw new Error(`w7-frame: open access is 'read', 'write' or 'readwrite': got ${String(access)}`);
|
|
858
898
|
return {
|
|
859
|
-
call: 'open', path: path(value.path, 'open path'), mode: u32(value.mode, 'open mode'),
|
|
899
|
+
call: 'open', path: path(value.path, 'open path'), mode: u32(value.mode, 'open mode'), access,
|
|
860
900
|
...(value.ino === undefined ? {} : { ino: inodeNumber(value.ino, 'open ino') }),
|
|
861
901
|
...(value.umask === undefined ? {} : { umask: umaskOf(value.umask, 'open umask') }),
|
|
862
902
|
...(value.create === true ? { create: true } : {}),
|
|
863
903
|
...(value.truncate === true ? { truncate: true } : {}),
|
|
864
904
|
...(value.exclusive === true ? { exclusive: true } : {}),
|
|
865
905
|
...(value.nofollow === true ? { nofollow: true } : {}),
|
|
866
|
-
...(value.read === true ? { read: true } : {}),
|
|
867
906
|
...(value.description === undefined ? {} : { description: descriptionId(value.description, 'open description') }),
|
|
907
|
+
...(value.fchmod === undefined ? {} : { fchmod: u32(value.fchmod, 'open fchmod') }),
|
|
908
|
+
...expectations(value, 'open'),
|
|
868
909
|
};
|
|
869
910
|
}
|
|
870
911
|
case 'lutimes':
|
|
@@ -936,6 +977,23 @@ function inodeNumber(value, label) {
|
|
|
936
977
|
throw new Error(`w7-frame: ${label} must be at least 2`);
|
|
937
978
|
return ino;
|
|
938
979
|
}
|
|
980
|
+
/** A record's expectations (W7Expected), checked: `expect`, and a rename's `expectTo`. */
|
|
981
|
+
function expectations(value, label) {
|
|
982
|
+
const expected = (field, name) => {
|
|
983
|
+
if (field === null)
|
|
984
|
+
return null;
|
|
985
|
+
if (isObject(field) && !Array.isArray(field)) {
|
|
986
|
+
if (Object.keys(field).join(',') !== 'made')
|
|
987
|
+
throw new Error(`w7-frame: ${label} ${name} is a number, null or { made }`);
|
|
988
|
+
return { made: descriptionId(field.made, `${label} ${name} made`) };
|
|
989
|
+
}
|
|
990
|
+
return inodeNumber(field, `${label} ${name}`);
|
|
991
|
+
};
|
|
992
|
+
return {
|
|
993
|
+
...(value.expect === undefined ? {} : { expect: expected(value.expect, 'expect') }),
|
|
994
|
+
...(value.expectTo === undefined ? {} : { expectTo: expected(value.expectTo, 'expectTo') }),
|
|
995
|
+
};
|
|
996
|
+
}
|
|
939
997
|
function parseObject(bytes, label, required, optional = []) {
|
|
940
998
|
let value;
|
|
941
999
|
try {
|
|
@@ -1092,17 +1150,7 @@ class PathOwnership {
|
|
|
1092
1150
|
}
|
|
1093
1151
|
}
|
|
1094
1152
|
function normalizePath(path) {
|
|
1095
|
-
|
|
1096
|
-
for (const segment of path.split('/')) {
|
|
1097
|
-
if (segment === '..') {
|
|
1098
|
-
if (out.length > 0)
|
|
1099
|
-
out.pop();
|
|
1100
|
-
}
|
|
1101
|
-
else if (segment !== '' && segment !== '.') {
|
|
1102
|
-
out.push(segment);
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
return out.join('/');
|
|
1153
|
+
return rootClampedSegments(path).join('/');
|
|
1106
1154
|
}
|
|
1107
1155
|
function parentPath(path) {
|
|
1108
1156
|
const index = path.lastIndexOf('/');
|
|
@@ -1122,6 +1170,10 @@ function safeInteger(value, label) {
|
|
|
1122
1170
|
}
|
|
1123
1171
|
return value;
|
|
1124
1172
|
}
|
|
1173
|
+
/** A chown side: a uid or gid, or null, which keeps the file's. */
|
|
1174
|
+
function ownerId(value, label) {
|
|
1175
|
+
return value === null ? null : u32(value, label);
|
|
1176
|
+
}
|
|
1125
1177
|
function u32(value, label) {
|
|
1126
1178
|
const integer = safeInteger(value, label);
|
|
1127
1179
|
if (integer > 0xffff_ffff)
|
package/dist/wave-writer.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export interface WaveWriterOptions<Meta = undefined> {
|
|
|
133
133
|
root: string | null;
|
|
134
134
|
/** An existing worktree: only directories strictly below it are published. */
|
|
135
135
|
worktreeRoot?: string | null;
|
|
136
|
-
/** Record paths are relative to it (joined with '/'); omitted, they are VFS paths. */
|
|
136
|
+
/** Record paths are relative to it (joined with '/'; '' is the base itself); omitted, they are VFS paths. */
|
|
137
137
|
base?: string;
|
|
138
138
|
/** Wall-clock time (ms) after which no new wave starts. */
|
|
139
139
|
deadline?: number | null;
|
|
@@ -192,7 +192,6 @@ export declare class WaveWriter<Meta = undefined> {
|
|
|
192
192
|
private ownedPathBytes;
|
|
193
193
|
private pin;
|
|
194
194
|
private inFlight;
|
|
195
|
-
private cutQueue;
|
|
196
195
|
private sequence;
|
|
197
196
|
private failure;
|
|
198
197
|
/** Owners (records' `meta`) whose records a failed wave carried. */
|
|
@@ -277,7 +276,13 @@ export declare class WaveWriter<Meta = undefined> {
|
|
|
277
276
|
private tally;
|
|
278
277
|
private hasBuffered;
|
|
279
278
|
private ownPath;
|
|
280
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* The directories a wave publishes for something at or below `dir`:
|
|
281
|
+
* `dir` and each one above it, deepest first, up to the writer's root
|
|
282
|
+
* (included) and inside its worktree root.
|
|
283
|
+
*/
|
|
284
|
+
private chain;
|
|
285
|
+
/** The chain from `path` upward, to the first directory already owned. */
|
|
281
286
|
private walkChain;
|
|
282
287
|
/**
|
|
283
288
|
* Send what is buffered when a removal of `path` is pending in it: a wave
|
|
@@ -292,11 +297,11 @@ export declare class WaveWriter<Meta = undefined> {
|
|
|
292
297
|
private drop;
|
|
293
298
|
private cutIfFull;
|
|
294
299
|
/**
|
|
295
|
-
* Send the buffered wave once the one in flight has published.
|
|
296
|
-
*
|
|
300
|
+
* Send the buffered wave once the one in flight has published. Only a
|
|
301
|
+
* mutation cuts, and mutations run one at a time, so at most one wave is
|
|
302
|
+
* in flight and one buffers.
|
|
297
303
|
*/
|
|
298
304
|
private cut;
|
|
299
|
-
private cutNow;
|
|
300
305
|
/**
|
|
301
306
|
* How a wave of `bytes` content crosses: an attempt's stream, made anew
|
|
302
307
|
* for each attempt.
|