@heyramzi/utils 0.0.0-stage → 1.13.0

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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -2
  3. package/dist/ai/image.js +220 -0
  4. package/dist/ai/models.js +261 -0
  5. package/dist/analytics/client.js +57 -0
  6. package/dist/analytics/server.js +54 -0
  7. package/dist/analytics/svelte.js +24 -0
  8. package/dist/analytics/types.js +0 -0
  9. package/dist/analytics/umami/protocol.js +35 -0
  10. package/dist/analytics/umami/reporting.js +189 -0
  11. package/dist/auth/config/otp.js +4 -0
  12. package/dist/auth/config/rate-limits.js +20 -0
  13. package/dist/auth/config/trusted-origins.js +15 -0
  14. package/dist/auth/email/index.js +3 -0
  15. package/dist/auth/email/layout.js +84 -0
  16. package/dist/auth/email/resend.js +30 -0
  17. package/dist/auth/email/template.js +57 -0
  18. package/dist/auth/email/types.js +0 -0
  19. package/dist/auth/index.js +3 -0
  20. package/dist/cache.js +4 -0
  21. package/dist/cn.js +5 -0
  22. package/dist/collection.js +8 -0
  23. package/dist/crypto.js +117 -0
  24. package/dist/error.js +15 -0
  25. package/dist/errors/sentry/index.js +15 -0
  26. package/dist/errors/sentry/protocol.js +130 -0
  27. package/dist/errors/sentry/sveltekit.js +145 -0
  28. package/dist/format.js +62 -0
  29. package/dist/grab.js +112 -0
  30. package/dist/index.js +10 -0
  31. package/dist/logger-types.js +0 -0
  32. package/dist/logger.js +22 -0
  33. package/dist/math.js +3 -0
  34. package/dist/og/fonts.js +46 -0
  35. package/dist/og/index.js +3 -0
  36. package/dist/og/markup.js +31 -0
  37. package/dist/og/renderer.js +29 -0
  38. package/dist/og/types.js +0 -0
  39. package/dist/result.js +26 -0
  40. package/dist/string.js +32 -0
  41. package/dist/testimonials/render-content.js +15 -0
  42. package/dist/testimonials/server.js +21 -0
  43. package/dist/testimonials/types.js +0 -0
  44. package/dist/timing.js +20 -0
  45. package/dist/vite.js +265 -0
  46. package/package.json +201 -4
  47. package/src/ai/image-agnes.test.ts +130 -0
  48. package/src/ai/image.ts +435 -0
  49. package/src/ai/models.test.ts +98 -0
  50. package/src/ai/models.ts +450 -0
  51. package/src/analytics/client.test.ts +149 -0
  52. package/src/analytics/client.ts +129 -0
  53. package/src/analytics/server.test.ts +165 -0
  54. package/src/analytics/server.ts +88 -0
  55. package/src/analytics/svelte.test.ts +105 -0
  56. package/src/analytics/svelte.ts +58 -0
  57. package/src/analytics/types.ts +26 -0
  58. package/src/analytics/umami/protocol.test.ts +18 -0
  59. package/src/analytics/umami/protocol.ts +104 -0
  60. package/src/analytics/umami/reporting.test.ts +391 -0
  61. package/src/analytics/umami/reporting.ts +627 -0
  62. package/src/auth/config/otp.ts +8 -0
  63. package/src/auth/config/rate-limits.ts +11 -0
  64. package/src/auth/config/trusted-origins.ts +32 -0
  65. package/src/auth/email/index.ts +7 -0
  66. package/src/auth/email/layout.test.ts +73 -0
  67. package/src/auth/email/layout.ts +122 -0
  68. package/src/auth/email/resend.test.ts +35 -0
  69. package/src/auth/email/resend.ts +26 -0
  70. package/src/auth/email/template.ts +77 -0
  71. package/src/auth/email/types.ts +74 -0
  72. package/src/auth/index.ts +5 -0
  73. package/src/cache.test.ts +67 -0
  74. package/src/cache.ts +5 -0
  75. package/src/cn.test.ts +52 -0
  76. package/src/cn.ts +16 -0
  77. package/src/collection.test.ts +66 -0
  78. package/src/collection.ts +8 -0
  79. package/src/crypto.test.ts +86 -0
  80. package/src/crypto.ts +157 -0
  81. package/src/error.test.ts +79 -0
  82. package/src/error.ts +16 -0
  83. package/src/errors/sentry/index.ts +20 -0
  84. package/src/errors/sentry/protocol.test.ts +85 -0
  85. package/src/errors/sentry/protocol.ts +144 -0
  86. package/src/errors/sentry/sveltekit.test.ts +149 -0
  87. package/src/errors/sentry/sveltekit.ts +226 -0
  88. package/src/format.test.ts +225 -0
  89. package/src/format.ts +103 -0
  90. package/src/grab.d.ts +2 -0
  91. package/src/grab.js +112 -0
  92. package/src/index.ts +38 -0
  93. package/src/logger-types.ts +14 -0
  94. package/src/logger.ts +24 -0
  95. package/src/math.test.ts +41 -0
  96. package/src/math.ts +3 -0
  97. package/src/og/fonts.ts +69 -0
  98. package/src/og/index.ts +5 -0
  99. package/src/og/markup.test.ts +73 -0
  100. package/src/og/markup.ts +90 -0
  101. package/src/og/renderer.ts +42 -0
  102. package/src/og/types.ts +26 -0
  103. package/src/og/wawoff2.d.ts +6 -0
  104. package/src/package-imports.test.mjs +22 -0
  105. package/src/result.test.ts +149 -0
  106. package/src/result.ts +29 -0
  107. package/src/string.test.ts +205 -0
  108. package/src/string.ts +71 -0
  109. package/src/testimonials/render-content.ts +21 -0
  110. package/src/testimonials/server.ts +47 -0
  111. package/src/testimonials/types.ts +35 -0
  112. package/src/timing.test.ts +166 -0
  113. package/src/timing.ts +28 -0
  114. package/src/vite.d.ts +12 -0
  115. package/src/vite.js +265 -0
  116. package/src/vite.test.js +235 -0
