@spacefast/wpcloud-sdk 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,2 @@
1
- export { makeFakeAtomic, defaultFakeFetch } from "./server.js";
2
- export { FakeAtomicStore, type FakeSite } from "./store.js";
3
- export { routeFakeRequest, type FakeResult } from "./router.js";
1
+ export { makeFakeAtomic, makeFakeAtomicWithStore } from "./server.js";
4
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fake/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fake/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
@@ -1,3 +1 @@
1
- export { makeFakeAtomic, defaultFakeFetch } from "./server.js";
2
- export { FakeAtomicStore } from "./store.js";
3
- export { routeFakeRequest } from "./router.js";
1
+ export { makeFakeAtomic, makeFakeAtomicWithStore } from "./server.js";
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/fake/router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA0C3D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,UAAU,CAmJZ"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/fake/router.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA6C3D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,UAAU,CAqMZ"}
@@ -10,17 +10,19 @@ const fail = (status, message) => ({
10
10
  body: { message, data: [] },
11
11
  });
12
12
  const RESTRICTED_DOMAINS = new Set(["wordpress.com", "wp.com", "automattic.com"]);
13
- function isRecord(value) {
14
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
15
- }
16
13
  function asStringMap(value) {
17
14
  const out = {};
18
- if (isRecord(value)) {
15
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
19
16
  for (const [k, v] of Object.entries(value))
20
17
  out[k] = String(v);
21
18
  }
22
19
  return out;
23
20
  }
