@infuro/cms-core 1.0.12 → 1.0.15

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.
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { C as CompanyDetails, T as TemplateContext, E as EmailTemplateResult, a as EmailTemplateName, O as OrderPlacedLineItem, S as StorageService } from './index-C4Yl7js9.js';
2
- export { A as AnalyticsHandlerConfig, b as AuthHandlersConfig, B as BlogBySlugConfig, c as ChangePasswordConfig, d as CmsApiHandlerConfig, e as CmsGetter, f as CrudHandlerOptions, D as DashboardStatsConfig, g as EntityMap, F as ForgotPasswordConfig, h as FormBySlugConfig, G as GetPublicSettingsGroupConfig, I as InviteAcceptConfig, i as SetPasswordConfig, j as SettingsApiConfig, k as SocialLinkItem, l as StorefrontApiConfig, U as UploadHandlerConfig, m as UserAuthApiConfig, n as UserAvatarConfig, o as UserProfileConfig, p as UsersApiConfig, q as createAnalyticsHandlers, r as createBlogBySlugHandler, s as createChangePasswordHandler, t as createCmsApiHandler, u as createCrudByIdHandler, v as createCrudHandler, w as createDashboardStatsHandler, x as createForgotPasswordHandler, y as createFormBySlugHandler, z as createInviteAcceptHandler, H as createSetPasswordHandler, J as createSettingsApiHandlers, K as createStorefrontApiHandler, L as createUploadHandler, M as createUserAuthApiRouter, N as createUserAvatarHandler, P as createUserProfileHandler, Q as createUsersApiHandlers, R as getCompanyDetailsFromSettings, V as getPublicSettingsGroup, W as mergeEmailLayoutCompanyDetails } from './index-C4Yl7js9.js';
1
+ import { C as CompanyDetails, T as TemplateContext, E as EmailTemplateResult, a as EmailTemplateName, O as OrderPlacedLineItem, S as StorageService, b as EntityMap$1 } from './index-BiagwMjV.js';
2
+ export { A as AnalyticsHandlerConfig, c as AuthHandlersConfig, B as BlogBySlugConfig, d as ChangePasswordConfig, e as CmsApiHandlerConfig, f as CmsGetter, g as CrudHandlerOptions, D as DashboardStatsConfig, F as ForgotPasswordConfig, h as FormBySlugConfig, G as GetPublicSettingsGroupConfig, i as GetPublicSettingsGroupDataSource, I as InviteAcceptConfig, j as SetPasswordConfig, k as SettingsApiConfig, l as SocialLinkItem, m as StorefrontApiConfig, n as StorefrontOtpFlags, U as UploadHandlerConfig, o as UserAuthApiConfig, p as UserAvatarConfig, q as UserProfileConfig, r as UsersApiConfig, s as createAnalyticsHandlers, t as createBlogBySlugHandler, u as createChangePasswordHandler, v as createCmsApiHandler, w as createCrudByIdHandler, x as createCrudHandler, y as createDashboardStatsHandler, z as createForgotPasswordHandler, H as createFormBySlugHandler, J as createInviteAcceptHandler, K as createSetPasswordHandler, L as createSettingsApiHandlers, M as createStorefrontApiHandler, N as createUploadHandler, P as createUserAuthApiRouter, Q as createUserAvatarHandler, R as createUserProfileHandler, V as createUsersApiHandlers, W as getCompanyDetailsFromSettings, X as getPublicSettingsGroup, Y as mergeEmailLayoutCompanyDetails } from './index-BiagwMjV.js';
3
3
  import { ClassValue } from 'clsx';
4
4
  import * as typeorm from 'typeorm';
5
5
  import { DataSource, EntityTarget, ObjectLiteral } from 'typeorm';
6
+ import { Metadata } from 'next';
6
7
  export { A as ADMIN_GROUP_NAME, a as AuthHelpers, E as EntityCrudAction, b as EntityPermissionFlags, G as GetSession, O as OPEN_ENDPOINTS, P as PERMISSION_REQUIRED_ENDPOINTS, R as RBAC_ADMIN_ONLY_ENTITIES, S as SessionUser, c as canManageRoles, d as createAuthHelpers, g as getPermissionableEntityKeys, e as getRequiredPermission, h as hasEntityPermission, i as isOpenEndpoint, f as isPublicMethod, j as isSuperAdminGroupName, p as permissionRowsToRecord, s as sessionHasEntityAccess } from './helpers-dlrF_49e.js';