@@ -0,0 +1,31 @@
1
+ import { html as satoriHtml } from "satori-html";
2
+ import { __unsafeHTML } from "ultrahtml";
3
+ const RAW_MARKUP = Symbol.for("@heyramzi/marketing/imagegen/RawMarkup");
4
+ export function raw(markup) {
5
+ const marked = __unsafeHTML(markup);
6
+ Object.defineProperty(marked, RAW_MARKUP, {
7
+ value: true,
8
+ enumerable: false
9
+ });
10
+ return marked;
11
+ }
12
+ export function html(strings, ...values) {
13
+ const flattened = values.map((value)=>{
14
+ if (Array.isArray(value)) {
15
+ const parts = value.map((item)=>{
16
+ if (item == null || item === false) return "";
17
+ if (isRawMarkup(item)) return item.value;
18
+ return escapeHtml(String(item));
19
+ });
20
+ return raw(parts.join(""));
21
+ }
22
+ return value;
23
+ });
24
+ return satoriHtml(strings, ...flattened);
25
+ }
26
+ function isRawMarkup(value) {
27
+ return typeof value === "object" && value !== null && RAW_MARKUP in value;
28
+ }
29
+ function escapeHtml(input) {
30
+ return input.replace(/[&<>]/g, (c)=>c === "&" ? "&amp;" : c === "<" ? "&lt;" : "&gt;");
31
+ }
@@ -0,0 +1,29 @@
1
+ import satori from "satori";
2
+ import sharp from "sharp";
3
+ export async function renderImage(markup, options) {
4
+ const format = options.format ?? "png";
5
+ const svg = await satori(markup, {
6
+ width: options.size.width,
7
+ height: options.size.height,
8
+ fonts: options.fonts
9
+ });
10
+ if (format === "svg") {
11
+ return new TextEncoder().encode(svg);
12
+ }
13
+ const pipeline = sharp(Buffer.from(svg));
14
+ if (format === "avif") {
15
+ const buffer = await pipeline.avif({
16
+ quality: options.quality ?? 82
17
+ }).toBuffer();
18
+ return new Uint8Array(buffer);
19
+ }
20
+ const buffer = await pipeline.png({
21
+ quality: options.quality ?? 90
22
+ }).toBuffer();
23
+ return new Uint8Array(buffer);
24
+ }
25
+ export const MIME_TYPES = {
26
+ svg: "image/svg+xml",
27
+ png: "image/png",
28
+ avif: "image/avif"
29
+ };
File without changes
package/dist/result.js ADDED
@@ -0,0 +1,26 @@
1
+ export function Ok(value) {
2
+ return {
3
+ ok: true,
4
+ value
5
+ };
6
+ }
7
+ export function Err(error) {
8
+ return {
9
+ ok: false,
10
+ error
11
+ };
12
+ }
13
+ export function tryCatch(fn) {
14
+ try {
15
+ return Ok(fn());
16
+ } catch (e) {
17
+ return Err(e instanceof Error ? e : new Error(String(e)));
18
+ }
19
+ }
20
+ export async function tryCatchAsync(fn) {
21
+ try {
22
+ return Ok(await fn());
23
+ } catch (e) {
24
+ return Err(e instanceof Error ? e : new Error(String(e)));
25
+ }
26
+ }
package/dist/string.js ADDED
@@ -0,0 +1,32 @@
1
+ export function slugify(text) {
2
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
3
+ }
4
+ export function generateSlug(text) {
5
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
6
+ }
7
+ export function buildSearchParams(params) {
8
+ const entries = Object.entries(params).filter((entry)=>entry[1] !== undefined);
9
+ if (entries.length === 0) return "";
10
+ return `?${entries.map(([k, v])=>`${k}=${encodeURIComponent(v)}`).join("&")}`;
11
+ }
12
+ export function parseJSON(raw) {
13
+ if (!raw) return undefined;
14
+ try {
15
+ return JSON.parse(raw);
16
+ } catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ export function parseCommaSeparatedList(text) {
21
+ if (!text) return [];
22
+ return text.split(",").map((s)=>s.trim()).filter(Boolean);
23
+ }
24
+ function foldForSearch(text) {
25
+ return text.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase();
26
+ }
27
+ export function matchesQuery(haystack, query) {
28
+ const words = foldForSearch(query).split(/\s+/).filter(Boolean);
29
+ if (words.length === 0) return true;
30
+ const target = foldForSearch(haystack);
31
+ return words.every((word)=>target.includes(word));
32
+ }
@@ -0,0 +1,15 @@
1
+ const HTML_ESCAPES = {
2
+ "&": "&amp;",
3
+ "<": "&lt;",
4
+ ">": "&gt;",
5
+ '"': "&quot;",
6
+ "'": "&#39;"
7
+ };
8
+ function escapeHtml(input) {
9
+ return input.replace(/[&<>"']/g, (ch)=>HTML_ESCAPES[ch] ?? ch);
10
+ }
11
+ export function renderTestimonialContent(content) {
12
+ if (!content) return "";
13
+ const escaped = escapeHtml(content);
14
+ return escaped.replace(/\*\*([^*]+?)\*\*/g, "<strong>$1</strong>");
15
+ }
@@ -0,0 +1,21 @@
1
+ export const DEFAULT_TESTIMONIALS_BASE_URL = "https://app.upsys-consulting.com";
2
+ export async function fetchPublicTestimonialCollection(token, opts = {}) {
3
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
4
+ const baseUrl = (opts.baseUrl ?? DEFAULT_TESTIMONIALS_BASE_URL).replace(/\/$/, "");
5
+ const params = new URLSearchParams();
6
+ if (opts.locale) params.set("locale", opts.locale);
7
+ if (opts.layout) params.set("layout", opts.layout);
8
+ const query = params.toString();
9
+ const url = `${baseUrl}/api/testimonials/public/${encodeURIComponent(token)}${query ? `?${query}` : ""}`;
10
+ const response = await fetchImpl(url);
11
+ if (!response.ok) return null;
12
+ return await response.json();
13
+ }
14
+ export async function fetchPublicTestimonials(token, opts = {}) {
15
+ const data = await fetchPublicTestimonialCollection(token, opts);
16
+ return data?.testimonials ?? [];
17
+ }
18
+ export async function fetchPublicTestimonialStats(token, opts = {}) {
19
+ const data = await fetchPublicTestimonialCollection(token, opts);
20
+ return data?.collection.stats ?? [];
21
+ }
File without changes
package/dist/timing.js ADDED
@@ -0,0 +1,20 @@
1
+ export function debounce(fn, delay) {
2
+ let timeoutId;
3
+ return (...args)=>{
4
+ clearTimeout(timeoutId);
5
+ timeoutId = setTimeout(()=>fn(...args), delay);
6
+ };
7
+ }
8
+ export function throttle(fn, limit) {
9
+ let inThrottle = false;
10
+ return (...args)=>{
11
+ if (!inThrottle) {
12
+ fn(...args);
13
+ inThrottle = true;
14
+ setTimeout(()=>inThrottle = false, limit);
15
+ }
16
+ };
17
+ }
18
+ export function sleep(ms) {
19
+ return new Promise((resolve)=>setTimeout(resolve, ms));
20
+ }
package/dist/vite.js ADDED
@@ -0,0 +1,265 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import net from "node:net";
3
+
4
+ /** Let dependencies resolve their own package imports instead of Kit's app aliases. */
5
+ export function packageImportsOverKitAlias() {
6
+ return {
7
+ name: "package-imports-over-kit-alias",
8
+ config(config) {
9
+ const alias = config.resolve?.alias;
10
+ if (!config.resolve || !Array.isArray(alias)) return;
11
+ config.resolve.alias = alias.filter(
12
+ (entry) => !/^\^#(src|scripts)\\?\//.test(String(entry.find).slice(1)),
13
+ );
14
+ },
15
+ };
16
+ }
17
+
18
+ /** Both loopback stacks: a dev server may be bound to only one of them. */
19
+ const HOSTS = ["127.0.0.1", "::1"];
20
+
21
+ /**
22
+ * Vite plugin: before the dev server binds, kill whatever is already listening on the configured port and take the port back.
23
+ */
24
+
25
+ /*
26
+ * Without this, Vite silently increments to the next free port when the configured one is busy (e.g. a stale dev server left running),
27
+ */
28
+
29
+ /*
30
+ * so you end up on 3161/3162 instead of the 3160 you asked for. This plugin reclaims the intended port instead of drifting.
31
+ */
32
+
33
+ /*
34
+ * It reclaims a port from a listener that has stopped answering.
35
+ */
36
+
37
+ /*
38
+ * It never takes one from a dev server that is still serving: that server belongs to somebody, and on this machine "somebody" is routinely a second agent session working in
39
+ */
40
+
41
+ /*
42
+ * the same repo. A dedicated port range per project makes the owner predictable, not disposable. Pass `RECLAIM_PORT_FORCE=1` to take the port from a live server anyway.
43
+ */
44
+
45
+ /*
46
+ * Reads `server.port` from the resolved config, so it works whether the port is set in `vite.config.ts` or passed as `--port` on the CLI.
47
+ */
48
+
49
+ /*
50
+ * Dev-only and macOS/Linux only (uses `lsof`). On Windows or any failure it is a no-op and Vite falls back to its default behaviour.
51
+ */
52
+
53
+ /*
54
+ * WHY `apply: "serve"` is not enough on its own: Vitest and `vite preview` both resolve the same config in serve mode without ever binding `server.port`,
55
+ */
56
+
57
+ /*
58
+ * so either one used to reach this hook, find the running dev server on that port and SIGTERM it.
59
+ */
60
+
61
+ /*
62
+ * The developer sees their dev terminal exit 143 with no explanation, which reads as the machine dying on its own.
63
+ */
64
+
65
+ /*
66
+ * Anything that does not bind the port must never reclaim it.
67
+ */
68
+
69
+ /*
70
+ * WHY this is plain JS (not vite.ts): vite.config.ts is read by config loaders (vp / native node strip-types) that refuse to type-strip a `.ts` file under
71
+ */
72
+
73
+ /*
74
+ * node_modules. Shipping the entry as JS + a `.d.ts` lets every consumer load it from their config regardless of loader. Types live in vite.d.ts.
75
+ */
76
+
77
+ /*
78
+ * Usage in vite.config.ts: import { reclaimPort } from "@heyramzi/utils/vite"; plugins: [reclaimPort(), ...]
79
+ */
80
+ export function reclaimPort() {
81
+ // WHY a closure: `isPreview` rides on the ConfigEnv the `config` hook is
82
+ // handed, and never reaches the resolved config, so `configResolved` alone
83
+ // cannot tell `vite preview` from `vite dev`.
84
+ let isPreview = false;
85
+
86
+ return {
87
+ name: "reclaim-port",
88
+ apply: "serve",
89
+ enforce: "pre",
90
+ config(_userConfig, env) {
91
+ isPreview = Boolean(env?.isPreview);
92
+ },
93
+ async configResolved(config) {
94
+ if (isPreview || isNotBindingTheDevPort(config)) return;
95
+
96
+ const port = config.server.port;
97
+ if (!port || process.platform === "win32") return;
98
+
99
+ // WHY the self/parent filter: a `vite.config.ts` edit restarts the dev server inside the SAME process, and the old server still holds the port at that moment.
100
+
101
+ // Without this the plugin SIGTERMs its own pid and the developer's dev server dies on a config save.
102
+ const pids = listenerPids(port).filter((pid) => pid !== process.pid && pid !== process.ppid);
103
+ if (pids.length === 0) return;
104
+
105
+ // WHY liveness is tested and not assumed: this plugin was written for a stale server, and "stale" was never checked.
106
+
107
+ // It killed whatever held the port, so a second terminal, a second worktree or a second agent session running `pnpm dev` SIGTERMed a colleague's working server and that terminal died
108
+
109
+ // on `Exit status 143` with nothing to read. A port that still answers has an owner; only a port that has stopped answering is abandoned.
110
+ if (!isForced() && (await isServing(port))) {
111
+ throw new Error(
112
+ `[reclaim-port] Port ${port} is already serving (pid ${pids.join(", ")}).\n` +
113
+ `Another dev server is live on it, so this one will not take it.\n` +
114
+ `Use the server that is already running at http://localhost:${port},\n` +
115
+ `or stop it first. To take the port from a live server anyway, set ` +
116
+ `RECLAIM_PORT_FORCE=1.`,
117
+ );
118
+ }
119
+
120
+ config.logger.info(
121
+ `[reclaim-port] port ${port} held by an unresponsive listener, reclaiming (killing ${pids.join(", ")})`,
122
+ );
123
+ for (const pid of pids) {
124
+ try {
125
+ process.kill(pid, "SIGTERM");
126
+ } catch {
127
+ // already gone, or not ours to kill -- ignore and let the wait below decide
128
+ }
129
+ }
130
+ await waitForPortFree(port);
131
+ },
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Whether this config is being resolved by something other than a dev server about to bind `server.port`.
137
+ */
138
+
139
+ /*
140
+ * Several independent signals, because any one alone is a single point of failure: Vitest sets `VITEST` before the config loads and its resolved config carries a `test` block that a
141
+ */
142
+
143
+ /*
144
+ * plain dev server never has, and `middlewareMode` marks an embedder such as Storybook or an SSR harness that never listens itself.
145
+ */
146
+
147
+ /*
148
+ * `vite preview` is caught in the `config` hook above, the only place it declares itself.
149
+ */
150
+
151
+ /*
152
+ * A false positive costs a port that is not reclaimed; a false negative kills the developer's session, so every doubtful case resolves to "do not touch it".
153
+ */
154
+ function isNotBindingTheDevPort(config) {
155
+ return Boolean(
156
+ config.server?.middlewareMode ||
157
+ process.env.VITEST ||
158
+ process.env.VITEST_WORKER_ID ||
159
+ config.test,
160
+ );
161
+ }
162
+
163
+ /** PIDs of processes LISTENing on the port (IPv4 + IPv6), via lsof. */
164
+ function listenerPids(port) {
165
+ try {
166
+ const out = execFileSync("lsof", ["-ti", `tcp:${port}`, "-sTCP:LISTEN"], {
167
+ encoding: "utf8",
168
+ stdio: ["ignore", "pipe", "ignore"],
169
+ });
170
+ return [
171
+ ...new Set(
172
+ out
173
+ .split("\n")
174
+ .map((line) => Number(line.trim()))
175
+ .filter((pid) => Number.isInteger(pid) && pid > 0),
176
+ ),
177
+ ];
178
+ } catch {
179
+ // lsof missing, or nothing listening (non-zero exit) -> nothing to reclaim
180
+ return [];
181
+ }
182
+ }
183
+
184
+ /** Resolve once the port stops accepting connections, or the timeout elapses. */
185
+ function waitForPortFree(port, timeoutMs = 2000) {
186
+ const deadline = Date.now() + timeoutMs;
187
+ return new Promise((resolve) => {
188
+ const probe = () => {
189
+ const socket = net.connect({ port, host: "127.0.0.1" });
190
+ socket.once("connect", () => {
191
+ socket.destroy();
192
+ if (Date.now() > deadline) return resolve();
193
+ setTimeout(probe, 50);
194
+ });
195
+ socket.once("error", () => {
196
+ socket.destroy();
197
+ resolve(); // connection refused -> port is free
198
+ });
199
+ };
200
+ probe();
201
+ });
202
+ }
203
+
204
+ /** An explicit opt-in to take a port from a server that is still serving. */
205
+ function isForced() {
206
+ return process.env.RECLAIM_PORT_FORCE === "1";
207
+ }
208
+
209
+ /**
210
+ * Whether something on this port still answers a request.
211
+ */
212
+
213
+ /*
214
+ * WHY both stacks: vite binds `localhost`, which on macOS resolves to `::1` first, so a running dev server routinely appears on IPv6 ONLY.
215
+ */
216
+
217
+ /*
218
+ * A probe to 127.0.0.1 is refused, the port reads as free, and the guard waves through the exact start it exists to stop.
219
+ */
220
+
221
+ /*
222
+ * Verified against a live server on 3160: the IPv4-only probe returned "free" while the server was serving.
223
+ */
224
+
225
+ /*
226
+ * WHY a written request and not a bare `connect`: connecting succeeds against a listener that will never reply, and telling those two apart is the whole job.
227
+ */
228
+
229
+ /*
230
+ * Any byte back counts; the status line does not matter.
231
+ */
232
+ function isServing(port, timeoutMs = 1000) {
233
+ return Promise.all(HOSTS.map((host) => answers(host, port, timeoutMs))).then((results) =>
234
+ results.some(Boolean),
235
+ );
236
+ }
237
+
238
+ function answers(host, port, timeoutMs) {
239
+ return new Promise((resolve) => {
240
+ let settled = false;
241
+ const done = (answer) => {
242
+ if (settled) return;
243
+ settled = true;
244
+ clearTimeout(timer);
245
+ socket.destroy();
246
+ resolve(answer);
247
+ };
248
+
249
+ const timer = setTimeout(() => done(false), timeoutMs);
250
+ const socket = net.connect({ port, host });
251
+
252
+ socket.setTimeout(timeoutMs, () => done(false));
253
+ socket.once("error", () => done(false));
254
+ socket.once("data", () => done(true));
255
+ socket.once("close", () => done(false));
256
+ socket.once("connect", () => {
257
+ socket.write(`GET / HTTP/1.0\r\nHost: ${formatHost(host)}:${port}\r\n\r\n`);
258
+ });
259
+ });
260
+ }
261
+
262
+ /** An IPv6 literal needs brackets in a Host header. */
263
+ function formatHost(host) {
264
+ return host.includes(":") ? `[${host}]` : host;
265
+ }
package/package.json CHANGED
@@ -1,6 +1,203 @@
1
1
  {
2
2
  "name": "@heyramzi/utils",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.13.0",
4
+ "description": "Shared TypeScript decisions for every Studio app: cn, result, formatting, crypto, auth defaults, email, Sentry errors, Umami analytics, OG images, testimonials and the AI model table.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/heyramzi/vibe-kit.git",
8
+ "directory": "packages/utils"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "package.json",
13
+ "dist"
14
+ ],
15
+ "type": "module",
16
+ "imports": {
17
+ "#src/vite.js": "./src/vite.js",
18
+ "#src/*.js": "./src/*.ts",
19
+ "#src/*": "./src/*"
20
+ },
21
+ "exports": {
22
+ ".": {
23
+ "types": "./src/index.ts",
24
+ "module": "./src/index.ts",
25
+ "default": "./dist/index.js"
26
+ },
27
+ "./cn": {
28
+ "types": "./src/cn.ts",
29
+ "module": "./src/cn.ts",
30
+ "default": "./dist/cn.js"
31
+ },
32
+ "./format": {
33
+ "types": "./src/format.ts",
34
+ "module": "./src/format.ts",
35
+ "default": "./dist/format.js"
36
+ },
37
+ "./string": {
38
+ "types": "./src/string.ts",
39
+ "module": "./src/string.ts",
40
+ "default": "./dist/string.js"
41
+ },
42
+ "./result": {
43
+ "types": "./src/result.ts",
44
+ "module": "./src/result.ts",
45
+ "default": "./dist/result.js"
46
+ },
47
+ "./timing": {
48
+ "types": "./src/timing.ts",
49
+ "module": "./src/timing.ts",
50
+ "default": "./dist/timing.js"
51
+ },
52
+ "./math": {
53
+ "types": "./src/math.ts",
54
+ "module": "./src/math.ts",
55
+ "default": "./dist/math.js"
56
+ },
57
+ "./collection": {
58
+ "types": "./src/collection.ts",
59
+ "module": "./src/collection.ts",
60
+ "default": "./dist/collection.js"
61
+ },
62
+ "./vite": {
63
+ "types": "./src/vite.d.ts",
64
+ "default": "./src/vite.js"
65
+ },
66
+ "./grab": {
67
+ "types": "./src/grab.d.ts",
68
+ "default": "./src/grab.js"
69
+ },
70
+ "./crypto": {
71
+ "types": "./src/crypto.ts",
72
+ "module": "./src/crypto.ts",
73
+ "default": "./dist/crypto.js"
74
+ },
75
+ "./auth": {
76
+ "types": "./src/auth/index.ts",
77
+ "module": "./src/auth/index.ts",
78
+ "default": "./dist/auth/index.js"
79
+ },
80
+ "./email": {
81
+ "types": "./src/auth/email/index.ts",
82
+ "module": "./src/auth/email/index.ts",
83
+ "default": "./dist/auth/email/index.js"
84
+ },
85
+ "./errors": {
86
+ "types": "./src/errors/sentry/index.ts",
87
+ "module": "./src/errors/sentry/index.ts",
88
+ "default": "./dist/errors/sentry/index.js"
89
+ },
90
+ "./errors/sveltekit": {
91
+ "types": "./src/errors/sentry/sveltekit.ts",
92
+ "module": "./src/errors/sentry/sveltekit.ts",
93
+ "default": "./dist/errors/sentry/sveltekit.js"
94
+ },
95
+ "./analytics": {
96
+ "types": "./src/analytics/client.ts",
97
+ "module": "./src/analytics/client.ts",
98
+ "default": "./dist/analytics/client.js"
99
+ },
100
+ "./analytics/server": {
101
+ "types": "./src/analytics/server.ts",
102
+ "module": "./src/analytics/server.ts",
103
+ "default": "./dist/analytics/server.js"
104
+ },
105
+ "./analytics/svelte": {
106
+ "types": "./src/analytics/svelte.ts",
107
+ "module": "./src/analytics/svelte.ts",
108
+ "default": "./dist/analytics/svelte.js"
109
+ },
110
+ "./analytics/umami": {
111
+ "types": "./src/analytics/umami/protocol.ts",
112
+ "module": "./src/analytics/umami/protocol.ts",
113
+ "default": "./dist/analytics/umami/protocol.js"
114
+ },
115
+ "./analytics/reporting": {
116
+ "types": "./src/analytics/umami/reporting.ts",
117
+ "module": "./src/analytics/umami/reporting.ts",
118
+ "default": "./dist/analytics/umami/reporting.js"
119
+ },
120
+ "./og": {
121
+ "types": "./src/og/index.ts",
122
+ "module": "./src/og/index.ts",
123
+ "default": "./dist/og/index.js"
124
+ },
125
+ "./testimonials": {
126
+ "types": "./src/testimonials/types.ts",
127
+ "module": "./src/testimonials/types.ts",
128
+ "default": "./dist/testimonials/types.js"
129
+ },
130
+ "./testimonials/server": {
131
+ "types": "./src/testimonials/server.ts",
132
+ "module": "./src/testimonials/server.ts",
133
+ "default": "./dist/testimonials/server.js"
134
+ },
135
+ "./testimonials/render-content": {
136
+ "types": "./src/testimonials/render-content.ts",
137
+ "module": "./src/testimonials/render-content.ts",
138
+ "default": "./dist/testimonials/render-content.js"
139
+ },
140
+ "./ai": {
141
+ "types": "./src/ai/models.ts",
142
+ "module": "./src/ai/models.ts",
143
+ "default": "./dist/ai/models.js"
144
+ },
145
+ "./ai/image": {
146
+ "types": "./src/ai/image.ts",
147
+ "module": "./src/ai/image.ts",
148
+ "default": "./dist/ai/image.js"
149
+ }
150
+ },
151
+ "publishConfig": {
152
+ "access": "public",
153
+ "registry": "https://registry.npmjs.org/"
154
+ },
155
+ "dependencies": {
156
+ "clsx": "^2.1.1",
157
+ "tailwind-merge": "^3.7.0"
158
+ },
159
+ "devDependencies": {
160
+ "@keygraph/shannon": "^3.4.0",
161
+ "vite-plus": "1.3.0",
162
+ "vitest": "5.0.5"
163
+ },
164
+ "peerDependencies": {
165
+ "ai": ">=7.0.11",
166
+ "satori": ">=0.63.2",
167
+ "satori-html": ">=0.3.2",
168
+ "sharp": ">=0.33.0",
169
+ "ultrahtml": ">=1.7.0",
170
+ "vite": ">=5.0.0",
171
+ "wawoff2": ">=2.0.1"
172
+ },
173
+ "peerDependenciesMeta": {
174
+ "vite": {
175
+ "optional": true
176
+ },
177
+ "ai": {
178
+ "optional": true
179
+ },
180
+ "satori": {
181
+ "optional": true
182
+ },
183
+ "satori-html": {
184
+ "optional": true
185
+ },
186
+ "ultrahtml": {
187
+ "optional": true
188
+ },
189
+ "wawoff2": {
190
+ "optional": true
191
+ },
192
+ "sharp": {
193
+ "optional": true
194
+ }
195
+ },
196
+ "scripts": {
197
+ "test": "vp test run",
198
+ "typecheck": "tsc --noEmit",
199
+ "shannon": "shannon",
200
+ "build": "node ../../scripts/build-dist.mjs"
201
+ },
202
+ "license": "MIT"
203
+ }