UNPKG

@wallethero/sdk

Version:

TypeScript SDK for WalletHero API - manage pass templates and passes for Apple Wallet and Google Pay

1,941 lines (1,931 loc) 63.8 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { CampaignsService: () => CampaignsService, DistributionsService: () => DistributionsService, EventsService: () => EventsService, FilesService: () => FilesService, HttpClient: () => HttpClient, PassTemplatesService: () => PassTemplatesService, PassesService: () => PassesService, ProjectsService: () => ProjectsService, QRCodeService: () => QRCodeService, SegmentsService: () => SegmentsService, WalletHero: () => WalletHero, WalletHeroError: () => WalletHeroError, WorkspacesService: () => WorkspacesService, default: () => WalletHero }); module.exports = __toCommonJS(index_exports); // src/campaigns.ts var CampaignsService = class { constructor(httpClient) { this.http = httpClient; } // ============================================================================ // CRUD Operations // ============================================================================ /** * Get a list of campaigns */ async list(options) { return this.http.get( "/items/campaigns", options ); } /** * Get a specific campaign by ID */ async get(id, options) { return this.http.get( `/items/campaigns/${id}`, options ); } /** * Create a new campaign */ async create(data) { return this.http.post("/items/campaigns", data); } /** * Update an existing campaign */ async update(id, data) { return this.http.patch( `/items/campaigns/${id}`, data ); } /** * Delete a campaign */ async delete(id) { await this.http.delete(`/items/campaigns/${id}`); } // ============================================================================ // Query Methods // ============================================================================ /** * Get campaigns by workspace */ async getByWorkspace(workspaceId, options) { const filterOptions = { ...options, filter: { ...options?.filter, workspace_id: { _eq: workspaceId } } }; return this.list(filterOptions); } /** * Get campaigns by status */ async getByStatus(status, options) { const filterOptions = { ...options, filter: { ...options?.filter, status: { _eq: status } } }; return this.list(filterOptions); } /** * Get campaigns for calendar view */ async getCalendar(workspaceId, options) { const params = { workspace_id: workspaceId }; if (options?.status) params.status = options.status; if (options?.start) params.start = options.start; if (options?.end) params.end = options.end; const response = await this.http.get( "/wallethero-api/campaigns/calendar", params ); return response.data; } // ============================================================================ // Lifecycle Methods // ============================================================================ /** * Schedule a campaign for future execution */ async schedule(id, scheduledAt) { const response = await this.http.post( `/wallethero-api/campaigns/${id}/schedule`, { scheduled_at: scheduledAt } ); return response.data; } /** * Start a campaign immediately (bypasses scheduling) */ async startNow(id) { const response = await this.http.post( `/wallethero-api/campaigns/${id}/start-now` ); return response.data; } /** * Complete a campaign */ async complete(id) { const response = await this.http.post( `/wallethero-api/campaigns/${id}/complete` ); return response.data; } /** * Cancel a campaign */ async cancel(id) { const response = await this.http.post( `/wallethero-api/campaigns/${id}/cancel` ); return response.data; } // ============================================================================ // Audience Methods // ============================================================================ /** * Preview campaign audience */ async previewAudience(id, limit = 100) { const response = await this.http.get(`/wallethero-api/campaigns/${id}/preview`, { limit }); return { passes: response.data, total: response.meta?.total ?? 0 }; } /** * Count campaign audience */ async countAudience(id) { const response = await this.http.get( `/wallethero-api/campaigns/${id}/count` ); return response.data; } // ============================================================================ // Statistics // ============================================================================ /** * Get campaign statistics */ async getStatistics(id) { const response = await this.http.get( `/wallethero-api/campaigns/${id}/statistics` ); return response.data; } /** * Get campaign events (events logged during execution) */ async getEvents(id, options) { const params = {}; if (options?.limit) params.limit = options.limit; if (options?.offset) params.offset = options.offset; return this.http.get( `/wallethero-api/campaigns/${id}/events`, params ); } /** * Get campaign errors (failed passes with error details) */ async getErrors(id) { const response = await this.http.get( `/wallethero-api/campaigns/${id}/errors` ); return response.data; } /** * Get all campaign passes (affected users) */ async getPasses(id, options) { const params = {}; if (options?.limit) params.limit = options.limit; if (options?.offset) params.offset = options.offset; return this.http.get( `/wallethero-api/campaigns/${id}/passes`, params ); } // ============================================================================ // Actions Management // ============================================================================ /** * Get all actions for a campaign */ async getActions(campaignId) { const response = await this.http.get( `/wallethero-api/campaigns/${campaignId}/actions` ); return response.data; } /** * Add an action to a campaign */ async addAction(campaignId, action) { const response = await this.http.post( `/wallethero-api/campaigns/${campaignId}/actions`, action ); return response.data; } /** * Update a campaign action */ async updateAction(actionId, data) { const response = await this.http.patch( `/wallethero-api/campaign-actions/${actionId}`, data ); return response.data; } /** * Delete a campaign action */ async deleteAction(actionId) { await this.http.delete(`/wallethero-api/campaign-actions/${actionId}`); } /** * Execute a single action */ async executeAction(actionId) { const response = await this.http.post( `/wallethero-api/campaign-actions/${actionId}/execute` ); return response.data; } /** * Execute all pending actions in a campaign */ async executeAllActions(campaignId) { const response = await this.http.post( `/wallethero-api/campaigns/${campaignId}/execute-all` ); return response.data; } // ============================================================================ // Action Helpers // ============================================================================ /** * Add a push notification action */ async addPushNotificationAction(campaignId, message) { return this.addAction(campaignId, { action_type: "push_notification", action_config: { notification_message: message } }); } /** * Add a template switch action */ async addTemplateSwitchAction(campaignId, targetTemplateId) { return this.addAction(campaignId, { action_type: "template_switch", action_config: { target_template_id: targetTemplateId } }); } /** * Add a custom field update action */ async addCustomFieldUpdateAction(campaignId, fieldUpdates) { return this.addAction(campaignId, { action_type: "custom_field_update", action_config: { custom_field_updates: fieldUpdates } }); } // ============================================================================ // Utility Methods // ============================================================================ /** * Duplicate a campaign */ async duplicate(id, options) { const response = await this.http.post( `/wallethero-api/campaigns/${id}/duplicate`, options ?? {} ); return response.data; } /** * Get draft campaigns */ async getDrafts(options) { return this.getByStatus("draft", options); } /** * Get scheduled campaigns */ async getScheduled(options) { return this.getByStatus("scheduled", options); } /** * Get active campaigns */ async getActive(options) { return this.getByStatus("active", options); } /** * Get completed campaigns */ async getCompleted(options) { return this.getByStatus("completed", options); } /** * Assign a segment to a campaign */ async assignSegment(campaignId, segmentId) { const response = await this.update(campaignId, { segment_id: segmentId }); return response.data; } /** * Remove segment from a campaign */ async removeSegment(campaignId) { const response = await this.http.patch( `/items/campaigns/${campaignId}`, { segment_id: null } ); return response.data; } }; // src/distributions.ts var DistributionsService = class { constructor(httpClient) { this.http = httpClient; } /** * Get a list of distributions */ async list(options) { return this.http.get( "/items/distributions", options ); } /** * Get a specific distribution by ID */ async get(id, options) { return this.http.get( `/items/distributions/${id}`, options ); } /** * Create a new distribution */ async create(data) { return this.http.post( "/items/distributions", data ); } /** * Update an existing distribution */ async update(id, data) { return this.http.patch( `/items/distributions/${id}`, data ); } /** * Delete a distribution */ async delete(id) { await this.http.delete(`/items/distributions/${id}`); } /** * Get distributions by workspace */ async getByWorkspace(workspaceId, options) { const filterOptions = { ...options, filter: { ...options?.filter, workspace_id: { _eq: workspaceId } } }; return this.list(filterOptions); } /** * Get distributions by project */ async getByProject(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId } } }; return this.list(filterOptions); } /** * Get distributions by template */ async getByTemplate(templateId, options) { const filterOptions = { ...options, filter: { ...options?.filter, pass_template_id: { _eq: templateId } } }; return this.list(filterOptions); } /** * Get distributions by status */ async getByStatus(status, options) { const filterOptions = { ...options, filter: { ...options?.filter, status: { _eq: status } } }; return this.list(filterOptions); } /** * Get a published distribution by page address * Only returns published distributions (for public access) * @param pageAddress - The page address to look up * @param workspaceFilter - Optional workspace filter (slug or ID) * @param options - Optional query options */ async getByPageAddress(pageAddress, workspaceFilter, options) { const filter = { ...options?.filter, page_address: { _eq: pageAddress }, status: { _eq: "published" } }; if (workspaceFilter?.workspaceSlug) { filter.workspace_id = { slug: { _eq: workspaceFilter.workspaceSlug } }; } else if (workspaceFilter?.workspaceId) { filter.workspace_id = { _eq: workspaceFilter.workspaceId }; } const filterOptions = { ...options, filter, limit: 1 }; const result = await this.list(filterOptions); if (result.data.length === 0) { throw new Error( `Published distribution with page address '${pageAddress}' not found` ); } return { data: result.data[0] }; } /** * Search distributions by page address */ async search(query, options) { const searchOptions = { ...options, search: query }; return this.list(searchOptions); } /** * Publish a distribution (change status to published) */ async publish(id) { return this.update(id, { status: "published" }); } /** * Unpublish a distribution (change status to draft) */ async unpublish(id) { return this.update(id, { status: "draft" }); } /** * Duplicate a distribution * @param id - The ID of the distribution to duplicate * @param options - Options for duplication * @param options.newPageAddress - New page address (optional, defaults to same address for versioning) * @param options.keepSameAddress - If true, keeps the same page_address (for versioning). Defaults to true. */ async duplicate(id, options) { const { newPageAddress, keepSameAddress = true } = options || {}; const distribution = await this.get(id); const { id: _, user_created: _uc, date_created: _dc, user_updated: _uu, date_updated: _du, page_address, ...distributionData } = distribution.data; let finalPageAddress; if (newPageAddress) { finalPageAddress = newPageAddress; } else if (keepSameAddress) { finalPageAddress = page_address || ""; } else { finalPageAddress = `${page_address}-copy`; } return this.create({ ...distributionData, page_address: finalPageAddress, status: "draft" }); } /** * Get complete distribution data with related entities */ async getComplete(id) { return this.get(id, { fields: ["*", "project_id.*", "pass_template_id.*", "workspace_id.*"] }); } /** * Bulk update distributions */ async bulkUpdate(updates) { const updatePromises = updates.map(({ id, data }) => this.update(id, data)); const results = await Promise.all(updatePromises); return { data: results.map((result) => result.data), meta: { filter_count: results.length, total_count: results.length } }; } /** * Bulk publish distributions */ async bulkPublish(ids) { return this.bulkUpdate( ids.map((id) => ({ data: { status: "published" }, id })) ); } /** * Bulk unpublish distributions (set status to draft) */ async bulkUnpublish(ids) { return this.bulkUpdate( ids.map((id) => ({ data: { status: "draft" }, id })) ); } }; // src/events.ts var EventsService = class { constructor(httpClient) { this.http = httpClient; } /** * Create a single event */ async create(data) { return this.http.post( "/wallethero-api/events", data ); } /** * Create multiple events in batch (up to 1000) */ async createBatch(events) { return this.http.post( "/wallethero-api/events/batch", { events } ); } /** * Get a single event by ID */ async get(id) { return this.http.get( `/wallethero-api/events/${id}` ); } /** * Delete multiple events by IDs */ async deleteBatch(eventIds, workspaceId) { return this.http.delete( "/wallethero-api/events/batch", { event_ids: eventIds, workspace_id: workspaceId } ); } /** * List events with filters */ async list(query) { const params = new URLSearchParams(); if (query.workspace_id) params.set("workspace_id", query.workspace_id); if (query.pass_id) params.set("pass_id", query.pass_id); if (query.project_id) params.set("project_id", query.project_id); if (query.pass_template_id) params.set("pass_template_id", query.pass_template_id); if (query.event_category) params.set("event_category", query.event_category); if (query.event_types?.length) params.set("event_types", query.event_types.join(",")); if (query.field_name) params.set("field_name", query.field_name); if (query.from_date) params.set("from_date", query.from_date); if (query.to_date) params.set("to_date", query.to_date); if (query.source) params.set("source", query.source); if (query.limit) params.set("limit", String(query.limit)); if (query.offset) params.set("offset", String(query.offset)); return this.http.get( `/wallethero-api/events?${params.toString()}` ); } /** * Get events for a specific pass */ async getByPass(passId, options) { const params = new URLSearchParams(); if (options?.event_category) params.set("event_category", options.event_category); if (options?.limit) params.set("limit", String(options.limit)); if (options?.offset) params.set("offset", String(options.offset)); const queryString = params.toString(); const url = `/wallethero-api/passes/${passId}/events${queryString ? `?${queryString}` : ""}`; return this.http.get(url); } /** * Aggregate events (count, sum, avg, min, max) */ async aggregate(query) { return this.http.post( "/wallethero-api/events/aggregate", query ); } /** * Aggregate events grouped by pass */ async aggregateByPass(query) { return this.http.post( "/wallethero-api/events/aggregate", { ...query, group_by_pass: true } ); } /** * Get distinct event types used in a workspace */ async getEventTypes(workspaceId) { return this.http.get( `/wallethero-api/events/types?workspace_id=${workspaceId}` ); } /** * Get distinct event sources in a workspace */ async getSources(workspaceId) { return this.http.get( `/wallethero-api/events/sources?workspace_id=${workspaceId}` ); } // ===== CONVENIENCE METHODS ===== /** * Record a purchase transaction */ async recordPurchase(passId, amount, currency, options) { return this.create({ event_category: "transaction", pass_id: passId, event_type: "purchase", amount, currency, ...options }); } /** * Record a refund transaction */ async recordRefund(passId, amount, currency, options) { return this.create({ event_category: "transaction", pass_id: passId, event_type: "refund", amount, currency, ...options }); } /** * Record a custom transaction */ async recordTransaction(passId, eventType, amount, currency, options) { return this.create({ event_category: "transaction", pass_id: passId, event_type: eventType, amount, currency, ...options }); } /** * Record a check-in activity */ async recordCheckIn(passId, options) { return this.create({ event_category: "activity", pass_id: passId, event_type: "check_in", ...options }); } /** * Record a visit activity */ async recordVisit(passId, options) { return this.create({ event_category: "activity", pass_id: passId, event_type: "visit", ...options }); } /** * Record a custom activity */ async recordActivity(passId, eventType, options) { return this.create({ event_category: "activity", pass_id: passId, event_type: eventType, ...options }); } /** * Record a field change (e.g., points balance update) */ async recordFieldChange(passId, fieldName, oldValue, newValue, options) { return this.create({ event_category: "field_change", pass_id: passId, event_type: options?.event_type || "field_updated", field_name: fieldName, old_value: oldValue, new_value: newValue, event_timestamp: options?.event_timestamp, external_id: options?.external_id, source: options?.source, metadata: options?.metadata }); } /** * Record points earned (increment) */ async recordPointsEarned(passId, pointsFieldName, pointsEarned, currentBalance, options) { const oldBalance = currentBalance - pointsEarned; return this.recordFieldChange( passId, pointsFieldName, String(oldBalance), String(currentBalance), { event_type: "field_increment", ...options } ); } /** * Record points redeemed (decrement) */ async recordPointsRedeemed(passId, pointsFieldName, pointsRedeemed, currentBalance, options) { const oldBalance = currentBalance + pointsRedeemed; return this.recordFieldChange( passId, pointsFieldName, String(oldBalance), String(currentBalance), { event_type: "field_decrement", ...options } ); } // ===== AGGREGATION HELPERS ===== /** * Get total transaction amount for a workspace */ async getTotalTransactionAmount(workspaceId, options) { const result = await this.aggregate({ workspace_id: workspaceId, function: "sum", field: "amount", event_category: "transaction", event_types: options?.event_types, time_window: options?.time_window, custom_days: options?.custom_days }); return result.data.value; } /** * Get event count for a workspace */ async getEventCount(workspaceId, options) { const result = await this.aggregate({ workspace_id: workspaceId, function: "count", event_category: options?.event_category, event_types: options?.event_types, time_window: options?.time_window, custom_days: options?.custom_days }); return result.data.value; } }; // src/files.ts var FilesService = class { constructor(httpClient) { this.http = httpClient; } /** * Upload a file */ async upload(file, options) { const formData = new FormData(); if (file instanceof File) { formData.append("file", file); } else { formData.append("file", file, options?.title || "upload"); } if (options?.title) { formData.append("title", options.title); } if (options?.description) { formData.append("description", options.description); } if (options?.folder) { formData.append("folder", options.folder); } if (options?.tags) { formData.append("tags", JSON.stringify(options.tags)); } const response = await fetch(`${this.http.apiBaseUrl}/files`, { method: "POST", headers: { Authorization: `Bearer ${this.http.apiToken}` }, body: formData }); if (!response.ok) { throw new Error(`File upload failed: ${response.statusText}`); } return response.json(); } /** * Get file information by ID */ async get(id, options) { return this.http.get(`/files/${id}`, options); } /** * List files */ async list(options) { return this.http.get("/files", options); } /** * Delete a file */ async delete(id) { await this.http.delete(`/files/${id}`); } /** * Update file metadata */ async update(id, data) { return this.http.patch(`/files/${id}`, data); } /** * Get file URL for download/display */ getFileUrl(id, options) { const baseUrl = this.http.apiBaseUrl; let url = `${baseUrl}/assets/${id}`; if (options) { const params = new URLSearchParams(); if (options.width) params.set("width", options.width.toString()); if (options.height) params.set("height", options.height.toString()); if (options.fit) params.set("fit", options.fit); if (options.quality) params.set("quality", options.quality.toString()); if (options.format) params.set("format", options.format); const queryString = params.toString(); if (queryString) { url += `?${queryString}`; } } return url; } /** * Upload image with automatic optimization for pass templates */ async uploadPassImage(file, type, options) { const folderMap = { icon: "166196bd-e7f2-46f4-aee8-02f1ad8a6b4d", logo: "6c49ee61-a3c6-44c1-8d1e-49d755bd4bf7", cover_image: "5e7dc33a-16ee-4cf3-bc41-2c8617e15500" }; return this.upload(file, { ...options, folder: folderMap[type], title: options?.title || `Pass ${type} image` }); } }; // src/http-client.ts var WalletHeroError = class extends Error { constructor(message, code, status, details) { super(message); this.code = code; this.status = status; this.details = details; this.name = "WalletHeroError"; } }; var HttpClient = class { constructor(apiUrl, apiToken, timeout = 3e4) { this.baseUrl = apiUrl.replace(/\/$/, ""); this.token = apiToken; this.timeout = timeout; } // Public getters for use in other services get apiBaseUrl() { return this.baseUrl; } get apiToken() { return this.token; } buildUrl(endpoint, options) { const url = new URL(`${this.baseUrl}${endpoint}`); if (options) { if (options.fields) { url.searchParams.set("fields", options.fields.join(",")); } if (options.filter) { url.searchParams.set("filter", JSON.stringify(options.filter)); } if (options.sort) { url.searchParams.set("sort", options.sort.join(",")); } if (options.limit) { url.searchParams.set("limit", options.limit.toString()); } if (options.offset) { url.searchParams.set("offset", options.offset.toString()); } if (options.search) { url.searchParams.set("search", options.search); } } return url.toString(); } async handleResponse(response) { if (!response.ok) { let errorData; try { errorData = await response.json(); } catch { errorData = { message: response.statusText }; } if (errorData.errors && Array.isArray(errorData.errors)) { const error = errorData.errors[0]; throw new WalletHeroError( error.message || "Unknown error", error.extensions?.code || void 0, response.status, errorData ); } throw new WalletHeroError( errorData.message || `HTTP ${response.status}: ${response.statusText}`, "HTTP_ERROR", response.status, errorData ); } const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return response.json(); } return response.text(); } async get(endpoint, options) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(this.buildUrl(endpoint, options), { method: "GET", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, signal: controller.signal }); return this.handleResponse(response); } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new WalletHeroError("Request timeout", "TIMEOUT", 0); } throw error; } finally { clearTimeout(timeoutId); } } async post(endpoint, data) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "POST", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, body: data ? JSON.stringify(data) : void 0, signal: controller.signal }); return this.handleResponse(response); } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new WalletHeroError("Request timeout", "TIMEOUT", 0); } throw error; } finally { clearTimeout(timeoutId); } } async patch(endpoint, data) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "PATCH", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, body: data ? JSON.stringify(data) : void 0, signal: controller.signal }); return this.handleResponse(response); } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new WalletHeroError("Request timeout", "TIMEOUT", 0); } throw error; } finally { clearTimeout(timeoutId); } } async put(endpoint, data) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "PUT", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, body: data ? JSON.stringify(data) : void 0, signal: controller.signal }); return this.handleResponse(response); } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new WalletHeroError("Request timeout", "TIMEOUT", 0); } throw error; } finally { clearTimeout(timeoutId); } } async delete(endpoint, data) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const response = await fetch(`${this.baseUrl}${endpoint}`, { method: "DELETE", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, body: data ? JSON.stringify(data) : void 0, signal: controller.signal }); return this.handleResponse(response); } catch (error) { if (error instanceof Error && error.name === "AbortError") { throw new WalletHeroError("Request timeout", "TIMEOUT", 0); } throw error; } finally { clearTimeout(timeoutId); } } }; // src/pass-templates.ts var PassTemplatesService = class { constructor(httpClient) { this.http = httpClient; this.files = new FilesService(httpClient); } /** * Filter out fields that shouldn't be sent in update requests */ filterUpdateData(data) { const { id: _id, user_created: _user_created, date_created: _date_created, user_updated: _user_updated, date_updated: _date_updated, statistics: _statistics, uuid: _uuid, ...filteredData } = data; return filteredData; } /** * Get a list of pass templates */ async list(options) { return this.http.get( "/items/pass_templates", options ); } /** * Get a specific pass template by ID (UUID) */ async get(id, options) { return this.http.get( `/items/pass_templates/${id}`, options ); } /** * Create a new pass template */ async create(data) { const filteredData = this.filterUpdateData(data); return this.http.post( "/items/pass_templates", filteredData ); } /** * Update an existing pass template */ async update(id, data) { const filteredData = this.filterUpdateData(data); return this.http.patch( `/items/pass_templates/${id}`, filteredData ); } /** * Delete a pass template */ async delete(id) { await this.http.delete(`/items/pass_templates/${id}`); } /** * Get pass templates by workspace */ async getByWorkspace(workspaceId, options) { const filterOptions = { ...options, filter: { ...options?.filter, workspace_id: { _eq: workspaceId } } }; return this.list(filterOptions); } /** * Get pass templates by project */ async getByProject(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId } } }; return this.list(filterOptions); } /** * Search pass templates by name */ async search(query, options) { const searchOptions = { ...options, search: query }; return this.list(searchOptions); } /** * Get pass templates by type */ async getByType(passType, options) { const filterOptions = { ...options, filter: { ...options?.filter, pass_type: { _eq: passType } } }; return this.list(filterOptions); } /** * Duplicate a pass template */ async duplicate(id) { const template = await this.get(id); const filteredData = this.filterUpdateData(template.data); return this.create({ ...filteredData, name: `${template.data.name} (Copy)`, uuid: crypto.randomUUID() // Generate new UUID for the copy }); } /** * Upload and set template images */ async updateImages(id, images) { const updateData = {}; for (const [key, value] of Object.entries(images)) { if (value instanceof File || value instanceof Blob) { const uploadResult = await this.files.uploadPassImage( value, key ); updateData[key] = uploadResult.data.id; } else if (typeof value === "string") { updateData[key] = value; } } return this.update(id, updateData); } /** * Get image URLs for a template */ getImageUrls(template) { const urls = {}; if (template.icon) { urls.icon = this.files.getFileUrl(template.icon); } if (template.logo) { urls.logo = this.files.getFileUrl(template.logo); } if (template.cover_image) { urls.cover_image = this.files.getFileUrl(template.cover_image); } return urls; } /** * Get optimized image URLs for different uses */ getOptimizedImageUrls(template) { const urls = {}; if (template.icon) { urls.icon = { small: this.files.getFileUrl(template.icon, { width: 50, height: 50, fit: "cover" }), medium: this.files.getFileUrl(template.icon, { width: 100, height: 100, fit: "cover" }), large: this.files.getFileUrl(template.icon, { width: 200, height: 200, fit: "cover" }) }; } if (template.logo) { urls.logo = { small: this.files.getFileUrl(template.logo, { width: 100, height: 50, fit: "contain" }), medium: this.files.getFileUrl(template.logo, { width: 200, height: 100, fit: "contain" }), large: this.files.getFileUrl(template.logo, { width: 400, height: 200, fit: "contain" }) }; } if (template.cover_image) { urls.cover_image = { small: this.files.getFileUrl(template.cover_image, { width: 300, height: 200, fit: "cover" }), medium: this.files.getFileUrl(template.cover_image, { width: 600, height: 400, fit: "cover" }), large: this.files.getFileUrl(template.cover_image, { width: 1200, height: 800, fit: "cover" }) }; } return urls; } /** * Remove images from a template */ async removeImages(id, imageTypes) { const updateData = {}; for (const type of imageTypes) { updateData[type] = null; } return this.update(id, updateData); } /** * Upload icon image */ async uploadIcon(id, file, _options) { return this.updateImages(id, { icon: file }); } /** * Upload logo image */ async uploadLogo(id, file, _options) { return this.updateImages(id, { logo: file }); } /** * Upload cover image */ async uploadCoverImage(id, file, _options) { return this.updateImages(id, { cover_image: file }); } /** * Set iOS deeplink configuration for a pass template */ async setIOSDeeplink(id, deeplinkConfig) { return this.update(id, deeplinkConfig); } /** * Remove iOS deeplink configuration from a pass template */ async removeIOSDeeplink(id) { return this.update(id, { ios_deeplink_id: void 0, ios_deeplink_url: void 0 }); } /** * Get iOS deeplink configuration for a pass template */ async getIOSDeeplink(id) { const template = await this.get(id); return { ios_deeplink_id: template.data.ios_deeplink_id, ios_deeplink_url: template.data.ios_deeplink_url }; } /** * Get templates by tier */ async getByTier(tierId, options) { const filterOptions = { ...options, filter: { ...options?.filter, tier_id: { _eq: tierId } } }; return this.list(filterOptions); } /** * Set template tier */ async setTier(id, tierId, tierLabel) { return this.update(id, { tier_id: tierId, tier_label: tierLabel || void 0 }); } /** * Remove template tier */ async removeTier(id) { return this.update(id, { tier_id: void 0, tier_label: void 0 }); } }; // src/passes.ts var PassesService = class { constructor(httpClient) { this.http = httpClient; } /** * Get a list of passes */ async list(options) { return this.http.get("/items/passes", options); } /** * Get a specific pass by ID (UUID) */ async get(id, options) { return this.http.get(`/items/passes/${id}`, options); } /** * Create a new pass */ async create(data) { return this.http.post("/items/passes", data); } /** * Update an existing pass */ async update(id, data) { return this.http.patch(`/items/passes/${id}`, data); } /** * Delete a pass */ async delete(id) { await this.http.delete(`/items/passes/${id}`); } /** * Get passes by workspace */ async getByWorkspace(workspaceId, options) { const filterOptions = { ...options, filter: { ...options?.filter, workspace_id: { _eq: workspaceId } } }; return this.list(filterOptions); } /** * Get passes by template */ async getByTemplate(templateId, options) { const filterOptions = { ...options, filter: { ...options?.filter, pass_template_id: { _eq: templateId } } }; return this.list(filterOptions); } /** * Get passes by project */ async getByProject(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId } } }; return this.list(filterOptions); } /** * Search passes by email or name */ async search(query, options) { const searchOptions = { ...options, search: query }; return this.list(searchOptions); } /** * Send push notification to update a pass */ async sendNotification(id, message) { const updateData = {}; if (message) { updateData.notification = message; } await this.update(id, updateData); } /** * Resend pass email to user * @param passId - The pass ID (UUID string) * @param emailTemplate - Optional custom email HTML template * @param email - Optional email address override (if not provided, uses pass's email) * @returns Object with pass URLs */ async resendEmail(passId, emailTemplate, email) { const payload = { pass_id: passId }; if (emailTemplate) { payload.email_template = emailTemplate; } if (email) { payload.email = email; } return this.http.post("/wallethero-api/pass/resend", payload); } /** * Bulk create passes from template */ async bulkCreateFromTemplate(templateId, passes) { const passesData = passes.map((pass) => ({ ...pass, pass_template_id: templateId // ID will be auto-generated as UUID by the database })); return this.http.post("/items/passes", passesData); } // ===== CUSTOM FIELDS MANAGEMENT ===== /** * Update pass custom fields (replaces the old updatePayload method) */ async updateCustomFields(id, customFields) { return this.update(id, { custom_fields: customFields }); } /** * Get custom fields for a specific pass */ async getCustomFields(id) { const response = await this.get(id, { fields: ["custom_fields"] }); return response.data.custom_fields || {}; } /** * Set a single custom field value */ async setCustomField(id, fieldName, value) { const currentFields = await this.getCustomFields(id); const updatedFields = { ...currentFields, [fieldName]: value }; return this.updateCustomFields(id, updatedFields); } /** * Get a single custom field value */ async getCustomField(id, fieldName) { const customFields = await this.getCustomFields(id); return customFields[fieldName]; } /** * Remove a custom field */ async removeCustomField(id, fieldName) { const currentFields = await this.getCustomFields(id); const { [fieldName]: _removed, ...remainingFields } = currentFields; return this.updateCustomFields(id, remainingFields); } /** * Merge custom fields (adds new fields without removing existing ones) */ async mergeCustomFields(id, fieldsToMerge) { const currentFields = await this.getCustomFields(id); const mergedFields = { ...currentFields, ...fieldsToMerge }; return this.updateCustomFields(id, mergedFields); } /** * Clear all custom fields */ async clearCustomFields(id) { return this.updateCustomFields(id, {}); } /** * Check if a custom field exists */ async hasCustomField(id, fieldName) { const customFields = await this.getCustomFields(id); return fieldName in customFields; } /** * Get all custom field names for a pass */ async getCustomFieldNames(id) { const customFields = await this.getCustomFields(id); return Object.keys(customFields); } /** * Bulk update custom fields for multiple passes */ async bulkUpdateCustomFields(updates) { const updatePromises = updates.map( ({ passId, customFields }) => this.updateCustomFields(passId, customFields) ); const results = await Promise.all(updatePromises); return { data: results.map((result) => result.data), meta: { filter_count: results.length, total_count: results.length } }; } /** * Search passes by custom field value */ async searchByCustomField(fieldName, value, options) { const searchOptions = { ...options, filter: { ...options?.filter, [`custom_fields.${fieldName}`]: { _eq: value } } }; return this.list(searchOptions); } /** * Filter passes by multiple custom field criteria */ async filterByCustomFields(criteria, options) { const customFieldFilters = {}; for (const [fieldName, value] of Object.entries(criteria)) { customFieldFilters[`custom_fields.${fieldName}`] = { _eq: value }; } const filterOptions = { ...options, filter: { ...options?.filter, ...customFieldFilters } }; return this.list(filterOptions); } // ===== LEGACY SUPPORT ===== /** * @deprecated Use updateCustomFields instead * Update pass payload (custom data) - kept for backward compatibility */ async updatePayload(id, payload) { console.warn( "updatePayload is deprecated. Use updateCustomFields instead." ); return this.updateCustomFields(id, payload); } // ===== TEMPLATE SWITCHING SUPPORT ===== /** * Switch pass to a different template within the same project */ async switchTemplate(id, newTemplateId) { return this.update(id, { pass_template_id: newTemplateId }); } /** * Get complete pass data with template inheritance */ async getCompleteData(id) { return this.http.get(`/passes/${id}/complete-data`); } /** * Bulk switch passes to a different template within the same project */ async bulkSwitchTemplate(passIds, newTemplateId) { const updatePromises = passIds.map( (passId) => this.switchTemplate(passId, newTemplateId) ); const results = await Promise.all(updatePromises); return { data: results.map((result) => result.data), meta: { filter_count: results.length, total_count: results.length } }; } }; // src/projects.ts var ProjectsService = class { constructor(httpClient) { this.http = httpClient; } /** * Get a list of projects */ async list(options) { const listOptions = { ...options, fields: options?.fields || ["*"] }; return this.http.get( "/items/projects", listOptions ); } /** * Get a specific project by ID (UUID) */ async get(id, options) { const getOptions = { ...options, fields: options?.fields || ["*"] }; return this.http.get( `/items/projects/${id}`, getOptions ); } /** * Create a new project */ async create(data) { return this.http.post("/items/projects", data); } /** * Update an existing project */ async update(id, data) { return this.http.patch(`/items/projects/${id}`, data); } /** * Delete a project */ async delete(id) { await this.http.delete(`/items/projects/${id}`); } /** * Get projects by workspace */ async getByWorkspace(workspaceId, options) { const filterOptions = { ...options, fields: options?.fields || ["*"], filter: { ...options?.filter, workspace_id: { _eq: workspaceId } } }; return this.list(filterOptions); } /** * Search projects by name */ async search(query, options) { const searchOptions = { ...options, search: query }; return this.list(searchOptions); } /** * Get all templates for a project */ async getTemplates(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId } } }; return this.http.get( "/items/pass_templates", filterOptions ); } /** * Get all passes for a project */ async getPasses(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId } } }; return this.http.get("/items/passes", filterOptions); } /** * Get complete pass data with template inheritance for a pass */ async getCompletePassData(passId) { return this.http.get( `/passes/${passId}/complete-data` ); } /** * Get project statistics */ async getStats(id) { const [templatesResponse, passesResponse] = await Promise.all([ this.getTemplates(id, { limit: 0 }), // Only need count this.getPasses(id, { limit: 0 }) // Only need count ]); return { templateCount: templatesResponse.meta?.total_count || 0, passCount: passesResponse.meta?.total_count || 0 }; } /** * Duplicate a project with all its templates */ async duplicate(id, newName) { const project = await this.get(id); const { id: _, user_created: _uc, date_created: _dc, user_updated: _uu, date_updated: _du, ...projectData } = project.data; return this.create({ ...projectData, name: newName || `${projectData.name} (Copy)` }); } }; // src/qrcode.ts var QRCodeService = class { constructor(httpClient) { this.http = httpClient; } /** * Get pass data to retrieve QR code URLs */ async getPassData(passId) { const response = await this.http.get( `/items/passes/${passId}` ); return response.data; } /** * Private helper method to build QR code URL with query parameters */ async getQRCodeUrlForPassType(passType, passId, options) { const passData = await this.getPassData(passId); const qrCodeUrl = passType === "apple" ? passData.apple_qrcode_url : passData.google_qrcode_url; if (!qrCodeUrl) { throw new Error(`${passType} QR code URL not found for pass ${passId}`); } const params = new URLSearchParams(); if (options?.width) params.append("width",