@phreshos/node 0.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/system.js ADDED
@@ -0,0 +1,441 @@
1
+ import { ClientServiceHandler as CoreClientServiceHandler, ServerServiceHandler as CoreServerServiceHandler } from "@phreshos/core";
2
+ import { homedir } from "node:os";
3
+ import Events from "./events.js";
4
+ import { filesystemStorage } from "./storage.js";
5
+ import Uploads from "./uploads.js";
6
+ /** Build the exact shared System contract over an owner-local Gateway transport. */
7
+ export function gatewaySystem(transport) {
8
+ return new GatewaySystem(transport);
9
+ }
10
+ class GatewaySystem {
11
+ transport;
12
+ storage = filesystemStorage(homedir(), "the native home directory");
13
+ appearance;
14
+ program;
15
+ process;
16
+ uploads;
17
+ constructor(transport) {
18
+ this.transport = transport;
19
+ this.appearance = new GatewayAppearance(transport);
20
+ this.program = new ProgramRegistry(this);
21
+ this.process = new ProcessRegistry(this);
22
+ this.uploads = new Uploads(request => transport.api(request));
23
+ }
24
+ service(key) {
25
+ return key.endpoint === "server"
26
+ ? new ServerService(this, key)
27
+ : new ClientService(this, key);
28
+ }
29
+ }
30
+ class GatewayAppearance extends Events {
31
+ transport;
32
+ constructor(transport) {
33
+ super(["change"], (_event, signal) => transport.api({ capability: "appearance", operation: "wait" }, signal));
34
+ this.transport = transport;
35
+ }
36
+ async snapshot() {
37
+ return await this.transport.api({ capability: "appearance", operation: "snapshot" });
38
+ }
39
+ async update(appearance) {
40
+ await this.transport.api({ capability: "appearance", operation: "update", value: appearance });
41
+ }
42
+ }
43
+ class ProgramRegistry extends Events {
44
+ system;
45
+ constructor(system) {
46
+ super(["create", "forget", "install", "uninstall"], (event, signal, timeout) => (system.transport.control({ capability: "program", operation: "wait", input: { event, timeout } }, signal)
47
+ .then(value => this.event(value))));
48
+ this.system = system;
49
+ }
50
+ async list(onlyInstalled = false) {
51
+ const programs = [];
52
+ let offset = 0;
53
+ while (true) {
54
+ const page = await this.system.transport.control({
55
+ capability: "program",
56
+ operation: "list",
57
+ input: { installedOnly: onlyInstalled, limit: 100, offset }
58
+ });
59
+ programs.push(...page.data.map(snapshot => new ProgramHandle(this.system, snapshot)));
60
+ offset += page.data.length;
61
+ if (!page.truncated || !page.data.length)
62
+ return programs;
63
+ }
64
+ }
65
+ async find(identity) {
66
+ try {
67
+ const snapshot = await this.system.transport.control({ capability: "program", operation: "inspect", input: { program: identity } });
68
+ return new ProgramHandle(this.system, snapshot);
69
+ }
70
+ catch (error) {
71
+ if (unknown(error, "Program"))
72
+ return null;
73
+ throw error;
74
+ }
75
+ }
76
+ async create(source) {
77
+ let identity = null;
78
+ for await (const event of this.system.transport.lifecycle({ word: "create", program: source })) {
79
+ if (event.event === "created")
80
+ identity = String(event.identity);
81
+ }
82
+ if (!identity)
83
+ throw new Error("The System did not confirm the created Program");
84
+ const program = await this.find(identity);
85
+ if (!program)
86
+ throw new Error("The created Program cannot be found");
87
+ return program;
88
+ }
89
+ async event(value) {
90
+ const waited = value;
91
+ if (waited.event === "uninstall") {
92
+ const payload = waited.payload;
93
+ return { program: new ProgramHandle(this.system, required(payload.program)), everythingRemoved: payload.everythingRemoved === true };
94
+ }
95
+ return new ProgramHandle(this.system, required(waited.payload));
96
+ }
97
+ }
98
+ class ProgramHandle extends Events {
99
+ system;
100
+ identity;
101
+ name;
102
+ version;
103
+ description;
104
+ hasAgent;
105
+ server;
106
+ client;
107
+ process;
108
+ constructor(system, snapshot) {
109
+ super(["forget", "uninstall"], (event, signal, timeout) => system.transport.control({
110
+ capability: "program", operation: "wait", input: { program: snapshot.identity, event, timeout }
111
+ }, signal).then(value => value.payload));
112
+ this.system = system;
113
+ this.identity = snapshot.identity;
114
+ this.name = snapshot.name;
115
+ this.version = snapshot.version;
116
+ this.description = snapshot.description;
117
+ this.hasAgent = snapshot.hasAgent;
118
+ this.server = snapshot.server ? Object.freeze({ start: snapshot.server.start }) : null;
119
+ this.client = snapshot.client ? Object.freeze({
120
+ start: snapshot.client.start,
121
+ title: snapshot.client.title,
122
+ size: snapshot.client.size,
123
+ position: snapshot.client.position,
124
+ layer: snapshot.client.layer,
125
+ minimize: snapshot.client.minimize
126
+ }) : null;
127
+ this.process = new ProgramProcesses(system, this);
128
+ }
129
+ async agent() {
130
+ if (!this.hasAgent)
131
+ return null;
132
+ const value = await this.system.transport.control({ capability: "program", operation: "agent", input: { program: this.identity } });
133
+ return typeof value.content === "string" ? value.content : null;
134
+ }
135
+ async installed() {
136
+ const value = await this.system.transport.control({ capability: "program", operation: "inspect", input: { program: this.identity } });
137
+ if (typeof value.installed !== "boolean")
138
+ throw new Error("The System returned no Program installation state");
139
+ return value.installed;
140
+ }
141
+ install() { return command(this.system, { word: "install-existing", identity: this.identity }); }
142
+ uninstall(everything = false) { return command(this.system, { word: "uninstall-existing", identity: this.identity, everything }); }
143
+ async forget() {
144
+ for await (const _event of this.system.transport.lifecycle({ word: "forget", identity: this.identity })) { /* consume completion */ }
145
+ }
146
+ }
147
+ class ProgramProcesses extends Events {
148
+ system;
149
+ program;
150
+ constructor(system, program) {
151
+ super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => system.transport.control({
152
+ capability: "process", operation: "wait", input: { program: program.identity, event, timeout }
153
+ }, signal).then(value => processEvent(system, value)));
154
+ this.system = system;
155
+ this.program = program;
156
+ }
157
+ async list() { return await listProcesses(this.system, this.program.identity); }
158
+ async first() { return (await this.list()).sort(chronological)[0] ?? null; }
159
+ async last() { return (await this.list()).sort(chronological).at(-1) ?? null; }
160
+ async find(identityOrName) {
161
+ const found = (await this.list()).find(process => process.identity === identityOrName || process.name === identityOrName);
162
+ return found ?? null;
163
+ }
164
+ create(launch = {}) { return createProcess(this.system, "create", this.program.identity, launch); }
165
+ findOrCreate(launch) { return createProcess(this.system, "findOrCreate", this.program.identity, launch); }
166
+ async exitAll() {
167
+ const processes = await this.list();
168
+ await Promise.all(processes.map(process => process.exit()));
169
+ return processes.map(process => process.identity);
170
+ }
171
+ }
172
+ class ProcessRegistry extends Events {
173
+ system;
174
+ constructor(system) {
175
+ super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => system.transport.control({
176
+ capability: "process", operation: "wait", input: { event, timeout }
177
+ }, signal).then(value => processEvent(system, value)));
178
+ this.system = system;
179
+ }
180
+ list() { return listProcesses(this.system); }
181
+ async find(identity) {
182
+ try {
183
+ const snapshot = await this.system.transport.control({ capability: "process", operation: "inspect", input: { process: identity } });
184
+ return new ProcessHandle(this.system, snapshot);
185
+ }
186
+ catch (error) {
187
+ if (unknown(error, "Process"))
188
+ return null;
189
+ throw error;
190
+ }
191
+ }
192
+ }
193
+ class ProcessHandle extends Events {
194
+ system;
195
+ snapshot;
196
+ identity;
197
+ name;
198
+ startedAt;
199
+ server;
200
+ client;
201
+ constructor(system, snapshot) {
202
+ super(["endpointStart", "endpointStop", "exit"], (event, signal, timeout) => system.transport.control({
203
+ capability: "process", operation: "wait", input: { process: snapshot.identity, event, timeout }
204
+ }, signal).then(value => processEvent(system, value)));
205
+ this.system = system;
206
+ this.snapshot = snapshot;
207
+ this.identity = snapshot.identity;
208
+ this.name = snapshot.name;
209
+ this.startedAt = new Date(snapshot.startedAt);
210
+ this.server = new ServerEndpoint(system, this);
211
+ this.client = new ClientEndpoint(system, this);
212
+ }
213
+ program() { return new ProgramHandle(this.system, required(this.snapshot.programSnapshot, this.snapshot.program)); }
214
+ async exit() {
215
+ await this.system.transport.control({ capability: "process", operation: "exit", input: { process: this.identity } });
216
+ }
217
+ async exited() { return await this.system.process.find(this.identity) === null; }
218
+ }
219
+ class EndpointHandle extends Events {
220
+ system;
221
+ owner;
222
+ constructor(system, owner, endpoint) {
223
+ super([], (event, signal, timeout) => event === null
224
+ ? system.transport.api({ capability: "endpoint", operation: "wait", process: owner.identity, endpoint, event, timeout }, signal)
225
+ : system.transport.control({
226
+ capability: "endpoint", operation: "wait", input: { process: owner.identity, endpoint, event, timeout }
227
+ }, signal).then(value => value.payload));
228
+ this.system = system;
229
+ this.owner = owner;
230
+ }
231
+ process() { return Promise.resolve(this.owner); }
232
+ async exists() {
233
+ const value = await this.inspect();
234
+ return value.running;
235
+ }
236
+ async start() { await this.operation("start"); }
237
+ async stop() { await this.operation("stop"); }
238
+ async service() {
239
+ const key = await this.system.transport.api({ capability: "endpoint", operation: "service", process: this.owner.identity, endpoint: this.endpoint });
240
+ return key ? this.system.service(key) : null;
241
+ }
242
+ publish(event, payload) {
243
+ void this.system.transport.control({ capability: "endpoint", operation: "publish", input: {
244
+ process: this.owner.identity, endpoint: this.endpoint, event, payload
245
+ } });
246
+ }
247
+ inspect() {
248
+ return this.system.transport.control({ capability: "endpoint", operation: "inspect", input: {
249
+ process: this.owner.identity, endpoint: this.endpoint
250
+ } });
251
+ }
252
+ async operation(operation, client) {
253
+ await this.system.transport.control({ capability: "endpoint", operation, input: {
254
+ process: this.owner.identity, endpoint: this.endpoint, ...(client ? { client } : {})
255
+ } });
256
+ }
257
+ }
258
+ class ServerEndpoint extends EndpointHandle {
259
+ endpoint = "server";
260
+ constructor(system, owner) { super(system, owner, "server"); }
261
+ async ask(event, payload) {
262
+ return await this.system.transport.control({ capability: "endpoint", operation: "ask", input: {
263
+ process: this.owner.identity, endpoint: "server", event, payload
264
+ } });
265
+ }
266
+ timeout(milliseconds) {
267
+ return { ask: (event, payload) => this.system.transport.control({
268
+ capability: "endpoint", operation: "ask", input: { process: this.owner.identity, endpoint: "server", event, payload, timeout: milliseconds }
269
+ }) };
270
+ }
271
+ async waitReady(timeout) {
272
+ await this.system.transport.control({ capability: "endpoint", operation: "waitReady", input: { process: this.owner.identity, endpoint: "server", timeout } });
273
+ }
274
+ async service() {
275
+ return await super.service();
276
+ }
277
+ }
278
+ class ClientEndpoint extends EndpointHandle {
279
+ endpoint = "client";
280
+ window;
281
+ constructor(system, owner) {
282
+ super(system, owner, "client");
283
+ this.window = new GatewayWindow(system, owner);
284
+ }
285
+ async start(overrides) { await this.operation("start", overrides); }
286
+ async service() {
287
+ return await super.service();
288
+ }
289
+ }
290
+ class GatewayWindow extends Events {
291
+ system;
292
+ process;
293
+ constructor(system, process) {
294
+ super(["move", "resize", "geometry", "minimize", "changeTitle", "front"], (event, signal, timeout) => system.transport.control({
295
+ capability: "window", operation: "wait", input: { process: process.identity, event, timeout }
296
+ }, signal).then(value => value.payload));
297
+ this.system = system;
298
+ this.process = process;
299
+ }
300
+ async title() { return (await this.snapshot()).title; }
301
+ async position() { return (await this.snapshot()).position; }
302
+ async size() { return (await this.snapshot()).size; }
303
+ async minimized() { return (await this.snapshot()).minimized; }
304
+ async front() { return (await this.snapshot()).front; }
305
+ async layer() { return (await this.snapshot()).layer; }
306
+ async location() { return (await this.snapshot()).location; }
307
+ async move(position) { await this.change("move", { position }); }
308
+ async resize(size) { await this.change("resize", { size }); }
309
+ async setGeometry(geometry) { await this.change("setGeometry", geometry); }
310
+ async minimize(minimized = true) { await this.change("minimize", { minimized }); }
311
+ async changeTitle(title) { await this.change("changeTitle", { title }); }
312
+ async raise() { await this.change("raise", {}); }
313
+ snapshot() {
314
+ return this.system.transport.control({ capability: "window", operation: "inspect", input: { process: this.process.identity } });
315
+ }
316
+ async change(operation, input) {
317
+ await this.system.transport.control({ capability: "window", operation, input: { process: this.process.identity, ...input } });
318
+ }
319
+ }
320
+ class ServiceBase extends Events {
321
+ system;
322
+ key;
323
+ name;
324
+ constructor(system, key) {
325
+ super(["enable", "disable"], (event, signal, timeout) => system.transport.api({
326
+ capability: "service", operation: "wait", scope: "lifecycle", key, event, timeout
327
+ }, signal));
328
+ this.system = system;
329
+ this.key = key;
330
+ this.name = key.name;
331
+ }
332
+ async enabled() { return await this.system.transport.api({ capability: "service", operation: "enabled", key: this.key }); }
333
+ async waitReady(timeout) { await this.system.transport.api({ capability: "service", operation: "waitReady", key: this.key, timeout }); }
334
+ }
335
+ class ServerService extends CoreServerServiceHandler {
336
+ name;
337
+ channel;
338
+ base;
339
+ constructor(system, key) {
340
+ super();
341
+ this.base = new ServiceBase(system, key);
342
+ this.name = key.name;
343
+ this.channel = new ServerServiceChannelHandle(system, key);
344
+ Object.assign(this, eventsOf(this.base));
345
+ }
346
+ enabled() { return this.base.enabled(); }
347
+ waitReady(timeout) { return this.base.waitReady(timeout); }
348
+ }
349
+ class ClientService extends CoreClientServiceHandler {
350
+ name;
351
+ channel;
352
+ base;
353
+ constructor(system, key) {
354
+ super();
355
+ this.base = new ServiceBase(system, key);
356
+ this.name = key.name;
357
+ this.channel = new ClientServiceChannelHandle(system, key);
358
+ Object.assign(this, eventsOf(this.base));
359
+ }
360
+ enabled() { return this.base.enabled(); }
361
+ waitReady(timeout) { return this.base.waitReady(timeout); }
362
+ }
363
+ class ClientServiceChannelHandle extends Events {
364
+ system;
365
+ key;
366
+ constructor(system, key) {
367
+ super([], (event, signal, timeout) => system.transport.api({ capability: "service", operation: "wait", scope: "channel", key, event, timeout }, signal));
368
+ this.system = system;
369
+ this.key = key;
370
+ }
371
+ }
372
+ class ServerServiceChannelHandle extends ClientServiceChannelHandle {
373
+ async ask(event, payload) {
374
+ return await this.system.transport.api({ capability: "service", operation: "ask", key: this.key, event, payload });
375
+ }
376
+ timeout(milliseconds) {
377
+ return { ask: (event, payload) => this.system.transport.api({
378
+ capability: "service", operation: "ask", key: this.key, event, payload, timeout: milliseconds
379
+ }) };
380
+ }
381
+ publish(event, payload) {
382
+ void this.system.transport.api({ capability: "service", operation: "publish", key: this.key, event, payload });
383
+ }
384
+ }
385
+ async function listProcesses(system, program) {
386
+ const processes = [];
387
+ let offset = 0;
388
+ while (true) {
389
+ const page = await system.transport.control({ capability: "process", operation: "list", input: { program, limit: 100, offset } });
390
+ processes.push(...page.data.map(snapshot => new ProcessHandle(system, snapshot)));
391
+ offset += page.data.length;
392
+ if (!page.truncated || !page.data.length)
393
+ return processes;
394
+ }
395
+ }
396
+ async function createProcess(system, operation, program, launch) {
397
+ const snapshot = await system.transport.control({ capability: "process", operation, input: { program, launch } });
398
+ return new ProcessHandle(system, snapshot);
399
+ }
400
+ async function* command(system, request) {
401
+ for await (const event of system.transport.lifecycle(request)) {
402
+ if (event.event === "output")
403
+ yield {
404
+ stream: event.stream === "stderr" ? "stderr" : "stdout",
405
+ text: String(event.text ?? "")
406
+ };
407
+ }
408
+ }
409
+ function processEvent(system, value) {
410
+ const waited = value;
411
+ const payload = waited.payload;
412
+ if (waited.event === "exit" && payload)
413
+ return {
414
+ process: new ProcessHandle(system, required(payload.processSnapshot, String(payload.process ?? ""))),
415
+ status: payload.status,
416
+ code: payload.code,
417
+ signal: payload.signal
418
+ };
419
+ if ((waited.event === "endpointStart" || waited.event === "endpointStop") && payload?.processSnapshot) {
420
+ const process = new ProcessHandle(system, payload.processSnapshot);
421
+ return payload.endpoint === "client" ? process.client : process.server;
422
+ }
423
+ if (payload && typeof payload.identity === "string")
424
+ return new ProcessHandle(system, payload);
425
+ return payload;
426
+ }
427
+ function eventsOf(events) {
428
+ return {
429
+ subscribe: events.subscribe,
430
+ waitFor: events.waitFor,
431
+ events: events.events,
432
+ observe: events.observe
433
+ };
434
+ }
435
+ function chronological(left, right) { return left.startedAt.getTime() - right.startedAt.getTime(); }
436
+ function unknown(error, entity) { return error instanceof Error && error.message.startsWith(`Unknown ${entity}`); }
437
+ function required(value, identity = "") {
438
+ if (value !== undefined)
439
+ return value;
440
+ throw new Error(`The System returned no ${identity ? `${identity} ` : ""}snapshot`);
441
+ }
@@ -0,0 +1,11 @@
1
+ import { type Socket } from "node:net";
2
+ export interface GatewayEvent {
3
+ event?: string;
4
+ [key: string]: unknown;
5
+ }
6
+ /** Open and retain one owner-local Gateway connection. */
7
+ export declare function openConnection(path: string): Promise<Socket>;
8
+ /** Execute one short authoritative System-control request. */
9
+ export declare function request(path: string, target: "api" | "system", request: unknown, signal?: AbortSignal): Promise<unknown>;
10
+ /** Stream one Program lifecycle operation until the System completes it. */
11
+ export declare function streamProgram(path: string, request: unknown, signal?: AbortSignal): AsyncGenerator<GatewayEvent, void, unknown>;
@@ -0,0 +1,113 @@
1
+ import { connect } from "node:net";
2
+ /** Open and retain one owner-local Gateway connection. */
3
+ export function openConnection(path) {
4
+ return new Promise((resolve, reject) => {
5
+ const socket = connect(path);
6
+ const failed = () => reject(unavailable(path));
7
+ socket.once("connect", () => {
8
+ socket.off("error", failed);
9
+ socket.on("error", () => undefined);
10
+ resolve(socket);
11
+ });
12
+ socket.once("error", failed);
13
+ });
14
+ }
15
+ /** Execute one short authoritative System-control request. */
16
+ export function request(path, target, request, signal) {
17
+ return new Promise((resolve, reject) => {
18
+ const socket = connect(path);
19
+ let buffer = "";
20
+ let settled = false;
21
+ const finish = (work) => {
22
+ if (settled)
23
+ return;
24
+ settled = true;
25
+ signal?.removeEventListener("abort", cancel);
26
+ socket.destroy();
27
+ work();
28
+ };
29
+ const cancel = () => finish(() => reject(signal?.reason instanceof Error ? signal.reason : new Error("The request was cancelled")));
30
+ signal?.addEventListener("abort", cancel, { once: true });
31
+ socket.on("connect", () => socket.write(`${JSON.stringify({ target, request })}\n`));
32
+ socket.on("data", chunk => {
33
+ buffer += String(chunk);
34
+ const boundary = buffer.indexOf("\n");
35
+ if (boundary < 0)
36
+ return;
37
+ let outcome;
38
+ try {
39
+ outcome = JSON.parse(buffer.slice(0, boundary));
40
+ }
41
+ catch {
42
+ return finish(() => reject(new Error("The System returned an invalid Gateway response")));
43
+ }
44
+ if (outcome.success)
45
+ finish(() => resolve(outcome.result));
46
+ else
47
+ finish(() => reject(new Error(outcome.error)));
48
+ });
49
+ socket.on("error", () => finish(() => reject(unavailable(path))));
50
+ socket.on("close", () => finish(() => reject(new Error("The System closed the Gateway request without an answer"))));
51
+ if (signal?.aborted)
52
+ cancel();
53
+ });
54
+ }
55
+ /** Stream one Program lifecycle operation until the System completes it. */
56
+ export function streamProgram(path, request, signal) {
57
+ const events = [];
58
+ let wake = null;
59
+ let ended = false;
60
+ let failure = null;
61
+ const socket = connect(path);
62
+ const cancel = () => socket.destroy(signal?.reason instanceof Error ? signal.reason : undefined);
63
+ if (signal?.aborted)
64
+ cancel();
65
+ else
66
+ signal?.addEventListener("abort", cancel, { once: true });
67
+ let buffer = "";
68
+ socket.on("connect", () => socket.write(`${JSON.stringify({ target: "program", request })}\n`));
69
+ socket.on("data", chunk => {
70
+ buffer += String(chunk);
71
+ const lines = buffer.split("\n");
72
+ buffer = lines.pop() ?? "";
73
+ for (const line of lines)
74
+ if (line.trim()) {
75
+ const event = JSON.parse(line);
76
+ if (event.event === "error")
77
+ failure = new Error(String(event.message));
78
+ else
79
+ events.push(event);
80
+ }
81
+ wake?.();
82
+ wake = null;
83
+ });
84
+ socket.on("error", error => {
85
+ failure = signal?.aborted
86
+ ? signal.reason instanceof Error ? signal.reason : new Error("The operation was cancelled")
87
+ : error;
88
+ wake?.();
89
+ wake = null;
90
+ });
91
+ socket.on("close", () => {
92
+ ended = true;
93
+ signal?.removeEventListener("abort", cancel);
94
+ wake?.();
95
+ wake = null;
96
+ });
97
+ return (async function* () {
98
+ while (true) {
99
+ if (events.length) {
100
+ yield events.shift();
101
+ continue;
102
+ }
103
+ if (failure)
104
+ throw failure;
105
+ if (ended)
106
+ return;
107
+ await new Promise(resolve => { wake = resolve; });
108
+ }
109
+ })();
110
+ }
111
+ function unavailable(path) {
112
+ return new Error(`No System Gateway is listening at ${path} — start PhreshOS first`);
113
+ }
@@ -0,0 +1,21 @@
1
+ import { type SystemUploads, type Upload } from "@phreshos/core";
2
+ type Ask = (request: object) => Promise<unknown>;
3
+ /** Owner-local implementation of the System's opaque upload collection. */
4
+ export default class Uploads implements SystemUploads {
5
+ private readonly ask;
6
+ private accessPromise;
7
+ constructor(ask: Ask);
8
+ write(value: unknown): Promise<Upload>;
9
+ stream(file: string): Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
10
+ bytes(file: string): Promise<Uint8Array<ArrayBuffer>>;
11
+ text(file: string): Promise<string>;
12
+ json<Value>(file: string): Promise<Value>;
13
+ stat(file: string): Promise<Readonly<{
14
+ file: string;
15
+ type: string | null;
16
+ size: number;
17
+ time: number;
18
+ }> | null>;
19
+ private access;
20
+ }
21
+ export {};