eddev 2.0.0-beta.72 → 2.0.0-beta.73

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.
@@ -368,8 +368,8 @@ export declare function useTailwindConfig(): {
368
368
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
369
369
  readonly [x: string]: string;
370
370
  } | undefined;
371
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
372
- readonly [x: string]: string;
371
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
372
+ readonly [x: string]: string | readonly string[];
373
373
  } | undefined;
374
374
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
375
375
  readonly [x: string]: string | any;
@@ -776,8 +776,8 @@ export declare function useTailwindConfig(): {
776
776
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
777
777
  readonly [x: string]: string;
778
778
  } | undefined;
779
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
780
- readonly [x: string]: string;
779
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
780
+ readonly [x: string]: string | readonly string[];
781
781
  } | undefined;
782
782
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
783
783
  readonly [x: string]: string | any;
@@ -1413,8 +1413,8 @@ export declare function useTailwindConfig(): {
1413
1413
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
1414
1414
  readonly [x: string]: string;
1415
1415
  } | undefined;
1416
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
1417
- readonly [x: string]: string;
1416
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
1417
+ readonly [x: string]: string | readonly string[];
1418
1418
  } | undefined;
1419
1419
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
1420
1420
  readonly [x: string]: string | any;
@@ -1821,8 +1821,8 @@ export declare function useTailwindConfig(): {
1821
1821
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
1822
1822
  readonly [x: string]: string;
1823
1823
  } | undefined;
1824
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
1825
- readonly [x: string]: string;
1824
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
1825
+ readonly [x: string]: string | readonly string[];
1826
1826
  } | undefined;
1827
1827
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
1828
1828
  readonly [x: string]: string | any;
@@ -2433,8 +2433,8 @@ export declare function useTailwindConfig(): {
2433
2433
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
2434
2434
  readonly [x: string]: string;
2435
2435
  } | undefined;
2436
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
2437
- readonly [x: string]: string;
2436
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
2437
+ readonly [x: string]: string | readonly string[];
2438
2438
  } | undefined;
2439
2439
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
2440
2440
  readonly [x: string]: string | any;
@@ -2841,8 +2841,8 @@ export declare function useTailwindConfig(): {
2841
2841
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
2842
2842
  readonly [x: string]: string;
2843
2843
  } | undefined;
2844
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
2845
- readonly [x: string]: string;
2844
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
2845
+ readonly [x: string]: string | readonly string[];
2846
2846
  } | undefined;
2847
2847
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
2848
2848
  readonly [x: string]: string | any;
@@ -3478,8 +3478,8 @@ export declare function useTailwindConfig(): {
3478
3478
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
3479
3479
  readonly [x: string]: string;
3480
3480
  } | undefined;
3481
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
3482
- readonly [x: string]: string;
3481
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
3482
+ readonly [x: string]: string | readonly string[];
3483
3483
  } | undefined;
3484
3484
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
3485
3485
  readonly [x: string]: string | any;
@@ -3886,8 +3886,8 @@ export declare function useTailwindConfig(): {
3886
3886
  readonly opacity?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
3887
3887
  readonly [x: string]: string;
3888
3888
  } | undefined;
3889
- readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string>) | {
3890
- readonly [x: string]: string;
3889
+ readonly boxShadow?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").KeyValuePair<string, string | string[]>) | {
3890
+ readonly [x: string]: string | readonly string[];
3891
3891
  } | undefined;
