@micacms/runtime 0.6.0-rc.1 → 0.6.0-rc.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"file-type.d.ts","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CA2CrE;AA2BD,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,KAAG,OAAoC,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,KAAK,GAAG,OAAO,CAI1F;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,kBAAkB,CAAC"}
1
+ {"version":3,"file":"file-type.d.ts","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CA2CrE;AAqBD,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,KAAG,OAAoC,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,KAAK,GAAG,OAAO,CAI1F;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,kBAAkB,CAAC"}
@@ -58,14 +58,8 @@ function isSvg(bytes) {
58
58
  }
59
59
  return false;
60
60
  }
61
- const IMAGE_TYPES = new Set([
62
- "image/jpeg",
63
- "image/png",
64
- "image/gif",
65
- "image/webp",
66
- "image/avif",
67
- "image/heic",
68
- ]);
61
+ // No HEIC: Astro's image service cannot read it, so a site could not show it.
62
+ const IMAGE_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif"]);
69
63
  const DOCUMENT_TYPES = new Set(["application/pdf"]);
70
64
  export const isImageType = (mimeType) => IMAGE_TYPES.has(mimeType);
71
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"file-type.js","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Content sniffing for uploads.\n *\n * The browser-supplied MIME type is attacker-controlled and must not be\n * trusted: an executable renamed to `.jpg` arrives as `image/jpeg`. The stored\n * type is derived from the bytes instead, and the extension is regenerated\n * from it rather than taken from the filename.\n */\n\nexport interface DetectedType {\n mimeType: string;\n extension: string;\n}\n\nconst startsWith = (bytes: Uint8Array, signature: number[], offset = 0): boolean =>\n signature.every((byte, index) => bytes[offset + index] === byte);\n\nconst ascii = (bytes: Uint8Array, offset: number, length: number): string =>\n Array.from(bytes.slice(offset, offset + length), (byte) => String.fromCharCode(byte)).join(\"\");\n\nexport function detectFileType(bytes: Uint8Array): DetectedType | null {\n if (bytes.length < 12) return null;\n\n if (startsWith(bytes, [0xff, 0xd8, 0xff])) {\n return { mimeType: \"image/jpeg\", extension: \"jpg\" };\n }\n\n if (startsWith(bytes, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) {\n return { mimeType: \"image/png\", extension: \"png\" };\n }\n\n const header = ascii(bytes, 0, 6);\n if (header === \"GIF87a\" || header === \"GIF89a\") {\n return { mimeType: \"image/gif\", extension: \"gif\" };\n }\n\n if (ascii(bytes, 0, 4) === \"RIFF\" && ascii(bytes, 8, 4) === \"WEBP\") {\n return { mimeType: \"image/webp\", extension: \"webp\" };\n }\n\n // ISO base media formats declare their brand at offset 8, after the box size.\n if (ascii(bytes, 4, 4) === \"ftyp\") {\n const brand = ascii(bytes, 8, 4);\n if (brand === \"avif\" || brand === \"avis\") {\n return { mimeType: \"image/avif\", extension: \"avif\" };\n }\n if (brand === \"heic\" || brand === \"heix\" || brand === \"mif1\") {\n return { mimeType: \"image/heic\", extension: \"heic\" };\n }\n if (brand === \"isom\" || brand === \"mp42\" || brand === \"M4V \") {\n return { mimeType: \"video/mp4\", extension: \"mp4\" };\n }\n }\n\n if (ascii(bytes, 0, 5) === \"%PDF-\") {\n return { mimeType: \"application/pdf\", extension: \"pdf\" };\n }\n\n if (isSvg(bytes)) {\n return { mimeType: \"image/svg+xml\", extension: \"svg\" };\n }\n\n return null;\n}\n\n/** SVG has no magic number, so look for a root `<svg>` in the leading text. */\nfunction isSvg(bytes: Uint8Array): boolean {\n const head = new TextDecoder(\"utf-8\", { fatal: false })\n .decode(bytes.slice(0, 1024))\n .trimStart()\n .toLowerCase();\n\n if (head.startsWith(\"<svg\")) return true;\n if (head.startsWith(\"<?xml\") || head.startsWith(\"<!doctype svg\")) {\n return head.includes(\"<svg\");\n }\n return false;\n}\n\nconst IMAGE_TYPES = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/avif\",\n \"image/heic\",\n]);\n\nconst DOCUMENT_TYPES = new Set([\"application/pdf\"]);\n\nexport const isImageType = (mimeType: string): boolean => IMAGE_TYPES.has(mimeType);\n\n/**\n * Whether a detected type is acceptable for a field's `accept` setting.\n * `any` deliberately does not mean \"anything\": only the types listed here can\n * be stored at all.\n */\nexport function isAccepted(mimeType: string, accept: \"image\" | \"document\" | \"any\"): boolean {\n if (accept === \"image\") return IMAGE_TYPES.has(mimeType);\n if (accept === \"document\") return DOCUMENT_TYPES.has(mimeType);\n return IMAGE_TYPES.has(mimeType) || DOCUMENT_TYPES.has(mimeType) || mimeType === \"video/mp4\";\n}\n\n/**\n * SVG can carry scripts, so Mica neither stores nor shows it (#86). It is still\n * detected, so that the refusal can say why.\n */\nexport const SVG_TYPE = \"image/svg+xml\";\n"]}
1
+ {"version":3,"file":"file-type.js","sourceRoot":"","sources":["../../src/lib/file-type.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Content sniffing for uploads.\n *\n * The browser-supplied MIME type is attacker-controlled and must not be\n * trusted: an executable renamed to `.jpg` arrives as `image/jpeg`. The stored\n * type is derived from the bytes instead, and the extension is regenerated\n * from it rather than taken from the filename.\n */\n\nexport interface DetectedType {\n mimeType: string;\n extension: string;\n}\n\nconst startsWith = (bytes: Uint8Array, signature: number[], offset = 0): boolean =>\n signature.every((byte, index) => bytes[offset + index] === byte);\n\nconst ascii = (bytes: Uint8Array, offset: number, length: number): string =>\n Array.from(bytes.slice(offset, offset + length), (byte) => String.fromCharCode(byte)).join(\"\");\n\nexport function detectFileType(bytes: Uint8Array): DetectedType | null {\n if (bytes.length < 12) return null;\n\n if (startsWith(bytes, [0xff, 0xd8, 0xff])) {\n return { mimeType: \"image/jpeg\", extension: \"jpg\" };\n }\n\n if (startsWith(bytes, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) {\n return { mimeType: \"image/png\", extension: \"png\" };\n }\n\n const header = ascii(bytes, 0, 6);\n if (header === \"GIF87a\" || header === \"GIF89a\") {\n return { mimeType: \"image/gif\", extension: \"gif\" };\n }\n\n if (ascii(bytes, 0, 4) === \"RIFF\" && ascii(bytes, 8, 4) === \"WEBP\") {\n return { mimeType: \"image/webp\", extension: \"webp\" };\n }\n\n // ISO base media formats declare their brand at offset 8, after the box size.\n if (ascii(bytes, 4, 4) === \"ftyp\") {\n const brand = ascii(bytes, 8, 4);\n if (brand === \"avif\" || brand === \"avis\") {\n return { mimeType: \"image/avif\", extension: \"avif\" };\n }\n if (brand === \"heic\" || brand === \"heix\" || brand === \"mif1\") {\n return { mimeType: \"image/heic\", extension: \"heic\" };\n }\n if (brand === \"isom\" || brand === \"mp42\" || brand === \"M4V \") {\n return { mimeType: \"video/mp4\", extension: \"mp4\" };\n }\n }\n\n if (ascii(bytes, 0, 5) === \"%PDF-\") {\n return { mimeType: \"application/pdf\", extension: \"pdf\" };\n }\n\n if (isSvg(bytes)) {\n return { mimeType: \"image/svg+xml\", extension: \"svg\" };\n }\n\n return null;\n}\n\n/** SVG has no magic number, so look for a root `<svg>` in the leading text. */\nfunction isSvg(bytes: Uint8Array): boolean {\n const head = new TextDecoder(\"utf-8\", { fatal: false })\n .decode(bytes.slice(0, 1024))\n .trimStart()\n .toLowerCase();\n\n if (head.startsWith(\"<svg\")) return true;\n if (head.startsWith(\"<?xml\") || head.startsWith(\"<!doctype svg\")) {\n return head.includes(\"<svg\");\n }\n return false;\n}\n\n// No HEIC: Astro's image service cannot read it, so a site could not show it.\nconst IMAGE_TYPES = new Set([\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\", \"image/avif\"]);\n\nconst DOCUMENT_TYPES = new Set([\"application/pdf\"]);\n\nexport const isImageType = (mimeType: string): boolean => IMAGE_TYPES.has(mimeType);\n\n/**\n * Whether a detected type is acceptable for a field's `accept` setting.\n * `any` deliberately does not mean \"anything\": only the types listed here can\n * be stored at all.\n */\nexport function isAccepted(mimeType: string, accept: \"image\" | \"document\" | \"any\"): boolean {\n if (accept === \"image\") return IMAGE_TYPES.has(mimeType);\n if (accept === \"document\") return DOCUMENT_TYPES.has(mimeType);\n return IMAGE_TYPES.has(mimeType) || DOCUMENT_TYPES.has(mimeType) || mimeType === \"video/mp4\";\n}\n\n/**\n * SVG can carry scripts, so Mica neither stores nor shows it (#86). It is still\n * detected, so that the refusal can say why.\n */\nexport const SVG_TYPE = \"image/svg+xml\";\n"]}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The policy for the developer's own checkout. Mica never fetches, merges or
3
3
  * resets here. Publish pushes with the developer's `git`, credentials and
