@wp-playground/blueprints 0.1.40 → 0.1.43

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 (51) hide show
  1. package/index.cjs +140 -0
  2. package/index.d.ts +7 -0
  3. package/index.js +2106 -0
  4. package/lib/blueprint.d.ts +27 -0
  5. package/lib/compile.d.ts +37 -0
  6. package/lib/resources.d.ts +211 -0
  7. package/lib/steps/activate-plugin.d.ts +12 -0
  8. package/lib/steps/apply-wordpress-patches/index.d.ts +12 -0
  9. package/lib/steps/client-methods.d.ts +56 -0
  10. package/lib/steps/common.d.ts +9 -0
  11. package/lib/steps/define-site-url.d.ts +12 -0
  12. package/lib/steps/define-virtual-wp-config-consts.d.ts +19 -0
  13. package/lib/steps/define-wp-config-consts.d.ts +16 -0
  14. package/{src/lib/steps/handlers.ts → lib/steps/handlers.d.ts} +2 -12
  15. package/lib/steps/import-export.d.ts +67 -0
  16. package/lib/steps/index.d.ts +28 -0
  17. package/lib/steps/install-plugin.d.ts +46 -0
  18. package/lib/steps/install-theme.d.ts +22 -0
  19. package/lib/steps/login.d.ts +16 -0
  20. package/lib/steps/migration.d.ts +2 -0
  21. package/lib/steps/run-wp-installation-wizard.d.ts +16 -0
  22. package/lib/steps/site-data.d.ts +12 -0
  23. package/package.json +4 -3
  24. package/.eslintrc.json +0 -18
  25. package/LICENSE +0 -339
  26. package/README.md +0 -11
  27. package/project.json +0 -58
  28. package/src/index.ts +0 -31
  29. package/src/lib/blueprint.ts +0 -28
  30. package/src/lib/compile.spec.ts +0 -34
  31. package/src/lib/compile.ts +0 -262
  32. package/src/lib/resources.ts +0 -417
  33. package/src/lib/steps/activate-plugin.ts +0 -35
  34. package/src/lib/steps/apply-wordpress-patches/index.ts +0 -113
  35. package/src/lib/steps/apply-wordpress-patches/wp-content/mu-plugins/1-show-admin-credentials-on-wp-login.php +0 -15
  36. package/src/lib/steps/client-methods.ts +0 -121
  37. package/src/lib/steps/common.ts +0 -30
  38. package/src/lib/steps/define-site-url.ts +0 -25
  39. package/src/lib/steps/define-wp-config-consts.ts +0 -45
  40. package/src/lib/steps/import-export.ts +0 -225
  41. package/src/lib/steps/index.ts +0 -67
  42. package/src/lib/steps/install-plugin.ts +0 -145
  43. package/src/lib/steps/install-theme.ts +0 -99
  44. package/src/lib/steps/login.ts +0 -37
  45. package/src/lib/steps/migration.ts +0 -63
  46. package/src/lib/steps/run-wp-installation-wizard.ts +0 -38
  47. package/src/lib/steps/site-data.ts +0 -58
  48. package/tsconfig.json +0 -23
  49. package/tsconfig.lib.json +0 -10
  50. package/tsconfig.spec.json +0 -19
  51. package/vite.config.ts +0 -58
