@wp-playground/client 0.7.0 → 0.7.3

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.
Files changed (4) hide show
  1. package/index.cjs +23 -27
  2. package/index.d.ts +232 -110
  3. package/index.js +1408 -1741
  4. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -183,6 +183,7 @@ export declare class PHPResponse implements PHPResponseData {
183
183
  /** @inheritDoc */
184
184
  readonly httpStatusCode: number;
185
185
  constructor(httpStatusCode: number, headers: Record<string, string[]>, body: ArrayBuffer, errors?: string, exitCode?: number);
186
+ static forHttpCode(httpStatusCode: number, text?: string): PHPResponse;
186
187
  static fromRawData(data: PHPResponseData): PHPResponse;
187
188
  toRawData(): PHPResponseData;
188
189
  /**
@@ -487,7 +488,13 @@ export type ChildProcess = EventEmitter & {
487
488
  stderr: EventEmitter;
488
489
  };
489
490
  export type SpawnHandler = (command: string, args: string[]) => ChildProcess;
490
- export type IsomorphicRemotePHP = Remote<IsomorphicLocalPHP>;
491
+ /**
492
+ * The omited methods must either be called synchronously before
493
+ * the PHP internal state is initialized, or with a complex argument
494
+ * that can't be serialized over a remote connection. Therefeore,
495
+ * they don't make sense in a remote PHP instance.
496
+ */
497
+ export type IsomorphicRemotePHP = Remote<Omit<IsomorphicLocalPHP, "setSapiName" | "setPhpIniEntry" | "setPhpIniPath">>;
491
498
  export type UniversalPHP = IsomorphicLocalPHP | IsomorphicRemotePHP;
492
499
  export type HTTPMethod = "GET" | "POST" | "HEAD" | "OPTIONS" | "PATCH" | "PUT" | "DELETE";
493
500
  export type PHPRequestHeaders = Record<string, string>;
@@ -574,39 +581,60 @@ export interface ListFilesOptions {
574
581
  */
575
582
  prependPath: boolean;
576
583
  }
577
- /**
578
- * Emscripten's filesystem-related Exception.
579
- *
580
- * @see https://emscripten.org/docs/api_reference/Filesystem-API.html
581
- * @see https://github.com/emscripten-core/emscripten/blob/main/system/lib/libc/musl/arch/emscripten/bits/errno.h
582
- * @see https://github.com/emscripten-core/emscripten/blob/38eedc630f17094b3202fd48ac0c2c585dbea31e/system/include/wasi/api.h#L336
583
- */
584
- export interface ErrnoError extends Error {
585
- node?: any;
586
- errno: number;
587
- message: string;
584
+ export type RuntimeType = "NODE" | "WEB" | "WORKER";
585
+ export type PHPRuntimeId = number;
586
+ export type PHPRuntime = any;
587
+ export type PHPLoaderModule = {
588
+ dependencyFilename: string;
589
+ dependenciesTotalSize: number;
590
+ init: (jsRuntime: string, options: EmscriptenOptions) => PHPRuntime;
591
+ };
592
+ export type EmscriptenOptions = {
593
+ onAbort?: (message: string) => void;
594
+ /**
595
+ * Set to true for debugging tricky WebAssembly errors.
596
+ */
597
+ debug?: boolean;
598
+ ENV?: Record<string, string>;
599
+ locateFile?: (path: string) => string;
600
+ noInitialRun?: boolean;
601
+ print?: (message: string) => void;
602
+ printErr?: (message: string) => void;
603
+ quit?: (status: number, toThrow: any) => void;
604
+ onRuntimeInitialized?: () => void;
605
+ monitorRunDependencies?: (left: number) => void;
606
+ onMessage?: (listener: EmscriptenMessageListener) => void;
607
+ instantiateWasm?: (info: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => void;
608
+ } & Record<string, any>;
609
+ export type EmscriptenMessageListener = (type: string, data: string) => void;
610
+ export interface SemaphoreOptions {
611
+ /**
612
+ * The maximum number of concurrent locks.
613
+ */
614
+ concurrency: number;
615
+ /**
616
+ * The maximum time to wait for a lock to become available.
617
+ */
618
+ timeout?: number;
588
619
  }
589
- export declare const SupportedPHPVersions: readonly [
590
- "8.3",
591
- "8.2",
592
- "8.1",
593
- "8.0",
594
- "7.4",
595
- "7.3",
596
- "7.2",
597
- "7.1",
598
- "7.0"
599
- ];
600
- export declare const LatestSupportedPHPVersion: "8.3";
601
- export declare const SupportedPHPVersionsList: string[];
602
- export type SupportedPHPVersion = (typeof SupportedPHPVersions)[number];
603
- export type SupportedPHPExtension = "iconv" | "mbstring" | "xml-bundle" | "gd";
604
- export type SupportedPHPExtensionBundle = "kitchen-sink" | "light";
620
+ declare class Semaphore {
621
+ private _running;
622
+ private concurrency;
623
+ private timeout?;
624
+ private queue;
625
+ constructor({ concurrency, timeout }: SemaphoreOptions);
626
+ get remaining(): number;
627
+ get running(): number;
628
+ acquire(): Promise<() => void>;
629
+ run<T>(fn: () => T | Promise<T>): Promise<T>;
630
+ }
631
+ export declare function phpVar(value: unknown): string;
632
+ export declare function phpVars<T extends Record<string, unknown>>(vars: T): Record<keyof T, string>;
605
633
  export type RewriteRule = {
606
634
  match: RegExp;
607
635
  replacement: string;
608
636
  };
609
- export interface PHPRequestHandlerConfiguration {
637
+ export interface BaseConfiguration {
610
638
  /**
611
639
  * The directory in the PHP filesystem where the server will look
612
640
  * for the files to serve. Default: `/var/www`.
@@ -621,6 +649,30 @@ export interface PHPRequestHandlerConfiguration {
621
649
  */
622
650
  rewriteRules?: RewriteRule[];
623
651
  }
652
+ export type PHPRequestHandlerFactoryArgs<PHP extends BasePHP> = PHPFactoryOptions & {
653
+ requestHandler: PHPRequestHandler<PHP>;
654
+ };
655
+ export type PHPRequestHandlerConfiguration<PHP extends BasePHP> = BaseConfiguration & ({
656
+ /**
657
+ * PHPProcessManager is required because the request handler needs
658
+ * to make a decision for each request.
659
+ *
660
+ * Static assets are served using the primary PHP's filesystem, even
661
+ * when serving 100 static files concurrently. No new PHP interpreter
662
+ * is ever created as there's no need for it.
663
+ *
664
+ * Dynamic PHP requests, however, require grabbing an available PHP
665
+ * interpreter, and that's where the PHPProcessManager comes in.
666
+ */
667
+ processManager: PHPProcessManager<PHP>;
668
+ } | {
669
+ phpFactory: (requestHandler: PHPRequestHandlerFactoryArgs<PHP>) => Promise<PHP>;
670
+ /**
671
+ * The maximum number of PHP instances that can exist at
672
+ * the same time.
673
+ */
674
+ maxPhpInstances?: number;
675
+ });
624
676
  /**
625
677
  * Handles HTTP requests using PHP runtime as a backend.
626
678
  *
@@ -675,18 +727,23 @@ export interface PHPRequestHandlerConfiguration {
675
727
  * // "Hi from PHP!"
676
728
  * ```
677
729
  */
678
- export declare class PHPRequestHandler {
730
+ export declare class PHPRequestHandler<PHP extends BasePHP> {
679
731
  #private;
680
732
  rewriteRules: RewriteRule[];
733
+ processManager: PHPProcessManager<PHP>;
681
734
  /**
682
- * The PHP instance
683
- */
684
- php: BasePHP;
685
- /**
735
+ * The request handler needs to decide whether to serve a static asset or
736
+ * run the PHP interpreter. For static assets it should just reuse the primary
737
+ * PHP even if there's 50 concurrent requests to serve. However, for
738
+ * dynamic PHP requests, it needs to grab an available interpreter.
739
+ * Therefore, it cannot just accept PHP as an argument as serving requests
740
+ * requires access to ProcessManager.
741
+ *
686
742
  * @param php - The PHP instance.
687
743
  * @param config - Request Handler configuration.
688
744
  */
689
- constructor(php: BasePHP, config?: PHPRequestHandlerConfiguration);
745
+ constructor(config: PHPRequestHandlerConfiguration<PHP>);
746
+ getPrimaryPhp(): Promise<PHP>;
690
747
  /**
691
748
  * Converts a path to an absolute URL based at the PHPRequestHandler
692
749
  * root.
@@ -703,7 +760,6 @@ export declare class PHPRequestHandler {
703
760
  * @returns The relative path.
704
761
  */
705
762
  internalUrlToPath(internalUrl: string): string;
706
- get isRequestRunning(): boolean;
707
763
  /**
708
764
  * The absolute URL of this PHPRequestHandler instance.
709
765
  */
@@ -763,60 +819,11 @@ export declare class PHPRequestHandler {
763
819
  */
764
820
  request(request: PHPRequest): Promise<PHPResponse>;
765
821
  }
766
- export type RuntimeType = "NODE" | "WEB" | "WORKER";
767
- export type PHPRuntimeId = number;
768
- export type PHPRuntime = any;
769
- export type PHPLoaderModule = {
770
- dependencyFilename: string;
771
- dependenciesTotalSize: number;
772
- init: (jsRuntime: string, options: EmscriptenOptions) => PHPRuntime;
773
- };
774
- export type EmscriptenOptions = {
775
- onAbort?: (message: string) => void;
776
- /**
777
- * Set to true for debugging tricky WebAssembly errors.
778
- */
779
- debug?: boolean;
780
- ENV?: Record<string, string>;
781
- locateFile?: (path: string) => string;
782
- noInitialRun?: boolean;
783
- print?: (message: string) => void;
784
- printErr?: (message: string) => void;
785
- quit?: (status: number, toThrow: any) => void;
786
- onRuntimeInitialized?: () => void;
787
- monitorRunDependencies?: (left: number) => void;
788
- onMessage?: (listener: EmscriptenMessageListener) => void;
789
- instantiateWasm?: (info: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, module: WebAssembly.Module) => void) => void;
790
- } & Record<string, any>;
791
- export type EmscriptenMessageListener = (type: string, data: string) => void;
792
- export interface SemaphoreOptions {
793
- /**
794
- * The maximum number of concurrent locks.
795
- */
796
- concurrency: number;
797
- /**
798
- * The maximum time to wait for a lock to become available.
799
- */
800
- timeout?: number;
801
- }
802
- declare class Semaphore {
803
- private _running;
804
- private concurrency;
805
- private timeout?;
806
- private queue;
807
- constructor({ concurrency, timeout }: SemaphoreOptions);
808
- get remaining(): number;
809
- get running(): number;
810
- acquire(): Promise<() => void>;
811
- run<T>(fn: () => T | Promise<T>): Promise<T>;
812
- }
813
- export declare function phpVar(value: unknown): string;
814
- export declare function phpVars<T extends Record<string, unknown>>(vars: T): Record<keyof T, string>;
815
822
  declare const __private__dont__use: unique symbol;
816
- declare abstract class BasePHP implements IsomorphicLocalPHP {
823
+ declare abstract class BasePHP implements IsomorphicLocalPHP, Disposable {
817
824
  #private;
818
825
  protected [__private__dont__use]: any;
819
- requestHandler?: PHPRequestHandler;
826
+ requestHandler?: PHPRequestHandler<any>;
820
827
  /**
821
828
  * An exclusive lock that prevent multiple requests from running at
822
829
  * the same time.
@@ -827,9 +834,9 @@ declare abstract class BasePHP implements IsomorphicLocalPHP {
827
834
  *
828
835
  * @internal
829
836
  * @param PHPRuntime - Optional. PHP Runtime ID as initialized by loadPHPRuntime.
830
- * @param serverOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
837
+ * @param requestHandlerOptions - Optional. Options for the PHPRequestHandler. If undefined, no request handler will be initialized.
831
838
  */
832
- constructor(PHPRuntimeId?: PHPRuntimeId, serverOptions?: PHPRequestHandlerConfiguration);
839
+ constructor(PHPRuntimeId?: PHPRuntimeId);
833
840
  addEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
834
841
  removeEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
835
842
  dispatchEvent<Event extends PHPEvent>(event: Event): void;
@@ -854,7 +861,10 @@ declare abstract class BasePHP implements IsomorphicLocalPHP {
854
861
  setPhpIniEntry(key: string, value: string): void;
855
862
  /** @inheritDoc */
856
863
  chdir(path: string): void;
857
- /** @inheritDoc */
864
+ /**
865
+ * Do not use. Use new PHPRequestHandler() instead.
866
+ * @deprecated
867
+ */
858
868
  request(request: PHPRequest): Promise<PHPResponse>;
859
869
  /** @inheritDoc */
860
870
  run(request: PHPRunOptions): Promise<PHPResponse>;
@@ -893,7 +903,114 @@ declare abstract class BasePHP implements IsomorphicLocalPHP {
893
903
  */
894
904
  hotSwapPHPRuntime(runtime: number, cwd?: string): void;
895
905
  exit(code?: number): void;
906
+ [Symbol.dispose](): void;
896
907
  }
908
+ export type PHPFactoryOptions = {
909
+ isPrimary: boolean;
910
+ };
911
+ export type PHPFactory<PHP extends BasePHP> = (options: PHPFactoryOptions) => Promise<PHP>;
912
+ export interface ProcessManagerOptions<PHP extends BasePHP> {
913
+ /**
914
+ * The maximum number of PHP instances that can exist at
915
+ * the same time.
916
+ */
917
+ maxPhpInstances?: number;
918
+ /**
919
+ * The number of milliseconds to wait for a PHP instance when
920
+ * we have reached the maximum number of PHP instances and
921
+ * cannot spawn a new one. If the timeout is reached, we assume
922
+ * all the PHP instances are deadlocked and a throw MaxPhpInstancesError.
923
+ *
924
+ * Default: 5000
925
+ */
926
+ timeout?: number;
927
+ /**
928
+ * The primary PHP instance that's never killed. This instance
929
+ * contains the reference filesystem used by all other PHP instances.
930
+ */
931
+ primaryPhp?: PHP;
932
+ /**
933
+ * A factory function used for spawning new PHP instances.
934
+ */
935
+ phpFactory?: PHPFactory<PHP>;
936
+ }
937
+ export interface SpawnedPHP<PHP extends BasePHP> {
938
+ php: PHP;
939
+ reap: () => void;
940
+ }
941
+ declare class PHPProcessManager<PHP extends BasePHP> implements AsyncDisposable {
942
+ private primaryPhp?;
943
+ private primaryIdle;
944
+ private nextInstance;
945
+ /**
946
+ * All spawned PHP instances, including the primary PHP instance.
947
+ * Used for bookkeeping and reaping all instances on dispose.
948
+ */
949
+ private allInstances;
950
+ private phpFactory?;
951
+ private maxPhpInstances;
952
+ private semaphore;
953
+ constructor(options?: ProcessManagerOptions<PHP>);
954
+ /**
955
+ * Get the primary PHP instance.
956
+ *
957
+ * If the primary PHP instance is not set, it will be spawned
958
+ * using the provided phpFactory.
959
+ *
960
+ * @throws {Error} when called twice before the first call is resolved.
961
+ */
962
+ getPrimaryPhp(): Promise<PHP>;
963
+ /**
964
+ * Get a PHP instance.
965
+ *
966
+ * It could be either the primary PHP instance, an idle disposable PHP instance,
967
+ * or a newly spawned PHP instance – depending on the resource availability.
968
+ *
969
+ * @throws {MaxPhpInstancesError} when the maximum number of PHP instances is reached
970
+ * and the waiting timeout is exceeded.
971
+ */
972
+ acquirePHPInstance(): Promise<SpawnedPHP<PHP>>;
973
+ /**
974
+ * Initiated spawning of a new PHP instance.
975
+ * This function is synchronous on purpose – it needs to synchronously
976
+ * add the spawn promise to the allInstances array without waiting
977
+ * for PHP to spawn.
978
+ */
979
+ private spawn;
980
+ /**
981
+ * Actually acquires the lock and spawns a new PHP instance.
982
+ */
983
+ private doSpawn;
984
+ [Symbol.asyncDispose](): Promise<void>;
985
+ }
986
+ /**
987
+ * Emscripten's filesystem-related Exception.
988
+ *
989
+ * @see https://emscripten.org/docs/api_reference/Filesystem-API.html
990
+ * @see https://github.com/emscripten-core/emscripten/blob/main/system/lib/libc/musl/arch/emscripten/bits/errno.h
991
+ * @see https://github.com/emscripten-core/emscripten/blob/38eedc630f17094b3202fd48ac0c2c585dbea31e/system/include/wasi/api.h#L336
992
+ */
993
+ export interface ErrnoError extends Error {
994
+ node?: any;
995
+ errno: number;
996
+ message: string;
997
+ }
998
+ export declare const SupportedPHPVersions: readonly [
999
+ "8.3",
1000
+ "8.2",
1001
+ "8.1",
1002
+ "8.0",
1003
+ "7.4",
1004
+ "7.3",
1005
+ "7.2",
1006
+ "7.1",
1007
+ "7.0"
1008
+ ];
1009
+ export declare const LatestSupportedPHPVersion: "8.3";
1010
+ export declare const SupportedPHPVersionsList: string[];
1011
+ export type SupportedPHPVersion = (typeof SupportedPHPVersions)[number];
1012
+ export type SupportedPHPExtension = "iconv" | "mbstring" | "xml-bundle" | "gd";
1013
+ export type SupportedPHPExtensionBundle = "kitchen-sink" | "light";
897
1014
  export declare const ResourceTypes: readonly [
898
1015
  "vfs",
899
1016
  "literal",
@@ -1569,6 +1686,7 @@ export interface RunPHPWithOptionsStep {
1569
1686
  */
1570
1687
  export declare const runPHPWithOptions: StepHandler<RunPHPWithOptionsStep>;
1571
1688
  /**
1689
+ * @private
1572
1690
  * @inheritDoc request
1573
1691
  * @needsLogin
1574
1692
  * @hasRunnableExample
@@ -2045,7 +2163,6 @@ export type WithAPIState = {
2045
2163
  export type RemoteAPI<T> = Comlink.Remote<T> & WithAPIState;
2046
2164
  export interface PHPWebLoaderOptions {
2047
2165
  emscriptenOptions?: EmscriptenOptions;
2048
- requestHandler?: PHPRequestHandlerConfiguration;
2049
2166
  onPhpLoaderModuleLoaded?: (module: PHPLoaderModule) => void;
2050
2167
  /** @deprecated To be replaced with `extensions` in the future */
2051
2168
  loadAllExtensions?: boolean;
@@ -2066,26 +2183,35 @@ declare class WebPHP extends BasePHP {
2066
2183
  static load(phpVersion: SupportedPHPVersion, options?: PHPWebLoaderOptions): Promise<WebPHP>;
2067
2184
  static loadRuntime(phpVersion: SupportedPHPVersion, options?: PHPWebLoaderOptions): Promise<number>;
2068
2185
  }
2069
- declare class WebPHPEndpoint implements IsomorphicLocalPHP {
2186
+ declare class WebPHPEndpoint implements Omit<IsomorphicLocalPHP, "setSapiName" | "setPhpIniEntry" | "setPhpIniPath"> {
2070
2187
  /** @inheritDoc @php-wasm/universal!RequestHandler.absoluteUrl */
2071
2188
  absoluteUrl: string;
2072
2189
  /** @inheritDoc @php-wasm/universal!RequestHandler.documentRoot */
2073
2190
  documentRoot: string;
2074
2191
  /** @inheritDoc */
2075
- constructor(php: BasePHP, monitor?: EmscriptenDownloadMonitor);
2076
- /** @inheritDoc @php-wasm/universal!RequestHandler.pathToInternalUrl */
2192
+ constructor(requestHandler: PHPRequestHandler<WebPHP>, monitor?: EmscriptenDownloadMonitor);
2193
+ /**
2194
+ * @internal
2195
+ * @deprecated
2196
+ * Do not use this method directly in the code consuming
2197
+ * the web API. It will change or even be removed without
2198
+ * a warning.
2199
+ */
2200
+ protected __internal_getPHP(): WebPHP | undefined;
2201
+ setPrimaryPHP(php: WebPHP): Promise<void>;
2202
+ /** @inheritDoc @php-wasm/universal!PHPRequestHandler.pathToInternalUrl */
2077
2203
  pathToInternalUrl(path: string): string;
2078
- /** @inheritDoc @php-wasm/universal!RequestHandler.internalUrlToPath */
2204
+ /** @inheritDoc @php-wasm/universal!PHPRequestHandler.internalUrlToPath */
2079
2205
  internalUrlToPath(internalUrl: string): string;
2080
2206
  /**
2081
2207
  * The onDownloadProgress event listener.
2082
2208
  */
2083
2209
  onDownloadProgress(callback: (progress: CustomEvent<ProgressEvent>) => void): Promise<void>;
2084
2210
  /** @inheritDoc @php-wasm/universal!IsomorphicLocalPHP.mv */
2085
- mv(fromPath: string, toPath: string): void;
2211
+ mv(fromPath: string, toPath: string): Promise<void>;
2086
2212
  /** @inheritDoc @php-wasm/universal!IsomorphicLocalPHP.rmdir */
2087
- rmdir(path: string, options?: RmDirOptions): void;
2088
- /** @inheritDoc @php-wasm/universal!RequestHandler.request */
2213
+ rmdir(path: string, options?: RmDirOptions): Promise<void>;
2214
+ /** @inheritDoc @php-wasm/universal!PHPRequestHandler.request */
2089
2215
  request(request: PHPRequest): Promise<PHPResponse>;
2090
2216
  /** @inheritDoc @php-wasm/web!WebPHP.run */
2091
2217
  run(request: PHPRunOptions): Promise<PHPResponse>;
@@ -2124,6 +2250,13 @@ declare class WebPHPEndpoint implements IsomorphicLocalPHP {
2124
2250
  /** @inheritDoc @php-wasm/web!WebPHP.removeEventListener */
2125
2251
  removeEventListener(eventType: PHPEvent["type"], listener: PHPEventListener): void;
2126
2252
  }
2253
+ export type SyncProgress = {
2254
+ /** The number of files that have been synced. */
2255
+ files: number;
2256
+ /** The number of all files that need to be synced. */
2257
+ total: number;
2258
+ };
2259
+ export type SyncProgressCallback = (progress: SyncProgress) => void;
2127
2260
  /**
2128
2261
  * Represents the type of node in PHP file system.
2129
2262
  */
@@ -2173,13 +2306,6 @@ export type RenameOperation = {
2173
2306
  nodeType: FSNodeType;
2174
2307
  };
2175
2308
  export type FilesystemOperation = CreateOperation | UpdateFileOperation | DeleteOperation | RenameOperation;
2176
- export type SyncProgress = {
2177
- /** The number of files that have been synced. */
2178
- files: number;
2179
- /** The number of all files that need to be synced. */
2180
- total: number;
2181
- };
2182
- export type SyncProgressCallback = (progress: SyncProgress) => void;
2183
2309
  declare class PlaygroundWorkerEndpoint extends WebPHPEndpoint {
2184
2310
  /**
2185
2311
  * A string representing the scope of the Playground instance.
@@ -2189,11 +2315,7 @@ declare class PlaygroundWorkerEndpoint extends WebPHPEndpoint {
2189
2315
  * A string representing the version of WordPress being used.
2190
2316
  */
2191
2317
  wordPressVersion: string;
2192
- /**
2193
- * A string representing the version of PHP being used.
2194
- */
2195
- phpVersion: string;
2196
- constructor(php: WebPHP, monitor: EmscriptenDownloadMonitor, scope: string, wordPressVersion: string, phpVersion: string);
2318
+ constructor(requestHandler: PHPRequestHandler<WebPHP>, monitor: EmscriptenDownloadMonitor, scope: string, wordPressVersion: string);
2197
2319
  /**
2198
2320
  * @returns WordPress module details, including the static assets directory and default theme.
2199
2321
  */