@thebookingkit/server 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +50 -0
  2. package/dist/adapters/email-adapter.js +2 -2
  3. package/dist/adapters/email-adapter.js.map +1 -1
  4. package/dist/adapters/job-adapter.d.ts +10 -10
  5. package/dist/adapters/job-adapter.d.ts.map +1 -1
  6. package/dist/adapters/job-adapter.js +10 -10
  7. package/dist/adapters/job-adapter.js.map +1 -1
  8. package/dist/api.d.ts +3 -3
  9. package/dist/api.js +5 -5
  10. package/dist/api.js.map +1 -1
  11. package/package.json +21 -2
  12. package/.turbo/turbo-build.log +0 -6
  13. package/.turbo/turbo-test.log +0 -20
  14. package/CHANGELOG.md +0 -9
  15. package/dist/__tests__/api.test.d.ts +0 -2
  16. package/dist/__tests__/api.test.d.ts.map +0 -1
  17. package/dist/__tests__/api.test.js +0 -280
  18. package/dist/__tests__/api.test.js.map +0 -1
  19. package/dist/__tests__/auth.test.d.ts +0 -2
  20. package/dist/__tests__/auth.test.d.ts.map +0 -1
  21. package/dist/__tests__/auth.test.js +0 -78
  22. package/dist/__tests__/auth.test.js.map +0 -1
  23. package/dist/__tests__/concurrent-booking.test.d.ts +0 -2
  24. package/dist/__tests__/concurrent-booking.test.d.ts.map +0 -1
  25. package/dist/__tests__/concurrent-booking.test.js +0 -111
  26. package/dist/__tests__/concurrent-booking.test.js.map +0 -1
  27. package/dist/__tests__/multi-tenancy.test.d.ts +0 -2
  28. package/dist/__tests__/multi-tenancy.test.d.ts.map +0 -1
  29. package/dist/__tests__/multi-tenancy.test.js +0 -196
  30. package/dist/__tests__/multi-tenancy.test.js.map +0 -1
  31. package/dist/__tests__/serialization-retry.test.d.ts +0 -2
  32. package/dist/__tests__/serialization-retry.test.d.ts.map +0 -1
  33. package/dist/__tests__/serialization-retry.test.js +0 -53
  34. package/dist/__tests__/serialization-retry.test.js.map +0 -1
  35. package/dist/__tests__/webhooks.test.d.ts +0 -2
  36. package/dist/__tests__/webhooks.test.d.ts.map +0 -1
  37. package/dist/__tests__/webhooks.test.js +0 -286
  38. package/dist/__tests__/webhooks.test.js.map +0 -1
  39. package/dist/__tests__/workflows.test.d.ts +0 -2
  40. package/dist/__tests__/workflows.test.d.ts.map +0 -1
  41. package/dist/__tests__/workflows.test.js +0 -299
  42. package/dist/__tests__/workflows.test.js.map +0 -1
  43. package/src/__tests__/api.test.ts +0 -354
  44. package/src/__tests__/auth.test.ts +0 -111
  45. package/src/__tests__/concurrent-booking.test.ts +0 -170
  46. package/src/__tests__/multi-tenancy.test.ts +0 -267
  47. package/src/__tests__/serialization-retry.test.ts +0 -76
  48. package/src/__tests__/webhooks.test.ts +0 -412
  49. package/src/__tests__/workflows.test.ts +0 -422
  50. package/src/adapters/calendar-adapter.ts +0 -49
  51. package/src/adapters/email-adapter.ts +0 -108
  52. package/src/adapters/index.ts +0 -36
  53. package/src/adapters/job-adapter.ts +0 -26
  54. package/src/adapters/payment-adapter.ts +0 -118
  55. package/src/adapters/sms-adapter.ts +0 -35
  56. package/src/adapters/storage-adapter.ts +0 -11
  57. package/src/api.ts +0 -446
  58. package/src/auth.ts +0 -146
  59. package/src/booking-tokens.ts +0 -61
  60. package/src/email-templates.ts +0 -140
  61. package/src/index.ts +0 -192
  62. package/src/multi-tenancy.ts +0 -301
  63. package/src/notification-jobs.ts +0 -428
  64. package/src/serialization-retry.ts +0 -94
  65. package/src/webhooks.ts +0 -378
  66. package/src/workflows.ts +0 -441
  67. package/tsconfig.json +0 -9
  68. package/vitest.config.ts +0 -7
