@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 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 ?? error}). ` +
464
- `Local preview only — live-domain/editor views + draft sync disabled.`,
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
- syncDraft: Boolean(session),
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
- const session = await fetchDevSession({ url: creds.url, token: creds.token });
590
- instance = session.draftInstanceId;
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(
@@ -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
- body: JSON.stringify({ path: pathname, templates }),
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 {
@@ -25,10 +25,14 @@ function canonicalHeaders(extra = {}) {
25
25
  async function errorText(res) {
26
26
  const text = await res.text();
27
27
  try {
28
- return JSON.parse(text).error ?? text;
28
+ const parsed = JSON.parse(text).error;
29
+ if (parsed) return parsed;
29
30
  } catch {
30
- return text;
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) throw new Error(await errorText(res));
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocofy/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Develop your Blocofy theme locally against live data with instant preview — login, theme dev, pull, push.",
5
5
  "type": "module",
6
6
  "license": "MIT",