@setzkasten-cms/astro-admin 1.4.0 → 1.4.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.
Files changed (153) hide show
  1. package/dist/api-routes/_auth-guard.d.ts +47 -0
  2. package/dist/api-routes/_auth-guard.js +18 -0
  3. package/dist/api-routes/_commit-trailers.d.ts +8 -0
  4. package/dist/api-routes/_commit-trailers.js +8 -0
  5. package/dist/api-routes/_feature-gate.d.ts +23 -0
  6. package/dist/api-routes/_feature-gate.js +7 -0
  7. package/dist/api-routes/_github-cache.d.ts +4 -0
  8. package/dist/api-routes/_github-cache.js +8 -0
  9. package/dist/api-routes/_github-token.d.ts +27 -0
  10. package/dist/api-routes/_github-token.js +8 -0
  11. package/dist/api-routes/_license-tier.d.ts +22 -0
  12. package/dist/api-routes/_license-tier.js +6 -0
  13. package/dist/api-routes/_pages-meta-store.d.ts +32 -0
  14. package/dist/api-routes/_pages-meta-store.js +9 -0
  15. package/dist/api-routes/_role-resolver.d.ts +15 -0
  16. package/dist/api-routes/_role-resolver.js +13 -0
  17. package/dist/api-routes/_session-cookie.d.ts +18 -0
  18. package/dist/api-routes/_session-cookie.js +6 -0
  19. package/dist/api-routes/_storage-config.d.ts +60 -0
  20. package/dist/api-routes/_storage-config.js +10 -0
  21. package/dist/api-routes/_vercel-origin.d.ts +16 -0
  22. package/dist/api-routes/_vercel-origin.js +6 -0
  23. package/dist/api-routes/_webhook-dispatcher.d.ts +13 -0
  24. package/dist/api-routes/_webhook-dispatcher.js +97 -0
  25. package/dist/api-routes/_webhook-signing.d.ts +11 -0
  26. package/dist/api-routes/_webhook-signing.js +6 -0
  27. package/dist/api-routes/_webhook-status-store.d.ts +19 -0
  28. package/dist/api-routes/_webhook-status-store.js +10 -0
  29. package/dist/api-routes/_website-resolver.d.ts +49 -0
  30. package/dist/api-routes/_website-resolver.js +14 -0
  31. package/dist/api-routes/_websites-store.d.ts +30 -0
  32. package/dist/api-routes/_websites-store.js +11 -0
  33. package/dist/api-routes/asset-proxy.d.ts +12 -0
  34. package/dist/api-routes/asset-proxy.js +67 -0
  35. package/dist/api-routes/auth-callback.d.ts +9 -0
  36. package/dist/api-routes/auth-callback.js +68 -0
  37. package/dist/api-routes/auth-login.d.ts +11 -0
  38. package/dist/api-routes/auth-login.js +27 -0
  39. package/dist/api-routes/auth-logout.d.ts +10 -0
  40. package/dist/api-routes/auth-logout.js +13 -0
  41. package/dist/api-routes/auth-session.d.ts +9 -0
  42. package/dist/api-routes/auth-session.js +31 -0
  43. package/dist/api-routes/auth-setzkasten-login.d.ts +18 -0
  44. package/dist/api-routes/auth-setzkasten-login.js +74 -0
  45. package/dist/api-routes/catalog-add.d.ts +14 -0
  46. package/dist/api-routes/catalog-add.js +153 -0
  47. package/dist/api-routes/catalog-export.d.ts +13 -0
  48. package/dist/api-routes/catalog-export.js +71 -0
  49. package/dist/api-routes/catalog-helpers.d.ts +41 -0
  50. package/dist/api-routes/catalog-helpers.js +11 -0
  51. package/dist/api-routes/catalog-list.d.ts +11 -0
  52. package/dist/api-routes/catalog-list.js +12 -0
  53. package/dist/api-routes/config.d.ts +12 -0
  54. package/dist/api-routes/config.js +43 -0
  55. package/dist/api-routes/deploy-hook.d.ts +14 -0
  56. package/dist/api-routes/deploy-hook.js +52 -0
  57. package/dist/api-routes/editors.d.ts +29 -0
  58. package/dist/api-routes/editors.js +18 -0
  59. package/dist/api-routes/github-proxy.d.ts +12 -0
  60. package/dist/api-routes/github-proxy.js +82 -0
  61. package/dist/api-routes/global-config.d.ts +20 -0
  62. package/dist/api-routes/global-config.js +19 -0
  63. package/dist/api-routes/history-rollback.d.ts +22 -0
  64. package/dist/api-routes/history-rollback.js +111 -0
  65. package/dist/api-routes/history-version.d.ts +11 -0
  66. package/dist/api-routes/history-version.js +57 -0
  67. package/dist/api-routes/history.d.ts +13 -0
  68. package/dist/api-routes/history.js +85 -0
  69. package/dist/api-routes/icons-local.d.ts +28 -0
  70. package/dist/api-routes/icons-local.js +115 -0
  71. package/dist/api-routes/init-add-section.d.ts +23 -0
  72. package/dist/api-routes/init-add-section.js +396 -0
  73. package/dist/api-routes/init-apply.d.ts +11 -0
  74. package/dist/api-routes/init-apply.js +266 -0
  75. package/dist/api-routes/init-migrate.d.ts +16 -0
  76. package/dist/api-routes/init-migrate.js +205 -0
  77. package/dist/api-routes/init-scan-page.d.ts +39 -0
  78. package/dist/api-routes/init-scan-page.js +260 -0
  79. package/dist/api-routes/init-scan.d.ts +11 -0
  80. package/dist/api-routes/init-scan.js +128 -0
  81. package/dist/api-routes/migrate-to-multi.d.ts +26 -0
  82. package/dist/api-routes/migrate-to-multi.js +188 -0
  83. package/dist/api-routes/pages.d.ts +39 -0
  84. package/dist/api-routes/pages.js +88 -0
  85. package/dist/api-routes/section-add.d.ts +18 -0
  86. package/dist/api-routes/section-add.js +173 -0
  87. package/dist/api-routes/section-commit-pending.d.ts +18 -0
  88. package/dist/api-routes/section-commit-pending.js +207 -0
  89. package/dist/api-routes/section-delete.d.ts +15 -0
  90. package/dist/api-routes/section-delete.js +149 -0
  91. package/dist/api-routes/section-duplicate.d.ts +15 -0
  92. package/dist/api-routes/section-duplicate.js +143 -0
  93. package/dist/api-routes/section-management.d.ts +41 -0
  94. package/dist/api-routes/section-management.js +14 -0
  95. package/dist/api-routes/section-prepare-copy.d.ts +25 -0
  96. package/dist/api-routes/section-prepare-copy.js +69 -0
  97. package/dist/api-routes/section-prepare.d.ts +18 -0
  98. package/dist/api-routes/section-prepare.js +104 -0
  99. package/dist/api-routes/setup-github-app-bounce.d.ts +13 -0
  100. package/dist/api-routes/setup-github-app-bounce.js +45 -0
  101. package/dist/api-routes/setup-github-app-branches.d.ts +14 -0
  102. package/dist/api-routes/setup-github-app-branches.js +58 -0
  103. package/dist/api-routes/setup-github-app-callback.d.ts +15 -0
  104. package/dist/api-routes/setup-github-app-callback.js +45 -0
  105. package/dist/api-routes/setup-github-app-installed.d.ts +15 -0
  106. package/dist/api-routes/setup-github-app-installed.js +33 -0
  107. package/dist/api-routes/setup-github-app-repos.d.ts +17 -0
  108. package/dist/api-routes/setup-github-app-repos.js +41 -0
  109. package/dist/api-routes/setup-github-app.d.ts +15 -0
  110. package/dist/api-routes/setup-github-app.js +41 -0
  111. package/dist/api-routes/updater-check.d.ts +10 -0
  112. package/dist/api-routes/updater-check.js +37 -0
  113. package/dist/api-routes/updater-register.d.ts +14 -0
  114. package/dist/api-routes/updater-register.js +71 -0
  115. package/dist/api-routes/updater-transfer.d.ts +11 -0
  116. package/dist/api-routes/updater-transfer.js +37 -0
  117. package/dist/api-routes/updater-unbind.d.ts +17 -0
  118. package/dist/api-routes/updater-unbind.js +35 -0
  119. package/dist/api-routes/webhooks-status.d.ts +12 -0
  120. package/dist/api-routes/webhooks-status.js +22 -0
  121. package/dist/api-routes/webhooks-test.d.ts +13 -0
  122. package/dist/api-routes/webhooks-test.js +124 -0
  123. package/dist/api-routes/webhooks.d.ts +6 -0
  124. package/dist/api-routes/webhooks.js +148 -0
  125. package/dist/api-routes/websites-add.d.ts +15 -0
  126. package/dist/api-routes/websites-add.js +92 -0
  127. package/dist/api-routes/websites-list.d.ts +12 -0
  128. package/dist/api-routes/websites-list.js +35 -0
  129. package/dist/api-routes/websites-remove.d.ts +15 -0
  130. package/dist/api-routes/websites-remove.js +69 -0
  131. package/dist/chunk-35S35OIV.js +80 -0
  132. package/dist/chunk-45ARVNT3.js +25 -0
  133. package/dist/chunk-5PIMDP4N.js +25 -0
  134. package/dist/chunk-5ZFTG4BW.js +10 -0
  135. package/dist/chunk-6UIKVKED.js +51 -0
  136. package/dist/chunk-737TIZRU.js +9 -0
  137. package/dist/chunk-AM4DZXXM.js +120 -0
  138. package/dist/chunk-FXNOTESI.js +87 -0
  139. package/dist/chunk-GHNK2GFE.js +48 -0
  140. package/dist/chunk-GRG3LNKH.js +37 -0
  141. package/dist/chunk-INIWFKQ3.js +236 -0
  142. package/dist/chunk-JHY6XTLL.js +24 -0
  143. package/dist/chunk-K22A4ZBS.js +1574 -0
  144. package/dist/chunk-KH22FJO5.js +17 -0
  145. package/dist/chunk-NKDATSPA.js +43 -0
  146. package/dist/chunk-RHJONMLK.js +1267 -0
  147. package/dist/chunk-TJNJKPUL.js +11 -0
  148. package/dist/chunk-V6IMPVF3.js +120 -0
  149. package/dist/chunk-W3QHY5GW.js +19 -0
  150. package/dist/chunk-ZQDGGWJP.js +43 -0
  151. package/package.json +249 -53
  152. package/src/api-routes/__tests__/route-registry.test.ts +7 -1
  153. package/tsconfig.json +0 -9
