@nebutra/landing-builder 0.1.1 → 0.1.2
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/.nebutra/debug/content-store.jsonl +5 -5
- package/.nebutra/debug/event-log.jsonl +1 -1
- package/.nebutra/debug/landing-builder.jsonl +1 -1
- package/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-test.log +5 -6
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/{chunk-E4I5POU4.js → chunk-HQNDYIXK.js} +10 -12
- package/dist/chunk-HQNDYIXK.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +7 -7
- package/src/index.ts +11 -13
- package/dist/chunk-E4I5POU4.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
{"at":"2026-05-
|
|
2
|
-
{"at":"2026-05-
|
|
3
|
-
{"at":"2026-05-
|
|
4
|
-
{"at":"2026-05-
|
|
5
|
-
{"at":"2026-05-
|
|
1
|
+
{"at":"2026-05-19T08:15:26.801Z","type":"reindex","tenantId":"tenant_a","files":0}
|
|
2
|
+
{"at":"2026-05-19T08:15:26.871Z","type":"write","tenantId":"tenant_a","path":"company/landing/index.html"}
|
|
3
|
+
{"at":"2026-05-19T08:15:26.902Z","type":"write","tenantId":"tenant_a","path":"company/landing/theme.css"}
|
|
4
|
+
{"at":"2026-05-19T08:15:26.909Z","type":"write","tenantId":"tenant_a","path":"company/landing/deploy.json"}
|
|
5
|
+
{"at":"2026-05-19T08:15:27.113Z","type":"reindex","tenantId":"","files":0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"traceId":"
|
|
1
|
+
{"at":"2026-05-19T08:15:26.951Z","traceId":"landing_builder_loop_mpccyhfq","kind":"content_write","affected":["company/landing/index.html","company/landing/theme.css","company/landing/deploy.json"],"parent":null,"snapshot":{"company/landing/index.html":"<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>Loop</title>\n <link rel=\"stylesheet\" href=\"./theme.css\" />\n</head>\n<body data-brand-id=\"loop\">\n <main class=\"page\">\n <section class=\"hero\">\n <p class=\"eyebrow\">electric / terminal-native</p>\n <h1>Loop</h1>\n <p class=\"lead\">AI debugging for indie devs</p>\n <a class=\"cta\" href=\"#waitlist\">Join the waitlist</a>\n </section>\n <section class=\"proof\">\n <h2>Why it matters</h2>\n <p>technical, direct, warm execution, grounded in one shared brand context.</p>\n </section>\n <section id=\"waitlist\" class=\"waitlist\">\n <h2>Join the waitlist</h2>\n <form>\n <input aria-label=\"Email\" placeholder=\"you@example.com\" />\n <button type=\"button\">Join</button>\n </form>\n </section>\n </main>\n</body>\n</html>\n<!-- #00D4FF #F3C14B -->\n","company/landing/theme.css":":root {\n --brand-primary: #00D4FF;\n --brand-background: #111318;\n --brand-accent: #F3C14B;\n --brand-heading: Geist Mono;\n --brand-body: Geist Sans;\n}\n\n* { box-sizing: border-box; }\nbody { margin: 0; background: var(--brand-background); color: color-mix(in oklab, var(--brand-primary) 80%, black); font-family: var(--brand-body), system-ui, sans-serif; }\n.page { min-height: 100vh; }\n.hero { min-height: 82vh; display: grid; align-content: center; gap: 24px; padding: clamp(32px, 8vw, 96px); }\n.eyebrow { color: var(--brand-accent); font-family: var(--brand-heading), monospace; text-transform: uppercase; letter-spacing: 0; }\nh1 { max-width: 11ch; margin: 0; font-family: var(--brand-heading), system-ui; font-size: clamp(56px, 12vw, 132px); line-height: .92; letter-spacing: 0; }\n.lead { max-width: 760px; font-size: clamp(20px, 3vw, 34px); line-height: 1.2; }\n.cta, button { width: fit-content; border: 0; border-radius: 8px; background: var(--brand-primary); color: var(--brand-background); padding: 14px 20px; font: inherit; text-decoration: none; }\n.proof, .waitlist { padding: 48px clamp(32px, 8vw, 96px); border-top: 1px solid color-mix(in oklab, var(--brand-primary) 20%, transparent); }\ninput { min-width: min(360px, 100%); border: 1px solid color-mix(in oklab, var(--brand-primary) 35%, transparent); border-radius: 8px; padding: 14px 16px; font: inherit; }\nform { display: flex; flex-wrap: wrap; gap: 12px; }\n","company/landing/deploy.json":"{\n \"mode\": \"one_pager\",\n \"brandId\": \"loop\",\n \"entrypoint\": \"company/landing/index.html\",\n \"targets\": [\n \"managed-hosting\",\n \"edge-pages\",\n \"static-hosting\",\n \"git-pages\"\n ],\n \"requiresExplicitDeployConsent\": true\n}\n"},"id":"5b8c9ee05fdcc8ad9c0602d11b1f24bc253b4ef8e9cc986882f070f057ca19c5","tenantId":"tenant_a","summary":"content_write touched 3 path(s)","objectHashes":["282b113584ef5dca1d7d5a8ab0acda009a083ae1d338148e3b5f72d7e2326f0d","efd0c917e24ab7adf2d36be8e9800535dcc7e1c3a8c433826a1a13bb996fe967","2515ff8fb770aeb2584f10e096f9e430af9d9e69022d76f2c96c7feccbfe19eb"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"at":"2026-05-
|
|
1
|
+
{"at":"2026-05-19T08:15:26.970Z","type":"run","tenantId":"tenant_a","brandId":"loop","eventId":"5b8c9ee05fdcc8ad9c0602d11b1f24bc253b4ef8e9cc986882f070f057ca19c5"}
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @nebutra/landing-builder@0.1.
|
|
2
|
+
> @nebutra/landing-builder@0.1.2 build /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/landing-builder
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[32mESM[39m [1mdist/cli.js [22m[32m1.78 KB[39m
|
|
13
13
|
[32mESM[39m [1mdist/index.js [22m[32m262.00 B[39m
|
|
14
|
-
[32mESM[39m [1mdist/chunk-
|
|
14
|
+
[32mESM[39m [1mdist/chunk-HQNDYIXK.js [22m[32m8.25 KB[39m
|
|
15
15
|
[32mESM[39m [1mdist/cli.js.map [22m[32m3.24 KB[39m
|
|
16
16
|
[32mESM[39m [1mdist/index.js.map [22m[32m71.00 B[39m
|
|
17
|
-
[32mESM[39m [1mdist/chunk-
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
17
|
+
[32mESM[39m [1mdist/chunk-HQNDYIXK.js.map [22m[32m15.31 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 60ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 10057ms
|
|
21
21
|
[32mDTS[39m [1mdist/cli.d.ts [22m[32m13.00 B[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.64 KB[39m
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
> @nebutra/landing-builder@0.1.
|
|
2
|
+
> @nebutra/landing-builder@0.1.2 test /home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/landing-builder
|
|
3
3
|
> vitest run
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.4 [39m[90m/home/runner/work/Nebutra-Sailor/Nebutra-Sailor/packages/ai/landing-builder[39m
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[32m✓[39m src/index.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 287[2mms[22m[39m
|
|
8
|
+
[32m✓[39m src/index.test.ts [2m([22m[2m4 tests[22m[2m)[22m[33m 718[2mms[22m[39m
|
|
9
|
+
[33m[2m✓[22m[39m runs one_pager and writes site files plus a deploy handoff [33m 469[2mms[22m[39m
|
|
11
10
|
|
|
12
11
|
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
13
12
|
[2m Tests [22m [1m[32m4 passed[39m[22m[90m (4)[39m
|
|
14
|
-
[2m Start at [22m
|
|
15
|
-
[2m Duration [22m
|
|
13
|
+
[2m Start at [22m 08:15:23
|
|
14
|
+
[2m Duration [22m 3.51s[2m (transform 1.30s, setup 0ms, import 1.73s, tests 718ms, environment 0ms)[22m
|
|
16
15
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @nebutra/landing-builder
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Publish registry package metadata under the MIT license.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies []:
|
|
10
|
+
- @nebutra/capability-kit@0.2.1
|
|
11
|
+
- @nebutra/content-store@0.1.1
|
|
12
|
+
- @nebutra/errors@0.1.1
|
|
13
|
+
- @nebutra/event-log@0.1.1
|
|
14
|
+
- @nebutra/generation-context@0.1.1
|
|
15
|
+
- @nebutra/play-loader@0.1.1
|
|
16
|
+
|
|
3
17
|
## 0.1.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { mkdir } from "fs/promises";
|
|
3
3
|
import { dirname, join } from "path";
|
|
4
|
+
import { requireCapabilityTenant } from "@nebutra/capability-kit";
|
|
4
5
|
import { appendCapabilityDebug, readCapabilityDebug } from "@nebutra/capability-kit/debug";
|
|
5
6
|
import { ContentStore } from "@nebutra/content-store";
|
|
6
7
|
import { CapabilityError } from "@nebutra/errors";
|
|
7
8
|
import { EventLog } from "@nebutra/event-log";
|
|
8
9
|
import { assetId } from "@nebutra/generation-context";
|
|
9
10
|
var PLAY_NAME = "one_pager";
|
|
10
|
-
function requireTenant(explicit, fallback) {
|
|
11
|
-
const tenantId = explicit ?? fallback;
|
|
12
|
-
if (!tenantId?.trim()) {
|
|
13
|
-
throw new CapabilityError("landing-builder", "Landing Builder requires tenant context", {
|
|
14
|
-
suggestion: "Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.",
|
|
15
|
-
statusCode: 400
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
return tenantId;
|
|
19
|
-
}
|
|
20
11
|
function colorByRole(brand, role, fallback) {
|
|
21
12
|
return brand.palette.find((color) => color.role === role)?.hex ?? fallback;
|
|
22
13
|
}
|
|
@@ -133,7 +124,14 @@ var LandingBuilder = class _LandingBuilder {
|
|
|
133
124
|
return new _LandingBuilder({ ...options, tenantId, root, contentStore, eventLog });
|
|
134
125
|
}
|
|
135
126
|
async runOnePager(input) {
|
|
136
|
-
const tenantId =
|
|
127
|
+
const tenantId = requireCapabilityTenant({
|
|
128
|
+
explicit: input.tenantId ?? input.brand.tenantId,
|
|
129
|
+
fallback: this.#tenantId,
|
|
130
|
+
onMissing: () => new CapabilityError("landing-builder", "Landing Builder requires tenant context", {
|
|
131
|
+
suggestion: "Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.",
|
|
132
|
+
statusCode: 400
|
|
133
|
+
})
|
|
134
|
+
});
|
|
137
135
|
const html = renderOnePagerHtml(input);
|
|
138
136
|
const css = renderThemeCss(input.brand);
|
|
139
137
|
const manifest = deployManifest(input);
|
|
@@ -206,4 +204,4 @@ export {
|
|
|
206
204
|
LandingBuilder,
|
|
207
205
|
readLandingBuilderDebug
|
|
208
206
|
};
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
207
|
+
//# sourceMappingURL=chunk-HQNDYIXK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { requireCapabilityTenant } from \"@nebutra/capability-kit\";\nimport { appendCapabilityDebug, readCapabilityDebug } from \"@nebutra/capability-kit/debug\";\nimport { ContentStore } from \"@nebutra/content-store\";\nimport { CapabilityError } from \"@nebutra/errors\";\nimport { EventLog } from \"@nebutra/event-log\";\nimport { assetId, type BrandContext } from \"@nebutra/generation-context\";\n\nexport type LandingMode = \"one_pager\" | \"marketing_site\" | \"webapp_mvp\" | \"site_edit\";\n\nexport interface OnePagerInput {\n readonly tenantId?: string;\n readonly brand: BrandContext;\n readonly productDesc: string;\n readonly ctaText: string;\n readonly mode?: LandingMode;\n}\n\nexport interface SiteFile {\n readonly path: string;\n readonly mime: string;\n readonly bytes: number;\n}\n\nexport interface SitePreview {\n readonly kind: \"static-content-store\";\n readonly entrypoint: string;\n}\n\nexport interface DeployHandoff {\n readonly status: \"handoff\";\n readonly targets: readonly string[];\n readonly manifestPath: string;\n readonly suggestion: string;\n}\n\nexport interface SitePackage {\n readonly tenantId: string;\n readonly play: \"one_pager\";\n readonly brandId: string;\n readonly mode: \"one_pager\";\n readonly files: readonly SiteFile[];\n readonly preview: SitePreview;\n readonly deploy: DeployHandoff;\n readonly eventId: string;\n}\n\nexport interface TailwindTheme {\n readonly cssVariables: string;\n readonly primary: string;\n readonly background: string;\n readonly accent: string;\n}\n\nexport interface LandingBuilderDoctorReport {\n readonly capability: \"landing-builder\";\n readonly ok: boolean;\n readonly checkedAt: string;\n readonly plays: readonly string[];\n readonly modes: readonly LandingMode[];\n readonly deployAdapters: readonly { readonly provider: string; readonly ok: boolean }[];\n readonly suggestion?: string;\n}\n\nexport interface LandingBuilderOptions {\n readonly tenantId?: string;\n readonly root?: string;\n readonly debugRoot?: string;\n readonly contentStore?: ContentStore;\n readonly eventLog?: EventLog;\n}\n\nconst PLAY_NAME = \"one_pager\" as const;\n\nfunction colorByRole(brand: BrandContext, role: string, fallback: string): string {\n return brand.palette.find((color) => color.role === role)?.hex ?? fallback;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n\nexport function renderTailwindTheme(brand: BrandContext): TailwindTheme {\n const primary = colorByRole(brand, \"primary\", \"#111827\");\n const background = colorByRole(brand, \"background\", \"#FFFFFF\");\n const accent = colorByRole(brand, \"accent\", primary);\n return {\n primary,\n background,\n accent,\n cssVariables: [\n \":root {\",\n ` --brand-primary: ${primary};`,\n ` --brand-background: ${background};`,\n ` --brand-accent: ${accent};`,\n ` --brand-heading: ${brand.typography.heading};`,\n ` --brand-body: ${brand.typography.body};`,\n \"}\",\n \"\",\n ].join(\"\\n\"),\n };\n}\n\nexport function renderOnePagerHtml(input: OnePagerInput): string {\n const { brand } = input;\n const theme = renderTailwindTheme(brand);\n const keywords = brand.visualStyle.keywords.join(\" / \");\n return [\n \"<!doctype html>\",\n '<html lang=\"en\">',\n \"<head>\",\n ' <meta charset=\"utf-8\" />',\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n ` <title>${escapeHtml(brand.name)}</title>`,\n ' <link rel=\"stylesheet\" href=\"./theme.css\" />',\n \"</head>\",\n `<body data-brand-id=\"${escapeHtml(brand.brandId)}\">`,\n ' <main class=\"page\">',\n ' <section class=\"hero\">',\n ` <p class=\"eyebrow\">${escapeHtml(keywords)}</p>`,\n ` <h1>${escapeHtml(brand.name)}</h1>`,\n ` <p class=\"lead\">${escapeHtml(input.productDesc)}</p>`,\n ` <a class=\"cta\" href=\"#waitlist\">${escapeHtml(input.ctaText)}</a>`,\n \" </section>\",\n ' <section class=\"proof\">',\n \" <h2>Why it matters</h2>\",\n ` <p>${escapeHtml(brand.toneKeywords.join(\", \"))} execution, grounded in one shared brand context.</p>`,\n \" </section>\",\n ' <section id=\"waitlist\" class=\"waitlist\">',\n \" <h2>Join the waitlist</h2>\",\n \" <form>\",\n ' <input aria-label=\"Email\" placeholder=\"you@example.com\" />',\n ' <button type=\"button\">Join</button>',\n \" </form>\",\n \" </section>\",\n \" </main>\",\n \"</body>\",\n \"</html>\",\n `<!-- ${theme.primary} ${theme.accent} -->`,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction renderThemeCss(brand: BrandContext): string {\n const theme = renderTailwindTheme(brand);\n return [\n theme.cssVariables,\n \"* { box-sizing: border-box; }\",\n \"body { margin: 0; background: var(--brand-background); color: color-mix(in oklab, var(--brand-primary) 80%, black); font-family: var(--brand-body), system-ui, sans-serif; }\",\n \".page { min-height: 100vh; }\",\n \".hero { min-height: 82vh; display: grid; align-content: center; gap: 24px; padding: clamp(32px, 8vw, 96px); }\",\n \".eyebrow { color: var(--brand-accent); font-family: var(--brand-heading), monospace; text-transform: uppercase; letter-spacing: 0; }\",\n \"h1 { max-width: 11ch; margin: 0; font-family: var(--brand-heading), system-ui; font-size: clamp(56px, 12vw, 132px); line-height: .92; letter-spacing: 0; }\",\n \".lead { max-width: 760px; font-size: clamp(20px, 3vw, 34px); line-height: 1.2; }\",\n \".cta, button { width: fit-content; border: 0; border-radius: 8px; background: var(--brand-primary); color: var(--brand-background); padding: 14px 20px; font: inherit; text-decoration: none; }\",\n \".proof, .waitlist { padding: 48px clamp(32px, 8vw, 96px); border-top: 1px solid color-mix(in oklab, var(--brand-primary) 20%, transparent); }\",\n \"input { min-width: min(360px, 100%); border: 1px solid color-mix(in oklab, var(--brand-primary) 35%, transparent); border-radius: 8px; padding: 14px 16px; font: inherit; }\",\n \"form { display: flex; flex-wrap: wrap; gap: 12px; }\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction deployManifest(input: OnePagerInput): string {\n return `${JSON.stringify(\n {\n mode: \"one_pager\",\n brandId: input.brand.brandId,\n entrypoint: \"company/landing/index.html\",\n targets: [\"managed-hosting\", \"edge-pages\", \"static-hosting\", \"git-pages\"],\n requiresExplicitDeployConsent: true,\n },\n null,\n 2,\n )}\\n`;\n}\n\nexport class LandingBuilder {\n readonly #tenantId: string | undefined;\n readonly #debugRoot: string;\n readonly #contentStore: ContentStore;\n readonly #eventLog: EventLog;\n\n private constructor(\n options: LandingBuilderOptions & { contentStore: ContentStore; eventLog: EventLog },\n ) {\n this.#tenantId = options.tenantId;\n this.#debugRoot = options.debugRoot ?? process.cwd();\n this.#contentStore = options.contentStore;\n this.#eventLog = options.eventLog;\n }\n\n static async open(\n root = \".nebutra/landing-builder\",\n options: Omit<LandingBuilderOptions, \"root\" | \"contentStore\" | \"eventLog\"> = {},\n ): Promise<LandingBuilder> {\n const tenantId = options.tenantId ?? \"local\";\n await mkdir(root, { recursive: true });\n const contentStore = await ContentStore.open(join(root, \"content\"), { tenantId });\n const eventLog = await EventLog.open(join(root, \"event-log\"), { tenantId });\n return new LandingBuilder({ ...options, tenantId, root, contentStore, eventLog });\n }\n\n async runOnePager(input: OnePagerInput): Promise<SitePackage> {\n const tenantId = requireCapabilityTenant({\n explicit: input.tenantId ?? input.brand.tenantId,\n fallback: this.#tenantId,\n onMissing: () =>\n new CapabilityError(\"landing-builder\", \"Landing Builder requires tenant context\", {\n suggestion:\n \"Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.\",\n statusCode: 400,\n }),\n });\n const html = renderOnePagerHtml(input);\n const css = renderThemeCss(input.brand);\n const manifest = deployManifest(input);\n const files = [\n { path: \"company/landing/index.html\", mime: \"text/html\", content: html },\n { path: \"company/landing/theme.css\", mime: \"text/css\", content: css },\n { path: \"company/landing/deploy.json\", mime: \"application/json\", content: manifest },\n ] as const;\n for (const file of files) await this.#contentStore.write(file.path, file.content);\n const eventId = await this.#eventLog.commit({\n traceId: assetId(\"landing_builder\", input.brand.brandId),\n kind: \"content_write\",\n affected: files.map((file) => file.path),\n parent: null,\n snapshot: Object.fromEntries(files.map((file) => [file.path, file.content])),\n });\n const result: SitePackage = {\n tenantId,\n play: PLAY_NAME,\n brandId: input.brand.brandId,\n mode: \"one_pager\",\n files: files.map((file) => ({\n path: file.path,\n mime: file.mime,\n bytes: file.content.length,\n })),\n preview: { kind: \"static-content-store\", entrypoint: \"company/landing/index.html\" },\n deploy: {\n status: \"handoff\",\n targets: [\"managed-hosting\", \"edge-pages\", \"static-hosting\", \"git-pages\"],\n manifestPath: \"company/landing/deploy.json\",\n suggestion: \"Connect a deploy integration and call the explicit deploy Play after review.\",\n },\n eventId,\n };\n await this.#debug({ type: \"run\", tenantId, brandId: input.brand.brandId, eventId });\n return result;\n }\n\n async doctor(): Promise<LandingBuilderDoctorReport> {\n return {\n capability: \"landing-builder\",\n ok: true,\n checkedAt: new Date().toISOString(),\n plays: [PLAY_NAME],\n modes: [\"one_pager\", \"marketing_site\", \"webapp_mvp\", \"site_edit\"],\n deployAdapters: [\n { provider: \"static-content-store\", ok: true },\n { provider: \"integration-vault\", ok: false },\n ],\n suggestion:\n \"Static generation is ready; real deploy requires integration-vault deploy adapters.\",\n };\n }\n\n async close(): Promise<void> {\n await this.#contentStore.close();\n }\n\n async #debug(entry: Record<string, unknown>): Promise<void> {\n await mkdir(dirname(join(this.#debugRoot, \".nebutra\", \"debug\", \"landing-builder.jsonl\")), {\n recursive: true,\n });\n await appendCapabilityDebug(\"landing-builder\", entry, { root: this.#debugRoot });\n }\n}\n\nexport async function readLandingBuilderDebug(\n root = process.cwd(),\n limit = 20,\n): Promise<unknown[]> {\n return readCapabilityDebug(\"landing-builder\", { root, limit });\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/landing-builder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "BrandContext-first landing page Play package for generating deployable launch sites",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
".": "./src/index.ts"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@nebutra/capability-kit": "0.2.
|
|
33
|
-
"@nebutra/content-store": "0.1.
|
|
34
|
-
"@nebutra/errors": "0.1.
|
|
35
|
-
"@nebutra/event-log": "0.1.
|
|
36
|
-
"@nebutra/generation-context": "0.1.
|
|
37
|
-
"@nebutra/play-loader": "0.1.
|
|
32
|
+
"@nebutra/capability-kit": "0.2.1",
|
|
33
|
+
"@nebutra/content-store": "0.1.1",
|
|
34
|
+
"@nebutra/errors": "0.1.1",
|
|
35
|
+
"@nebutra/event-log": "0.1.1",
|
|
36
|
+
"@nebutra/generation-context": "0.1.1",
|
|
37
|
+
"@nebutra/play-loader": "0.1.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.19.15",
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
+
import { requireCapabilityTenant } from "@nebutra/capability-kit";
|
|
3
4
|
import { appendCapabilityDebug, readCapabilityDebug } from "@nebutra/capability-kit/debug";
|
|
4
5
|
import { ContentStore } from "@nebutra/content-store";
|
|
5
6
|
import { CapabilityError } from "@nebutra/errors";
|
|
@@ -72,18 +73,6 @@ export interface LandingBuilderOptions {
|
|
|
72
73
|
|
|
73
74
|
const PLAY_NAME = "one_pager" as const;
|
|
74
75
|
|
|
75
|
-
function requireTenant(explicit: string | undefined, fallback: string | undefined): string {
|
|
76
|
-
const tenantId = explicit ?? fallback;
|
|
77
|
-
if (!tenantId?.trim()) {
|
|
78
|
-
throw new CapabilityError("landing-builder", "Landing Builder requires tenant context", {
|
|
79
|
-
suggestion:
|
|
80
|
-
"Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.",
|
|
81
|
-
statusCode: 400,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return tenantId;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
76
|
function colorByRole(brand: BrandContext, role: string, fallback: string): string {
|
|
88
77
|
return brand.palette.find((color) => color.role === role)?.hex ?? fallback;
|
|
89
78
|
}
|
|
@@ -217,7 +206,16 @@ export class LandingBuilder {
|
|
|
217
206
|
}
|
|
218
207
|
|
|
219
208
|
async runOnePager(input: OnePagerInput): Promise<SitePackage> {
|
|
220
|
-
const tenantId =
|
|
209
|
+
const tenantId = requireCapabilityTenant({
|
|
210
|
+
explicit: input.tenantId ?? input.brand.tenantId,
|
|
211
|
+
fallback: this.#tenantId,
|
|
212
|
+
onMissing: () =>
|
|
213
|
+
new CapabilityError("landing-builder", "Landing Builder requires tenant context", {
|
|
214
|
+
suggestion:
|
|
215
|
+
"Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.",
|
|
216
|
+
statusCode: 400,
|
|
217
|
+
}),
|
|
218
|
+
});
|
|
221
219
|
const html = renderOnePagerHtml(input);
|
|
222
220
|
const css = renderThemeCss(input.brand);
|
|
223
221
|
const manifest = deployManifest(input);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { appendCapabilityDebug, readCapabilityDebug } from \"@nebutra/capability-kit/debug\";\nimport { ContentStore } from \"@nebutra/content-store\";\nimport { CapabilityError } from \"@nebutra/errors\";\nimport { EventLog } from \"@nebutra/event-log\";\nimport { assetId, type BrandContext } from \"@nebutra/generation-context\";\n\nexport type LandingMode = \"one_pager\" | \"marketing_site\" | \"webapp_mvp\" | \"site_edit\";\n\nexport interface OnePagerInput {\n readonly tenantId?: string;\n readonly brand: BrandContext;\n readonly productDesc: string;\n readonly ctaText: string;\n readonly mode?: LandingMode;\n}\n\nexport interface SiteFile {\n readonly path: string;\n readonly mime: string;\n readonly bytes: number;\n}\n\nexport interface SitePreview {\n readonly kind: \"static-content-store\";\n readonly entrypoint: string;\n}\n\nexport interface DeployHandoff {\n readonly status: \"handoff\";\n readonly targets: readonly string[];\n readonly manifestPath: string;\n readonly suggestion: string;\n}\n\nexport interface SitePackage {\n readonly tenantId: string;\n readonly play: \"one_pager\";\n readonly brandId: string;\n readonly mode: \"one_pager\";\n readonly files: readonly SiteFile[];\n readonly preview: SitePreview;\n readonly deploy: DeployHandoff;\n readonly eventId: string;\n}\n\nexport interface TailwindTheme {\n readonly cssVariables: string;\n readonly primary: string;\n readonly background: string;\n readonly accent: string;\n}\n\nexport interface LandingBuilderDoctorReport {\n readonly capability: \"landing-builder\";\n readonly ok: boolean;\n readonly checkedAt: string;\n readonly plays: readonly string[];\n readonly modes: readonly LandingMode[];\n readonly deployAdapters: readonly { readonly provider: string; readonly ok: boolean }[];\n readonly suggestion?: string;\n}\n\nexport interface LandingBuilderOptions {\n readonly tenantId?: string;\n readonly root?: string;\n readonly debugRoot?: string;\n readonly contentStore?: ContentStore;\n readonly eventLog?: EventLog;\n}\n\nconst PLAY_NAME = \"one_pager\" as const;\n\nfunction requireTenant(explicit: string | undefined, fallback: string | undefined): string {\n const tenantId = explicit ?? fallback;\n if (!tenantId?.trim()) {\n throw new CapabilityError(\"landing-builder\", \"Landing Builder requires tenant context\", {\n suggestion:\n \"Pass tenantId on the input, BrandContext, or construct LandingBuilder with tenantId.\",\n statusCode: 400,\n });\n }\n return tenantId;\n}\n\nfunction colorByRole(brand: BrandContext, role: string, fallback: string): string {\n return brand.palette.find((color) => color.role === role)?.hex ?? fallback;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n}\n\nexport function renderTailwindTheme(brand: BrandContext): TailwindTheme {\n const primary = colorByRole(brand, \"primary\", \"#111827\");\n const background = colorByRole(brand, \"background\", \"#FFFFFF\");\n const accent = colorByRole(brand, \"accent\", primary);\n return {\n primary,\n background,\n accent,\n cssVariables: [\n \":root {\",\n ` --brand-primary: ${primary};`,\n ` --brand-background: ${background};`,\n ` --brand-accent: ${accent};`,\n ` --brand-heading: ${brand.typography.heading};`,\n ` --brand-body: ${brand.typography.body};`,\n \"}\",\n \"\",\n ].join(\"\\n\"),\n };\n}\n\nexport function renderOnePagerHtml(input: OnePagerInput): string {\n const { brand } = input;\n const theme = renderTailwindTheme(brand);\n const keywords = brand.visualStyle.keywords.join(\" / \");\n return [\n \"<!doctype html>\",\n '<html lang=\"en\">',\n \"<head>\",\n ' <meta charset=\"utf-8\" />',\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n ` <title>${escapeHtml(brand.name)}</title>`,\n ' <link rel=\"stylesheet\" href=\"./theme.css\" />',\n \"</head>\",\n `<body data-brand-id=\"${escapeHtml(brand.brandId)}\">`,\n ' <main class=\"page\">',\n ' <section class=\"hero\">',\n ` <p class=\"eyebrow\">${escapeHtml(keywords)}</p>`,\n ` <h1>${escapeHtml(brand.name)}</h1>`,\n ` <p class=\"lead\">${escapeHtml(input.productDesc)}</p>`,\n ` <a class=\"cta\" href=\"#waitlist\">${escapeHtml(input.ctaText)}</a>`,\n \" </section>\",\n ' <section class=\"proof\">',\n \" <h2>Why it matters</h2>\",\n ` <p>${escapeHtml(brand.toneKeywords.join(\", \"))} execution, grounded in one shared brand context.</p>`,\n \" </section>\",\n ' <section id=\"waitlist\" class=\"waitlist\">',\n \" <h2>Join the waitlist</h2>\",\n \" <form>\",\n ' <input aria-label=\"Email\" placeholder=\"you@example.com\" />',\n ' <button type=\"button\">Join</button>',\n \" </form>\",\n \" </section>\",\n \" </main>\",\n \"</body>\",\n \"</html>\",\n `<!-- ${theme.primary} ${theme.accent} -->`,\n \"\",\n ].join(\"\\n\");\n}\n\nfunction renderThemeCss(brand: BrandContext): string {\n const theme = renderTailwindTheme(brand);\n return [\n theme.cssVariables,\n \"* { box-sizing: border-box; }\",\n \"body { margin: 0; background: var(--brand-background); color: color-mix(in oklab, var(--brand-primary) 80%, black); font-family: var(--brand-body), system-ui, sans-serif; }\",\n \".page { min-height: 100vh; }\",\n \".hero { min-height: 82vh; display: grid; align-content: center; gap: 24px; padding: clamp(32px, 8vw, 96px); }\",\n \".eyebrow { color: var(--brand-accent); font-family: var(--brand-heading), monospace; text-transform: uppercase; letter-spacing: 0; }\",\n \"h1 { max-width: 11ch; margin: 0; font-family: var(--brand-heading), system-ui; font-size: clamp(56px, 12vw, 132px); line-height: .92; letter-spacing: 0; }\",\n \".lead { max-width: 760px; font-size: clamp(20px, 3vw, 34px); line-height: 1.2; }\",\n \".cta, button { width: fit-content; border: 0; border-radius: 8px; background: var(--brand-primary); color: var(--brand-background); padding: 14px 20px; font: inherit; text-decoration: none; }\",\n \".proof, .waitlist { padding: 48px clamp(32px, 8vw, 96px); border-top: 1px solid color-mix(in oklab, var(--brand-primary) 20%, transparent); }\",\n \"input { min-width: min(360px, 100%); border: 1px solid color-mix(in oklab, var(--brand-primary) 35%, transparent); border-radius: 8px; padding: 14px 16px; font: inherit; }\",\n \"form { display: flex; flex-wrap: wrap; gap: 12px; }\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction deployManifest(input: OnePagerInput): string {\n return `${JSON.stringify(\n {\n mode: \"one_pager\",\n brandId: input.brand.brandId,\n entrypoint: \"company/landing/index.html\",\n targets: [\"managed-hosting\", \"edge-pages\", \"static-hosting\", \"git-pages\"],\n requiresExplicitDeployConsent: true,\n },\n null,\n 2,\n )}\\n`;\n}\n\nexport class LandingBuilder {\n readonly #tenantId: string | undefined;\n readonly #debugRoot: string;\n readonly #contentStore: ContentStore;\n readonly #eventLog: EventLog;\n\n private constructor(\n options: LandingBuilderOptions & { contentStore: ContentStore; eventLog: EventLog },\n ) {\n this.#tenantId = options.tenantId;\n this.#debugRoot = options.debugRoot ?? process.cwd();\n this.#contentStore = options.contentStore;\n this.#eventLog = options.eventLog;\n }\n\n static async open(\n root = \".nebutra/landing-builder\",\n options: Omit<LandingBuilderOptions, \"root\" | \"contentStore\" | \"eventLog\"> = {},\n ): Promise<LandingBuilder> {\n const tenantId = options.tenantId ?? \"local\";\n await mkdir(root, { recursive: true });\n const contentStore = await ContentStore.open(join(root, \"content\"), { tenantId });\n const eventLog = await EventLog.open(join(root, \"event-log\"), { tenantId });\n return new LandingBuilder({ ...options, tenantId, root, contentStore, eventLog });\n }\n\n async runOnePager(input: OnePagerInput): Promise<SitePackage> {\n const tenantId = requireTenant(input.tenantId ?? input.brand.tenantId, this.#tenantId);\n const html = renderOnePagerHtml(input);\n const css = renderThemeCss(input.brand);\n const manifest = deployManifest(input);\n const files = [\n { path: \"company/landing/index.html\", mime: \"text/html\", content: html },\n { path: \"company/landing/theme.css\", mime: \"text/css\", content: css },\n { path: \"company/landing/deploy.json\", mime: \"application/json\", content: manifest },\n ] as const;\n for (const file of files) await this.#contentStore.write(file.path, file.content);\n const eventId = await this.#eventLog.commit({\n traceId: assetId(\"landing_builder\", input.brand.brandId),\n kind: \"content_write\",\n affected: files.map((file) => file.path),\n parent: null,\n snapshot: Object.fromEntries(files.map((file) => [file.path, file.content])),\n });\n const result: SitePackage = {\n tenantId,\n play: PLAY_NAME,\n brandId: input.brand.brandId,\n mode: \"one_pager\",\n files: files.map((file) => ({\n path: file.path,\n mime: file.mime,\n bytes: file.content.length,\n })),\n preview: { kind: \"static-content-store\", entrypoint: \"company/landing/index.html\" },\n deploy: {\n status: \"handoff\",\n targets: [\"managed-hosting\", \"edge-pages\", \"static-hosting\", \"git-pages\"],\n manifestPath: \"company/landing/deploy.json\",\n suggestion: \"Connect a deploy integration and call the explicit deploy Play after review.\",\n },\n eventId,\n };\n await this.#debug({ type: \"run\", tenantId, brandId: input.brand.brandId, eventId });\n return result;\n }\n\n async doctor(): Promise<LandingBuilderDoctorReport> {\n return {\n capability: \"landing-builder\",\n ok: true,\n checkedAt: new Date().toISOString(),\n plays: [PLAY_NAME],\n modes: [\"one_pager\", \"marketing_site\", \"webapp_mvp\", \"site_edit\"],\n deployAdapters: [\n { provider: \"static-content-store\", ok: true },\n { provider: \"integration-vault\", ok: false },\n ],\n suggestion:\n \"Static generation is ready; real deploy requires integration-vault deploy adapters.\",\n };\n }\n\n async close(): Promise<void> {\n await this.#contentStore.close();\n }\n\n async #debug(entry: Record<string, unknown>): Promise<void> {\n await mkdir(dirname(join(this.#debugRoot, \".nebutra\", \"debug\", \"landing-builder.jsonl\")), {\n recursive: true,\n });\n await appendCapabilityDebug(\"landing-builder\", entry, { root: this.#debugRoot });\n }\n}\n\nexport async function readLandingBuilderDebug(\n root = process.cwd(),\n limit = 20,\n): Promise<unknown[]> {\n return readCapabilityDebug(\"landing-builder\", { root, limit });\n}\n"],"mappings":"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","names":[]}
|