use standalone functions to reduce bundle size

This commit is contained in:
Shawn Erquhart
2025-07-25 09:41:11 -04:00
parent 61cbd6246a
commit edeedbfac9
9 changed files with 74 additions and 40 deletions

View File

@@ -17,6 +17,7 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*

View File

@@ -1,10 +1,12 @@
import { Polar } from "@polar-sh/sdk";
import { PolarCore } from "@polar-sh/sdk/core.js";
import { productsCreate } from "@polar-sh/sdk/funcs/productsCreate.js";
import { productsList } from "@polar-sh/sdk/funcs/productsList.js";
import { internalAction, internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
const accessToken = process.env.POLAR_ORGANIZATION_TOKEN;
const polar = new Polar({
const polar = new PolarCore({
accessToken,
server: "sandbox",
});
@@ -36,7 +38,7 @@ const seed = internalAction({
return items.length > 0;
}
}
const result = await polar.products.list({
const result = await productsList(polar, {
isArchived: false,
limit: 1,
});
@@ -52,7 +54,7 @@ const seed = internalAction({
// Create example products. In a real app you would likely create your
// products in the Polar dashboard and reference them by id in your application.
await Promise.all([
polar.products.create({
productsCreate(polar, {
name: PREMIUM_PLAN_NAME,
description: "All the things for one low monthly price.",
recurringInterval: "month",
@@ -63,7 +65,7 @@ const seed = internalAction({
},
],
}),
polar.products.create({
productsCreate(polar, {
name: PREMIUM_PLAN_NAME,
description: "All the things for one low annual price.",
recurringInterval: "year",
@@ -74,7 +76,7 @@ const seed = internalAction({
},
],
}),
polar.products.create({
productsCreate(polar, {
name: PREMIUM_PLUS_PLAN_NAME,
description: "All the things for one low monthly price.",
recurringInterval: "month",
@@ -85,7 +87,7 @@ const seed = internalAction({
},
],
}),
polar.products.create({
productsCreate(polar, {
name: PREMIUM_PLUS_PLAN_NAME,
description: "All the things for one low annual price.",
recurringInterval: "year",

View File

@@ -48,11 +48,9 @@
},
"..": {
"name": "@convex-dev/polar",
"version": "0.4.4",
"version": "0.5.0",
"license": "Apache-2.0",
"dependencies": {
"@polar-sh/checkout": "0.1.10",
"@polar-sh/sdk": "0.32.11",
"buffer": "^6.0.3",
"convex-helpers": "^0.1.63",
"remeda": "^2.20.2",
@@ -66,12 +64,14 @@
"eslint": "^9.9.1",
"globals": "^15.9.0",
"prettier": "3.2.5",
"typescript": "~5.0.3",
"typescript": "^5.5.0",
"typescript-eslint": "^8.4.0",
"vitest": "^2.1.4"
},
"peerDependencies": {
"convex": "^1.19.2 || >=1.17.0 <1.25.0",
"@polar-sh/checkout": ">=0.1.10",
"@polar-sh/sdk": ">=0.32.11",
"convex": "^1.25.4",
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
}

View File

@@ -41,14 +41,16 @@ export default function TodoList() {
const getButtonText = (targetProductId: string) => {
if (!user?.subscription) return "Upgrade";
const currentAmount = user.subscription.amount ?? 0;
const targetProduct = Object.values(products ?? {}).find(
(p) => p?.id === targetProductId
);
const targetAmount = targetProduct?.prices[0].priceAmount ?? 0;
if (targetAmount > currentAmount) return "Upgrade";
if (targetAmount < currentAmount) return "Downgrade";
return "Switch";
const isPremium =
user.subscription.productId === premiumMonthly?.id ||
user.subscription.productId === premiumYearly?.id;
const targetIsPremiumPlus =
targetProductId === premiumPlusMonthly?.id ||
targetProductId === premiumPlusYearly?.id;
if (isPremium && targetIsPremiumPlus) {
return "Upgrade";
}
return "Downgrade";
};
const handlePlanChange = async (productId: string) => {

View File

@@ -73,7 +73,9 @@
}
},
"peerDependencies": {
"convex": "^1.19.2 || >=1.17.0 <1.35.0",
"@polar-sh/checkout": ">=0.1.10",
"@polar-sh/sdk": ">=0.32.11",
"convex": "^1.25.4",
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
},
@@ -93,8 +95,6 @@
"types": "./dist/commonjs/client/index.d.ts",
"module": "./dist/esm/client/index.js",
"dependencies": {
"@polar-sh/checkout": "0.1.10",
"@polar-sh/sdk": "0.32.11",
"buffer": "^6.0.3",
"convex-helpers": "^0.1.63",
"remeda": "^2.20.2",

View File

@@ -1,5 +1,10 @@
import "./polyfill";
import { Polar as PolarSdk } from "@polar-sh/sdk";
import { PolarCore } from "@polar-sh/sdk/core.js";
import { customersCreate } from "@polar-sh/sdk/funcs/customersCreate.js";
import { checkoutsCreate } from "@polar-sh/sdk/funcs/checkoutsCreate.js";
import { customerSessionsCreate } from "@polar-sh/sdk/funcs/customerSessionsCreate.js";
import { subscriptionsUpdate } from "@polar-sh/sdk/funcs/subscriptionsUpdate.js";
import type { Checkout } from "@polar-sh/sdk/models/components/checkout.js";
import type { WebhookProductCreatedPayload } from "@polar-sh/sdk/models/components/webhookproductcreatedpayload.js";
import type { WebhookProductUpdatedPayload } from "@polar-sh/sdk/models/components/webhookproductupdatedpayload.js";
@@ -48,7 +53,7 @@ export class Polar<
DataModel extends GenericDataModel = GenericDataModel,
Products extends Record<string, string> = Record<string, string>,
> {
public sdk: PolarSdk;
public polar: PolarCore;
public products: Products;
private organizationToken: string;
private webhookSecret: string;
@@ -77,7 +82,7 @@ export class Polar<
(process.env["POLAR_SERVER"] as "sandbox" | "production") ??
"sandbox";
this.sdk = new PolarSdk({
this.polar = new PolarCore({
accessToken: this.organizationToken,
server: this.server,
});
@@ -116,20 +121,23 @@ export class Polar<
const customerId =
dbCustomer?.id ||
(
await this.sdk.customers.create({
await customersCreate(this.polar, {
email,
metadata: {
userId,
},
})
).id;
).value?.id;
if (!customerId) {
throw new Error("Customer not created");
}
if (!dbCustomer) {
await ctx.runMutation(this.component.lib.insertCustomer, {
id: customerId,
userId,
});
}
return this.sdk.checkouts.create({
const checkout = await checkoutsCreate(this.polar, {
allowDiscountCodes: true,
customerId,
embedOrigin: origin,
@@ -138,6 +146,10 @@ export class Polar<
? { products: productIds }
: { products: productIds }),
});
if (!checkout.value) {
throw new Error("Checkout not created");
}
return checkout.value;
}
async createCustomerPortalSession(
ctx: GenericActionCtx<DataModel>,
@@ -152,11 +164,14 @@ export class Polar<
throw new Error("Customer not found");
}
const session = await this.sdk.customerSessions.create({
const session = await customerSessionsCreate(this.polar, {
customerId: customer.id,
});
if (!session.value) {
throw new Error("Customer session not created");
}
return { url: session.customerPortalUrl };
return { url: session.value.customerPortalUrl };
}
listProducts(
ctx: RunQueryCtx,
@@ -209,12 +224,16 @@ export class Polar<
if (subscription.productId === productId) {
throw new Error("Subscription already on this product");
}
await this.sdk.subscriptions.update({
const updatedSubscription = await subscriptionsUpdate(this.polar, {
id: subscription.id,
subscriptionUpdate: {
productId,
},
});
if (!updatedSubscription.value) {
throw new Error("Subscription not updated");
}
return updatedSubscription.value;
}
async cancelSubscription(
ctx: RunActionCtx,
@@ -228,13 +247,17 @@ export class Polar<
if (subscription.status !== "active") {
throw new Error("Subscription is not active");
}
await this.sdk.subscriptions.update({
const updatedSubscription = await subscriptionsUpdate(this.polar, {
id: subscription.id,
subscriptionUpdate: {
cancelAtPeriodEnd: revokeImmediately ? undefined : true,
revoke: revokeImmediately ? true : undefined,
},
});
if (!updatedSubscription.value) {
throw new Error("Subscription not updated");
}
return updatedSubscription.value;
}
api() {
return {

View File

@@ -16,6 +16,7 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*

View File

@@ -1,4 +1,6 @@
import { Polar as PolarSdk } from "@polar-sh/sdk";
import { PolarCore } from "@polar-sh/sdk/core";
import { productsList } from "@polar-sh/sdk/funcs/productsList.js";
import { v } from "convex/values";
import { action, mutation, query } from "./_generated/server";
import schema from "./schema";
@@ -278,22 +280,25 @@ export const syncProducts = action({
server: v.union(v.literal("sandbox"), v.literal("production")),
},
handler: async (ctx, args) => {
const sdk = new PolarSdk({
const polar = new PolarCore({
accessToken: args.polarAccessToken,
server: args.server,
});
let page = 1;
let maxPage;
do {
const products = await sdk.products.list({
const products = await productsList(polar, {
page,
limit: 100,
});
if (!products.value) {
throw new Error("Failed to get products");
}
page = page + 1;
maxPage = products.result.pagination.maxPage;
maxPage = products.value.result.pagination.maxPage;
await ctx.runMutation(api.lib.updateProducts, {
polarAccessToken: args.polarAccessToken,
products: products.result.items.map(convertToDatabaseProduct),
products: products.value.result.items.map(convertToDatabaseProduct),
});
} while (maxPage >= page);
},

View File

@@ -12,8 +12,8 @@ import type {
import { GenericId } from "convex/values";
import type { api } from "./_generated/api";
import type { Doc } from "./_generated/dataModel";
import { Subscription } from "@polar-sh/sdk/models/components/subscription.js";
import { Product } from "@polar-sh/sdk/models/components/product.js";
import type { Subscription } from "@polar-sh/sdk/models/components/subscription.js";
import type { Product } from "@polar-sh/sdk/models/components/product.js";
export const omitSystemFields = <
T extends { _id: string; _creationTime: number } | null | undefined,