21
+ /** The `data` form field of `POST /site-persist-data/{site}` — a bag of per-key
22
+ * set/delete operations, matching `FakeAtomicStore#applyPersistentData`. */
23
+ function isPersistDataOperations(value) {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25
+ }
24
26
  const READABLE_CLIENT_META_KEYS = new Set([
25
27
  "webhook_url",
26
28
  "client_ssh_default_from",
@@ -89,7 +91,9 @@ export function routeFakeRequest(store, method, path, body) {
89
91
  return ok({ status: "success" });
90
92
  // POST /site-persist-data/{site}
91
93
  if (method === "POST" && head === "site-persist-data") {
92
- const data = isRecord(body.data) ? store.applyPersistentData(seg[1] ?? "", body.data) : null;
94
+ const data = isPersistDataOperations(body.data)
95
+ ? store.applyPersistentData(seg[1] ?? "", body.data)
96
+ : null;
93
97
  if (!data)
94
98
  return fail(404, "Site not found.");
95
99
  return ok({
@@ -126,6 +130,46 @@ export function routeFakeRequest(store, method, path, body) {
126
130
  return ok([]);
127
131
  }
128
132
  }
133
+ // /crontab/{site}/add|list|update/{cron_id}|remove — matches the documented
134
+ // envelope exactly (docs/wpcloud/openapi.json): add/update return
135
+ // {cron_id}, list returns the array of entries verbatim, remove returns [].
136
+ if (head === "crontab") {
137
+ const id = seg[1] ?? "";
138
+ const action = seg[2];
139
+ if (method === "POST" && action === "add") {
140
+ const schedule = String(body.schedule ?? "");
141
+ const command = String(body.command ?? "");
142
+ if (!schedule || !command)
143
+ return fail(400, "Invalid or empty schedule or command.");
144
+ const cron = store.addCron(id, { schedule, command });
145
+ if (!cron)
146
+ return fail(404, "Site not found.");
147
+ return ok({ cron_id: cron.cronId });
148
+ }
149
+ if (method === "GET" && action === "list") {
150
+ if (!store.get(id))
151
+ return fail(404, "Site not found.");
152
+ return ok(store.listCrons(id).map((cron) => ({
153
+ cron_id: cron.cronId,
154
+ schedule: cron.schedule,
155
+ requested_schedule: cron.requestedSchedule,
156
+ command: cron.command,
157
+ })));
158
+ }
159
+ if (method === "POST" && action === "update") {
160
+ const cronId = Number(seg[3] ?? "");
161
+ const command = String(body.command ?? "");
162
+ if (!command)
163
+ return fail(400, "Invalid or empty command.");
164
+ return store.updateCron(id, cronId, command)
165
+ ? ok({ cron_id: cronId })
166
+ : fail(404, "Cron entry not found.");
167
+ }
168
+ if (method === "POST" && action === "remove") {
169
+ const cronId = Number(body.cron_id ?? "");
170
+ return store.removeCron(id, cronId) ? ok([]) : fail(404, "Cron entry not found.");
171
+ }
172
+ }
129
173
  // /edge-cache/{id}/{op}/{host} — lean: benign success so publish/serve never trips on it.
130
174
  // Real edge-cache behavior is covered by the gated `direct/` suites, not the fake.
131
175
  if (head === "edge-cache") {
@@ -183,6 +227,14 @@ export function routeFakeRequest(store, method, path, body) {
183
227
  return ok([]);
184
228
  }
185
229
  }
230
+ // POST /metrics/{type}/{key}[/summarize]
231
+ if (method === "POST" && head === "metrics") {
232
+ const type = decodeURIComponent(seg[1] ?? "");
233
+ const key = decodeURIComponent(seg[2] ?? "");
234
+ const summarize = seg[3] === "summarize";
235
+ store.recordMetricsRequest({ type, key, summarize, body });
236
+ return ok(store.metricsResponse(type, key));
237
+ }
186
238
  // Anything the control plane calls that we don't model: surface it loudly, don't silently pass.
187
239
  return fail(404, `fake_endpoint_not_implemented: ${method} /${path}`);
188
240
  }
@@ -4,6 +4,17 @@
4
4
  * `defaultFakeFetch` is a process-global instance for env-driven `WP_CLOUD_PROVIDER_MODE=fake`.
5
5
  */
6
6
  import type { WpCloudFakeFetch } from "../runtime.js";
7
+ import { FakeAtomicStore } from "./store.js";
8
+ /**
9
+ * Same fake as `makeFakeAtomic()`, but also hands back the underlying store
10
+ * so a test can assert on recorded state directly (house rule: assert the
11
+ * effect, not that a mock was called) — e.g. crontab registration, aliases,
12
+ * persistent data.
13
+ */
14
+ export declare function makeFakeAtomicWithStore(): {
15
+ fetch: WpCloudFakeFetch;
16
+ store: FakeAtomicStore;
17
+ };
7
18
  export declare function makeFakeAtomic(): WpCloudFakeFetch;
8
19
  /** Process-global fake for `WP_CLOUD_PROVIDER_MODE=fake` (no per-suite override installed). */
9
20
  export declare const defaultFakeFetch: WpCloudFakeFetch;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/fake/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA6CtD,wBAAgB,cAAc,IAAI,gBAAgB,CAiBjD;AAED,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB,EAAE,gBAAmC,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/fake/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA2C7C;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAkB7F;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAEjD;AAED,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB,EAAE,gBAAmC,CAAC"}
@@ -42,9 +42,15 @@ async function readBody(input, init) {
42
42
  }
43
43
  return raw ? parseNestedForm(raw) : {};
44
44
  }
45
- export function makeFakeAtomic() {
45
+ /**
46
+ * Same fake as `makeFakeAtomic()`, but also hands back the underlying store
47
+ * so a test can assert on recorded state directly (house rule: assert the
48
+ * effect, not that a mock was called) — e.g. crontab registration, aliases,
49
+ * persistent data.
50
+ */
51
+ export function makeFakeAtomicWithStore() {
46
52
  const store = new FakeAtomicStore();
47
- return async (input, init) => {
53
+ const fetch = async (input, init) => {
48
54
  const rawUrl = input instanceof Request ? input.url : String(input);
49
55
  const url = new URL(rawUrl);
50
56
  // path after /api/v1.0/, trailing slash trimmed; segments stay encoded (router decodes its own)
@@ -57,6 +63,10 @@ export function makeFakeAtomic() {
57
63
  headers: { "content-type": "application/json" },
58
64
  });
59
65
  };
66
+ return { fetch, store };
67
+ }
68
+ export function makeFakeAtomic() {
69
+ return makeFakeAtomicWithStore().fetch;
60
70
  }
61
71
  /** Process-global fake for `WP_CLOUD_PROVIDER_MODE=fake` (no per-suite override installed). */
62
72
  export const defaultFakeFetch = makeFakeAtomic();
@@ -1,28 +1,46 @@
1
1
  /**
2
2
  * In-memory state for the lean wp.cloud fake. Models only the lifecycle we captured real shapes for
3
- * (sites, jobs, aliases, meta) — see internal-docs/testing-system-plan.html §4. Edge-cache / SSL /
4
- * domain-claim are answered leniently by the router but not modelled here; their real behavior lives
5
- * in the gated `direct/` suites.
3
+ * (sites, jobs, aliases, meta, crontab) — see internal-docs/testing-system-plan.html §4. Edge-cache /
4
+ * SSL / domain-claim are answered leniently by the router but not modelled here; their real behavior
5
+ * lives in the gated `direct/` suites.
6
6
  */
7
+ export type FakeCronEntry = {
8
+ cronId: number;
9
+ schedule: string;
10
+ requestedSchedule: string;
11
+ command: string;
12
+ };
7
13
  export type FakeSite = {
8
14
  atomicSiteId: string;
9
15
  domainName: string;
10
16
  meta: Record<string, string>;
11
17
  persistentData: Record<string, string>;
18
+ persistentDataEnv: Record<string, boolean>;
12
19
  aliases: Set<string>;
20
+ crons: FakeCronEntry[];
13
21
  };
14
22
  export type FakeSiteSnapshot = {
15
23
  atomicSiteId: string;
16
24
  domainName: string;
17
25
  meta: Record<string, string>;
18
26
  persistentData: Record<string, string>;
27
+ persistentDataEnv: Record<string, boolean>;
19
28
  aliases: string[];
20
29
  };
30
+ export type FakeMetricsRequest = {
31
+ type: string;
32
+ key: string;
33
+ summarize: boolean;
34
+ body: Record<string, unknown>;
35
+ };
21
36
  export declare class FakeAtomicStore {
22
37
  private readonly sites;
23
38
  private readonly clientMeta;
39
+ private readonly metricsResponses;
40
+ private readonly metricRequestLog;
24
41
  private siteSeq;
25
42
  private jobSeq;
43
+ private cronSeq;
26
44
  datacenters(): string[];
27
45
  phpVersions(): string[];
28
46
  nextJobId(): number;
@@ -39,6 +57,7 @@ export declare class FakeAtomicStore {
39
57
  domainName: string;
40
58
  meta?: Record<string, string>;
41
59
  persistentData?: Record<string, string>;
60
+ persistentDataEnv?: Record<string, boolean>;
42
61
  aliases?: Iterable<string>;
43
62
  }): FakeSite;
44
63
  snapshot(): FakeSiteSnapshot[];
@@ -52,10 +71,21 @@ export declare class FakeAtomicStore {
52
71
  addAlias(id: string, domain: string): void;
53
72
  removeAlias(id: string, domain: string): void;
54
73
  aliases(id: string): string[];
74
+ addCron(id: string, input: {
75
+ schedule: string;
76
+ command: string;
77
+ }): FakeCronEntry | null;
78
+ listCrons(id: string): FakeCronEntry[];
79
+ updateCron(id: string, cronId: number, command: string): boolean;
80
+ removeCron(id: string, cronId: number): boolean;
55
81
  accessLogs(id: string): Array<Record<string, unknown>> | null;
56
82
  setClientMeta(key: string, value: string): void;
57
83
  getClientMeta(key: string): string | undefined;
58
84
  deleteClientMeta(key: string): boolean;
85
+ setMetricsResponse(type: string, key: string, response: Record<string, unknown>): void;
86
+ metricsResponse(type: string, key: string): Record<string, unknown>;
87
+ recordMetricsRequest(request: FakeMetricsRequest): void;
88
+ metricsRequests(): FakeMetricsRequest[];
59
89
  applyPersistentData(id: string, operations: Record<string, unknown>): Record<string, string> | null;
60
90
  persistentData(id: string): Record<string, string> | null;
61
91
  /** `GET /get-site/{id}/extra` shape — note `_data` is intentionally ABSENT (faithful to the real
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/fake/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/fake/store.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * In-memory state for the lean wp.cloud fake. Models only the lifecycle we captured real shapes for
3
- * (sites, jobs, aliases, meta) — see internal-docs/testing-system-plan.html §4. Edge-cache / SSL /
4
- * domain-claim are answered leniently by the router but not modelled here; their real behavior lives
5
- * in the gated `direct/` suites.
3
+ * (sites, jobs, aliases, meta, crontab) — see internal-docs/testing-system-plan.html §4. Edge-cache /
4
+ * SSL / domain-claim are answered leniently by the router but not modelled here; their real behavior
5
+ * lives in the gated `direct/` suites.
6
6
  */
7
7
  const DATACENTERS = ["ams", "bur", "dca", "dfw"];
8
8
  const PHP_VERSIONS = ["8.2", "8.3", "8.4", "8.5"];
@@ -12,8 +12,11 @@ function formFlag(value) {
12
12
  export class FakeAtomicStore {
13
13
  sites = new Map();
14
14
  clientMeta = new Map();
15
+ metricsResponses = new Map();
16
+ metricRequestLog = [];
15
17
  siteSeq = 151_000_000;
16
18
  jobSeq = 154_000_000;
19
+ cronSeq = 900_000;
17
20
  datacenters() {
18
21
  return [...DATACENTERS];
19
22
  }
@@ -30,7 +33,9 @@ export class FakeAtomicStore {
30
33
  domainName: input.domainName,
31
34
  meta: { ...input.meta },
32
35
  persistentData: {},
36
+ persistentDataEnv: {},
33
37
  aliases: new Set(),
38
+ crons: [],
34
39
  });
35
40
  return {
36
41
  job_id: this.nextJobId(),
@@ -48,7 +53,9 @@ export class FakeAtomicStore {
48
53
  domainName: input.domainName,
49
54
  meta: input.meta ? { ...input.meta } : {},
50
55
  persistentData: input.persistentData ? { ...input.persistentData } : {},
56
+ persistentDataEnv: input.persistentDataEnv ? { ...input.persistentDataEnv } : {},
51
57
  aliases: new Set(input.aliases ?? []),
58
+ crons: [],
52
59
  };
53
60
  this.sites.set(input.atomicSiteId, site);
54
61
  return site;
@@ -59,6 +66,7 @@ export class FakeAtomicStore {
59
66
  domainName: site.domainName,
60
67
  meta: { ...site.meta },
61
68
  persistentData: { ...site.persistentData },
69
+ persistentDataEnv: { ...site.persistentDataEnv },
62
70
  aliases: [...site.aliases],
63
71
  }));
64
72
  }
@@ -89,6 +97,37 @@ export class FakeAtomicStore {
89
97
  aliases(id) {
90
98
  return [...(this.sites.get(id)?.aliases ?? [])];
91
99
  }
100
+ addCron(id, input) {
101
+ const site = this.sites.get(id);
102
+ if (!site)
103
+ return null;
104
+ const cron = {
105
+ cronId: ++this.cronSeq,
106
+ schedule: input.schedule,
107
+ requestedSchedule: input.schedule,
108
+ command: input.command,
109
+ };
110
+ site.crons.push(cron);
111
+ return cron;
112
+ }
113
+ listCrons(id) {
114
+ return [...(this.sites.get(id)?.crons ?? [])];
115
+ }
116
+ updateCron(id, cronId, command) {
117
+ const cron = this.sites.get(id)?.crons.find((entry) => entry.cronId === cronId);
118
+ if (!cron)
119
+ return false;
120
+ cron.command = command;
121
+ return true;
122
+ }
123
+ removeCron(id, cronId) {
124
+ const site = this.sites.get(id);
125
+ if (!site)
126
+ return false;
127
+ const before = site.crons.length;
128
+ site.crons = site.crons.filter((entry) => entry.cronId !== cronId);
129
+ return site.crons.length < before;
130
+ }
92
131
  accessLogs(id) {
93
132
  const site = this.sites.get(id);
94
133
  if (!site)
@@ -114,6 +153,18 @@ export class FakeAtomicStore {
114
153
  deleteClientMeta(key) {
115
154
  return this.clientMeta.delete(key);
116
155
  }
156
+ setMetricsResponse(type, key, response) {
157
+ this.metricsResponses.set(`${type}:${key}`, response);
158
+ }
159
+ metricsResponse(type, key) {
160
+ return this.metricsResponses.get(`${type}:${key}`) ?? { periods: [] };
161
+ }
162
+ recordMetricsRequest(request) {
163
+ this.metricRequestLog.push(request);
164
+ }
165
+ metricsRequests() {
166
+ return [...this.metricRequestLog];
167
+ }
117
168
  applyPersistentData(id, operations) {
118
169
  const site = this.sites.get(id);
119
170
  if (!site)
@@ -124,9 +175,11 @@ export class FakeAtomicStore {
124
175
  const record = operation;
125
176
  if (formFlag(record.delete)) {
126
177
  delete site.persistentData[key];
178
+ delete site.persistentDataEnv[key];
127
179
  continue;
128
180
  }
129
181
  site.persistentData[key] = String(record.value ?? "");
182
+ site.persistentDataEnv[key] = formFlag(record.env);
130
183
  }
131
184
  return this.persistentData(id);
132
185
  }
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@spacefast/wpcloud-sdk",
3
- "version": "0.0.3",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "description": "Generated TypeScript client for the WP.Cloud API.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/spacefast/monorepo.git",
9
+ "directory": "packages/wpcloud-sdk"
10
+ },
6
11
  "type": "module",
7
12
  "publishConfig": {
8
13
  "access": "public"
@@ -30,8 +35,5 @@
30
35
  "import": "./dist/fake/index.js",
31
36
  "default": "./dist/fake/index.js"
32
37
  }
33
- },
34
- "dependencies": {
35
- "@hey-api/client-fetch": "0.13.1"
36
38
  }
37
39
  }