@@ -1,262 +0,0 @@
1
- import { ProgressTracker } from '@php-wasm/progress';
2
- import { Semaphore } from '@php-wasm/util';
3
- import {
4
- LatestSupportedPHPVersion,
5
- SupportedPHPVersion,
6
- SupportedPHPVersions,
7
- UniversalPHP,
8
- } from '@php-wasm/universal';
9
- import { isFileReference, Resource } from './resources';
10
- import { Step, StepDefinition } from './steps';
11
- import * as stepHandlers from './steps/handlers';
12
- import { Blueprint } from './blueprint';
13
-
14
- export type CompiledStep = (php: UniversalPHP) => Promise<void> | void;
15
-
16
- const supportedWordPressVersions = ['6.2', '6.1', '6.0', '5.9'] as const;
17
- type supportedWordPressVersion = (typeof supportedWordPressVersions)[number];
18
- export interface CompiledBlueprint {
19
- /** The requested versions of PHP and WordPress for the blueprint */
20
- versions: {
21
- php: SupportedPHPVersion;
22
- wp: supportedWordPressVersion;
23
- };
24
- /** The compiled steps for the blueprint */
25
- run: (playground: UniversalPHP) => Promise<void>;
26
- }
27
-
28
- export type OnStepCompleted = (output: any, step: StepDefinition) => any;
29
-
30
- export interface CompileBlueprintOptions {
31
- /** Optional progress tracker to monitor progress */
32
- progress?: ProgressTracker;
33
- /** Optional semaphore to control access to a shared resource */
34
- semaphore?: Semaphore;
35
- /** Optional callback with step output */
36
- onStepCompleted?: OnStepCompleted;
37
- }
38
-
39
- /**
40
- * Compiles Blueprint into a form that can be executed.
41
- *
42
- * @param playground The PlaygroundClient to use for the compilation
43
- * @param blueprint The bBueprint to compile
44
- * @param options Additional options for the compilation
45
- * @returns The compiled blueprint
46
- */
47
- export function compileBlueprint(
48
- blueprint: Blueprint,
49
- {
50
- progress = new ProgressTracker(),
51
- semaphore = new Semaphore({ concurrency: 3 }),
52
- onStepCompleted = () => {},
53
- }: CompileBlueprintOptions = {}
54
- ): CompiledBlueprint {
55
- const steps = (blueprint.steps || []).filter(isStepDefinition);
56
-
57
- const totalProgressWeight = steps.reduce(
58
- (total, step) => total + (step.progress?.weight || 1),
59
- 0
60
- );
61
- const compiled = steps.map((step) =>
62
- compileStep(step, {
63
- semaphore,
64
- rootProgressTracker: progress,
65
- totalProgressWeight,
66
- })
67
- );
68
-
69
- return {
70
- versions: {
71
- php: compileVersion(
72
- blueprint.preferredVersions?.php,
73
- SupportedPHPVersions,
74
- LatestSupportedPHPVersion
75
- ),
76
- wp: compileVersion(
77
- blueprint.preferredVersions?.wp,
78
- supportedWordPressVersions,
79
- '6.2'
80
- ),
81
- },
82
- run: async (playground: UniversalPHP) => {
83
- try {
84
- // Start resolving resources early
85
- for (const { resources } of compiled) {
86
- for (const resource of resources) {
87
- resource.setPlayground(playground);
88
- if (resource.isAsync) {
89
- resource.resolve();
90
- }
91
- }
92
- }
93
-
94
- for (const { run, step } of compiled) {
95
- const result = await run(playground);
96
- onStepCompleted(result, step);
97
- }
98
- try {
99
- await (playground as any).goTo(
100
- blueprint.landingPage || '/'
101
- );
102
- } catch (e) {
103
- /*
104
- * NodePHP exposes no goTo method.
105
- * We can't use `goto` in playground here,
106
- * because it may be a Comlink proxy object
107
- * with no such method.
108
- */
109
- }
110
- } finally {
111
- progress.finish();
112
- }
113
- },
114
- };
115
- }
116
-
117
- /**
118
- * Compiles a preferred version string into a supported version
119
- *
120
- * @param value The value to compile
121
- * @param supported The list of supported versions
122
- * @param latest The latest supported version
123
- * @returns The compiled version
124
- */
125
- function compileVersion<T>(
126
- value: string | undefined | null,
127
- supported: readonly T[],
128
- latest: string
129
- ): T {
130
- if (value && supported.includes(value as any)) {
131
- return value as T;
132
- }
133
- return latest as T;
134
- }
135
-
136
- /**
137
- * Determines if a step is a StepDefinition object
138
- *
139
- * @param step The object to test
140
- * @returns Whether the object is a StepDefinition
141
- */
142
- function isStepDefinition(
143
- step: Step | string | undefined | false | null
144
- ): step is StepDefinition {
145
- return !!(typeof step === 'object' && step);
146
- }
147
-
148
- interface CompileStepArgsOptions {
149
- /** Optional semaphore to control access to a shared resource */
150
- semaphore?: Semaphore;
151
- /** The root progress tracker for the compilation */
152
- rootProgressTracker: ProgressTracker;
153
- /** The total progress weight of all the steps in the blueprint */
154
- totalProgressWeight: number;
155
- }
156
-
157
- /**
158
- * Compiles a single Blueprint step into a form that can be executed
159
- *
160
- * @param playground The PlaygroundClient to use for the compilation
161
- * @param step The step to compile
162
- * @param options Additional options for the compilation
163
- * @returns The compiled step
164
- */
165
- function compileStep<S extends StepDefinition>(
166
- step: S,
167
- {
168
- semaphore,
169
- rootProgressTracker,
170
- totalProgressWeight,
171
- }: CompileStepArgsOptions
172
- ): { run: CompiledStep; step: S; resources: Array<Resource> } {
173
- const stepProgress = rootProgressTracker.stage(
174
- (step.progress?.weight || 1) / totalProgressWeight
175
- );
176
-
177
- const args: any = {};
178
- for (const key of Object.keys(step)) {
179
- let value = (step as any)[key];
180
- if (isFileReference(value)) {
181
- value = Resource.create(value, {
182
- semaphore,
183
- });
184
- }
185
- args[key] = value;
186
- }
187
-
188
- const run = async (playground: UniversalPHP) => {
189
- try {
190
- stepProgress.fillSlowly();
191
- return await stepHandlers[step.step](
192
- playground,
193
- await resolveArguments(args),
194
- {
195
- tracker: stepProgress,
196
- initialCaption: step.progress?.caption,
197
- }
198
- );
199
- } finally {
200
- stepProgress.finish();
201
- }
202
- };
203
-
204
- /**
205
- * The weight of each async resource is the same, and is the same as the
206
- * weight of the step itself.
207
- */
208
- const resources = getResources(args);
209
- const asyncResources = getResources(args).filter(
210
- (resource) => resource.isAsync
211
- );
212
-
213
- const evenWeight = 1 / (asyncResources.length + 1);
214
- for (const resource of asyncResources) {
215
- resource.progress = stepProgress.stage(evenWeight);
216
- }
217
-
218
- return { run, step, resources };
219
- }
220
-
221
- /**
222
- * Gets the resources used by a specific compiled step
223
- *
224
- * @param step The compiled step
225
- * @returns The resources used by the compiled step
226
- */
227
- function getResources<S extends StepDefinition>(args: S) {
228
- const result: Resource[] = [];
229
- for (const argName in args) {
230
- const resourceMaybe = (args as any)[argName];
231
- if (resourceMaybe instanceof Resource) {
232
- result.push(resourceMaybe);
233
- }
234
- }
235
- return result;
236
- }
237
-
238
- /**
239
- * Replaces Resource objects with their resolved values
240
- *
241
- * @param step The compiled step
242
- * @returns The resources used by the compiled step
243
- */
244
- async function resolveArguments<T extends Record<string, unknown>>(args: T) {
245
- const resolved: any = {};
246
- for (const argName in args) {
247
- const resourceMaybe = (args as any)[argName];
248
- if (resourceMaybe instanceof Resource) {
249
- resolved[argName] = await resourceMaybe.resolve();
250
- } else {
251
- resolved[argName] = resourceMaybe;
252
- }
253
- }
254
- return resolved;
255
- }
256
-
257
- export async function runBlueprintSteps(
258
- compiledBlueprint: CompiledBlueprint,
259
- playground: UniversalPHP
260
- ) {
261
- await compiledBlueprint.run(playground);
262
- }
@@ -1,417 +0,0 @@
1
- import {
2
- cloneResponseMonitorProgress,
3
- ProgressTracker,
4
- } from '@php-wasm/progress';
5
- import { UniversalPHP } from '@php-wasm/universal';
6
- import { Semaphore } from '@php-wasm/util';
7
- import { zipNameToHumanName } from './steps/common';
8
-
9
- export const ResourceTypes = [
10
- 'vfs',
11
- 'literal',
12
- 'wordpress.org/themes',
13
- 'wordpress.org/plugins',
14
- 'url',
15
- ] as const;
16
-
17
- export type VFSReference = {
18
- /** Identifies the file resource as Virtual File System (VFS) */
19
- resource: 'vfs';
20
- /** The path to the file in the VFS */
21
- path: string;
22
- };
23
- export type LiteralReference = {
24
- /** Identifies the file resource as a literal file */
25
- resource: 'literal';
26
- /** The name of the file */
27
- name: string;
28
- /** The contents of the file */
29
- contents: string | Uint8Array;
30
- };
31
- export type CoreThemeReference = {
32
- /** Identifies the file resource as a WordPress Core theme */
33
- resource: 'wordpress.org/themes';
34
- /** The slug of the WordPress Core theme */
35
- slug: string;
36
- };
37
- export type CorePluginReference = {
38
- /** Identifies the file resource as a WordPress Core plugin */
39
- resource: 'wordpress.org/plugins';
40
- /** The slug of the WordPress Core plugin */
41
- slug: string;
42
- };
43
- export type UrlReference = {
44
- /** Identifies the file resource as a URL */
45
- resource: 'url';
46
- /** The URL of the file */
47
- url: string;
48
- /** Optional caption for displaying a progress message */
49
- caption?: string;
50
- };
51
-
52
- export type FileReference =
53
- | VFSReference
54
- | LiteralReference
55
- | CoreThemeReference
56
- | CorePluginReference
57
- | UrlReference;
58
-
59
- export function isFileReference(ref: any): ref is FileReference {
60
- return (
61
- ref &&
62
- typeof ref === 'object' &&
63
- typeof ref.resource === 'string' &&
64
- ResourceTypes.includes(ref.resource)
65
- );
66
- }
67
-
68
- export interface ResourceOptions {
69
- /** Optional semaphore to limit concurrent downloads */
70
- semaphore?: Semaphore;
71
- progress?: ProgressTracker;
72
- }
73
- export abstract class Resource {
74
- /** Optional progress tracker to monitor progress */
75
- public abstract progress?: ProgressTracker;
76
- /** A Promise that resolves to the file contents */
77
- protected promise?: Promise<File>;
78
-
79
- protected playground?: UniversalPHP;
80
-
81
- /**
82
- * Creates a new Resource based on the given file reference
83
- *
84
- * @param ref The file reference to create the Resource for
85
- * @param options Additional options for the Resource
86
- * @returns A new Resource instance
87
- */
88
- static create(
89
- ref: FileReference,
90
- { semaphore, progress }: ResourceOptions
91
- ): Resource {
92
- let resource: Resource;
93
- switch (ref.resource) {
94
- case 'vfs':
95
- resource = new VFSResource(ref, progress);
96
- break;
97
- case 'literal':
98
- resource = new LiteralResource(ref, progress);
99
- break;
100
- case 'wordpress.org/themes':
101
- resource = new CoreThemeResource(ref, progress);
102
- break;
103
- case 'wordpress.org/plugins':
104
- resource = new CorePluginResource(ref, progress);
105
- break;
106
- case 'url':
107
- resource = new UrlResource(ref, progress);
108
- break;
109
- default:
110
- throw new Error(`Invalid resource: ${ref}`);
111
- }
112
- resource = new CachedResource(resource);
113
-
114
- if (semaphore) {
115
- resource = new SemaphoreResource(resource, semaphore);
116
- }
117
-
118
- return resource;
119
- }
120
-
121
- setPlayground(playground: UniversalPHP) {
122
- this.playground = playground;
123
- }
124
-
125
- /**
126
- * Resolves the file contents
127
- * @returns The resolved file.
128
- */
129
- abstract resolve(): Promise<File>;
130
-
131
- /** The name of the referenced file */
132
- abstract get name(): string;
133
-
134
- /** Whether this Resource is loaded asynchronously */
135
- get isAsync(): boolean {
136
- return false;
137
- }
138
- }
139
- /**
140
- * A `Resource` that represents a file in the VFS (virtual file system) of the playground.
141
- */
142
- export class VFSResource extends Resource {
143
- /**
144
- * Creates a new instance of `VFSResource`.
145
- * @param playground The playground client.
146
- * @param resource The VFS reference.
147
- * @param progress The progress tracker.
148
- */
149
- constructor(
150
- private resource: VFSReference,
151
- public override progress?: ProgressTracker
152
- ) {
153
- super();
154
- }
155
-
156
- /** @inheritDoc */
157
- async resolve() {
158
- const buffer = await this.playground!.readFileAsBuffer(
159
- this.resource.path
160
- );
161
- this.progress?.set(100);
162
- return new File([buffer], this.name);
163
- }
164
-
165
- /** @inheritDoc */
166
- get name() {
167
- return this.resource.path;
168
- }
169
- }
170
-
171
- /**
172
- * A `Resource` that represents a literal file.
173
- */
174
- export class LiteralResource extends Resource {
175
- /**
176
- * Creates a new instance of `LiteralResource`.
177
- * @param resource The literal reference.
178
- * @param progress The progress tracker.
179
- */
180
- constructor(
181
- private resource: LiteralReference,
182
- public override progress?: ProgressTracker
183
- ) {
184
- super();
185
- }
186
-
187
- /** @inheritDoc */
188
- async resolve() {
189
- this.progress?.set(100);
190
- return new File([this.resource.contents], this.resource.name);
191
- }
192
-
193
- /** @inheritDoc */
194
- get name() {
195
- return this.resource.name;
196
- }
197
- }
198
-
199
- /**
200
- * A base class for `Resource`s that require fetching data from a remote URL.
201
- */
202
- export abstract class FetchResource extends Resource {
203
- /**
204
- * Creates a new instance of `FetchResource`.
205
- * @param progress The progress tracker.
206
- */
207
- constructor(public override progress?: ProgressTracker) {
208
- super();
209
- }
210
-
211
- /** @inheritDoc */
212
- async resolve() {
213
- this.progress?.setCaption(this.caption);
214
- const url = this.getURL();
215
- let response = await fetch(url);
216
- response = await cloneResponseMonitorProgress(
217
- response,
218
- this.progress?.loadingListener ?? noop
219
- );
220
- if (response.status !== 200) {
221
- throw new Error(`Could not download "${url}"`);
222
- }
223
- return new File([await response.blob()], this.name);
224
- }
225
-
226
- /**
227
- * Gets the URL to fetch the data from.
228
- * @returns The URL.
229
- */
230
- protected abstract getURL(): string;
231
-
232
- /**
233
- * Gets the caption for the progress tracker.
234
- * @returns The caption.
235
- */
236
- protected get caption() {
237
- return `Downloading ${this.name}`;
238
- }
239
-
240
- /** @inheritDoc */
241
- get name() {
242
- try {
243
- return new URL(this.getURL(), 'http://example.com').pathname
244
- .split('/')
245
- .pop()!;
246
- } catch (e) {
247
- return this.getURL();
248
- }
249
- }
250
-
251
- /** @inheritDoc */
252
- override get isAsync(): boolean {
253
- return true;
254
- }
255
- }
256
-
257
- // eslint-disable-next-line @typescript-eslint/no-empty-function
258
- const noop = (() => {}) as any;
259
-
260
- /**
261
- * A `Resource` that represents a file available from a URL.
262
- */
263
- export class UrlResource extends FetchResource {
264
- /**
265
- * Creates a new instance of `UrlResource`.
266
- * @param resource The URL reference.
267
- * @param progress The progress tracker.
268
- */
269
- constructor(private resource: UrlReference, progress?: ProgressTracker) {
270
- super(progress);
271
- }
272
-
273
- /** @inheritDoc */
274
- getURL() {
275
- return this.resource.url;
276
- }
277
-
278
- /** @inheritDoc */
279
- protected override get caption() {
280
- return this.resource.caption ?? super.caption;
281
- }
282
- }
283
-
284
- let pluginProxyURL = 'https://playground.wordpress.net/plugin-proxy';
285
- export function setPluginProxyURL(url: string) {
286
- pluginProxyURL = url;
287
- }
288
-
289
- /**
290
- * A `Resource` that represents a WordPress core theme.
291
- */
292
- export class CoreThemeResource extends FetchResource {
293
- constructor(
294
- private resource: CoreThemeReference,
295
- progress?: ProgressTracker
296
- ) {
297
- super(progress);
298
- }
299
- override get name() {
300
- return zipNameToHumanName(this.resource.slug);
301
- }
302
- getURL() {
303
- const zipName = toDirectoryZipName(this.resource.slug);
304
- return `${pluginProxyURL}?theme=` + zipName;
305
- }
306
- }
307
-
308
- /**
309
- * A resource that fetches a WordPress plugin from wordpress.org.
310
- */
311
- export class CorePluginResource extends FetchResource {
312
- constructor(
313
- private resource: CorePluginReference,
314
- progress?: ProgressTracker
315
- ) {
316
- super(progress);
317
- }
318
-
319
- /** @inheritDoc */
320
- override get name() {
321
- return zipNameToHumanName(this.resource.slug);
322
- }
323
-
324
- /** @inheritDoc */
325
- getURL() {
326
- const zipName = toDirectoryZipName(this.resource.slug);
327
- return `${pluginProxyURL}?plugin=` + zipName;
328
- }
329
- }
330
-
331
- /**
332
- * Transforms a plugin slug into a directory zip name.
333
- * If the input already ends with ".zip", returns it unchanged.
334
- * Otherwise, appends ".latest-stable.zip".
335
- */
336
- export function toDirectoryZipName(rawInput: string) {
337
- if (!rawInput) {
338
- return rawInput;
339
- }
340
- if (rawInput.endsWith('.zip')) {
341
- return rawInput;
342
- }
343
- return rawInput + '.latest-stable.zip';
344
- }
345
-
346
- /**
347
- * A decorator for a resource that adds functionality such as progress tracking and caching.
348
- */
349
- export class DecoratedResource<T extends Resource> extends Resource {
350
- constructor(private resource: T) {
351
- super();
352
- }
353
-
354
- /** @inheritDoc */
355
- async resolve() {
356
- return this.resource.resolve();
357
- }
358
-
359
- /** @inheritDoc */
360
- override async setPlayground(playground: UniversalPHP) {
361
- return this.resource.setPlayground(playground);
362
- }
363
-
364
- /** @inheritDoc */
365
- get progress() {
366
- return this.resource.progress;
367
- }
368
-
369
- /** @inheritDoc */
370
- set progress(value) {
371
- this.resource.progress = value;
372
- }
373
-
374
- /** @inheritDoc */
375
- get name() {
376
- return this.resource.name;
377
- }
378
-
379
- /** @inheritDoc */
380
- override get isAsync() {
381
- return this.resource.isAsync;
382
- }
383
- }
384
-
385
- /**
386
- * A decorator for a resource that adds caching functionality.
387
- */
388
- export class CachedResource<T extends Resource> extends DecoratedResource<T> {
389
- protected override promise?: Promise<File>;
390
-
391
- /** @inheritDoc */
392
- override async resolve() {
393
- if (!this.promise) {
394
- this.promise = super.resolve();
395
- }
396
- return this.promise;
397
- }
398
- }
399
-
400
- /**
401
- * A decorator for a resource that adds concurrency control functionality through a semaphore.
402
- */
403
- export class SemaphoreResource<
404
- T extends Resource
405
- > extends DecoratedResource<T> {
406
- constructor(resource: T, private readonly semaphore: Semaphore) {
407
- super(resource);
408
- }
409
-
410
- /** @inheritDoc */
411
- override async resolve() {
412
- if (!this.isAsync) {
413
- return super.resolve();
414
- }
415
- return this.semaphore.run(() => super.resolve());
416
- }
417
- }
@@ -1,35 +0,0 @@
1
- import { StepHandler } from '.';
2
- import { asDOM } from './common';
3
-
4
- export interface ActivatePluginStep {
5
- step: 'activatePlugin';
6
- plugin: string;
7
- }
8
-
9
- /**
10
- * Activates a WordPress plugin in the Playground.
11
- *
12
- * @param playground The playground client.
13
- * @param plugin The plugin slug.
14
- */
15
- export const activatePlugin: StepHandler<ActivatePluginStep> = async (
16
- playground,
17
- { plugin },
18
- progress
19
- ) => {
20
- progress?.tracker.setCaption(`Activating ${plugin}`);
21
- const pluginsPage = asDOM(
22
- await playground.request({
23
- url: '/wp-admin/plugins.php',
24
- })
25
- );
26
-
27
- const link = pluginsPage.querySelector(
28
- `tr[data-slug="${plugin}"] a`
29
- )! as HTMLAnchorElement;
30
- const href = link.attributes.getNamedItem('href')!.value;
31
-
32
- await playground.request({
33
- url: '/wp-admin/' + href,
34
- });
35
- };