@zappie/sdk 0.1.21 → 0.1.22
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/CHANGELOG.md +6 -0
- package/dist/debug.js +1 -1
- package/dist/index.js +1 -1
- package/dist/template/index.d.ts +23 -5
- package/dist/template/index.js +18 -8
- package/dist/template/index.js.map +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @zappie/sdk
|
|
2
2
|
|
|
3
|
+
## 0.1.22
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c1ffb88: Images resize only from `RESIZE_HOSTS` (or your `resizeHosts`), never as `.avif`, `.jxl` or `.svg`. `Image` falls back once to the original when a resized image fails.
|
|
8
|
+
|
|
3
9
|
## 0.1.21
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/debug.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { ZappieError, isZappieError } from "./errors.js";
|
|
|
10
10
|
export * from "./models.js";
|
|
11
11
|
import { format, money } from "./money.js";
|
|
12
12
|
import { RestTransport } from "./transport/rest.js";
|
|
13
|
-
var VERSION = true ? "0.1.
|
|
13
|
+
var VERSION = true ? "0.1.22" : "0.0.0-development";
|
|
14
14
|
export {
|
|
15
15
|
API_VERSION,
|
|
16
16
|
RestTransport,
|
package/dist/template/index.d.ts
CHANGED
|
@@ -19,9 +19,27 @@ declare function editorOrigin(ancestorOrigins: ArrayLike<string> | null | undefi
|
|
|
19
19
|
declare function settingsFromMessage(data: unknown): Record<string, TemplateSettingValue> | null;
|
|
20
20
|
/** Cloudflare image widths shared by template sites. */
|
|
21
21
|
declare const IMAGE_WIDTHS: readonly [320, 480, 640, 960, 1280, 1600];
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The image hosts that same-origin resizing accepts by default: Zappie's production photo bucket.
|
|
24
|
+
* Pass your own lowercase host list as `resizeHosts` to `resized`, `srcSet` or foundations' `Image`
|
|
25
|
+
* when your zone's resizing allows other sources.
|
|
26
|
+
*/
|
|
27
|
+
declare const RESIZE_HOSTS: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* Whether same-origin resizing can serve `src`. It must be an http or https URL whose host is in
|
|
30
|
+
* `resizeHosts`. Its path must not end in `.avif` or `.jxl`, which resizing refuses, or in `.svg`,
|
|
31
|
+
* which resizing serves unchanged. `resized` and `srcSet` leave any other source unchanged.
|
|
32
|
+
*/
|
|
33
|
+
declare function canResize(src: string, resizeHosts?: readonly string[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Build a same-origin Cloudflare resize URL for an image. A source that `canResize` rejects is
|
|
36
|
+
* returned unchanged.
|
|
37
|
+
*/
|
|
38
|
+
declare function resized(src: string, width: number, quality?: number, resizeHosts?: readonly string[]): string;
|
|
39
|
+
/**
|
|
40
|
+
* Build the responsive Cloudflare image candidates used by a template. Returns an empty string for
|
|
41
|
+
* a source that `canResize` rejects, so the browser loads `src` as it is.
|
|
42
|
+
*/
|
|
43
|
+
declare function srcSet(src: string, quality?: number, resizeHosts?: readonly string[]): string;
|
|
26
44
|
|
|
27
|
-
export { EDITOR_ORIGINS, IMAGE_WIDTHS, type TemplateMode, type TemplateSettingValue, editorOrigin, resized, settingsFromMessage, srcSet, templateSettings };
|
|
45
|
+
export { EDITOR_ORIGINS, IMAGE_WIDTHS, RESIZE_HOSTS, type TemplateMode, type TemplateSettingValue, canResize, editorOrigin, resized, settingsFromMessage, srcSet, templateSettings };
|
package/dist/template/index.js
CHANGED
|
@@ -56,20 +56,30 @@ function settingsFromMessage(data) {
|
|
|
56
56
|
return values;
|
|
57
57
|
}
|
|
58
58
|
var IMAGE_WIDTHS = [320, 480, 640, 960, 1280, 1600];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
var RESIZE_HOSTS = Object.freeze([
|
|
60
|
+
"zappie-public-prod.s3.eu-west-2.amazonaws.com"
|
|
61
|
+
]);
|
|
62
|
+
function canResize(src, resizeHosts = RESIZE_HOSTS) {
|
|
63
|
+
try {
|
|
64
|
+
const url = new URL(src);
|
|
65
|
+
return /^https?:$/.test(url.protocol) && resizeHosts.includes(url.hostname) && !/\.(avif|jxl|svg)$/i.test(url.pathname);
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
62
68
|
}
|
|
63
|
-
return `/cdn-cgi/image/width=${width},quality=${quality},format=auto,fit=scale-down/${src}`;
|
|
64
69
|
}
|
|
65
|
-
function
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
function resized(src, width, quality = 80, resizeHosts) {
|
|
71
|
+
return canResize(src, resizeHosts) ? `/cdn-cgi/image/width=${width},quality=${quality},format=auto,fit=scale-down/${src}` : src;
|
|
72
|
+
}
|
|
73
|
+
function srcSet(src, quality, resizeHosts) {
|
|
74
|
+
return canResize(src, resizeHosts) ? IMAGE_WIDTHS.map(
|
|
75
|
+
(width) => `${resized(src, width, quality, resizeHosts)} ${width}w`
|
|
76
|
+
).join(", ") : "";
|
|
69
77
|
}
|
|
70
78
|
export {
|
|
71
79
|
EDITOR_ORIGINS,
|
|
72
80
|
IMAGE_WIDTHS,
|
|
81
|
+
RESIZE_HOSTS,
|
|
82
|
+
canResize,
|
|
73
83
|
editorOrigin,
|
|
74
84
|
resized,
|
|
75
85
|
settingsFromMessage,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/template/index.ts"],
|
|
4
|
-
"sourcesContent": ["/** A value accepted by a template manifest and the live preview bridge. */\nexport type TemplateSettingValue = string | number | boolean;\n\n/** Where a template is running. Live sites never accept preview messages. */\nexport type TemplateMode = \"site\" | \"draft\" | \"review\";\n\n/** Merge defaults, saved settings, and preview values for the current mode. */\nexport function templateSettings<T extends Record<string, unknown>>(\n defaults: T,\n saved: Record<string, TemplateSettingValue> | undefined,\n preview: Record<string, TemplateSettingValue> | null,\n mode: TemplateMode,\n): T {\n if (mode === \"review\") {\n return { ...defaults, ...(preview ?? {}) };\n }\n if (mode === \"draft\") {\n return { ...defaults, ...saved, ...(preview ?? {}) };\n }\n return { ...defaults, ...saved };\n}\n\n/**\n * The Zappie editors allowed to frame a draft or review preview and talk to it: the merchant\n * dashboard (Studio), the admin dashboard (template review), and each one's local dev server.\n * The preview bridge posts only to the editor framing it, and only when that editor is listed here.\n */\nexport const EDITOR_ORIGINS: readonly string[] = Object.freeze([\n \"https://dashboard.zappie.ai\",\n \"https://dashboard.stg.zappie.ai\",\n \"https://admin.zappie.ai\",\n \"https://admin.stg.zappie.ai\",\n \"http://localhost:5181\",\n \"http://localhost:5180\",\n]);\n\n/**\n * Find the origin of the editor framing this page. The browser's `ancestorOrigins` is used where\n * it exists, and the referrer otherwise. Returns null unless that origin is in `EDITOR_ORIGINS`.\n */\nexport function editorOrigin(\n ancestorOrigins: ArrayLike<string> | null | undefined,\n referrer: string,\n allowed: readonly string[] = EDITOR_ORIGINS,\n): string | null {\n let origin: string | null = null;\n if (ancestorOrigins && ancestorOrigins.length > 0) {\n origin = ancestorOrigins[0] ?? null;\n } else if (referrer) {\n try {\n origin = new URL(referrer).origin;\n } catch {\n origin = null;\n }\n }\n return origin && allowed.includes(origin) ? origin : null;\n}\n\nconst MAX_SETTINGS = 200;\nconst MAX_SETTING_KEY = 100;\nconst MAX_SETTING_TEXT = 10_000;\n\n/** Read primitive setting values from an editor message. Oversized messages and values are dropped. */\nexport function settingsFromMessage(\n data: unknown,\n): Record<string, TemplateSettingValue> | null {\n if (!data || typeof data !== \"object\") {\n return null;\n }\n const message = data as { zappie?: unknown; values?: unknown };\n if (\n message.zappie !== \"settings\" ||\n !message.values ||\n typeof message.values !== \"object\" ||\n Array.isArray(message.values)\n ) {\n return null;\n }\n const entries = Object.entries(message.values);\n if (entries.length > MAX_SETTINGS) {\n return null;\n }\n const values: Record<string, TemplateSettingValue> = {};\n for (const [key, value] of entries) {\n if (key.length > MAX_SETTING_KEY) {\n continue;\n }\n if (\n (typeof value === \"string\" && value.length <= MAX_SETTING_TEXT) ||\n (typeof value === \"number\" && Number.isFinite(value)) ||\n typeof value === \"boolean\"\n ) {\n values[key] = value;\n }\n }\n return values;\n}\n\n/** Cloudflare image widths shared by template sites. */\nexport const IMAGE_WIDTHS = [320, 480, 640, 960, 1280, 1600] as const;\n\n
|
|
5
|
-
"mappings": ";AAOO,SAAS,iBACd,UACA,OACA,SACA,MACG;AACH,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,GAAG,UAAU,GAAI,WAAW,CAAC,EAAG;AAAA,EAC3C;AACA,MAAI,SAAS,SAAS;AACpB,WAAO,EAAE,GAAG,UAAU,GAAG,OAAO,GAAI,WAAW,CAAC,EAAG;AAAA,EACrD;AACA,SAAO,EAAE,GAAG,UAAU,GAAG,MAAM;AACjC;AAOO,IAAM,iBAAoC,OAAO,OAAO;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,SAAS,aACd,iBACA,UACA,UAA6B,gBACd;AACf,MAAI,SAAwB;AAC5B,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,aAAS,gBAAgB,CAAC,KAAK;AAAA,EACjC,WAAW,UAAU;AACnB,QAAI;AACF,eAAS,IAAI,IAAI,QAAQ,EAAE;AAAA,IAC7B,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,UAAU,QAAQ,SAAS,MAAM,IAAI,SAAS;AACvD;AAEA,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAGlB,SAAS,oBACd,MAC6C;AAC7C,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,UAAU;AAChB,MACE,QAAQ,WAAW,cACnB,CAAC,QAAQ,UACT,OAAO,QAAQ,WAAW,YAC1B,MAAM,QAAQ,QAAQ,MAAM,GAC5B;AACA,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,QAAQ,QAAQ,MAAM;AAC7C,MAAI,QAAQ,SAAS,cAAc;AACjC,WAAO;AAAA,EACT;AACA,QAAM,SAA+C,CAAC;AACtD,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,QAAI,IAAI,SAAS,iBAAiB;AAChC;AAAA,IACF;AACA,QACG,OAAO,UAAU,YAAY,MAAM,UAAU,oBAC7C,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KACnD,OAAO,UAAU,WACjB;AACA,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,eAAe,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,IAAI;
|
|
4
|
+
"sourcesContent": ["/** A value accepted by a template manifest and the live preview bridge. */\nexport type TemplateSettingValue = string | number | boolean;\n\n/** Where a template is running. Live sites never accept preview messages. */\nexport type TemplateMode = \"site\" | \"draft\" | \"review\";\n\n/** Merge defaults, saved settings, and preview values for the current mode. */\nexport function templateSettings<T extends Record<string, unknown>>(\n defaults: T,\n saved: Record<string, TemplateSettingValue> | undefined,\n preview: Record<string, TemplateSettingValue> | null,\n mode: TemplateMode,\n): T {\n if (mode === \"review\") {\n return { ...defaults, ...(preview ?? {}) };\n }\n if (mode === \"draft\") {\n return { ...defaults, ...saved, ...(preview ?? {}) };\n }\n return { ...defaults, ...saved };\n}\n\n/**\n * The Zappie editors allowed to frame a draft or review preview and talk to it: the merchant\n * dashboard (Studio), the admin dashboard (template review), and each one's local dev server.\n * The preview bridge posts only to the editor framing it, and only when that editor is listed here.\n */\nexport const EDITOR_ORIGINS: readonly string[] = Object.freeze([\n \"https://dashboard.zappie.ai\",\n \"https://dashboard.stg.zappie.ai\",\n \"https://admin.zappie.ai\",\n \"https://admin.stg.zappie.ai\",\n \"http://localhost:5181\",\n \"http://localhost:5180\",\n]);\n\n/**\n * Find the origin of the editor framing this page. The browser's `ancestorOrigins` is used where\n * it exists, and the referrer otherwise. Returns null unless that origin is in `EDITOR_ORIGINS`.\n */\nexport function editorOrigin(\n ancestorOrigins: ArrayLike<string> | null | undefined,\n referrer: string,\n allowed: readonly string[] = EDITOR_ORIGINS,\n): string | null {\n let origin: string | null = null;\n if (ancestorOrigins && ancestorOrigins.length > 0) {\n origin = ancestorOrigins[0] ?? null;\n } else if (referrer) {\n try {\n origin = new URL(referrer).origin;\n } catch {\n origin = null;\n }\n }\n return origin && allowed.includes(origin) ? origin : null;\n}\n\nconst MAX_SETTINGS = 200;\nconst MAX_SETTING_KEY = 100;\nconst MAX_SETTING_TEXT = 10_000;\n\n/** Read primitive setting values from an editor message. Oversized messages and values are dropped. */\nexport function settingsFromMessage(\n data: unknown,\n): Record<string, TemplateSettingValue> | null {\n if (!data || typeof data !== \"object\") {\n return null;\n }\n const message = data as { zappie?: unknown; values?: unknown };\n if (\n message.zappie !== \"settings\" ||\n !message.values ||\n typeof message.values !== \"object\" ||\n Array.isArray(message.values)\n ) {\n return null;\n }\n const entries = Object.entries(message.values);\n if (entries.length > MAX_SETTINGS) {\n return null;\n }\n const values: Record<string, TemplateSettingValue> = {};\n for (const [key, value] of entries) {\n if (key.length > MAX_SETTING_KEY) {\n continue;\n }\n if (\n (typeof value === \"string\" && value.length <= MAX_SETTING_TEXT) ||\n (typeof value === \"number\" && Number.isFinite(value)) ||\n typeof value === \"boolean\"\n ) {\n values[key] = value;\n }\n }\n return values;\n}\n\n/** Cloudflare image widths shared by template sites. */\nexport const IMAGE_WIDTHS = [320, 480, 640, 960, 1280, 1600] as const;\n\n/**\n * The image hosts that same-origin resizing accepts by default: Zappie's production photo bucket.\n * Pass your own lowercase host list as `resizeHosts` to `resized`, `srcSet` or foundations' `Image`\n * when your zone's resizing allows other sources.\n */\nexport const RESIZE_HOSTS: readonly string[] = Object.freeze([\n \"zappie-public-prod.s3.eu-west-2.amazonaws.com\",\n]);\n\n/**\n * Whether same-origin resizing can serve `src`. It must be an http or https URL whose host is in\n * `resizeHosts`. Its path must not end in `.avif` or `.jxl`, which resizing refuses, or in `.svg`,\n * which resizing serves unchanged. `resized` and `srcSet` leave any other source unchanged.\n */\nexport function canResize(\n src: string,\n resizeHosts: readonly string[] = RESIZE_HOSTS,\n): boolean {\n try {\n const url = new URL(src);\n return (\n /^https?:$/.test(url.protocol) &&\n resizeHosts.includes(url.hostname) &&\n !/\\.(avif|jxl|svg)$/i.test(url.pathname)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Build a same-origin Cloudflare resize URL for an image. A source that `canResize` rejects is\n * returned unchanged.\n */\nexport function resized(\n src: string,\n width: number,\n quality = 80,\n resizeHosts?: readonly string[],\n): string {\n return canResize(src, resizeHosts)\n ? `/cdn-cgi/image/width=${width},quality=${quality},format=auto,fit=scale-down/${src}`\n : src;\n}\n\n/**\n * Build the responsive Cloudflare image candidates used by a template. Returns an empty string for\n * a source that `canResize` rejects, so the browser loads `src` as it is.\n */\nexport function srcSet(\n src: string,\n quality?: number,\n resizeHosts?: readonly string[],\n): string {\n return canResize(src, resizeHosts)\n ? IMAGE_WIDTHS.map(\n (width) => `${resized(src, width, quality, resizeHosts)} ${width}w`,\n ).join(\", \")\n : \"\";\n}\n"],
|
|
5
|
+
"mappings": ";AAOO,SAAS,iBACd,UACA,OACA,SACA,MACG;AACH,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,GAAG,UAAU,GAAI,WAAW,CAAC,EAAG;AAAA,EAC3C;AACA,MAAI,SAAS,SAAS;AACpB,WAAO,EAAE,GAAG,UAAU,GAAG,OAAO,GAAI,WAAW,CAAC,EAAG;AAAA,EACrD;AACA,SAAO,EAAE,GAAG,UAAU,GAAG,MAAM;AACjC;AAOO,IAAM,iBAAoC,OAAO,OAAO;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,SAAS,aACd,iBACA,UACA,UAA6B,gBACd;AACf,MAAI,SAAwB;AAC5B,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,aAAS,gBAAgB,CAAC,KAAK;AAAA,EACjC,WAAW,UAAU;AACnB,QAAI;AACF,eAAS,IAAI,IAAI,QAAQ,EAAE;AAAA,IAC7B,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,UAAU,QAAQ,SAAS,MAAM,IAAI,SAAS;AACvD;AAEA,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AAGlB,SAAS,oBACd,MAC6C;AAC7C,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AACA,QAAM,UAAU;AAChB,MACE,QAAQ,WAAW,cACnB,CAAC,QAAQ,UACT,OAAO,QAAQ,WAAW,YAC1B,MAAM,QAAQ,QAAQ,MAAM,GAC5B;AACA,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,QAAQ,QAAQ,MAAM;AAC7C,MAAI,QAAQ,SAAS,cAAc;AACjC,WAAO;AAAA,EACT;AACA,QAAM,SAA+C,CAAC;AACtD,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,QAAI,IAAI,SAAS,iBAAiB;AAChC;AAAA,IACF;AACA,QACG,OAAO,UAAU,YAAY,MAAM,UAAU,oBAC7C,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KACnD,OAAO,UAAU,WACjB;AACA,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAGO,IAAM,eAAe,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,IAAI;AAOpD,IAAM,eAAkC,OAAO,OAAO;AAAA,EAC3D;AACF,CAAC;AAOM,SAAS,UACd,KACA,cAAiC,cACxB;AACT,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,GAAG;AACvB,WACE,YAAY,KAAK,IAAI,QAAQ,KAC7B,YAAY,SAAS,IAAI,QAAQ,KACjC,CAAC,qBAAqB,KAAK,IAAI,QAAQ;AAAA,EAE3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,QACd,KACA,OACA,UAAU,IACV,aACQ;AACR,SAAO,UAAU,KAAK,WAAW,IAC7B,wBAAwB,KAAK,YAAY,OAAO,+BAA+B,GAAG,KAClF;AACN;AAMO,SAAS,OACd,KACA,SACA,aACQ;AACR,SAAO,UAAU,KAAK,WAAW,IAC7B,aAAa;AAAA,IACX,CAAC,UAAU,GAAG,QAAQ,KAAK,OAAO,SAAS,WAAW,CAAC,IAAI,KAAK;AAAA,EAClE,EAAE,KAAK,IAAI,IACX;AACN;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|