@php-wasm/universal 0.1.39 → 0.1.41
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/index.cjs +40 -0
- package/index.js +1070 -0
- package/lib/base-php.d.ts +70 -0
- package/lib/error-event-polyfill.d.ts +30 -0
- package/{src/lib/index.ts → lib/index.d.ts} +4 -36
- package/lib/is-local-php.d.ts +2 -0
- package/lib/is-remote-php.d.ts +2 -0
- package/{src/lib/load-php-runtime.ts → lib/load-php-runtime.d.ts} +23 -100
- package/lib/php-browser.d.ts +52 -0
- package/lib/php-request-handler.d.ts +43 -0
- package/lib/php-response.d.ts +54 -0
- package/lib/rethrow-file-system-error.d.ts +13 -0
- package/lib/supported-php-versions.d.ts +4 -0
- package/lib/universal-php.d.ts +309 -0
- package/{src/lib/urls.ts → lib/urls.d.ts} +4 -19
- package/lib/wasm-error-reporting.d.ts +26 -0
- package/package.json +4 -3
- package/.eslintrc.json +0 -18
- package/LICENSE +0 -339
- package/README.md +0 -0
- package/project.json +0 -50
- package/src/lib/base-php.ts +0 -555
- package/src/lib/error-event-polyfill.ts +0 -50
- package/src/lib/is-local-php.ts +0 -8
- package/src/lib/is-remote-php.ts +0 -8
- package/src/lib/php-browser.ts +0 -137
- package/src/lib/php-request-handler.ts +0 -381
- package/src/lib/php-response.ts +0 -104
- package/src/lib/rethrow-file-system-error.ts +0 -125
- package/src/lib/supported-php-versions.ts +0 -14
- package/src/lib/universal-php.ts +0 -354
- package/src/lib/wasm-error-reporting.ts +0 -172
- package/tsconfig.json +0 -22
- package/tsconfig.lib.json +0 -14
- package/tsconfig.spec.json +0 -20
- package/vite.config.ts +0 -47
- /package/{src/index.ts → index.d.ts} +0 -0
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Emscripten's filesystem-related Exception.
|
|
3
|
-
*
|
|
4
|
-
* @see https://emscripten.org/docs/api_reference/Filesystem-API.html
|
|
5
|
-
* @see https://github.com/emscripten-core/emscripten/blob/main/system/lib/libc/musl/arch/emscripten/bits/errno.h
|
|
6
|
-
* @see https://github.com/emscripten-core/emscripten/blob/38eedc630f17094b3202fd48ac0c2c585dbea31e/system/include/wasi/api.h#L336
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface ErrnoError extends Error {
|
|
10
|
-
node?: any;
|
|
11
|
-
errno: number;
|
|
12
|
-
message: string;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* @see https://github.com/emscripten-core/emscripten/blob/38eedc630f17094b3202fd48ac0c2c585dbea31e/system/include/wasi/api.h#L336
|
|
16
|
-
*/
|
|
17
|
-
const FileErrorCodes = {
|
|
18
|
-
0: 'No error occurred. System call completed successfully.',
|
|
19
|
-
1: 'Argument list too long.',
|
|
20
|
-
2: 'Permission denied.',
|
|
21
|
-
3: 'Address in use.',
|
|
22
|
-
4: 'Address not available.',
|
|
23
|
-
5: 'Address family not supported.',
|
|
24
|
-
6: 'Resource unavailable, or operation would block.',
|
|
25
|
-
7: 'Connection already in progress.',
|
|
26
|
-
8: 'Bad file descriptor.',
|
|
27
|
-
9: 'Bad message.',
|
|
28
|
-
10: 'Device or resource busy.',
|
|
29
|
-
11: 'Operation canceled.',
|
|
30
|
-
12: 'No child processes.',
|
|
31
|
-
13: 'Connection aborted.',
|
|
32
|
-
14: 'Connection refused.',
|
|
33
|
-
15: 'Connection reset.',
|
|
34
|
-
16: 'Resource deadlock would occur.',
|
|
35
|
-
17: 'Destination address required.',
|
|
36
|
-
18: 'Mathematics argument out of domain of function.',
|
|
37
|
-
19: 'Reserved.',
|
|
38
|
-
20: 'File exists.',
|
|
39
|
-
21: 'Bad address.',
|
|
40
|
-
22: 'File too large.',
|
|
41
|
-
23: 'Host is unreachable.',
|
|
42
|
-
24: 'Identifier removed.',
|
|
43
|
-
25: 'Illegal byte sequence.',
|
|
44
|
-
26: 'Operation in progress.',
|
|
45
|
-
27: 'Interrupted function.',
|
|
46
|
-
28: 'Invalid argument.',
|
|
47
|
-
29: 'I/O error.',
|
|
48
|
-
30: 'Socket is connected.',
|
|
49
|
-
31: 'There is a directory under that path.',
|
|
50
|
-
32: 'Too many levels of symbolic links.',
|
|
51
|
-
33: 'File descriptor value too large.',
|
|
52
|
-
34: 'Too many links.',
|
|
53
|
-
35: 'Message too large.',
|
|
54
|
-
36: 'Reserved.',
|
|
55
|
-
37: 'Filename too long.',
|
|
56
|
-
38: 'Network is down.',
|
|
57
|
-
39: 'Connection aborted by network.',
|
|
58
|
-
40: 'Network unreachable.',
|
|
59
|
-
41: 'Too many files open in system.',
|
|
60
|
-
42: 'No buffer space available.',
|
|
61
|
-
43: 'No such device.',
|
|
62
|
-
44: 'There is no such file or directory OR the parent directory does not exist.',
|
|
63
|
-
45: 'Executable file format error.',
|
|
64
|
-
46: 'No locks available.',
|
|
65
|
-
47: 'Reserved.',
|
|
66
|
-
48: 'Not enough space.',
|
|
67
|
-
49: 'No message of the desired type.',
|
|
68
|
-
50: 'Protocol not available.',
|
|
69
|
-
51: 'No space left on device.',
|
|
70
|
-
52: 'Function not supported.',
|
|
71
|
-
53: 'The socket is not connected.',
|
|
72
|
-
54: 'Not a directory or a symbolic link to a directory.',
|
|
73
|
-
55: 'Directory not empty.',
|
|
74
|
-
56: 'State not recoverable.',
|
|
75
|
-
57: 'Not a socket.',
|
|
76
|
-
58: 'Not supported, or operation not supported on socket.',
|
|
77
|
-
59: 'Inappropriate I/O control operation.',
|
|
78
|
-
60: 'No such device or address.',
|
|
79
|
-
61: 'Value too large to be stored in data type.',
|
|
80
|
-
62: 'Previous owner died.',
|
|
81
|
-
63: 'Operation not permitted.',
|
|
82
|
-
64: 'Broken pipe.',
|
|
83
|
-
65: 'Protocol error.',
|
|
84
|
-
66: 'Protocol not supported.',
|
|
85
|
-
67: 'Protocol wrong type for socket.',
|
|
86
|
-
68: 'Result too large.',
|
|
87
|
-
69: 'Read-only file system.',
|
|
88
|
-
70: 'Invalid seek.',
|
|
89
|
-
71: 'No such process.',
|
|
90
|
-
72: 'Reserved.',
|
|
91
|
-
73: 'Connection timed out.',
|
|
92
|
-
74: 'Text file busy.',
|
|
93
|
-
75: 'Cross-device link.',
|
|
94
|
-
76: 'Extension: Capabilities insufficient.',
|
|
95
|
-
} as any;
|
|
96
|
-
export function rethrowFileSystemError(messagePrefix = '') {
|
|
97
|
-
return function catchFileSystemError(
|
|
98
|
-
target: any,
|
|
99
|
-
methodName: string,
|
|
100
|
-
descriptor: PropertyDescriptor
|
|
101
|
-
) {
|
|
102
|
-
const method = descriptor.value;
|
|
103
|
-
descriptor.value = function (...args: any[]) {
|
|
104
|
-
try {
|
|
105
|
-
return method.apply(this, args);
|
|
106
|
-
} catch (e) {
|
|
107
|
-
const errno =
|
|
108
|
-
typeof e === 'object' ? ((e as any)?.errno as any) : null;
|
|
109
|
-
if (errno in FileErrorCodes) {
|
|
110
|
-
const errmsg = FileErrorCodes[errno];
|
|
111
|
-
const path = typeof args[0] === 'string' ? args[0] : null;
|
|
112
|
-
const formattedPrefix =
|
|
113
|
-
path !== null
|
|
114
|
-
? messagePrefix.replaceAll('{path}', path)
|
|
115
|
-
: messagePrefix;
|
|
116
|
-
throw new Error(`${formattedPrefix}: ${errmsg}`, {
|
|
117
|
-
cause: e,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
throw e;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const SupportedPHPVersions = [
|
|
2
|
-
'8.2',
|
|
3
|
-
'8.1',
|
|
4
|
-
'8.0',
|
|
5
|
-
'7.4',
|
|
6
|
-
'7.3',
|
|
7
|
-
'7.2',
|
|
8
|
-
'7.1',
|
|
9
|
-
'7.0',
|
|
10
|
-
'5.6',
|
|
11
|
-
] as const;
|
|
12
|
-
export const LatestSupportedPHPVersion = SupportedPHPVersions[0];
|
|
13
|
-
export const SupportedPHPVersionsList = SupportedPHPVersions as any as string[];
|
|
14
|
-
export type SupportedPHPVersion = (typeof SupportedPHPVersions)[number];
|
package/src/lib/universal-php.ts
DELETED
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import { Remote } from 'comlink';
|
|
2
|
-
import { PHPResponse } from './php-response';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Handles HTTP requests using PHP runtime as a backend.
|
|
6
|
-
*
|
|
7
|
-
* @public
|
|
8
|
-
* @example Use PHPRequestHandler implicitly with a new PHP instance:
|
|
9
|
-
* ```js
|
|
10
|
-
* import { PHP } from '@php-wasm/web';
|
|
11
|
-
*
|
|
12
|
-
* const php = await PHP.load( '7.4', {
|
|
13
|
-
* requestHandler: {
|
|
14
|
-
* // PHP FS path to serve the files from:
|
|
15
|
-
* documentRoot: '/www',
|
|
16
|
-
*
|
|
17
|
-
* // Used to populate $_SERVER['SERVER_NAME'] etc.:
|
|
18
|
-
* absoluteUrl: 'http://127.0.0.1'
|
|
19
|
-
* }
|
|
20
|
-
* } );
|
|
21
|
-
*
|
|
22
|
-
* php.mkdirTree('/www');
|
|
23
|
-
* php.writeFile('/www/index.php', '<?php echo "Hi from PHP!"; ');
|
|
24
|
-
*
|
|
25
|
-
* const response = await php.request({ path: '/index.php' });
|
|
26
|
-
* console.log(response.text);
|
|
27
|
-
* // "Hi from PHP!"
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* @example Explicitly create a PHPRequestHandler instance and run a PHP script:
|
|
31
|
-
* ```js
|
|
32
|
-
* import {
|
|
33
|
-
* loadPHPRuntime,
|
|
34
|
-
* PHP,
|
|
35
|
-
* PHPRequestHandler,
|
|
36
|
-
* getPHPLoaderModule,
|
|
37
|
-
* } from '@php-wasm/web';
|
|
38
|
-
*
|
|
39
|
-
* const runtime = await loadPHPRuntime( await getPHPLoaderModule('7.4') );
|
|
40
|
-
* const php = new PHP( runtime );
|
|
41
|
-
*
|
|
42
|
-
* php.mkdirTree('/www');
|
|
43
|
-
* php.writeFile('/www/index.php', '<?php echo "Hi from PHP!"; ');
|
|
44
|
-
*
|
|
45
|
-
* const server = new PHPRequestHandler(php, {
|
|
46
|
-
* // PHP FS path to serve the files from:
|
|
47
|
-
* documentRoot: '/www',
|
|
48
|
-
*
|
|
49
|
-
* // Used to populate $_SERVER['SERVER_NAME'] etc.:
|
|
50
|
-
* absoluteUrl: 'http://127.0.0.1'
|
|
51
|
-
* });
|
|
52
|
-
*
|
|
53
|
-
* const response = server.request({ path: '/index.php' });
|
|
54
|
-
* console.log(response.text);
|
|
55
|
-
* // "Hi from PHP!"
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export interface RequestHandler {
|
|
59
|
-
/**
|
|
60
|
-
* Serves the request – either by serving a static file, or by
|
|
61
|
-
* dispatching it to the PHP runtime.
|
|
62
|
-
* Cannot be used in conjunction with `cli()`.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```js
|
|
66
|
-
* const output = await php.request({
|
|
67
|
-
* method: 'GET',
|
|
68
|
-
* url: '/index.php',
|
|
69
|
-
* headers: {
|
|
70
|
-
* 'X-foo': 'bar',
|
|
71
|
-
* },
|
|
72
|
-
* formData: {
|
|
73
|
-
* foo: 'bar',
|
|
74
|
-
* },
|
|
75
|
-
* });
|
|
76
|
-
* console.log(output.stdout); // "Hello world!"
|
|
77
|
-
* ```
|
|
78
|
-
*
|
|
79
|
-
* @param request - PHP Request data.
|
|
80
|
-
*/
|
|
81
|
-
request(request: PHPRequest, maxRedirects?: number): Promise<PHPResponse>;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Converts a path to an absolute URL based at the PHPRequestHandler
|
|
85
|
-
* root.
|
|
86
|
-
*
|
|
87
|
-
* @param path The server path to convert to an absolute URL.
|
|
88
|
-
* @returns The absolute URL.
|
|
89
|
-
*/
|
|
90
|
-
pathToInternalUrl(path: string): string;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Converts an absolute URL based at the PHPRequestHandler to a relative path
|
|
94
|
-
* without the server pathname and scope.
|
|
95
|
-
*
|
|
96
|
-
* @param internalUrl An absolute URL based at the PHPRequestHandler root.
|
|
97
|
-
* @returns The relative path.
|
|
98
|
-
*/
|
|
99
|
-
internalUrlToPath(internalUrl: string): string;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* The absolute URL of this PHPRequestHandler instance.
|
|
103
|
-
*/
|
|
104
|
-
absoluteUrl: string;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* The directory in the PHP filesystem where the server will look
|
|
108
|
-
* for the files to serve. Default: `/var/www`.
|
|
109
|
-
*/
|
|
110
|
-
documentRoot: string;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface IsomorphicLocalPHP extends RequestHandler {
|
|
114
|
-
setPhpIniPath(path: string): void;
|
|
115
|
-
setPhpIniEntry(key: string, value: string): void;
|
|
116
|
-
/**
|
|
117
|
-
* Recursively creates a directory with the given path in the PHP filesystem.
|
|
118
|
-
* For example, if the path is `/root/php/data`, and `/root` already exists,
|
|
119
|
-
* it will create the directories `/root/php` and `/root/php/data`.
|
|
120
|
-
*
|
|
121
|
-
* @param path - The directory path to create.
|
|
122
|
-
*/
|
|
123
|
-
mkdir(path: string): void;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* @deprecated Use mkdir instead.
|
|
127
|
-
*/
|
|
128
|
-
mkdirTree(path: string): void;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Reads a file from the PHP filesystem and returns it as a string.
|
|
132
|
-
*
|
|
133
|
-
* @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
|
|
134
|
-
* @param path - The file path to read.
|
|
135
|
-
* @returns The file contents.
|
|
136
|
-
*/
|
|
137
|
-
readFileAsText(path: string): string;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Reads a file from the PHP filesystem and returns it as an array buffer.
|
|
141
|
-
*
|
|
142
|
-
* @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
|
|
143
|
-
* @param path - The file path to read.
|
|
144
|
-
* @returns The file contents.
|
|
145
|
-
*/
|
|
146
|
-
readFileAsBuffer(path: string): Uint8Array;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Overwrites data in a file in the PHP filesystem.
|
|
150
|
-
* Creates a new file if one doesn't exist yet.
|
|
151
|
-
*
|
|
152
|
-
* @param path - The file path to write to.
|
|
153
|
-
* @param data - The data to write to the file.
|
|
154
|
-
*/
|
|
155
|
-
writeFile(path: string, data: string | Uint8Array): void;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Removes a file from the PHP filesystem.
|
|
159
|
-
*
|
|
160
|
-
* @throws {@link @php-wasm/universal:ErrnoError} – If the file doesn't exist.
|
|
161
|
-
* @param path - The file path to remove.
|
|
162
|
-
*/
|
|
163
|
-
unlink(path: string): void;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Moves a file or directory in the PHP filesystem to a
|
|
167
|
-
* new location.
|
|
168
|
-
*
|
|
169
|
-
* @param oldPath The path to rename.
|
|
170
|
-
* @param newPath The new path.
|
|
171
|
-
*/
|
|
172
|
-
mv(oldPath: string, newPath: string): void;
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Removes a directory from the PHP filesystem.
|
|
176
|
-
*
|
|
177
|
-
* @param path The directory path to remove.
|
|
178
|
-
* @param options Options for the removal.
|
|
179
|
-
*/
|
|
180
|
-
rmdir(path: string, options?: RmDirOptions): void;
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Lists the files and directories in the given directory.
|
|
184
|
-
*
|
|
185
|
-
* @param path - The directory path to list.
|
|
186
|
-
* @returns The list of files and directories in the given directory.
|
|
187
|
-
*/
|
|
188
|
-
listFiles(path: string): string[];
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Checks if a directory exists in the PHP filesystem.
|
|
192
|
-
*
|
|
193
|
-
* @param path – The path to check.
|
|
194
|
-
* @returns True if the path is a directory, false otherwise.
|
|
195
|
-
*/
|
|
196
|
-
isDir(path: string): boolean;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Checks if a file (or a directory) exists in the PHP filesystem.
|
|
200
|
-
*
|
|
201
|
-
* @param path - The file path to check.
|
|
202
|
-
* @returns True if the file exists, false otherwise.
|
|
203
|
-
*/
|
|
204
|
-
fileExists(path: string): boolean;
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Changes the current working directory in the PHP filesystem.
|
|
208
|
-
* This is the directory that will be used as the base for relative paths.
|
|
209
|
-
* For example, if the current working directory is `/root/php`, and the
|
|
210
|
-
* path is `data`, the absolute path will be `/root/php/data`.
|
|
211
|
-
*
|
|
212
|
-
* @param path - The new working directory.
|
|
213
|
-
*/
|
|
214
|
-
chdir(path: string): void;
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Runs PHP code.
|
|
218
|
-
* Cannot be used in conjunction with `cli()`.
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* ```js
|
|
222
|
-
* const output = await php.run('<?php echo "Hello world!";');
|
|
223
|
-
* console.log(output.stdout); // "Hello world!"
|
|
224
|
-
* ```
|
|
225
|
-
*
|
|
226
|
-
* @example
|
|
227
|
-
* ```js
|
|
228
|
-
* console.log(await php.run(`<?php
|
|
229
|
-
* $fp = fopen('php://stderr', 'w');
|
|
230
|
-
* fwrite($fp, "Hello, world!");
|
|
231
|
-
* `));
|
|
232
|
-
* // {"exitCode":0,"stdout":"","stderr":["Hello, world!"]}
|
|
233
|
-
* ```
|
|
234
|
-
*
|
|
235
|
-
* @param options - PHP run options.
|
|
236
|
-
*/
|
|
237
|
-
run(options: PHPRunOptions): Promise<PHPResponse>;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export type IsomorphicRemotePHP = Remote<IsomorphicLocalPHP>;
|
|
241
|
-
export type UniversalPHP = IsomorphicLocalPHP | IsomorphicRemotePHP;
|
|
242
|
-
|
|
243
|
-
export type HTTPMethod =
|
|
244
|
-
| 'GET'
|
|
245
|
-
| 'POST'
|
|
246
|
-
| 'HEAD'
|
|
247
|
-
| 'OPTIONS'
|
|
248
|
-
| 'PATCH'
|
|
249
|
-
| 'PUT'
|
|
250
|
-
| 'DELETE';
|
|
251
|
-
export type PHPRequestHeaders = Record<string, string>;
|
|
252
|
-
export interface PHPRequest {
|
|
253
|
-
/**
|
|
254
|
-
* Request method. Default: `GET`.
|
|
255
|
-
*/
|
|
256
|
-
method?: HTTPMethod;
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Request path or absolute URL.
|
|
260
|
-
*/
|
|
261
|
-
url: string;
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Request headers.
|
|
265
|
-
*/
|
|
266
|
-
headers?: PHPRequestHeaders;
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Uploaded files
|
|
270
|
-
*/
|
|
271
|
-
files?: Record<string, File>;
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Request body without the files.
|
|
275
|
-
*/
|
|
276
|
-
body?: string;
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Form data. If set, the request body will be ignored and
|
|
280
|
-
* the content-type header will be set to `application/x-www-form-urlencoded`.
|
|
281
|
-
*/
|
|
282
|
-
formData?: Record<string, unknown>;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export interface PHPRunOptions {
|
|
286
|
-
/**
|
|
287
|
-
* Request path following the domain:port part.
|
|
288
|
-
*/
|
|
289
|
-
relativeUri?: string;
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Path of the .php file to execute.
|
|
293
|
-
*/
|
|
294
|
-
scriptPath?: string;
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Request protocol.
|
|
298
|
-
*/
|
|
299
|
-
protocol?: string;
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Request method. Default: `GET`.
|
|
303
|
-
*/
|
|
304
|
-
method?: HTTPMethod;
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Request headers.
|
|
308
|
-
*/
|
|
309
|
-
headers?: PHPRequestHeaders;
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Request body without the files.
|
|
313
|
-
*/
|
|
314
|
-
body?: string;
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Uploaded files.
|
|
318
|
-
*/
|
|
319
|
-
fileInfos?: FileInfo[];
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* The code snippet to eval instead of a php file.
|
|
323
|
-
*/
|
|
324
|
-
code?: string;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Output of the PHP.wasm runtime.
|
|
329
|
-
*/
|
|
330
|
-
export interface PHPOutput {
|
|
331
|
-
/** Exit code of the PHP process. 0 means success, 1 and 2 mean error. */
|
|
332
|
-
exitCode: number;
|
|
333
|
-
|
|
334
|
-
/** Stdout data */
|
|
335
|
-
stdout: ArrayBuffer;
|
|
336
|
-
|
|
337
|
-
/** Stderr lines */
|
|
338
|
-
stderr: string[];
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export interface FileInfo {
|
|
342
|
-
key: string;
|
|
343
|
-
name: string;
|
|
344
|
-
type: string;
|
|
345
|
-
data: Uint8Array;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export interface RmDirOptions {
|
|
349
|
-
/**
|
|
350
|
-
* If true, recursively removes the directory and all its contents.
|
|
351
|
-
* Default: true.
|
|
352
|
-
*/
|
|
353
|
-
recursive?: boolean;
|
|
354
|
-
}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { ErrorEvent } from './error-event-polyfill';
|
|
2
|
-
|
|
3
|
-
type Runtime = {
|
|
4
|
-
asm: Record<string, unknown>;
|
|
5
|
-
lastAsyncifyStackSource?: Error;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export class UnhandledRejectionsTarget extends EventTarget {
|
|
9
|
-
listenersCount = 0;
|
|
10
|
-
override addEventListener(type: unknown, callback: unknown): void {
|
|
11
|
-
++this.listenersCount;
|
|
12
|
-
super.addEventListener(type as string, callback as EventListener);
|
|
13
|
-
}
|
|
14
|
-
override removeEventListener(type: unknown, callback: unknown): void {
|
|
15
|
-
--this.listenersCount;
|
|
16
|
-
super.removeEventListener(type as string, callback as EventListener);
|
|
17
|
-
}
|
|
18
|
-
hasListeners() {
|
|
19
|
-
return this.listenersCount > 0;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Creates Asyncify errors listener.
|
|
25
|
-
*
|
|
26
|
-
* Emscripten turns Asyncify errors into unhandled rejections by
|
|
27
|
-
* throwing them outside of the context of the original function call.
|
|
28
|
-
*
|
|
29
|
-
* With this listener, we can catch and rethrow them in a proper context,
|
|
30
|
-
* or at least log them in a more readable way.
|
|
31
|
-
*
|
|
32
|
-
* @param runtime
|
|
33
|
-
*/
|
|
34
|
-
export function improveWASMErrorReporting(runtime: Runtime) {
|
|
35
|
-
runtime.asm = {
|
|
36
|
-
...runtime.asm,
|
|
37
|
-
};
|
|
38
|
-
const target = new UnhandledRejectionsTarget();
|
|
39
|
-
for (const key in runtime.asm) {
|
|
40
|
-
if (typeof runtime.asm[key] == 'function') {
|
|
41
|
-
const original = runtime.asm[key] as any;
|
|
42
|
-
runtime.asm[key] = function (...args: any[]) {
|
|
43
|
-
try {
|
|
44
|
-
return original(...args);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
if (!(e instanceof Error)) {
|
|
47
|
-
throw e;
|
|
48
|
-
}
|
|
49
|
-
if ('exitCode' in e && e?.exitCode === 0) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const clearMessage = clarifyErrorMessage(
|
|
53
|
-
e,
|
|
54
|
-
runtime.lastAsyncifyStackSource?.stack
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (runtime.lastAsyncifyStackSource) {
|
|
58
|
-
e.cause = runtime.lastAsyncifyStackSource;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (!target.hasListeners()) {
|
|
62
|
-
showCriticalErrorBox(clearMessage);
|
|
63
|
-
throw e;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
target.dispatchEvent(
|
|
67
|
-
new ErrorEvent('error', {
|
|
68
|
-
error: e,
|
|
69
|
-
message: clearMessage,
|
|
70
|
-
})
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return target;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let functionsMaybeMissingFromAsyncify: string[] = [];
|
|
80
|
-
export function getFunctionsMaybeMissingFromAsyncify() {
|
|
81
|
-
return functionsMaybeMissingFromAsyncify;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function clarifyErrorMessage(
|
|
85
|
-
crypticError: Error,
|
|
86
|
-
asyncifyStack?: string
|
|
87
|
-
) {
|
|
88
|
-
if (crypticError.message === 'unreachable') {
|
|
89
|
-
let betterMessage = UNREACHABLE_ERROR;
|
|
90
|
-
if (!asyncifyStack) {
|
|
91
|
-
betterMessage +=
|
|
92
|
-
`\n\nThis stack trace is lacking. For a better one initialize \n` +
|
|
93
|
-
`the PHP runtime with { debug: true }, e.g. PHPNode.load('8.1', { debug: true }).\n\n`;
|
|
94
|
-
}
|
|
95
|
-
functionsMaybeMissingFromAsyncify = extractPHPFunctionsFromStack(
|
|
96
|
-
asyncifyStack || crypticError.stack || ''
|
|
97
|
-
);
|
|
98
|
-
for (const fn of functionsMaybeMissingFromAsyncify) {
|
|
99
|
-
betterMessage += ` * ${fn}\n`;
|
|
100
|
-
}
|
|
101
|
-
return betterMessage;
|
|
102
|
-
}
|
|
103
|
-
return crypticError.message;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const UNREACHABLE_ERROR = `
|
|
107
|
-
"unreachable" WASM instruction executed.
|
|
108
|
-
|
|
109
|
-
The typical reason is a PHP function missing from the ASYNCIFY_ONLY
|
|
110
|
-
list when building PHP.wasm.
|
|
111
|
-
|
|
112
|
-
You will need to file a new issue in the WordPress Playground repository
|
|
113
|
-
and paste this error message there:
|
|
114
|
-
|
|
115
|
-
https://github.com/WordPress/wordpress-playground/issues/new
|
|
116
|
-
|
|
117
|
-
If you're a core developer, the typical fix is to:
|
|
118
|
-
|
|
119
|
-
* Isolate a minimal reproduction of the error
|
|
120
|
-
* Add a reproduction of the error to php-asyncify.spec.ts in the WordPress Playground repository
|
|
121
|
-
* Run 'npm run fix-asyncify'
|
|
122
|
-
* Commit the changes, push to the repo, release updated NPM packages
|
|
123
|
-
|
|
124
|
-
Below is a list of all the PHP functions found in the stack trace to
|
|
125
|
-
help with the minimal reproduction. If they're all already listed in
|
|
126
|
-
the Dockerfile, you'll need to trigger this error again with long stack
|
|
127
|
-
traces enabled. In node.js, you can do it using the --stack-trace-limit=100
|
|
128
|
-
CLI option: \n\n`;
|
|
129
|
-
|
|
130
|
-
// ANSI escape codes for CLI colors and formats
|
|
131
|
-
const redBg = '\x1b[41m';
|
|
132
|
-
const bold = '\x1b[1m';
|
|
133
|
-
const reset = '\x1b[0m';
|
|
134
|
-
const eol = '\x1B[K';
|
|
135
|
-
|
|
136
|
-
let logged = false;
|
|
137
|
-
export function showCriticalErrorBox(message: string) {
|
|
138
|
-
if (logged) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
logged = true;
|
|
142
|
-
console.log(`${redBg}\n${eol}\n${bold} WASM ERROR${reset}${redBg}`);
|
|
143
|
-
for (const line of message.split('\n')) {
|
|
144
|
-
console.log(`${eol} ${line} `);
|
|
145
|
-
}
|
|
146
|
-
console.log(`${reset}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function extractPHPFunctionsFromStack(stack: string) {
|
|
150
|
-
try {
|
|
151
|
-
const names = stack
|
|
152
|
-
.split('\n')
|
|
153
|
-
.slice(1)
|
|
154
|
-
.map((line) => {
|
|
155
|
-
const parts = line.trim().substring('at '.length).split(' ');
|
|
156
|
-
return {
|
|
157
|
-
fn: parts.length >= 2 ? parts[0] : '<unknown>',
|
|
158
|
-
isWasm: line.includes('wasm://'),
|
|
159
|
-
};
|
|
160
|
-
})
|
|
161
|
-
.filter(
|
|
162
|
-
({ fn, isWasm }) =>
|
|
163
|
-
isWasm &&
|
|
164
|
-
!fn.startsWith('dynCall_') &&
|
|
165
|
-
!fn.startsWith('invoke_')
|
|
166
|
-
)
|
|
167
|
-
.map(({ fn }) => fn);
|
|
168
|
-
return Array.from(new Set(names));
|
|
169
|
-
} catch (err) {
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"forceConsistentCasingInFileNames": true,
|
|
5
|
-
"strict": true,
|
|
6
|
-
"noImplicitOverride": true,
|
|
7
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
8
|
-
"noImplicitReturns": true,
|
|
9
|
-
"noFallthroughCasesInSwitch": true,
|
|
10
|
-
"types": ["vitest", "vite/client"]
|
|
11
|
-
},
|
|
12
|
-
"files": [],
|
|
13
|
-
"include": [],
|
|
14
|
-
"references": [
|
|
15
|
-
{
|
|
16
|
-
"path": "./tsconfig.lib.json"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"path": "./tsconfig.spec.json"
|
|
20
|
-
}
|
|
21
|
-
]
|
|
22
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../../dist/out-tsc",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"types": ["node"]
|
|
7
|
-
},
|
|
8
|
-
"include": [
|
|
9
|
-
"src/**/*.ts",
|
|
10
|
-
"../web/src/php-library/parse-worker-startup-options.ts",
|
|
11
|
-
"../web/src/lib/api.ts"
|
|
12
|
-
],
|
|
13
|
-
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
|
14
|
-
}
|