7
- export { CmsMiddlewareConfig, NextAuthOptionsConfig, NextAuthUser, createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, seedAdministratorPermissions } from './auth.js';
8
+ export { AuthorizeOtpInput, CmsMiddlewareConfig, NextAuthOptionsConfig, NextAuthUser, createCmsMiddleware, defaultPublicApiMethods, getNextAuthOptions, seedAdministratorPermissions } from './auth.js';
8
9
  export { A as AdminNavItem, D as DEFAULT_ADMIN_NAV } from './config-DJ5CmQvS.js';
9
10
  import 'next-auth';
10
11
 
@@ -38,27 +39,6 @@ interface CmsApp {
38
39
  }
39
40
  declare function createCmsApp(options: CreateCmsAppOptions): Promise<CmsApp>;
40
41
 
41
- interface ERPAuthToken {
42
- access_token: string;
43
- token_type: string;
44
- expires_in: number;
45
- expires_at?: number;
46
- }
47
- declare class ERPAuthService {
48
- private baseUrl;
49
- private credentials;
50
- private token;
51
- constructor(config: {
52
- baseUrl?: string;
53
- clientId: string;
54
- clientSecret: string;
55
- tenantId: string;
56
- });
57
- authenticate(): Promise<ERPAuthToken>;
58
- getValidToken(): Promise<string>;
59
- makeAuthenticatedRequest(url: string, options?: RequestInit): Promise<Response>;
60
- }
61
-
62
42
  interface ContactFormData {
63
43
  firstName: string;
64
44
  lastName: string;
@@ -69,22 +49,70 @@ interface ContactFormData {
69
49
  }
70
50
  interface ERPSubmissionResult {
71
51
  success: boolean;
72
- contactId: string;
73
- opportunityId?: string;
74
52
  error?: string;
53
+ status?: number;
54
+ }
55
+ type PipelineNames = {
56
+ pipelineName: string;
57
+ pipelineStageName: string;
58
+ };
59
+ /** Payload for ERP `create-contact` (§7c) — CRM contact upsert, no lead/opportunity row. */
60
+ interface ErpCreateContactPayload {
61
+ email: string;
62
+ firstName: string;
63
+ lastName: string;
64
+ phone?: string;
65
+ companyName?: string;
66
+ type?: string;
67
+ notes?: string;
68
+ tags?: string[];
75
69
  }
76
70
  declare class ERPSubmissionService {
77
- private baseUrl;
78
- private pipelineId;
79
- private pipelineStageId;
80
- private auth;
71
+ private webhookUrl;
72
+ private webhookJwt;
73
+ private getPipelineNames?;
81
74
  constructor(config: {
82
- baseUrl?: string;
83
- pipelineId: string;
84
- pipelineStageId: string;
85
- auth: ERPAuthService;
75
+ webhookUrl: string;
76
+ webhookJwt: string;
77
+ getPipelineNames?: () => Promise<PipelineNames>;
86
78
  });
79
+ /** Replace trailing path segment of webhook URL with `action` (e.g. `get-order-status`, `invoice-pdf`). */
80
+ resolveErpActionUrl(action: string): string;
81
+ /**
82
+ * Synchronous ERP read (§7d): POST JSON to `.../generic-webhook/<action>`.
83
+ * Body is sent as-is (or wrap with envelope if ERP expects `event_type` — caller may pass envelope).
84
+ */
85
+ postErpReadAction(action: string, body: Record<string, unknown>): Promise<{
86
+ ok: boolean;
87
+ status?: number;
88
+ json?: unknown;
89
+ error?: string;
90
+ }>;
91
+ /** GET PDF bytes for `invoice-pdf` action (§7d). */
92
+ fetchInvoicePdf(invoiceId: string): Promise<{
93
+ ok: boolean;
94
+ buffer?: ArrayBuffer;
95
+ contentType?: string;
96
+ error?: string;
97
+ }>;
98
+ /** `product.updated` envelope → `update-product` (§7e). */
99
+ submitProductUpsert(productData: Record<string, unknown>): Promise<ERPSubmissionResult>;
100
+ private postWebhookJson;
101
+ private resolvePipelineNames;
102
+ /** Generic webhook POST (e.g. order.created envelope). */
103
+ postWebhook(body: unknown): Promise<ERPSubmissionResult>;
104
+ /** Shared CRM inbound fields (§7a/b in ERP-plugin.md). */
105
+ private buildCrmInboundData;
106
+ /** `lead.created` → ERP create-lead (CRM leads table). */
87
107
  submitContact(formData: ContactFormData): Promise<ERPSubmissionResult>;
108
+ /** `create-contact` → ERP ContactService upsert (§7c). */
109
+ submitCreateContact(payload: ErpCreateContactPayload): Promise<ERPSubmissionResult>;
110
+ /** `form.submitted` → ERP create-opportunity (pipeline). */
111
+ submitFormOpportunity(formData: ContactFormData): Promise<ERPSubmissionResult>;
112
+ /**
113
+ * Order payload aligned with ERP generic webhook create-order (§6).
114
+ */
115
+ submitOrder(orderDto: Record<string, unknown>): Promise<ERPSubmissionResult>;
88
116
  extractContactData(formData: Record<string, unknown>, formFields: {
89
117
  id: string | number;
90
118
  type: string;
@@ -92,16 +120,71 @@ declare class ERPSubmissionService {
92
120
  }[]): ContactFormData | null;
93
121
  }
94
122
 
123
+ interface CmsAppLike$2 {
124
+ getPlugin(name: string): unknown;
125
+ }
126
+ type ErpJobPayload = {
127
+ kind: 'lead';
128
+ contact: ContactFormData;
129
+ } | {
130
+ kind: 'formOpportunity';
131
+ contact: ContactFormData;
132
+ } | {
133
+ kind: 'createContact';
134
+ contact: ErpCreateContactPayload;
135
+ } | {
136
+ kind: 'order';
137
+ order: Record<string, unknown>;
138
+ } | {
139
+ kind: 'productUpsert';
140
+ product: Record<string, unknown>;
141
+ };
142
+ declare function queueErp(cms: CmsAppLike$2, payload: ErpJobPayload): Promise<void>;
143
+ declare function registerErpQueueProcessor(cms: CmsAppLike$2): void;
144
+
145
+ /**
146
+ * Duck-typed DataSource so host apps (e.g. Next) can pass their own TypeORM instance
147
+ * without duplicate-package type errors vs core’s node_modules/typeorm.
148
+ */
149
+ type ErpPaidOrderDataSource = {
150
+ getRepository(entity: unknown): {
151
+ find(options?: object): Promise<unknown[]>;
152
+ findOne(options?: object): Promise<unknown | null>;
153
+ };
154
+ };
155
+ /**
156
+ * Entity map with `configs` and `orders` entries (e.g. ENTITY_MAP).
157
+ * Typed as `Record<string, unknown>` so apps that cast their map to `Record<string, EntityTarget<…>>` still pass.
158
+ */
159
+ type ErpPaidOrderEntityMap = Record<string, unknown>;
160
+ /**
161
+ * Enqueues ERP `order.created` only when the order has at least one **completed** payment.
162
+ * Include `payments` in the payload.
163
+ */
164
+ declare function queueErpPaidOrderForOrderId(cms: CmsAppLike$2, dataSource: ErpPaidOrderDataSource, entityMap: ErpPaidOrderEntityMap, orderId: number): Promise<void>;
165
+
166
+ interface ErpContactSyncInput {
167
+ name: string;
168
+ email: string;
169
+ phone?: string | null;
170
+ type?: string | null;
171
+ company?: string | null;
172
+ notes?: string | null;
173
+ /** Passed to ERP as `tags` when non-empty (§7c). */
174
+ tags?: string[];
175
+ }
176
+ /**
177
+ * When ERP is enabled and plugin loaded, enqueue `create-contact` (non-fatal on failure).
178
+ * Used for admin CRUD and storefront contact paths — not form submissions (those use lead/opportunity).
179
+ */
180
+ declare function queueErpCreateContactIfEnabled(cms: CmsAppLike$2, dataSource: ErpPaidOrderDataSource, entityMap: ErpPaidOrderEntityMap, input: ErpContactSyncInput): Promise<void>;
181
+
95
182
  interface ERPPluginConfig {
96
- baseUrl?: string;
97
- clientId: string;
98
- clientSecret: string;
99
- tenantId: string;
100
- pipelineId?: string;
101
- pipelineStageId?: string;
183
+ webhookUrl: string;
184
+ webhookJwt: string;
185
+ getPipelineNames?: () => Promise<PipelineNames>;
102
186
  }
103
187
  interface ERPPluginInstance {
104
- auth: ERPAuthService;
105
188
  submission: ERPSubmissionService;
106
189
  }
107
190
  declare function erpPlugin(config: ERPPluginConfig): CmsPlugin<ERPPluginInstance>;
@@ -194,7 +277,7 @@ declare function renderLayout(options: {
194
277
  companyDetails: CompanyDetails;
195
278
  }): string;
196
279
 
197
- interface CmsAppLike {
280
+ interface CmsAppLike$1 {
198
281
  getPlugin(name: string): unknown;
199
282
  }
200
283
  /** Context for template rendering: at least companyDetails; template-specific fields (formName, etc.) are allowed. */
@@ -208,11 +291,13 @@ interface EmailJobPayload {
208
291
  html?: string;
209
292
  text?: string;
210
293
  }
211
- declare function registerEmailQueueProcessor(cms: CmsAppLike): void;
212
- declare function queueEmail(cms: CmsAppLike, payload: EmailJobPayload): Promise<void>;
294
+ declare function registerEmailQueueProcessor(cms: CmsAppLike$1): void;
295
+ declare function queueEmail(cms: CmsAppLike$1, payload: EmailJobPayload): Promise<void>;
213
296
  interface OrderPlacedEmailPayload {
214
297
  orderNumber: string;
215
298
  total?: string | number;
299
+ subtotal?: string | number;
300
+ tax?: string | number;
216
301
  currency?: string;
217
302
  customerName?: string;
218
303
  /** Customer receipt; omitted if empty */
@@ -221,9 +306,11 @@ interface OrderPlacedEmailPayload {
221
306
  salesTeamEmails: string[];
222
307
  companyDetails: CompanyDetails;
223
308
  lineItems: OrderPlacedLineItem[];
309
+ billingAddress?: Record<string, string>;
310
+ shippingAddress?: Record<string, string>;
224
311
  }
225
312
  /** Queues one `orderPlaced` email per recipient (customer + each unique sales address; skips sales if same as customer). */
226
- declare function queueOrderPlacedEmails(cms: CmsAppLike, payload: OrderPlacedEmailPayload): Promise<void>;
313
+ declare function queueOrderPlacedEmails(cms: CmsAppLike$1, payload: OrderPlacedEmailPayload): Promise<void>;
227
314
 
228
315
  interface EmailPluginConfig {
229
316
  type: 'AWS' | 'SMTP' | 'GMAIL' | 'SENDGRID';
@@ -291,16 +378,76 @@ interface AnalyticsPluginConfig {
291
378
  }
292
379
  declare function analyticsPlugin(config?: AnalyticsPluginConfig): CmsPlugin<AnalyticsService>;
293
380
 
294
- /** Provider-agnostic SMS interface. Implementations (Twilio, AWS SNS, etc.) can be added later. */
295
- interface SmsServiceInterface {
296
- send(to: string, message: string): Promise<boolean>;
381
+ type MessageTemplateRowLoader = (channel: string, templateKey: string) => Promise<{
382
+ body: string;
383
+ externalTemplateRef: string | null;
384
+ providerMeta: Record<string, unknown> | null;
385
+ enabled: boolean;
386
+ } | null>;
387
+ /** Which transport to use for outbound SMS. */
388
+ type SmsProviderId = 'msg91' | 'twilio' | 'webhook';
389
+ /** Stored in admin `smsProvider` or env `SMS_PROVIDER`. `auto` picks first with credentials: msg91 → twilio → webhook. */
390
+ type SmsProviderChoice = SmsProviderId | 'auto';
391
+ /** MSG91: use DLT Flow API vs legacy sendhttp (non‑India / legacy only). */
392
+ type Msg91ApiMode = 'flow' | 'sendhttp' | 'auto';
393
+ interface SmsServiceConfig {
394
+ provider?: SmsProviderChoice;
395
+ /** Admin settings key `smsProvider` (msg91 | twilio | webhook | auto). */
396
+ smsProvider?: string;
397
+ /** MSG91: auth key */
398
+ msg91AuthKey?: string;
399
+ /** DLT-approved sender ID (required for sendhttp; Flow uses template/header mapped in MSG91 panel). */
400
+ msg91SenderId?: string;
401
+ /** Transactional route; default 4 (sendhttp only). */
402
+ msg91Route?: string;
403
+ /**
404
+ * MSG91 Flow ID / template_id shown in panel after mapping DLT template (India).
405
+ * Required for `flow` mode.
406
+ */
407
+ msg91TemplateId?: string;
408
+ /** `flow` = POST control.msg91.com/api/v5/flow/ (DLT). `sendhttp` = legacy GET API. `auto` = flow if template id set, else sendhttp. */
409
+ msg91ApiMode?: Msg91ApiMode;
410
+ /** Recipient JSON key for OTP value in Flow API (must match MSG91 template variables). Default var1. */
411
+ msg91OtpVarKey?: string;
412
+ /** Flow API short_url flag; default 0. */
413
+ msg91FlowShortUrl?: string;
414
+ accountSid?: string;
415
+ authToken?: string;
416
+ from?: string;
417
+ webhookUrl?: string;
418
+ webhookSecret?: string;
297
419
  }
298
- interface SmsPluginConfig {
299
- provider?: string;
300
- [key: string]: string | undefined;
420
+
421
+ interface CmsAppLike {
422
+ getPlugin(name: string): unknown;
301
423
  }
302
- /** Stub SMS plugin - no implementation yet. Register and implement in app or extend core later. */
303
- declare function smsPlugin(_config?: SmsPluginConfig): CmsPlugin<SmsServiceInterface | null>;
424
+ interface SmsJobPayload {
425
+ to: string;
426
+ /** Legacy / Twilio / sendhttp plain text. */
427
+ body?: string;
428
+ templateKey?: string;
429
+ variables?: Record<string, string>;
430
+ otpCode?: string;
431
+ }
432
+ declare function registerSmsQueueProcessor(cms: CmsAppLike): void;
433
+ declare function queueSms(cms: CmsAppLike, payload: SmsJobPayload): Promise<void>;
434
+
435
+ interface SmsPluginConfig extends Partial<SmsServiceConfig> {
436
+ /** Load admin settings group `sms` (enabled, smsProvider). Secrets typically stay in env. */
437
+ getSmsSettings?: () => Promise<Record<string, string>>;
438
+ /** Resolve `message_templates` rows for SMS (required for `templateKey` sends). */
439
+ getMessageTemplateRow?: MessageTemplateRowLoader;
440
+ }
441
+ interface SmsServiceInterface {
442
+ send(opts: {
443
+ to: string;
444
+ body?: string;
445
+ templateKey?: string;
446
+ otpCode?: string;
447
+ variables?: Record<string, string>;
448
+ }): Promise<boolean>;
449
+ }
450
+ declare function smsPlugin(config?: SmsPluginConfig): CmsPlugin<SmsServiceInterface | null>;
304
451
 
305
452
  interface PaymentIntent {
306
453
  id: string;
@@ -419,6 +566,43 @@ interface QueuePluginConfig {
419
566
  }
420
567
  declare function queuePlugin(config?: QueuePluginConfig): CmsPlugin<QueueService>;
421
568
 
569
+ type CaptchaProviderId = 'turnstile' | 'recaptcha_v3';
570
+ interface CaptchaPublicConfig {
571
+ enabled: boolean;
572
+ activeProvider: CaptchaProviderId | null;
573
+ siteKey: string | null;
574
+ availableProviders: Array<{
575
+ id: CaptchaProviderId;
576
+ siteKey: string;
577
+ }>;
578
+ multipleProviders: boolean;
579
+ }
580
+ type CaptchaVerifyResult = {
581
+ ok: true;
582
+ } | {
583
+ ok: false;
584
+ status: number;
585
+ message: string;
586
+ };
587
+ declare function buildCaptchaPublicConfig(config: Record<string, string>): CaptchaPublicConfig;
588
+ declare class CaptchaService {
589
+ private readonly env;
590
+ constructor(env: Record<string, string>);
591
+ isEnforced(): boolean;
592
+ getPublicConfig(): CaptchaPublicConfig;
593
+ verify(body: Record<string, unknown>, req: Request): Promise<CaptchaVerifyResult>;
594
+ }
595
+
596
+ declare function captchaPlugin(): CmsPlugin<CaptchaService | void>;
597
+
598
+ type JsonResponse = (body: unknown, init?: {
599
+ status?: number;
600
+ headers?: HeadersInit;
601
+ }) => Response;
602
+ declare function assertCaptchaOk(getCms: (() => Promise<{
603
+ getPlugin: (name: string) => unknown;
604
+ }>) | undefined, body: Record<string, unknown>, req: Request, json: JsonResponse): Promise<Response | null>;
605
+
422
606
  declare function cn(...inputs: ClassValue[]): string;
423
607
  declare function generateSlug(title: string): string;
424
608
  declare function validateSlug(slug: string): boolean;
@@ -427,6 +611,42 @@ declare function formatDateTime(date: Date | string): string;
427
611
  declare function formatDateOnly(date: Date | string): string;
428
612
  declare function truncateText(text: string, maxLength: number): string;
429
613
 
614
+ /** Minimal SEO row shape (DB or API). */
615
+ type SeoLike = {
616
+ title?: string | null;
617
+ description?: string | null;
618
+ keywords?: string | null;
619
+ ogTitle?: string | null;
620
+ ogDescription?: string | null;
621
+ ogImage?: string | null;
622
+ };
623
+ type SeoMetadataOverrides = {
624
+ title?: string;
625
+ description?: string;
626
+ keywords?: string;
627
+ ogTitle?: string;
628
+ ogDescription?: string;
629
+ ogImage?: string;
630
+ };
631
+ /** Per-field: join row wins over slug row. */
632
+ declare function mergeSeoBySlug(join: SeoLike | null | undefined, bySlug: SeoLike | null): SeoLike | null;
633
+ declare function fetchSeoBySlug(dataSource: DataSource, entityMap: EntityMap$1, slug: string): Promise<SeoLike | null>;
634
+ /**
635
+ * Build Next.js Metadata from merged SEO + optional layers.
636
+ * `overrides` win over SEO; `fallbacks` apply when SEO (and overrides) omit a field.
637
+ * Returns a partial object; omit fields so the root layout defaults apply.
638
+ */
639
+ declare function resolvePublicMetadata(args: {
640
+ dataSource: DataSource;
641
+ entityMap: EntityMap$1;
642
+ slug: string;
643
+ seoFromJoin?: SeoLike | null;
644
+ overrides?: SeoMetadataOverrides;
645
+ fallbacks?: SeoMetadataOverrides;
646
+ canonicalPath?: string;
647
+ metadataBase?: URL;
648
+ }): Promise<Metadata>;
649
+
430
650
  /** Links an unclaimed contact (same email, no userId) to the new user after signup or invite. */
431
651
  declare function linkUnclaimedContactToUser(dataSource: DataSource, contactsEntity: EntityTarget<ObjectLiteral>, userId: number, email: string): Promise<void>;
432
652
 
@@ -437,6 +657,41 @@ declare function serializeEmailRecipients(emails: string[]): string;
437
657
  /** Join for SMTP `to` header (multiple recipients). */
438
658
  declare function joinRecipientsForSend(emails: string[]): string | null;
439
659
 
660
+ type OtpPurpose = 'login' | 'verify_email' | 'verify_phone';
661
+ type OtpChannel = 'email' | 'sms';
662
+ declare function hashOtpCode(code: string, purpose: string, identifier: string, pepper?: string): string;
663
+ declare function verifyOtpCodeHash(code: string, storedHash: string, purpose: string, identifier: string, pepper?: string): boolean;
664
+ declare function generateNumericOtp(length?: number): string;
665
+ /** Normalize to E.164-like +digits */
666
+ declare function normalizePhoneE164(raw: string, defaultCountryCode?: string): string | null;
667
+ type EntityMap = Record<string, EntityTarget<ObjectLiteral>>;
668
+ declare function countRecentOtpSends(dataSource: DataSource, entityMap: EntityMap, purpose: OtpPurpose, identifier: string, since: Date): Promise<number>;
669
+ declare function createOtpChallenge(dataSource: DataSource, entityMap: EntityMap, input: {
670
+ purpose: OtpPurpose;
671
+ channel: OtpChannel;
672
+ identifier: string;
673
+ code: string;
674
+ pepper?: string;
675
+ }): Promise<{
676
+ ok: true;
677
+ } | {
678
+ ok: false;
679
+ error: string;
680
+ status: number;
681
+ }>;
682
+ declare function verifyAndConsumeOtpChallenge(dataSource: DataSource, entityMap: EntityMap, input: {
683
+ purpose: OtpPurpose;
684
+ identifier: string;
685
+ code: string;
686
+ pepper?: string;
687
+ }): Promise<{
688
+ ok: true;
689
+ } | {
690
+ ok: false;
691
+ error: string;
692
+ status: number;
693
+ }>;
694
+
440
695
  declare class Permission {
441
696
  id: number;
442
697
  groupId: number;
@@ -473,6 +728,10 @@ declare class User {
473
728
  id: number;
474
729
  name: string;
475
730
  email: string;
731
+ /** E.164 when set; unique among non-null values (partial index in DB). */
732
+ phone: string | null;
733
+ phoneVerifiedAt: Date | null;
734
+ emailVerifiedAt: Date | null;
476
735
  password: string | null;
477
736
  blocked: boolean;
478
737
  adminAccess: boolean;
@@ -487,6 +746,18 @@ declare class User {
487
746
  group: UserGroup | null;
488
747
  }
489
748
 
749
+ declare class OtpChallenge {
750
+ id: number;
751
+ purpose: string;
752
+ channel: string;
753
+ identifier: string;
754
+ codeHash: string;
755
+ expiresAt: Date;
756
+ attempts: number;
757
+ consumedAt: Date | null;
758
+ createdAt: Date;
759
+ }
760
+
490
761
  declare class PasswordResetToken {
491
762
  id: number;
492
763
  email: string;
@@ -785,6 +1056,9 @@ declare class Product {
785
1056
  categoryId: number | null;
786
1057
  sku: string | null;
787
1058
  hsn: string | null;
1059
+ /** Unit of measure, e.g. pcs, kg, hrs */
1060
+ uom: string | null;
1061
+ type: 'product' | 'service';
788
1062
  slug: string | null;
789
1063
  name: string | null;
790
1064
  price: number;
@@ -817,6 +1091,13 @@ declare class OrderItem {
817
1091
  unitPrice: number;
818
1092
  tax: number;
819
1093
  total: number;
1094
+ /** Snapshot at order time */
1095
+ hsn: string | null;
1096
+ uom: string | null;
1097
+ productType: string | null;
1098
+ taxRate: number | null;
1099
+ /** Tax slug snapshot (from taxes.slug) */
1100
+ taxCode: string | null;
820
1101
  metadata: Record<string, unknown> | null;
821
1102
  createdAt: Date;
822
1103
  updatedAt: Date;
@@ -846,9 +1127,12 @@ declare class Payment {
846
1127
  contact: Contact | null;
847
1128
  }
848
1129
 
1130
+ type OrderKind = 'sale' | 'return' | 'replacement';
849
1131
  declare class Order {
850
1132
  id: number;
851
1133
  orderNumber: string;
1134
+ orderKind: OrderKind;
1135
+ parentOrderId: number | null;
852
1136
  contactId: number;
853
1137
  billingAddressId: number | null;
854
1138
  shippingAddressId: number | null;
@@ -866,6 +1150,8 @@ declare class Order {
866
1150
  createdBy: number | null;
867
1151
  updatedBy: number | null;
868
1152
  deletedBy: number | null;
1153
+ parentOrder: Order | null;
1154
+ children: Order[];
869
1155
  contact: Contact;
870
1156
  billingAddress: Address | null;
871
1157
  shippingAddress: Address | null;
@@ -932,6 +1218,25 @@ declare class Config {
932
1218
  deletedBy: number | null;
933
1219
  }
934
1220
 
1221
+ declare class MessageTemplate {
1222
+ id: number;
1223
+ channel: string;
1224
+ templateKey: string;
1225
+ name: string | null;
1226
+ subject: string | null;
1227
+ body: string;
1228
+ externalTemplateRef: string | null;
1229
+ providerMeta: Record<string, unknown> | null;
1230
+ enabled: boolean;
1231
+ createdAt: Date;
1232
+ updatedAt: Date;
1233
+ deletedAt: Date | null;
1234
+ deleted: boolean;
1235
+ createdBy: number | null;
1236
+ updatedBy: number | null;
1237
+ deletedBy: number | null;
1238
+ }
1239
+
935
1240
  declare class Media {
936
1241
  id: number;
937
1242
  filename: string;
@@ -1034,4 +1339,4 @@ declare class Wishlist {
1034
1339
  /** Map API resource segment (e.g. "blogs", "form_submissions") to entity. Used by CRUD handler. */
1035
1340
  declare const CMS_ENTITY_MAP: Record<string, EntityTarget<typeorm.ObjectLiteral>>;
1036
1341
 
1037
- export { type AnalyticsPluginConfig, Attribute, Blog, Brand, CMS_ENTITY_MAP, type CachePluginConfig, type CacheService, Cart, CartItem, Category, ChatConversation, ChatMessage, type CmsApp, type CmsPlugin, Collection, Comment, CompanyDetails, Config, Contact, type CreateCmsAppOptions, type ERPPluginConfig, type ERPPluginInstance, type EmailData, type EmailJobPayload, type EmailPluginConfig, EmailService, type EmailServiceInterface, EmailTemplateName, EmailTemplateResult, Form, FormField, FormSubmission, KnowledgeBaseChunk, KnowledgeBaseDocument, type LlmChatOptions, type LlmMessage, type LlmPluginConfig, LlmService, type LlmServiceInterface, type LocalStoragePluginConfig, type Logger, Media, Order, OrderItem, type OrderPlacedEmailPayload, OrderPlacedLineItem, Page, PasswordResetToken, Payment, type PaymentIntent, type PaymentPluginConfig, type PaymentServiceInterface, Permission, type PluginContext, Product, ProductAttribute, ProductCategory, ProductTax, type QueuePluginConfig, type QueueService, type RenderEmailOptions, type RenderedEmail, type S3StoragePluginConfig, Seo, StorageService, Tag, Tax, TemplateContext, User, UserGroup, Wishlist, WishlistItem, analyticsPlugin, cachePlugin, cn, createCmsApp, emailPlugin, emailTemplates, erpPlugin, formatDate, formatDateOnly, formatDateTime, generateSlug, joinRecipientsForSend, linkUnclaimedContactToUser, llmPlugin, localStoragePlugin, parseEmailRecipientsFromConfig, paymentPlugin, queueEmail, queueOrderPlacedEmails, queuePlugin, registerEmailQueueProcessor, renderEmail, renderLayout, s3StoragePlugin, serializeEmailRecipients, smsPlugin, truncateText, validateSlug };
1342
+ export { type AnalyticsPluginConfig, Attribute, Blog, Brand, CMS_ENTITY_MAP, type CachePluginConfig, type CacheService, type CaptchaProviderId, type CaptchaPublicConfig, CaptchaService, type CaptchaVerifyResult, Cart, CartItem, Category, ChatConversation, ChatMessage, type CmsApp, type CmsPlugin, Collection, Comment, CompanyDetails, Config, Contact, type CreateCmsAppOptions, type ERPPluginConfig, type ERPPluginInstance, ERPSubmissionService, type EmailData, type EmailJobPayload, type EmailPluginConfig, EmailService, type EmailServiceInterface, EmailTemplateName, EmailTemplateResult, EntityMap$1 as EntityMap, type ErpContactSyncInput, type ErpCreateContactPayload, type ErpJobPayload, type ErpPaidOrderDataSource, type ErpPaidOrderEntityMap, Form, FormField, FormSubmission, KnowledgeBaseChunk, KnowledgeBaseDocument, type LlmChatOptions, type LlmMessage, type LlmPluginConfig, LlmService, type LlmServiceInterface, type LocalStoragePluginConfig, type Logger, Media, MessageTemplate, Order, OrderItem, type OrderPlacedEmailPayload, OrderPlacedLineItem, OtpChallenge, type OtpChannel, type OtpPurpose, Page, PasswordResetToken, Payment, type PaymentIntent, type PaymentPluginConfig, type PaymentServiceInterface, Permission, type PipelineNames, type PluginContext, Product, ProductAttribute, ProductCategory, ProductTax, type QueuePluginConfig, type QueueService, type RenderEmailOptions, type RenderedEmail, type S3StoragePluginConfig, Seo, type SeoLike, type SeoMetadataOverrides, type SmsJobPayload, type SmsPluginConfig, type SmsProviderChoice, type SmsProviderId, type SmsServiceConfig, type SmsServiceInterface, StorageService, Tag, Tax, TemplateContext, User, UserGroup, Wishlist, WishlistItem, analyticsPlugin, assertCaptchaOk, buildCaptchaPublicConfig, cachePlugin, captchaPlugin, cn, countRecentOtpSends, createCmsApp, createOtpChallenge, emailPlugin, emailTemplates, erpPlugin, fetchSeoBySlug, formatDate, formatDateOnly, formatDateTime, generateNumericOtp, generateSlug, hashOtpCode, joinRecipientsForSend, linkUnclaimedContactToUser, llmPlugin, localStoragePlugin, mergeSeoBySlug, normalizePhoneE164, parseEmailRecipientsFromConfig, paymentPlugin, queueEmail, queueErp, queueErpCreateContactIfEnabled, queueErpPaidOrderForOrderId, queueOrderPlacedEmails, queuePlugin, queueSms, registerEmailQueueProcessor, registerErpQueueProcessor, registerSmsQueueProcessor, renderEmail, renderLayout, resolvePublicMetadata, s3StoragePlugin, serializeEmailRecipients, smsPlugin, truncateText, validateSlug, verifyAndConsumeOtpChallenge, verifyOtpCodeHash };