@blocofy/cli 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/README.md +13 -0
- package/bin/blocofy.mjs +27 -6
- package/lib/dev-server.mjs +39 -8
- package/lib/theme-sync.mjs +19 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,19 @@ blocofy --help
|
|
|
33
33
|
|
|
34
34
|
## Changelog
|
|
35
35
|
|
|
36
|
+
- **0.6.0** — `theme dev`'s local view finally shows what you are working on. It used to render your
|
|
37
|
+
DRAFT theme against the site's LIVE page content, so draft page documents were invisible; each render
|
|
38
|
+
now carries the draft theme instance and the platform resolves that instance's pages. Three failures
|
|
39
|
+
caused by a retired server endpoint (`/api/dev/session`, gone since the platform's preview-security
|
|
40
|
+
work) are fixed at the root: **draft sync** was switched off whenever that endpoint failed even though
|
|
41
|
+
sync never used it (it posts to `/api/dev/theme`); **`theme publish`** without `--instance` called it
|
|
42
|
+
with no error handling and died with an empty message — it now resolves the CLI draft from
|
|
43
|
+
`blocofy status` (`source: "import"`); and an empty response body produced the undiagnosable
|
|
44
|
+
`dev session unavailable ()` — errors now fall back to `HTTP <status>` and a 410 explains itself.
|
|
45
|
+
If the server ever stops returning a draft handle, the CLI now says so instead of silently rendering
|
|
46
|
+
live content. Remote (shared-link and editor) preview is NOT restored — that surface was retired on
|
|
47
|
+
the platform side; `theme dev` prints the local view only.
|
|
48
|
+
|
|
36
49
|
- **0.5.0** — M4 canonical deploy protocol. `theme push` now deploys through the platform's atomic
|
|
37
50
|
source pipeline: the CLI declares the protocol handshake and auto-generates a per-push idempotency
|
|
38
51
|
key (transport retries converge; `--idempotency-key <k>` overrides it for scripting). A `--live`
|
package/bin/blocofy.mjs
CHANGED
|
@@ -460,8 +460,8 @@ async function themeDev(rest) {
|
|
|
460
460
|
session = await fetchDevSession({ url: creds.url, token: creds.token, name });
|
|
461
461
|
} catch (error) {
|
|
462
462
|
console.warn(
|
|
463
|
-
`Warning: dev session unavailable (${error?.message
|
|
464
|
-
`
|
|
463
|
+
`Warning: dev session unavailable (${error?.message || error}). ` +
|
|
464
|
+
`Live-domain and editor views are disabled; draft sync and local preview keep working.`,
|
|
465
465
|
);
|
|
466
466
|
}
|
|
467
467
|
}
|
|
@@ -523,8 +523,12 @@ async function themeDev(rest) {
|
|
|
523
523
|
url: creds.url,
|
|
524
524
|
token: creds.token,
|
|
525
525
|
port,
|
|
526
|
-
|
|
526
|
+
// Taslak senkronu dev session'a BAĞLI DEĞİL: `pushTheme({draft:true})` /api/dev/theme'e gider ve
|
|
527
|
+
// session'dan hiçbir veri kullanmaz. Bu satır `Boolean(session)` iken, session 410 alınca senkron
|
|
528
|
+
// da sessizce kapanıyordu — ölü bir uç, çalışan bir özelliği götürüyordu. Tek kapatma yolu --no-sync.
|
|
529
|
+
syncDraft: !flags["no-sync"],
|
|
527
530
|
onRetry: (info) => console.error(` ${retryNotice(info)}`),
|
|
531
|
+
onWarn: (msg) => console.warn(` ⚠ ${msg}`),
|
|
528
532
|
// Her kaydetmede ne olduğunu bas — "reloaded" = watch tetiklendi; "0 views"
|
|
529
533
|
// = hiçbir tarayıcı sekmesi bağlı değil (yanlış görünüme bakıyorsun); sync
|
|
530
534
|
// hatası = draft güncellenemedi (preview/editör eski kalır, local yine yenilenir).
|
|
@@ -585,9 +589,26 @@ async function themePublish(rest) {
|
|
|
585
589
|
const creds = requireCreds();
|
|
586
590
|
let instance = typeof flags.instance === "string" ? flags.instance : null;
|
|
587
591
|
if (!instance) {
|
|
588
|
-
// Belirtilmediyse: `theme dev` / `theme push --draft`'ın yazdığı taslağı yayınla.
|
|
589
|
-
|
|
590
|
-
|
|
592
|
+
// Belirtilmediyse: `theme dev` / `theme push --draft`'ın yazdığı taslağı yayınla. Kaynak
|
|
593
|
+
// `GET /api/dev/site` — eski `fetchDevSession` yolu sunucuda 410'a döndü ve bu komutu
|
|
594
|
+
// try/catch'siz, boş mesajlı bir Error ile tamamen çalışmaz hâle getirmişti.
|
|
595
|
+
//
|
|
596
|
+
// `drafts` canlı OLMAYAN HER instance'ı içerir; sunucunun `ensureDraftInstance`'ı ise
|
|
597
|
+
// `source === "import"` olanı seçer (yayınlanan taslak import'tan çıkarılır). Aynı seçimi
|
|
598
|
+
// burada tekrarla — yoksa bir kez yayın yapmış her sitede iki taslak görünür ve komut takılır.
|
|
599
|
+
const status = await fetchSiteStatus({ url: creds.url, token: creds.token });
|
|
600
|
+
const drafts = status?.drafts ?? [];
|
|
601
|
+
const cliDrafts = drafts.filter((d) => d.source === "import");
|
|
602
|
+
if (cliDrafts.length === 1) {
|
|
603
|
+
instance = cliDrafts[0].id;
|
|
604
|
+
} else if (drafts.length === 0) {
|
|
605
|
+
console.error("No draft theme to publish. Create one first: blocofy theme push --draft");
|
|
606
|
+
process.exit(1);
|
|
607
|
+
} else {
|
|
608
|
+
console.error("Could not tell which draft to publish — pick one with --instance <handle>:");
|
|
609
|
+
for (const d of drafts) console.error(` ${d.id} ${d.name ?? "(unnamed)"}`);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
591
612
|
}
|
|
592
613
|
const result = await publishInstance({ url: creds.url, token: creds.token, instanceId: instance });
|
|
593
614
|
console.log(
|
package/lib/dev-server.mjs
CHANGED
|
@@ -20,16 +20,52 @@ import { pushTheme } from "./theme-sync.mjs";
|
|
|
20
20
|
* separate request); direct `/asset/*` references are served from disk. Images/JS
|
|
21
21
|
* are remote (Directus/CDN), so no local serving is needed for them.
|
|
22
22
|
*/
|
|
23
|
-
export function startDevServer({ dir, url, token, port, syncDraft = false, onError, onReload, onRetry }) {
|
|
23
|
+
export function startDevServer({ dir, url, token, port, syncDraft = false, onError, onReload, onRetry, onWarn }) {
|
|
24
24
|
const base = url.replace(/\/+$/, "");
|
|
25
25
|
const clients = new Set();
|
|
26
26
|
|
|
27
|
+
// Taslak instance handle'ı: `pushTheme({draft:true})` yanıtından gelir (ek istek YOK). Render
|
|
28
|
+
// istekleri bunu taşıyınca platform CANLI sayfa belgeleri yerine TASLAĞIN sayfa belgelerini
|
|
29
|
+
// çözer — `theme dev`'in yerel görünümü nihayet üzerinde çalışılan şeyi gösterir.
|
|
30
|
+
let draftInstance = null;
|
|
31
|
+
// Kanonik yazma yolu ham control-plane JSON'u döndürür ve `instanceId` içermeyebilir. O durumda
|
|
32
|
+
// sessizce canlıya dönmek, bu onarımın kapattığı arıza sınıfının aynısı olurdu — bir kez uyar.
|
|
33
|
+
let warnedNoHandle = false;
|
|
34
|
+
const noteHandle = (pushed) => {
|
|
35
|
+
if (pushed?.instanceId) {
|
|
36
|
+
draftInstance = pushed.instanceId;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!warnedNoHandle) {
|
|
40
|
+
warnedNoHandle = true;
|
|
41
|
+
onWarn?.(
|
|
42
|
+
"the server did not return a draft theme handle — local preview will render LIVE page content, not your draft.",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Açılıştaki push'u BİR promise olarak tut: ilk render isteği bunu bekler, yoksa ilk sayfa
|
|
48
|
+
// yüklemesi canlı içerikle gelir ve geliştirici "taslağım görünmüyor" sanır.
|
|
49
|
+
const draftReady = syncDraft
|
|
50
|
+
? pushTheme({ dir, url, token, draft: true, onRetry })
|
|
51
|
+
.then(noteHandle)
|
|
52
|
+
.catch(() => {})
|
|
53
|
+
: Promise.resolve();
|
|
54
|
+
|
|
27
55
|
async function renderPath(pathname) {
|
|
56
|
+
// Açılış push'u bitene kadar bekle — yalnız ilk istekte gerçekten bekler, sonra çözülmüş promise.
|
|
57
|
+
if (!draftInstance) await draftReady;
|
|
28
58
|
const templates = readLocalTemplates(dir);
|
|
29
59
|
const res = await fetch(`${base}/api/dev/render`, {
|
|
30
60
|
method: "POST",
|
|
31
61
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
32
|
-
|
|
62
|
+
// `preview` BİLİNÇLE gönderilmez: sunucuda preview yalnız tazelik değil — önizleme bandı,
|
|
63
|
+
// editör bridge'i ve blok işaretleyicileri de ona bağlı. `theme dev`'in vaadi prod paritesi.
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
path: pathname,
|
|
66
|
+
templates,
|
|
67
|
+
...(draftInstance ? { instance: draftInstance } : {}),
|
|
68
|
+
}),
|
|
33
69
|
});
|
|
34
70
|
const text = await res.text();
|
|
35
71
|
if (!res.ok) {
|
|
@@ -100,7 +136,7 @@ export function startDevServer({ dir, url, token, port, syncDraft = false, onErr
|
|
|
100
136
|
// BİLİNÇLE key'siz (0.5.0): dev sync her kayıtta koşar — kanonik mühür (key'li dal) kayıt
|
|
101
137
|
// başına revizyon+deployment üretirdi (retention yok) ve taslak önizlemeye katkısı sıfır.
|
|
102
138
|
// Key'siz istek sunucuda legacy draft yazımıdır; kanonik mühür bilinçli `theme push` işidir.
|
|
103
|
-
await pushTheme({ dir, url, token, draft: true, onRetry });
|
|
139
|
+
noteHandle(await pushTheme({ dir, url, token, draft: true, onRetry }));
|
|
104
140
|
} catch (err) {
|
|
105
141
|
error = err?.message ?? String(err); // senkron hatası reload'u engellemesin
|
|
106
142
|
}
|
|
@@ -109,11 +145,6 @@ export function startDevServer({ dir, url, token, port, syncDraft = false, onErr
|
|
|
109
145
|
onReload?.({ file, synced: syncDraft && !error, clients: clients.size, error });
|
|
110
146
|
});
|
|
111
147
|
|
|
112
|
-
// Açılışta taslağı bir kez doldur → uzak görünümler hemen güncel local'i gösterir.
|
|
113
|
-
if (syncDraft) {
|
|
114
|
-
void pushTheme({ dir, url, token, draft: true, onRetry }).catch(() => {});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
148
|
server.listen(port);
|
|
118
149
|
|
|
119
150
|
return {
|
package/lib/theme-sync.mjs
CHANGED
|
@@ -25,10 +25,14 @@ function canonicalHeaders(extra = {}) {
|
|
|
25
25
|
async function errorText(res) {
|
|
26
26
|
const text = await res.text();
|
|
27
27
|
try {
|
|
28
|
-
|
|
28
|
+
const parsed = JSON.parse(text).error;
|
|
29
|
+
if (parsed) return parsed;
|
|
29
30
|
} catch {
|
|
30
|
-
|
|
31
|
+
/* JSON değil — ham metne düş */
|
|
31
32
|
}
|
|
33
|
+
// İçeriksiz yanıt (ör. 410 tombstone) boş dize döndürüp `Error("")` üretiyordu; kullanıcı
|
|
34
|
+
// `unavailable ()` görüyordu. Gövde yoksa statü tek teşhis kaynağıdır — onu taşı.
|
|
35
|
+
return text || `HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : ""}`;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
/**
|
|
@@ -158,7 +162,19 @@ export async function fetchDevSession({ url, token, name = null }) {
|
|
|
158
162
|
const res = await fetch(`${base}/api/dev/session${query}`, {
|
|
159
163
|
headers: { authorization: `Bearer ${token}` },
|
|
160
164
|
});
|
|
161
|
-
if (!res.ok)
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
const detail = await errorText(res);
|
|
167
|
+
// Yeni sunucular 410'da açıklayıcı bir gövde döndürür — onu olduğu gibi göster, o otoritedir.
|
|
168
|
+
// Gövdesiz 410 (eski sunucu) `errorText`'ten "HTTP 410 …" olarak gelir; o durumda teşhisi
|
|
169
|
+
// biz veririz, yoksa kullanıcı yalnız bir statü kodu görür.
|
|
170
|
+
if (res.status === 410 && /^HTTP 410\b/.test(detail)) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"CLI remote preview (the signed preview URL and the editor view) is retired on this server " +
|
|
173
|
+
"(HTTP 410). Draft sync and `theme publish` are unaffected — they use different endpoints.",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
throw new Error(detail);
|
|
177
|
+
}
|
|
162
178
|
return res.json();
|
|
163
179
|
}
|
|
164
180
|
|