fake-node 0.3.0 → 0.4.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/lib/_fs.d.ts +254 -0
- package/lib/_fs.js +750 -0
- package/lib/_fs.js.map +1 -0
- package/lib/buffer.d.ts +9 -0
- package/lib/buffer.js +12 -0
- package/lib/buffer.js.map +1 -0
- package/lib/fs.d.ts +110 -0
- package/lib/fs.js +223 -0
- package/lib/fs.js.map +1 -0
- package/lib/index.d.ts +34 -13
- package/lib/index.js +113 -37
- package/lib/index.js.map +1 -0
- package/lib/os.js +3 -13
- package/lib/os.js.map +1 -0
- package/lib/path.d.ts +55 -0
- package/lib/path.js +105 -0
- package/lib/path.js.map +1 -0
- package/lib/process.js +2 -9
- package/lib/process.js.map +1 -0
- package/lib/querystring.js +1 -0
- package/lib/querystring.js.map +1 -0
- package/lib/util.d.ts +145 -0
- package/lib/util.js +460 -0
- package/lib/util.js.map +1 -0
- package/package.json +5 -2
- package/src/_fs.ts +852 -0
- package/src/buffer.ts +13 -0
- package/src/fs.ts +168 -455
- package/src/in_fake_node.d.ts +12 -0
- package/src/index.ts +134 -42
- package/src/os.ts +3 -10
- package/src/path.ts +141 -0
- package/src/process.ts +2 -20
- package/src/util.ts +126 -13
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
import {FakeNode, Process} from './index';
|
3
|
+
|
4
|
+
declare global {
|
5
|
+
var __fakeNode__: FakeNode;
|
6
|
+
var __fakeNode_process__: Process;
|
7
|
+
var module: {
|
8
|
+
exports: {[key: string]: any},
|
9
|
+
};
|
10
|
+
var exports: {[key: string]: any};
|
11
|
+
var require: typeof FakeNode.prototype.require;
|
12
|
+
}
|
package/src/index.ts
CHANGED
@@ -1,18 +1,44 @@
|
|
1
1
|
|
2
|
+
import * as baseProcessObject from './process';
|
3
|
+
import {FileSystem} from './_fs';
|
2
4
|
import * as module_os from './os';
|
3
|
-
import * as
|
4
|
-
import * as module_punycode from 'punycode/';
|
5
|
+
import * as module_util from './util';
|
5
6
|
import * as module_querystring from './querystring';
|
7
|
+
import * as module_punycode from 'punycode/';
|
8
|
+
import * as module_path from './path';
|
9
|
+
import * as module_buffer from './buffer';
|
10
|
+
import * as module_fs from './fs';
|
6
11
|
import WEB_ONLY_GLOBALS from './web_only_globals.json';
|
7
12
|
|
8
13
|
|
9
|
-
|
14
|
+
export * as fs from './_fs';
|
15
|
+
|
16
|
+
|
17
|
+
const IS_BROWSER = (('window' in globalThis && window === globalThis && 'document' in window && 'navigator' in window && 'window' in window && window.window === window) || ('self' in globalThis && self === globalThis && typeof self.postMessage === 'function' && 'self' in self && self.self === self));
|
18
|
+
|
19
|
+
|
20
|
+
type BaseProcessObject = typeof import ('./process');
|
21
|
+
interface ProcessObject extends BaseProcessObject {
|
22
|
+
argv: string[];
|
23
|
+
argv0: string;
|
24
|
+
env: {[key: string]: string};
|
25
|
+
execArgv: string[];
|
26
|
+
execPath: string;
|
27
|
+
mainModule: string;
|
28
|
+
platform: string;
|
29
|
+
version: string;
|
30
|
+
versions: {[key: string]: string};
|
31
|
+
}
|
32
|
+
|
10
33
|
|
11
34
|
const BUILTIN_MODULES: [string, any][] = [
|
12
35
|
['os', module_os],
|
13
|
-
['
|
36
|
+
['util', module_util],
|
14
37
|
['querystring', module_querystring],
|
15
38
|
['punycode', module_punycode],
|
39
|
+
['path', module_path],
|
40
|
+
['buffer', module_buffer],
|
41
|
+
['fs', module_fs],
|
16
42
|
];
|
17
43
|
|
18
44
|
const DEFAULT_ENV = {
|
@@ -47,21 +73,21 @@ export class Process {
|
|
47
73
|
argv: string[] = [];
|
48
74
|
argv0: string = '';
|
49
75
|
execArgv: string[] = [];
|
50
|
-
|
76
|
+
execPath: string = '/usr/bin/local/node';
|
51
77
|
|
52
78
|
path: string;
|
53
79
|
module: false | string;
|
54
|
-
code
|
80
|
+
code?: string;
|
55
81
|
|
56
|
-
constructor(fakeNode: FakeNode, {path = '', code, module}: {path?: string, code
|
82
|
+
constructor(fakeNode: FakeNode, {path = '<anonymous>', code, module = false}: {path?: string, code?: string, module?: false | string}) {
|
57
83
|
this.fakeNode = fakeNode;
|
58
84
|
this.pid = fakeNode.nextPid;
|
59
85
|
fakeNode.nextPid++;
|
60
86
|
fakeNode.processes.set(this.pid, this);
|
61
|
-
this.code = code;
|
62
87
|
this.path = path;
|
63
88
|
this.cwd = path.split('/').slice(0, -1).join('/');
|
64
|
-
this.module = module
|
89
|
+
this.module = module;
|
90
|
+
this.code = code;
|
65
91
|
}
|
66
92
|
|
67
93
|
get env(): {[key: string]: string} {
|
@@ -69,49 +95,69 @@ export class Process {
|
|
69
95
|
}
|
70
96
|
|
71
97
|
run(): void {
|
72
|
-
let code
|
98
|
+
let code = this.code;
|
99
|
+
if (code === undefined) {
|
100
|
+
code = this.fakeNode.fs.readFrom(this.path);
|
101
|
+
}
|
102
|
+
let injectCode: string;
|
73
103
|
if (this.module) {
|
74
|
-
|
104
|
+
injectCode = `with(${this.fakeNode.globalName}.getGlobals(${this.pid})){__fakeNode__.modules.set('${this.module},(function(){${code};return module.exports;})());}`;
|
75
105
|
} else {
|
76
|
-
|
106
|
+
injectCode = `with(${this.fakeNode.globalName}.getGlobals(${this.pid})){(function(){${code}})();}`;
|
77
107
|
}
|
78
108
|
let elt = document.createElement('script');
|
79
|
-
elt.textContent =
|
109
|
+
elt.textContent = injectCode;
|
80
110
|
document.body.appendChild(elt);
|
81
111
|
}
|
82
112
|
|
83
113
|
}
|
84
114
|
|
85
115
|
|
116
|
+
if (!('__fakeNode_next_instance_id__' in globalThis)) {
|
117
|
+
// @ts-ignore
|
118
|
+
globalThis.__fakeNode_next_instance_id__ = 0;
|
119
|
+
}
|
120
|
+
|
121
|
+
|
86
122
|
export class FakeNode {
|
87
123
|
|
88
124
|
version: string = '0.3.0';
|
125
|
+
versions: {[key: string]: string} = {
|
126
|
+
'fake-node': '0.3.0',
|
127
|
+
'punycode': '2.3.1',
|
128
|
+
};
|
89
129
|
|
90
|
-
static nextId: number = 0;
|
91
130
|
id: number;
|
92
131
|
globalName: string;
|
93
132
|
|
94
|
-
|
95
|
-
|
96
|
-
modules: Map<string, unknown> = new Map();
|
97
|
-
|
98
|
-
globals: {[key: string]: unknown};
|
133
|
+
fs: FileSystem;
|
99
134
|
|
100
135
|
processes: Map<number, Process> = new Map();
|
101
136
|
nextPid: number = 3;
|
102
137
|
|
103
|
-
|
138
|
+
globalenv: {[key: string]: string} = DEFAULT_ENV;
|
139
|
+
|
140
|
+
modules: Map<string, unknown> = new Map();
|
141
|
+
|
142
|
+
// @ts-ignore
|
143
|
+
window: Window;
|
104
144
|
|
105
145
|
errorCallbacks: (Function | undefined)[] = [];
|
106
146
|
|
107
147
|
constructor() {
|
108
|
-
|
109
|
-
|
148
|
+
// @ts-ignore
|
149
|
+
this.id = globalThis.__fakeNode_next_instance_id;
|
150
|
+
// @ts-ignore
|
151
|
+
globalThis.__fakeNode_next_instance_id++;
|
110
152
|
this.globalName = '__fakeNode_' + this.id + '__';
|
111
153
|
// @ts-ignore
|
112
154
|
globalThis[this.globalName] = this;
|
113
|
-
|
114
|
-
|
155
|
+
if (IS_BROWSER) {
|
156
|
+
this.window = window;
|
157
|
+
} else {
|
158
|
+
Object.defineProperty(this, 'window', {get() {throw new ReferenceError('fake-node is not running in a browser')}});
|
159
|
+
}
|
160
|
+
this.fs = new FileSystem();
|
115
161
|
window.addEventListener('error', ({error}) => this.onError(error));
|
116
162
|
window.addEventListener('unhandledrejection', ({reason}) => reason instanceof Error ? this.onError(reason) : this.onError(new Error(String(reason))));
|
117
163
|
for (const [name, module] of BUILTIN_MODULES) {
|
@@ -121,25 +167,70 @@ export class FakeNode {
|
|
121
167
|
}
|
122
168
|
}
|
123
169
|
|
124
|
-
require(module: string): unknown {
|
170
|
+
require(module: string, pid: number): unknown {
|
171
|
+
if (module.startsWith('/') || module.startsWith('./') || module.startsWith('../')) {
|
172
|
+
const process = this.processes.get(pid);
|
173
|
+
if (process === undefined) {
|
174
|
+
throw new TypeError(`nonexistent PID in FakeNode.getProcessObject call: ${pid}. If you do not know why this occured, it is probably a bug in fake-node.`);
|
175
|
+
}
|
176
|
+
module = module_path.resolve(process.cwd, module);
|
177
|
+
}
|
125
178
|
if (this.modules.has(module)) {
|
126
179
|
return this.modules.get(module);
|
180
|
+
} else if (module === 'process' || module === 'node:process' || module === 'fake-node:process') {
|
181
|
+
return this.getProcessObject(pid);
|
127
182
|
} else {
|
128
183
|
throw new Error(`cannot find module '${module}'`);
|
129
184
|
}
|
130
185
|
}
|
131
186
|
|
187
|
+
addModuleFromValue(name: string, module: unknown): void {
|
188
|
+
this.modules.set(name, module);
|
189
|
+
}
|
190
|
+
|
191
|
+
eval(code: string): void {
|
192
|
+
(new Process(this, {code, path: '/'})).run();
|
193
|
+
}
|
194
|
+
|
195
|
+
evalModule(name: string, code: string): void {
|
196
|
+
(new Process(this, {code, path: '/', module: name})).run();
|
197
|
+
}
|
198
|
+
|
199
|
+
addModule(path: string, data: string): void {
|
200
|
+
this.fs.writeTo(path, data);
|
201
|
+
(new Process(this, {path, module: path}))
|
202
|
+
}
|
203
|
+
|
204
|
+
getProcessObject(pid: number): ProcessObject {
|
205
|
+
let out = Object.create(baseProcessObject);
|
206
|
+
const process = this.processes.get(pid);
|
207
|
+
if (process === undefined) {
|
208
|
+
throw new TypeError(`nonexistent PID in FakeNode.getProcessObject call: ${pid}. If you do not know why this occured, it is probably a bug in fake-node.`);
|
209
|
+
}
|
210
|
+
out.argv = process.argv;
|
211
|
+
out.argv0 = process.argv0;
|
212
|
+
out.env = process.env;
|
213
|
+
out.execArgv = process.execArgv;
|
214
|
+
out.execPath = process.execPath;
|
215
|
+
out.mainModule = '';
|
216
|
+
out.platform = this.getPlatform();
|
217
|
+
out.version = this.version;
|
218
|
+
out.versions = this.versions;
|
219
|
+
return out;
|
220
|
+
}
|
221
|
+
|
132
222
|
getGlobals(pid: number): object {
|
133
223
|
const process = this.processes.get(pid);
|
134
224
|
if (process === undefined) {
|
135
|
-
throw new TypeError(`nonexistent PID in FakeNode.getGlobals call: ${pid}. If you do not know why this occured, it is probably a bug in fake-node
|
225
|
+
throw new TypeError(`nonexistent PID in FakeNode.getGlobals call: ${pid}. If you do not know why this occured, it is probably a bug in fake-node.`);
|
136
226
|
}
|
137
|
-
let scope
|
138
|
-
|
227
|
+
let scope = Object.assign({
|
228
|
+
__fakeNode__: this,
|
229
|
+
__fakeNode_process__: process,
|
230
|
+
require: ((module: string) => this.require(module, pid)).bind(this)
|
231
|
+
}, Object.defineProperties({}, Object.fromEntries(WEB_ONLY_GLOBALS.map((name: string) => [name, {get(): void {throw new ReferenceError(`${name} is not defined`);}}]))));
|
139
232
|
scope.global = scope;
|
140
233
|
scope.globalThis = scope;
|
141
|
-
scope.__fakeNode_process__ = process;
|
142
|
-
scope.require = this.require.bind(this);
|
143
234
|
if (process.path !== '') {
|
144
235
|
const pathParts = process.path.split('/');
|
145
236
|
scope.__dirname = pathParts.slice(0, -1).join('/');
|
@@ -165,18 +256,6 @@ export class FakeNode {
|
|
165
256
|
return env;
|
166
257
|
}
|
167
258
|
|
168
|
-
run(code: string): void {
|
169
|
-
(new Process(this, {code, path: '/'})).run();
|
170
|
-
}
|
171
|
-
|
172
|
-
addModule(name: string, code: string): void {
|
173
|
-
(new Process(this, {code, path: '/', module: name})).run();
|
174
|
-
}
|
175
|
-
|
176
|
-
addModuleFromValue(name: string, module: unknown): void {
|
177
|
-
this.modules.set(name, module);
|
178
|
-
}
|
179
|
-
|
180
259
|
getUserFromUID(uid: number | string): string {
|
181
260
|
if (typeof uid === 'string') {
|
182
261
|
return uid;
|
@@ -226,4 +305,17 @@ export class FakeNode {
|
|
226
305
|
this.errorCallbacks[callbackID] = undefined;
|
227
306
|
}
|
228
307
|
|
308
|
+
getPlatform(): string {
|
309
|
+
const data = navigator.userAgent.slice('Mozilla/5.0 ('.length, navigator.userAgent.indexOf(')'));
|
310
|
+
if (data.includes('Windows')) {
|
311
|
+
return 'win32';
|
312
|
+
} else if (data.includes('Linux')) {
|
313
|
+
return 'linux';
|
314
|
+
} else if (data.includes('Mac')) {
|
315
|
+
return 'darwin';
|
316
|
+
} else {
|
317
|
+
return 'unknown';
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
229
321
|
}
|
package/src/os.ts
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
/// <reference path="./in_fake_node.d.ts" />
|
3
|
+
|
2
4
|
export const EOL = '\n';
|
3
5
|
|
4
6
|
export function availableParallelism(): number {
|
@@ -196,16 +198,7 @@ export function networkInterfaces(): {[key: string]: {address: string, netmask:
|
|
196
198
|
}
|
197
199
|
|
198
200
|
export function platform(): string {
|
199
|
-
|
200
|
-
if (data.includes('Windows')) {
|
201
|
-
return 'win32';
|
202
|
-
} else if (data.includes('Linux')) {
|
203
|
-
return 'linux';
|
204
|
-
} else if (data.includes('Mac')) {
|
205
|
-
return 'darwin';
|
206
|
-
} else {
|
207
|
-
return 'unknown';
|
208
|
-
}
|
201
|
+
return __fakeNode__.getPlatform();
|
209
202
|
}
|
210
203
|
|
211
204
|
export function release(): string {
|
package/src/path.ts
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
/// <reference path="./in_fake_node.d.ts" />
|
3
|
+
|
4
|
+
|
5
|
+
export interface PathObject {
|
6
|
+
dir: string;
|
7
|
+
root: string;
|
8
|
+
base: string;
|
9
|
+
name: string;
|
10
|
+
ext: string;
|
11
|
+
};
|
12
|
+
|
13
|
+
|
14
|
+
abstract class PathModule {
|
15
|
+
|
16
|
+
abstract sep: string;
|
17
|
+
abstract delimiter: string;
|
18
|
+
abstract _normalizeSplit: string | RegExp;
|
19
|
+
|
20
|
+
basename(path: string, suffix?: string): string {
|
21
|
+
const split = path.split(this.sep);
|
22
|
+
let out = split[split.length - 1];
|
23
|
+
if (suffix !== undefined && out.endsWith(suffix)) {
|
24
|
+
out = out.slice(0, out.length - suffix.length);
|
25
|
+
}
|
26
|
+
return out;
|
27
|
+
}
|
28
|
+
|
29
|
+
dirname(path: string): string {
|
30
|
+
return path.split('/').slice(0, -1).join('/');
|
31
|
+
}
|
32
|
+
|
33
|
+
extname(path: string): string {
|
34
|
+
let pos = path.indexOf('.');
|
35
|
+
if (pos === -1) {
|
36
|
+
return '';
|
37
|
+
} else {
|
38
|
+
return path.slice(pos);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
format(path: string): string {
|
43
|
+
throw new TypeError('path.format is not supported in fake-node');
|
44
|
+
}
|
45
|
+
|
46
|
+
matchesGlob(path: string, pattern: string): boolean {
|
47
|
+
throw new TypeError('path.matchesGlob is not supported in fake-node');
|
48
|
+
}
|
49
|
+
|
50
|
+
abstract isAbsolute(path: string): boolean;
|
51
|
+
|
52
|
+
join(...paths: string[]) {
|
53
|
+
return this.normalize(paths.join(this.sep));
|
54
|
+
}
|
55
|
+
|
56
|
+
normalize(path: string): string {
|
57
|
+
let out: string[] = [];
|
58
|
+
for (const segment of path.split(this._normalizeSplit)) {
|
59
|
+
if (segment === '' || segment === '.') {
|
60
|
+
continue;
|
61
|
+
} else if (segment === '..') {
|
62
|
+
out.pop();
|
63
|
+
} else {
|
64
|
+
out.push(segment);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
return out.join(this.sep);
|
68
|
+
}
|
69
|
+
|
70
|
+
parse(path: string): PathObject {
|
71
|
+
throw new TypeError('path.parse is not supported in fake-node');
|
72
|
+
}
|
73
|
+
|
74
|
+
relative(from: string, to: string): string {
|
75
|
+
throw new TypeError('path.relative is not supported in fake-node');
|
76
|
+
}
|
77
|
+
|
78
|
+
resolve(...paths: string[]): string {
|
79
|
+
let out = '';
|
80
|
+
for (let i = paths.length - 1; i > 0; i--) {
|
81
|
+
out += paths[i];
|
82
|
+
if (this.isAbsolute(out)) {
|
83
|
+
return out;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return __fakeNode_process__.cwd + out;
|
87
|
+
}
|
88
|
+
|
89
|
+
abstract toNamespacedPath(path: string): string;
|
90
|
+
|
91
|
+
};
|
92
|
+
|
93
|
+
|
94
|
+
export const posix = new class extends PathModule {
|
95
|
+
|
96
|
+
sep = '/';
|
97
|
+
delimiter = ':';
|
98
|
+
_normalizeSplit = '/';
|
99
|
+
|
100
|
+
isAbsolute(path: string): boolean {
|
101
|
+
return path.startsWith(this.sep);
|
102
|
+
}
|
103
|
+
|
104
|
+
toNamespacedPath(path: string): string {
|
105
|
+
return path;
|
106
|
+
}
|
107
|
+
|
108
|
+
};
|
109
|
+
|
110
|
+
|
111
|
+
export const win32 = new class extends PathModule {
|
112
|
+
|
113
|
+
sep = '\\';
|
114
|
+
delimiter = ';';
|
115
|
+
_normalizeSplit = /\/|\\/;
|
116
|
+
|
117
|
+
isAbsolute(path: string): boolean {
|
118
|
+
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.includes(path[0]) && path[1] === ':' && path[2] === this.sep;
|
119
|
+
}
|
120
|
+
|
121
|
+
toNamespacedPath(path: string): string {
|
122
|
+
throw new TypeError('path.windows.toNamespacedPath is not supported in fake-node');
|
123
|
+
}
|
124
|
+
|
125
|
+
};
|
126
|
+
|
127
|
+
|
128
|
+
export const basename = posix.basename;
|
129
|
+
export const delimiter = posix.delimiter;
|
130
|
+
export const dirname = posix.dirname;
|
131
|
+
export const extname = posix.extname;
|
132
|
+
export const format = posix.format;
|
133
|
+
export const matchesGlob = posix.matchesGlob;
|
134
|
+
export const isAbsolute = posix.isAbsolute;
|
135
|
+
export const join = posix.join;
|
136
|
+
export const normalize = posix.normalize;
|
137
|
+
export const parse = posix.parse;
|
138
|
+
export const relative = posix.relative;
|
139
|
+
export const resolve = posix.resolve;
|
140
|
+
export const sep = posix.sep;
|
141
|
+
export const toNamespacedPath = posix.toNamespacedPath;
|
package/src/process.ts
CHANGED
@@ -1,18 +1,14 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
/// <reference path="./in_fake_node.d.ts" />
|
3
3
|
|
4
4
|
export function abort(): void {
|
5
5
|
__fakeNode__.window.close();
|
6
6
|
}
|
7
|
-
|
7
|
+
|
8
8
|
export const allowedNodeEnvironmentFlags = new Set<never>();
|
9
9
|
|
10
10
|
export const arch = 'fake';
|
11
11
|
|
12
|
-
// export const argv = __fakeNode_process__.argv;
|
13
|
-
|
14
|
-
// export const argv0 = __fakeNode_process__.argv0;
|
15
|
-
|
16
12
|
export const channel = undefined;
|
17
13
|
|
18
14
|
export function chdir(path: string): void {
|
@@ -51,12 +47,6 @@ export function emitWarning(warning: string | Error, type_or_options: string | {
|
|
51
47
|
throw new TypeError('process.emitWarning is not supported in fake-node');
|
52
48
|
}
|
53
49
|
|
54
|
-
// export const env = __fakeNode_process__.env;
|
55
|
-
|
56
|
-
// export const execArgv = __fakeNode_process__.execArgv;
|
57
|
-
|
58
|
-
// export const execPath = __fakeNode_process__.execPath;
|
59
|
-
|
60
50
|
export function exit(code: number = 0): void {
|
61
51
|
window.console.log('Exit code', code);
|
62
52
|
window.close();
|
@@ -152,8 +142,6 @@ export function loadEnvFile(path: string = './.env'): void {
|
|
152
142
|
throw new TypeError('process.loadEnvFile is not supported in fake-node');
|
153
143
|
}
|
154
144
|
|
155
|
-
// export const mainModule = __fakeNode_process__.path === '' ? undefined : __fakeNode_process__.path;
|
156
|
-
|
157
145
|
export function memoryUsage(): {rss: number, heapTotal: number, heapUsed: number, external: number, arrayBuffers: number} {
|
158
146
|
throw new TypeError('process.memoryUsage is not supported in fake-node');
|
159
147
|
}
|
@@ -182,8 +170,6 @@ export function ref(maybeRefable: any): void {
|
|
182
170
|
|
183
171
|
export const pid = 1;
|
184
172
|
|
185
|
-
// export const platform = _platform();
|
186
|
-
|
187
173
|
export const ppid = 1;
|
188
174
|
|
189
175
|
export const release = {
|
@@ -257,7 +243,3 @@ export function unref(maybeRefable: any): void {
|
|
257
243
|
export function uptime(): number {
|
258
244
|
return __fakeNode_process__.fakeNode.window.performance.now() / 1000;
|
259
245
|
}
|
260
|
-
|
261
|
-
// export const version = __fakeNode__.version;
|
262
|
-
|
263
|
-
// export const versions = [version];
|