@billsdk/time-travel 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/overlay.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CSSProperties } from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\nexport interface TimeTravelOverlayProps {\n /**\n * Base URL for the BillSDK API\n * @default \"/api/billing\"\n */\n baseUrl?: string;\n\n /**\n * Position of the overlay\n * @default \"bottom-right\"\n */\n position?: \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\";\n\n /**\n * Initial collapsed state\n * @default true\n */\n defaultCollapsed?: boolean;\n\n /**\n * Customer ID to control time for\n * Required for per-customer time simulation\n */\n customerId?: string;\n}\n\ninterface TimeTravelState {\n customerId: string;\n simulatedTime: string | null;\n isSimulated: boolean;\n realTime: string;\n}\n\ninterface CustomerTimeState {\n customerId: string;\n simulatedTime: string | null;\n}\n\ninterface RenewalResult {\n processed: number;\n succeeded: number;\n failed: number;\n skipped: number;\n}\n\n/**\n * Time Travel Overlay Component\n *\n * A floating UI component that allows you to control simulated time\n * for testing billing cycles, trials, and renewals.\n *\n * @example\n * ```tsx\n * import { TimeTravelOverlay } from \"@billsdk/time-travel/react\";\n *\n * function App() {\n * const { user } = useAuth();\n * return (\n * <>\n * <YourApp />\n * {process.env.NODE_ENV === \"development\" && user?.customerId && (\n * <TimeTravelOverlay\n * baseUrl=\"/api/billing\"\n * customerId={user.customerId}\n * />\n * )}\n * </>\n * );\n * }\n * ```\n */\nexport function TimeTravelOverlay({\n baseUrl = \"/api/billing\",\n position = \"bottom-right\",\n defaultCollapsed = true,\n customerId,\n}: TimeTravelOverlayProps) {\n const [state, setState] = useState<TimeTravelState | null>(null);\n const [allCustomers, setAllCustomers] = useState<CustomerTimeState[]>([]);\n const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);\n const [isLoading, setIsLoading] = useState(false);\n const [isProcessingRenewals, setIsProcessingRenewals] = useState(false);\n const [renewalResult, setRenewalResult] = useState<RenewalResult | null>(\n null,\n );\n const [dateInput, setDateInput] = useState(\"\");\n const [customerIdInput, setCustomerIdInput] = useState(customerId ?? \"\");\n const [showAllCustomers, setShowAllCustomers] = useState(false);\n\n const activeCustomerId = customerId ?? customerIdInput;\n\n // Fetch current state for the active customer\n const fetchState = useCallback(async () => {\n if (!activeCustomerId) {\n setState(null);\n return;\n }\n\n try {\n const res = await fetch(`${baseUrl}/time-travel/get`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ customerId: activeCustomerId }),\n });\n if (res.ok) {\n const data = (await res.json()) as TimeTravelState;\n setState(data);\n if (data.simulatedTime) {\n const datePart = data.simulatedTime.split(\"T\")[0];\n if (datePart) {\n setDateInput(datePart);\n }\n }\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to fetch state:\", error);\n }\n }, [baseUrl, activeCustomerId]);\n\n // Fetch all customers with simulated time\n const fetchAllCustomers = useCallback(async () => {\n try {\n const res = await fetch(`${baseUrl}/time-travel/list`);\n if (res.ok) {\n const data = (await res.json()) as { customers: CustomerTimeState[] };\n setAllCustomers(data.customers);\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to fetch customers:\", error);\n }\n }, [baseUrl]);\n\n useEffect(() => {\n fetchState();\n fetchAllCustomers();\n }, [fetchState, fetchAllCustomers]);\n\n // Advance time\n const advance = async (days: number, months = 0, hours = 0) => {\n if (!activeCustomerId) return;\n\n setIsLoading(true);\n try {\n const res = await fetch(`${baseUrl}/time-travel/advance`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n customerId: activeCustomerId,\n days,\n months,\n hours,\n }),\n });\n if (res.ok) {\n await fetchState();\n await fetchAllCustomers();\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to advance:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n // Set specific time\n const setTime = async (date: string | null) => {\n if (!activeCustomerId) return;\n\n setIsLoading(true);\n try {\n const res = await fetch(`${baseUrl}/time-travel/set`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ customerId: activeCustomerId, date }),\n });\n if (res.ok) {\n await fetchState();\n await fetchAllCustomers();\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to set time:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n // Reset to real time\n const reset = async () => {\n if (!activeCustomerId) return;\n\n setIsLoading(true);\n try {\n const res = await fetch(`${baseUrl}/time-travel/reset`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ customerId: activeCustomerId }),\n });\n if (res.ok) {\n setDateInput(\"\");\n await fetchState();\n await fetchAllCustomers();\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to reset:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n // Handle date input submission\n const handleDateSubmit = () => {\n if (dateInput) {\n const date = new Date(dateInput);\n date.setUTCHours(12, 0, 0, 0);\n setTime(date.toISOString());\n }\n };\n\n // Process renewals for the current customer\n const processRenewals = async (dryRun = false) => {\n if (!activeCustomerId) return;\n\n setIsProcessingRenewals(true);\n setRenewalResult(null);\n try {\n const params = new URLSearchParams({\n customerId: activeCustomerId,\n ...(dryRun && { dryRun: \"true\" }),\n });\n const res = await fetch(`${baseUrl}/renewals?${params.toString()}`);\n if (res.ok) {\n const data = (await res.json()) as RenewalResult;\n setRenewalResult(data);\n } else {\n console.error(\"[TimeTravelOverlay] Renewals failed:\", res.statusText);\n }\n } catch (error) {\n console.error(\"[TimeTravelOverlay] Failed to process renewals:\", error);\n } finally {\n setIsProcessingRenewals(false);\n }\n };\n\n // Position styles\n const positionStyles: Record<string, CSSProperties> = {\n \"bottom-right\": { bottom: 16, right: 16 },\n \"bottom-left\": { bottom: 16, left: 16 },\n \"top-right\": { top: 16, right: 16 },\n \"top-left\": { top: 16, left: 16 },\n };\n\n const formatDate = (isoString: string) => {\n const date = new Date(isoString);\n return date.toLocaleDateString(undefined, {\n weekday: \"short\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n };\n\n const formatTime = (isoString: string) => {\n const date = new Date(isoString);\n return date.toLocaleTimeString(undefined, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n };\n\n const truncateId = (id: string) => {\n if (id.length <= 12) return id;\n return `${id.slice(0, 6)}...${id.slice(-4)}`;\n };\n\n return (\n <div\n style={{\n position: \"fixed\",\n ...positionStyles[position],\n zIndex: 99999,\n fontFamily:\n 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: 14,\n }}\n >\n {/* Collapsed Badge */}\n {isCollapsed ? (\n <button\n type=\"button\"\n onClick={() => setIsCollapsed(false)}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"8px 12px\",\n borderRadius: 8,\n border: \"none\",\n cursor: \"pointer\",\n boxShadow:\n \"0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)\",\n backgroundColor: state?.isSimulated ? \"#fef3c7\" : \"#f3f4f6\",\n color: state?.isSimulated ? \"#92400e\" : \"#374151\",\n fontWeight: 500,\n transition: \"all 0.2s\",\n }}\n >\n <span style={{ fontSize: 16 }}>\n {state?.isSimulated ? \"⏰\" : \"🕐\"}\n </span>\n <span>\n {state?.isSimulated && state.simulatedTime\n ? formatDate(state.simulatedTime)\n : \"Real Time\"}\n </span>\n {allCustomers.length > 0 && (\n <span\n style={{\n backgroundColor: \"#3b82f6\",\n color: \"white\",\n borderRadius: 9999,\n padding: \"2px 6px\",\n fontSize: 11,\n fontWeight: 600,\n }}\n >\n {allCustomers.length}\n </span>\n )}\n </button>\n ) : (\n /* Expanded Panel */\n <div\n style={{\n width: 320,\n backgroundColor: \"white\",\n borderRadius: 12,\n boxShadow:\n \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)\",\n border: \"1px solid #e5e7eb\",\n overflow: \"hidden\",\n }}\n >\n {/* Header */}\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n padding: \"12px 16px\",\n backgroundColor: state?.isSimulated ? \"#fef3c7\" : \"#f9fafb\",\n borderBottom: \"1px solid #e5e7eb\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n <span style={{ fontSize: 18 }}>\n {state?.isSimulated ? \"⏰\" : \"🕐\"}\n </span>\n <span style={{ fontWeight: 600, color: \"#111827\" }}>\n Time Travel\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => setIsCollapsed(true)}\n style={{\n padding: 4,\n borderRadius: 4,\n border: \"none\",\n backgroundColor: \"transparent\",\n cursor: \"pointer\",\n color: \"#6b7280\",\n fontSize: 18,\n lineHeight: 1,\n }}\n >\n ×\n </button>\n </div>\n\n <div style={{ padding: 16 }}>\n {/* Customer ID Input (only if not provided via prop) */}\n {!customerId && (\n <div style={{ marginBottom: 16 }}>\n <div\n style={{\n fontSize: 12,\n color: \"#6b7280\",\n marginBottom: 8,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n Customer ID\n </div>\n <input\n type=\"text\"\n value={customerIdInput}\n onChange={(e) => setCustomerIdInput(e.target.value)}\n onBlur={fetchState}\n placeholder=\"Enter customer ID…\"\n style={{\n width: \"100%\",\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1px solid #e5e7eb\",\n fontSize: 14,\n color: \"#374151\",\n boxSizing: \"border-box\",\n }}\n />\n </div>\n )}\n\n {/* Customer indicator (when provided via prop) */}\n {customerId && (\n <div\n style={{\n marginBottom: 16,\n padding: \"8px 12px\",\n backgroundColor: \"#eff6ff\",\n borderRadius: 6,\n fontSize: 12,\n color: \"#1e40af\",\n }}\n >\n Customer: <strong>{truncateId(customerId)}</strong>\n </div>\n )}\n\n {activeCustomerId ? (\n <>\n {/* Current Time Display */}\n <div\n style={{\n marginBottom: 16,\n padding: 12,\n backgroundColor: \"#f9fafb\",\n borderRadius: 8,\n }}\n >\n <div\n style={{\n fontSize: 12,\n color: \"#6b7280\",\n marginBottom: 4,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n {state?.isSimulated ? \"Simulated Time\" : \"Current Time\"}\n </div>\n <div\n style={{ fontSize: 16, fontWeight: 600, color: \"#111827\" }}\n >\n {state?.simulatedTime\n ? formatDate(state.simulatedTime)\n : state?.realTime\n ? formatDate(state.realTime)\n : \"Loading...\"}\n </div>\n <div style={{ fontSize: 14, color: \"#6b7280\" }}>\n {state?.simulatedTime\n ? formatTime(state.simulatedTime)\n : state?.realTime\n ? formatTime(state.realTime)\n : \"\"}\n </div>\n </div>\n\n {/* Quick Actions */}\n <div style={{ marginBottom: 16 }}>\n <div\n style={{\n fontSize: 12,\n color: \"#6b7280\",\n marginBottom: 8,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n Quick Advance\n </div>\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: \"repeat(3, 1fr)\",\n gap: 8,\n }}\n >\n {[\n { label: \"+1 day\", days: 1 },\n { label: \"+1 week\", days: 7 },\n { label: \"+1 month\", months: 1 },\n ].map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() =>\n advance(action.days ?? 0, action.months ?? 0)\n }\n disabled={isLoading}\n style={{\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1px solid #e5e7eb\",\n backgroundColor: \"white\",\n cursor: isLoading ? \"not-allowed\" : \"pointer\",\n color: \"#374151\",\n fontSize: 13,\n fontWeight: 500,\n transition: \"all 0.15s\",\n opacity: isLoading ? 0.5 : 1,\n }}\n >\n {action.label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Date Picker */}\n <div style={{ marginBottom: 16 }}>\n <div\n style={{\n fontSize: 12,\n color: \"#6b7280\",\n marginBottom: 8,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n Go to Date\n </div>\n <div style={{ display: \"flex\", gap: 8 }}>\n <input\n type=\"date\"\n value={dateInput}\n onChange={(e) => {\n const target = e.target as HTMLInputElement;\n setDateInput(target.value);\n }}\n style={{\n flex: 1,\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1px solid #e5e7eb\",\n fontSize: 14,\n color: \"#374151\",\n }}\n />\n <button\n type=\"button\"\n onClick={handleDateSubmit}\n disabled={isLoading || !dateInput}\n style={{\n padding: \"8px 16px\",\n borderRadius: 6,\n border: \"none\",\n backgroundColor: \"#3b82f6\",\n color: \"white\",\n cursor:\n isLoading || !dateInput ? \"not-allowed\" : \"pointer\",\n fontSize: 14,\n fontWeight: 500,\n opacity: isLoading || !dateInput ? 0.5 : 1,\n }}\n >\n Go\n </button>\n </div>\n </div>\n\n {/* Reset Button */}\n {state?.isSimulated && (\n <button\n type=\"button\"\n onClick={reset}\n disabled={isLoading}\n style={{\n width: \"100%\",\n padding: \"10px 16px\",\n borderRadius: 6,\n border: \"1px solid #ef4444\",\n backgroundColor: \"white\",\n color: \"#ef4444\",\n cursor: isLoading ? \"not-allowed\" : \"pointer\",\n fontSize: 14,\n fontWeight: 500,\n opacity: isLoading ? 0.5 : 1,\n marginBottom: 16,\n }}\n >\n Reset to Real Time\n </button>\n )}\n\n {/* Billing Actions */}\n <div style={{ marginBottom: 16 }}>\n <div\n style={{\n fontSize: 12,\n color: \"#6b7280\",\n marginBottom: 8,\n textTransform: \"uppercase\",\n letterSpacing: \"0.05em\",\n }}\n >\n Billing Actions\n </div>\n <div style={{ display: \"flex\", gap: 8 }}>\n <button\n type=\"button\"\n onClick={() => processRenewals(true)}\n disabled={isProcessingRenewals}\n title=\"Check what would happen without actually charging\"\n style={{\n flex: 1,\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1px solid #e5e7eb\",\n backgroundColor: \"white\",\n cursor: isProcessingRenewals\n ? \"not-allowed\"\n : \"pointer\",\n color: \"#374151\",\n fontSize: 13,\n fontWeight: 500,\n opacity: isProcessingRenewals ? 0.5 : 1,\n }}\n >\n {isProcessingRenewals ? \"...\" : \"Dry Run\"}\n </button>\n <button\n type=\"button\"\n onClick={() => processRenewals(false)}\n disabled={isProcessingRenewals}\n title=\"Process renewals and charge customers\"\n style={{\n flex: 1,\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"none\",\n backgroundColor: \"#10b981\",\n cursor: isProcessingRenewals\n ? \"not-allowed\"\n : \"pointer\",\n color: \"white\",\n fontSize: 13,\n fontWeight: 500,\n opacity: isProcessingRenewals ? 0.5 : 1,\n }}\n >\n {isProcessingRenewals\n ? \"Processing...\"\n : \"Process Renewals\"}\n </button>\n </div>\n\n {/* Renewal Result */}\n {renewalResult && (\n <div\n style={{\n marginTop: 8,\n padding: 12,\n backgroundColor:\n renewalResult.failed > 0 ? \"#fef2f2\" : \"#f0fdf4\",\n borderRadius: 6,\n fontSize: 12,\n }}\n >\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: \"repeat(2, 1fr)\",\n gap: 4,\n }}\n >\n <div>\n <span style={{ color: \"#6b7280\" }}>Processed:</span>{\" \"}\n <strong>{renewalResult.processed}</strong>\n </div>\n <div>\n <span style={{ color: \"#059669\" }}>Succeeded:</span>{\" \"}\n <strong style={{ color: \"#059669\" }}>\n {renewalResult.succeeded}\n </strong>\n </div>\n <div>\n <span style={{ color: \"#dc2626\" }}>Failed:</span>{\" \"}\n <strong style={{ color: \"#dc2626\" }}>\n {renewalResult.failed}\n </strong>\n </div>\n <div>\n <span style={{ color: \"#6b7280\" }}>Skipped:</span>{\" \"}\n <strong>{renewalResult.skipped}</strong>\n </div>\n </div>\n </div>\n )}\n </div>\n </>\n ) : (\n <div\n style={{\n padding: 16,\n textAlign: \"center\",\n color: \"#6b7280\",\n }}\n >\n Enter a customer ID to control time\n </div>\n )}\n\n {/* All Customers Toggle */}\n {allCustomers.length > 0 && (\n <div>\n <button\n type=\"button\"\n onClick={() => setShowAllCustomers(!showAllCustomers)}\n style={{\n width: \"100%\",\n padding: \"8px 12px\",\n borderRadius: 6,\n border: \"1px solid #e5e7eb\",\n backgroundColor: \"#f9fafb\",\n cursor: \"pointer\",\n color: \"#374151\",\n fontSize: 13,\n fontWeight: 500,\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n }}\n >\n <span>\n {allCustomers.length} customer(s) with simulated time\n </span>\n <span>{showAllCustomers ? \"▲\" : \"▼\"}</span>\n </button>\n\n {showAllCustomers && (\n <div\n style={{\n marginTop: 8,\n maxHeight: 150,\n overflowY: \"auto\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 6,\n }}\n >\n {allCustomers.map((c) => (\n <div\n key={c.customerId}\n style={{\n padding: \"8px 12px\",\n borderBottom: \"1px solid #f3f4f6\",\n fontSize: 12,\n display: \"flex\",\n justifyContent: \"space-between\",\n backgroundColor:\n c.customerId === activeCustomerId\n ? \"#eff6ff\"\n : \"transparent\",\n }}\n >\n <span\n style={{\n fontFamily: \"monospace\",\n color: \"#374151\",\n }}\n >\n {truncateId(c.customerId)}\n </span>\n <span style={{ color: \"#6b7280\" }}>\n {c.simulatedTime ? formatDate(c.simulatedTime) : \"—\"}\n </span>\n </div>\n ))}\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Footer Warning */}\n <div\n style={{\n padding: \"8px 16px\",\n backgroundColor: \"#fef2f2\",\n borderTop: \"1px solid #fecaca\",\n fontSize: 11,\n color: \"#991b1b\",\n textAlign: \"center\",\n }}\n >\n Development only - Do not use in production\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,57 @@
1
+ import { BillSDKPlugin, BillSDKPluginSchema, DBAdapter, TimeProvider } from '@billsdk/core';
2
+
3
+ /**
4
+ * Time Travel Plugin for BillSDK
5
+ *
6
+ * Allows you to simulate time per customer for testing subscription cycles,
7
+ * trials, renewals, and other time-based billing logic.
8
+ *
9
+ * WARNING: This plugin is for development/testing only.
10
+ * Do NOT use in production.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { billsdk } from "billsdk";
15
+ * import { timeTravelPlugin } from "@billsdk/time-travel";
16
+ *
17
+ * const billing = billsdk({
18
+ * // ... your config
19
+ * plugins: [timeTravelPlugin()],
20
+ * });
21
+ * ```
22
+ */
23
+ declare function timeTravelPlugin(): BillSDKPlugin;
24
+
25
+ /**
26
+ * Schema for the time_travel_state table
27
+ *
28
+ * Stores the simulated time for time-travel testing per customer.
29
+ * Each row uses the customerId as the id field.
30
+ */
31
+ declare const timeTravelSchema: BillSDKPluginSchema;
32
+
33
+ interface TimeTravelState extends Record<string, unknown> {
34
+ id: string;
35
+ simulatedTime: Date | null;
36
+ createdAt: Date;
37
+ updatedAt: Date;
38
+ }
39
+ /**
40
+ * Create a time travel provider that reads directly from the database
41
+ * Supports per-customer time simulation
42
+ */
43
+ declare function createTimeTravelProvider(adapter: DBAdapter): TimeProvider;
44
+ /**
45
+ * Get the simulated time for a specific customer
46
+ */
47
+ declare function getSimulatedTime(customerId: string, adapter?: DBAdapter): Promise<Date | null>;
48
+ /**
49
+ * Check if time travel is active for a specific customer
50
+ */
51
+ declare function isTimeTravelActive(customerId: string, adapter?: DBAdapter): Promise<boolean>;
52
+ /**
53
+ * List all customers with active time travel
54
+ */
55
+ declare function listTimeTravelStates(adapter?: DBAdapter): Promise<TimeTravelState[]>;
56
+
57
+ export { type TimeTravelState, createTimeTravelProvider, getSimulatedTime, isTimeTravelActive, listTimeTravelStates, timeTravelPlugin, timeTravelSchema };
package/dist/index.js ADDED
@@ -0,0 +1,243 @@
1
+ // src/plugin.ts
2
+ import { z } from "zod";
3
+
4
+ // src/schema.ts
5
+ var timeTravelSchema = {
6
+ time_travel_state: {
7
+ fields: {
8
+ id: { type: "string", required: true },
9
+ // customerId
10
+ simulatedTime: { type: "date", required: false },
11
+ createdAt: { type: "date", required: true },
12
+ updatedAt: { type: "date", required: true }
13
+ }
14
+ }
15
+ };
16
+
17
+ // src/time-provider.ts
18
+ var dbAdapter = null;
19
+ function createTimeTravelProvider(adapter) {
20
+ dbAdapter = adapter;
21
+ return {
22
+ now: async (customerId) => {
23
+ if (!customerId) {
24
+ return /* @__PURE__ */ new Date();
25
+ }
26
+ try {
27
+ const state = await adapter.findOne({
28
+ model: "time_travel_state",
29
+ where: [{ field: "id", operator: "eq", value: customerId }]
30
+ });
31
+ return state?.simulatedTime ? new Date(state.simulatedTime) : /* @__PURE__ */ new Date();
32
+ } catch {
33
+ return /* @__PURE__ */ new Date();
34
+ }
35
+ }
36
+ };
37
+ }
38
+ async function setSimulatedTime(adapter, customerId, time) {
39
+ const realNow = /* @__PURE__ */ new Date();
40
+ const existing = await adapter.findOne({
41
+ model: "time_travel_state",
42
+ where: [{ field: "id", operator: "eq", value: customerId }]
43
+ });
44
+ if (existing) {
45
+ await adapter.update({
46
+ model: "time_travel_state",
47
+ where: [{ field: "id", operator: "eq", value: customerId }],
48
+ update: {
49
+ simulatedTime: time,
50
+ updatedAt: realNow
51
+ }
52
+ });
53
+ } else {
54
+ await adapter.create({
55
+ model: "time_travel_state",
56
+ data: {
57
+ id: customerId,
58
+ simulatedTime: time,
59
+ createdAt: realNow,
60
+ updatedAt: realNow
61
+ }
62
+ });
63
+ }
64
+ }
65
+ async function getSimulatedTime(customerId, adapter) {
66
+ const db = adapter ?? dbAdapter;
67
+ if (!db) return null;
68
+ try {
69
+ const state = await db.findOne({
70
+ model: "time_travel_state",
71
+ where: [{ field: "id", operator: "eq", value: customerId }]
72
+ });
73
+ return state?.simulatedTime ? new Date(state.simulatedTime) : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ async function isTimeTravelActive(customerId, adapter) {
79
+ const time = await getSimulatedTime(customerId, adapter);
80
+ return time !== null;
81
+ }
82
+ async function listTimeTravelStates(adapter) {
83
+ const db = adapter ?? dbAdapter;
84
+ if (!db) return [];
85
+ try {
86
+ const states = await db.findMany({
87
+ model: "time_travel_state",
88
+ where: []
89
+ });
90
+ return states.filter((s) => s.simulatedTime !== null);
91
+ } catch {
92
+ return [];
93
+ }
94
+ }
95
+
96
+ // src/plugin.ts
97
+ var setTimeSchema = z.object({
98
+ customerId: z.string(),
99
+ date: z.string().nullable()
100
+ // ISO string or null
101
+ });
102
+ var getTimeSchema = z.object({
103
+ customerId: z.string()
104
+ });
105
+ var advanceTimeSchema = z.object({
106
+ customerId: z.string(),
107
+ days: z.number().optional().default(0),
108
+ hours: z.number().optional().default(0),
109
+ months: z.number().optional().default(0)
110
+ });
111
+ var resetTimeSchema = z.object({
112
+ customerId: z.string()
113
+ });
114
+ function timeTravelPlugin() {
115
+ const endpoints = {
116
+ timeTravelSet: {
117
+ path: "/time-travel/set",
118
+ options: {
119
+ method: "POST",
120
+ body: setTimeSchema
121
+ },
122
+ handler: async (context) => {
123
+ const { body, ctx } = context;
124
+ const { customerId } = body;
125
+ const date = body.date ? new Date(body.date) : null;
126
+ await setSimulatedTime(ctx.adapter, customerId, date);
127
+ return {
128
+ success: true,
129
+ customerId,
130
+ simulatedTime: date?.toISOString() ?? null,
131
+ isSimulated: date !== null
132
+ };
133
+ }
134
+ },
135
+ timeTravelGet: {
136
+ path: "/time-travel/get",
137
+ options: {
138
+ method: "POST",
139
+ body: getTimeSchema
140
+ },
141
+ handler: async (context) => {
142
+ const { body, ctx } = context;
143
+ const { customerId } = body;
144
+ const simulatedTime = await getSimulatedTime(
145
+ customerId,
146
+ // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple
147
+ ctx.adapter
148
+ );
149
+ return {
150
+ customerId,
151
+ simulatedTime: simulatedTime?.toISOString() ?? null,
152
+ isSimulated: simulatedTime !== null,
153
+ realTime: (/* @__PURE__ */ new Date()).toISOString()
154
+ };
155
+ }
156
+ },
157
+ timeTravelAdvance: {
158
+ path: "/time-travel/advance",
159
+ options: {
160
+ method: "POST",
161
+ body: advanceTimeSchema
162
+ },
163
+ handler: async (context) => {
164
+ const { body, ctx } = context;
165
+ const { customerId, days = 0, hours = 0, months = 0 } = body;
166
+ const adapter = ctx.adapter;
167
+ const simulatedTime = await getSimulatedTime(customerId, adapter);
168
+ const current = simulatedTime ? new Date(simulatedTime) : /* @__PURE__ */ new Date();
169
+ current.setMonth(current.getMonth() + months);
170
+ current.setDate(current.getDate() + days);
171
+ current.setHours(current.getHours() + hours);
172
+ await setSimulatedTime(adapter, customerId, current);
173
+ return {
174
+ success: true,
175
+ customerId,
176
+ simulatedTime: current.toISOString(),
177
+ advanced: { days, hours, months }
178
+ };
179
+ }
180
+ },
181
+ timeTravelReset: {
182
+ path: "/time-travel/reset",
183
+ options: {
184
+ method: "POST",
185
+ body: resetTimeSchema
186
+ },
187
+ handler: async (context) => {
188
+ const { body, ctx } = context;
189
+ const { customerId } = body;
190
+ await setSimulatedTime(ctx.adapter, customerId, null);
191
+ return {
192
+ success: true,
193
+ customerId,
194
+ simulatedTime: null,
195
+ realTime: (/* @__PURE__ */ new Date()).toISOString()
196
+ };
197
+ }
198
+ },
199
+ timeTravelList: {
200
+ path: "/time-travel/list",
201
+ options: {
202
+ method: "GET"
203
+ },
204
+ handler: async (context) => {
205
+ const { ctx } = context;
206
+ const states = await listTimeTravelStates(ctx.adapter);
207
+ return {
208
+ customers: states.map((s) => ({
209
+ customerId: s.id,
210
+ simulatedTime: s.simulatedTime ? new Date(s.simulatedTime).toISOString() : null
211
+ })),
212
+ realTime: (/* @__PURE__ */ new Date()).toISOString()
213
+ };
214
+ }
215
+ }
216
+ };
217
+ return {
218
+ id: "time-travel",
219
+ name: "Time Travel",
220
+ schema: timeTravelSchema,
221
+ endpoints,
222
+ init: async (ctx) => {
223
+ const adapter = ctx.adapter;
224
+ ctx.timeProvider = createTimeTravelProvider(adapter);
225
+ ctx.logger.warn("Time Travel plugin enabled. DO NOT USE IN PRODUCTION.");
226
+ const states = await listTimeTravelStates(adapter);
227
+ if (states.length > 0) {
228
+ ctx.logger.info(
229
+ `Time Travel: ${states.length} customer(s) with simulated time`
230
+ );
231
+ }
232
+ }
233
+ };
234
+ }
235
+ export {
236
+ createTimeTravelProvider,
237
+ getSimulatedTime,
238
+ isTimeTravelActive,
239
+ listTimeTravelStates,
240
+ timeTravelPlugin,
241
+ timeTravelSchema
242
+ };
243
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/schema.ts","../src/time-provider.ts"],"sourcesContent":["import type { BillingEndpoint, BillSDKPlugin } from \"@billsdk/core\";\nimport { z } from \"zod\";\nimport { timeTravelSchema } from \"./schema\";\nimport {\n createTimeTravelProvider,\n getSimulatedTime,\n listTimeTravelStates,\n setSimulatedTime,\n} from \"./time-provider\";\n\n/**\n * Schema for set time endpoint\n */\nconst setTimeSchema = z.object({\n customerId: z.string(),\n date: z.string().nullable(), // ISO string or null\n});\n\n/**\n * Schema for get time endpoint\n */\nconst getTimeSchema = z.object({\n customerId: z.string(),\n});\n\n/**\n * Schema for advance time endpoint\n */\nconst advanceTimeSchema = z.object({\n customerId: z.string(),\n days: z.number().optional().default(0),\n hours: z.number().optional().default(0),\n months: z.number().optional().default(0),\n});\n\n/**\n * Schema for reset time endpoint\n */\nconst resetTimeSchema = z.object({\n customerId: z.string(),\n});\n\n/**\n * Time Travel Plugin for BillSDK\n *\n * Allows you to simulate time per customer for testing subscription cycles,\n * trials, renewals, and other time-based billing logic.\n *\n * WARNING: This plugin is for development/testing only.\n * Do NOT use in production.\n *\n * @example\n * ```typescript\n * import { billsdk } from \"billsdk\";\n * import { timeTravelPlugin } from \"@billsdk/time-travel\";\n *\n * const billing = billsdk({\n * // ... your config\n * plugins: [timeTravelPlugin()],\n * });\n * ```\n */\nexport function timeTravelPlugin(): BillSDKPlugin {\n const endpoints: Record<string, BillingEndpoint> = {\n timeTravelSet: {\n path: \"/time-travel/set\",\n options: {\n method: \"POST\",\n body: setTimeSchema,\n },\n handler: async (context: {\n body: z.infer<typeof setTimeSchema>;\n ctx: { adapter: unknown };\n }) => {\n const { body, ctx } = context;\n const { customerId } = body;\n const date = body.date ? new Date(body.date) : null;\n\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n await setSimulatedTime(ctx.adapter as any, customerId, date);\n\n return {\n success: true,\n customerId,\n simulatedTime: date?.toISOString() ?? null,\n isSimulated: date !== null,\n };\n },\n },\n\n timeTravelGet: {\n path: \"/time-travel/get\",\n options: {\n method: \"POST\",\n body: getTimeSchema,\n },\n handler: async (context: {\n body: z.infer<typeof getTimeSchema>;\n ctx: { adapter: unknown };\n }) => {\n const { body, ctx } = context;\n const { customerId } = body;\n const simulatedTime = await getSimulatedTime(\n customerId,\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n ctx.adapter as any,\n );\n return {\n customerId,\n simulatedTime: simulatedTime?.toISOString() ?? null,\n isSimulated: simulatedTime !== null,\n realTime: new Date().toISOString(),\n };\n },\n },\n\n timeTravelAdvance: {\n path: \"/time-travel/advance\",\n options: {\n method: \"POST\",\n body: advanceTimeSchema,\n },\n handler: async (context: {\n body: z.infer<typeof advanceTimeSchema>;\n ctx: { adapter: unknown };\n }) => {\n const { body, ctx } = context;\n const { customerId, days = 0, hours = 0, months = 0 } = body;\n\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n const adapter = ctx.adapter as any;\n\n // Get current time for this customer (simulated or real)\n const simulatedTime = await getSimulatedTime(customerId, adapter);\n const current = simulatedTime ? new Date(simulatedTime) : new Date();\n\n // Advance time\n current.setMonth(current.getMonth() + months);\n current.setDate(current.getDate() + days);\n current.setHours(current.getHours() + hours);\n\n await setSimulatedTime(adapter, customerId, current);\n\n return {\n success: true,\n customerId,\n simulatedTime: current.toISOString(),\n advanced: { days, hours, months },\n };\n },\n },\n\n timeTravelReset: {\n path: \"/time-travel/reset\",\n options: {\n method: \"POST\",\n body: resetTimeSchema,\n },\n handler: async (context: {\n body: z.infer<typeof resetTimeSchema>;\n ctx: { adapter: unknown };\n }) => {\n const { body, ctx } = context;\n const { customerId } = body;\n\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n await setSimulatedTime(ctx.adapter as any, customerId, null);\n\n return {\n success: true,\n customerId,\n simulatedTime: null,\n realTime: new Date().toISOString(),\n };\n },\n },\n\n timeTravelList: {\n path: \"/time-travel/list\",\n options: {\n method: \"GET\",\n },\n handler: async (context: { ctx: { adapter: unknown } }) => {\n const { ctx } = context;\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n const states = await listTimeTravelStates(ctx.adapter as any);\n return {\n customers: states.map((s) => ({\n customerId: s.id,\n simulatedTime: s.simulatedTime\n ? new Date(s.simulatedTime).toISOString()\n : null,\n })),\n realTime: new Date().toISOString(),\n };\n },\n },\n };\n\n return {\n id: \"time-travel\",\n name: \"Time Travel\",\n schema: timeTravelSchema,\n endpoints,\n\n init: async (ctx) => {\n // biome-ignore lint/suspicious/noExplicitAny: Plugin context is loosely typed to keep plugin authoring simple\n const adapter = (ctx as any).adapter;\n\n // Replace the default time provider with our time travel provider\n // biome-ignore lint/suspicious/noExplicitAny: Plugin init can modify context - this is intentional for extensibility\n (ctx as any).timeProvider = createTimeTravelProvider(adapter);\n\n ctx.logger.warn(\"Time Travel plugin enabled. DO NOT USE IN PRODUCTION.\");\n\n // Log active time travel states\n const states = await listTimeTravelStates(adapter);\n if (states.length > 0) {\n ctx.logger.info(\n `Time Travel: ${states.length} customer(s) with simulated time`,\n );\n }\n },\n };\n}\n","import type { BillSDKPluginSchema } from \"@billsdk/core\";\n\n/**\n * Schema for the time_travel_state table\n *\n * Stores the simulated time for time-travel testing per customer.\n * Each row uses the customerId as the id field.\n */\nexport const timeTravelSchema: BillSDKPluginSchema = {\n time_travel_state: {\n fields: {\n id: { type: \"string\", required: true }, // customerId\n simulatedTime: { type: \"date\", required: false },\n createdAt: { type: \"date\", required: true },\n updatedAt: { type: \"date\", required: true },\n },\n },\n};\n","import type { DBAdapter, TimeProvider } from \"@billsdk/core\";\n\nexport interface TimeTravelState extends Record<string, unknown> {\n id: string; // customerId\n simulatedTime: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\n// Store adapter reference for the time provider\nlet dbAdapter: DBAdapter | null = null;\n\n/**\n * Create a time travel provider that reads directly from the database\n * Supports per-customer time simulation\n */\nexport function createTimeTravelProvider(adapter: DBAdapter): TimeProvider {\n dbAdapter = adapter;\n return {\n now: async (customerId?: string) => {\n if (!customerId) {\n return new Date();\n }\n\n try {\n const state = await adapter.findOne<TimeTravelState>({\n model: \"time_travel_state\",\n where: [{ field: \"id\", operator: \"eq\", value: customerId }],\n });\n return state?.simulatedTime\n ? new Date(state.simulatedTime)\n : new Date();\n } catch {\n return new Date();\n }\n },\n };\n}\n\n/**\n * Set the simulated time for a specific customer\n */\nexport async function setSimulatedTime(\n adapter: DBAdapter,\n customerId: string,\n time: Date | null,\n): Promise<void> {\n const realNow = new Date();\n\n const existing = await adapter.findOne<TimeTravelState>({\n model: \"time_travel_state\",\n where: [{ field: \"id\", operator: \"eq\", value: customerId }],\n });\n\n if (existing) {\n await adapter.update<TimeTravelState>({\n model: \"time_travel_state\",\n where: [{ field: \"id\", operator: \"eq\", value: customerId }],\n update: {\n simulatedTime: time,\n updatedAt: realNow,\n },\n });\n } else {\n await adapter.create<TimeTravelState>({\n model: \"time_travel_state\",\n data: {\n id: customerId,\n simulatedTime: time,\n createdAt: realNow,\n updatedAt: realNow,\n },\n });\n }\n}\n\n/**\n * Get the simulated time for a specific customer\n */\nexport async function getSimulatedTime(\n customerId: string,\n adapter?: DBAdapter,\n): Promise<Date | null> {\n const db = adapter ?? dbAdapter;\n if (!db) return null;\n\n try {\n const state = await db.findOne<TimeTravelState>({\n model: \"time_travel_state\",\n where: [{ field: \"id\", operator: \"eq\", value: customerId }],\n });\n return state?.simulatedTime ? new Date(state.simulatedTime) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if time travel is active for a specific customer\n */\nexport async function isTimeTravelActive(\n customerId: string,\n adapter?: DBAdapter,\n): Promise<boolean> {\n const time = await getSimulatedTime(customerId, adapter);\n return time !== null;\n}\n\n/**\n * List all customers with active time travel\n */\nexport async function listTimeTravelStates(\n adapter?: DBAdapter,\n): Promise<TimeTravelState[]> {\n const db = adapter ?? dbAdapter;\n if (!db) return [];\n\n try {\n const states = await db.findMany<TimeTravelState>({\n model: \"time_travel_state\",\n where: [],\n });\n return states.filter((s) => s.simulatedTime !== null);\n } catch {\n return [];\n }\n}\n\n/**\n * Reset time travel for a specific customer (remove simulated time)\n */\nexport async function resetSimulatedTime(\n adapter: DBAdapter,\n customerId: string,\n): Promise<void> {\n await setSimulatedTime(adapter, customerId, null);\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@billsdk/time-travel",
3
+ "version": "0.1.0",
4
+ "description": "Time travel plugin for BillSDK - test subscription cycles by simulating time",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "decker",
9
+ "url": "https://github.com/decker-dev"
10
+ },
11
+ "homepage": "https://github.com/commet-labs/billsdk/tree/main/packages/time-travel#readme",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/commet-labs/billsdk.git",
15
+ "directory": "packages/time-travel"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/commet-labs/billsdk/issues"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "sideEffects": false,
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "./react": {
30
+ "types": "./dist/client/index.d.ts",
31
+ "default": "./dist/client/index.js"
32
+ }
33
+ },
34
+ "main": "./dist/index.js",
35
+ "module": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "dev": "tsup --watch",
43
+ "test": "vitest run --passWithNoTests",
44
+ "test:watch": "vitest",
45
+ "typecheck": "tsc --noEmit",
46
+ "lint": "biome check .",
47
+ "lint:fix": "biome check . --write",
48
+ "clean": "rm -rf dist .turbo node_modules"
49
+ },
50
+ "peerDependencies": {
51
+ "@billsdk/core": "workspace:*",
52
+ "react": ">=18",
53
+ "zod": ">=4"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "react": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@billsdk/core": "workspace:*",
62
+ "@biomejs/biome": "2.3.11",
63
+ "@types/node": "22.15.29",
64
+ "@types/react": "19.1.8",
65
+ "react": "19.1.0",
66
+ "tsup": "8.5.1",
67
+ "typescript": "5.9.3",
68
+ "vitest": "4.0.17",
69
+ "zod": "4.3.5"
70
+ },
71
+ "keywords": [
72
+ "billing",
73
+ "sdk",
74
+ "time-travel",
75
+ "testing",
76
+ "development",
77
+ "plugin",
78
+ "subscription",
79
+ "trial"
80
+ ]
81
+ }