3892
3892
  readonly boxShadowColor?: ((utils: import("tailwindcss/types/config.js").PluginUtils) => import("tailwindcss/types/config.js").RecursiveKeyValuePair<string, string>) | {
3893
3893
  readonly [x: string]: string | any;
@@ -9,7 +9,7 @@ export async function proxyWpAdmin(event) {
9
9
  const reqUrl = getRequestURL(event);
10
10
  const serverlessConfig = serverContext.config.serverless;
11
11
  if (!serverContext.dev) {
12
- if (reqUrl.pathname.toLowerCase().match(/\/+(graphql|wp\-(admin|login|json))/)) {
12
+ if (reqUrl.pathname.toLowerCase().match(/\/+(graphql|xmlrpc|index\.php|wp\-(admin|login|json))/)) {
13
13
  if (serverlessConfig.admin === "hide") {
14
14
  return renderErrorPage({
15
15
  code: 404,
@@ -57,9 +57,6 @@ export async function renderPage(args) {
57
57
  serverContext.fetchAppData(),
58
58
  serverContext.fetchRouteData({
59
59
  pathname: args.pathname,
60
- withAppData: false,
61
- headers: {},
62
- query: {},
63
60
  newOrigin: args.newOrigin,
64
61
  }),
65
62
  ]);
@@ -1,8 +1,8 @@
1
- import { RequestHeaders } from "vinxi/http";
2
- import { UrlReplacerConf } from "./utils/replace-host.js";
3
1
  import { Manifest } from "vinxi/dist/types/types/manifest";
4
- import { TrackerTags } from "../lib/routing/types.js";
2
+ import { RequestHeaders } from "vinxi/http";
5
3
  import type { EDConfig } from "../../node/project/config.js";
4
+ import { TrackerTags } from "../lib/routing/types.js";
5
+ import { UrlReplacerConf } from "./utils/replace-host.js";
6
6
  export type ServerContextArgs = {
7
7
  dev: boolean;
8
8
  origin: string;
@@ -38,9 +38,7 @@ export declare class ServerContext {
38
38
  }): Promise<Response>;
39
39
  fetchRouteData(req: {
40
40
  pathname: string;
41
- query?: Record<string, any>;
42
41
  headers?: RequestHeaders;
43
- withAppData?: boolean;
44
42
  newOrigin?: string;
45
43
  }): Promise<Response>;
46
44
  fetchAppData(): Promise<ServerAppData>;
@@ -1,6 +1,8 @@
1
1
  import { parseURL, stringifyParsedURL, withQuery, withTrailingSlash } from "ufo";
2
+ import { fetchWP } from "../../node/utils/fetch-wp.js";
2
3
  import { filterHeader } from "./utils/headers.js";
3
4
  import { createUrlReplacer } from "./utils/replace-host.js";
5
+ import { swr } from "./utils/swr-cache.js";
4
6
  const PROXY_RESPONSE_HEADERS = ["content-type", "set-cookie", /^x-/, "cache-control", /woocommerce/];
5
7
  const PROXY_REQUEST_HEADERS = [
6
8
  "content-type",
@@ -13,6 +15,7 @@ const PROXY_REQUEST_HEADERS = [
13
15
  "authorization",
14
16
  "woocommerce-session",
15
17
  ];
18
+ const VARIES_HEADERS = ["Authorization", "woocommerce-session"];
16
19
  let runtime;
17
20
  export class ServerContext {
18
21
  dev;
@@ -52,7 +55,7 @@ export class ServerContext {
52
55
  }
53
56
  async fetchOrigin(url, opts) {
54
57
  url = this.getOriginUrl(url);
55
- const response = await fetch(url, {
58
+ const response = await fetchWP(url, {
56
59
  ...opts,
57
60
  headers: {
58
61
  "Content-Type": "application/json",
@@ -82,53 +85,88 @@ export class ServerContext {
82
85
  });
83
86
  }
84
87
  async fetchRouteData(req) {
85
- const fetchUrl = withQuery(withTrailingSlash(req.pathname), {
86
- ...req.query,
87
- _props: "1",
88
- _ssr: "1",
89
- _debug: this.dev ? "1" : undefined,
90
- });
91
- // console.log("Fetching route data", req.pathname)
92
- const result = await this.fetchOrigin(fetchUrl, {
93
- cache: "no-cache",
94
- replaceUrls: true,
95
- newOrigin: req.newOrigin,
96
- headers: {
97
- "Content-Type": "application/json",
98
- Accept: "application/json",
88
+ /**
89
+ * Calculcate the cache key.
90
+ * For route data, the cache key is the pathname.
91
+ * TODO: Potential support for Vercel draft mode.
92
+ */
93
+ let cacheKey = "fetchRouteData:" + req.pathname;
94
+ const result = await swr({
95
+ key: cacheKey,
96
+ getFreshValue: async (ctx) => {
97
+ ctx.metadata.createdTime;
98
+ const fetchUrl = withQuery(withTrailingSlash(req.pathname), {
99
+ _props: "1",
100
+ _ssr: "1",
101
+ _debug: this.dev ? "1" : undefined,
102
+ });
103
+ const result = await this.fetchOrigin(fetchUrl, {
104
+ cache: "no-cache",
105
+ replaceUrls: true,
106
+ newOrigin: req.newOrigin,
107
+ headers: {
108
+ "Content-Type": "application/json",
109
+ Accept: "application/json",
110
+ },
111
+ redirect: "manual",
112
+ });
113
+ const preferredCacheDuration = parseInt(result.headers.get("x-ed-cache-duration") ?? "");
114
+ if (isFinite(preferredCacheDuration)) {
115
+ ctx.metadata.ttl = preferredCacheDuration * 1000;
116
+ }
117
+ let resultStatus = result.status;
118
+ let resultHeaders = result.headers;
119
+ let resultData = await result.json();
120
+ // Special case for redirects
121
+ if (result.headers.get("location")) {
122
+ let location = result.headers.get("location");
123
+ let status = result.status;
124
+ if (this.replaceUrls) {
125
+ location = this.replaceUrls(location);
126
+ }
127
+ resultHeaders.delete("location");
128
+ resultData = {
129
+ redirect: location,
130
+ status: status,
131
+ };
132
+ }
133
+ if (resultData && typeof resultData === "object") {
134
+ resultData.__generated = new Date().toISOString();
135
+ }
136
+ return {
137
+ status: resultStatus,
138
+ headers: resultHeaders,
139
+ data: JSON.stringify(resultData),
140
+ };
99
141
  },
100
- redirect: "manual",
101
142
  });
102
- if (result.headers.get("content-type") && result.headers.get("location")) {
103
- let location = result.headers.get("location");
104
- let status = result.status;
105
- if (this.replaceUrls) {
106
- location = this.replaceUrls(location);
107
- }
108
- const headers = new Headers({
109
- ...result.headers,
110
- });
111
- headers.delete("location");
112
- return new Response(JSON.stringify({
113
- redirect: location,
114
- status: status,
115
- }), {
116
- headers: headers,
117
- });
118
- }
119
- return result;
143
+ return new Response(result.data, {
144
+ status: result.status,
145
+ headers: result.headers,
146
+ });
120
147
  }
121
148
  async fetchAppData() {
122
- const response = await this.fetchOrigin("/_appdata", {
123
- cache: "no-cache",
124
- replaceUrls: true,
125
- headers: {
126
- "Content-Type": "application/json",
127
- Accept: "application/json",
149
+ const data = await swr({
150
+ key: "fetchAppData",
151
+ getFreshValue: async (ctx) => {
152
+ const response = await this.fetchOrigin("/_appdata", {
153
+ cache: "no-cache",
154
+ replaceUrls: true,
155
+ headers: {
156
+ "Content-Type": "application/json",
157
+ Accept: "application/json",
158
+ },
159
+ });
160
+ const result = await response.json();
161
+ const preferredCacheDuration = parseInt(response.headers.get("x-ed-cache-duration") ?? "");
162
+ if (isFinite(preferredCacheDuration)) {
163
+ ctx.metadata.ttl = preferredCacheDuration * 1000;
164
+ }
165
+ result.__generated = new Date().toISOString();
166
+ return result;
128
167
  },
129
168
  });
130
- const result = await response.json();
131
- return result;
169
+ return data;
132
170
  }
133
171
  extractRequestHeaders(req) {
134
172
  const headers = {};
@@ -143,15 +181,41 @@ export class ServerContext {
143
181
  }
144
182
  async fetchNamedQuery(req) {
145
183
  const url = `/wp-json/ed/v1/query/${req.name}?params=${encodeURIComponent(JSON.stringify(req.params))}`;
146
- return this.fetchOrigin(url, {
147
- cache: "no-cache",
148
- replaceUrls: true,
149
- headers: {
150
- ...this.extractRequestHeaders(req.headers),
151
- "Content-Type": "application/json",
152
- Accept: "application/json",
184
+ const hasVariedHeaders = VARIES_HEADERS.some((key) => !!req.headers[key]);
185
+ const key = `fetchNamedQuery:${req.name}:${JSON.stringify(req.params)}`;
186
+ const result = await swr({
187
+ key,
188
+ getFreshValue: async (ctx) => {
189
+ const result = await this.fetchOrigin(url, {
190
+ cache: "no-cache",
191
+ replaceUrls: true,
192
+ headers: {
193
+ ...this.extractRequestHeaders(req.headers),
194
+ "Content-Type": "application/json",
195
+ Accept: "application/json",
196
+ },
197
+ });
198
+ const preferredCacheDuration = parseInt(result.headers.get("x-ed-cache-duration") ?? "");
199
+ if (isFinite(preferredCacheDuration)) {
200
+ ctx.metadata.ttl = preferredCacheDuration * 1000;
201
+ }
202
+ let resultStatus = result.status;
203
+ let resultHeaders = result.headers;
204
+ let resultData = await result.json();
205
+ if (resultData && typeof resultData === "object") {
206
+ resultData.__generated = new Date().toISOString();
207
+ }
208
+ return {
209
+ status: resultStatus,
210
+ headers: resultHeaders,
211
+ data: JSON.stringify(resultData),
212
+ };
153
213
  },
154
214
  });
215
+ return new Response(result.data, {
216
+ status: result.status,
217
+ headers: result.headers,
218
+ });
155
219
  }
156
220
  async fetchMutation(req) {
157
221
  const url = `/wp-json/ed/v1/mutation/${req.name}`;
@@ -22,7 +22,6 @@ export function createUrlReplacer(conf) {
22
22
  }
23
23
  }
24
24
  }
25
- console.log("Replacing", url, "with", newOrigin);
26
25
  if (newOrigin) {
27
26
  if (isEscaped) {
28
27
  return newOrigin.replace(/\//g, "\\/") + path;
@@ -1,7 +1,4 @@
1
- import { Cache } from "@epic-web/cachified";
2
- export declare function swr<T>(args: {
3
- key: string;
4
- fetcher: () => Promise<T>;
5
- ttl?: number;
1
+ import { Cache, CachifiedOptions } from "@epic-web/cachified";
2
+ export declare function swr<T>(args: Omit<CachifiedOptions<T>, "cache"> & {
6
3
  cache?: Cache;
7
4
  }): Promise<T>;
@@ -1,6 +1,5 @@
1
1
  import { LRUCache } from "lru-cache";
2
- import { cachified, totalTtl } from "@epic-web/cachified";
3
- /* lru cache is not part of this package but a simple non-persistent cache */
2
+ import { cachified, totalTtl, verboseReporter } from "@epic-web/cachified";
4
3
  const lruInstance = new LRUCache({ max: 1000 });
5
4
  const lru = {
6
5
  set(key, value) {
@@ -19,11 +18,10 @@ const lru = {
19
18
  };
20
19
  export function swr(args) {
21
20
  return cachified({
22
- key: args.key,
23
- cache: args.cache ?? lru,
24
- getFreshValue: args.fetcher,
25
- ttl: args.ttl,
26
- swr: 3600_000,
21
+ ttl: args.ttl ?? 10_000,
22
+ swr: 3600_000 * 12,
27
23
  fallbackToCache: 15_000,
28
- });
24
+ cache: args.cache ?? lru,
25
+ ...args,
26
+ }, verboseReporter());
29
27
  }
@@ -47,7 +47,7 @@ export class CLIWorker {
47
47
  if (!isMainThread) {
48
48
  const mode = workerData.mode;
49
49
  /** Ignore self-signed certificate errors in dev mode */
50
- process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
50
+ // process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
51
51
  configureCliMode({
52
52
  interactive: true,
53
53
  readonly: false,
@@ -42,7 +42,7 @@ program
42
42
  // }
43
43
  process.env["NODE_ENV"] = "development";
44
44
  /** Ignore self-signed certificate errors in dev mode */
45
- process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
45
+ // process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
46
46
  serverlessLog.info(chalk.yellowBright(`⚡️ ED. Stack v${VERSION}`));
47
47
  if (options.fast) {
48
48
  options.mode = "graphql,serverless";
@@ -79,6 +79,12 @@ program
79
79
  rootDir: process.cwd(),
80
80
  reportPluginCompatibility: true,
81
81
  });
82
+ const result = await project.verifyOriginAccess();
83
+ if (!result.success) {
84
+ projectLog.fail(result.message);
85
+ process.exit(1);
86
+ return;
87
+ }
82
88
  const infoWriter = new BuildInfoWriter(project);
83
89
  infoWriter.watch();
84
90
  infoWriter.write();
@@ -152,7 +158,7 @@ program
152
158
  .action(async (options) => {
153
159
  process.env["NODE_ENV"] = "production";
154
160
  /** Ignore self-signed certificate errors in dev mode */
155
- process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
161
+ // process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
156
162
  init(options.verbose);
157
163
  configureCliMode({
158
164
  interactive: false,
@@ -170,12 +176,20 @@ program
170
176
  });
171
177
  const buildServerless = options.serverless || process.env.VERCEL;
172
178
  if (buildServerless) {
179
+ // Verify that the we have access to the origin
180
+ const result = await project.verifyOriginAccess();
181
+ if (!result.success) {
182
+ console.error(result.message);
183
+ process.exit(1);
184
+ return;
185
+ }
173
186
  await buildVinxi({
174
187
  project,
175
188
  console,
176
189
  });
177
190
  }
178
191
  else {
192
+ const infoWriter = new BuildInfoWriter(project);
179
193
  // Generate bootstrap files
180
194
  const codegen = createVinxiCodegen({
181
195
  mode: "production",
@@ -194,7 +208,7 @@ program
194
208
  const graphql = new GraphQLGenerator(project, {
195
209
  optimize: true,
196
210
  });
197
- await Promise.all([admin.start(), frontend.start(), graphql.start()]);
211
+ await Promise.all([infoWriter.write(), admin.start(), frontend.start(), graphql.start()]);
198
212
  console.log("Done building SPA WordPress");
199
213
  }
200
214
  });
@@ -204,7 +218,7 @@ program
204
218
  .option("--host", "Hostname to serve the application", "127.0.0.1")
205
219
  .option("--port", "Port to serve the application", "3000")
206
220
  .action(async (options) => {
207
- process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
221
+ // process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
208
222
  process.env["NODE_ENV"] = "production";
209
223
  process.env["HOST"] = options.host;
210
224
  process.env["PORT"] = options.port;
@@ -1 +1 @@
1
- export declare const VERSION = "2.0.0-beta.72";
1
+ export declare const VERSION = "2.0.0-beta.73";
@@ -1 +1 @@
1
- export const VERSION = "2.0.0-beta.72";
1
+ export const VERSION = "2.0.0-beta.73";
@@ -193,7 +193,6 @@ export function createVinxiCodegen(opts) {
193
193
 
194
194
  return await serverContext.fetchRouteData({
195
195
  pathname: id,
196
- withAppData: false,
197
196
  newOrigin: url.origin,
198
197
  })
199
198
  }),
@@ -206,7 +205,6 @@ export function createVinxiCodegen(opts) {
206
205
 
207
206
  return await serverContext.fetchRouteData({
208
207
  pathname: id,
209
- withAppData: false,
210
208
  newOrigin: url.origin,
211
209
  })
212
210
  }),
@@ -268,7 +266,7 @@ export function createVinxiCodegen(opts) {
268
266
  return handleRPC(event)
269
267
  }
270
268
 
271
- if (url.pathname.includes(".")) {
269
+ if (url.pathname.includes(".") || url.pathname.startsWith("/graphql")) {
272
270
  return proxyWpAdmin(event)
273
271
  }
274
272
 
@@ -65,7 +65,7 @@ export class GraphQLGenerator {
65
65
  constructor(project, opts) {
66
66
  this.project = project;
67
67
  this.opts = opts;
68
- this.schemaLoader = new GraphQLSchemaLoader(ProjectEnvUtils.getSafe("DEBUG_GRAPHQL_URL"));
68
+ this.schemaLoader = new GraphQLSchemaLoader(ProjectEnvUtils.getSafe("DEBUG_GRAPHQL_URL"), ProjectEnvUtils.getSafe("SITE_API_KEY"));
69
69
  this.fileLoader = createGraphQLFileLoader(this.project, [
70
70
  {
71
71
  baseFolder: "queries/fragments",
@@ -6,8 +6,9 @@ type LoadResult = {
6
6
  };
7
7
  export declare class GraphQLSchemaLoader {
8
8
  url: string;
9
+ apiKey?: string | undefined;
9
10
  lastHash: string;
10
- constructor(url: string);
11
+ constructor(url: string, apiKey?: string | undefined);
11
12
  private cachedResult;
12
13
  get(forceReload?: boolean): Promise<Error | LoadResult>;
13
14
  load(): Promise<Error | LoadResult>;
@@ -4,11 +4,14 @@ import { introspectionFromSchema } from "graphql";
4
4
  import { graphqlLog as console } from "./graphql-codegen.js";
5
5
  import chalk from "chalk";
6
6
  import { hash } from "object-code";
7
+ import { fetchWP } from "../utils/fetch-wp.js";
7
8
  export class GraphQLSchemaLoader {
8
9
  url;
10
+ apiKey;
9
11
  lastHash = "";
10
- constructor(url) {
12
+ constructor(url, apiKey) {
11
13
  this.url = url;
14
+ this.apiKey = apiKey;
12
15
  }
13
16
  cachedResult = null;
14
17
  async get(forceReload = false) {
@@ -21,21 +24,7 @@ export class GraphQLSchemaLoader {
21
24
  console.info(`${this.lastHash ? "Reloading" : "Loading"} GraphQL schema from ${chalk.yellowBright(this.url)}`);
22
25
  const rawSchema = await loadSchema(this.url, {
23
26
  loaders: [new UrlLoader()],
24
- // fetch: (url: string, opts: any) => {
25
- // const httpsAgent = new Agent({
26
- // rejectUnauthorized: false,
27
- // })
28
- // return fetch(url, {
29
- // ...opts,
30
- // agent: function (_parsedURL) {
31
- // if (_parsedURL.protocol == "http:") {
32
- // return undefined
33
- // } else {
34
- // return httpsAgent
35
- // }
36
- // },
37
- // })
38
- // },
27
+ fetch: fetchWP,
39
28
  }).catch((e) => {
40
29
  return e;
41
30
  });
@@ -29,11 +29,15 @@ export declare const EDConfigSchema: z.ZodObject<{
29
29
  uploads: z.ZodEnum<["proxy", "remote"]>;
30
30
  plugins: z.ZodDefault<z.ZodEnum<["proxy", "remote"]>>;
31
31
  admin: z.ZodDefault<z.ZodEnum<["proxy", "hide"]>>;
32
+ originProtection: z.ZodDefault<z.ZodObject<{
33
+ requireLogin: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
34
+ }, "strip", z.ZodTypeAny, {
35
+ requireLogin: boolean;
36
+ }, {
37
+ requireLogin?: boolean | undefined;
38
+ }>>;
32
39
  themeAssets: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
33
- apiOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
34
40
  endpoints: z.ZodRecord<z.ZodString, z.ZodString>;
35
- defaultRevalidate: z.ZodOptional<z.ZodNumber>;
36
- defaultRevalidateQueries: z.ZodOptional<z.ZodNumber>;
37
41
  cors: z.ZodOptional<z.ZodObject<{
38
42
  origins: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
39
43
  }, "strip", z.ZodTypeAny, {
@@ -46,11 +50,11 @@ export declare const EDConfigSchema: z.ZodObject<{
46
50
  enabled: boolean;
47
51
  uploads: "proxy" | "remote";
48
52
  admin: "hide" | "proxy";
53
+ originProtection: {
54
+ requireLogin: boolean;
55
+ };
49
56
  themeAssets: string[];
50
- apiOnly: boolean;
51
57
  endpoints: Record<string, string>;
52
- defaultRevalidate?: number | undefined;
53
- defaultRevalidateQueries?: number | undefined;
54
58
  cors?: {
55
59
  origins?: string[] | undefined;
56
60
  } | undefined;
@@ -60,10 +64,10 @@ export declare const EDConfigSchema: z.ZodObject<{
60
64
  plugins?: "proxy" | "remote" | undefined;
61
65
  enabled?: boolean | undefined;
62
66
  admin?: "hide" | "proxy" | undefined;
67
+ originProtection?: {
68
+ requireLogin?: boolean | undefined;
69
+ } | undefined;
63
70
  themeAssets?: string[] | undefined;
64
- apiOnly?: boolean | undefined;
65
- defaultRevalidate?: number | undefined;
66
- defaultRevalidateQueries?: number | undefined;
67
71
  cors?: {
68
72
  origins?: string[] | undefined;
69
73
  } | undefined;
@@ -88,11 +92,11 @@ export declare const EDConfigSchema: z.ZodObject<{
88
92
  enabled: boolean;
89
93
  uploads: "proxy" | "remote";
90
94
  admin: "hide" | "proxy";
95
+ originProtection: {
96
+ requireLogin: boolean;
97
+ };
91
98
  themeAssets: string[];
92
- apiOnly: boolean;
93
99
  endpoints: Record<string, string>;
94
- defaultRevalidate?: number | undefined;
95
- defaultRevalidateQueries?: number | undefined;
96
100
  cors?: {
97
101
  origins?: string[] | undefined;
98
102
  } | undefined;
@@ -118,10 +122,10 @@ export declare const EDConfigSchema: z.ZodObject<{
118
122
  plugins?: "proxy" | "remote" | undefined;
119
123
  enabled?: boolean | undefined;
120
124
  admin?: "hide" | "proxy" | undefined;
125
+ originProtection?: {
126
+ requireLogin?: boolean | undefined;
127
+ } | undefined;
121
128
  themeAssets?: string[] | undefined;
122
- apiOnly?: boolean | undefined;
123
- defaultRevalidate?: number | undefined;
124
- defaultRevalidateQueries?: number | undefined;
125
129
  cors?: {
126
130
  origins?: string[] | undefined;
127
131
  } | undefined;
@@ -41,17 +41,23 @@ export const EDConfigSchema = z.object({
41
41
  .enum(["proxy", "hide"])
42
42
  .default("proxy")
43
43
  .describe("How to handle the WordPress admin URLs.\nDefault is `proxy`, which will proxy the admin URLs to the CMS origin. When set to `hide`, all /wp-admin, /wp-json and /wp-login.php URLs will show a 404 page.\nHas no effect in dev mode."),
44
+ originProtection: z
45
+ .object({
46
+ requireLogin: z
47
+ .boolean()
48
+ .optional()
49
+ .default(false)
50
+ .describe("Enabling this will prevent access to the frontend for non-logged-in users on the CMS origin (eg. Flywheel, WPEngine), but allow access to the serverless frontend (eg. Vercel)."),
51
+ })
52
+ .default({}),
44
53
  themeAssets: z
45
54
  .array(z.string())
46
55
  .optional()
47
56
  .default(["assets"])
48
57
  .describe('Asset folders to include in a serverless deployment.\nDefault is `["assets"]`'),
49
- apiOnly: z.boolean().optional().default(false).describe("Whether to deploy only the API, not the frontend"),
50
58
  endpoints: z
51
59
  .record(z.string(), z.string())
52
60
  .describe("A map of WordPress hostnames -> serverless hostnames. You can use `*` as a wildcard for the key.\n\nFor example, `{'cms.ed.studio': 'ed.studio'}` tells the WordPress frontend where it can find any serverless API endpoints."),
53
- defaultRevalidate: z.number().optional(),
54
- defaultRevalidateQueries: z.number().optional(),
55
61
  cors: z
56
62
  .object({
57
63
  origins: z
@@ -66,7 +72,8 @@ export const EDConfigSchema = z.object({
66
72
  props: z.number(),
67
73
  queries: z.number(),
68
74
  }))
69
- .optional(),
75
+ .optional()
76
+ .describe("A map of origin domains -> cache settings. Wildcards can be used for each domain."),
70
77
  devUI: z.enum(["disabled", "enabled"]).default("enabled"),
71
78
  });
72
79
  export class Configurator {
@@ -5,22 +5,26 @@ export declare const fields: {
5
5
  readonly SITE_URL: z.ZodString;
6
6
  readonly DEV_HOT_PORT: z.ZodEffects<z.ZodEffects<z.ZodDefault<z.ZodString>, string | number, string | undefined>, number, string | undefined>;
7
7
  readonly DEV_SERVERLESS_PORT: z.ZodEffects<z.ZodEffects<z.ZodDefault<z.ZodString>, string | number, string | undefined>, number, string | undefined>;
8
+ readonly SITE_API_KEY: z.ZodOptional<z.ZodString>;
8
9
  };
9
10
  export declare const ProjectEnvSchema: z.ZodObject<{
10
11
  readonly DEBUG_GRAPHQL_URL: z.ZodOptional<z.ZodString>;
11
12
  readonly SITE_URL: z.ZodOptional<z.ZodString>;
12
13
  readonly DEV_HOT_PORT: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodDefault<z.ZodString>, string | number, string | undefined>, number, string | undefined>>;
13
14
  readonly DEV_SERVERLESS_PORT: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodDefault<z.ZodString>, string | number, string | undefined>, number, string | undefined>>;
15
+ readonly SITE_API_KEY: z.ZodOptional<z.ZodOptional<z.ZodString>>;
14
16
  }, "strip", z.ZodTypeAny, {
15
17
  DEBUG_GRAPHQL_URL?: string | undefined;
16
18
  SITE_URL?: string | undefined;
17
19
  DEV_HOT_PORT?: number | undefined;
18
20
  DEV_SERVERLESS_PORT?: number | undefined;
21
+ SITE_API_KEY?: string | undefined;
19
22
  }, {
20
23
  DEBUG_GRAPHQL_URL?: string | undefined;
21
24
  SITE_URL?: string | undefined;
22
25
  DEV_HOT_PORT?: string | undefined;
23
26
  DEV_SERVERLESS_PORT?: string | undefined;
27
+ SITE_API_KEY?: string | undefined;
24
28
  }>;
25
29
  export type ProjectEnvKey = keyof typeof fields;
26
30
  export declare const ProjectEnvUtils: {
@@ -15,6 +15,7 @@ export const fields = {
15
15
  SITE_URL: z.string().url().describe("The URL of the site"),
16
16
  DEV_HOT_PORT: portNumber,
17
17
  DEV_SERVERLESS_PORT: portNumber,
18
+ SITE_API_KEY: z.string().optional().describe("The API key for the site, when origin protection is enabled"),
18
19
  };
19
20
  export const ProjectEnvSchema = z.object(fields).partial();
20
21
  export const ProjectEnvUtils = {
@@ -29,6 +29,12 @@ export declare class Project {
29
29
  private wp;
30
30
  reportPluginCompatibility: boolean;
31
31
  private constructor();
32
+ verifyOriginAccess(): Promise<{
33
+ success: true;
34
+ } | {
35
+ success: false;
36
+ message: string;
37
+ }>;
32
38
  private load;
33
39
  reportProjectInfo(): string;
34
40
  getWordpressInfo(): Promise<{
@@ -12,6 +12,7 @@ import { loadRouteManifest } from "./manifest/routes-manifest.js";
12
12
  import { loadViewManifest } from "./manifest/view-manifest.js";
13
13
  import { loadWidgetManifest } from "./manifest/widget-manifest.js";
14
14
  import { WPInfo } from "./wp-info.js";
15
+ import { fetchWP } from "../utils/fetch-wp.js";
15
16
  const console = createConsole("Project", "project");
16
17
  export const projectLog = console;
17
18
  /**
@@ -44,6 +45,34 @@ export class Project {
44
45
  this.serverRoutes = loadRouteManifest(this);
45
46
  this.wp = new WPInfo(this.origin);
46
47
  }
48
+ async verifyOriginAccess() {
49
+ try {
50
+ const response = await fetchWP(this.wp.infoUrl);
51
+ if (!response.ok) {
52
+ return {
53
+ success: false,
54
+ message: [
55
+ `Failed to access WordPress origin at ${this.origin}`,
56
+ `Using API Key?: ${ProjectEnvUtils.getSafe("SITE_API_KEY") ? "Yes" : "No"}`,
57
+ `Response: ${response.status} ${response.statusText}`,
58
+ ].join("\n"),
59
+ };
60
+ }
61
+ return {
62
+ success: true,
63
+ };
64
+ }
65
+ catch (err) {
66
+ return {
67
+ success: false,
68
+ message: [
69
+ `Failed to access WordPress origin at ${this.origin}`,
70
+ `Using API Key?: ${ProjectEnvUtils.getSafe("SITE_API_KEY") ? "Yes" : "No"}`,
71
+ `The following error occurred:\n${err}`,
72
+ ].join("\n"),
73
+ };
74
+ }
75
+ }
47
76
  async load() {
48
77
  console.setWorking(true);
49
78
  console.setState(this);
@@ -26,6 +26,7 @@ export declare class WPInfo {
26
26
  siteUrl: string;
27
27
  private cached?;
28
28
  constructor(siteUrl: string);
29
+ get infoUrl(): string;
29
30
  getInfo(force?: boolean): Promise<WPInfoData>;
30
31
  getPluginIssues(): Promise<string[]>;
31
32
  }
@@ -1,18 +1,22 @@
1
1
  import { resolveURL } from "ufo";
2
+ import { fetchWP } from "../utils/fetch-wp.js";
2
3
  export class WPInfo {
3
4
  siteUrl;
4
5
  cached;
5
6
  constructor(siteUrl) {
6
7
  this.siteUrl = siteUrl;
7
8
  }
9
+ get infoUrl() {
10
+ return resolveURL(this.siteUrl, "/wp-json/ed/v1/dev/site-info");
11
+ }
8
12
  async getInfo(force = false) {
9
13
  if (!this.siteUrl) {
10
14
  throw new Error("Attempted to get site info from WordPress, but SITE_URL was not present in the environment.");
11
15
  }
12
16
  if (this.cached && !force)
13
17
  return this.cached;
14
- const url = resolveURL(this.siteUrl, "/wp-json/ed/v1/dev/site-info");
15
- const response = await fetch(url);
18
+ const url = this.infoUrl;
19
+ const response = await fetchWP(url);
16
20
  const result = (await response.json());
17
21
  this.cached = result;
18
22
  return result;
@@ -0,0 +1 @@
1
+ export declare function fetchWP(url: string | URL, args?: RequestInit): Promise<Response>;
@@ -0,0 +1,27 @@
1
+ import { ProjectEnvUtils } from "../project/env.js";
2
+ import { Agent, fetch, setGlobalDispatcher, Headers } from "undici";
3
+ const agent = new Agent({
4
+ connect: {
5
+ rejectUnauthorized: false,
6
+ },
7
+ });
8
+ export function fetchWP(url, args = {}) {
9
+ const headers = new Headers(args.headers);
10
+ // If an API key has been set in the environment, use it
11
+ const accessKey = ProjectEnvUtils.getSafe("SITE_API_KEY");
12
+ if (accessKey) {
13
+ headers.set("x-ed-api-key", accessKey);
14
+ }
15
+ // Allow self-signed certificates when using a local development domain
16
+ let allowSelfSigned = isDevelopmentDomain(url);
17
+ return fetch(url, {
18
+ ...args,
19
+ dispatcher: allowSelfSigned ? agent : undefined,
20
+ headers,
21
+ });
22
+ }
23
+ setGlobalDispatcher(agent);
24
+ function isDevelopmentDomain(url) {
25
+ const hostname = typeof url === "string" ? new URL(url).hostname : url.hostname;
26
+ return !!hostname?.match(/(^[0-9\.]+$|\.local$)/);
27
+ }
@@ -2,5 +2,5 @@
2
2
  * Returns true if we think that the compiler is running in a deployment context (Vercel, GitHub Actions, etc.)
3
3
  */
4
4
  export function isDeploying() {
5
- return !!process.env.VERCEL;
5
+ return !!process.env.VERCEL || !!process.env.GITHUB_ACTIONS;
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eddev",
3
- "version": "2.0.0-beta.72",
3
+ "version": "2.0.0-beta.73",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -106,8 +106,9 @@
106
106
  "ink-spinner": "^5.0.0",
107
107
  "ink-text-input": "^6.0.0",
108
108
  "listhen": "^1.6.0",
109
- "lru-cache": "^11.0.2",
109
+ "lru-cache": "10.4.1",
110
110
  "mkcert": "^3.2.0",
111
+ "mnemonist": "^0.39.8",
111
112
  "obj-console": "^1.0.2",
112
113
  "object-code": "^1.3.3",
113
114
  "qs": "^6.13.0",
@@ -116,6 +117,7 @@
116
117
  "ts-poet": "^6.6.0",
117
118
  "ufo": "^1.3.1",
118
119
  "undent": "^0.1.0",
120
+ "undici": "^6.21.0",
119
121
  "valtio": "^1.13.2",
120
122
  "vinxi": "^0.4.3",
121
123
  "vite-tsconfig-paths": "^4.2.1",