@mohasinac/appkit 4.42.6 → 4.42.8

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.
@@ -64,6 +64,33 @@ export const MEDIA_URL_MESSAGE = "Must be a stored media reference (/media/<slug
64
64
  * validated before stops validating now.
65
65
  */
66
66
  export function isStoredMediaRef(value) {
67
+ /*
68
+ * 🛑 Type-guard first: this predicate is reached through `.refine()` from two
69
+ * schemas and from several hand-written call sites, so it receives whatever
70
+ * the document actually holds — not whatever the signature says.
71
+ *
72
+ * Without this line a non-string reached `value.startsWith(...)` and threw
73
+ * `TypeError: e.startsWith is not a function`, which killed the whole render
74
+ * of the panel it was validating. Measured on production 2026-10-02: expanding
75
+ * the Branding section of /admin/site threw exactly that and the section body
76
+ * never appeared — so the Site Settings editor looked like it rendered no
77
+ * fields for ANY section, and five checklist cases were blocked behind what
78
+ * read as a missing feature.
79
+ *
80
+ * `!value` already caught null/undefined/"" and 0; an object or an array is
81
+ * truthy with `length === undefined`, so `undefined > MAX` is false and
82
+ * execution fell straight through to `.startsWith`.
83
+ *
84
+ * A VALIDATOR MUST REJECT, NEVER THROW. Returning false is the correct answer
85
+ * for a non-string, and the warn keeps the real defect findable — some caller
86
+ * is passing the wrong shape (a `{ url }` object where its `.url` was meant
87
+ * is the obvious candidate) and that is a separate fix at the call site.
88
+ */
89
+ if (typeof value !== "string") {
90
+ // eslint-disable-next-line no-console -- client-side; the server path has its own recorder
91
+ console.warn("[isStoredMediaRef] expected a string, received", Object.prototype.toString.call(value));
92
+ return false;
93
+ }
67
94
  if (!value || value.length > MEDIA_URL_MAX_LENGTH)
68
95
  return false;
69
96
  if (value.startsWith(MEDIA_PROXY_PREFIX))
@@ -102,6 +129,23 @@ const OWN_STORAGE_BUCKET = process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET ?? ""
102
129
  export function resolveMediaUrl(url, opts) {
103
130
  if (!url)
104
131
  return undefined;
132
+ /*
133
+ * 🛑 Same type-guard as `isStoredMediaRef`, and for the same reason: this is a
134
+ * RENDER-path helper called from `MediaImage`, every avatar, every logo and
135
+ * every card, with whatever the document happens to hold. `!url` catches
136
+ * null/undefined/""; an object or array is truthy and falls straight through
137
+ * to `.startsWith`, throwing `TypeError: e.startsWith is not a function` and
138
+ * taking out the whole subtree it was rendering.
139
+ *
140
+ * Returning undefined is the documented behaviour for a falsy input and is the
141
+ * right answer here too — the caller renders its placeholder instead of
142
+ * crashing. The warn names the shape so the real caller stays findable.
143
+ */
144
+ if (typeof url !== "string") {
145
+ // eslint-disable-next-line no-console -- render path; a throw here kills the subtree
146
+ console.warn("[resolveMediaUrl] expected a string, received", Object.prototype.toString.call(url));
147
+ return undefined;
148
+ }
105
149
  if (url.startsWith(PROXY_PREFIX))
106
150
  return url;
107
151
  // A blob:/data: URI is only ever valid in the tab that created it (e.g. a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.42.6",
3
+ "version": "4.42.8",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"