UNPKG

@wallethero/sdk

Version:

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

1,650 lines (1,640 loc) 43.2 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, { FilesService: () => FilesService, HttpClient: () => HttpClient, PassTemplatesService: () => PassTemplatesService, PassesService: () => PassesService, ProjectsService: () => ProjectsService, QRCodeService: () => QRCodeService, WalletHero: () => WalletHero, WalletHeroError: () => WalletHeroError, WorkspacesService: () => WorkspacesService, default: () => WalletHero }); module.exports = __toCommonJS(index_exports); // 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 delete(endpoint) { 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" }, 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/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/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, user_created, date_created, user_updated, date_updated, statistics, 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 }); } /** * Add a custom field to a pass template */ async addCustomField(id, customField) { const template = await this.get(id); const existingFields = template.data.custom_fields || []; if (existingFields.some((field) => field.name === customField.name)) { throw new Error( `Custom field with name '${customField.name}' already exists` ); } const updatedFields = [...existingFields, customField]; return this.update(id, { custom_fields: updatedFields }); } /** * Update a custom field in a pass template */ async updateCustomField(id, fieldName, updatedField) { const template = await this.get(id); const existingFields = template.data.custom_fields || []; const fieldIndex = existingFields.findIndex( (field) => field.name === fieldName ); if (fieldIndex === -1) { throw new Error(`Custom field with name '${fieldName}' not found`); } if (updatedField.name && updatedField.name !== fieldName) { if (existingFields.some((field) => field.name === updatedField.name)) { throw new Error( `Custom field with name '${updatedField.name}' already exists` ); } } const updatedFields = [...existingFields]; updatedFields[fieldIndex] = { ...updatedFields[fieldIndex], ...updatedField }; return this.update(id, { custom_fields: updatedFields }); } /** * Remove a custom field from a pass template */ async removeCustomField(id, fieldName) { const template = await this.get(id); const existingFields = template.data.custom_fields || []; const updatedFields = existingFields.filter( (field) => field.name !== fieldName ); if (updatedFields.length === existingFields.length) { throw new Error(`Custom field with name '${fieldName}' not found`); } return this.update(id, { custom_fields: updatedFields }); } /** * Get all custom fields for a pass template */ async getCustomFields(id) { const template = await this.get(id); return template.data.custom_fields || []; } /** * Set all custom fields for a pass template (replaces existing) */ async setCustomFields(id, customFields) { const fieldNames = customFields.map((field) => field.name); const uniqueNames = new Set(fieldNames); if (fieldNames.length !== uniqueNames.size) { throw new Error("Duplicate field names are not allowed"); } return this.update(id, { custom_fields: customFields }); } /** * 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 main templates only */ async getMainTemplates(options) { const filterOptions = { ...options, filter: { ...options?.filter, is_main_template: { _eq: true } } }; return this.list(filterOptions); } /** * Get non-main templates only */ async getCurrentTemplates(options) { const filterOptions = { ...options, filter: { ...options?.filter, is_main_template: { _neq: true } } }; return this.list(filterOptions); } /** * Get the main template for a project */ async getMainTemplateForProject(projectId, options) { const filterOptions = { ...options, filter: { ...options?.filter, project_id: { _eq: projectId }, is_main_template: { _eq: true } }, limit: 1 }; const response = await this.list(filterOptions); return { data: response.data.length > 0 ? response.data[0] : null }; } /** * Set template as main template for its project */ async setAsMainTemplate(id) { const template = await this.get(id); const projectId = template.data.project_id; const existingMainResponse = await this.getMainTemplateForProject( projectId ); if (existingMainResponse.data) { await this.update(existingMainResponse.data.id, { is_main_template: false }); } return this.update(id, { is_main_template: true }); } /** * Unset template as main template */ async unsetAsMainTemplate(id) { return this.update(id, { is_main_template: false }); } /** * Update template custom fields (new template-level custom fields) */ async updateTemplateCustomFields(id, customFields) { return this.update(id, { template_custom_fields: customFields }); } /** * Get template custom fields */ async getTemplateCustomFields(id) { const template = await this.get(id); return template.data.template_custom_fields || {}; } /** * Merge template custom fields with existing ones */ async mergeTemplateCustomFields(id, customFields) { const existingFields = await this.getTemplateCustomFields(id); const mergedFields = { ...existingFields, ...customFields }; return this.updateTemplateCustomFields(id, mergedFields); } /** * Remove specific template custom fields */ async removeTemplateCustomFields(id, fieldNames) { const existingFields = await this.getTemplateCustomFields(id); const updatedFields = { ...existingFields }; for (const fieldName of fieldNames) { delete updatedFields[fieldName]; } return this.updateTemplateCustomFields(id, updatedFields); } /** * 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); } /** * Get passes by current template (for template inheritance) */ async getByCurrentTemplate(currentTemplateId, options) { const filterOptions = { ...options, filter: { ...options?.filter, current_template_id: { _eq: currentTemplateId } } }; 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); } /** * 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: { total_count: results.length, filter_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 INHERITANCE SUPPORT ===== /** * Switch pass to a different template within the same project */ async switchTemplate(id, newTemplateId) { return this.update(id, { current_template_id: newTemplateId }); } /** * Reset pass to use its original template (unset current_template_id) */ async resetToOriginalTemplate(id) { return this.update(id, { current_template_id: void 0 }); } /** * 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: { total_count: results.length, filter_count: results.length } }; } /** * Get passes that are using template inheritance (have current_template_id) */ async getPassesWithTemplateInheritance(options) { const filterOptions = { ...options, filter: { ...options?.filter, current_template_id: { _nnull: true } } }; return this.list(filterOptions); } /** * Get passes that are NOT using template inheritance (no current_template_id) */ async getPassesWithoutTemplateInheritance(options) { const filterOptions = { ...options, filter: { ...options?.filter, current_template_id: { _null: true } } }; return this.list(filterOptions); } /** * Update pass with template inheritance context */ async updateWithTemplateInheritance(id, data) { return this.update(id, data); } }; // 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 the main template for a project */ async getMainTemplate(projectId) { const response = await this.http.get( "/items/pass_templates", { filter: { project_id: { _eq: projectId }, is_main_template: { _eq: true } }, limit: 1 } ); return { data: response.data.length > 0 ? response.data[0] : null }; } /** * Set a template as the main template for a project */ async setMainTemplate(projectId, templateId) { const existingMainResponse = await this.getMainTemplate(projectId); if (existingMainResponse.data) { await this.http.patch( `/items/pass_templates/${existingMainResponse.data.id}`, { is_main_template: false } ); } return this.http.patch( `/items/pass_templates/${templateId}`, { is_main_template: true } ); } /** * Unset the main template for a project */ async unsetMainTemplate(projectId) { const existingMainResponse = await this.getMainTemplate(projectId); if (existingMainResponse.data) { await this.http.patch( `/items/pass_templates/${existingMainResponse.data.id}`, { is_main_template: false } ); } } /** * 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` ); } /** * Merge templates using inheritance logic (main template + current template) */ async mergeTemplates(mainTemplateId, currentTemplateId) { return this.http.post("/templates/merge", { mainTemplateId, currentTemplateId }); } /** * Update project custom fields */ async updateCustomFields(id, customFields) { return this.update(id, { custom_fields: customFields }); } /** * Get project custom fields */ async getCustomFields(id) { const project = await this.get(id); return project.data.custom_fields || {}; } /** * Merge custom fields with existing ones */ async mergeCustomFields(id, customFields) { const existingFields = await this.getCustomFields(id); const mergedFields = { ...existingFields, ...customFields }; return this.updateCustomFields(id, mergedFields); } /** * Remove specific custom fields */ async removeCustomFields(id, fieldNames) { const existingFields = await this.getCustomFields(id); const updatedFields = { ...existingFields }; for (const fieldName of fieldNames) { delete updatedFields[fieldName]; } return this.updateCustomFields(id, updatedFields); } /** * Get project statistics */ async getStats(id) { const [templatesResponse, passesResponse, mainTemplateResponse] = await Promise.all([ this.getTemplates(id, { limit: 0 }), // Only need count this.getPasses(id, { limit: 0 }), // Only need count this.getMainTemplate(id) ]); return { templateCount: templatesResponse.meta?.total_count || 0, passCount: passesResponse.meta?.total_count || 0, mainTemplate: mainTemplateResponse.data }; } /** * 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", options.width.toString()); if (options?.margin) params.append("margin", options.margin.toString()); if (options?.dark) params.append("dark", options.dark); if (options?.light) params.append("light", options.light); if (options?.format) params.append("format", options.format); return `${qrCodeUrl}${params.toString() ? `?${params.toString()}` : ""}`; } /** * Get QR code URL for Apple Pass * Returns URL that can be used directly in img src or for JSON format */ async getApplePassQRCode(passId, options) { const url = await this.getQRCodeUrlForPassType("apple", passId, options); if (options?.format === "json") { return { passId, url: await this.getApplePassURL(passId), qrCode: url, format: "url" }; } return url; } /** * Get QR code URL for Google Wallet Pass * Returns URL that can be used directly in img src or for JSON format */ async getGooglePassQRCode(passId, options) { const url = await this.getQRCodeUrlForPassType("google", passId, options); if (options?.format === "json") { return { passId, url: await this.getGooglePassURL(passId), qrCode: url, format: "url" }; } return url; } /** * Get Apple Pass URL for QR code generation from pass data */ async getApplePassURL(passId) { const passData = await this.getPassData(passId); if (!passData.apple_pass_url) { throw new Error(`Apple pass URL not found for pass ${passId}`); } return passData.apple_pass_url; } /** * Get Google Wallet Pass URL for QR code generation from pass data */ async getGooglePassURL(passId) { const passData = await this.getPassData(passId); if (!passData.google_pass_url) { throw new Error(`Google pass URL not found for pass ${passId}`); } return passData.google_pass_url; } }; // src/workspaces.ts var WorkspacesService = class { constructor(httpClient) { this.http = httpClient; } /** * Get a list of workspaces */ async list(options) { return this.http.get( "/items/workspaces", options ); } /** * Get a specific workspace by ID (UUID) */ async get(id, options) { return this.http.get( `/items/workspaces/${id}`, options ); } /** * Create a new workspace */ async create(data) { return this.http.post("/items/workspaces", data); } /** * Update an existing workspace */ async update(id, data) { return this.http.patch( `/items/workspaces/${id}`, data ); } /** * Delete a workspace */ async delete(id) { await this.http.delete(`/items/workspaces/${id}`); } /** * Search workspaces by name */ async search(query, options) { const searchOptions = { ...options, search: query }; return this.list(searchOptions); } /** * Get workspaces by name (exact match) */ async getByName(name, options) { const filterOptions = { ...options, filter: { ...options?.filter, name: { _eq: name } } }; return this.list(filterOptions); } /** * Get workspace with related data (passes, templates, etc.) */ async getWithRelations(id, includeFields) { const defaultFields = [ "*", "passes.id", "passes.full_name", "passes.email", "passes.date_created", "apple_registrations.id", "apple_registrations.device_library_identifier", "google_registrations.id", "google_registrations.class_id" ]; const fields = includeFields || defaultFields; return this.get(id, { fields }); } /** * Get workspace statistics */ async getStats(id) { const workspace = await this.get(id, { fields: [ "*", "passes_func.count", "apple_registrations_func.count", "google_registrations_func.count" ] }); const templatesResponse = await this.http.get( "/items/pass_templates", { filter: { workspace_id: { _eq: id } }, limit: 0 // We only need the count from meta } ); return { passCount: workspace.data.passes_func?.count || 0, templateCount: templatesResponse.meta?.total_count || 0, appleRegistrationCount: workspace.data.apple_registrations_func?.count || 0, googleRegistrationCount: workspace.data.google_registrations_func?.count || 0 }; } /** * Get current user's workspaces */ async getMy(options) { return this.list(options); } /** * Check if workspace name is available */ async isNameAvailable(name) { const response = await this.getByName(name, { limit: 1 }); return response.data.length === 0; } /** * Get workspace members (requires junction table access) */ async getMembers(id) { return this.http.get( "/items/junction_directus_users_workspaces", { filter: { workspaces_id: { _eq: id } }, fields: [ "*", "directus_users_id.id", "directus_users_id.first_name", "directus_users_id.last_name", "directus_users_id.email" ] } ); } /** * Add member to workspace */ async addMember(workspaceId, userId) { return this.http.post( "/items/junction_directus_users_workspaces", { workspaces_id: workspaceId, directus_users_id: userId } ); } /** * Remove member from workspace */ async removeMember(workspaceId, userId) { const junctionResponse = await this.http.get( "/items/junction_directus_users_workspaces", { filter: { workspaces_id: { _eq: workspaceId }, directus_users_id: { _eq: userId } }, limit: 1 } ); if (junctionResponse.data.length > 0) { const junctionId = junctionResponse.data[0].id; await this.http.delete( `/items/junction_directus_users_workspaces/${junctionId}` ); } } }; // src/client.ts var WalletHero = class { constructor(config) { if (!config.apiToken) { throw new Error("API token is required"); } const apiUrl = config.apiUrl || "https://api.wallethero.app"; this.http = new HttpClient(apiUrl, config.apiToken, config.timeout); this.passTemplates = new PassTemplatesService(this.http); this.passes = new PassesService(this.http); this.projects = new ProjectsService(this.http); this.files = new FilesService(this.http); this.qrCodes = new QRCodeService(this.http); this.workspaces = new WorkspacesService(this.http); } /** * Test the connection to the API */ async ping() { try { await this.http.get("/server/ping"); return true; } catch { return false; } } /** * Get current user information */ async me() { return this.http.get("/users/me"); } /** * Get API server information */ async serverInfo() { return this.http.get("/server/info"); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { FilesService, HttpClient, PassTemplatesService, PassesService, ProjectsService, QRCodeService, WalletHero, WalletHeroError, WorkspacesService });