4
- * upstream; a failed push keeps the commit.
4
+ * upstream; a new branch without one is pushed to `origin` and tracks it. A
5
+ * failed push keeps the commit.
5
6
  */
6
7
  import type { Logger } from "../lib/logger.js";
7
8
  import type { OwnerPolicy } from "./policy.js";
@@ -1 +1 @@
1
- {"version":3,"file":"developer.d.ts","sourceRoot":"","sources":["../../src/workspace/developer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAGpE,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CA0B5E"}
1
+ {"version":3,"file":"developer.d.ts","sourceRoot":"","sources":["../../src/workspace/developer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAGpE,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,WAAW,CA+B5E"}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The policy for the developer's own checkout. Mica never fetches, merges or
3
3
  * resets here. Publish pushes with the developer's `git`, credentials and
4
- * upstream; a failed push keeps the commit.
4
+ * upstream; a new branch without one is pushed to `origin` and tracks it. A
5
+ * failed push keeps the commit.
5
6
  */
6
7
  import { GitError } from "./git.js";
7
8
  export function developerPolicy(options) {
@@ -17,15 +18,28 @@ export function developerPolicy(options) {
17
18
  commitAuthor: () => undefined,
18
19
  async push(tree, revision) {
19
20
  try {
20
- await tree.git.run(["push"]);
21
+ // Publishing is the request to push, so a new branch gets its upstream here.
22
+ const upstream = await tree.git.tryRun(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]);
23
+ const remotes = (await tree.git.tryRun(["remote"]))?.split("\n").filter(Boolean) ?? [];
24
+ if (upstream === null && remotes.includes("origin"))
25
+ await tree.git.run(["push", "-u", "origin", "HEAD"]);
26
+ else if (upstream === null)
27
+ throw new Error(noUpstream(remotes));
28
+ else
29
+ await tree.git.run(["push"]);
21
30
  return { state: "published", revision };
22
31
  }
23
32
  catch (error) {
24
- const pushError = error instanceof GitError ? error.message : String(error);
33
+ const pushError = error instanceof GitError || error instanceof Error ? error.message : String(error);
25
34
  options.logger.warn("Publish committed but the push failed", { revision, pushError });
26
35
  return { state: "committed-pending-push", revision, pushError };
27
36
  }
28
37
  },
29
38
  };
30
39
  }
40
+ function noUpstream(remotes) {
41
+ return remotes.length === 0
42
+ ? "This repository has no remote. Add one with `git remote add origin <url>`, then publish again."
43
+ : `This branch has no upstream. Push it once with \`git push -u ${remotes[0]} HEAD\`, then publish again.`;
44
+ }
31
45
  //# sourceMappingURL=developer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"developer.js","sourceRoot":"","sources":["../../src/workspace/developer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAUpC,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,OAAO;QACL,KAAK,EAAE,WAAW;QAElB,mBAAmB;YACjB,OAAO,OAAO,CAAC,cAAc;gBAC3B,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;gBACnB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,oEAAoE,EAAE,CAAC;QACvG,CAAC;QAED,KAAK,CAAC,OAAO,KAAI,CAAC;QAElB,qDAAqD;QACrD,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAE7B,KAAK,CAAC,IAAI,CAAC,IAAa,EAAE,QAAgB;YACxC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC7B,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;YAC1C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,SAAS,GAAG,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;gBACtF,OAAO,EAAE,KAAK,EAAE,wBAAwB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YAClE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The policy for the developer's own checkout. Mica never fetches, merges or\n * resets here. Publish pushes with the developer's `git`, credentials and\n * upstream; a failed push keeps the commit.\n */\n\nimport type { PublishResult } from \"@micacms/core\";\n\nimport type { Logger } from \"../lib/logger.js\";\nimport { GitError } from \"./git.js\";\nimport type { OwnerPolicy, PublishAvailability } from \"./policy.js\";\nimport type { GitTree } from \"./tree.js\";\n\nexport interface DeveloperPolicyOptions {\n /** `MICA_LOCAL_PUBLISH`. */\n publishEnabled: boolean;\n logger: Logger;\n}\n\nexport function developerPolicy(options: DeveloperPolicyOptions): OwnerPolicy {\n return {\n owner: \"developer\",\n\n publishAvailability(): PublishAvailability {\n return options.publishEnabled\n ? { enabled: true }\n : { enabled: false, reason: \"Publishing from local mode is disabled (MICA_LOCAL_PUBLISH=false).\" };\n },\n\n async prepare() {},\n\n // The commit keeps the developer's own Git identity.\n commitAuthor: () => undefined,\n\n async push(tree: GitTree, revision: string): Promise<PublishResult> {\n try {\n await tree.git.run([\"push\"]);\n return { state: \"published\", revision };\n } catch (error) {\n const pushError = error instanceof GitError ? error.message : String(error);\n options.logger.warn(\"Publish committed but the push failed\", { revision, pushError });\n return { state: \"committed-pending-push\", revision, pushError };\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"developer.js","sourceRoot":"","sources":["../../src/workspace/developer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAUpC,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,OAAO;QACL,KAAK,EAAE,WAAW;QAElB,mBAAmB;YACjB,OAAO,OAAO,CAAC,cAAc;gBAC3B,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE;gBACnB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,oEAAoE,EAAE,CAAC;QACvG,CAAC;QAED,KAAK,CAAC,OAAO,KAAI,CAAC;QAElB,qDAAqD;QACrD,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAE7B,KAAK,CAAC,IAAI,CAAC,IAAa,EAAE,QAAgB;YACxC,IAAI,CAAC;gBACH,6EAA6E;gBAC7E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,cAAc,EAAE,sBAAsB,EAAE,aAAa,CAAC,CAAC,CAAC;gBAC7G,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACvF,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;oBAAE,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;qBACrG,IAAI,QAAQ,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;;oBAC5D,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBAClC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;YAC1C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,SAAS,GAAG,KAAK,YAAY,QAAQ,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACtG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;gBACtF,OAAO,EAAE,KAAK,EAAE,wBAAwB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YAClE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,OAAiB;IACnC,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC;QACzB,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,gEAAgE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;AAC/G,CAAC","sourcesContent":["/**\n * The policy for the developer's own checkout. Mica never fetches, merges or\n * resets here. Publish pushes with the developer's `git`, credentials and\n * upstream; a new branch without one is pushed to `origin` and tracks it. A\n * failed push keeps the commit.\n */\n\nimport type { PublishResult } from \"@micacms/core\";\n\nimport type { Logger } from \"../lib/logger.js\";\nimport { GitError } from \"./git.js\";\nimport type { OwnerPolicy, PublishAvailability } from \"./policy.js\";\nimport type { GitTree } from \"./tree.js\";\n\nexport interface DeveloperPolicyOptions {\n /** `MICA_LOCAL_PUBLISH`. */\n publishEnabled: boolean;\n logger: Logger;\n}\n\nexport function developerPolicy(options: DeveloperPolicyOptions): OwnerPolicy {\n return {\n owner: \"developer\",\n\n publishAvailability(): PublishAvailability {\n return options.publishEnabled\n ? { enabled: true }\n : { enabled: false, reason: \"Publishing from local mode is disabled (MICA_LOCAL_PUBLISH=false).\" };\n },\n\n async prepare() {},\n\n // The commit keeps the developer's own Git identity.\n commitAuthor: () => undefined,\n\n async push(tree: GitTree, revision: string): Promise<PublishResult> {\n try {\n // Publishing is the request to push, so a new branch gets its upstream here.\n const upstream = await tree.git.tryRun([\"rev-parse\", \"--abbrev-ref\", \"--symbolic-full-name\", \"@{upstream}\"]);\n const remotes = (await tree.git.tryRun([\"remote\"]))?.split(\"\\n\").filter(Boolean) ?? [];\n if (upstream === null && remotes.includes(\"origin\")) await tree.git.run([\"push\", \"-u\", \"origin\", \"HEAD\"]);\n else if (upstream === null) throw new Error(noUpstream(remotes));\n else await tree.git.run([\"push\"]);\n return { state: \"published\", revision };\n } catch (error) {\n const pushError = error instanceof GitError || error instanceof Error ? error.message : String(error);\n options.logger.warn(\"Publish committed but the push failed\", { revision, pushError });\n return { state: \"committed-pending-push\", revision, pushError };\n }\n },\n };\n}\n\nfunction noUpstream(remotes: string[]): string {\n return remotes.length === 0\n ? \"This repository has no remote. Add one with `git remote add origin <url>`, then publish again.\"\n : `This branch has no upstream. Push it once with \\`git push -u ${remotes[0]} HEAD\\`, then publish again.`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micacms/runtime",
3
- "version": "0.6.0-rc.1",
3
+ "version": "0.6.0-rc.2",
4
4
  "type": "module",
5
5
  "description": "The Mica single-site runtime: the Hono API, application services and staging orchestration, authentication-neutral. Composed by @micacms/editor locally and @micacms/self-hosted behind OIDC.",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "hono": "^4.13.7",
38
38
  "image-size": "^2.0.3",
39
39
  "zod": "^4.3.6",
40
- "@micacms/core": "0.6.0-rc.1"
40
+ "@micacms/core": "0.6.0-rc.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.20.1",
@@ -77,14 +77,8 @@ function isSvg(bytes: Uint8Array): boolean {
77
77
  return false;
78
78
  }
79
79
 
80
- const IMAGE_TYPES = new Set([
81
- "image/jpeg",
82
- "image/png",
83
- "image/gif",
84
- "image/webp",
85
- "image/avif",
86
- "image/heic",
87
- ]);
80
+ // No HEIC: Astro's image service cannot read it, so a site could not show it.
81
+ const IMAGE_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif"]);
88
82
 
89
83
  const DOCUMENT_TYPES = new Set(["application/pdf"]);
90
84
 
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The policy for the developer's own checkout. Mica never fetches, merges or
3
3
  * resets here. Publish pushes with the developer's `git`, credentials and
4
- * upstream; a failed push keeps the commit.
4
+ * upstream; a new branch without one is pushed to `origin` and tracks it. A
5
+ * failed push keeps the commit.
5
6
  */
6
7
 
7
8
  import type { PublishResult } from "@micacms/core";
@@ -34,13 +35,24 @@ export function developerPolicy(options: DeveloperPolicyOptions): OwnerPolicy {
34
35
 
35
36
  async push(tree: GitTree, revision: string): Promise<PublishResult> {
36
37
  try {
37
- await tree.git.run(["push"]);
38
+ // Publishing is the request to push, so a new branch gets its upstream here.
39
+ const upstream = await tree.git.tryRun(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]);
40
+ const remotes = (await tree.git.tryRun(["remote"]))?.split("\n").filter(Boolean) ?? [];
41
+ if (upstream === null && remotes.includes("origin")) await tree.git.run(["push", "-u", "origin", "HEAD"]);
42
+ else if (upstream === null) throw new Error(noUpstream(remotes));
43
+ else await tree.git.run(["push"]);
38
44
  return { state: "published", revision };
39
45
  } catch (error) {
40
- const pushError = error instanceof GitError ? error.message : String(error);
46
+ const pushError = error instanceof GitError || error instanceof Error ? error.message : String(error);
41
47
  options.logger.warn("Publish committed but the push failed", { revision, pushError });
42
48
  return { state: "committed-pending-push", revision, pushError };
43
49
  }
44
50
  },
45
51
  };
46
52
  }
53
+
54
+ function noUpstream(remotes: string[]): string {
55
+ return remotes.length === 0
56
+ ? "This repository has no remote. Add one with `git remote add origin <url>`, then publish again."
57
+ : `This branch has no upstream. Push it once with \`git push -u ${remotes[0]} HEAD\`, then publish again.`;
58
+ }