@php-wasm/universal 0.1.40 → 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.
@@ -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];
@@ -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
- }