@calo-design/cli 0.11.0 → 0.12.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 (2) hide show
  1. package/bin/mirror-push.js +40 -4
  2. package/package.json +1 -1
@@ -102,11 +102,24 @@ function extractEasGroup(captured) {
102
102
  return found ? found[1] : undefined;
103
103
  }
104
104
 
105
+ // The per-version EAS branch/channel name. Deterministic from (slug, group) so any
106
+ // party — CLI, broker page, Mirror launcher — can derive it, but it's also stored in
107
+ // artifact.easBranch so the convention can evolve without re-deriving. EAS channel
108
+ // names share the slug charset; slug ≤63 + "--g" + 8 = ≤74, under EAS's limits.
109
+ // A hoisted `function` on purpose: it's called from cmdPush, which runs ABOVE this
110
+ // definition — a `const` here sat in the TDZ and silently killed the pin (the
111
+ // best-effort catch ate the ReferenceError; caught only by live-push verification).
112
+ function versionBranchOf(slug, group) {
113
+ return `${slug}--g${String(group).slice(0, 8).toLowerCase()}`;
114
+ }
115
+
105
116
  function runtimeDir() {
106
117
  const home = process.env.DESIGNCHEF_HOME || path.join(os.homedir(), ".designchef");
107
118
  return path.join(home, "runtime");
108
119
  }
109
- const slugify = (s) => String(s).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "prototype";
120
+ // Capped at 63 (SLUG_RE's bound everywhere else) an uncapped slug would silently
121
+ // break the checkpoint stamp and overflow EAS channel-name limits on version pins.
122
+ const slugify = (s) => String(s).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63).replace(/-+$/, "") || "prototype";
110
123
  const titleize = (s) => String(s).replace(/[-_]+/g, " ").replace(/\b\w/g, (m) => m.toUpperCase());
111
124
 
112
125
  function appDir(root) {
@@ -854,6 +867,7 @@ async function cmdPush(args) {
854
867
  // capture is best-effort (a quick update:list after the publish); so is the whole
855
868
  // stamp — never fails the push. (Lazy require: mirror-push is also loaded by art-push.)
856
869
  let easGroup;
870
+ let easBranch;
857
871
  try {
858
872
  // easCapture → { status, out }. Tolerant extraction: eas --json shapes (and
859
873
  // banner noise) vary by version, so regex the output for the first uuid-shaped
@@ -861,14 +875,36 @@ async function cmdPush(args) {
861
875
  // lists the branch head, so a concurrent push to the same slug in this window
862
876
  // could stamp the other push's group — rare, and a stamp is advisory metadata.
863
877
  easGroup = extractEasGroup(easCapture(["update:list", "--branch", slug, "--limit", "1", "--json", "--non-interactive"], { cwd: stage }));
878
+ if (easGroup) {
879
+ // Pin this exact version behind its own channel: `slug--g<group[:8]>`.
880
+ // republish is a metadata copy (no rebundle); ensureChannel links channel →
881
+ // branch of the same name, which is what the Mirror's channel-name header
882
+ // switch loads. This is what makes "open v3 on your phone" possible after
883
+ // the channel head (slug) has moved on. Best-effort like the whole stamp.
884
+ const vb = versionBranchOf(slug, easGroup);
885
+ ensureChannel(stage, vb);
886
+ const rp = easCapture(
887
+ ["update:republish", "--group", easGroup, "--destination-branch", vb, "-m", `pin ${slug} @ ${easGroup.slice(0, 8)}`, "--non-interactive"],
888
+ { cwd: stage }
889
+ );
890
+ if (rp.status === 0) easBranch = vb;
891
+ else warn(`version pin skipped (republish → ${rp.out.trim().split("\n").slice(-1)[0] || rp.status})`);
892
+ }
864
893
  } catch {
865
894
  /* no artifact ref — the stamp still records source + deep link */
866
895
  }
867
896
  await require("./checkpoints").autoCheckpoint({
868
897
  root: process.cwd(),
869
- note: flag(args, "note") || `pushed to the Mirror (${slug})`,
898
+ // NB: mirror-push's flag() takes the full "--name" (unlike share.js's bare-name helper).
899
+ note: flag(args, "--note") || `pushed to the Mirror (${slug})`,
870
900
  publishedUrl: `designchef://open/${slug}`,
871
- artifact: { kind: "mirror", channel: slug, runtimeVersion: RUNTIME_VERSION, ...(easGroup ? { easGroup } : {}) },
901
+ artifact: {
902
+ kind: "mirror",
903
+ channel: slug,
904
+ runtimeVersion: RUNTIME_VERSION,
905
+ ...(easGroup ? { easGroup } : {}),
906
+ ...(easBranch ? { easBranch } : {}),
907
+ },
872
908
  });
873
909
 
874
910
  // Share target: the deep link the Mirror scanner (and iOS camera) opens.
@@ -889,4 +925,4 @@ async function cmdPush(args) {
889
925
  }
890
926
  }
891
927
 
892
- module.exports = { cmdPush, _s3Put: s3Put, _upsertRegistry: upsertRegistry, _publicBase: PUBLIC_BASE, _extractEasGroup: extractEasGroup };
928
+ module.exports = { cmdPush, _s3Put: s3Put, _upsertRegistry: upsertRegistry, _publicBase: PUBLIC_BASE, _extractEasGroup: extractEasGroup, _versionBranchOf: versionBranchOf };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calo-design/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "One-line setup for Calo design tooling: logs in with your Calo email and installs the calo-design skill + design-system packages. No GitHub account needed.",
5
5
  "bin": {
6
6
  "calo-design": "bin/cli.js"