@wp-playground/blueprints 0.1.29 → 0.1.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-playground/blueprints",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./index.js",
@@ -20,5 +20,5 @@
20
20
  "publishConfig": {
21
21
  "access": "public"
22
22
  },
23
- "gitHead": "ad98893a9638c07cd9bdf849632c35db45caae0d"
23
+ "gitHead": "df3e9bff1de33d53d026c35a6ba5534aeff21a78"
24
24
  }
package/src/index.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  export * from './lib/steps';
2
- export type { Step } from './lib/steps';
3
- export { runBlueprintSteps } from './lib/run';
4
- export { compileBlueprint } from './lib/compile';
2
+ export * from './lib/steps/handlers';
3
+ export { runBlueprintSteps, compileBlueprint } from './lib/compile';
4
+ export type { Blueprint } from './lib/blueprint';
5
5
  export type {
6
- Blueprint,
7
- StepDefinition,
8
6
  CompiledStep,
9
7
  CompiledBlueprint,
10
8
  CompileBlueprintOptions,
9
+ OnStepCompleted,
11
10
  } from './lib/compile';
12
11
  export type {
13
12
  CachedResource,
@@ -0,0 +1,28 @@
1
+ import { SupportedPHPVersion } from '@php-wasm/universal';
2
+ import { StepDefinition } from './steps';
3
+
4
+ export interface Blueprint {
5
+ /**
6
+ * The URL to navigate to after the blueprint has been run.
7
+ */
8
+ landingPage?: string;
9
+ /**
10
+ * The preferred PHP and WordPress versions to use.
11
+ */
12
+ preferredVersions?: {
13
+ /**
14
+ * The preferred PHP version to use.
15
+ * If not specified, the latest supported version will be used
16
+ */
17
+ php: SupportedPHPVersion | 'latest';
18
+ /**
19
+ * The preferred WordPress version to use.
20
+ * If not specified, the latest supported version will be used
21
+ */
22
+ wp: string | 'latest';
23
+ };
24
+ /**
25
+ * The steps to run.
26
+ */
27
+ steps?: Array<StepDefinition | string | undefined | false | null>;
28
+ }
@@ -1,6 +1,5 @@
1
1
  import { NodePHP } from '@php-wasm/node';
2
- import { compileBlueprint } from './compile';
3
- import { runBlueprintSteps } from './run';
2
+ import { compileBlueprint, runBlueprintSteps } from './compile';
4
3
 
5
4
  const phpVersion = '8.0';
6
5
  describe('Blueprints', () => {
@@ -4,64 +4,36 @@ import {
4
4
  LatestSupportedPHPVersion,
5
5
  SupportedPHPVersion,
6
6
  SupportedPHPVersions,
7
+ UniversalPHP,
7
8
  } from '@php-wasm/universal';
8
- import { FileReference, isFileReference, Resource } from './resources';
9
- import { Step } from './steps';
9
+ import { isFileReference, Resource } from './resources';
10
+ import { Step, StepDefinition } from './steps';
11
+ import * as stepHandlers from './steps/handlers';
12
+ import { Blueprint } from './blueprint';
10
13
 
11
- export type StepDefinition = Step<FileReference>;
12
- export type CompiledStep = {
13
- args: Step<Resource>;
14
- progress: ProgressTracker;
15
- };
16
-
17
- export interface Blueprint {
18
- /**
19
- * The URL to navigate to after the blueprint has been run.
20
- */
21
- landingPage?: string;
22
- /**
23
- * The preferred PHP and WordPress versions to use.
24
- */
25
- preferredVersions?: {
26
- /**
27
- * The preferred PHP version to use.
28
- * If not specified, the latest supported version will be used
29
- */
30
- php: SupportedPHPVersion | 'latest';
31
- /**
32
- * The preferred WordPress version to use.
33
- * If not specified, the latest supported version will be used
34
- */
35
- wp: string | 'latest';
36
- };
37
- /**
38
- * The steps to run.
39
- */
40
- steps?: Array<StepDefinition | string | undefined | false | null>;
41
- }
14
+ export type CompiledStep = (php: UniversalPHP) => Promise<void> | void;
42
15
 
43
16
  const supportedWordPressVersions = ['6.2', '6.1', '6.0', '5.9'] as const;
44
17
  type supportedWordPressVersion = (typeof supportedWordPressVersions)[number];
45
18
  export interface CompiledBlueprint {
46
- /** The URL of the landing page for the blueprint */
47
- landingPage: string;
48
- /** The preferred versions of PHP and WordPress for the blueprint */
19
+ /** The requested versions of PHP and WordPress for the blueprint */
49
20
  versions: {
50
21
  php: SupportedPHPVersion;
51
22
  wp: supportedWordPressVersion;
52
23
  };
53
24
  /** The compiled steps for the blueprint */
54
- steps: Array<CompiledStep>;
55
- /** The resources used by the compiled steps */
56
- resources: Array<{ resource: Resource; step: CompiledStep }>;
57
- progressTracker: ProgressTracker;
25
+ run: (playground: UniversalPHP) => Promise<void>;
58
26
  }
59
27
 
28
+ export type OnStepCompleted = (output: any, step: StepDefinition) => any;
29
+
60
30
  export interface CompileBlueprintOptions {
61
31
  /** Optional progress tracker to monitor progress */
62
32
  progress?: ProgressTracker;
63
33
  /** Optional semaphore to control access to a shared resource */
64
34
  semaphore?: Semaphore;
35
+ /** Optional callback with step output */
36
+ onStepCompleted?: OnStepCompleted;
65
37
  }
66
38
 
67
39
  /**
@@ -77,6 +49,7 @@ export function compileBlueprint(
77
49
  {
78
50
  progress = new ProgressTracker(),
79
51
  semaphore = new Semaphore({ concurrency: 3 }),
52
+ onStepCompleted = () => {},
80
53
  }: CompileBlueprintOptions = {}
81
54
  ): CompiledBlueprint {
82
55
  const steps = (blueprint.steps || []).filter(isStepDefinition);
@@ -85,15 +58,15 @@ export function compileBlueprint(
85
58
  (total, step) => total + (step.progress?.weight || 1),
86
59
  0
87
60
  );
88
- const compiledSteps = steps.map((step) =>
61
+ const compiled = steps.map((step) =>
89
62
  compileStep(step, {
90
63
  semaphore,
91
64
  rootProgressTracker: progress,
92
65
  totalProgressWeight,
93
66
  })
94
67
  );
68
+
95
69
  return {
96
- landingPage: blueprint.landingPage || '/',
97
70
  versions: {
98
71
  php: compileVersion(
99
72
  blueprint.preferredVersions?.php,
@@ -106,9 +79,28 @@ export function compileBlueprint(
106
79
  '6.2'
107
80
  ),
108
81
  },
109
- steps: compiledSteps,
110
- resources: getResources(compiledSteps),
111
- progressTracker: progress,
82
+ run: async (playground: UniversalPHP) => {
83
+ try {
84
+ // Start resolving resources early
85
+ for (const { asyncResources } of compiled) {
86
+ for (const resource of asyncResources) {
87
+ resource.resolve();
88
+ }
89
+ }
90
+
91
+ for (const { run, step } of compiled) {
92
+ const result = await run(playground);
93
+ onStepCompleted(result, step);
94
+ }
95
+ if ('goTo' in playground) {
96
+ await (playground as any).goTo(
97
+ blueprint.landingPage || '/'
98
+ );
99
+ }
100
+ } finally {
101
+ progress.finish();
102
+ }
103
+ },
112
104
  };
113
105
  }
114
106
 
@@ -138,7 +130,7 @@ function compileVersion<T>(
138
130
  * @returns Whether the object is a StepDefinition
139
131
  */
140
132
  function isStepDefinition(
141
- step: StepDefinition | string | undefined | false | null
133
+ step: Step | string | undefined | false | null
142
134
  ): step is StepDefinition {
143
135
  return !!(typeof step === 'object' && step);
144
136
  }
@@ -160,14 +152,14 @@ interface CompileStepArgsOptions {
160
152
  * @param options Additional options for the compilation
161
153
  * @returns The compiled step
162
154
  */
163
- function compileStep(
164
- step: StepDefinition,
155
+ function compileStep<S extends StepDefinition>(
156
+ step: S,
165
157
  {
166
158
  semaphore,
167
159
  rootProgressTracker,
168
160
  totalProgressWeight,
169
161
  }: CompileStepArgsOptions
170
- ): CompiledStep {
162
+ ): { run: CompiledStep; step: S; asyncResources: Array<Resource> } {
171
163
  const stepProgress = rootProgressTracker.stage(
172
164
  (step.progress?.weight || 1) / totalProgressWeight
173
165
  );
@@ -183,56 +175,77 @@ function compileStep(
183
175
  args[key] = value;
184
176
  }
185
177
 
186
- const compiledStep = {
187
- args: args as CompiledStep['args'],
188
- progress: stepProgress,
178
+ const run = async (playground: UniversalPHP) => {
179
+ try {
180
+ stepProgress.fillSlowly();
181
+ return await stepHandlers[step.step](
182
+ playground,
183
+ await resolveArguments(args),
184
+ {
185
+ tracker: stepProgress,
186
+ initialCaption: step.progress?.caption,
187
+ }
188
+ );
189
+ } finally {
190
+ stepProgress.finish();
191
+ }
189
192
  };
190
193
 
191
- const asyncResources = getResourcesFromStep(compiledStep).filter(
192
- (resource) => resource.isAsync
193
- );
194
-
195
194
  /**
196
195
  * The weight of each async resource is the same, and is the same as the
197
196
  * weight of the step itself.
198
197
  */
198
+ const asyncResources = getResources(args).filter(
199
+ (resource) => resource.isAsync
200
+ );
201
+
199
202
  const evenWeight = 1 / (asyncResources.length + 1);
200
203
  for (const resource of asyncResources) {
201
204
  resource.progress = stepProgress.stage(evenWeight);
202
205
  }
203
206
 
204
- return compiledStep;
207
+ return { run, step, asyncResources };
205
208
  }
206
209
 
207
210
  /**
208
- * Gets all the resources used by compiled steps
211
+ * Gets the resources used by a specific compiled step
209
212
  *
210
- * @param steps The list of compiled steps
211
- * @returns The resources used by the compiled steps
213
+ * @param step The compiled step
214
+ * @returns The resources used by the compiled step
212
215
  */
213
- function getResources(steps: CompiledStep[]) {
214
- let result: { resource: Resource; step: CompiledStep }[] = [];
215
- for (const step of steps) {
216
- result = result.concat(
217
- getResourcesFromStep(step).map((resource) => ({ resource, step }))
218
- );
216
+ function getResources<S extends StepDefinition>(args: S) {
217
+ const result: Resource[] = [];
218
+ for (const argName in args) {
219
+ const resourceMaybe = (args as any)[argName];
220
+ if (resourceMaybe instanceof Resource) {
221
+ result.push(resourceMaybe);
222
+ }
219
223
  }
220
224
  return result;
221
225
  }
222
226
 
223
227
  /**
224
- * Gets the resources used by a specific compiled step
228
+ * Replaces Resource objects with their resolved values
225
229
  *
226
230
  * @param step The compiled step
227
231
  * @returns The resources used by the compiled step
228
232
  */
229
- function getResourcesFromStep(step: CompiledStep) {
230
- const result: Resource[] = [];
231
- for (const argName in step.args) {
232
- const resourceMaybe = (step.args as any)[argName];
233
+ async function resolveArguments<T extends Record<string, unknown>>(args: T) {
234
+ const resolved: any = {};
235
+ for (const argName in args) {
236
+ const resourceMaybe = (args as any)[argName];
233
237
  if (resourceMaybe instanceof Resource) {
234
- result.push(resourceMaybe);
238
+ resolved[argName] = await resourceMaybe.resolve();
239
+ } else {
240
+ resolved[argName] = resourceMaybe;
235
241
  }
236
242
  }
237
- return result;
243
+ return resolved;
244
+ }
245
+
246
+ export async function runBlueprintSteps(
247
+ compiledBlueprint: CompiledBlueprint,
248
+ playground: UniversalPHP
249
+ ) {
250
+ await compiledBlueprint.run(playground);
238
251
  }
@@ -159,7 +159,7 @@ export class VFSResource extends Resource {
159
159
  this.resource.path
160
160
  );
161
161
  this.progress?.set(100);
162
- return new File([buffer], this.resource.path);
162
+ return new File([buffer], this.name);
163
163
  }
164
164
 
165
165
  /** @inheritDoc */
@@ -220,7 +220,7 @@ export abstract class FetchResource extends Resource {
220
220
  if (response.status !== 200) {
221
221
  throw new Error(`Could not download "${url}"`);
222
222
  }
223
- return new File([await response.blob()], url);
223
+ return new File([await response.blob()], this.name);
224
224
  }
225
225
 
226
226
  /**
@@ -1,8 +1,7 @@
1
- import { UniversalPHP } from '@php-wasm/universal';
2
- import { BaseStep } from '.';
1
+ import { StepHandler } from '.';
3
2
  import { asDOM } from './common';
4
3
 
5
- export interface ActivatePluginStep extends BaseStep {
4
+ export interface ActivatePluginStep {
6
5
  step: 'activatePlugin';
7
6
  plugin: string;
8
7
  }
@@ -13,7 +12,12 @@ export interface ActivatePluginStep extends BaseStep {
13
12
  * @param playground The playground client.
14
13
  * @param plugin The plugin slug.
15
14
  */
16
- export async function activatePlugin(playground: UniversalPHP, plugin: string) {
15
+ export const activatePlugin: StepHandler<ActivatePluginStep> = async (
16
+ playground,
17
+ { plugin },
18
+ progress
19
+ ) => {
20
+ progress?.tracker.setCaption(`Activating ${plugin}`);
17
21
  const pluginsPage = asDOM(
18
22
  await playground.request({
19
23
  url: '/wp-admin/plugins.php',
@@ -28,4 +32,4 @@ export async function activatePlugin(playground: UniversalPHP, plugin: string) {
28
32
  await playground.request({
29
33
  url: '/wp-admin/' + href,
30
34
  });
31
- }
35
+ };
@@ -1,13 +1,9 @@
1
1
  import { UniversalPHP } from '@php-wasm/universal';
2
- import { BaseStep } from '..';
2
+ import { StepHandler } from '..';
3
3
  import { updateFile } from '../common';
4
4
 
5
- export interface ApplyWordPressPatchesStep extends BaseStep {
5
+ export interface ApplyWordPressPatchesStep {
6
6
  step: 'applyWordPressPatches';
7
- wordpressPath: string;
8
- }
9
-
10
- export interface PatchOptions {
11
7
  siteUrl: string;
12
8
  wordpressPath?: string;
13
9
  patchSqlitePlugin?: boolean;
@@ -17,10 +13,9 @@ export interface PatchOptions {
17
13
  disableWpNewBlogNotification?: boolean;
18
14
  }
19
15
 
20
- export async function applyWordPressPatches(
21
- php: UniversalPHP,
22
- options: PatchOptions
23
- ) {
16
+ export const applyWordPressPatches: StepHandler<
17
+ ApplyWordPressPatchesStep
18
+ > = async (php, options) => {
24
19
  const patch = new WordPressPatcher(
25
20
  php,
26
21
  options.siteUrl,
@@ -42,7 +37,7 @@ export async function applyWordPressPatches(
42
37
  if (options.disableWpNewBlogNotification !== false) {
43
38
  await patch.disableWpNewBlogNotification();
44
39
  }
45
- }
40
+ };
46
41
 
47
42
  class WordPressPatcher {
48
43
  php: UniversalPHP;
@@ -1,56 +1,121 @@
1
1
  import { PHPRunOptions, PHPRequest } from '@php-wasm/universal';
2
- import { BaseStep } from '.';
2
+ import { StepHandler } from '.';
3
+ import { fileToUint8Array } from './common';
3
4
 
4
- export interface RunPHPStep extends BaseStep {
5
+ export interface RunPHPStep {
5
6
  step: 'runPHP';
6
7
  code: string;
7
8
  }
8
9
 
9
- export interface RunPHPWithOptionsStep extends BaseStep {
10
+ export const runPHP: StepHandler<RunPHPStep> = async (playground, { code }) => {
11
+ return await playground.run({ code });
12
+ };
13
+
14
+ export interface RunPHPWithOptionsStep {
10
15
  step: 'runPHPWithOptions';
11
16
  options: PHPRunOptions;
12
17
  }
13
18
 
14
- export interface SetPhpIniEntryStep extends BaseStep {
19
+ export const runPHPWithOptions: StepHandler<RunPHPWithOptionsStep> = async (
20
+ playground,
21
+ { options }
22
+ ) => {
23
+ return await playground.run(options);
24
+ };
25
+
26
+ export interface SetPhpIniEntryStep {
15
27
  step: 'setPhpIniEntry';
16
28
  key: string;
17
29
  value: string;
18
30
  }
19
31
 
20
- export interface RequestStep extends BaseStep {
32
+ export const setPhpIniEntry: StepHandler<SetPhpIniEntryStep> = async (
33
+ playground,
34
+ { key, value }
35
+ ) => {
36
+ await playground.setPhpIniEntry(key, value);
37
+ };
38
+
39
+ export interface RequestStep {
21
40
  step: 'request';
22
41
  request: PHPRequest;
23
42
  }
24
43
 
25
- export interface CpStep extends BaseStep {
44
+ export const request: StepHandler<RequestStep> = async (
45
+ playground,
46
+ { request }
47
+ ) => {
48
+ return await playground.request(request);
49
+ };
50
+
51
+ export interface CpStep {
26
52
  step: 'cp';
27
53
  fromPath: string;
28
54
  toPath: string;
29
55
  }
30
56
 
31
- export interface MvStep extends BaseStep {
57
+ export const cp: StepHandler<CpStep> = async (
58
+ playground,
59
+ { fromPath, toPath }
60
+ ) => {
61
+ await playground.writeFile(
62
+ toPath,
63
+ await playground.readFileAsBuffer(fromPath)
64
+ );
65
+ };
66
+
67
+ export interface MvStep {
32
68
  step: 'mv';
33
69
  fromPath: string;
34
70
  toPath: string;
35
71
  }
36
72
 
37
- export interface MkdirStep extends BaseStep {
73
+ export const mv: StepHandler<MvStep> = async (
74
+ playground,
75
+ { fromPath, toPath }
76
+ ) => {
77
+ await playground.mv(fromPath, toPath);
78
+ };
79
+
80
+ export interface MkdirStep {
38
81
  step: 'mkdir';
39
82
  path: string;
40
83
  }
41
84
 
42
- export interface RmStep extends BaseStep {
85
+ export const mkdir: StepHandler<MkdirStep> = async (playground, { path }) => {
86
+ await playground.mkdir(path);
87
+ };
88
+
89
+ export interface RmStep {
43
90
  step: 'rm';
44
91
  path: string;
45
92
  }
46
93
 
47
- export interface RmdirStep extends BaseStep {
94
+ export const rm: StepHandler<RmStep> = async (playground, { path }) => {
95
+ await playground.unlink(path);
96
+ };
97
+
98
+ export interface RmdirStep {
48
99
  step: 'rmdir';
49
100
  path: string;
50
101
  }
51
102
 
52
- export interface WriteFileStep<ResourceType> extends BaseStep {
103
+ export const rmdir: StepHandler<RmdirStep> = async (playground, { path }) => {
104
+ await playground.rmdir(path);
105
+ };
106
+
107
+ export interface WriteFileStep<ResourceType> {
53
108
  step: 'writeFile';
54
109
  path: string;
55
110
  data: ResourceType | string | Uint8Array;
56
111
  }
112
+
113
+ export const writeFile: StepHandler<WriteFileStep<File>> = async (
114
+ playground,
115
+ { path, data }
116
+ ) => {
117
+ if (data instanceof File) {
118
+ data = await fileToUint8Array(data);
119
+ }
120
+ await playground.writeFile(path, data);
121
+ };
@@ -20,3 +20,7 @@ export async function updateFile(
20
20
  ) {
21
21
  await php.writeFile(path, callback(await php.readFileAsText(path)));
22
22
  }
23
+
24
+ export async function fileToUint8Array(file: File) {
25
+ return new Uint8Array(await file.arrayBuffer());
26
+ }
@@ -1,8 +1,7 @@
1
- import { UniversalPHP } from '@php-wasm/universal';
2
- import { BaseStep } from '.';
1
+ import { StepHandler } from '.';
3
2
  import { updateFile } from './common';
4
3
 
5
- export interface DefineSiteUrlStep extends BaseStep {
4
+ export interface DefineSiteUrlStep {
6
5
  step: 'defineSiteUrl';
7
6
  siteUrl: string;
8
7
  }
@@ -13,7 +12,10 @@ export interface DefineSiteUrlStep extends BaseStep {
13
12
  * @param playground The playground client.
14
13
  * @param siteUrl
15
14
  */
16
- export async function defineSiteUrl(playground: UniversalPHP, siteUrl: string) {
15
+ export const defineSiteUrl: StepHandler<DefineSiteUrlStep> = async (
16
+ playground,
17
+ { siteUrl }
18
+ ) => {
17
19
  await updateFile(
18
20
  playground,
19
21
  `/wordpress/wp-config.php`,
@@ -27,4 +29,4 @@ export async function defineSiteUrl(playground: UniversalPHP, siteUrl: string) {
27
29
  }
28
30
  ?>${contents}`
29
31
  );
30
- }
32
+ };
@@ -0,0 +1,21 @@
1
+ export { activatePlugin } from './activate-plugin';
2
+ export { applyWordPressPatches } from './apply-wordpress-patches';
3
+ export {
4
+ rm,
5
+ cp,
6
+ mkdir,
7
+ rmdir,
8
+ mv,
9
+ setPhpIniEntry,
10
+ runPHP,
11
+ runPHPWithOptions,
12
+ request,
13
+ writeFile,
14
+ } from './client-methods';
15
+ export { defineSiteUrl } from './define-site-url';
16
+ export { importFile, unzip, replaceSite, zipEntireSite } from './import-export';
17
+ export { installPlugin } from './install-plugin';
18
+ export { installTheme } from './install-theme';
19
+ export { login } from './login';
20
+ export { runWpInstallationWizard } from './run-wp-installation-wizard';
21
+ export { setSiteOptions, updateUserMeta } from './site-data';