@stacksjs/browser 0.70.57 → 0.70.59

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.
@@ -0,0 +1,61 @@
1
+ import { createBrowserModel } from "bun-query-builder/browser";
2
+ const modelModules = typeof import.meta.glob === "function" ? import.meta.glob("~/app/Models/*.ts", { eager: !0 }) : {};
3
+ export function loadBrowserModels() {
4
+ if (typeof window > "u")
5
+ return;
6
+ if (!window.StacksBrowser)
7
+ window.StacksBrowser = {};
8
+ for (const [path, module] of Object.entries(modelModules)) {
9
+ const definition = module.default;
10
+ if (!definition || !definition.name) {
11
+ console.warn(`[model-loader] Skipping ${path}: no valid model definition`);
12
+ continue;
13
+ }
14
+ if (!definition.traits?.useApi?.uri)
15
+ continue;
16
+ try {
17
+ const browserModel = createBrowserModel({
18
+ name: definition.name,
19
+ table: definition.table,
20
+ primaryKey: definition.primaryKey || "id",
21
+ traits: {
22
+ useUuid: definition.traits?.useUuid ?? !1,
23
+ useTimestamps: definition.traits?.useTimestamps ?? !0,
24
+ useSoftDeletes: definition.traits?.useSoftDeletes ?? !1,
25
+ useApi: definition.traits?.useApi
26
+ },
27
+ attributes: extractBrowserAttributes(definition.attributes ?? {})
28
+ });
29
+ window.StacksBrowser[definition.name] = browserModel;
30
+ } catch (error) {
31
+ console.error(`[model-loader] Failed to create browser model for ${definition.name}:`, error);
32
+ }
33
+ }
34
+ }
35
+ function extractBrowserAttributes(attributes) {
36
+ const browserAttrs = {};
37
+ for (const [name, attr] of Object.entries(attributes))
38
+ browserAttrs[name] = {
39
+ fillable: attr.fillable ?? !1,
40
+ hidden: attr.hidden ?? !1,
41
+ guarded: attr.guarded ?? !1,
42
+ nullable: attr.nullable ?? !1
43
+ };
44
+ return browserAttrs;
45
+ }
46
+ export function getBrowserModel(name) {
47
+ if (typeof window > "u")
48
+ return null;
49
+ return window.StacksBrowser?.[name] ?? null;
50
+ }
51
+ export function getBrowserModelNames() {
52
+ if (typeof window > "u")
53
+ return [];
54
+ const stacksBrowser = window.StacksBrowser;
55
+ if (!stacksBrowser)
56
+ return [];
57
+ return Object.keys(stacksBrowser).filter((key) => {
58
+ const value = stacksBrowser[key];
59
+ return value != null && typeof value.all === "function" && typeof value.find === "function";
60
+ });
61
+ }
@@ -0,0 +1,3 @@
1
+ export function isGeneralError(error) {
2
+ return "error" in error;
3
+ }
@@ -0,0 +1,4 @@
1
+ export { toString } from "@stacksjs/strings";
2
+ export async function loop(times, callback) {
3
+ Array.from({ length: times }).forEach(async (_, i) => await callback(i));
4
+ }
@@ -0,0 +1,64 @@
1
+ import { loadStripe } from "@stripe/stripe-js";
2
+ const stacksConfig = globalThis.__STACKS_CONFIG__ || {};
3
+ export const publishableKey = stacksConfig.FRONTEND_STRIPE_PUBLIC_KEY || "";
4
+ let client;
5
+ export async function loadCardElement(clientSecret) {
6
+ client = await loadStripe(publishableKey);
7
+ const cardElement = client.elements({ clientSecret }).create("card");
8
+ cardElement.mount("#card-element");
9
+ return cardElement;
10
+ }
11
+ export async function loadPaymentElement(clientSecret) {
12
+ client = await loadStripe(publishableKey);
13
+ const elements = client.elements({ clientSecret });
14
+ elements.create("payment", {
15
+ fields: { billingDetails: "auto" }
16
+ }).mount("#payment-element");
17
+ return elements;
18
+ }
19
+ export async function confirmCardSetup(clientSecret, elements) {
20
+ const data = await client.confirmCardSetup(clientSecret, { payment_method: { card: elements } }), { setupIntent, error } = data;
21
+ return { setupIntent, error };
22
+ }
23
+ export async function confirmCardPayment(clientSecret, elements) {
24
+ try {
25
+ const data = await client.confirmCardPayment(clientSecret, {
26
+ payment_method: {
27
+ card: elements,
28
+ billing_details: {
29
+ name: stacksConfig.USER_NAME || ""
30
+ }
31
+ }
32
+ }), { paymentIntent, error } = data;
33
+ return { paymentIntent, error };
34
+ } catch (err) {
35
+ console.error("Error confirming card payment:", err);
36
+ return { paymentIntent: null, error: err };
37
+ }
38
+ }
39
+ export async function createPaymentMethod(elements) {
40
+ try {
41
+ const data = await client.createPaymentMethod({
42
+ type: "card",
43
+ card: elements
44
+ }), { paymentIntent, error } = data;
45
+ return { paymentIntent, error };
46
+ } catch (err) {
47
+ console.error("Error confirming card payment:", err);
48
+ return { paymentIntent: null, error: err };
49
+ }
50
+ }
51
+ export async function confirmPayment(elements) {
52
+ try {
53
+ const data = await client.confirmPayment({
54
+ elements,
55
+ confirmParams: {
56
+ return_url: `${window.location.origin}/settings/billing`
57
+ }
58
+ }), { paymentIntent, error } = data;
59
+ return { paymentIntent, error };
60
+ } catch (err) {
61
+ console.error("Error confirming card payment:", err);
62
+ return { paymentIntent: null, error: err };
63
+ }
64
+ }
@@ -0,0 +1,2 @@
1
+ export { useDateFormat, useNow } from "@stacksjs/composables";
2
+ export { format, parse } from "@stacksjs/datetime";
@@ -0,0 +1,43 @@
1
+ export function debounce(fn, wait = 0, options = {}) {
2
+ const { leading = !1, trailing = !0 } = options;
3
+ let timeout = null, lastArgs = null, lastThis = null, result;
4
+ const invokeFunc = () => {
5
+ if (lastArgs) {
6
+ result = fn.apply(lastThis, lastArgs);
7
+ lastArgs = null;
8
+ lastThis = null;
9
+ }
10
+ return result;
11
+ }, cancel = () => {
12
+ if (timeout) {
13
+ clearTimeout(timeout);
14
+ timeout = null;
15
+ }
16
+ lastArgs = null;
17
+ lastThis = null;
18
+ }, flush = () => {
19
+ if (timeout) {
20
+ clearTimeout(timeout);
21
+ timeout = null;
22
+ return invokeFunc();
23
+ }
24
+ return result;
25
+ }, debounced = function(...args) {
26
+ lastArgs = args;
27
+ lastThis = this;
28
+ const shouldCallNow = leading && !timeout;
29
+ if (timeout)
30
+ clearTimeout(timeout);
31
+ timeout = setTimeout(() => {
32
+ timeout = null;
33
+ if (trailing && lastArgs)
34
+ invokeFunc();
35
+ }, wait);
36
+ if (shouldCallNow)
37
+ return invokeFunc();
38
+ return result;
39
+ };
40
+ debounced.cancel = cancel;
41
+ debounced.flush = flush;
42
+ return debounced;
43
+ }
@@ -0,0 +1,94 @@
1
+ let loading = !1, token = "";
2
+ const baseURL = "/";
3
+ function appendParam(search, key, value) {
4
+ if (value === void 0 || value === null)
5
+ return;
6
+ if (Array.isArray(value)) {
7
+ for (const v of value)
8
+ appendParam(search, key, v);
9
+ return;
10
+ }
11
+ if (typeof value === "object") {
12
+ search.append(key, JSON.stringify(value));
13
+ return;
14
+ }
15
+ search.append(key, String(value));
16
+ }
17
+ function buildUrl(url, params) {
18
+ const full = /^https?:\/\//i.test(url) ? url : `${baseURL.replace(/\/$/, "")}/${url.replace(/^\//, "")}`;
19
+ if (!params || Object.keys(params).length === 0)
20
+ return full;
21
+ const search = new URLSearchParams;
22
+ for (const [key, value] of Object.entries(params))
23
+ appendParam(search, key, value);
24
+ const qs = search.toString();
25
+ if (!qs)
26
+ return full;
27
+ return full.includes("?") ? `${full}&${qs}` : `${full}?${qs}`;
28
+ }
29
+ function applyAuth(headers) {
30
+ const h = headers ?? new Headers;
31
+ if (token && !h.has("Authorization"))
32
+ h.set("Authorization", `Bearer ${token}`);
33
+ return h;
34
+ }
35
+ async function parseBody(response) {
36
+ const contentType = response.headers.get("content-type") ?? "";
37
+ if (contentType.includes("application/json"))
38
+ return await response.json();
39
+ if (contentType.startsWith("text/") || contentType.includes("xml"))
40
+ return await response.text();
41
+ return await response.blob();
42
+ }
43
+ async function request(method, url, params, headers) {
44
+ const sendsBody = method !== "GET" && method !== "DELETE", finalUrl = sendsBody ? buildUrl(url) : buildUrl(url, params), finalHeaders = applyAuth(headers), init = { method, headers: finalHeaders };
45
+ if (sendsBody && params !== void 0) {
46
+ if (!finalHeaders.has("Content-Type"))
47
+ finalHeaders.set("Content-Type", "application/json");
48
+ init.body = JSON.stringify(params);
49
+ }
50
+ if (sendsBody)
51
+ loading = !0;
52
+ try {
53
+ const response = await fetch(finalUrl, init);
54
+ if (!response.ok) {
55
+ const errorBody = await parseBody(response).catch(() => null), error = Error(`Request failed with status ${response.status}`);
56
+ error.status = response.status;
57
+ error.data = errorBody;
58
+ throw error;
59
+ }
60
+ return await parseBody(response);
61
+ } finally {
62
+ if (sendsBody)
63
+ loading = !1;
64
+ }
65
+ }
66
+ async function get(url, params, headers) {
67
+ return await request("GET", url, params, headers);
68
+ }
69
+ async function post(url, params, headers) {
70
+ return await request("POST", url, params, headers);
71
+ }
72
+ async function patch(url, params, headers) {
73
+ return await request("PATCH", url, params, headers);
74
+ }
75
+ async function put(url, params, headers) {
76
+ return await request("PUT", url, params, headers);
77
+ }
78
+ async function destroy(url, params, headers) {
79
+ return await request("DELETE", url, params, headers);
80
+ }
81
+ function setToken(authToken) {
82
+ token = authToken;
83
+ }
84
+ export const Fetch = {
85
+ get,
86
+ post,
87
+ patch,
88
+ put,
89
+ destroy,
90
+ baseURL,
91
+ token,
92
+ setToken,
93
+ loading
94
+ };
@@ -0,0 +1,7 @@
1
+ export function batchInvoke(functions) {
2
+ functions.forEach((fn) => fn?.());
3
+ }
4
+ export function tap(value, callback) {
5
+ callback(value);
6
+ return value;
7
+ }
@@ -0,0 +1,12 @@
1
+ export function notNullish(v) {
2
+ return v != null;
3
+ }
4
+ export function noNull(v) {
5
+ return v !== null;
6
+ }
7
+ export function notUndefined(v) {
8
+ return v !== void 0;
9
+ }
10
+ export function isTruthy(v) {
11
+ return Boolean(v);
12
+ }
@@ -0,0 +1,17 @@
1
+ export * from "./base";
2
+ export * from "./billable";
3
+ export * from "./date";
4
+ export * from "./debounce";
5
+ export * from "./fetch";
6
+ export * from "./function";
7
+ export * from "./guards";
8
+ export * from "./lazy";
9
+ export * from "./math";
10
+ export * from "./plans";
11
+ export * from "./promise";
12
+ export * from "./random";
13
+ export * from "./regex";
14
+ export * from "./retry";
15
+ export * from "./sleep";
16
+ export * from "./throttle";
17
+ export * from "./vendors";
@@ -0,0 +1,9 @@
1
+ export function lazy(getter) {
2
+ return {
3
+ get value() {
4
+ const value = getter();
5
+ Object.defineProperty(this, "value", { value });
6
+ return value;
7
+ }
8
+ };
9
+ }
@@ -0,0 +1,23 @@
1
+ export const clamp = (n, min, max) => Math.min(max, Math.max(min, n));
2
+ export function rand(min, max) {
3
+ min = Math.ceil(min);
4
+ max = Math.floor(max);
5
+ return Math.floor(Math.random() * (max - min + 1)) + min;
6
+ }
7
+ export {
8
+ and,
9
+ logicNot,
10
+ logicOr,
11
+ or,
12
+ useAbs,
13
+ useAverage,
14
+ useCeil,
15
+ useClamp,
16
+ useFloor,
17
+ useMax,
18
+ useMin,
19
+ usePrecision,
20
+ useRound,
21
+ useSum,
22
+ useTrunc
23
+ } from "@stacksjs/composables";
@@ -0,0 +1,135 @@
1
+ export const saas = {
2
+ plans: [
3
+ {
4
+ productName: "Stacks Hobby",
5
+ description: "All the Stacks features.",
6
+ pricing: [
7
+ {
8
+ key: "stacks_hobby_early_monthly",
9
+ price: 1900,
10
+ interval: "month",
11
+ currency: "usd"
12
+ },
13
+ {
14
+ key: "stacks_hobby_launch_monthly",
15
+ price: 2900,
16
+ interval: "month",
17
+ currency: "usd"
18
+ },
19
+ {
20
+ key: "stacks_hobby_monthly",
21
+ price: 3900,
22
+ interval: "month",
23
+ currency: "usd"
24
+ },
25
+ {
26
+ key: "stacks_hobby_yearly",
27
+ price: 37900,
28
+ interval: "year",
29
+ currency: "usd"
30
+ }
31
+ ],
32
+ metadata: {
33
+ createdBy: "admin",
34
+ version: "1.0.0"
35
+ }
36
+ },
37
+ {
38
+ productName: "Stacks Pro",
39
+ description: "All the Stacks features, including being able to invite team members.",
40
+ pricing: [
41
+ {
42
+ key: "stacks_pro_early_monthly",
43
+ price: 3900,
44
+ interval: "month",
45
+ currency: "usd"
46
+ },
47
+ {
48
+ key: "stacks_pro_monthly",
49
+ price: 5900,
50
+ interval: "month",
51
+ currency: "usd"
52
+ },
53
+ {
54
+ key: "stacks_pro_yearly",
55
+ price: 57900,
56
+ interval: "year",
57
+ currency: "usd"
58
+ },
59
+ {
60
+ key: "stacks_pro_early_yearly",
61
+ price: 39000,
62
+ interval: "year",
63
+ currency: "usd"
64
+ }
65
+ ],
66
+ metadata: {
67
+ createdBy: "admin",
68
+ version: "1.0.0"
69
+ }
70
+ },
71
+ {
72
+ productName: "Stacks Lifetime",
73
+ description: "One-time lifetime access to all Stacks features.",
74
+ pricing: [
75
+ {
76
+ key: "stacks_hobby_early_lifetime",
77
+ price: 17900,
78
+ currency: "usd"
79
+ },
80
+ {
81
+ key: "stacks_hobby_launch_lifetime",
82
+ price: 27900,
83
+ currency: "usd"
84
+ },
85
+ {
86
+ key: "stacks_hobby_lifetime",
87
+ price: 47900,
88
+ currency: "usd"
89
+ },
90
+ {
91
+ key: "stacks_pro_early_lifetime",
92
+ price: 27900,
93
+ currency: "usd"
94
+ },
95
+ {
96
+ key: "stacks_pro_launch_lifetime",
97
+ price: 37900,
98
+ currency: "usd"
99
+ },
100
+ {
101
+ key: "stacks_pro_lifetime",
102
+ price: 74900,
103
+ currency: "usd"
104
+ }
105
+ ],
106
+ metadata: {
107
+ createdBy: "admin",
108
+ version: "1.0.0"
109
+ }
110
+ }
111
+ ],
112
+ webhook: {
113
+ endpoint: "your-webhook-endpoint",
114
+ secret: "your-webhook-secret"
115
+ },
116
+ currencies: ["usd"],
117
+ coupons: [],
118
+ products: [
119
+ {
120
+ name: "Stacks Hobby",
121
+ description: "All the Stacks features.",
122
+ images: ["image-url"]
123
+ },
124
+ {
125
+ name: "Stacks Pro",
126
+ description: "All the Stacks features, including team invites.",
127
+ images: ["image-url"]
128
+ },
129
+ {
130
+ name: "Stacks Lifetime",
131
+ description: "Lifetime access to Stacks features.",
132
+ images: ["image-url"]
133
+ }
134
+ ]
135
+ };
@@ -0,0 +1,55 @@
1
+ export function createSingletonPromise(fn) {
2
+ let _promise;
3
+ function wrapper() {
4
+ if (!_promise)
5
+ _promise = fn();
6
+ return _promise;
7
+ }
8
+ wrapper.reset = async () => {
9
+ const _prev = _promise;
10
+ _promise = void 0;
11
+ if (_prev)
12
+ await _prev;
13
+ };
14
+ return wrapper;
15
+ }
16
+ export function createPromiseLock() {
17
+ let currentPromise = Promise.resolve();
18
+ const queue = [];
19
+ return {
20
+ async run(fn) {
21
+ const taskPromise = (async () => {
22
+ await currentPromise;
23
+ return fn();
24
+ })();
25
+ queue.push(taskPromise);
26
+ currentPromise = taskPromise.catch(() => {}).finally(() => {
27
+ const index = queue.indexOf(taskPromise);
28
+ if (index > -1)
29
+ queue.splice(index, 1);
30
+ });
31
+ return taskPromise;
32
+ },
33
+ async wait() {
34
+ while (queue.length > 0)
35
+ await Promise.all(queue);
36
+ },
37
+ isWaiting() {
38
+ return queue.length > 0;
39
+ },
40
+ clear() {
41
+ queue.length = 0;
42
+ currentPromise = Promise.resolve();
43
+ }
44
+ };
45
+ }
46
+ export function createControlledPromise() {
47
+ let resolve, reject;
48
+ const promise = new Promise((_resolve, _reject) => {
49
+ resolve = _resolve;
50
+ reject = _reject;
51
+ });
52
+ promise.resolve = resolve;
53
+ promise.reject = reject;
54
+ return promise;
55
+ }
@@ -0,0 +1,67 @@
1
+ const POOL_SIZE_MULTIPLIER = 128;
2
+ let pool = null, poolOffset = 0;
3
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
4
+ function fillPool(bytes) {
5
+ if (!pool || pool.length < bytes) {
6
+ pool = new Uint8Array(bytes * POOL_SIZE_MULTIPLIER);
7
+ globalThis.crypto.getRandomValues(pool);
8
+ poolOffset = 0;
9
+ } else if (poolOffset + bytes > pool.length) {
10
+ globalThis.crypto.getRandomValues(pool);
11
+ poolOffset = 0;
12
+ }
13
+ poolOffset += bytes;
14
+ }
15
+ function randomPool(bytes) {
16
+ fillPool(bytes |= 0);
17
+ if (!pool)
18
+ throw Error("Pool is not initialized");
19
+ return pool.subarray(poolOffset - bytes, poolOffset);
20
+ }
21
+ function customRandom(alphabet, defaultSize, getRandom) {
22
+ const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1, step = Math.ceil(1.6 * mask * defaultSize / alphabet.length);
23
+ return (size = defaultSize) => {
24
+ let id = "";
25
+ while (!0) {
26
+ const bytes = getRandom(step);
27
+ let i = step;
28
+ while (i--) {
29
+ const byte = bytes[i];
30
+ if (byte === void 0)
31
+ continue;
32
+ id += alphabet[byte & mask] || "";
33
+ if (id.length >= size)
34
+ return id;
35
+ }
36
+ }
37
+ };
38
+ }
39
+ function customAlphabet(alphabet, size = 21) {
40
+ return customRandom(alphabet, size, randomPool);
41
+ }
42
+ function randomNonSecure(size = 21) {
43
+ let id = "", i = size | 0;
44
+ while (i--)
45
+ id += urlAlphabet[Math.random() * 64 | 0];
46
+ return id;
47
+ }
48
+ function random(size = 21) {
49
+ fillPool(size |= 0);
50
+ if (!pool)
51
+ return "";
52
+ let id = "";
53
+ for (let i = poolOffset - size;i < poolOffset; i++) {
54
+ const byte = pool[i];
55
+ if (byte === void 0)
56
+ continue;
57
+ id += urlAlphabet[byte & 63];
58
+ }
59
+ return id;
60
+ }
61
+
62
+ export {
63
+ customAlphabet,
64
+ customRandom,
65
+ random,
66
+ randomNonSecure
67
+ };
@@ -0,0 +1,29 @@
1
+ export {
2
+ anyOf,
3
+ carriageReturn,
4
+ caseInsensitive,
5
+ char,
6
+ charIn,
7
+ charNotIn,
8
+ digit,
9
+ dotAll,
10
+ exactly,
11
+ global,
12
+ letter,
13
+ linefeed,
14
+ maybe,
15
+ multiline,
16
+ not,
17
+ oneOrMore,
18
+ sticky,
19
+ tab,
20
+ unicode,
21
+ whitespace,
22
+ withIndices,
23
+ word,
24
+ wordBoundary,
25
+ wordChar
26
+ } from "magic-regexp";
27
+ export function createRegExp(pattern, options = {}) {
28
+ return new RegExp(pattern, options.flags);
29
+ }
@@ -0,0 +1,28 @@
1
+ export function retry(fn, options = {}) {
2
+ const { retries = 3, initialDelay = 1000, backoffFactor = 2, jitter = !0 } = options;
3
+ return new Promise((resolve, reject) => {
4
+ let attemptCount = 0;
5
+ const attempt = async () => {
6
+ try {
7
+ resolve(await fn());
8
+ } catch (err) {
9
+ if (attemptCount >= retries)
10
+ reject(err);
11
+ else {
12
+ const delay = calculateDelay(attemptCount, initialDelay, backoffFactor, jitter);
13
+ setTimeout(() => attempt(), delay);
14
+ attemptCount++;
15
+ }
16
+ }
17
+ };
18
+ attempt();
19
+ });
20
+ }
21
+ export function calculateDelay(attemptCount, initialDelay, backoffFactor, jitter) {
22
+ let delay = initialDelay * backoffFactor ** attemptCount;
23
+ if (jitter) {
24
+ const random = Math.random(), jitterValue = delay * 0.3;
25
+ delay = delay + jitterValue * (random - 0.5) * 2;
26
+ }
27
+ return delay;
28
+ }