@tailorkit/app 0.1.0-beta.5 → 0.1.0-beta.7

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.
@@ -2,8 +2,12 @@ import { z } from "zod";
2
2
  export declare const tailorkitUploadManifestSchema: z.ZodObject<{
3
3
  assets: z.ZodObject<{
4
4
  client: z.ZodLiteral<"client.js">;
5
+ logos: z.ZodOptional<z.ZodObject<{
6
+ dark: z.ZodOptional<z.ZodString>;
7
+ light: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>>;
5
9
  }, z.core.$strip>;
6
10
  version: z.ZodLiteral<1>;
7
11
  }, z.core.$strip>;
8
12
  export type TailorKitUploadManifest = z.output<typeof tailorkitUploadManifestSchema>;
9
- export declare const createTailorKitUploadManifest: () => TailorKitUploadManifest;
13
+ export declare const createTailorKitUploadManifest: (logos?: TailorKitUploadManifest["assets"]["logos"]) => TailorKitUploadManifest;
package/dist/builder.js CHANGED
@@ -1,12 +1,77 @@
1
1
  import {
2
2
  loadTailorKitConfig
3
- } from "./chunk-7L6ZZHCK.js";
4
- import "./chunk-AGGRUUWG.js";
3
+ } from "./chunk-EKCUETAV.js";
4
+ import "./chunk-ZFYKVXDO.js";
5
5
 
6
6
  // src/builder/index.ts
7
7
  import path from "path";
8
8
  import { createRequire } from "module";
