@mapled/cli 0.7.1 → 0.8.0
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/README.md +3 -1
- package/dist/commands.js +19 -8
- package/dist/doctor.d.ts +25 -4
- package/dist/doctor.js +112 -22
- package/dist/types.d.ts +1 -0
- package/dist/types.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,7 +175,7 @@ Mapled doctor — Dolphin Landing
|
|
|
175
175
|
✓ Environment MAPLED_KEY and MAPLED_WEBHOOK_SECRET in .env.local
|
|
176
176
|
✓ Secrets in git No env files or Mapled secrets are tracked.
|
|
177
177
|
✓ @mapled/next 0.8.0 (current)
|
|
178
|
-
✓ mapled CLI 0.
|
|
178
|
+
✓ mapled CLI 0.8.0 (current)
|
|
179
179
|
✓ Revalidation route app/api/mapled/revalidate/route.ts
|
|
180
180
|
⚠ Preview route app/api/mapled/preview/route.ts is missing — mount createPreviewHandler from "@mapled/next/server" so Preview from Mapled works.
|
|
181
181
|
✓ Site reads content Last read 3m ago
|
|
@@ -188,6 +188,8 @@ Mapled doctor — Dolphin Landing
|
|
|
188
188
|
|
|
189
189
|
The repository side: the link, the sign-in, the generated types, the schema pin, the site manifest (and whether it matches what was pushed), the integration hash — one line saying whether the pin and the manifest here describe the same integration Mapled holds, and if not, what moved (the schema, the bindings, or edits made in Mapled) and which command catches up — `MAPLED.md` (and whether it is what Mapled would render now), `MAPLED_KEY` and `MAPLED_WEBHOOK_SECRET` in your env files (names only — values are never read out), env files or Mapled secrets tracked by git, the installed `@mapled/next` and CLI versions, the revalidation and preview routes of a Next.js site. The Mapled side, from the same status the AI agent's `check_integration` tool reads: whether the site has read content with the delivery key, the publish webhook and its last delivery, the preview route on the deployed site, the bindings health.
|
|
190
190
|
|
|
191
|
+
A site rendered in the visitor's browser — a React single-page app (Vite and the like), plain HTML — is checked for what it has: the package line is `@mapled/react` (or `@mapled/client`), Environment wants `VITE_MAPLED_KEY` and no webhook secret, and the routes and the webhook read ○ «Not needed» — such a site shows a publish on its next load and trades Mapled's preview link on the page itself. doctor takes the framework from `mapled.json`, or works it out from `package.json`. Whatever the framework, a server key or a signing secret under a public prefix (`VITE_…`, `NEXT_PUBLIC_…`) is a ✗: it would ship to every visitor.
|
|
192
|
+
|
|
191
193
|
Exit code 1 when something is marked ✗; `--json` prints the checks for scripts and agents.
|
|
192
194
|
|
|
193
195
|
## Options
|
package/dist/commands.js
CHANGED
|
@@ -142,7 +142,8 @@ async function openSession(ctx, flags) {
|
|
|
142
142
|
export async function generate(ctx, flags) {
|
|
143
143
|
const { found, session } = await openSession(ctx, flags);
|
|
144
144
|
const schema = await session.get("/v1/agent/schema");
|
|
145
|
-
const
|
|
145
|
+
const framework = found.config.framework ?? (await dr.detectFramework(found.dir));
|
|
146
|
+
const generated = generateTypes(schema, { projectName: session.conn.projectName, sdk: dr.sdkOf(framework) });
|
|
146
147
|
const out = stringFlag(flags, "out") ?? found.config.types ?? DEFAULT_TYPES_PATH;
|
|
147
148
|
const file = path.resolve(found.dir, out);
|
|
148
149
|
await writeFile(file, generated.text);
|
|
@@ -582,7 +583,7 @@ export async function doctor(ctx, flags) {
|
|
|
582
583
|
checks.push(dr.checkAuth({ conn: conn ?? null, status, error: authError }));
|
|
583
584
|
const typesPath = found?.config.types ?? DEFAULT_TYPES_PATH;
|
|
584
585
|
const existing = await readFile(path.join(dir, typesPath), "utf8").catch(() => null);
|
|
585
|
-
checks.push(dr.checkTypes(existing, schema ? generateTypes(schema, { projectName: status?.project.name }) : null, typesPath));
|
|
586
|
+
checks.push(dr.checkTypes(existing, schema ? generateTypes(schema, { projectName: status?.project.name, sdk: dr.sdkOf(found?.config.framework ?? (await dr.detectFramework(dir))) }) : null, typesPath));
|
|
586
587
|
const pin = found ? await readPin(dir).catch(() => null) : null;
|
|
587
588
|
const livePin = schema && found ? pinSchema(schema, found.config.project) : null;
|
|
588
589
|
const pinChanges = pin && livePin ? diffSchema(pin.pin.collections, livePin.collections) : null;
|
|
@@ -600,16 +601,19 @@ export async function doctor(ctx, flags) {
|
|
|
600
601
|
checks.push(dr.checkManifestFile(localManifest ? { manifest: localManifest.manifest, problems: localManifest.problems } : null, remoteManifest));
|
|
601
602
|
checks.push(dr.checkIntegration({ pin: pin?.pin ?? null, manifest: localManifest?.manifest ?? null }, conn && status ? { integration: status.integration, manifest: remoteManifest } : null));
|
|
602
603
|
checks.push(checkMapledMd(await readFile(path.join(dir, MD_FILE), "utf8").catch(() => null), guide));
|
|
603
|
-
|
|
604
|
+
const framework = found?.config.framework ?? (await dr.detectFramework(dir));
|
|
605
|
+
// a site rendered in the browser has no webhook, no routes and no secret to check
|
|
606
|
+
const browser = dr.rendersInBrowser(framework);
|
|
607
|
+
const sdk = dr.sdkOf(framework);
|
|
608
|
+
checks.push(dr.checkEnv(await dr.envNames(dir), ctx.env, framework));
|
|
604
609
|
checks.push(dr.checkSecrets(await dr.gitFacts(dir)));
|
|
605
|
-
checks.push(dr.checkSdk(await dr.installedVersion(dir,
|
|
610
|
+
checks.push(dr.checkSdk(await dr.installedVersion(dir, sdk), status?.sdk[sdk], sdk));
|
|
606
611
|
checks.push(dr.checkCli(ctx.version, status?.sdk["@mapled/cli"]));
|
|
607
|
-
const framework = found?.config.framework ?? (await dr.detectFramework(dir));
|
|
608
612
|
checks.push(dr.checkRoute("revalidate", await dr.findRoute(dir, "revalidate"), framework));
|
|
609
613
|
checks.push(dr.checkRoute("preview", await dr.findRoute(dir, "preview"), framework));
|
|
610
614
|
if (status) {
|
|
611
615
|
checks.push(dr.checkReads(status.delivery.lastReadAt));
|
|
612
|
-
checks.push(dr.checkWebhook(status.webhook));
|
|
616
|
+
checks.push(dr.checkWebhook(status.webhook, { local: browser, remote: status.site }));
|
|
613
617
|
let origin = null;
|
|
614
618
|
try {
|
|
615
619
|
origin = status.webhook.url ? new URL(status.webhook.url).origin : null;
|
|
@@ -617,8 +621,15 @@ export async function doctor(ctx, flags) {
|
|
|
617
621
|
catch {
|
|
618
622
|
origin = null;
|
|
619
623
|
}
|
|
620
|
-
|
|
621
|
-
|
|
624
|
+
if (browser) {
|
|
625
|
+
// no route to probe: the page itself, where Mapled knows the site to live
|
|
626
|
+
origin = origin ?? status.site?.url ?? null;
|
|
627
|
+
checks.push(dr.checkPreviewOnSite(origin, status.previewPath, origin ? await dr.probeSite(origin, ctx.fetch) : null, true));
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const probe = origin ? await dr.probePreview(origin, status.previewPath, ctx.fetch) : null;
|
|
631
|
+
checks.push(dr.checkPreviewOnSite(origin, status.previewPath, probe));
|
|
632
|
+
}
|
|
622
633
|
checks.push(dr.checkBindings(status.bindings));
|
|
623
634
|
}
|
|
624
635
|
else {
|
package/dist/doctor.d.ts
CHANGED
|
@@ -51,6 +51,12 @@ export type IntegrationStatus = {
|
|
|
51
51
|
};
|
|
52
52
|
/** the integration hash (§21.4); missing from an API older than the hash */
|
|
53
53
|
integration?: IntegrationState;
|
|
54
|
+
/** what Mapled takes the site for, and where Preview opens it; missing from an older API */
|
|
55
|
+
site?: {
|
|
56
|
+
framework: string;
|
|
57
|
+
rendering: "server" | "browser";
|
|
58
|
+
url: string | null;
|
|
59
|
+
};
|
|
54
60
|
previewPath: string;
|
|
55
61
|
sdk: Record<string, string>;
|
|
56
62
|
};
|
|
@@ -87,6 +93,13 @@ export type PackageVersion = {
|
|
|
87
93
|
declared: string | null;
|
|
88
94
|
};
|
|
89
95
|
export declare function installedVersion(dir: string, pkg: string): Promise<PackageVersion>;
|
|
96
|
+
/** A site rendered in the visitor's browser — a React single-page app,
|
|
97
|
+
plain HTML — reads Mapled from the page: a publish shows on the next
|
|
98
|
+
load, so it has no webhook, no routes to mount and no secret. The two
|
|
99
|
+
keys are the ones Mapled knows by (lib/siteStack.ts of the API). */
|
|
100
|
+
export declare const rendersInBrowser: (framework: string | null | undefined) => boolean;
|
|
101
|
+
/** The first-party package a site of this framework reads through. */
|
|
102
|
+
export declare const sdkOf: (framework: string | null | undefined) => string;
|
|
90
103
|
export declare function detectFramework(dir: string): Promise<string | undefined>;
|
|
91
104
|
/** -1, 0 or 1 comparing dotted numeric versions; anything else compares by number. */
|
|
92
105
|
export declare function compareVersions(a: string, b: string): number;
|
|
@@ -101,6 +114,11 @@ export declare function findRoute(dir: string, kind: RouteKind): Promise<RouteFi
|
|
|
101
114
|
export declare function probePreview(origin: string, previewPath: string, f: Fetch): Promise<{
|
|
102
115
|
status: number | null;
|
|
103
116
|
}>;
|
|
117
|
+
/** A page of a site rendered in the browser: any answer means it is
|
|
118
|
+
deployed (a single-page app's host may say 404 and serve the app anyway). */
|
|
119
|
+
export declare function probeSite(origin: string, f: Fetch): Promise<{
|
|
120
|
+
status: number | null;
|
|
121
|
+
}>;
|
|
104
122
|
export declare function checkLink(found: {
|
|
105
123
|
path: string;
|
|
106
124
|
} | null, projectName: string | null): Check;
|
|
@@ -110,16 +128,19 @@ export declare function checkAuth(state: {
|
|
|
110
128
|
error: string | null;
|
|
111
129
|
}): Check;
|
|
112
130
|
export declare function checkTypes(existing: string | null, generated: Generated | null, typesPath: string): Check;
|
|
113
|
-
export declare function checkEnv(names: Map<string, string>, env: NodeJS.ProcessEnv): Check;
|
|
131
|
+
export declare function checkEnv(names: Map<string, string>, env: NodeJS.ProcessEnv, framework?: string | null): Check;
|
|
114
132
|
export declare function checkSecrets(facts: GitFacts): Check;
|
|
115
|
-
export declare function checkSdk(pkg: PackageVersion, latest: string | undefined): Check;
|
|
133
|
+
export declare function checkSdk(pkg: PackageVersion, latest: string | undefined, name?: string): Check;
|
|
116
134
|
export declare function checkCli(current: string, latest: string | undefined): Check;
|
|
117
135
|
export declare function checkRoute(kind: RouteKind, found: RouteFile | null, framework: string | undefined): Check;
|
|
118
136
|
export declare function checkReads(lastReadAt: string | null): Check;
|
|
119
|
-
export declare function checkWebhook(webhook: IntegrationStatus["webhook"]
|
|
137
|
+
export declare function checkWebhook(webhook: IntegrationStatus["webhook"], site?: {
|
|
138
|
+
local: boolean;
|
|
139
|
+
remote: IntegrationStatus["site"];
|
|
140
|
+
}): Check;
|
|
120
141
|
export declare function checkPreviewOnSite(origin: string | null, previewPath: string, probe: {
|
|
121
142
|
status: number | null;
|
|
122
|
-
} | null): Check;
|
|
143
|
+
} | null, browser?: boolean): Check;
|
|
123
144
|
export declare function checkBindings(bindings: IntegrationStatus["bindings"]): Check;
|
|
124
145
|
export declare function checkSchemaPin(pin: SchemaPin | null, liveHash: string | null, changes: SchemaChange[] | null): Check;
|
|
125
146
|
/** The pushed manifest as `GET /v1/agent/manifest` answers it. */
|
package/dist/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { integrationHash } from "./integrationHash.js";
|
|
@@ -102,26 +102,43 @@ export async function installedVersion(dir, pkg) {
|
|
|
102
102
|
}
|
|
103
103
|
return { installed, declared };
|
|
104
104
|
}
|
|
105
|
+
/** A site rendered in the visitor's browser — a React single-page app,
|
|
106
|
+
plain HTML — reads Mapled from the page: a publish shows on the next
|
|
107
|
+
load, so it has no webhook, no routes to mount and no secret. The two
|
|
108
|
+
keys are the ones Mapled knows by (lib/siteStack.ts of the API). */
|
|
109
|
+
export const rendersInBrowser = (framework) => framework === "react-spa" || framework === "plain-html";
|
|
110
|
+
/** The first-party package a site of this framework reads through. */
|
|
111
|
+
export const sdkOf = (framework) => !framework || framework === "nextjs" ? "@mapled/next" : framework === "react-spa" ? "@mapled/react" : "@mapled/client";
|
|
105
112
|
export async function detectFramework(dir) {
|
|
113
|
+
const hasIndexHtml = () => access(path.join(dir, "index.html")).then(() => true, () => false);
|
|
114
|
+
let deps;
|
|
106
115
|
try {
|
|
107
116
|
const raw = await readFile(path.join(dir, "package.json"), "utf8");
|
|
108
117
|
const p = JSON.parse(raw);
|
|
109
|
-
|
|
110
|
-
if (deps.next)
|
|
111
|
-
return "nextjs";
|
|
112
|
-
if (deps.astro)
|
|
113
|
-
return "astro";
|
|
114
|
-
if (deps.nuxt)
|
|
115
|
-
return "nuxt";
|
|
116
|
-
if (deps["@sveltejs/kit"])
|
|
117
|
-
return "sveltekit";
|
|
118
|
-
if (deps["@remix-run/react"] || deps["react-router"])
|
|
119
|
-
return "remix";
|
|
120
|
-
return undefined;
|
|
118
|
+
deps = { ...p.devDependencies, ...p.dependencies };
|
|
121
119
|
}
|
|
122
120
|
catch {
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
// no package.json at all: pages written by hand
|
|
122
|
+
return (await hasIndexHtml()) ? "plain-html" : undefined;
|
|
123
|
+
}
|
|
124
|
+
if (deps.next)
|
|
125
|
+
return "nextjs";
|
|
126
|
+
if (deps.astro)
|
|
127
|
+
return "astro";
|
|
128
|
+
if (deps.nuxt)
|
|
129
|
+
return "nuxt";
|
|
130
|
+
if (deps["@sveltejs/kit"])
|
|
131
|
+
return "sveltekit";
|
|
132
|
+
// react-router alone is a library a single-page app routes with; its framework mode brings @react-router/dev
|
|
133
|
+
if (deps["@remix-run/react"] || deps["@react-router/dev"])
|
|
134
|
+
return "remix";
|
|
135
|
+
if (deps.react && (deps.vite || deps["@vitejs/plugin-react"] || deps["@vitejs/plugin-react-swc"] || deps["react-scripts"] || deps["@mapled/react"])) {
|
|
136
|
+
return "react-spa";
|
|
137
|
+
}
|
|
138
|
+
if (!deps.react && !deps.vue && !deps.svelte && !deps["solid-js"] && !deps.express && !deps.fastify && (await hasIndexHtml())) {
|
|
139
|
+
return "plain-html";
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
125
142
|
}
|
|
126
143
|
/** -1, 0 or 1 comparing dotted numeric versions; anything else compares by number. */
|
|
127
144
|
export function compareVersions(a, b) {
|
|
@@ -186,6 +203,17 @@ export async function probePreview(origin, previewPath, f) {
|
|
|
186
203
|
}
|
|
187
204
|
}
|
|
188
205
|
/* ---- the checks ---- */
|
|
206
|
+
/** A page of a site rendered in the browser: any answer means it is
|
|
207
|
+
deployed (a single-page app's host may say 404 and serve the app anyway). */
|
|
208
|
+
export async function probeSite(origin, f) {
|
|
209
|
+
try {
|
|
210
|
+
const res = await f(`${origin}/`, { method: "GET", redirect: "manual", signal: AbortSignal.timeout(5000) });
|
|
211
|
+
return { status: res.status };
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return { status: null };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
189
217
|
export function checkLink(found, projectName) {
|
|
190
218
|
const label = "Project link";
|
|
191
219
|
if (!found)
|
|
@@ -218,9 +246,36 @@ export function checkTypes(existing, generated, typesPath) {
|
|
|
218
246
|
}
|
|
219
247
|
return { key: "types", label, status: "warning", detail: `${typesPath} is out of date — run \`mapled types generate\`.` };
|
|
220
248
|
}
|
|
221
|
-
|
|
249
|
+
/** Prefixes that put an env var into the browser's bundle. */
|
|
250
|
+
const PUBLIC_ENV = /^(VITE_|REACT_APP_|NEXT_PUBLIC_|NUXT_PUBLIC_|PUBLIC_|GATSBY_)/;
|
|
251
|
+
export function checkEnv(names, env, framework) {
|
|
222
252
|
const label = "Environment";
|
|
223
253
|
const where = (name) => names.get(name) ?? (env[name] ? "the environment" : null);
|
|
254
|
+
// whatever the framework: a server key or a signing secret under a public prefix ships to every visitor
|
|
255
|
+
const exposed = [...names.keys()].filter((n) => PUBLIC_ENV.test(n) && /MAPLED/.test(n) && /(SERVER_KEY|SECRET)/.test(n));
|
|
256
|
+
if (exposed.length > 0) {
|
|
257
|
+
return {
|
|
258
|
+
key: "env",
|
|
259
|
+
label,
|
|
260
|
+
status: "failed",
|
|
261
|
+
detail: `${exposed.join(", ")} in ${names.get(exposed[0])} is bundled into the browser. Only the delivery key may be: rename it and rotate the value in Integrations.`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (framework === "plain-html") {
|
|
265
|
+
return { key: "env", label, status: "skipped", detail: "No env files: the delivery key sits in the page — the one key that is safe there." };
|
|
266
|
+
}
|
|
267
|
+
if (framework === "react-spa") {
|
|
268
|
+
const name = ["VITE_MAPLED_KEY", "REACT_APP_MAPLED_KEY"].find((n) => where(n));
|
|
269
|
+
if (!name) {
|
|
270
|
+
return {
|
|
271
|
+
key: "env",
|
|
272
|
+
label,
|
|
273
|
+
status: "failed",
|
|
274
|
+
detail: "VITE_MAPLED_KEY isn't set. Put the delivery key (Integrations → Your site) in .env — it is the one key that is safe in a browser.",
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return { key: "env", label, status: "passed", detail: `${name} in ${where(name)} — no webhook secret needed: the site reads in the browser` };
|
|
278
|
+
}
|
|
224
279
|
const key = where("MAPLED_KEY");
|
|
225
280
|
const secret = where("MAPLED_WEBHOOK_SECRET");
|
|
226
281
|
if (!key) {
|
|
@@ -268,10 +323,10 @@ export function checkSecrets(facts) {
|
|
|
268
323
|
}
|
|
269
324
|
return { key: "secrets", label, status: "passed", detail: "No env files or Mapled secrets are tracked." };
|
|
270
325
|
}
|
|
271
|
-
export function checkSdk(pkg, latest) {
|
|
272
|
-
const label =
|
|
326
|
+
export function checkSdk(pkg, latest, name = "@mapled/next") {
|
|
327
|
+
const label = name;
|
|
273
328
|
if (!pkg.installed && !pkg.declared) {
|
|
274
|
-
return { key: "sdk", label, status: "failed", detail:
|
|
329
|
+
return { key: "sdk", label, status: "failed", detail: `Not installed. Run \`npm install ${name}\`.` };
|
|
275
330
|
}
|
|
276
331
|
if (!pkg.installed) {
|
|
277
332
|
return { key: "sdk", label, status: "warning", detail: `${pkg.declared} is declared but not installed — run npm install.` };
|
|
@@ -283,7 +338,7 @@ export function checkSdk(pkg, latest) {
|
|
|
283
338
|
key: "sdk",
|
|
284
339
|
label,
|
|
285
340
|
status: "warning",
|
|
286
|
-
detail: `${pkg.installed} installed, ${latest} available — npm install @
|
|
341
|
+
detail: `${pkg.installed} installed, ${latest} available — npm install ${name}@latest.`,
|
|
287
342
|
};
|
|
288
343
|
}
|
|
289
344
|
return { key: "sdk", label, status: "passed", detail: `${pkg.installed} (current)` };
|
|
@@ -299,6 +354,16 @@ export function checkRoute(kind, found, framework) {
|
|
|
299
354
|
const key = `route_${kind}`;
|
|
300
355
|
const label = kind === "revalidate" ? "Revalidation route" : "Preview route";
|
|
301
356
|
const handler = ROUTE_HANDLER[kind];
|
|
357
|
+
if (rendersInBrowser(framework)) {
|
|
358
|
+
return {
|
|
359
|
+
key,
|
|
360
|
+
label,
|
|
361
|
+
status: "skipped",
|
|
362
|
+
detail: kind === "revalidate"
|
|
363
|
+
? "Not needed: the site reads in the browser, so a publish shows on the next load."
|
|
364
|
+
: "Not needed: the client trades Mapled's preview link on the page itself.",
|
|
365
|
+
};
|
|
366
|
+
}
|
|
302
367
|
if (framework && framework !== "nextjs") {
|
|
303
368
|
return { key, label, status: "skipped", detail: `No ${kind} route check for ${framework} sites yet.` };
|
|
304
369
|
}
|
|
@@ -334,8 +399,20 @@ export function checkReads(lastReadAt) {
|
|
|
334
399
|
}
|
|
335
400
|
return { key: "reads", label, status: "passed", detail: `Last read ${ago(lastReadAt)}` };
|
|
336
401
|
}
|
|
337
|
-
export function checkWebhook(webhook) {
|
|
402
|
+
export function checkWebhook(webhook, site) {
|
|
338
403
|
const label = "Publish webhook";
|
|
404
|
+
if (!webhook.url && site?.local) {
|
|
405
|
+
// the repository renders in the browser; does Mapled know?
|
|
406
|
+
if (site.remote && site.remote.rendering !== "browser") {
|
|
407
|
+
return {
|
|
408
|
+
key: "webhook",
|
|
409
|
+
label,
|
|
410
|
+
status: "warning",
|
|
411
|
+
detail: `Mapled takes this site for ${site.remote.framework} and will ask for a webhook: run \`mapled scan --write\` and \`mapled bindings push\` so it knows the site renders in the browser.`,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return { key: "webhook", label, status: "skipped", detail: "Not needed: the site reads in the browser, so a publish shows on the next load." };
|
|
415
|
+
}
|
|
339
416
|
if (!webhook.url) {
|
|
340
417
|
return {
|
|
341
418
|
key: "webhook",
|
|
@@ -377,8 +454,21 @@ export function checkWebhook(webhook) {
|
|
|
377
454
|
detail: `The last delivery to ${host} ${reason}. Check the route and MAPLED_WEBHOOK_SECRET, then send a test ping.`,
|
|
378
455
|
};
|
|
379
456
|
}
|
|
380
|
-
export function checkPreviewOnSite(origin, previewPath, probe) {
|
|
457
|
+
export function checkPreviewOnSite(origin, previewPath, probe, browser = false) {
|
|
381
458
|
const label = "Preview on the site";
|
|
459
|
+
if (browser) {
|
|
460
|
+
if (!origin || !probe) {
|
|
461
|
+
return {
|
|
462
|
+
key: "preview_site",
|
|
463
|
+
label,
|
|
464
|
+
status: "warning",
|
|
465
|
+
detail: "Mapled doesn't know where the site is deployed — set Production domain in Project settings, or ask your agent (set_site_url).",
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return probe.status === null
|
|
469
|
+
? { key: "preview_site", label, status: "warning", detail: `${origin} is unreachable from here.` }
|
|
470
|
+
: { key: "preview_site", label, status: "passed", detail: `Opens at ${origin} — the client trades the link in the browser` };
|
|
471
|
+
}
|
|
382
472
|
if (!origin || !probe) {
|
|
383
473
|
return { key: "preview_site", label, status: "skipped", detail: "Needs the webhook URL to know where the site lives." };
|
|
384
474
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare function typeName(key: string, kind: "collection" | "single"): st
|
|
|
21
21
|
export declare function typeNames(collections: SchemaCollection[]): Map<string, string>;
|
|
22
22
|
export declare function generateTypes(schema: Schema, opts?: {
|
|
23
23
|
projectName?: string;
|
|
24
|
+
sdk?: string;
|
|
24
25
|
}): Generated;
|
|
25
26
|
/** Splits a generated file into its header and body, with the hash the
|
|
26
27
|
header names when it has one. */
|
package/dist/types.js
CHANGED
|
@@ -177,14 +177,18 @@ export function typeNames(collections) {
|
|
|
177
177
|
return out;
|
|
178
178
|
}
|
|
179
179
|
export function generateTypes(schema, opts = {}) {
|
|
180
|
+
// the package the site reads through: "@mapled/next" unless the framework says otherwise
|
|
181
|
+
const sdk = opts.sdk ?? "@mapled/next";
|
|
182
|
+
// a React app's assetUrl comes from the client its hooks are built on
|
|
183
|
+
const assets = sdk === "@mapled/react" ? "@mapled/client" : sdk;
|
|
180
184
|
const hash = schemaHash(schema);
|
|
181
185
|
const names = typeNames(schema.collections);
|
|
182
186
|
const blocks = [];
|
|
183
187
|
blocks.push([
|
|
184
|
-
|
|
188
|
+
`// Types of the published content this site reads through ${sdk}.`,
|
|
185
189
|
"// Sensitive fields never reach the site and are left out.",
|
|
186
190
|
"",
|
|
187
|
-
|
|
191
|
+
`/** An asset id — pass it to assetUrl(id, { width }) from "${assets}". */`,
|
|
188
192
|
"export type MapledAssetId = string;",
|
|
189
193
|
'/** The id of a record in another collection — attach it with { expand: ["field"] }. */',
|
|
190
194
|
"export type MapledRecordId = string;",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapled/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Mapled CLI — sign in, link a project, generate TypeScript types for its content, create forms and check the site's integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mapled.io",
|