package/src/workflows.ts DELETED
@@ -1,441 +0,0 @@
1
- /**
2
- * Workflow automation engine.
3
- *
4
- * Trigger-condition-action framework that automates tasks
5
- * based on booking lifecycle events.
6
- */
7
-
8
- // ---------------------------------------------------------------------------
9
- // Types
10
- // ---------------------------------------------------------------------------
11
-
12
- /** Supported workflow trigger events */
13
- export type WorkflowTrigger =
14
- | "booking_created"
15
- | "booking_confirmed"
16
- | "booking_cancelled"
17
- | "booking_rescheduled"
18
- | "before_event"
19
- | "after_event"
20
- | "payment_received"
21
- | "payment_failed"
22
- | "no_show_confirmed"
23
- | "form_submitted";
24
-
25
- /** Supported workflow action types */
26
- export type WorkflowActionType =
27
- | "send_email"
28
- | "send_sms"
29
- | "fire_webhook"
30
- | "update_status"
31
- | "create_calendar_event";
32
-
33
- /** Condition operator for filtering */
34
- export type ConditionOperator = "equals" | "not_equals" | "contains" | "in";
35
-
36
- /** A single workflow condition */
37
- export interface WorkflowCondition {
38
- /** The field to check (e.g., "event_type_id", "status", "customer_email") */
39
- field: string;
40
- /** Comparison operator */
41
- operator: ConditionOperator;
42
- /** Value(s) to compare against */
43
- value: string | string[];
44
- }
45
-
46
- /** Email action configuration */
47
- export interface EmailActionConfig {
48
- type: "send_email";
49
- /** Recipient: "customer", "host", or a specific email address */
50
- to: string;
51
- /** Email subject (supports template variables) */
52
- subject: string;
53
- /** Email body template (supports template variables) */
54
- body: string;
55
- }
56
-
57
- /** SMS action configuration */
58
- export interface SmsActionConfig {
59
- type: "send_sms";
60
- /** Phone number field key or literal number */
61
- to: string;
62
- /** SMS body template (supports template variables) */
63
- body: string;
64
- }
65
-
66
- /** Webhook action configuration */
67
- export interface WebhookActionConfig {
68
- type: "fire_webhook";
69
- /** Target URL */
70
- url: string;
71
- /** Payload template (JSON string with template variables) */
72
- payload?: string;
73
- /** HTTP method (default: POST) */
74
- method?: "POST" | "PUT" | "PATCH";
75
- /** Custom headers */
76
- headers?: Record<string, string>;
77
- }
78
-
79
- /** Status update action configuration */
80
- export interface StatusUpdateActionConfig {
81
- type: "update_status";
82
- /** New status to set */
83
- status: string;
84
- }
85
-
86
- /** Calendar event action configuration */
87
- export interface CalendarEventActionConfig {
88
- type: "create_calendar_event";
89
- /** Additional notes for the calendar event */
90
- notes?: string;
91
- }
92
-
93
- /** Union of all action configurations */
94
- export type WorkflowAction =
95
- | EmailActionConfig
96
- | SmsActionConfig
97
- | WebhookActionConfig
98
- | StatusUpdateActionConfig
99
- | CalendarEventActionConfig;
100
-
101
- /** A complete workflow definition */
102
- export interface WorkflowDefinition {
103
- id: string;
104
- name: string;
105
- trigger: WorkflowTrigger;
106
- conditions: WorkflowCondition[];
107
- actions: WorkflowAction[];
108
- isActive: boolean;
109
- }
110
-
111
- /** Context data passed when evaluating a workflow */
112
- export interface WorkflowContext {
113
- bookingId?: string;
114
- eventTypeId?: string;
115
- providerId?: string;
116
- customerEmail?: string;
117
- customerName?: string;
118
- customerPhone?: string;
119
- status?: string;
120
- startsAt?: Date;
121
- endsAt?: Date;
122
- hostName?: string;
123
- eventTitle?: string;
124
- eventDuration?: number;
125
- eventLocation?: string;
126
- managementUrl?: string;
127
- /** Additional fields for condition evaluation */
128
- [key: string]: unknown;
129
- }
130
-
131
- /** Result of a workflow execution log entry */
132
- export interface WorkflowLogEntry {
133
- workflowId: string;
134
- bookingId?: string;
135
- actionType: WorkflowActionType;
136
- status: "success" | "error" | "skipped";
137
- error?: string;
138
- executedAt: Date;
139
- }
140
-
141
- // ---------------------------------------------------------------------------
142
- // Errors
143
- // ---------------------------------------------------------------------------
144
-
145
- /** Error thrown when workflow validation fails */
146
- export class WorkflowValidationError extends Error {
147
- constructor(message: string) {
148
- super(message);
149
- this.name = "WorkflowValidationError";
150
- }
151
- }
152
-
153
- // ---------------------------------------------------------------------------
154
- // Template Variables
155
- // ---------------------------------------------------------------------------
156
-
157
- /** Standard template variables available in workflow messages */
158
- export const TEMPLATE_VARIABLES = [
159
- "{booking.title}",
160
- "{booking.startTime}",
161
- "{booking.endTime}",
162
- "{booking.date}",
163
- "{attendee.name}",
164
- "{attendee.email}",
165
- "{host.name}",
166
- "{event.location}",
167
- "{event.duration}",
168
- "{booking.managementUrl}",
169
- ] as const;
170
-
171
- /**
172
- * Resolve template variables in a string using workflow context.
173
- *
174
- * Missing variables are replaced with empty strings.
175
- *
176
- * @param template - The template string with `{variable}` placeholders
177
- * @param context - The workflow context with booking/event data
178
- * @returns The resolved string
179
- */
180
- export function resolveTemplateVariables(
181
- template: string,
182
- context: WorkflowContext,
183
- ): string {
184
- const vars: Record<string, string> = {
185
- "{booking.title}": context.eventTitle ?? "",
186
- "{booking.startTime}": context.startsAt
187
- ? formatTime(context.startsAt)
188
- : "",
189
- "{booking.endTime}": context.endsAt ? formatTime(context.endsAt) : "",
190
- "{booking.date}": context.startsAt ? formatDate(context.startsAt) : "",
191
- "{attendee.name}": context.customerName ?? "",
192
- "{attendee.email}": context.customerEmail ?? "",
193
- "{host.name}": context.hostName ?? "",
194
- "{event.location}": context.eventLocation ?? "",
195
- "{event.duration}": context.eventDuration
196
- ? `${context.eventDuration} minutes`
197
- : "",
198
- "{booking.managementUrl}": context.managementUrl ?? "",
199
- };
200
-
201
- let result = template;
202
- for (const [key, value] of Object.entries(vars)) {
203
- result = result.replaceAll(key, value);
204
- }
205
-
206
- return result;
207
- }
208
-
209
- function formatTime(date: Date): string {
210
- return date.toLocaleTimeString("en-US", {
211
- hour: "numeric",
212
- minute: "2-digit",
213
- hour12: true,
214
- });
215
- }
216
-
217
- function formatDate(date: Date): string {
218
- return date.toLocaleDateString("en-US", {
219
- weekday: "long",
220
- year: "numeric",
221
- month: "long",
222
- day: "numeric",
223
- });
224
- }
225
-
226
- // ---------------------------------------------------------------------------
227
- // Default Templates
228
- // ---------------------------------------------------------------------------
229
-
230
- /** Default workflow templates for common scenarios */
231
- export const DEFAULT_TEMPLATES = {
232
- confirmation: {
233
- subject: "Booking Confirmed: {booking.title}",
234
- body: "Hi {attendee.name},\n\nYour booking for {booking.title} on {booking.date} at {booking.startTime} has been confirmed.\n\nDuration: {event.duration}\nLocation: {event.location}\n\nManage your booking: {booking.managementUrl}\n\nBest regards,\n{host.name}",
235
- },
236
- reminder_24h: {
237
- subject: "Reminder: {booking.title} tomorrow",
238
- body: "Hi {attendee.name},\n\nThis is a reminder that you have a booking for {booking.title} tomorrow at {booking.startTime}.\n\nLocation: {event.location}\n\nManage your booking: {booking.managementUrl}\n\nSee you soon,\n{host.name}",
239
- },
240
- reminder_1h: {
241
- subject: "Reminder: {booking.title} in 1 hour",
242
- body: "Hi {attendee.name},\n\nYour booking for {booking.title} starts in 1 hour at {booking.startTime}.\n\nLocation: {event.location}\n\nSee you soon,\n{host.name}",
243
- },
244
- cancellation: {
245
- subject: "Booking Cancelled: {booking.title}",
246
- body: "Hi {attendee.name},\n\nYour booking for {booking.title} on {booking.date} at {booking.startTime} has been cancelled.\n\nIf you'd like to rebook, please visit our booking page.\n\nBest regards,\n{host.name}",
247
- },
248
- followup: {
249
- subject: "How was your {booking.title}?",
250
- body: "Hi {attendee.name},\n\nThank you for your recent {booking.title} with {host.name}.\n\nWe hope you had a great experience! If you'd like to book again, we'd love to see you.\n\nBest regards,\n{host.name}",
251
- },
252
- } as const;
253
-
254
- // ---------------------------------------------------------------------------
255
- // Condition Evaluation
256
- // ---------------------------------------------------------------------------
257
-
258
- /**
259
- * Evaluate whether a workflow's conditions are met for the given context.
260
- *
261
- * If no conditions are defined, returns true (unconditional trigger).
262
- * All conditions must match (AND logic).
263
- *
264
- * @param conditions - Array of workflow conditions
265
- * @param context - The workflow context data
266
- * @returns Whether all conditions are satisfied
267
- */
268
- export function evaluateConditions(
269
- conditions: WorkflowCondition[],
270
- context: WorkflowContext,
271
- ): boolean {
272
- if (conditions.length === 0) return true;
273
-
274
- return conditions.every((condition) => {
275
- const fieldValue = String(context[condition.field] ?? "");
276
-
277
- switch (condition.operator) {
278
- case "equals":
279
- return fieldValue === String(condition.value);
280
- case "not_equals":
281
- return fieldValue !== String(condition.value);
282
- case "contains":
283
- return fieldValue
284
- .toLowerCase()
285
- .includes(String(condition.value).toLowerCase());
286
- case "in": {
287
- const values = Array.isArray(condition.value)
288
- ? condition.value
289
- : [condition.value];
290
- return values.includes(fieldValue);
291
- }
292
- default:
293
- return false;
294
- }
295
- });
296
- }
297
-
298
- // ---------------------------------------------------------------------------
299
- // Workflow Validation
300
- // ---------------------------------------------------------------------------
301
-
302
- /** Valid triggers for workflows */
303
- const VALID_TRIGGERS: WorkflowTrigger[] = [
304
- "booking_created",
305
- "booking_confirmed",
306
- "booking_cancelled",
307
- "booking_rescheduled",
308
- "before_event",
309
- "after_event",
310
- "payment_received",
311
- "payment_failed",
312
- "no_show_confirmed",
313
- "form_submitted",
314
- ];
315
-
316
- /** Valid action types */
317
- const VALID_ACTION_TYPES: WorkflowActionType[] = [
318
- "send_email",
319
- "send_sms",
320
- "fire_webhook",
321
- "update_status",
322
- "create_calendar_event",
323
- ];
324
-
325
- /**
326
- * Validate a workflow definition.
327
- *
328
- * @param workflow - The workflow to validate
329
- * @throws {WorkflowValidationError} If the workflow is invalid
330
- */
331
- export function validateWorkflow(workflow: WorkflowDefinition): void {
332
- if (!workflow.name || workflow.name.trim().length === 0) {
333
- throw new WorkflowValidationError("Workflow name is required");
334
- }
335
-
336
- if (!VALID_TRIGGERS.includes(workflow.trigger)) {
337
- throw new WorkflowValidationError(
338
- `Invalid trigger: "${workflow.trigger}". Must be one of: ${VALID_TRIGGERS.join(", ")}`,
339
- );
340
- }
341
-
342
- if (!Array.isArray(workflow.actions) || workflow.actions.length === 0) {
343
- throw new WorkflowValidationError(
344
- "Workflow must have at least one action",
345
- );
346
- }
347
-
348
- for (const action of workflow.actions) {
349
- if (!VALID_ACTION_TYPES.includes(action.type)) {
350
- throw new WorkflowValidationError(
351
- `Invalid action type: "${action.type}"`,
352
- );
353
- }
354
-
355
- validateAction(action);
356
- }
357
-
358
- for (const condition of workflow.conditions) {
359
- if (!condition.field || condition.field.trim().length === 0) {
360
- throw new WorkflowValidationError("Condition field is required");
361
- }
362
-
363
- if (!["equals", "not_equals", "contains", "in"].includes(condition.operator)) {
364
- throw new WorkflowValidationError(
365
- `Invalid condition operator: "${condition.operator}"`,
366
- );
367
- }
368
- }
369
- }
370
-
371
- function validateAction(action: WorkflowAction): void {
372
- switch (action.type) {
373
- case "send_email":
374
- if (!action.to) {
375
- throw new WorkflowValidationError("Email action requires 'to' field");
376
- }
377
- if (!action.subject) {
378
- throw new WorkflowValidationError(
379
- "Email action requires 'subject' field",
380
- );
381
- }
382
- if (!action.body) {
383
- throw new WorkflowValidationError("Email action requires 'body' field");
384
- }
385
- break;
386
-
387
- case "send_sms":
388
- if (!action.to) {
389
- throw new WorkflowValidationError("SMS action requires 'to' field");
390
- }
391
- if (!action.body) {
392
- throw new WorkflowValidationError("SMS action requires 'body' field");
393
- }
394
- break;
395
-
396
- case "fire_webhook":
397
- if (!action.url) {
398
- throw new WorkflowValidationError(
399
- "Webhook action requires 'url' field",
400
- );
401
- }
402
- break;
403
-
404
- case "update_status":
405
- if (!action.status) {
406
- throw new WorkflowValidationError(
407
- "Status update action requires 'status' field",
408
- );
409
- }
410
- break;
411
-
412
- case "create_calendar_event":
413
- // No required fields
414
- break;
415
- }
416
- }
417
-
418
- // ---------------------------------------------------------------------------
419
- // Workflow Matching
420
- // ---------------------------------------------------------------------------
421
-
422
- /**
423
- * Find all active workflows that match a given trigger and context.
424
- *
425
- * @param workflows - All available workflows
426
- * @param trigger - The trigger event that occurred
427
- * @param context - The workflow context data
428
- * @returns Workflows that should be executed
429
- */
430
- export function matchWorkflows(
431
- workflows: WorkflowDefinition[],
432
- trigger: WorkflowTrigger,
433
- context: WorkflowContext,
434
- ): WorkflowDefinition[] {
435
- return workflows.filter(
436
- (w) =>
437
- w.isActive &&
438
- w.trigger === trigger &&
439
- evaluateConditions(w.conditions, context),
440
- );
441
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src"
6
- },
7
- "include": ["src/**/*.ts"],
8
- "exclude": ["node_modules", "dist"]
9
- }
package/vitest.config.ts DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- },
7
- });