@frigear-nu/subscriptions 0.0.0-stage → 0.0.3
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/README.md +127 -2
- package/dist/module.d.mts +155 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +120 -0
- package/dist/runtime/app/components/SubscriptionPlans.d.vue.ts +71 -0
- package/dist/runtime/app/components/SubscriptionPlans.vue +215 -0
- package/dist/runtime/app/components/SubscriptionPlans.vue.d.ts +71 -0
- package/dist/runtime/app/components/SubscriptionStatus.d.vue.ts +44 -0
- package/dist/runtime/app/components/SubscriptionStatus.vue +138 -0
- package/dist/runtime/app/components/SubscriptionStatus.vue.d.ts +44 -0
- package/dist/runtime/app/composables/index.d.ts +4 -0
- package/dist/runtime/app/composables/index.js +2 -0
- package/dist/runtime/app/composables/usePlans.d.ts +23 -0
- package/dist/runtime/app/composables/usePlans.js +10 -0
- package/dist/runtime/app/composables/useSubscription.d.ts +27 -0
- package/dist/runtime/app/composables/useSubscription.js +73 -0
- package/dist/runtime/app/middleware/subscription.d.ts +2 -0
- package/dist/runtime/app/middleware/subscription.js +14 -0
- package/dist/runtime/app/pages/cancel.d.vue.ts +3 -0
- package/dist/runtime/app/pages/cancel.vue +23 -0
- package/dist/runtime/app/pages/cancel.vue.d.ts +3 -0
- package/dist/runtime/app/pages/membership.d.vue.ts +3 -0
- package/dist/runtime/app/pages/membership.vue +18 -0
- package/dist/runtime/app/pages/membership.vue.d.ts +3 -0
- package/dist/runtime/app/pages/success.d.vue.ts +3 -0
- package/dist/runtime/app/pages/success.vue +33 -0
- package/dist/runtime/app/pages/success.vue.d.ts +3 -0
- package/dist/runtime/i18n/lang/da.json +55 -0
- package/dist/runtime/i18n/lang/en.json +55 -0
- package/dist/runtime/menus.d.ts +2 -0
- package/dist/runtime/menus.js +16 -0
- package/dist/runtime/server/db/schema.sqlite.d.ts +231 -0
- package/dist/runtime/server/db/schema.sqlite.js +20 -0
- package/dist/runtime/server/routes/subscriptions/checkout.post.d.ts +2 -0
- package/dist/runtime/server/routes/subscriptions/checkout.post.js +125 -0
- package/dist/runtime/server/routes/subscriptions/me.get.d.ts +2 -0
- package/dist/runtime/server/routes/subscriptions/me.get.js +22 -0
- package/dist/runtime/server/routes/subscriptions/plans.get.d.ts +2 -0
- package/dist/runtime/server/routes/subscriptions/plans.get.js +3 -0
- package/dist/runtime/server/routes/subscriptions/portal.post.d.ts +2 -0
- package/dist/runtime/server/routes/subscriptions/portal.post.js +17 -0
- package/dist/runtime/server/stripe/subscription-events.d.ts +2 -0
- package/dist/runtime/server/stripe/subscription-events.js +36 -0
- package/dist/runtime/server/utils/plans.d.ts +6 -0
- package/dist/runtime/server/utils/plans.js +64 -0
- package/dist/runtime/server/utils/subscriptions.d.ts +13 -0
- package/dist/runtime/server/utils/subscriptions.js +16 -0
- package/dist/runtime/utils/index.d.ts +4 -0
- package/dist/runtime/utils/index.js +2 -0
- package/dist/runtime/utils/plans.d.ts +32 -0
- package/dist/runtime/utils/plans.js +33 -0
- package/dist/types.d.mts +3 -0
- package/package.json +88 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { resolveSameOriginUrl } from "../../../utils/plans.js";
|
|
3
|
+
const bodySchema = z.object({ returnUrl: z.string().optional() });
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
const { user } = await requireUserSession(event);
|
|
6
|
+
const { returnUrl } = await readValidatedBody(event, bodySchema.parse);
|
|
7
|
+
const origin = getRequestURL(event).origin;
|
|
8
|
+
let returnTo = `${origin}${useRuntimeConfig().public.subscriptions?.pages?.membership ?? "/"}`;
|
|
9
|
+
if (returnUrl) {
|
|
10
|
+
const safe = resolveSameOriginUrl(returnUrl, origin);
|
|
11
|
+
if (!safe) throw createError({ statusCode: 400, statusMessage: "returnUrl must be on the same origin." });
|
|
12
|
+
returnTo = safe;
|
|
13
|
+
}
|
|
14
|
+
const customer = await ensureStripeCustomer(user.id);
|
|
15
|
+
const session = await useStripe().billingPortal.sessions.create({ customer, return_url: returnTo });
|
|
16
|
+
return { url: session.url };
|
|
17
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { db, schema } from "@nuxthub/db";
|
|
2
|
+
import { defineStripeEventHandler } from "@frigear-nu/stripe/utils";
|
|
3
|
+
import { resolvePlanKey } from "../../utils/plans.js";
|
|
4
|
+
const toDate = (seconds) => typeof seconds === "number" ? new Date(seconds * 1e3) : null;
|
|
5
|
+
export default defineStripeEventHandler(async (event, ctx) => {
|
|
6
|
+
const subscription = event.data.object;
|
|
7
|
+
if (ctx.userId === null) {
|
|
8
|
+
console.warn(`[@frigear-nu/subscriptions] Ignoring ${event.type} (${event.id}): no local user is linked to the Stripe customer.`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const items = subscription.items?.data ?? [];
|
|
12
|
+
const first = items[0];
|
|
13
|
+
const priceId = first?.price?.id ?? "";
|
|
14
|
+
const plans = useRuntimeConfig().subscriptions?.plans ?? {};
|
|
15
|
+
const planKey = first?.price ? resolvePlanKey(plans, { id: first.price.id, lookupKey: first.price.lookup_key }) : null;
|
|
16
|
+
const starts = items.map((item) => item.current_period_start).filter((v) => typeof v === "number");
|
|
17
|
+
const ends = items.map((item) => item.current_period_end).filter((v) => typeof v === "number");
|
|
18
|
+
const endedAt = toDate(subscription.ended_at) ?? (event.type === "customer.subscription.deleted" ? toDate(subscription.canceled_at) ?? /* @__PURE__ */ new Date() : null);
|
|
19
|
+
const values = {
|
|
20
|
+
userId: ctx.userId,
|
|
21
|
+
planKey,
|
|
22
|
+
priceId,
|
|
23
|
+
status: event.type === "customer.subscription.deleted" ? "canceled" : subscription.status,
|
|
24
|
+
cancelAtPeriodEnd: subscription.cancel_at_period_end ?? false,
|
|
25
|
+
currentPeriodStart: starts.length ? toDate(Math.min(...starts)) : null,
|
|
26
|
+
currentPeriodEnd: ends.length ? toDate(Math.max(...ends)) : null,
|
|
27
|
+
endedAt,
|
|
28
|
+
rawItems: items.map((item) => ({
|
|
29
|
+
id: item.id,
|
|
30
|
+
priceId: item.price.id,
|
|
31
|
+
lookupKey: item.price.lookup_key ?? null,
|
|
32
|
+
quantity: item.quantity ?? null
|
|
33
|
+
}))
|
|
34
|
+
};
|
|
35
|
+
await db.insert(schema.subscriptions).values({ id: subscription.id, ...values }).onConflictDoUpdate({ target: schema.subscriptions.id, set: { ...values, updatedAt: /* @__PURE__ */ new Date() } });
|
|
36
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { ResolvedPlan } from '../../../types.js';
|
|
3
|
+
/** The configured plans with their live prices from Stripe, cached in KV for ~5 minutes. Unfiltered. */
|
|
4
|
+
export declare function getResolvedPlans(): Promise<ResolvedPlan[]>;
|
|
5
|
+
/** Resolved plans passed through the `subscriptions:plans:filter` policy hook. */
|
|
6
|
+
export declare function getAvailablePlans(event?: H3Event): Promise<ResolvedPlan[]>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { kv } from "@nuxthub/kv";
|
|
2
|
+
const CACHE_TTL_SECONDS = 60 * 5;
|
|
3
|
+
const INTERVALS = ["day", "week", "month", "year"];
|
|
4
|
+
function toPlanPrice(price) {
|
|
5
|
+
const recurring = price.recurring;
|
|
6
|
+
if (!recurring || !INTERVALS.includes(recurring.interval)) return null;
|
|
7
|
+
return {
|
|
8
|
+
id: price.id,
|
|
9
|
+
amount: price.unit_amount ?? null,
|
|
10
|
+
currency: price.currency,
|
|
11
|
+
interval: recurring.interval,
|
|
12
|
+
intervalCount: recurring.interval_count ?? 1,
|
|
13
|
+
trialPeriodDays: recurring.trial_period_days ?? null,
|
|
14
|
+
lookupKey: price.lookup_key ?? null
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async function fetchPrices(stripe, refs) {
|
|
18
|
+
const byRef = /* @__PURE__ */ new Map();
|
|
19
|
+
const lookupKeys = refs.filter((ref) => typeof ref !== "string").map((ref) => ref.lookupKey);
|
|
20
|
+
const ids = refs.filter((ref) => typeof ref === "string");
|
|
21
|
+
for (let i = 0; i < lookupKeys.length; i += 10) {
|
|
22
|
+
const chunk = lookupKeys.slice(i, i + 10);
|
|
23
|
+
const list = await stripe.prices.list({ lookup_keys: chunk, active: true, limit: 100 });
|
|
24
|
+
for (const price of list.data) {
|
|
25
|
+
if (price.lookup_key) byRef.set(`lookup:${price.lookup_key}`, price);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (const id of ids) {
|
|
29
|
+
try {
|
|
30
|
+
byRef.set(`id:${id}`, await stripe.prices.retrieve(id));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.warn(`[@frigear-nu/subscriptions] Could not load Stripe price "${id}":`, error instanceof Error ? error.message : error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return byRef;
|
|
36
|
+
}
|
|
37
|
+
export async function getResolvedPlans() {
|
|
38
|
+
const config = useRuntimeConfig().subscriptions?.plans ?? {};
|
|
39
|
+
const entries = Object.entries(config);
|
|
40
|
+
if (!entries.length) return [];
|
|
41
|
+
const cacheKey = `subscriptions:plans:${JSON.stringify(config)}`;
|
|
42
|
+
const cached = await kv.get(cacheKey).catch(() => null);
|
|
43
|
+
if (cached) return cached;
|
|
44
|
+
const stripe = useStripe();
|
|
45
|
+
const prices = await fetchPrices(stripe, entries.flatMap(([, plan]) => plan.prices));
|
|
46
|
+
const plans = entries.map(([key, plan]) => {
|
|
47
|
+
const resolved = plan.prices.map((ref) => prices.get(typeof ref === "string" ? `id:${ref}` : `lookup:${ref.lookupKey}`)).filter((price) => !!price && price.active !== false).map(toPlanPrice).filter((price) => price !== null);
|
|
48
|
+
return {
|
|
49
|
+
key,
|
|
50
|
+
label: plan.label ?? key,
|
|
51
|
+
...plan.description ? { description: plan.description } : {},
|
|
52
|
+
rank: plan.rank,
|
|
53
|
+
prices: resolved
|
|
54
|
+
};
|
|
55
|
+
}).sort((a, b) => a.rank - b.rank);
|
|
56
|
+
await kv.set(cacheKey, plans, { ttl: CACHE_TTL_SECONDS }).catch(() => {
|
|
57
|
+
});
|
|
58
|
+
return plans;
|
|
59
|
+
}
|
|
60
|
+
export async function getAvailablePlans(event) {
|
|
61
|
+
const ctx = { event, plans: structuredClone(await getResolvedPlans()) };
|
|
62
|
+
await useNitroApp().hooks.callHook("subscriptions:plans:filter", ctx);
|
|
63
|
+
return ctx.plans;
|
|
64
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type HasActiveSubscriptionOptions = {
|
|
2
|
+
/** Only count subscriptions on one of these plan keys. Omit for "any plan". */
|
|
3
|
+
plans?: string[];
|
|
4
|
+
/** Statuses that count as entitled. Defaults to `active` and `trialing`. */
|
|
5
|
+
statuses?: string[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The user's current entitled subscription row (default statuses `active`/`trialing`, not ended), the one
|
|
9
|
+
* with the latest period end when several match, or `null`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getUserSubscription(userId: number, options?: Pick<HasActiveSubscriptionOptions, 'statuses'>): Promise<any>;
|
|
12
|
+
/** Whether the user holds an entitled subscription, optionally restricted to specific plans. */
|
|
13
|
+
export declare function hasActiveSubscription(userId: number, options?: HasActiveSubscriptionOptions): Promise<boolean>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { db, schema } from "@nuxthub/db";
|
|
2
|
+
import { desc, eq } from "drizzle-orm";
|
|
3
|
+
import { DEFAULT_ENTITLED_STATUSES } from "../../utils/plans.js";
|
|
4
|
+
export async function getUserSubscription(userId, options = {}) {
|
|
5
|
+
const statuses = options.statuses ?? DEFAULT_ENTITLED_STATUSES;
|
|
6
|
+
const rows = await db.select().from(schema.subscriptions).where(eq(schema.subscriptions.userId, userId)).orderBy(desc(schema.subscriptions.createdAt));
|
|
7
|
+
const entitled = rows.filter((row) => statuses.includes(row.status) && !row.endedAt).sort((a, b) => (b.currentPeriodEnd?.getTime() ?? 0) - (a.currentPeriodEnd?.getTime() ?? 0));
|
|
8
|
+
return entitled[0] ?? null;
|
|
9
|
+
}
|
|
10
|
+
export async function hasActiveSubscription(userId, options = {}) {
|
|
11
|
+
const statuses = options.statuses ?? DEFAULT_ENTITLED_STATUSES;
|
|
12
|
+
const rows = await db.select().from(schema.subscriptions).where(eq(schema.subscriptions.userId, userId));
|
|
13
|
+
return rows.some(
|
|
14
|
+
(row) => statuses.includes(row.status) && !row.endedAt && (!options.plans?.length || row.planKey !== null && options.plans.includes(row.planKey))
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getUserSubscription, hasActiveSubscription } from '../server/utils/subscriptions.js';
|
|
2
|
+
export type { HasActiveSubscriptionOptions } from '../server/utils/subscriptions.js';
|
|
3
|
+
export { decideChangeKind, resolvePlanKey, resolveSameOriginUrl, DEFAULT_ENTITLED_STATUSES } from './plans.js';
|
|
4
|
+
export type { CheckoutBeforeContext, PlansFilterContext, ResolvedPlan, PlanPrice, SubscriptionDto, ModuleOptions, } from '../../types.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PlanPriceRef, ResolvedPlan } from '../../types.js';
|
|
2
|
+
export declare const DEFAULT_ENTITLED_STATUSES: string[];
|
|
3
|
+
/** Statuses that count as "holds a live subscription" when deciding between a new checkout and a plan change. */
|
|
4
|
+
export declare const LIVE_STATUSES: string[];
|
|
5
|
+
type PlanConfigLike = Record<string, {
|
|
6
|
+
prices: PlanPriceRef[];
|
|
7
|
+
rank: number;
|
|
8
|
+
}>;
|
|
9
|
+
/** Maps a Stripe price (id and/or lookup key) to the configured plan key that lists it, or `null`. */
|
|
10
|
+
export declare function resolvePlanKey(plans: PlanConfigLike, price: {
|
|
11
|
+
id: string;
|
|
12
|
+
lookupKey?: string | null;
|
|
13
|
+
}): string | null;
|
|
14
|
+
export type ChangeKind = 'upgrade' | 'downgrade';
|
|
15
|
+
/**
|
|
16
|
+
* Decides how to move a subscriber between plans from the plans' `rank` (never from price amounts).
|
|
17
|
+
* A higher target rank is an upgrade (immediate, prorated); a lower or equal rank (including a
|
|
18
|
+
* billing-interval change within one plan) is deferred to the end of the current period. A current
|
|
19
|
+
* plan that isn't configured counts as the lowest rank.
|
|
20
|
+
*/
|
|
21
|
+
export declare function decideChangeKind(currentRank: number | null | undefined, targetRank: number): ChangeKind;
|
|
22
|
+
/**
|
|
23
|
+
* Validates a redirect URL supplied by a client: it must be a relative path (`/x`) or an absolute URL on
|
|
24
|
+
* `origin`. Returns an absolute URL on that origin, or `null` when it is unsafe.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveSameOriginUrl(input: string, origin: string): string | null;
|
|
27
|
+
/** Finds a price (and its plan) in already resolved plans by price id. */
|
|
28
|
+
export declare function findPrice(plans: ResolvedPlan[], priceId: string): {
|
|
29
|
+
plan: ResolvedPlan;
|
|
30
|
+
price: import(".").PlanPrice;
|
|
31
|
+
} | null;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const DEFAULT_ENTITLED_STATUSES = ["active", "trialing"];
|
|
2
|
+
export const LIVE_STATUSES = ["active", "trialing", "past_due"];
|
|
3
|
+
export function resolvePlanKey(plans, price) {
|
|
4
|
+
for (const [key, plan] of Object.entries(plans)) {
|
|
5
|
+
const match = plan.prices.some(
|
|
6
|
+
(ref) => typeof ref === "string" ? ref === price.id : !!price.lookupKey && ref.lookupKey === price.lookupKey
|
|
7
|
+
);
|
|
8
|
+
if (match) return key;
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
export function decideChangeKind(currentRank, targetRank) {
|
|
13
|
+
if (currentRank === null || currentRank === void 0) return "upgrade";
|
|
14
|
+
return targetRank > currentRank ? "upgrade" : "downgrade";
|
|
15
|
+
}
|
|
16
|
+
export function resolveSameOriginUrl(input, origin) {
|
|
17
|
+
if (input.startsWith("/")) {
|
|
18
|
+
if (input.startsWith("//") || input.startsWith("/\\")) return null;
|
|
19
|
+
return `${origin}${input}`;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return new URL(input).origin === new URL(origin).origin ? input : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function findPrice(plans, priceId) {
|
|
28
|
+
for (const plan of plans) {
|
|
29
|
+
const price = plan.prices.find((item) => item.id === priceId);
|
|
30
|
+
if (price) return { plan, price };
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default } from './module.mjs'
|
|
2
|
+
|
|
3
|
+
export { type CheckoutBeforeContext, type CheckoutBody, type CheckoutResponse, type MeResponse, type ModuleOptions, type PlanConfig, type PlanPrice, type PlanPriceRef, type PlansFilterContext, type PlansResponse, type PortalBody, type PublicModuleOptions, type PublicPlan, type ResolvedPlan, type SubscriptionDto, type SubscriptionsRuntimeConfig } from './module.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,91 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frigear-nu/subscriptions",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Recurring subscriptions (plans, checkout, upgrade/downgrade, billing portal and entitlements) for the frigear ecosystem, built on @frigear-nu/stripe.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/frigear-nu/frigear-nu.git",
|
|
8
|
+
"directory": "packages/subscriptions"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/module.mjs",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/types.d.mts",
|
|
16
|
+
"import": "./dist/module.mjs",
|
|
17
|
+
"default": "./dist/module.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./utils": {
|
|
20
|
+
"types": "./dist/runtime/utils/index.d.ts",
|
|
21
|
+
"import": "./dist/runtime/utils/index.js",
|
|
22
|
+
"default": "./dist/runtime/utils/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"workspaces": [
|
|
29
|
+
"playground"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@frigear-nu/menu": "0.0.3",
|
|
33
|
+
"@frigear-nu/module-kit": "0.1.2",
|
|
34
|
+
"@frigear-nu/stripe": "0.0.3",
|
|
35
|
+
"@frigear-nu/users": "1.0.3",
|
|
36
|
+
"@libsql/client": "^0.18.0",
|
|
37
|
+
"@nuxt/kit": "^4.6.0",
|
|
38
|
+
"@vueuse/core": "^15.0.0",
|
|
39
|
+
"drizzle-kit": "^0.31.11",
|
|
40
|
+
"drizzle-orm": "^0.45.3",
|
|
41
|
+
"h3": "^1.15.11",
|
|
42
|
+
"stripe": "^23.0.0",
|
|
43
|
+
"unstorage": "^1.17.5",
|
|
44
|
+
"zod": "^4.6.5"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@nuxt/ui": "^4.10.0",
|
|
48
|
+
"@nuxthub/core": "^0.10.8"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@nuxt/ui": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@nuxt/devtools": "^3.4.2",
|
|
57
|
+
"@nuxt/eslint-config": "^1.17.0",
|
|
58
|
+
"@nuxt/module-builder": "^1.0.3",
|
|
59
|
+
"@nuxt/schema": "^4.6.0",
|
|
60
|
+
"@nuxt/test-utils": "^4.3.2",
|
|
61
|
+
"@nuxt/ui": "^4.11.0",
|
|
62
|
+
"@nuxthub/core": "^0.10.8",
|
|
63
|
+
"@types/node": "latest",
|
|
64
|
+
"changelogen": "^0.6.2",
|
|
65
|
+
"eslint": "^10.10.0",
|
|
66
|
+
"nuxt": "^4.6.0",
|
|
67
|
+
"typescript": "^6.0.3",
|
|
68
|
+
"vitest": "^5.0.1",
|
|
69
|
+
"vue-tsc": "^3.3.11"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"postinstall": "nuxt-module-build build --stub && nuxt prepare",
|
|
73
|
+
"build": "nuxt-module-build build",
|
|
74
|
+
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
75
|
+
"dev:build": "nuxt build playground",
|
|
76
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
77
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
78
|
+
"lint": "eslint .",
|
|
79
|
+
"lint:fix": "eslint . --fix",
|
|
80
|
+
"test": "vitest run",
|
|
81
|
+
"test:watch": "vitest watch",
|
|
82
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
83
|
+
},
|
|
84
|
+
"typesVersions": {
|
|
85
|
+
"*": {
|
|
86
|
+
".": [
|
|
87
|
+
"./dist/types.d.mts"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
6
91
|
}
|