9
- import { writeFile } from "fs/promises";
9
+ import { readFile, writeFile } from "fs/promises";
10
+
11
+ // ../asset-delivery/dist/logo-validation.mjs
12
+ var maxLogoBytes = 262144;
13
+ var maxLogoDimension = 2048;
14
+ var readPngDimensions = (content) => {
15
+ if (content.length < 33 || ![
16
+ 137,
17
+ 80,
18
+ 78,
19
+ 71,
20
+ 13,
21
+ 10,
22
+ 26,
23
+ 10
24
+ ].every((byte, index) => content[index] === byte)) throw new Error("Logo content is not a valid PNG file.");
25
+ const view = new DataView(content.buffer, content.byteOffset, content.byteLength);
26
+ const isIhdr = content[12] === 73 && content[13] === 72 && content[14] === 68 && content[15] === 82;
27
+ if (view.getUint32(8) !== 13 || !isIhdr) throw new Error("Logo content is not a valid PNG file.");
28
+ return {
29
+ width: view.getUint32(16),
30
+ height: view.getUint32(20)
31
+ };
32
+ };
33
+ var readWebpDimensions = (content) => {
34
+ const text = (start, end) => new TextDecoder().decode(content.subarray(start, end));
35
+ if (content.length < 30 || text(0, 4) !== "RIFF" || text(8, 12) !== "WEBP") throw new Error("Logo content is not a valid WebP file.");
36
+ const view = new DataView(content.buffer, content.byteOffset, content.byteLength);
37
+ const chunk = text(12, 16);
38
+ if (chunk === "VP8X") return {
39
+ width: 1 + view.getUint16(24, true) + (content[26] ?? 0) * 65536,
40
+ height: 1 + view.getUint16(27, true) + (content[29] ?? 0) * 65536
41
+ };
42
+ if (chunk === "VP8L" && content[20] === 47) {
43
+ const bits = view.getUint32(21, true);
44
+ return {
45
+ width: bits % 16384 + 1,
46
+ height: Math.floor(bits / 16384) % 16384 + 1
47
+ };
48
+ }
49
+ if (chunk === "VP8 " && content[23] === 157 && content[24] === 1 && content[25] === 42) return {
50
+ width: view.getUint16(26, true) % 16384,
51
+ height: view.getUint16(28, true) % 16384
52
+ };
53
+ throw new Error("Logo content is not a supported WebP file.");
54
+ };
55
+ var validateSvg = (content) => {
56
+ const svg = new TextDecoder("utf-8", {
57
+ fatal: true,
58
+ ignoreBOM: false
59
+ }).decode(content);
60
+ if (!/<svg(?:\s|>)/iu.test(svg)) throw new Error("Logo content is not a valid SVG file.");
61
+ if (/<(?:script|foreignObject|iframe|object|embed)(?:\s|>)/iu.test(svg) || /\son[a-z]+\s*=/iu.test(svg) || /(?:href|src)\s*=\s*["'](?!#)/iu.test(svg) || /(?:@import|url\(\s*(?!["']?#))/iu.test(svg) || /<!DOCTYPE|<!ENTITY|<\?xml-stylesheet/iu.test(svg)) throw new Error("SVG logos cannot contain scripts or external resources.");
62
+ };
63
+ function validateLogoAsset(content, contentType) {
64
+ if (content.byteLength < 1 || content.byteLength > 262144) throw new Error(`Logo must be between 1 and ${maxLogoBytes} bytes.`);
65
+ if (contentType === "image/svg+xml") {
66
+ validateSvg(content);
67
+ return {};
68
+ }
69
+ const dimensions = contentType === "image/png" ? readPngDimensions(content) : readWebpDimensions(content);
70
+ if (dimensions.width < 1 || dimensions.height < 1 || dimensions.width > 2048 || dimensions.height > 2048) throw new Error(`Raster logos cannot exceed ${maxLogoDimension} by ${maxLogoDimension} pixels.`);
71
+ return dimensions;
72
+ }
73
+
74
+ // src/builder/index.ts
10
75
  import { build as viteBuild } from "vite";
11
76
 
12
77
  // src/preact-version.ts
@@ -35,13 +100,18 @@ var assertSupportedPreactVersion = (version) => {
35
100
  import { z } from "zod";
36
101
  var tailorkitUploadManifestSchema = z.object({
37
102
  assets: z.object({
38
- client: z.literal("client.js")
103
+ client: z.literal("client.js"),
104
+ logos: z.object({
105
+ dark: z.string().regex(/^logo-dark\.(?:png|svg|webp)$/u).optional(),
106
+ light: z.string().regex(/^logo-light\.(?:png|svg|webp)$/u).optional()
107
+ }).optional()
39
108
  }),
40
109
  version: z.literal(1)
41
110
  });
42
- var createTailorKitUploadManifest = () => tailorkitUploadManifestSchema.parse({
111
+ var createTailorKitUploadManifest = (logos) => tailorkitUploadManifestSchema.parse({
43
112
  assets: {
44
- client: "client.js"
113
+ client: "client.js",
114
+ ...logos && Object.keys(logos).length > 0 ? { logos } : {}
45
115
  },
46
116
  version: 1
47
117
  });
@@ -104,9 +174,30 @@ var buildApp = async (options = {}) => {
104
174
  ],
105
175
  root: loaded.root
106
176
  });
177
+ const logoManifest = {};
178
+ for (const variant of ["light", "dark"]) {
179
+ const configuredPath = loaded.config.logos?.[variant];
180
+ if (!configuredPath) {
181
+ continue;
182
+ }
183
+ const extension = path.extname(configuredPath).toLowerCase().slice(1);
184
+ const contentType = {
185
+ png: "image/png",
186
+ svg: "image/svg+xml",
187
+ webp: "image/webp"
188
+ }[extension];
189
+ if (!contentType) {
190
+ throw new Error(`The ${variant} logo must be an SVG, PNG, or WebP file.`);
191
+ }
192
+ const content = await readFile(path.resolve(loaded.root, configuredPath));
193
+ validateLogoAsset(content, contentType);
194
+ const filename = `logo-${variant}.${extension}`;
195
+ await writeFile(path.join(resolvedOutDir, filename), content);
196
+ logoManifest[variant] = filename;
197
+ }
107
198
  await writeFile(
108
199
  path.join(resolvedOutDir, "tailorkit-upload.json"),
109
- `${JSON.stringify(createTailorKitUploadManifest(), null, 2)}
200
+ `${JSON.stringify(createTailorKitUploadManifest(logoManifest), null, 2)}
110
201
  `,
111
202
  "utf-8"
112
203
  );
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  tailorkitConfigSchema
3
- } from "./chunk-AGGRUUWG.js";
3
+ } from "./chunk-ZFYKVXDO.js";
4
4
 
5
5
  // src/config/loader.ts
6
6
  import { cosmiconfig } from "cosmiconfig";
@@ -6,11 +6,16 @@ var clientConfigSchema = z.object({
6
6
  var buildConfigSchema = z.object({
7
7
  outDir: z.string().default(".tailorkit")
8
8
  });
9
+ var logosConfigSchema = z.object({
10
+ dark: z.string().min(1).optional(),
11
+ light: z.string().min(1).optional()
12
+ });
9
13
  var tailorkitConfigSchema = z.object({
10
14
  appId: z.string().min(1).optional(),
11
15
  build: buildConfigSchema.optional(),
12
16
  client: clientConfigSchema.optional(),
13
- host: z.string().url()
17
+ host: z.string().url(),
18
+ logos: logosConfigSchema.optional()
14
19
  });
15
20
  var defineConfig = (config) => config;
16
21
  var defineTailorKitConfig = defineConfig;
@@ -8,6 +8,10 @@ declare const tailorkitConfigSchema: z.ZodObject<{
8
8
  entry: z.ZodDefault<z.ZodString>;
9
9
  }, z.core.$strip>>;
10
10
  host: z.ZodString;
11
+ logos: z.ZodOptional<z.ZodObject<{
12
+ dark: z.ZodOptional<z.ZodString>;
13
+ light: z.ZodOptional<z.ZodString>;
14
+ }, z.core.$strip>>;
11
15
  }, z.core.$strip>;
12
16
  export type TailorKitConfig = z.input<typeof tailorkitConfigSchema>;
13
17
  export type ResolvedTailorKitConfig = z.output<typeof tailorkitConfigSchema>;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadTailorKitConfig
3
- } from "./chunk-7L6ZZHCK.js";
4
- import "./chunk-AGGRUUWG.js";
3
+ } from "./chunk-EKCUETAV.js";
4
+ import "./chunk-ZFYKVXDO.js";
5
5
  export {
6
6
  loadTailorKitConfig
7
7
  };
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineConfig,
3
3
  defineTailorKitConfig
4
- } from "./chunk-AGGRUUWG.js";
4
+ } from "./chunk-ZFYKVXDO.js";
5
5
  export {
6
6
  defineConfig,
7
7
  defineTailorKitConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailorkit/app",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.7",
4
4
  "license": "AGPL-3.0-only",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,12 +33,13 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@tailorkit/core": "0.1.0-beta.5",
36
+ "@tailorkit/core": "0.1.0-beta.7",
37
37
  "cosmiconfig": "^10.0.0",
38
38
  "vite": "^8.2.2",
39
39
  "zod": "^4.4.3"
40
40
  },
41
41
  "devDependencies": {
42
+ "@tailorkit/asset-delivery": "0.0.0",
42
43
  "@tailorkit/config": "0.0.0",
43
44
  "@types/node": "26.2.0",
44
45
  "preact": "^10.29.8",