gazetta 0.5.0 → 0.6.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/admin-dist/assets/index-B6pVot0Y.css +1 -0
- package/admin-dist/assets/index-DniLwxJA.js +609 -0
- package/admin-dist/assets/{vendor-primevue-BnR1c_bQ.js → vendor-primevue-C0Q_YTCb.js} +330 -431
- package/admin-dist/assets/vendor-vue-D3wBSmDf.js +1 -0
- package/admin-dist/index.html +4 -4
- package/dist/admin-api/index.d.ts +19 -4
- package/dist/admin-api/index.d.ts.map +1 -1
- package/dist/admin-api/index.js +154 -18
- package/dist/admin-api/index.js.map +1 -1
- package/dist/admin-api/routes/compare.d.ts +2 -1
- package/dist/admin-api/routes/compare.d.ts.map +1 -1
- package/dist/admin-api/routes/compare.js +33 -24
- package/dist/admin-api/routes/compare.js.map +1 -1
- package/dist/admin-api/routes/fields.d.ts +2 -2
- package/dist/admin-api/routes/fields.d.ts.map +1 -1
- package/dist/admin-api/routes/fields.js +10 -3
- package/dist/admin-api/routes/fields.js.map +1 -1
- package/dist/admin-api/routes/fragments.d.ts +2 -3
- package/dist/admin-api/routes/fragments.d.ts.map +1 -1
- package/dist/admin-api/routes/fragments.js +92 -19
- package/dist/admin-api/routes/fragments.js.map +1 -1
- package/dist/admin-api/routes/history.d.ts +23 -0
- package/dist/admin-api/routes/history.d.ts.map +1 -0
- package/dist/admin-api/routes/history.js +143 -0
- package/dist/admin-api/routes/history.js.map +1 -0
- package/dist/admin-api/routes/pages.d.ts +2 -3
- package/dist/admin-api/routes/pages.d.ts.map +1 -1
- package/dist/admin-api/routes/pages.js +118 -20
- package/dist/admin-api/routes/pages.js.map +1 -1
- package/dist/admin-api/routes/preview.d.ts +2 -2
- package/dist/admin-api/routes/preview.d.ts.map +1 -1
- package/dist/admin-api/routes/preview.js +50 -15
- package/dist/admin-api/routes/preview.js.map +1 -1
- package/dist/admin-api/routes/publish.d.ts +2 -1
- package/dist/admin-api/routes/publish.d.ts.map +1 -1
- package/dist/admin-api/routes/publish.js +213 -66
- package/dist/admin-api/routes/publish.js.map +1 -1
- package/dist/admin-api/routes/site.d.ts +2 -2
- package/dist/admin-api/routes/site.d.ts.map +1 -1
- package/dist/admin-api/routes/site.js +27 -4
- package/dist/admin-api/routes/site.js.map +1 -1
- package/dist/admin-api/routes/templates.d.ts +2 -2
- package/dist/admin-api/routes/templates.d.ts.map +1 -1
- package/dist/admin-api/routes/templates.js +19 -9
- package/dist/admin-api/routes/templates.js.map +1 -1
- package/dist/admin-api/schemas/compare.d.ts +29 -0
- package/dist/admin-api/schemas/compare.d.ts.map +1 -0
- package/dist/admin-api/schemas/compare.js +30 -0
- package/dist/admin-api/schemas/compare.js.map +1 -0
- package/dist/admin-api/schemas/dependents.d.ts +15 -0
- package/dist/admin-api/schemas/dependents.d.ts.map +1 -0
- package/dist/admin-api/schemas/dependents.js +14 -0
- package/dist/admin-api/schemas/dependents.js.map +1 -0
- package/dist/admin-api/schemas/fetch.d.ts +12 -0
- package/dist/admin-api/schemas/fetch.d.ts.map +1 -0
- package/dist/admin-api/schemas/fetch.js +11 -0
- package/dist/admin-api/schemas/fetch.js.map +1 -0
- package/dist/admin-api/schemas/fields.d.ts +11 -0
- package/dist/admin-api/schemas/fields.d.ts.map +1 -0
- package/dist/admin-api/schemas/fields.js +11 -0
- package/dist/admin-api/schemas/fields.js.map +1 -0
- package/dist/admin-api/schemas/fragments.d.ts +27 -0
- package/dist/admin-api/schemas/fragments.d.ts.map +1 -0
- package/dist/admin-api/schemas/fragments.js +26 -0
- package/dist/admin-api/schemas/fragments.js.map +1 -0
- package/dist/admin-api/schemas/history.d.ts +73 -0
- package/dist/admin-api/schemas/history.d.ts.map +1 -0
- package/dist/admin-api/schemas/history.js +35 -0
- package/dist/admin-api/schemas/history.js.map +1 -0
- package/dist/admin-api/schemas/index.d.ts +32 -0
- package/dist/admin-api/schemas/index.d.ts.map +1 -0
- package/dist/admin-api/schemas/index.js +32 -0
- package/dist/admin-api/schemas/index.js.map +1 -0
- package/dist/admin-api/schemas/pages.d.ts +46 -0
- package/dist/admin-api/schemas/pages.d.ts.map +1 -0
- package/dist/admin-api/schemas/pages.js +47 -0
- package/dist/admin-api/schemas/pages.js.map +1 -0
- package/dist/admin-api/schemas/publish.d.ts +67 -0
- package/dist/admin-api/schemas/publish.d.ts.map +1 -0
- package/dist/admin-api/schemas/publish.js +60 -0
- package/dist/admin-api/schemas/publish.js.map +1 -0
- package/dist/admin-api/schemas/site.d.ts +28 -0
- package/dist/admin-api/schemas/site.d.ts.map +1 -0
- package/dist/admin-api/schemas/site.js +24 -0
- package/dist/admin-api/schemas/site.js.map +1 -0
- package/dist/admin-api/schemas/targets.d.ts +36 -0
- package/dist/admin-api/schemas/targets.d.ts.map +1 -0
- package/dist/admin-api/schemas/targets.js +19 -0
- package/dist/admin-api/schemas/targets.js.map +1 -0
- package/dist/admin-api/schemas/templates.d.ts +17 -0
- package/dist/admin-api/schemas/templates.d.ts.map +1 -0
- package/dist/admin-api/schemas/templates.js +16 -0
- package/dist/admin-api/schemas/templates.js.map +1 -0
- package/dist/admin-api/source-context.d.ts +165 -0
- package/dist/admin-api/source-context.d.ts.map +1 -0
- package/dist/admin-api/source-context.js +95 -0
- package/dist/admin-api/source-context.js.map +1 -0
- package/dist/app.js +1 -1
- package/dist/app.js.map +1 -1
- package/dist/assemble.d.ts.map +1 -1
- package/dist/assemble.js +4 -1
- package/dist/assemble.js.map +1 -1
- package/dist/cli/bootstrap.d.ts +48 -0
- package/dist/cli/bootstrap.d.ts.map +1 -0
- package/dist/cli/bootstrap.js +85 -0
- package/dist/cli/bootstrap.js.map +1 -0
- package/dist/cli/history.d.ts +45 -0
- package/dist/cli/history.d.ts.map +1 -0
- package/dist/cli/history.js +165 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/index.js +630 -115
- package/dist/cli/index.js.map +1 -1
- package/dist/compare.d.ts +8 -5
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +53 -14
- package/dist/compare.js.map +1 -1
- package/dist/content-root.d.ts +38 -0
- package/dist/content-root.d.ts.map +1 -0
- package/dist/content-root.js +29 -0
- package/dist/content-root.js.map +1 -0
- package/dist/editor/mount.d.ts +1 -1
- package/dist/editor/mount.d.ts.map +1 -1
- package/dist/editor/mount.js +61 -29
- package/dist/editor/mount.js.map +1 -1
- package/dist/hash.d.ts +34 -3
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +64 -7
- package/dist/hash.js.map +1 -1
- package/dist/history-provider.d.ts +49 -0
- package/dist/history-provider.d.ts.map +1 -0
- package/dist/history-provider.js +226 -0
- package/dist/history-provider.js.map +1 -0
- package/dist/history-recorder.d.ts +98 -0
- package/dist/history-recorder.d.ts.map +1 -0
- package/dist/history-recorder.js +160 -0
- package/dist/history-recorder.js.map +1 -0
- package/dist/history-restorer.d.ts +46 -0
- package/dist/history-restorer.d.ts.map +1 -0
- package/dist/history-restorer.js +105 -0
- package/dist/history-restorer.js.map +1 -0
- package/dist/history.d.ts +111 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +25 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +26 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +74 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +150 -0
- package/dist/locale.js.map +1 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +16 -1
- package/dist/manifest.js.map +1 -1
- package/dist/providers/azure-blob.d.ts.map +1 -1
- package/dist/providers/azure-blob.js.map +1 -1
- package/dist/providers/r2.d.ts.map +1 -1
- package/dist/providers/r2.js +7 -4
- package/dist/providers/r2.js.map +1 -1
- package/dist/providers/s3.d.ts.map +1 -1
- package/dist/providers/s3.js +23 -15
- package/dist/providers/s3.js.map +1 -1
- package/dist/publish-locale.d.ts +44 -0
- package/dist/publish-locale.d.ts.map +1 -0
- package/dist/publish-locale.js +103 -0
- package/dist/publish-locale.js.map +1 -0
- package/dist/publish-rendered.d.ts +16 -5
- package/dist/publish-rendered.d.ts.map +1 -1
- package/dist/publish-rendered.js +89 -36
- package/dist/publish-rendered.js.map +1 -1
- package/dist/publish.d.ts +5 -7
- package/dist/publish.d.ts.map +1 -1
- package/dist/publish.js +21 -12
- package/dist/publish.js.map +1 -1
- package/dist/renderer.d.ts +14 -4
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +35 -23
- package/dist/renderer.js.map +1 -1
- package/dist/resolver.d.ts +7 -2
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +66 -15
- package/dist/resolver.js.map +1 -1
- package/dist/robots.d.ts +22 -0
- package/dist/robots.d.ts.map +1 -0
- package/dist/robots.js +25 -0
- package/dist/robots.js.map +1 -0
- package/dist/seo.d.ts +56 -0
- package/dist/seo.d.ts.map +1 -0
- package/dist/seo.js +72 -0
- package/dist/seo.js.map +1 -0
- package/dist/serve.d.ts +41 -3
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +206 -65
- package/dist/serve.js.map +1 -1
- package/dist/sidecars.d.ts +9 -5
- package/dist/sidecars.d.ts.map +1 -1
- package/dist/sidecars.js +112 -22
- package/dist/sidecars.js.map +1 -1
- package/dist/site-loader.d.ts +74 -6
- package/dist/site-loader.d.ts.map +1 -1
- package/dist/site-loader.js +138 -28
- package/dist/site-loader.js.map +1 -1
- package/dist/sitemap.d.ts +45 -0
- package/dist/sitemap.d.ts.map +1 -0
- package/dist/sitemap.js +67 -0
- package/dist/sitemap.js.map +1 -0
- package/dist/source-sidecars.d.ts +21 -2
- package/dist/source-sidecars.d.ts.map +1 -1
- package/dist/source-sidecars.js +51 -5
- package/dist/source-sidecars.js.map +1 -1
- package/dist/targets.d.ts +47 -1
- package/dist/targets.d.ts.map +1 -1
- package/dist/targets.js +78 -9
- package/dist/targets.js.map +1 -1
- package/dist/template-loader.d.ts +7 -3
- package/dist/template-loader.d.ts.map +1 -1
- package/dist/template-loader.js +27 -12
- package/dist/template-loader.js.map +1 -1
- package/dist/templates-scan-worker.js +1 -1
- package/dist/templates-scan-worker.js.map +1 -1
- package/dist/templates-scan.d.ts.map +1 -1
- package/dist/templates-scan.js +1 -1
- package/dist/templates-scan.js.map +1 -1
- package/dist/types.d.ts +116 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +28 -5
- package/dist/types.js.map +1 -1
- package/dist/workers/cloudflare-r2.d.ts +11 -2
- package/dist/workers/cloudflare-r2.d.ts.map +1 -1
- package/dist/workers/cloudflare-r2.js +120 -55
- package/dist/workers/cloudflare-r2.js.map +1 -1
- package/package.json +11 -2
- package/admin-dist/assets/index-BZAFKsUp.js +0 -608
- package/admin-dist/assets/index-BpRotMuK.css +0 -1
- package/admin-dist/assets/vendor-vue-DSjyxCX6.js +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import {
|
|
2
|
+
import { allPageEntries } from '../../site-loader.js';
|
|
3
|
+
import { loadSiteFromSource } from '../source-context.js';
|
|
3
4
|
import { resolveFragment, resolvePage } from '../../resolver.js';
|
|
4
5
|
import { renderFragment, renderPage } from '../../renderer.js';
|
|
5
|
-
export function previewRoutes(
|
|
6
|
+
export function previewRoutes(resolve, templatesDir) {
|
|
6
7
|
const app = new Hono();
|
|
7
8
|
// No caching — preview always serves fresh content for editing
|
|
8
9
|
app.use('/preview/*', async (c, next) => {
|
|
@@ -10,25 +11,50 @@ export function previewRoutes(siteDir, storage, templatesDir) {
|
|
|
10
11
|
c.header('Cache-Control', 'no-store');
|
|
11
12
|
});
|
|
12
13
|
app.get('/preview/*', async (c) => {
|
|
13
|
-
|
|
14
|
+
const source = await resolve(c.req.query('target'));
|
|
15
|
+
return renderPreview(c, source, undefined, templatesDir);
|
|
14
16
|
});
|
|
15
17
|
app.post('/preview/*', async (c) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
+
const source = await resolve(c.req.query('target'));
|
|
19
|
+
const body = (await c.req.json());
|
|
20
|
+
return renderPreview(c, source, body.overrides, templatesDir);
|
|
18
21
|
});
|
|
19
22
|
return app;
|
|
20
23
|
}
|
|
21
|
-
async function renderPreview(c,
|
|
22
|
-
|
|
24
|
+
async function renderPreview(c, source, overrides, templatesDir) {
|
|
25
|
+
// Empty target (no site.yaml) — preview returns a friendly placeholder
|
|
26
|
+
// so the admin can still show the iframe. Happens when the active
|
|
27
|
+
// target is a never-published publish-target.
|
|
28
|
+
let site;
|
|
29
|
+
try {
|
|
30
|
+
site = await loadSiteFromSource(source, { templatesDir });
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err.message.includes('No site.yaml found')) {
|
|
34
|
+
return c.html('<!doctype html><html><body style="font-family:system-ui;padding:2rem;color:#525252">' +
|
|
35
|
+
'<h2 style="margin:0 0 0.5rem">No content on this target yet</h2>' +
|
|
36
|
+
'<p style="margin:0;font-size:0.875rem">Publish from an editable target to see a preview here.</p>' +
|
|
37
|
+
'</body></html>', 404);
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
23
41
|
const requestPath = c.req.path.replace(/^.*\/preview/, '') || '/';
|
|
24
|
-
// Fragment preview: /preview/@fragmentName
|
|
25
|
-
if
|
|
26
|
-
|
|
42
|
+
// Fragment preview: /preview/@fragmentName or /preview/fr/@fragmentName
|
|
43
|
+
// Extract locale prefix if present before the @
|
|
44
|
+
let previewLocale;
|
|
45
|
+
let fragRequestPath = requestPath;
|
|
46
|
+
const localeFragMatch = requestPath.match(/^\/([a-z]{2}(?:-[a-z]+)?)\/(@.+)$/);
|
|
47
|
+
if (localeFragMatch) {
|
|
48
|
+
previewLocale = localeFragMatch[1];
|
|
49
|
+
fragRequestPath = `/${localeFragMatch[2]}`;
|
|
50
|
+
}
|
|
51
|
+
if (fragRequestPath.startsWith('/@')) {
|
|
52
|
+
const fragmentName = fragRequestPath.slice(2);
|
|
27
53
|
try {
|
|
28
|
-
const resolved = await resolveFragment(fragmentName, site);
|
|
54
|
+
const resolved = await resolveFragment(fragmentName, site, previewLocale);
|
|
29
55
|
if (overrides)
|
|
30
56
|
applyOverrides(resolved, overrides);
|
|
31
|
-
return c.html(await renderFragment(resolved));
|
|
57
|
+
return c.html(await renderFragment(resolved, previewLocale));
|
|
32
58
|
}
|
|
33
59
|
catch (err) {
|
|
34
60
|
const e = err;
|
|
@@ -37,14 +63,23 @@ async function renderPreview(c, siteDir, storage, overrides, templatesDir) {
|
|
|
37
63
|
return c.html(`<div style="font-family:system-ui;padding:2rem;color:#fca5a5;background:#1a1a2e;min-height:100vh"><h2 style="color:#f87171;margin-bottom:1rem">Template Error</h2><pre style="white-space:pre-wrap;font-size:0.875rem;line-height:1.7">${msg}</pre><details style="margin-top:1rem"><summary style="color:#52525b;cursor:pointer">Stack trace</summary><pre style="color:#52525b;font-size:0.75rem;margin-top:0.5rem">${stack}</pre></details></div>`, 500);
|
|
38
64
|
}
|
|
39
65
|
}
|
|
40
|
-
for (const
|
|
66
|
+
for (const { name: pageName, page, locale: pageLocale } of allPageEntries(site)) {
|
|
41
67
|
const params = matchRoute(page.route, requestPath);
|
|
42
68
|
if (params) {
|
|
43
69
|
try {
|
|
44
|
-
const resolved = await resolvePage(pageName, site);
|
|
70
|
+
const resolved = await resolvePage(pageName, site, pageLocale);
|
|
45
71
|
if (overrides)
|
|
46
72
|
applyOverrides(resolved, overrides);
|
|
47
|
-
return c.html(await renderPage(resolved,
|
|
73
|
+
return c.html(await renderPage(resolved, {
|
|
74
|
+
routeParams: params,
|
|
75
|
+
metadata: page.metadata,
|
|
76
|
+
route: page.route,
|
|
77
|
+
seo: {
|
|
78
|
+
siteName: site.manifest.name,
|
|
79
|
+
locale: pageLocale ?? site.manifest.locale,
|
|
80
|
+
defaultOgImage: site.manifest.defaultOgImage,
|
|
81
|
+
},
|
|
82
|
+
}));
|
|
48
83
|
}
|
|
49
84
|
catch (err) {
|
|
50
85
|
const e = err;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/admin-api/routes/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAA;AAEzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/admin-api/routes/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import type { StorageProvider, TargetConfig } from '../../types.js';
|
|
3
|
+
import type { SourceContextResolver } from '../source-context.js';
|
|
3
4
|
import type { PublishResult } from '../../publish.js';
|
|
4
5
|
import { type TemplateInfo } from '../../templates-scan.js';
|
|
5
6
|
/**
|
|
@@ -35,5 +36,5 @@ export type PublishProgress = {
|
|
|
35
36
|
errors: string[];
|
|
36
37
|
}[];
|
|
37
38
|
};
|
|
38
|
-
export declare function publishRoutes(
|
|
39
|
+
export declare function publishRoutes(resolve: SourceContextResolver, preInitTargets?: Map<string, StorageProvider>, targetConfigs?: Record<string, TargetConfig>, templatesDir?: string, scanTemplatesInjected?: (templatesDir: string, projectRoot: string) => Promise<TemplateInfo[]>): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
39
40
|
//# sourceMappingURL=publish.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../../src/admin-api/routes/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../../src/admin-api/routes/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAEnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAarD,OAAO,EAAqC,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAM9F;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CAAE,CAAA;AAE7F,wBAAgB,aAAa,CAC3B,OAAO,EAAE,qBAAqB,EAC9B,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC7C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,EAC5C,YAAY,CAAC,EAAE,MAAM,EAIrB,qBAAqB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,8EAkgB/F"}
|
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { streamSSE } from 'hono/streaming';
|
|
3
|
-
import {
|
|
3
|
+
import { getType, getEnvironment, isEditable, isHistoryEnabled, getHistoryRetention } from '../../types.js';
|
|
4
4
|
import { publishItems, resolveDependencies, findFragmentDependents, findDependentsFromSidecars } from '../../publish.js';
|
|
5
|
-
import { listSidecars } from '../../sidecars.js';
|
|
6
5
|
import { mapLimitStream } from '../../concurrency.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { publishPageStatic, publishSiteManifest, publishFragmentIndex, createCloudflarePurge, lookupCloudflareZoneId, } from '../../publish-rendered.js';
|
|
7
|
+
import { loadSiteFromSource } from '../source-context.js';
|
|
8
|
+
import { publishPageAllLocales, publishFragmentAllLocales } from '../../publish-locale.js';
|
|
9
9
|
import { resolveEnvVars } from '../../targets.js';
|
|
10
10
|
import { scanTemplates, templateHashesFrom } from '../../templates-scan.js';
|
|
11
11
|
import { hashManifest } from '../../hash.js';
|
|
12
|
-
|
|
12
|
+
import { createContentRoot } from '../../content-root.js';
|
|
13
|
+
import { createHistoryProvider } from '../../history-provider.js';
|
|
14
|
+
import { recordWrite } from '../../history-recorder.js';
|
|
15
|
+
export function publishRoutes(resolve, preInitTargets, targetConfigs, templatesDir,
|
|
13
16
|
// Optional injected scanner — the admin-api server memoizes it and
|
|
14
17
|
// clears the cache via its template file watcher. Default: fresh scan
|
|
15
18
|
// on every call (used by the CLI and tests).
|
|
16
19
|
scanTemplatesInjected) {
|
|
17
20
|
const scan = scanTemplatesInjected ?? scanTemplates;
|
|
18
21
|
const app = new Hono();
|
|
19
|
-
// Background target initialization
|
|
22
|
+
// Background target initialization — lazy, needs the resolved source's
|
|
23
|
+
// projectSiteDir to resolve filesystem target paths. We call resolve()
|
|
24
|
+
// once with undefined (→ default editable) to obtain projectSiteDir.
|
|
20
25
|
let targets = preInitTargets ?? null;
|
|
21
|
-
|
|
22
|
-
? Promise.resolve(preInitTargets)
|
|
23
|
-
: (!targetConfigs || Object.keys(targetConfigs).length === 0)
|
|
24
|
-
? Promise.resolve(new Map())
|
|
25
|
-
: (async () => {
|
|
26
|
-
const { createTargetRegistry } = await import('../../targets.js');
|
|
27
|
-
targets = await createTargetRegistry(targetConfigs, siteDir);
|
|
28
|
-
return targets;
|
|
29
|
-
})();
|
|
30
|
-
if (!preInitTargets) {
|
|
31
|
-
initPromise.then(t => { targets = t; }).catch(() => { targets = new Map(); });
|
|
32
|
-
}
|
|
26
|
+
let initPromise = null;
|
|
33
27
|
async function getTargets() {
|
|
34
28
|
if (targets)
|
|
35
29
|
return targets;
|
|
30
|
+
if (!targetConfigs || Object.keys(targetConfigs).length === 0) {
|
|
31
|
+
targets = new Map();
|
|
32
|
+
return targets;
|
|
33
|
+
}
|
|
34
|
+
if (!initPromise) {
|
|
35
|
+
initPromise = (async () => {
|
|
36
|
+
const { createTargetRegistry } = await import('../../targets.js');
|
|
37
|
+
const bootstrapSource = await resolve(undefined);
|
|
38
|
+
const t = await createTargetRegistry(targetConfigs, bootstrapSource.projectSiteDir);
|
|
39
|
+
targets = t;
|
|
40
|
+
return t;
|
|
41
|
+
})().catch(() => {
|
|
42
|
+
targets = new Map();
|
|
43
|
+
return targets;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
36
46
|
return initPromise;
|
|
37
47
|
}
|
|
38
48
|
function getTargetConfig(name) {
|
|
@@ -45,7 +55,8 @@ scanTemplatesInjected) {
|
|
|
45
55
|
return {
|
|
46
56
|
name,
|
|
47
57
|
environment: cfg ? getEnvironment(cfg) : 'local',
|
|
48
|
-
|
|
58
|
+
type: cfg ? getType(cfg) : 'static',
|
|
59
|
+
editable: cfg ? isEditable(cfg) : true,
|
|
49
60
|
};
|
|
50
61
|
}));
|
|
51
62
|
});
|
|
@@ -70,30 +81,41 @@ scanTemplatesInjected) {
|
|
|
70
81
|
return c.json({ error: 'Missing or invalid "item" query (must be fragments/<name>)' }, 400);
|
|
71
82
|
}
|
|
72
83
|
const fragmentName = item.slice('fragments/'.length);
|
|
84
|
+
// `target` — specific published target's sidecars (read-only listing).
|
|
85
|
+
// Useful for "what would need republish on staging?" queries
|
|
86
|
+
// where sidecars reflect the last publish, not the draft.
|
|
87
|
+
// `source` — the editable source target (authoritative for the draft).
|
|
88
|
+
// When target === source (common case: client sends the
|
|
89
|
+
// active editable target), route through source so the
|
|
90
|
+
// sidecar writer can backfill missing entries.
|
|
73
91
|
const targetName = c.req.query('target');
|
|
92
|
+
const sourceName = c.req.query('source');
|
|
74
93
|
try {
|
|
75
|
-
|
|
94
|
+
const source = await resolve(sourceName);
|
|
95
|
+
// Treat `target=local` (the active editable target) the same as no
|
|
96
|
+
// target — the source path is the authoritative one for the draft,
|
|
97
|
+
// and it's the only path that knows how to backfill sidecars on a
|
|
98
|
+
// fresh dev server.
|
|
99
|
+
const isTargetTheSource = !targetName || targetName === source.targetName;
|
|
100
|
+
if (!isTargetTheSource) {
|
|
76
101
|
const t = await getTargets();
|
|
77
102
|
const targetStorage = t.get(targetName);
|
|
78
103
|
if (!targetStorage)
|
|
79
104
|
return c.json({ error: `Unknown target: ${targetName}` }, 400);
|
|
80
|
-
const result = await findDependentsFromSidecars(targetStorage, { fragment: fragmentName });
|
|
105
|
+
const result = await findDependentsFromSidecars(createContentRoot(targetStorage), { fragment: fragmentName });
|
|
81
106
|
return c.json(result);
|
|
82
107
|
}
|
|
83
|
-
// Source-side:
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
listSidecars(sourceStorage, `${siteDir}/fragments`),
|
|
91
|
-
]);
|
|
92
|
-
if (pagesList.size || fragmentsList.size) {
|
|
93
|
-
const result = await findDependentsFromSidecars(sourceStorage, { fragment: fragmentName }, { baseDir: siteDir });
|
|
108
|
+
// Source-side path: ensure every item has a sidecar before reading.
|
|
109
|
+
// The writer memoizes the backfill — concurrent tree badges on a
|
|
110
|
+
// fresh dev server share one pass instead of racing to an empty index.
|
|
111
|
+
const sidecarWriter = source.sidecarWriter;
|
|
112
|
+
if (sidecarWriter) {
|
|
113
|
+
await sidecarWriter.ensureBackfilled();
|
|
114
|
+
const result = await findDependentsFromSidecars(source.contentRoot, { fragment: fragmentName });
|
|
94
115
|
return c.json(result);
|
|
95
116
|
}
|
|
96
|
-
|
|
117
|
+
// No writer injected (legacy setup) — fall back to the manifest walker.
|
|
118
|
+
const result = await findFragmentDependents(source.contentRoot, fragmentName);
|
|
97
119
|
return c.json(result);
|
|
98
120
|
}
|
|
99
121
|
catch (err) {
|
|
@@ -110,7 +132,7 @@ scanTemplatesInjected) {
|
|
|
110
132
|
* Pre-flight validation (unknown targets, invalid templates) is reported as
|
|
111
133
|
* 'fatal' before any 'target-start' event so callers can map to a 4xx.
|
|
112
134
|
*/
|
|
113
|
-
async function* runPublish(items, targetNames) {
|
|
135
|
+
async function* runPublish(items, targetNames, sourceName) {
|
|
114
136
|
if (!items?.length) {
|
|
115
137
|
yield { kind: 'fatal', error: 'No items specified' };
|
|
116
138
|
return;
|
|
@@ -119,6 +141,16 @@ scanTemplatesInjected) {
|
|
|
119
141
|
yield { kind: 'fatal', error: 'No targets specified' };
|
|
120
142
|
return;
|
|
121
143
|
}
|
|
144
|
+
// Resolve the source editable target for this publish run.
|
|
145
|
+
let source;
|
|
146
|
+
try {
|
|
147
|
+
source = await resolve(sourceName);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
yield { kind: 'fatal', error: err.message };
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const { projectSiteDir } = source;
|
|
122
154
|
const t = await getTargets();
|
|
123
155
|
for (const name of targetNames) {
|
|
124
156
|
if (!t.has(name)) {
|
|
@@ -126,12 +158,12 @@ scanTemplatesInjected) {
|
|
|
126
158
|
return;
|
|
127
159
|
}
|
|
128
160
|
}
|
|
129
|
-
const allItems = await resolveDependencies(
|
|
161
|
+
const allItems = await resolveDependencies(source.contentRoot, items);
|
|
130
162
|
console.log(` Publishing to ${targetNames.length} target(s):`);
|
|
131
163
|
console.log(` Items: ${items.join(', ')} (+ ${allItems.length - items.length} dependencies)`);
|
|
132
164
|
console.log(` Targets: ${targetNames.join(', ')}`);
|
|
133
|
-
const tdir = templatesDir ?? `${
|
|
134
|
-
const projectRoot =
|
|
165
|
+
const tdir = templatesDir ?? `${projectSiteDir}/templates`;
|
|
166
|
+
const projectRoot = projectSiteDir.replace(/\/sites\/[^/]+$/, '');
|
|
135
167
|
const templateInfos = await scan(tdir, projectRoot);
|
|
136
168
|
const invalidTpls = templateInfos.filter(t => !t.valid);
|
|
137
169
|
if (invalidTpls.length) {
|
|
@@ -143,13 +175,13 @@ scanTemplatesInjected) {
|
|
|
143
175
|
return;
|
|
144
176
|
}
|
|
145
177
|
const templateHashes = templateHashesFrom(templateInfos);
|
|
146
|
-
const site = await
|
|
178
|
+
const site = await loadSiteFromSource(source, { templatesDir: tdir });
|
|
147
179
|
yield { kind: 'start', targets: targetNames, itemsPerTarget: allItems.length };
|
|
148
180
|
const results = [];
|
|
149
181
|
for (const targetName of targetNames) {
|
|
150
182
|
const targetStorage = t.get(targetName);
|
|
151
183
|
const config = getTargetConfig(targetName);
|
|
152
|
-
const isStatic = config ?
|
|
184
|
+
const isStatic = config ? getType(config) === 'static' : true;
|
|
153
185
|
const purgeConfig = config?.cache?.purge;
|
|
154
186
|
// Static mode bakes fragments into pages at publish time — if the user
|
|
155
187
|
// picked @header, we must also republish every page that uses it.
|
|
@@ -161,7 +193,7 @@ scanTemplatesInjected) {
|
|
|
161
193
|
const expanded = new Set(allItems);
|
|
162
194
|
for (const frag of fragmentItems) {
|
|
163
195
|
const name = frag.replace('fragments/', '');
|
|
164
|
-
const deps = await findFragmentDependents(
|
|
196
|
+
const deps = await findFragmentDependents(source.contentRoot, name);
|
|
165
197
|
for (const p of deps.pages)
|
|
166
198
|
expanded.add(`pages/${p}`);
|
|
167
199
|
}
|
|
@@ -174,8 +206,35 @@ scanTemplatesInjected) {
|
|
|
174
206
|
let current = 0;
|
|
175
207
|
try {
|
|
176
208
|
let totalFiles = 0;
|
|
209
|
+
const targetRoot = createContentRoot(targetStorage);
|
|
210
|
+
// History must record BEFORE the writes so the baseline
|
|
211
|
+
// revision (emitted automatically by recordWrite on the first
|
|
212
|
+
// call against this target) captures pre-publish state.
|
|
213
|
+
// Otherwise "undo this publish" would restore the post-
|
|
214
|
+
// publish state and no-op. See pages.ts save handler.
|
|
215
|
+
if (config && isHistoryEnabled(config)) {
|
|
216
|
+
try {
|
|
217
|
+
const history = createHistoryProvider({
|
|
218
|
+
storage: targetStorage,
|
|
219
|
+
retention: getHistoryRetention(config),
|
|
220
|
+
});
|
|
221
|
+
const items = await collectPublishedItemsForHistory(source.contentRoot, targetRoot, targetItems);
|
|
222
|
+
await recordWrite({
|
|
223
|
+
history,
|
|
224
|
+
contentRoot: targetRoot,
|
|
225
|
+
operation: 'publish',
|
|
226
|
+
source: sourceName,
|
|
227
|
+
items,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
// History is a best-effort audit layer — a write failure
|
|
232
|
+
// here must not break the publish itself.
|
|
233
|
+
console.warn(` ${targetName}: history record failed — ${err.message}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
177
236
|
// 1. Source copy
|
|
178
|
-
const { copiedFiles } = await publishItems(
|
|
237
|
+
const { copiedFiles } = await publishItems(source.contentRoot, targetRoot, targetItems);
|
|
179
238
|
totalFiles += copiedFiles;
|
|
180
239
|
current++;
|
|
181
240
|
yield { kind: 'progress', target: targetName, current, total, label: 'source files' };
|
|
@@ -194,22 +253,33 @@ scanTemplatesInjected) {
|
|
|
194
253
|
}
|
|
195
254
|
}
|
|
196
255
|
const pageHashOpts = isStatic ? { templateHashes, fragmentHashes } : { templateHashes };
|
|
256
|
+
// SEO context for this target — built once, shared across all page renders.
|
|
257
|
+
const seo = {
|
|
258
|
+
siteName: site.manifest.name,
|
|
259
|
+
siteUrl: config?.siteUrl,
|
|
260
|
+
locale: site.manifest.locale,
|
|
261
|
+
defaultOgImage: site.manifest.defaultOgImage,
|
|
262
|
+
};
|
|
197
263
|
const renderItem = async (item) => {
|
|
198
264
|
if (item.startsWith('pages/')) {
|
|
199
|
-
const
|
|
265
|
+
const raw = item.replace('pages/', '');
|
|
266
|
+
// Support locale-qualified items: pages/home:fr → publish only FR
|
|
267
|
+
const [pageName, itemLocale] = raw.includes(':') ? raw.split(':') : [raw, undefined];
|
|
200
268
|
const page = site.pages.get(pageName);
|
|
201
269
|
const manifestHash = page ? hashManifest(page, pageHashOpts) : undefined;
|
|
202
270
|
if (isStatic) {
|
|
203
|
-
return publishPageStatic(pageName,
|
|
271
|
+
return publishPageStatic(pageName, source.contentRoot, targetStorage, tdir, manifestHash, site, seo, itemLocale);
|
|
204
272
|
}
|
|
205
|
-
|
|
273
|
+
// When a specific locale is requested, only publish that one
|
|
274
|
+
const onlyLocales = itemLocale ? [itemLocale] : undefined;
|
|
275
|
+
const { files } = await publishPageAllLocales(pageName, source.contentRoot, targetStorage, site, pageHashOpts, { cache: config?.cache, templatesDir: tdir, seo, targetLocales: onlyLocales ?? config?.locales });
|
|
206
276
|
return { files };
|
|
207
277
|
}
|
|
208
278
|
if (item.startsWith('fragments/') && !isStatic) {
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const { files } = await
|
|
279
|
+
const raw = item.replace('fragments/', '');
|
|
280
|
+
const [fragName, itemLocale] = raw.includes(':') ? raw.split(':') : [raw, undefined];
|
|
281
|
+
const onlyLocales = itemLocale ? [itemLocale] : undefined;
|
|
282
|
+
const { files } = await publishFragmentAllLocales(fragName, source.contentRoot, targetStorage, site, { templateHashes }, { templatesDir: tdir, targetLocales: onlyLocales ?? config?.locales });
|
|
213
283
|
return { files };
|
|
214
284
|
}
|
|
215
285
|
return { files: 0 }; // skipped (e.g. fragment on static target)
|
|
@@ -222,15 +292,46 @@ scanTemplatesInjected) {
|
|
|
222
292
|
yield { kind: 'progress', target: targetName, current, total, label: item };
|
|
223
293
|
}
|
|
224
294
|
// 3. Site manifest + fragment index
|
|
225
|
-
await publishSiteManifest(
|
|
226
|
-
await publishFragmentIndex(
|
|
295
|
+
await publishSiteManifest(source.contentRoot, targetStorage, site);
|
|
296
|
+
await publishFragmentIndex(source.contentRoot, targetStorage, site);
|
|
227
297
|
totalFiles += 2;
|
|
228
298
|
current++;
|
|
229
299
|
yield { kind: 'progress', target: targetName, current, total, label: 'site manifest' };
|
|
300
|
+
// 3b. Sitemap + robots.txt
|
|
301
|
+
const siteUrl = config?.siteUrl;
|
|
302
|
+
if (siteUrl) {
|
|
303
|
+
const { listSidecars } = await import('../../sidecars.js');
|
|
304
|
+
const { generateSitemap } = await import('../../sitemap.js');
|
|
305
|
+
const { generateRobotsTxt } = await import('../../robots.js');
|
|
306
|
+
const targetPageSidecars = await listSidecars(targetStorage, 'pages');
|
|
307
|
+
const sitemapXml = generateSitemap({
|
|
308
|
+
siteUrl,
|
|
309
|
+
pages: targetPageSidecars,
|
|
310
|
+
systemPages: site.manifest.systemPages,
|
|
311
|
+
});
|
|
312
|
+
if (sitemapXml) {
|
|
313
|
+
await targetStorage.writeFile('sitemap.xml', sitemapXml);
|
|
314
|
+
totalFiles++;
|
|
315
|
+
}
|
|
316
|
+
// robots.txt only at domain root — Google ignores it at subpaths.
|
|
317
|
+
const isRootDeploy = !new URL(siteUrl).pathname.replace(/\/+$/, '');
|
|
318
|
+
if (isRootDeploy) {
|
|
319
|
+
let robotsTxt;
|
|
320
|
+
try {
|
|
321
|
+
robotsTxt = await source.contentRoot.storage.readFile(source.contentRoot.path('robots.txt'));
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
robotsTxt = generateRobotsTxt({ siteUrl });
|
|
325
|
+
}
|
|
326
|
+
await targetStorage.writeFile('robots.txt', robotsTxt);
|
|
327
|
+
totalFiles++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
230
330
|
// 4. Purge CDN cache
|
|
231
331
|
if (purgeConfig?.type === 'cloudflare') {
|
|
232
332
|
const apiToken = resolveEnvVars(purgeConfig.apiToken);
|
|
233
|
-
const zoneId = resolveEnvVars(purgeConfig.zoneId) ??
|
|
333
|
+
const zoneId = resolveEnvVars(purgeConfig.zoneId) ??
|
|
334
|
+
(config?.siteUrl && apiToken ? await lookupCloudflareZoneId(config.siteUrl, apiToken) : null);
|
|
234
335
|
if (apiToken && zoneId) {
|
|
235
336
|
const purge = createCloudflarePurge(zoneId, apiToken);
|
|
236
337
|
const hasFragments = targetItems.some(i => i.startsWith('fragments/'));
|
|
@@ -239,14 +340,16 @@ scanTemplatesInjected) {
|
|
|
239
340
|
console.log(` ${targetName}: cache purged (all)`);
|
|
240
341
|
}
|
|
241
342
|
else if (config?.siteUrl) {
|
|
242
|
-
const siteForUrls = await
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
343
|
+
const siteForUrls = await loadSiteFromSource(source, { templatesDir });
|
|
344
|
+
const publishedPageNames = new Set(targetItems.filter(i => i.startsWith('pages/')).map(i => i.replace('pages/', '')));
|
|
345
|
+
// Collect URLs for all published pages including locale variants
|
|
346
|
+
const urls = [];
|
|
347
|
+
const { allPageEntries } = await import('../../site-loader.js');
|
|
348
|
+
for (const { name, page } of allPageEntries(siteForUrls)) {
|
|
349
|
+
if (publishedPageNames.has(name)) {
|
|
350
|
+
urls.push(`${config.siteUrl}${page.route}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
250
353
|
if (urls.length > 0) {
|
|
251
354
|
await purge.purgeUrls(urls);
|
|
252
355
|
console.log(` ${targetName}: cache purged (${urls.join(', ')})`);
|
|
@@ -272,10 +375,10 @@ scanTemplatesInjected) {
|
|
|
272
375
|
yield { kind: 'done', results };
|
|
273
376
|
}
|
|
274
377
|
app.post('/api/publish', async (c) => {
|
|
275
|
-
const body = await c.req.json();
|
|
378
|
+
const body = (await c.req.json());
|
|
276
379
|
let results = [];
|
|
277
380
|
let fatal = null;
|
|
278
|
-
for await (const ev of runPublish(body.items, body.targets)) {
|
|
381
|
+
for await (const ev of runPublish(body.items, body.targets, body.source)) {
|
|
279
382
|
if (ev.kind === 'fatal')
|
|
280
383
|
fatal = ev;
|
|
281
384
|
else if (ev.kind === 'done')
|
|
@@ -289,10 +392,10 @@ scanTemplatesInjected) {
|
|
|
289
392
|
return c.json({ results }, allSuccess ? 200 : 207);
|
|
290
393
|
});
|
|
291
394
|
app.post('/api/publish/stream', async (c) => {
|
|
292
|
-
const body = await c.req.json();
|
|
395
|
+
const body = (await c.req.json());
|
|
293
396
|
return streamSSE(c, async (stream) => {
|
|
294
397
|
try {
|
|
295
|
-
for await (const ev of runPublish(body.items, body.targets)) {
|
|
398
|
+
for await (const ev of runPublish(body.items, body.targets, body.source)) {
|
|
296
399
|
if (stream.aborted)
|
|
297
400
|
return;
|
|
298
401
|
await stream.writeSSE({ event: ev.kind, data: JSON.stringify(ev) });
|
|
@@ -300,19 +403,33 @@ scanTemplatesInjected) {
|
|
|
300
403
|
}
|
|
301
404
|
catch (err) {
|
|
302
405
|
if (!stream.aborted) {
|
|
303
|
-
await stream.writeSSE({
|
|
406
|
+
await stream.writeSSE({
|
|
407
|
+
event: 'fatal',
|
|
408
|
+
data: JSON.stringify({ kind: 'fatal', error: err.message }),
|
|
409
|
+
});
|
|
304
410
|
}
|
|
305
411
|
}
|
|
306
412
|
});
|
|
307
413
|
});
|
|
308
414
|
app.post('/api/fetch', async (c) => {
|
|
309
|
-
|
|
415
|
+
// `source` (body) — target to fetch FROM (a published target)
|
|
416
|
+
// `destination` (body) — optional editable target to write INTO; defaults
|
|
417
|
+
// to the resolver's default editable target (the author's current source)
|
|
418
|
+
const body = (await c.req.json());
|
|
310
419
|
if (!body.source)
|
|
311
420
|
return c.json({ error: 'Missing "source" target name' }, 400);
|
|
312
421
|
const t = await getTargets();
|
|
313
422
|
const targetStorage = t.get(body.source);
|
|
314
423
|
if (!targetStorage)
|
|
315
424
|
return c.json({ error: `Unknown target: ${body.source}` }, 400);
|
|
425
|
+
// Resolve the destination editable target for this fetch.
|
|
426
|
+
let destination;
|
|
427
|
+
try {
|
|
428
|
+
destination = await resolve(body.destination);
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
return c.json({ error: err.message }, 400);
|
|
432
|
+
}
|
|
316
433
|
let items;
|
|
317
434
|
if (body.items?.length) {
|
|
318
435
|
items = body.items;
|
|
@@ -351,7 +468,8 @@ scanTemplatesInjected) {
|
|
|
351
468
|
console.log(` Fetching ${items.length} items from "${body.source}":`);
|
|
352
469
|
console.log(` Items: ${items.join(', ')}`);
|
|
353
470
|
try {
|
|
354
|
-
const
|
|
471
|
+
const targetRoot = createContentRoot(targetStorage);
|
|
472
|
+
const { copiedFiles } = await publishItems(targetRoot, destination.contentRoot, items);
|
|
355
473
|
console.log(` ${copiedFiles} files copied to working copy`);
|
|
356
474
|
return c.json({ success: true, copiedFiles, items });
|
|
357
475
|
}
|
|
@@ -363,4 +481,33 @@ scanTemplatesInjected) {
|
|
|
363
481
|
});
|
|
364
482
|
return app;
|
|
365
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Build the history `items` for a publish — one entry per published
|
|
486
|
+
* item, content = the source-side manifest. Records semantic authored
|
|
487
|
+
* state (JSON manifests) rather than target-side artifacts (static
|
|
488
|
+
* HTML, fragment indexes), so Restore is a content-level operation.
|
|
489
|
+
*
|
|
490
|
+
* `_targetRoot` is accepted for symmetry with `recordWrite`'s other
|
|
491
|
+
* path-building needs; it's unused today because we hash source
|
|
492
|
+
* content directly and let recordWrite overlay paths relative to the
|
|
493
|
+
* target's rootPath (which is `''` for target-rooted storage, the
|
|
494
|
+
* common case).
|
|
495
|
+
*/
|
|
496
|
+
async function collectPublishedItemsForHistory(sourceRoot, _targetRoot, publishedItems) {
|
|
497
|
+
const out = [];
|
|
498
|
+
for (const item of publishedItems) {
|
|
499
|
+
const manifestName = item.startsWith('pages/') ? 'page.json' : 'fragment.json';
|
|
500
|
+
const key = `${item}/${manifestName}`;
|
|
501
|
+
const sourcePath = sourceRoot.path(key);
|
|
502
|
+
try {
|
|
503
|
+
const content = await sourceRoot.storage.readFile(sourcePath);
|
|
504
|
+
out.push({ path: key, content });
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
// Item missing on source (unusual — publish normally reads from
|
|
508
|
+
// source manifests). Skip; snapshot stays as-was for this item.
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return out;
|
|
512
|
+
}
|
|
366
513
|
//# sourceMappingURL=publish.js.map
|