@modelprofile.com/authswitch 7.0.0 → 8.1.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/authority-contract.d.ts +1 -3
- package/dist_ts/authority-contract.js +1 -1
- package/dist_ts/authority-import-contract.d.ts +207 -0
- package/dist_ts/authority-import-contract.js +18 -0
- package/dist_ts/classes.authoritybroker.js +1 -5
- package/dist_ts/classes.authoritycli.d.ts +4 -0
- package/dist_ts/classes.authoritycli.js +149 -12
- package/dist_ts/classes.authorityclient.d.ts +7 -0
- package/dist_ts/classes.authorityclient.js +13 -1
- package/dist_ts/classes.authoritydaemon.d.ts +4 -0
- package/dist_ts/classes.authoritydaemon.js +75 -9
- package/dist_ts/classes.authoritydatabase.d.ts +10 -26
- package/dist_ts/classes.authoritydatabase.js +68 -133
- package/dist_ts/classes.authorityimport.d.ts +161 -0
- package/dist_ts/classes.authorityimport.js +638 -0
- package/dist_ts/classes.authoritymodels.d.ts +39 -43
- package/dist_ts/classes.authoritymodels.js +209 -243
- package/dist_ts/classes.authoritypreuse.js +1 -2
- package/dist_ts/classes.authorityregistry.d.ts +2 -17
- package/dist_ts/classes.authorityregistry.js +18 -74
- package/dist_ts/classes.authoritysecrets.d.ts +1 -1
- package/dist_ts/classes.authoritysecrets.js +2 -2
- package/dist_ts/classes.authorityusage.d.ts +3 -3
- package/dist_ts/classes.claudeauthority.js +3 -7
- package/dist_ts/classes.claudenative.d.ts +1 -8
- package/dist_ts/classes.claudenative.js +2 -25
- package/dist_ts/classes.codexmanaged.d.ts +1 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +6 -3
- package/dist_ts/plugins.d.ts +7 -7
- package/dist_ts/plugins.js +8 -8
- package/dist_ts/ts_migration/0001_authority_meta.d.ts +13 -3
- package/dist_ts/ts_migration/0001_authority_meta.js +29 -17
- package/dist_ts/ts_migration/0002_remove_backup_records.d.ts +17 -0
- package/dist_ts/ts_migration/0002_remove_backup_records.js +48 -0
- package/dist_ts/ts_migration/index.d.ts +6 -3
- package/dist_ts/ts_migration/index.js +10 -4
- package/dist_ts/ts_migration/legacysources/authswitchstores.d.ts +2 -0
- package/dist_ts/ts_migration/legacysources/authswitchstores.js +244 -0
- package/dist_ts/ts_migration/legacysources/index.d.ts +18 -0
- package/dist_ts/ts_migration/legacysources/index.js +24 -0
- package/dist_ts/ts_migration/legacysources/material.d.ts +37 -0
- package/dist_ts/ts_migration/legacysources/material.js +120 -0
- package/dist_ts/ts_migration/legacysources/nativestores.d.ts +2 -0
- package/dist_ts/ts_migration/legacysources/nativestores.js +207 -0
- package/dist_ts/ts_migration/legacysources/shared.d.ts +124 -0
- package/dist_ts/ts_migration/legacysources/shared.js +142 -0
- package/package.json +6 -5
- package/readme.md +68 -26
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/authority-contract.ts +1 -3
- package/ts/authority-import-contract.ts +216 -0
- package/ts/classes.authoritybroker.ts +0 -4
- package/ts/classes.authoritycli.ts +165 -11
- package/ts/classes.authorityclient.ts +22 -0
- package/ts/classes.authoritydaemon.ts +75 -8
- package/ts/classes.authoritydatabase.ts +70 -145
- package/ts/classes.authorityimport.ts +718 -0
- package/ts/classes.authoritymodels.ts +253 -269
- package/ts/classes.authoritypreuse.ts +0 -1
- package/ts/classes.authorityregistry.ts +20 -91
- package/ts/classes.authoritysecrets.ts +1 -1
- package/ts/classes.authorityusage.ts +3 -3
- package/ts/classes.claudeauthority.ts +2 -6
- package/ts/classes.claudenative.ts +1 -32
- package/ts/classes.codexmanaged.ts +1 -1
- package/ts/index.ts +5 -2
- package/ts/plugins.ts +7 -7
- package/ts/ts_migration/0001_authority_meta.ts +29 -17
- package/ts/ts_migration/0002_remove_backup_records.ts +49 -0
- package/ts/ts_migration/index.ts +10 -5
- package/ts/ts_migration/legacysources/authswitchstores.ts +211 -0
- package/ts/ts_migration/legacysources/index.ts +35 -0
- package/ts/ts_migration/legacysources/material.ts +134 -0
- package/ts/ts_migration/legacysources/nativestores.ts +204 -0
- package/ts/ts_migration/legacysources/shared.ts +220 -0
- package/dist_ts/authorityrehearsal.child.d.ts +0 -1
- package/dist_ts/authorityrehearsal.child.js +0 -395
- package/dist_ts/classes.authoritybackup.d.ts +0 -134
- package/dist_ts/classes.authoritybackup.js +0 -769
- package/dist_ts/classes.authorityrehearsal.d.ts +0 -127
- package/dist_ts/classes.authorityrehearsal.js +0 -632
- package/ts/authorityrehearsal.child.ts +0 -330
- package/ts/classes.authoritybackup.ts +0 -780
- package/ts/classes.authorityrehearsal.ts +0 -625
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import * as plugins from './plugins.js';
|
|
2
|
-
import { AuthSwitchAuthorityDatabase } from './classes.authoritydatabase.js';
|
|
3
|
-
import { assertStoredAuthorityMeta, type IStoredAuthorityMeta } from './classes.authoritymodels.js';
|
|
4
|
-
import type { IAuthorityRestoredRecord } from './classes.authoritybackup.js';
|
|
5
|
-
import {
|
|
6
|
-
AuthorityRehearsalFramedTransport,
|
|
7
|
-
authorityRehearsalMaximumFrameBytes,
|
|
8
|
-
parseAuthorityRehearsalChildMessage,
|
|
9
|
-
parseAuthorityRehearsalParentMessage,
|
|
10
|
-
parseAuthorityRehearsalWireEntry,
|
|
11
|
-
type IAuthorityRehearsalWireSource,
|
|
12
|
-
type TAuthorityRehearsalChildMessage,
|
|
13
|
-
type TAuthorityRehearsalParentMessage,
|
|
14
|
-
type TAuthorityRehearsalWireEntry,
|
|
15
|
-
} from './classes.authorityrehearsal.js';
|
|
16
|
-
|
|
17
|
-
const spoolHeaderBytes = 4 + 12 + 16;
|
|
18
|
-
const childReceiveTimeoutMs = 24 * 60 * 60 * 1000;
|
|
19
|
-
|
|
20
|
-
const writeAll = async (file: plugins.fs.promises.FileHandle, bytes: Uint8Array): Promise<void> => {
|
|
21
|
-
let offset = 0;
|
|
22
|
-
while (offset < bytes.byteLength) {
|
|
23
|
-
const result = await file.write(bytes, offset, bytes.byteLength - offset, null);
|
|
24
|
-
if (result.bytesWritten < 1) throw new Error('Encrypted rehearsal staging stopped making progress.');
|
|
25
|
-
offset += result.bytesWritten;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const readExact = async (file: plugins.fs.promises.FileHandle, bytes: Uint8Array,
|
|
30
|
-
position: number): Promise<boolean> => {
|
|
31
|
-
let offset = 0;
|
|
32
|
-
while (offset < bytes.byteLength) {
|
|
33
|
-
const result = await file.read(bytes, offset, bytes.byteLength - offset, position + offset);
|
|
34
|
-
if (result.bytesRead === 0) {
|
|
35
|
-
if (offset === 0) return true;
|
|
36
|
-
throw new Error('Encrypted rehearsal staging ended within a frame.');
|
|
37
|
-
}
|
|
38
|
-
offset += result.bytesRead;
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/** Append-only encrypted staging. The key exists only in this child and every plaintext buffer is wiped. */
|
|
44
|
-
class EncryptedRehearsalSpool<T> {
|
|
45
|
-
private appendFile?: plugins.fs.promises.FileHandle;
|
|
46
|
-
private readonly readFiles = new Set<plugins.fs.promises.FileHandle>();
|
|
47
|
-
private appendSequence = 0;
|
|
48
|
-
private finished = false;
|
|
49
|
-
|
|
50
|
-
constructor(private readonly path: string, private readonly domain: string,
|
|
51
|
-
private readonly key: Uint8Array, private readonly parse: (value: unknown) => T) {}
|
|
52
|
-
|
|
53
|
-
public async open(): Promise<void> {
|
|
54
|
-
if (this.appendFile) throw new Error('Encrypted rehearsal staging is already open.');
|
|
55
|
-
this.appendFile = await plugins.fs.promises.open(this.path, 'wx', 0o600);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public async append(value: T): Promise<void> {
|
|
59
|
-
if (!this.appendFile || this.finished) throw new Error('Encrypted rehearsal staging is not writable.');
|
|
60
|
-
const plaintext = Buffer.from(JSON.stringify(value), 'utf8');
|
|
61
|
-
if (plaintext.byteLength < 1 || plaintext.byteLength > authorityRehearsalMaximumFrameBytes) {
|
|
62
|
-
plaintext.fill(0);
|
|
63
|
-
throw new Error('Encrypted rehearsal staging entry exceeds its size bound.');
|
|
64
|
-
}
|
|
65
|
-
const nonce = plugins.crypto.randomBytes(12);
|
|
66
|
-
const aad = Buffer.from(JSON.stringify(['authswitch-rehearsal-stage-v1', this.domain,
|
|
67
|
-
this.appendSequence]), 'utf8');
|
|
68
|
-
let ciphertext: Buffer | undefined;
|
|
69
|
-
try {
|
|
70
|
-
const cipher = plugins.crypto.createCipheriv('aes-256-gcm', this.key, nonce);
|
|
71
|
-
cipher.setAAD(aad);
|
|
72
|
-
ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
73
|
-
const tag = cipher.getAuthTag();
|
|
74
|
-
const frame = Buffer.allocUnsafe(spoolHeaderBytes + ciphertext.byteLength);
|
|
75
|
-
frame.writeUInt32BE(ciphertext.byteLength, 0);
|
|
76
|
-
nonce.copy(frame, 4); tag.copy(frame, 16); ciphertext.copy(frame, spoolHeaderBytes);
|
|
77
|
-
try { await writeAll(this.appendFile, frame); }
|
|
78
|
-
finally { frame.fill(0); tag.fill(0); }
|
|
79
|
-
this.appendSequence++;
|
|
80
|
-
} finally {
|
|
81
|
-
plaintext.fill(0); nonce.fill(0); aad.fill(0); ciphertext?.fill(0);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public async finish(): Promise<void> {
|
|
86
|
-
if (this.finished) return;
|
|
87
|
-
this.finished = true;
|
|
88
|
-
const file = this.appendFile;
|
|
89
|
-
this.appendFile = undefined;
|
|
90
|
-
if (file) await file.close();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public async *values(): AsyncGenerator<T> {
|
|
94
|
-
if (!this.finished) throw new Error('Encrypted rehearsal staging is not complete.');
|
|
95
|
-
const file = await plugins.fs.promises.open(this.path, 'r');
|
|
96
|
-
this.readFiles.add(file);
|
|
97
|
-
let position = 0;
|
|
98
|
-
let itemSequence = 0;
|
|
99
|
-
try {
|
|
100
|
-
while (true) {
|
|
101
|
-
const header = Buffer.alloc(spoolHeaderBytes);
|
|
102
|
-
const ended = await readExact(file, header, position);
|
|
103
|
-
if (ended) return;
|
|
104
|
-
const length = header.readUInt32BE(0);
|
|
105
|
-
if (length < 1 || length > authorityRehearsalMaximumFrameBytes) {
|
|
106
|
-
header.fill(0);
|
|
107
|
-
throw new Error('Encrypted rehearsal staging frame exceeds its size bound.');
|
|
108
|
-
}
|
|
109
|
-
const ciphertext = Buffer.allocUnsafe(length);
|
|
110
|
-
try {
|
|
111
|
-
if (await readExact(file, ciphertext, position + spoolHeaderBytes)) {
|
|
112
|
-
throw new Error('Encrypted rehearsal staging ended within a frame.');
|
|
113
|
-
}
|
|
114
|
-
const aad = Buffer.from(JSON.stringify(['authswitch-rehearsal-stage-v1', this.domain,
|
|
115
|
-
itemSequence]), 'utf8');
|
|
116
|
-
let plaintext: Buffer | undefined;
|
|
117
|
-
try {
|
|
118
|
-
const decipher = plugins.crypto.createDecipheriv('aes-256-gcm', this.key,
|
|
119
|
-
header.subarray(4, 16));
|
|
120
|
-
decipher.setAAD(aad);
|
|
121
|
-
decipher.setAuthTag(header.subarray(16, spoolHeaderBytes));
|
|
122
|
-
plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
123
|
-
let decoded: unknown;
|
|
124
|
-
try { decoded = JSON.parse(plaintext.toString('utf8')) as unknown; }
|
|
125
|
-
catch { throw new Error('Encrypted rehearsal staging contains invalid JSON.'); }
|
|
126
|
-
yield this.parse(decoded);
|
|
127
|
-
} finally { aad.fill(0); plaintext?.fill(0); }
|
|
128
|
-
} finally { header.fill(0); ciphertext.fill(0); }
|
|
129
|
-
position += spoolHeaderBytes + length;
|
|
130
|
-
itemSequence++;
|
|
131
|
-
}
|
|
132
|
-
} finally {
|
|
133
|
-
this.readFiles.delete(file);
|
|
134
|
-
await file.close();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
public async close(): Promise<void> {
|
|
139
|
-
const errors: unknown[] = [];
|
|
140
|
-
const appendFile = this.appendFile;
|
|
141
|
-
this.appendFile = undefined;
|
|
142
|
-
if (appendFile) try { await appendFile.close(); } catch (error) { errors.push(error); }
|
|
143
|
-
for (const file of this.readFiles) {
|
|
144
|
-
try { await file.close(); } catch (error) { errors.push(error); }
|
|
145
|
-
}
|
|
146
|
-
this.readFiles.clear();
|
|
147
|
-
if (errors.length) throw new AggregateError(errors, 'Encrypted rehearsal staging cleanup failed.');
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const validateRoot = async (rootInput: string): Promise<string> => {
|
|
152
|
-
if (!plugins.path.isAbsolute(rootInput)) throw new Error('Rehearsal root must be absolute.');
|
|
153
|
-
const root = await plugins.fs.promises.realpath(rootInput);
|
|
154
|
-
if (root !== rootInput) throw new Error('Rehearsal root must be canonical.');
|
|
155
|
-
const stat = await plugins.fs.promises.lstat(root);
|
|
156
|
-
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.()
|
|
157
|
-
|| (stat.mode & 0o077) !== 0) throw new Error('Rehearsal root is not private.');
|
|
158
|
-
return root;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const recordKey = (record: IAuthorityRestoredRecord): string => `${record.model}\u0000${record.id}`;
|
|
162
|
-
const parseRecord = (value: unknown): IAuthorityRestoredRecord => {
|
|
163
|
-
const entry = parseAuthorityRehearsalWireEntry(value);
|
|
164
|
-
if ('kind' in entry) throw new Error('Encrypted record staging contains a source.');
|
|
165
|
-
return entry;
|
|
166
|
-
};
|
|
167
|
-
const parseSource = (value: unknown): IAuthorityRehearsalWireSource => {
|
|
168
|
-
const entry = parseAuthorityRehearsalWireEntry(value);
|
|
169
|
-
if (!('kind' in entry)) throw new Error('Encrypted source staging contains a record.');
|
|
170
|
-
return entry;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const run = async (): Promise<void> => {
|
|
174
|
-
// tsrun deliberately presents script arguments from argv[0], while built Node entrypoints retain argv[0..1].
|
|
175
|
-
const root = await validateRoot(process.argv.at(-1) ?? '');
|
|
176
|
-
const reader = plugins.fs.createReadStream('', { fd: 3, autoClose: false });
|
|
177
|
-
const writer = plugins.fs.createWriteStream('', { fd: 4, autoClose: false });
|
|
178
|
-
const transport = new AuthorityRehearsalFramedTransport<TAuthorityRehearsalParentMessage,
|
|
179
|
-
TAuthorityRehearsalChildMessage>({ readable: reader, writable: writer,
|
|
180
|
-
parseIncoming: parseAuthorityRehearsalParentMessage,
|
|
181
|
-
validateOutgoing: parseAuthorityRehearsalChildMessage });
|
|
182
|
-
const key = plugins.crypto.randomBytes(32);
|
|
183
|
-
const metaSpool = new EncryptedRehearsalSpool<IAuthorityRestoredRecord>(
|
|
184
|
-
plugins.path.join(root, 'meta.stage'), 'meta', key, parseRecord);
|
|
185
|
-
const recordSpool = new EncryptedRehearsalSpool<IAuthorityRestoredRecord>(
|
|
186
|
-
plugins.path.join(root, 'records.stage'), 'records', key, parseRecord);
|
|
187
|
-
const sourceSpool = new EncryptedRehearsalSpool<IAuthorityRehearsalWireSource>(
|
|
188
|
-
plugins.path.join(root, 'sources.stage'), 'sources', key, parseSource);
|
|
189
|
-
let database: AuthSwitchAuthorityDatabase | undefined;
|
|
190
|
-
let recordRestoreIterator: AsyncIterator<IAuthorityRestoredRecord> | undefined;
|
|
191
|
-
let recordReadbackIterator: AsyncIterator<IAuthorityRestoredRecord> | undefined;
|
|
192
|
-
let sourceReadbackIterator: AsyncIterator<IAuthorityRehearsalWireSource> | undefined;
|
|
193
|
-
let expectedSequence = 0;
|
|
194
|
-
let metaCount = 0;
|
|
195
|
-
let recordsStarted = false;
|
|
196
|
-
let lastRecordKey = '';
|
|
197
|
-
let inputComplete = false;
|
|
198
|
-
let restored = false;
|
|
199
|
-
let recordsReadbackDone = false;
|
|
200
|
-
let phase: Extract<TAuthorityRehearsalChildMessage, { type: 'failed' }>['phase'] = 'staging';
|
|
201
|
-
let cleanClose = false;
|
|
202
|
-
|
|
203
|
-
const cleanup = async (): Promise<void> => {
|
|
204
|
-
const errors: unknown[] = [];
|
|
205
|
-
for (const iterator of [recordRestoreIterator, recordReadbackIterator, sourceReadbackIterator]) {
|
|
206
|
-
if (iterator?.return) try { await iterator.return(); } catch (error) { errors.push(error); }
|
|
207
|
-
}
|
|
208
|
-
if (database) try { await database.stop(); } catch (error) { errors.push(error); }
|
|
209
|
-
database = undefined;
|
|
210
|
-
for (const spool of [metaSpool, recordSpool, sourceSpool]) {
|
|
211
|
-
try { await spool.close(); } catch (error) { errors.push(error); }
|
|
212
|
-
}
|
|
213
|
-
key.fill(0);
|
|
214
|
-
if (errors.length) throw new AggregateError(errors, 'Rehearsal child cleanup failed.');
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
await Promise.all([metaSpool.open(), recordSpool.open(), sourceSpool.open()]);
|
|
219
|
-
await transport.send({ version: 1, type: 'ready' }, childReceiveTimeoutMs);
|
|
220
|
-
while (true) {
|
|
221
|
-
const message = await transport.receive(childReceiveTimeoutMs);
|
|
222
|
-
if (message.type === 'stage') {
|
|
223
|
-
phase = 'staging';
|
|
224
|
-
if (inputComplete || message.sequence !== expectedSequence) {
|
|
225
|
-
throw new Error('Rehearsal stage sequence is invalid.');
|
|
226
|
-
}
|
|
227
|
-
const entry: TAuthorityRehearsalWireEntry = message.entry;
|
|
228
|
-
if ('kind' in entry) {
|
|
229
|
-
if (recordsStarted) throw new Error('Rehearsal sources must precede records.');
|
|
230
|
-
await sourceSpool.append(entry);
|
|
231
|
-
} else {
|
|
232
|
-
recordsStarted = true;
|
|
233
|
-
const keyValue = recordKey(entry);
|
|
234
|
-
if (keyValue <= lastRecordKey) throw new Error('Rehearsal records must be strictly ordered.');
|
|
235
|
-
lastRecordKey = keyValue;
|
|
236
|
-
if (entry.model === 'authswitch_authority_meta') {
|
|
237
|
-
if (++metaCount !== 1) throw new Error('Rehearsal input contains duplicate metadata.');
|
|
238
|
-
await metaSpool.append(entry);
|
|
239
|
-
} else await recordSpool.append(entry);
|
|
240
|
-
}
|
|
241
|
-
expectedSequence++;
|
|
242
|
-
await transport.send({ version: 1, type: 'staged', sequence: message.sequence }, childReceiveTimeoutMs);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
if (message.type === 'finish') {
|
|
246
|
-
phase = 'staging';
|
|
247
|
-
if (inputComplete || message.sequence !== expectedSequence || metaCount !== 1) {
|
|
248
|
-
throw new Error('Rehearsal input is incomplete.');
|
|
249
|
-
}
|
|
250
|
-
await Promise.all([metaSpool.finish(), recordSpool.finish(), sourceSpool.finish()]);
|
|
251
|
-
inputComplete = true;
|
|
252
|
-
recordRestoreIterator = recordSpool.values()[Symbol.asyncIterator]();
|
|
253
|
-
await transport.send({ version: 1, type: 'input.complete' }, childReceiveTimeoutMs);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (message.type === 'restore.next') {
|
|
257
|
-
phase = 'restore';
|
|
258
|
-
if (!inputComplete || restored) throw new Error('Rehearsal restore state is invalid.');
|
|
259
|
-
if (!database) {
|
|
260
|
-
database = new AuthSwitchAuthorityDatabase({ dataDirectory: plugins.path.join(root, 'database'),
|
|
261
|
-
socketPath: plugins.path.join(root, 'run', 'database.sock') });
|
|
262
|
-
await database.start({ mode: 'closed_restore' });
|
|
263
|
-
const metaIterator = metaSpool.values()[Symbol.asyncIterator]();
|
|
264
|
-
try {
|
|
265
|
-
const first = await metaIterator.next();
|
|
266
|
-
const second = await metaIterator.next();
|
|
267
|
-
if (first.done || !second.done) throw new Error('Rehearsal metadata staging is not singular.');
|
|
268
|
-
assertStoredAuthorityMeta(first.value.value);
|
|
269
|
-
const meta: IStoredAuthorityMeta = first.value.value;
|
|
270
|
-
await database.restoreClosedMeta(meta);
|
|
271
|
-
} finally { await metaIterator.return?.(undefined); }
|
|
272
|
-
await transport.send({ version: 1, type: 'restore.progress', done: false }, childReceiveTimeoutMs);
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
const next = await recordRestoreIterator!.next();
|
|
276
|
-
if (!next.done) {
|
|
277
|
-
await database.insertClosedRestoredRecord(next.value);
|
|
278
|
-
await transport.send({ version: 1, type: 'restore.progress', done: false }, childReceiveTimeoutMs);
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
await database.stop();
|
|
282
|
-
database = new AuthSwitchAuthorityDatabase({ dataDirectory: plugins.path.join(root, 'database'),
|
|
283
|
-
socketPath: plugins.path.join(root, 'run', 'database.sock') });
|
|
284
|
-
await database.start();
|
|
285
|
-
recordReadbackIterator = database.readStoredRecords({ pageSize: 64 })[Symbol.asyncIterator]();
|
|
286
|
-
sourceReadbackIterator = sourceSpool.values()[Symbol.asyncIterator]();
|
|
287
|
-
restored = true;
|
|
288
|
-
await transport.send({ version: 1, type: 'restore.progress', done: true }, childReceiveTimeoutMs);
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (message.type === 'record.next') {
|
|
292
|
-
phase = 'readback';
|
|
293
|
-
if (!restored || recordsReadbackDone) throw new Error('Rehearsal record readback state is invalid.');
|
|
294
|
-
const next = await recordReadbackIterator!.next();
|
|
295
|
-
if (next.done) recordsReadbackDone = true;
|
|
296
|
-
await transport.send({ version: 1, type: 'record', record: next.done ? null : next.value }, childReceiveTimeoutMs);
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
if (message.type === 'source.next') {
|
|
300
|
-
phase = 'readback';
|
|
301
|
-
if (!restored || !recordsReadbackDone) throw new Error('Rehearsal source readback state is invalid.');
|
|
302
|
-
const next = await sourceReadbackIterator!.next();
|
|
303
|
-
await transport.send({ version: 1, type: 'source', source: next.done ? null : next.value }, childReceiveTimeoutMs);
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
if (message.type === 'close') {
|
|
307
|
-
phase = 'cleanup';
|
|
308
|
-
await cleanup();
|
|
309
|
-
await transport.send({ version: 1, type: 'closed' }, childReceiveTimeoutMs);
|
|
310
|
-
await transport.finishWritable();
|
|
311
|
-
cleanClose = true;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
throw new Error('Unknown rehearsal request.');
|
|
315
|
-
}
|
|
316
|
-
} catch {
|
|
317
|
-
try { await cleanup(); }
|
|
318
|
-
catch { phase = 'cleanup'; }
|
|
319
|
-
try {
|
|
320
|
-
await transport.send({ version: 1, type: 'failed', phase }, childReceiveTimeoutMs);
|
|
321
|
-
await transport.finishWritable();
|
|
322
|
-
} catch { /* The parent may already have closed the control channel. */ }
|
|
323
|
-
process.exitCode = 1;
|
|
324
|
-
} finally {
|
|
325
|
-
if (!cleanClose) key.fill(0);
|
|
326
|
-
transport.destroy();
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
await run().catch(() => { process.exitCode = 1; });
|