siteplane 0.2.0-next.1 → 0.2.0-rc.1
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 +13 -4
- package/dist/bin/siteplane.js +44 -15
- package/dist/route.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,18 +8,21 @@ content as fallback in your code, a Draft Mode preview for the editor, Booking a
|
|
|
8
8
|
Run this in the website's folder, or tell your coding agent "Install Siteplane":
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npx siteplane@
|
|
11
|
+
npx siteplane@latest init
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Version 0.2 is on the `next` tag until it becomes `latest`.
|
|
15
|
-
|
|
16
14
|
`init` opens the approval page in the browser. There you sign up or sign in, create the website in Siteplane or pick
|
|
17
15
|
an existing one, and click Connect. If you created the website in Siteplane first, its prompt names the site:
|
|
18
16
|
`npx siteplane@<version> init --site <site ID>`.
|
|
19
17
|
|
|
20
18
|
`init` then writes `siteplane.config.json`, installs this package, wraps `next.config` in `withSiteplane`, adds the
|
|
21
19
|
route `app/api/siteplane/[...siteplane]/route.ts` and `<SiteplaneBridge />`, and prints the task for the agent.
|
|
22
|
-
Siteplane needs no environment variables or secrets in your project
|
|
20
|
+
Siteplane needs no environment variables or secrets in your project, and it never deploys: deploy the way the project
|
|
21
|
+
always does.
|
|
22
|
+
|
|
23
|
+
The approval creates an agent connection for this one website. It may read the task, sync fields and check the
|
|
24
|
+
status; it cannot publish. It is listed under Settings → Connected agents in Siteplane, where it can be revoked, and
|
|
25
|
+
expires after 90 days without use.
|
|
23
26
|
|
|
24
27
|
## Field API
|
|
25
28
|
|
|
@@ -77,3 +80,9 @@ npx siteplane booking test
|
|
|
77
80
|
approval. The agent connection is stored in the user config directory (`$SITEPLANE_CONFIG_DIR`,
|
|
78
81
|
`$XDG_CONFIG_HOME/siteplane`, `%APPDATA%\siteplane` or `~/.config/siteplane`), never in the project; `--no-browser`
|
|
79
82
|
skips opening the browser.
|
|
83
|
+
|
|
84
|
+
## More
|
|
85
|
+
|
|
86
|
+
- Instructions for coding agents: https://siteplane.io/agents and https://siteplane.io/llms.txt
|
|
87
|
+
- Removing Siteplane: Settings → Export in Siteplane downloads every field with its published value and gives your
|
|
88
|
+
coding agent a task that writes the values into the code and removes this package.
|
package/dist/bin/siteplane.js
CHANGED
|
@@ -1349,6 +1349,9 @@ async function checkProject(root) {
|
|
|
1349
1349
|
}
|
|
1350
1350
|
}
|
|
1351
1351
|
const scan = scanFields(await collectSourceFiles2(root));
|
|
1352
|
+
if (scan.fields.length === 0 && scan.issues.length === 0) {
|
|
1353
|
+
warning("no_fields", "There are no fields yet. Wire the content the task names (npx siteplane task shows it again), then run check again.");
|
|
1354
|
+
}
|
|
1352
1355
|
return { project, fields: scan.fields, issues: [...issues, ...scan.issues] };
|
|
1353
1356
|
}
|
|
1354
1357
|
function reachesSiteplaneRoutes(source) {
|
|
@@ -1448,26 +1451,51 @@ import ts6 from "typescript";
|
|
|
1448
1451
|
var IMPORT = 'import { withSiteplane } from "siteplane/next";';
|
|
1449
1452
|
function wrapNextConfig(source) {
|
|
1450
1453
|
const file = ts6.createSourceFile("next.config.ts", source, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TS);
|
|
1451
|
-
const
|
|
1452
|
-
if (
|
|
1454
|
+
const edits = wrapDefaultExport(file, source);
|
|
1455
|
+
if (edits === "already")
|
|
1456
|
+
return { status: "already" };
|
|
1457
|
+
if (!edits) {
|
|
1453
1458
|
const commonJs = /\bmodule\.exports\s*=|\bexports\.default\s*=/u.test(source);
|
|
1454
1459
|
return { status: "manual", reason: commonJs ? "commonjs" : "unrecognized" };
|
|
1455
1460
|
}
|
|
1456
|
-
const expression = exported.expression;
|
|
1457
|
-
if (ts6.isCallExpression(expression) && expression.expression.getText(file) === "withSiteplane") {
|
|
1458
|
-
return { status: "already" };
|
|
1459
|
-
}
|
|
1460
|
-
const start = expression.getStart(file);
|
|
1461
|
-
let next = `${source.slice(0, start)}withSiteplane(${source.slice(start, expression.end)})${source.slice(expression.end)}`;
|
|
1462
1461
|
if (!source.includes(IMPORT)) {
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
${next}`;
|
|
1462
|
+
const last = file.statements.filter(ts6.isImportDeclaration).at(-1);
|
|
1463
|
+
edits.push(last ? { start: last.end, end: last.end, text: `
|
|
1464
|
+
${IMPORT}` } : { start: 0, end: 0, text: `${IMPORT}
|
|
1465
|
+
` });
|
|
1468
1466
|
}
|
|
1467
|
+
const next = edits.sort((a, b) => b.start - a.start || b.end - a.end).reduce((text, edit) => `${text.slice(0, edit.start)}${edit.text}${text.slice(edit.end)}`, source);
|
|
1469
1468
|
return { status: "wrapped", source: next };
|
|
1470
1469
|
}
|
|
1470
|
+
function wrapDefaultExport(file, source) {
|
|
1471
|
+
const exported = file.statements.find((statement) => ts6.isExportAssignment(statement) && !statement.isExportEquals);
|
|
1472
|
+
if (exported) {
|
|
1473
|
+
const expression = exported.expression;
|
|
1474
|
+
if (ts6.isCallExpression(expression) && expression.expression.getText(file) === "withSiteplane")
|
|
1475
|
+
return "already";
|
|
1476
|
+
const start = expression.getStart(file);
|
|
1477
|
+
return [
|
|
1478
|
+
{ start, end: start, text: "withSiteplane(" },
|
|
1479
|
+
{ start: expression.end, end: expression.end, text: ")" }
|
|
1480
|
+
];
|
|
1481
|
+
}
|
|
1482
|
+
const declaration = file.statements.find((statement) => ts6.isFunctionDeclaration(statement) && Boolean(ts6.getModifiers(statement)?.some((modifier) => modifier.kind === ts6.SyntaxKind.DefaultKeyword)));
|
|
1483
|
+
if (!declaration?.body)
|
|
1484
|
+
return null;
|
|
1485
|
+
const modifiers = ts6.getModifiers(declaration) ?? [];
|
|
1486
|
+
const exportStart = modifiers[0]?.getStart(file) ?? declaration.getStart(file);
|
|
1487
|
+
const defaultEnd = modifiers.find((modifier) => modifier.kind === ts6.SyntaxKind.DefaultKeyword)?.end ?? exportStart;
|
|
1488
|
+
const functionStart = defaultEnd + (/^\s*/u.exec(source.slice(defaultEnd))?.[0].length ?? 0);
|
|
1489
|
+
return declaration.name ? [
|
|
1490
|
+
{ start: exportStart, end: functionStart, text: "" },
|
|
1491
|
+
{ start: declaration.end, end: declaration.end, text: `
|
|
1492
|
+
|
|
1493
|
+
export default withSiteplane(${declaration.name.text});` }
|
|
1494
|
+
] : [
|
|
1495
|
+
{ start: exportStart, end: functionStart, text: "export default withSiteplane(" },
|
|
1496
|
+
{ start: declaration.end, end: declaration.end, text: ");" }
|
|
1497
|
+
];
|
|
1498
|
+
}
|
|
1471
1499
|
function newNextConfig(typescript) {
|
|
1472
1500
|
return typescript ? `import type { NextConfig } from "next";
|
|
1473
1501
|
${IMPORT}
|
|
@@ -1488,7 +1516,7 @@ export default withSiteplane(nextConfig);
|
|
|
1488
1516
|
function formatTask(task) {
|
|
1489
1517
|
const wish = clean(task.editableBrief ?? "").trim();
|
|
1490
1518
|
const rules = [
|
|
1491
|
-
"
|
|
1519
|
+
"Tell the user this task and the fields you picked in a few lines, then go on without waiting for an answer; ask only when something blocks you.",
|
|
1492
1520
|
"Wire content values only: texts, links and images. Keep components, layout, styles, animations and accessibility as they are.",
|
|
1493
1521
|
'Give every field one value call and put {...siteplane.attrs("<id>")} on each element that shows it.',
|
|
1494
1522
|
"Field IDs are lowercase with at least two dot-separated parts (page.section.item) and stay the same when the text changes. Repeated content gets its own IDs (services.haircut.price); one ID on several pages is one shared field.",
|
|
@@ -1501,6 +1529,7 @@ function formatTask(task) {
|
|
|
1501
1529
|
];
|
|
1502
1530
|
return [
|
|
1503
1531
|
`Siteplane task for ${JSON.stringify(normalizeSiteName(task.site.name))} (site ${task.site.id})`,
|
|
1532
|
+
"Carry out this task now: wire the fields, then check, sync, deploy as usual and confirm with npx siteplane status --url.",
|
|
1504
1533
|
"",
|
|
1505
1534
|
"What should be editable (the owner's wish in Siteplane):",
|
|
1506
1535
|
...wish ? wish.split("\n").map((line) => `> ${line.trim()}`) : ["Agent decides: pick the content the owner will want to change."],
|
|
@@ -2046,7 +2075,7 @@ async function writeStore(connections, options) {
|
|
|
2046
2075
|
}
|
|
2047
2076
|
|
|
2048
2077
|
// src/version.ts
|
|
2049
|
-
var PACKAGE_VERSION = "0.2.0-
|
|
2078
|
+
var PACKAGE_VERSION = "0.2.0-rc.1";
|
|
2050
2079
|
|
|
2051
2080
|
// src/bin/run-siteplane.ts
|
|
2052
2081
|
var usage = [
|
package/dist/route.js
CHANGED
|
@@ -114,7 +114,7 @@ async function readBoundedText(request) {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
// src/version.ts
|
|
117
|
-
var PACKAGE_VERSION = "0.2.0-
|
|
117
|
+
var PACKAGE_VERSION = "0.2.0-rc.1";
|
|
118
118
|
|
|
119
119
|
// src/route.ts
|
|
120
120
|
var embeddable = { httpOnly: true, secure: true, sameSite: "none", partitioned: true, path: "/" };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "siteplane",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.0-
|
|
4
|
+
"version": "0.2.0-rc.1",
|
|
5
5
|
"description": "Siteplane for Next.js websites: editable content with code fallbacks, Draft Mode preview, Booking and the siteplane CLI.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|