@@ -0,0 +1,266 @@
1
+ import {
2
+ patchTemplateForFields
3
+ } from "../chunk-RHJONMLK.js";
4
+ import {
5
+ resolveGitHubTokenForRequest
6
+ } from "../chunk-NKDATSPA.js";
7
+ import {
8
+ withTrailers
9
+ } from "../chunk-KH22FJO5.js";
10
+
11
+ // src/api-routes/init-apply.ts
12
+ import { generateConfigFile } from "@setzkasten-cms/core/init";
13
+
14
+ // src/init/astro-config-patcher.ts
15
+ function patchAstroConfig(source) {
16
+ if (source.includes("@setzkasten-cms/astro") || source.includes("setzkasten(")) {
17
+ return null;
18
+ }
19
+ let result = source;
20
+ const importLine = "import setzkasten from '@setzkasten-cms/astro'\n";
21
+ const lastImportIndex = findLastImportEnd(result);
22
+ if (lastImportIndex >= 0) {
23
+ result = result.slice(0, lastImportIndex) + "\n" + importLine + result.slice(lastImportIndex);
24
+ } else {
25
+ result = importLine + "\n" + result;
26
+ }
27
+ result = addToIntegrations(result);
28
+ return result;
29
+ }
30
+ function findLastImportEnd(source) {
31
+ const lines = source.split("\n");
32
+ let lastImportLineEnd = -1;
33
+ let pos = 0;
34
+ for (const line of lines) {
35
+ pos += line.length + 1;
36
+ if (line.trimStart().startsWith("import ")) {
37
+ lastImportLineEnd = pos;
38
+ }
39
+ }
40
+ return lastImportLineEnd;
41
+ }
42
+ function addToIntegrations(source) {
43
+ const integrationsMatch = source.match(/integrations\s*:\s*\[/);
44
+ if (integrationsMatch && integrationsMatch.index !== void 0) {
45
+ const insertPos = integrationsMatch.index + integrationsMatch[0].length;
46
+ const after = source.slice(insertPos).trimStart();
47
+ if (after.startsWith("]")) {
48
+ return source.slice(0, insertPos) + "setzkasten()" + source.slice(insertPos);
49
+ }
50
+ return source.slice(0, insertPos) + "\n setzkasten(),\n " + source.slice(insertPos);
51
+ }
52
+ const defineConfigMatch = source.match(/defineConfig\s*\(\s*\{/);
53
+ if (defineConfigMatch && defineConfigMatch.index !== void 0) {
54
+ const insertPos = defineConfigMatch.index + defineConfigMatch[0].length;
55
+ return source.slice(0, insertPos) + "\n integrations: [setzkasten()],\n" + source.slice(insertPos);
56
+ }
57
+ return source;
58
+ }
59
+
60
+ // src/api-routes/init-apply.ts
61
+ var POST = async ({ request, cookies }) => {
62
+ const session = cookies.get("setzkasten_session")?.value;
63
+ if (!session) {
64
+ return new Response("Unauthorized", { status: 401 });
65
+ }
66
+ const tokenResult = await resolveGitHubTokenForRequest(request);
67
+ if (!tokenResult.ok) {
68
+ return new Response(tokenResult.error.message, { status: 500 });
69
+ }
70
+ const githubToken = tokenResult.value;
71
+ try {
72
+ const body = await request.json();
73
+ const { owner, repo, branch = "main", projectRoot, astroConfigPath, sections, pages } = body;
74
+ const contentPath = body.contentPath ?? "content";
75
+ const filesToCommit = [];
76
+ const configInput = {
77
+ gitRepo: `${owner}/${repo}`,
78
+ productKey: "website",
79
+ sections
80
+ };
81
+ const configContent = generateConfigFile(configInput);
82
+ const configPath = projectRoot ? `${projectRoot}/setzkasten.config.ts` : "setzkasten.config.ts";
83
+ filesToCommit.push({ path: configPath, content: configContent });
84
+ if (astroConfigPath) {
85
+ const astroConfigSource = await fetchFileContent(owner, repo, branch, astroConfigPath, githubToken);
86
+ if (astroConfigSource) {
87
+ const patched = patchAstroConfig(astroConfigSource);
88
+ if (patched) {
89
+ filesToCommit.push({ path: astroConfigPath, content: patched });
90
+ }
91
+ }
92
+ }
93
+ for (const section of sections) {
94
+ const sectionData = {};
95
+ for (const field of section.fields) {
96
+ sectionData[field.key] = getDefaultValue(field.type);
97
+ }
98
+ const sectionPath = `${contentPath}/_sections/${section.key}.json`;
99
+ filesToCommit.push({
100
+ path: sectionPath,
101
+ content: JSON.stringify(sectionData, null, 2)
102
+ });
103
+ }
104
+ for (const section of sections) {
105
+ if (!section.componentPath) continue;
106
+ const componentSource = await fetchFileContent(owner, repo, branch, section.componentPath, githubToken);
107
+ if (!componentSource) continue;
108
+ const patched = await patchTemplateForFields(componentSource, section.key, section.fields);
109
+ if (patched !== componentSource) {
110
+ filesToCommit.push({ path: section.componentPath, content: patched });
111
+ }
112
+ }
113
+ for (const page of pages) {
114
+ const pageConfig = {
115
+ sections: page.sectionKeys.map((key, index) => ({
116
+ key,
117
+ enabled: true,
118
+ order: index
119
+ }))
120
+ };
121
+ const configKey = "_" + page.pageKey.replace(/\//g, "_");
122
+ const pagePath = `${contentPath}/pages/${configKey}.json`;
123
+ filesToCommit.push({
124
+ path: pagePath,
125
+ content: JSON.stringify(pageConfig, null, 2)
126
+ });
127
+ }
128
+ const commitResult = await batchCommit(
129
+ owner,
130
+ repo,
131
+ branch,
132
+ filesToCommit,
133
+ withTrailers("feat: initialize Setzkasten CMS"),
134
+ githubToken
135
+ );
136
+ if (!commitResult.ok) {
137
+ return Response.json(
138
+ { error: commitResult.error },
139
+ { status: 500 }
140
+ );
141
+ }
142
+ return Response.json({
143
+ success: true,
144
+ commitSha: commitResult.sha,
145
+ filesWritten: filesToCommit.map((f) => f.path)
146
+ });
147
+ } catch (error) {
148
+ console.error("[setzkasten] Init apply error:", error);
149
+ return Response.json(
150
+ { error: error instanceof Error ? error.message : "Apply failed" },
151
+ { status: 500 }
152
+ );
153
+ }
154
+ };
155
+ function getDefaultValue(fieldType) {
156
+ switch (fieldType) {
157
+ case "text":
158
+ return "";
159
+ case "number":
160
+ return 0;
161
+ case "boolean":
162
+ return false;
163
+ case "image":
164
+ return { path: "", alt: "" };
165
+ case "array":
166
+ return [];
167
+ case "color":
168
+ return "#000000";
169
+ case "date":
170
+ return "";
171
+ case "icon":
172
+ return "";
173
+ default:
174
+ return "";
175
+ }
176
+ }
177
+ async function fetchFileContent(owner, repo, branch, path, token) {
178
+ try {
179
+ const response = await fetch(
180
+ `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
181
+ {
182
+ headers: {
183
+ Authorization: `Bearer ${token}`,
184
+ Accept: "application/vnd.github+json",
185
+ "X-GitHub-Api-Version": "2022-11-28"
186
+ }
187
+ }
188
+ );
189
+ if (!response.ok) return null;
190
+ const data = await response.json();
191
+ return data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content;
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+ async function batchCommit(owner, repo, branch, files, message, token) {
197
+ const headers = {
198
+ Authorization: `Bearer ${token}`,
199
+ Accept: "application/vnd.github+json",
200
+ "X-GitHub-Api-Version": "2022-11-28",
201
+ "Content-Type": "application/json"
202
+ };
203
+ try {
204
+ const refRes = await fetch(
205
+ `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
206
+ { headers }
207
+ );
208
+ if (!refRes.ok) return { ok: false, error: `Failed to get HEAD: ${refRes.status}` };
209
+ const refData = await refRes.json();
210
+ const headSha = refData.object.sha;
211
+ const commitRes = await fetch(
212
+ `https://api.github.com/repos/${owner}/${repo}/git/commits/${headSha}`,
213
+ { headers }
214
+ );
215
+ if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
216
+ const commitData = await commitRes.json();
217
+ const baseTreeSha = commitData.tree.sha;
218
+ const treeRes = await fetch(
219
+ `https://api.github.com/repos/${owner}/${repo}/git/trees`,
220
+ {
221
+ method: "POST",
222
+ headers,
223
+ body: JSON.stringify({
224
+ base_tree: baseTreeSha,
225
+ tree: files.map((f) => ({
226
+ path: f.path,
227
+ mode: "100644",
228
+ type: "blob",
229
+ content: f.content
230
+ }))
231
+ })
232
+ }
233
+ );
234
+ if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
235
+ const treeData = await treeRes.json();
236
+ const newCommitRes = await fetch(
237
+ `https://api.github.com/repos/${owner}/${repo}/git/commits`,
238
+ {
239
+ method: "POST",
240
+ headers,
241
+ body: JSON.stringify({
242
+ tree: treeData.sha,
243
+ parents: [headSha],
244
+ message
245
+ })
246
+ }
247
+ );
248
+ if (!newCommitRes.ok) return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
249
+ const newCommitData = await newCommitRes.json();
250
+ const updateRes = await fetch(
251
+ `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
252
+ {
253
+ method: "PATCH",
254
+ headers,
255
+ body: JSON.stringify({ sha: newCommitData.sha })
256
+ }
257
+ );
258
+ if (!updateRes.ok) return { ok: false, error: `Failed to update ref: ${updateRes.status}` };
259
+ return { ok: true, sha: newCommitData.sha };
260
+ } catch (error) {
261
+ return { ok: false, error: error instanceof Error ? error.message : "Commit failed" };
262
+ }
263
+ }
264
+ export {
265
+ POST
266
+ };
@@ -0,0 +1,16 @@
1
+ import { APIRoute } from 'astro';
2
+
3
+ /**
4
+ * POST /api/setzkasten/init/migrate
5
+ *
6
+ * Adds `data-sk-field` attributes to an existing Astro section component.
7
+ * Handles two cases:
8
+ * 1. Fields using `getSection()` / `Astro.props` variable patterns
9
+ * 2. Hardcoded text matching content JSON values (for adopted fields without bindings)
10
+ *
11
+ * Body: { owner, repo, branch?, sectionKey, componentPath }
12
+ * Returns: { commitSha, patchedSource, originalSource }
13
+ */
14
+ declare const POST: APIRoute;
15
+
16
+ export { POST };
@@ -0,0 +1,205 @@
1
+ import {
2
+ patchTemplateForFields
3
+ } from "../chunk-RHJONMLK.js";
4
+ import {
5
+ prefixPath,
6
+ resolveStorageConfigForRequest
7
+ } from "../chunk-6UIKVKED.js";
8
+ import {
9
+ resolveGitHubTokenForRequest
10
+ } from "../chunk-NKDATSPA.js";
11
+ import {
12
+ withTrailers
13
+ } from "../chunk-KH22FJO5.js";
14
+
15
+ // src/api-routes/init-migrate.ts
16
+ var POST = async ({ request, cookies }) => {
17
+ const session = cookies.get("setzkasten_session")?.value;
18
+ if (!session) {
19
+ return new Response("Unauthorized", { status: 401 });
20
+ }
21
+ const tokenResult = await resolveGitHubTokenForRequest(request);
22
+ if (!tokenResult.ok) {
23
+ return new Response(tokenResult.error.message, { status: 500 });
24
+ }
25
+ const githubToken = tokenResult.value;
26
+ try {
27
+ const body = await request.json();
28
+ const storage = await resolveStorageConfigForRequest(request, body);
29
+ if (!storage) {
30
+ return Response.json({ error: "Could not resolve owner/repo. Set SETZKASTEN_OWNER and SETZKASTEN_REPO env vars." }, { status: 400 });
31
+ }
32
+ const { owner, repo, branch, projectPrefix } = storage;
33
+ const { sectionKey } = body;
34
+ const componentPath = prefixPath(
35
+ body.componentPath || deriveComponentPath(sectionKey),
36
+ projectPrefix
37
+ );
38
+ if (!sectionKey) {
39
+ return Response.json({ error: "sectionKey is required" }, { status: 400 });
40
+ }
41
+ const headers = {
42
+ Authorization: `Bearer ${githubToken}`,
43
+ Accept: "application/vnd.github+json",
44
+ "X-GitHub-Api-Version": "2022-11-28",
45
+ "Content-Type": "application/json"
46
+ };
47
+ const source = await fetchFileContent(owner, repo, branch, componentPath, githubToken);
48
+ if (!source) {
49
+ return Response.json({ error: "Could not read component source" }, { status: 404 });
50
+ }
51
+ const serverConfig = globalThis.__SETZKASTEN_CONFIG__;
52
+ const contentPath = serverConfig?.storage?.contentPath || "content";
53
+ const sectionJsonPath = `${contentPath}/_sections/${sectionKey}.json`;
54
+ const sectionJson = await fetchFileContent(owner, repo, branch, sectionJsonPath, githubToken);
55
+ const fullConfig = globalThis.__SETZKASTEN_FULL_CONFIG__;
56
+ const fieldDefs = getFieldDefs(fullConfig, sectionKey);
57
+ const fields = [];
58
+ let contentData = {};
59
+ if (sectionJson) {
60
+ try {
61
+ contentData = JSON.parse(sectionJson);
62
+ } catch {
63
+ }
64
+ }
65
+ for (const [key, type] of fieldDefs) {
66
+ fields.push({ key, type, defaultValue: contentData[key] });
67
+ }
68
+ for (const [key, value] of Object.entries(contentData)) {
69
+ if (!fieldDefs.has(key)) {
70
+ fields.push({ key, type: "text", defaultValue: value });
71
+ }
72
+ }
73
+ const patched = await patchTemplateForFields(source, sectionKey, fields);
74
+ if (patched === source) {
75
+ return Response.json({
76
+ success: true,
77
+ noChanges: true,
78
+ message: "No patchable patterns found in template"
79
+ });
80
+ }
81
+ const commitResult = await batchCommit(
82
+ owner,
83
+ repo,
84
+ branch,
85
+ [{ path: componentPath, content: patched }],
86
+ withTrailers(`chore: add live-preview bindings to ${sectionKey} section`),
87
+ headers
88
+ );
89
+ if (!commitResult.ok) {
90
+ return Response.json({ error: commitResult.error }, { status: 500 });
91
+ }
92
+ return Response.json({
93
+ success: true,
94
+ commitSha: commitResult.sha,
95
+ patchedSource: patched,
96
+ originalSource: source
97
+ });
98
+ } catch (error) {
99
+ console.error("[setzkasten] migrate error:", error);
100
+ return Response.json(
101
+ { error: error instanceof Error ? error.message : "Migration failed" },
102
+ { status: 500 }
103
+ );
104
+ }
105
+ };
106
+ function getFieldDefs(config, sectionKey) {
107
+ const defs = /* @__PURE__ */ new Map();
108
+ if (!config) return defs;
109
+ for (const product of Object.values(config.products)) {
110
+ const section = product.sections[sectionKey];
111
+ if (section) {
112
+ for (const [key, field] of Object.entries(section.fields)) {
113
+ defs.set(key, field.type || "text");
114
+ }
115
+ }
116
+ }
117
+ return defs;
118
+ }
119
+ function deriveComponentPath(sectionKey) {
120
+ const componentName = sectionKey.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") + "Section";
121
+ return `src/components/sections/${componentName}.astro`;
122
+ }
123
+ async function fetchFileContent(owner, repo, branch, path, token) {
124
+ try {
125
+ const response = await fetch(
126
+ `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
127
+ {
128
+ headers: {
129
+ Authorization: `Bearer ${token}`,
130
+ Accept: "application/vnd.github+json",
131
+ "X-GitHub-Api-Version": "2022-11-28"
132
+ }
133
+ }
134
+ );
135
+ if (!response.ok) return null;
136
+ const data = await response.json();
137
+ return data.encoding === "base64" ? Buffer.from(data.content, "base64").toString("utf-8") : data.content;
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
142
+ async function batchCommit(owner, repo, branch, files, message, headers) {
143
+ try {
144
+ const refRes = await fetch(
145
+ `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
146
+ { headers }
147
+ );
148
+ if (!refRes.ok) return { ok: false, error: `Failed to get HEAD: ${refRes.status}` };
149
+ const refData = await refRes.json();
150
+ const headSha = refData.object.sha;
151
+ const commitRes = await fetch(
152
+ `https://api.github.com/repos/${owner}/${repo}/git/commits/${headSha}`,
153
+ { headers }
154
+ );
155
+ if (!commitRes.ok) return { ok: false, error: `Failed to get commit: ${commitRes.status}` };
156
+ const commitData = await commitRes.json();
157
+ const treeRes = await fetch(
158
+ `https://api.github.com/repos/${owner}/${repo}/git/trees`,
159
+ {
160
+ method: "POST",
161
+ headers,
162
+ body: JSON.stringify({
163
+ base_tree: commitData.tree.sha,
164
+ tree: files.map((f) => ({
165
+ path: f.path,
166
+ mode: "100644",
167
+ type: "blob",
168
+ content: f.content
169
+ }))
170
+ })
171
+ }
172
+ );
173
+ if (!treeRes.ok) return { ok: false, error: `Failed to create tree: ${treeRes.status}` };
174
+ const treeData = await treeRes.json();
175
+ const newCommitRes = await fetch(
176
+ `https://api.github.com/repos/${owner}/${repo}/git/commits`,
177
+ {
178
+ method: "POST",
179
+ headers,
180
+ body: JSON.stringify({
181
+ tree: treeData.sha,
182
+ parents: [headSha],
183
+ message
184
+ })
185
+ }
186
+ );
187
+ if (!newCommitRes.ok) return { ok: false, error: `Failed to create commit: ${newCommitRes.status}` };
188
+ const newCommitData = await newCommitRes.json();
189
+ const updateRes = await fetch(
190
+ `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`,
191
+ {
192
+ method: "PATCH",
193
+ headers,
194
+ body: JSON.stringify({ sha: newCommitData.sha })
195
+ }
196
+ );
197
+ if (!updateRes.ok) return { ok: false, error: `Failed to update ref: ${updateRes.status}` };
198
+ return { ok: true, sha: newCommitData.sha };
199
+ } catch (error) {
200
+ return { ok: false, error: error instanceof Error ? error.message : "Commit failed" };
201
+ }
202
+ }
203
+ export {
204
+ POST
205
+ };
@@ -0,0 +1,39 @@
1
+ import { APIRoute } from 'astro';
2
+ import { SetzKastenConfig } from '@setzkasten-cms/core';
3
+
4
+ /**
5
+ * Resolves the full Setzkasten config.
6
+ * Reads the Vite build-time constant first; falls back to globalThis for
7
+ * local dev / test environments where the define is not applied.
8
+ *
9
+ * Without the build-time fallback, cold-start Vercel function invocations of
10
+ * this API route see managedSections={} and offer every adopted section
11
+ * (including _layout_header / _layout_footer) for re-adoption.
12
+ */
13
+ declare function resolveFullConfig(): SetzKastenConfig | undefined;
14
+ /**
15
+ * POST /api/setzkasten/init/scan-page
16
+ *
17
+ * Scans a single page's Astro template for section imports and identifies:
18
+ * 1. Unmanaged sections — not yet in the Setzkasten schema
19
+ * 2. Missing fields on managed sections — template fields not in the schema
20
+ *
21
+ * Body: { owner, repo, branch?, pagePath, projectRoot? }
22
+ * Returns: { unmanagedSections: InferredSection[], managedUpdates: ManagedUpdate[] }
23
+ */
24
+ declare const POST: APIRoute;
25
+ interface LayoutRegion {
26
+ /** Region name: 'header' or 'footer' */
27
+ name: string;
28
+ /** HTML tag used to identify this region */
29
+ tag: string;
30
+ /** Extracted HTML content */
31
+ html: string;
32
+ }
33
+ /**
34
+ * Extract header and footer regions from a layout file's template.
35
+ * Looks for top-level <header>...</header> and <footer>...</footer> blocks.
36
+ */
37
+ declare function extractLayoutRegions(layoutSource: string): LayoutRegion[];
38
+
39
+ export { POST, extractLayoutRegions, resolveFullConfig };