@dalmore/api-contracts 1.0.9 → 1.0.10

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 (147) hide show
  1. package/README.md +48 -39
  2. package/common/helpers/index.ts +59 -0
  3. package/{src/common → common}/types/account-contact.types.ts +2 -1
  4. package/{src/common → common}/types/account-manager.types.ts +3 -7
  5. package/common/types/account-setting.types.ts +65 -0
  6. package/{src/common → common}/types/account.types.ts +1 -0
  7. package/{src/common → common}/types/activity.types.ts +1 -1
  8. package/{src/common → common}/types/api-key-logs.types.ts +1 -1
  9. package/{src/common → common}/types/asset.types.ts +14 -14
  10. package/{src/common → common}/types/auth.types.ts +7 -18
  11. package/{src/common → common}/types/bonus-tier.types.ts +33 -0
  12. package/{src/common → common}/types/cart.types.ts +4 -1
  13. package/{src/common → common}/types/common.types.ts +39 -7
  14. package/{src/common → common}/types/contact-us.types.ts +6 -2
  15. package/{src/common → common}/types/covered-person.types.ts +2 -1
  16. package/{src/common → common}/types/dashboard.types.ts +2 -9
  17. package/{src/common → common}/types/data-record.types.ts +1 -1
  18. package/{src/common → common}/types/disbursements.types.ts +119 -3
  19. package/{src/common → common}/types/escrow-account.types.ts +3 -3
  20. package/{src/common → common}/types/file.types.ts +20 -4
  21. package/common/types/i-will-do-it-later.types.ts +68 -0
  22. package/{src/common → common}/types/index.ts +2 -0
  23. package/{src/common → common}/types/individuals.types.ts +5 -17
  24. package/{src/common → common}/types/investor-account.types.ts +2 -1
  25. package/{src/common → common}/types/invite.types.ts +27 -1
  26. package/{src/common → common}/types/issuer-offering.types.ts +113 -30
  27. package/{src/common → common}/types/issuer-payment-method.types.ts +41 -0
  28. package/{src/common → common}/types/issuer.types.ts +9 -0
  29. package/{src/common → common}/types/legal-entity.types.ts +3 -2
  30. package/{src/common → common}/types/note.types.ts +1 -1
  31. package/common/types/notification.types.ts +544 -0
  32. package/{src/common → common}/types/offering.types.ts +106 -20
  33. package/{src/common → common}/types/page.types.ts +1 -1
  34. package/{src/common → common}/types/phone.type.ts +1 -1
  35. package/{src/common → common}/types/site-settings.types.ts +2 -1
  36. package/{src/common → common}/types/site.types.ts +2 -9
  37. package/{src/common/types/trade-line-item.type.ts → common/types/trade-line-item.types.ts} +2 -9
  38. package/{src/common → common}/types/trade.types.ts +137 -4
  39. package/{src/common → common}/types/transaction.types.ts +12 -1
  40. package/{src/common → common}/types/trusted-contact.types.ts +7 -7
  41. package/{src/common → common}/types/user.types.ts +17 -33
  42. package/{src/common → common}/types/zip.type.ts +1 -1
  43. package/contracts/clients/cart/index.ts +80 -0
  44. package/{src/contracts → contracts}/clients/files/index.ts +14 -1
  45. package/{src/contracts → contracts}/clients/files-public/index.ts +15 -1
  46. package/{src/contracts → contracts}/clients/index.ts +10 -0
  47. package/contracts/clients/issuer-payment-methods/index.ts +39 -0
  48. package/contracts/clients/payment-methods/index.ts +85 -0
  49. package/contracts/clients/trade-line-items/index.ts +66 -0
  50. package/{src/contracts → contracts}/clients/trades/index.ts +65 -1
  51. package/contracts/clients/transactions/index.ts +37 -0
  52. package/index.ts +10 -0
  53. package/package.json +32 -11
  54. package/index.mjs +0 -10876
  55. package/src/common/types/address.spec.ts +0 -203
  56. package/src/common/types/common.types.spec.ts +0 -336
  57. package/src/common/types/contract-helpers.ts +0 -205
  58. package/src/common/types/files.types.spec.ts +0 -154
  59. package/src/common/types/issuer-payment-method.types.spec.ts +0 -612
  60. package/src/common/types/notification.types.ts +0 -58
  61. package/src/common/types/offering.types.spec.ts +0 -91
  62. package/src/common/types/phone.spec.ts +0 -76
  63. package/src/common/types/site-link.types.spec.ts +0 -134
  64. /package/{src/common → common}/types/account-detail.types.ts +0 -0
  65. /package/{src/common → common}/types/account-integration.types.ts +0 -0
  66. /package/{src/common → common}/types/address.types.ts +0 -0
  67. /package/{src/common → common}/types/aic.types.ts +0 -0
  68. /package/{src/common → common}/types/aml.types.ts +0 -0
  69. /package/{src/common → common}/types/api-keys.types.ts +0 -0
  70. /package/{src/common → common}/types/batch-jobs.types.ts +0 -0
  71. /package/{src/common → common}/types/checklist-items.types.ts +0 -0
  72. /package/{src/common → common}/types/checklist.types.ts +0 -0
  73. /package/{src/common → common}/types/comply-advantage-api.types.ts +0 -0
  74. /package/{src/common → common}/types/comply-advantage.types.ts +0 -0
  75. /package/{src/common → common}/types/countries.types.ts +0 -0
  76. /package/{src/common → common}/types/data-room.types.ts +0 -0
  77. /package/{src/common → common}/types/default-theme-config.types.ts +0 -0
  78. /package/{src/common → common}/types/disbursement-adjustment.types.ts +0 -0
  79. /package/{src/common → common}/types/disbursement-approval-user.types.ts +0 -0
  80. /package/{src/common → common}/types/disbursement-review.types.ts +0 -0
  81. /package/{src/common → common}/types/disbursement-transaction.types.ts +0 -0
  82. /package/{src/common → common}/types/domain-filter.types.ts +0 -0
  83. /package/{src/common → common}/types/email-theme.types.ts +0 -0
  84. /package/{src/common → common}/types/entity.types.ts +0 -0
  85. /package/{src/common → common}/types/error-responses.types.ts +0 -0
  86. /package/{src/common → common}/types/exchange-api-key.types.ts +0 -0
  87. /package/{src/common → common}/types/exchange-import.types.ts +0 -0
  88. /package/{src/common → common}/types/exchange-provider.types.ts +0 -0
  89. /package/{src/common → common}/types/health.types.ts +0 -0
  90. /package/{src/common → common}/types/investorAccountIdSchema.type.ts +0 -0
  91. /package/{src/common → common}/types/investors-offering.types.ts +0 -0
  92. /package/{src/common → common}/types/issuer-bank-account.types.ts +0 -0
  93. /package/{src/common → common}/types/job-item.types.ts +0 -0
  94. /package/{src/common → common}/types/jobs.types.ts +0 -0
  95. /package/{src/common → common}/types/kyb.types.ts +0 -0
  96. /package/{src/common → common}/types/kyc.types.ts +0 -0
  97. /package/{src/common → common}/types/login-history.types.ts +0 -0
  98. /package/{src/common → common}/types/mail-template.types.ts +0 -0
  99. /package/{src/common → common}/types/north-cap-integration.types.ts +0 -0
  100. /package/{src/common → common}/types/notion-api.types.ts +0 -0
  101. /package/{src/common → common}/types/notion-database.types.ts +0 -0
  102. /package/{src/common → common}/types/notion-page.types.ts +0 -0
  103. /package/{src/common → common}/types/offering-reports.types.ts +0 -0
  104. /package/{src/common → common}/types/offering-submission.types.ts +0 -0
  105. /package/{src/common → common}/types/page-revision.types.ts +0 -0
  106. /package/{src/common → common}/types/password.type.ts +0 -0
  107. /package/{src/common → common}/types/payment-methods.types.ts +0 -0
  108. /package/{src/common → common}/types/portfolio.types.ts +0 -0
  109. /package/{src/common → common}/types/privacy-policy-and-tos.types.ts +0 -0
  110. /package/{src/common → common}/types/queue.types.ts +0 -0
  111. /package/{src/common → common}/types/registered-reps.types.ts +0 -0
  112. /package/{src/common → common}/types/rejection-reasons.types.ts +0 -0
  113. /package/{src/common → common}/types/reminder-config.types.ts +0 -0
  114. /package/{src/common → common}/types/review.types.ts +0 -0
  115. /package/{src/common → common}/types/role.types.ts +0 -0
  116. /package/{src/common → common}/types/secondary-customer.types.ts +0 -0
  117. /package/{src/common → common}/types/secondary-issuer.types.ts +0 -0
  118. /package/{src/common → common}/types/secondary-order.types.ts +0 -0
  119. /package/{src/common → common}/types/secondary-security.types.ts +0 -0
  120. /package/{src/common → common}/types/secondary-trade.entity.ts +0 -0
  121. /package/{src/common → common}/types/secondary-trade.types.ts +0 -0
  122. /package/{src/common → common}/types/secure-request.types.ts +0 -0
  123. /package/{src/common → common}/types/signer.types.ts +0 -0
  124. /package/{src/common → common}/types/site-link.types.ts +0 -0
  125. /package/{src/common → common}/types/sms.types.ts +0 -0
  126. /package/{src/common → common}/types/state-machine.types.ts +0 -0
  127. /package/{src/common → common}/types/states.types.ts +0 -0
  128. /package/{src/common → common}/types/subdoc-preview.types.ts +0 -0
  129. /package/{src/common → common}/types/task.types.ts +0 -0
  130. /package/{src/common → common}/types/trade-adjustment.type.ts +0 -0
  131. /package/{src/common → common}/types/typography.types.ts +0 -0
  132. /package/{src/common → common}/types/user-manual.types.ts +0 -0
  133. /package/{src/common → common}/types/user-setting.types.ts +0 -0
  134. /package/{src/common → common}/types/webhook.types.ts +0 -0
  135. /package/{src/contracts → contracts}/clients/accounts/index.ts +0 -0
  136. /package/{src/contracts → contracts}/clients/aic/index.ts +0 -0
  137. /package/{src/contracts → contracts}/clients/api-key-logs/index.ts +0 -0
  138. /package/{src/contracts → contracts}/clients/api-keys/index.ts +0 -0
  139. /package/{src/contracts → contracts}/clients/assets/index.ts +0 -0
  140. /package/{src/contracts → contracts}/clients/auth/index.ts +0 -0
  141. /package/{src/contracts → contracts}/clients/individuals/index.ts +0 -0
  142. /package/{src/contracts → contracts}/clients/investor-accounts/index.ts +0 -0
  143. /package/{src/contracts → contracts}/clients/issuers/index.ts +0 -0
  144. /package/{src/contracts → contracts}/clients/legal-entities/index.ts +0 -0
  145. /package/{src/contracts → contracts}/clients/offerings/index.ts +0 -0
  146. /package/{src/contracts → contracts}/clients/secure-requests/index.ts +0 -0
  147. /package/{src/contracts → contracts}/clients/sites/index.ts +0 -0
package/README.md CHANGED
@@ -2,73 +2,82 @@
2
2
 
3
3
  Type-safe API contracts for Dalmore Client Portal, powered by [ts-rest](https://ts-rest.com/).
4
4
 
5
- This package provides **full end-to-end type inference** by shipping TypeScript source files alongside compiled JavaScript.
6
-
7
5
  ## Installation
8
6
 
9
7
  ```bash
8
+ # Production (stable releases from main branch)
10
9
  npm install @dalmore/api-contracts
11
- ```
12
-
13
- ### Peer Dependencies
14
10
 
15
- ```bash
16
- npm install @ts-rest/core zod @anatine/zod-openapi @nestjs/common libphonenumber-js neverthrow typeid-js
11
+ # Development (latest from dev branch)
12
+ npm install @dalmore/api-contracts@dev
17
13
  ```
18
14
 
19
15
  ## Usage
20
16
 
17
+ ### Import from main entry
18
+
21
19
  ```typescript
22
20
  import { clientsContract } from '@dalmore/api-contracts';
21
+ import type { IInvestorAccount } from '@dalmore/api-contracts';
22
+ ```
23
+
24
+ ### Import specific subpaths (recommended for tree-shaking)
25
+
26
+ ```typescript
27
+ // Import clients contract
28
+ import { clientsContract } from '@dalmore/api-contracts/clients';
29
+
30
+ // Import types
31
+ import type { IInvestorAccount, ITrade } from '@dalmore/api-contracts/types';
32
+ ```
33
+
34
+ ### Create a typed client
35
+
36
+ ```typescript
37
+ import { clientsContract } from '@dalmore/api-contracts/clients';
23
38
  import { initClient } from '@ts-rest/core';
24
39
 
25
40
  const client = initClient(clientsContract, {
26
- baseUrl: 'https://api.example.com',
41
+ baseUrl: 'https://platform.dalmoregroup.com',
27
42
  baseHeaders: {
28
- 'x-api-key': 'your-api-key',
43
+ 'X-API-Key': `${apiKey}`,
29
44
  },
30
45
  });
31
46
 
32
- // Fully typed API calls with complete autocomplete
33
- const { status, body } = await client.accounts.getAccounts();
34
-
35
- // Access nested endpoints with full type inference
36
- const trade = await client.trades.getTrade({ params: { id: 'trade_123' } });
47
+ // Fully typed API calls
48
+ const { status, body } = await client.accounts.getAccounts({
49
+ query: { page: 1, limit: 10 },
50
+ });
37
51
  ```
38
52
 
53
+ ## Peer Dependencies
54
+
55
+ This package requires the following peer dependencies:
56
+
57
+ - `@anatine/zod-openapi` ^1.14.2
58
+ - `@ts-rest/core` ^3.52.1
59
+ - `typeid-js` ^1.2.0
60
+ - `zod` ^3.24.4
61
+
62
+ ## Available Exports
63
+
64
+ - `clientsContract` - Client API (API key auth)
65
+
66
+ ## Available Subpath Exports
67
+
68
+ - `@dalmore/api-contracts` - Main entry (clients contract and types)
69
+ - `@dalmore/api-contracts/clients` - Clients contract
70
+ - `@dalmore/api-contracts/types` - All common types
71
+
39
72
  ## TypeScript Configuration
40
73
 
41
- For optimal type inference, ensure your `tsconfig.json` includes:
74
+ This package ships TypeScript source files for maximum type safety. Your `tsconfig.json` should include:
42
75
 
43
76
  ```json
44
77
  {
45
78
  "compilerOptions": {
46
- "skipLibCheck": true, // Recommended for faster compilation
47
79
  "moduleResolution": "node",
48
80
  "esModuleInterop": true
49
81
  }
50
82
  }
51
83
  ```
52
-
53
- ## Available Endpoints
54
-
55
- The `clientsContract` includes:
56
-
57
- | Endpoint | Description |
58
- | ------------------ | ----------------------- |
59
- | `accounts` | Account management |
60
- | `aic` | AIC verification |
61
- | `auth` | Authentication |
62
- | `apiKeys` | API key management |
63
- | `apiKeyLogs` | API key usage logs |
64
- | `assets` | Asset management |
65
- | `files` | File uploads |
66
- | `filesPublic` | Public file access |
67
- | `individuals` | Individual management |
68
- | `investorAccounts` | Investor accounts |
69
- | `issuers` | Issuer information |
70
- | `legalEntities` | Legal entity management |
71
- | `offerings` | Offering data |
72
- | `secureRequests` | Secure request handling |
73
- | `sites` | Site configuration |
74
- | `trades` | Trade management |
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Contract helpers - minimal subset for API documentation generation
3
+ */
4
+
5
+ type CurlOptions = {
6
+ query?: Record<string, string>;
7
+ headers?: Record<string, string>;
8
+ body?: Record<string, unknown>;
9
+ formData?: Record<string, string>;
10
+ };
11
+
12
+ export const authHeader: CurlOptions['headers'] = {
13
+ Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
14
+ };
15
+
16
+ const generateCurlExample = (
17
+ method: string,
18
+ path: string,
19
+ options: CurlOptions,
20
+ ) => {
21
+ const baseUrl = 'https://dalmore-client-portal-api-prod.onrender.com/api/v1';
22
+ let curl = `curl -X ${method.toUpperCase()} '${baseUrl}${path}`;
23
+ if (options.query) {
24
+ const queryString = new URLSearchParams(options.query).toString();
25
+ curl += `?${queryString}`;
26
+ }
27
+ curl += "'";
28
+ if (options.headers) {
29
+ Object.entries(options.headers).forEach(([key, value]) => {
30
+ curl += ` \\\n --header '${key}: ${value}'`;
31
+ });
32
+ }
33
+ if (options.body) {
34
+ curl += ` \\\n --header 'Content-Type: application/json'`;
35
+ curl += ` \\\n --data '${JSON.stringify(options.body)}'`;
36
+ }
37
+ if (options.formData) {
38
+ Object.entries(options.formData).forEach(([key, value]) => {
39
+ curl += ` \\\n --form '${key}=${value}'`;
40
+ });
41
+ }
42
+ return curl;
43
+ };
44
+
45
+ export const generateApiDescription = (
46
+ summary: string,
47
+ method: string,
48
+ path: string,
49
+ options: CurlOptions,
50
+ ) => {
51
+ const curlExample = generateCurlExample(method, path, options);
52
+ return `${summary}
53
+
54
+ ### Example curl request
55
+
56
+ \`\`\`bash
57
+ ${curlExample}
58
+ \`\`\``;
59
+ };
@@ -6,6 +6,7 @@ import { PhoneZodSchema } from './phone.type';
6
6
  import {
7
7
  AccountContactType,
8
8
  AccountZod,
9
+ EmailSchema,
9
10
  IPaginationMeta,
10
11
  } from './common.types';
11
12
  import { IBaseEntity } from './entity.types';
@@ -60,7 +61,7 @@ export const PatchAccountContactZod = z.object({
60
61
  .openapi({ example: 'Mr' }),
61
62
  firstName: z.string().min(1).max(150).openapi({ example: 'John' }),
62
63
  lastName: z.string().min(1).max(150).openapi({ example: 'Doe' }),
63
- email: z.string().email().openapi({ example: 'john.doe@example.com' }),
64
+ email: EmailSchema.openapi({ example: 'john.doe@example.com' }),
64
65
  phone: PhoneZodSchema.nullable()
65
66
  .optional()
66
67
  .openapi({ example: '+12124567890' }),
@@ -4,7 +4,7 @@ import { TypeID } from 'typeid-js';
4
4
  import { IBaseEntity } from './entity.types';
5
5
  import { PhoneNumberData } from './sms.types';
6
6
  import { userIdSchema, UserZod } from './user.types';
7
- import { IPaginationMeta } from './common.types';
7
+ import { EmailSchema, IPaginationMeta } from './common.types';
8
8
  import { PhoneZodSchema } from './phone.type';
9
9
 
10
10
  extendZodWithOpenApi(z);
@@ -68,7 +68,7 @@ export type IAccountManagerZod = z.infer<typeof IAccountManagerZod>;
68
68
  export const PostAccountManagerZod = z.object({
69
69
  firstName: z.string().min(1).max(255).openapi({ example: 'John' }),
70
70
  lastName: z.string().min(1).max(255).openapi({ example: 'Doe' }),
71
- email: z.string().email().openapi({ example: 'john.doe@example.com' }),
71
+ email: EmailSchema,
72
72
  phone: PhoneZodSchema.nullable()
73
73
  .optional()
74
74
  .openapi({ example: '+12124567890' }),
@@ -80,11 +80,7 @@ export type PostAccountManagerZod = z.infer<typeof PostAccountManagerZod>;
80
80
  export const PatchAccountManagerZod = z.object({
81
81
  firstName: z.string().min(1).max(255).optional().openapi({ example: 'John' }),
82
82
  lastName: z.string().min(1).max(255).optional().openapi({ example: 'Doe' }),
83
- email: z
84
- .string()
85
- .email()
86
- .optional()
87
- .openapi({ example: 'john.doe@example.com' }),
83
+ email: EmailSchema.optional(),
88
84
  phone: PhoneZodSchema.nullable()
89
85
  .optional()
90
86
  .openapi({ example: '+12124567890' }),
@@ -0,0 +1,65 @@
1
+ import { extendZodWithOpenApi } from '@anatine/zod-openapi';
2
+ import { z } from 'zod';
3
+ import { TypeID } from 'typeid-js';
4
+ import { IBaseEntity } from './entity.types';
5
+ import { IPaginationMeta } from './common.types';
6
+ import { accountIdSchema } from './account.types';
7
+
8
+ extendZodWithOpenApi(z);
9
+
10
+ export const accountSettingIdSchema = z.string().refine(
11
+ (value) => {
12
+ try {
13
+ const tid = TypeID.fromString(value);
14
+ return tid.getType() === 'account_setting';
15
+ } catch {
16
+ return false;
17
+ }
18
+ },
19
+ {
20
+ message:
21
+ 'Invalid account setting ID format. Must be a valid TypeID with "account_setting" prefix.',
22
+ },
23
+ );
24
+
25
+ export const IAccountSettingZod = IBaseEntity.extend({
26
+ id: accountSettingIdSchema.openapi({
27
+ example: 'account_setting_01j5y5ghx8fvc83dmx3pznq7hv',
28
+ }),
29
+ accountId: z.string().openapi({
30
+ example: 'account_01j5y5ghx8fvc83dmx3pznq7hv',
31
+ }),
32
+ });
33
+ export type IAccountSettingZod = z.infer<typeof IAccountSettingZod>;
34
+
35
+ export const IPaginatedAccountSetting = z.object({
36
+ items: z.array(IAccountSettingZod),
37
+ meta: IPaginationMeta,
38
+ });
39
+ export type IPaginatedAccountSetting = z.infer<typeof IPaginatedAccountSetting>;
40
+
41
+ export const AccountSettingsFilters = z.object({
42
+ accountId: accountIdSchema.optional(),
43
+ });
44
+ export type AccountSettingsFilters = z.infer<typeof AccountSettingsFilters>;
45
+
46
+ export const accountSettingsInclude = z.enum(['notificationChannels']);
47
+
48
+ export const AccountSettingsIncludeQuery = z.object({
49
+ include: z
50
+ .string()
51
+ .optional()
52
+ .transform((str) => (str ? str.split(',') : []))
53
+ .refine(
54
+ (includes) =>
55
+ includes.every((include) =>
56
+ accountSettingsInclude.options.includes(include as any),
57
+ ),
58
+ {
59
+ message: `Invalid include value. Valid values are: ${accountSettingsInclude.options.join(', ')}`,
60
+ },
61
+ ),
62
+ });
63
+ export type AccountSettingsIncludeQuery = z.infer<
64
+ typeof AccountSettingsIncludeQuery
65
+ >;
@@ -91,6 +91,7 @@ export const AccountFiltersZod = z.object({
91
91
  const accountsInclude = z.enum([
92
92
  'accountManager',
93
93
  'accountIntegrations',
94
+ 'accountSettings',
94
95
  'apiKeys',
95
96
  'assets',
96
97
  'dataRooms',
@@ -199,7 +199,7 @@ export const ActivityZod = IBaseEntity.extend({
199
199
  userId: z.string().nullable(),
200
200
  activityTypeId: z.string(),
201
201
  accountId: z.string().nullable(),
202
- user: UserForActivityZod,
202
+ user: UserForActivityZod.nullable(),
203
203
  activityType: ActivityTypeZod,
204
204
  targetObject: z.string().nullable().optional(),
205
205
  __entity: z.string().optional(),
@@ -12,7 +12,7 @@ export const ApiLogsFiltersZod = z.object({
12
12
  endpoint: z.string().optional(),
13
13
  apiKeyId: apiKeyIdSchema.optional(),
14
14
  method: z.nativeEnum(HttpMethod).optional(),
15
- status: z.nativeEnum(HttpStatus).optional(),
15
+ status: z.preprocess(Number, z.nativeEnum(HttpStatus)).optional(),
16
16
  from: dateSchema.optional().openapi({ example: 'MM/DD/YYYY' }),
17
17
  to: dateSchema.optional().openapi({ example: 'MM/DD/YYYY' }),
18
18
  });
@@ -29,7 +29,7 @@ export const assetIdSchema = z.string().refine(
29
29
  message: `Invalid asset ID format. Must be a valid TypeID with "asset" prefix. Example: asset_01j5y5ghx5fg68d663j1fvy2x7`,
30
30
  },
31
31
  );
32
- export enum TemplateType {
32
+ export enum AssetTemplateType {
33
33
  STANDARD = 'STANDARD',
34
34
  TIERED = 'TIERED',
35
35
  }
@@ -51,7 +51,7 @@ export const IAsset = IBaseEntity.extend({
51
51
  .lazy(() => IOffering)
52
52
  .optional()
53
53
  .nullable(), // Use z.lazy here
54
- template: z.nativeEnum(TemplateType),
54
+ template: z.nativeEnum(AssetTemplateType),
55
55
  tiers: z.array(z.number().positive()).nullable(),
56
56
  enableBonus: z.boolean(),
57
57
  });
@@ -102,16 +102,16 @@ const PostAssetBase = z.object({
102
102
  .optional()
103
103
  .openapi({ example: DurationType.DAY }),
104
104
  template: z
105
- .nativeEnum(TemplateType)
106
- .default(TemplateType.STANDARD)
107
- .openapi({ example: TemplateType.STANDARD }),
105
+ .nativeEnum(AssetTemplateType)
106
+ .default(AssetTemplateType.STANDARD)
107
+ .openapi({ example: AssetTemplateType.STANDARD }),
108
108
  tiers: z.array(z.number().positive()).nullable().optional(),
109
109
  enableBonus: z.boolean().default(false).openapi({ example: false }),
110
110
  });
111
111
 
112
- const postAssetRefinement = (data: any, ctx: any) => {
112
+ export const postAssetRefinement = (data: any, ctx: any) => {
113
113
  // If type is bond, yield and duration must be provided (cannot be null or undefined)
114
- if (data.type === AssetType.BOND) {
114
+ if (data.assetType === AssetType.BOND || data.type === AssetType.BOND) {
115
115
  if (data.yield === null || data.yield === undefined) {
116
116
  ctx.addIssue({
117
117
  path: ['yield'],
@@ -139,7 +139,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
139
139
  }
140
140
 
141
141
  // If type is stock, yield and duration must be either null or undefined
142
- if (data.type === AssetType.STOCK) {
142
+ if (data.assetType === AssetType.STOCK || data.type === AssetType.STOCK) {
143
143
  if (data.yield !== null && data.yield !== undefined) {
144
144
  ctx.addIssue({
145
145
  path: ['yield'],
@@ -162,7 +162,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
162
162
  });
163
163
  }
164
164
  }
165
- if (data.template === TemplateType.TIERED) {
165
+ if (data.template === AssetTemplateType.TIERED) {
166
166
  if (data.tiers === null || data.tiers === undefined) {
167
167
  ctx.addIssue({
168
168
  path: ['tiers'],
@@ -172,7 +172,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
172
172
  }
173
173
  }
174
174
  // If template is STANDARD, tiers must be null or undefined
175
- if (data.template === TemplateType.STANDARD) {
175
+ if (data.template === AssetTemplateType.STANDARD) {
176
176
  if (data.tiers !== null && data.tiers !== undefined) {
177
177
  ctx.addIssue({
178
178
  path: ['tiers'],
@@ -233,9 +233,9 @@ export const PutAsset = z.object({
233
233
  .optional()
234
234
  .openapi({ example: DurationType.DAY }),
235
235
  template: z
236
- .nativeEnum(TemplateType)
237
- .default(TemplateType.STANDARD)
238
- .openapi({ example: TemplateType.STANDARD })
236
+ .nativeEnum(AssetTemplateType)
237
+ .default(AssetTemplateType.STANDARD)
238
+ .openapi({ example: AssetTemplateType.STANDARD })
239
239
  .nullable()
240
240
  .optional(),
241
241
  tiers: z.array(z.number().positive()).nullable().optional(),
@@ -262,7 +262,7 @@ export const AssetsIncludeQuery = z.object({
262
262
  assetsInclude.options.includes(include as any),
263
263
  ),
264
264
  {
265
- message: `Invalid include option provided. Valid options are: ${assetsInclude.options.join(', ')}`,
265
+ message: `Invalid include option provided. Valid options are: ${assetsInclude.options.join(',')}`,
266
266
  },
267
267
  )
268
268
  .openapi({
@@ -8,6 +8,7 @@ import {
8
8
  ManagedByType,
9
9
  UserRole,
10
10
  createUrlSchema,
11
+ EmailSchema,
11
12
  } from './common.types';
12
13
  import { TypeID } from 'typeid-js';
13
14
  import { TwoFactorMethod } from './sms.types';
@@ -60,13 +61,7 @@ const BaseAuthBody = z.object({
60
61
  .openapi({
61
62
  example: 'Armstrong',
62
63
  }),
63
- email: z
64
- .string()
65
- .email()
66
- .transform((val) => val.toLowerCase())
67
- .openapi({
68
- example: 'neil@dalmoregroup.com',
69
- }),
64
+ email: EmailSchema,
70
65
  password: PasswordSchema,
71
66
  });
72
67
 
@@ -99,13 +94,7 @@ export const IChangePasswordBodyZod = z
99
94
  });
100
95
 
101
96
  export const LoginBody = z.object({
102
- email: z
103
- .string()
104
- .email()
105
- .transform((val) => val.toLowerCase())
106
- .openapi({
107
- example: 'neil@dalmoregroup.com',
108
- }),
97
+ email: EmailSchema,
109
98
  password: z.string().openapi({
110
99
  example: 'Secretpassword6#',
111
100
  }),
@@ -297,7 +286,7 @@ export const RegisterBodyInvestors = BaseAuthBody.extend({
297
286
  });
298
287
 
299
288
  export const ResetPasswordVerifyBody = z.object({
300
- email: z.string().email(),
289
+ email: EmailSchema,
301
290
  code: z.string().length(6, 'code is not 6 characters').openapi({
302
291
  example: '123456',
303
292
  }),
@@ -315,7 +304,7 @@ export type ResetPasswordVerifyResponseType = z.infer<
315
304
  >;
316
305
 
317
306
  export const ResetPasswordBody = z.object({
318
- email: z.string().email(),
307
+ email: EmailSchema,
319
308
  code: z.string().length(6, 'code is not 6 characters').openapi({
320
309
  example: '123456',
321
310
  }),
@@ -337,7 +326,7 @@ export type ClientAuthSuccessResponse = z.infer<
337
326
  >;
338
327
 
339
328
  export const AdminForgotPasswordRequestZod = z.object({
340
- email: z.string().email(),
329
+ email: EmailSchema,
341
330
  portal: z.nativeEnum(PortalType),
342
331
  });
343
332
  export type AdminForgotPasswordRequestZod = z.infer<
@@ -345,7 +334,7 @@ export type AdminForgotPasswordRequestZod = z.infer<
345
334
  >;
346
335
 
347
336
  export const InvestorForgotPasswordRequestZod = z.object({
348
- email: z.string().email(),
337
+ email: EmailSchema,
349
338
  url: createUrlSchema({ strict: true }),
350
339
  });
351
340
  export type InvestorForgotPasswordRequestZod = z.infer<
@@ -98,6 +98,15 @@ export type EstimateBonusTierCalculationZod = z.infer<
98
98
  typeof EstimateBonusTierCalculationZod
99
99
  >;
100
100
 
101
+ export const ComplianceEstimateBonusTierCalculationZod =
102
+ EstimateBonusTierCalculationZod.and(
103
+ z.object({
104
+ accountId: accountIdSchema,
105
+ }),
106
+ );
107
+ export type ComplianceEstimateBonusTierCalculationZod = z.infer<
108
+ typeof ComplianceEstimateBonusTierCalculationZod
109
+ >;
101
110
  export const EstimateBonusTierCalculationResponseZod = z.object({
102
111
  bonusTierId: bonusTierIdSchema
103
112
  .nullable()
@@ -145,3 +154,27 @@ export const CompliancePostBonusTierZod = PostBonusTierZod.extend({
145
154
  export type CompliancePostBonusTierZod = z.infer<
146
155
  typeof CompliancePostBonusTierZod
147
156
  >;
157
+ export const PurchaseCalculationZod = z.object({
158
+ assetId: assetIdSchema,
159
+ totalAmount: z.number().positive().openapi({ example: 1000 }),
160
+ purchasedShares: z.number().positive().int().openapi({ example: 100 }),
161
+ });
162
+ export type PurchaseCalculationZod = z.infer<typeof PurchaseCalculationZod>;
163
+
164
+ export const PurchaseCalculationResponseZod = z.object({
165
+ bonusTierId: bonusTierIdSchema
166
+ .nullable()
167
+ .openapi({ example: 'bonus_tier_01j5y5ghx5fg68d663j1fvy2x7' }),
168
+ assetId: assetIdSchema.openapi({
169
+ example: 'asset_00041061050r3gg28a1c60t3gf',
170
+ }),
171
+ type: z
172
+ .nativeEnum(BonusType)
173
+ .nullable()
174
+ .openapi({ example: BonusType.PERCENTAGE }),
175
+ bonusShares: z.number().int().openapi({ example: 100 }),
176
+ });
177
+
178
+ export type PurchaseCalculationResponseZod = z.infer<
179
+ typeof PurchaseCalculationResponseZod
180
+ >;
@@ -3,6 +3,7 @@ import { extendZodWithOpenApi } from '@anatine/zod-openapi';
3
3
  import { TradeStatus } from './common.types';
4
4
  import { investorAccountIdSchema } from './investor-account.types';
5
5
  import { paymentMethodIdSchema } from './payment-methods.types';
6
+ import { userIdSchema } from './user.types';
6
7
 
7
8
  extendZodWithOpenApi(z);
8
9
 
@@ -10,7 +11,9 @@ export const PlaceTradeResponse = z.object({
10
11
  tradeStatus: z.nativeEnum(TradeStatus).optional(),
11
12
  });
12
13
  export const PlaceTradeBody = z.object({});
13
-
14
+ export const ClientPlacetradeBody = z.object({
15
+ userId: userIdSchema,
16
+ });
14
17
  export const PatchCartBody = z.object({
15
18
  investorAccountId: investorAccountIdSchema.optional(),
16
19
  paymentMethodId: paymentMethodIdSchema.optional(),
@@ -4,7 +4,7 @@ import { z } from 'zod';
4
4
  import { TypeID } from 'typeid-js';
5
5
  import { ErrorHttpStatusCode } from '@ts-rest/core';
6
6
  import { TwoFactorMethod } from './sms.types';
7
- import { normalizeShortDate } from './contract-helpers';
7
+ import { normalizeShortDate } from '../helpers';
8
8
  import { HttpStatus } from '@nestjs/common';
9
9
 
10
10
  extendZodWithOpenApi(z);
@@ -69,6 +69,10 @@ export enum BaseStatus {
69
69
  VOIDED = 'VOIDED',
70
70
  JOIN = 'JOIN',
71
71
  RESTORE = 'RESTORE',
72
+ ACTIVE = 'ACTIVE',
73
+ LOCKED = 'LOCKED',
74
+ ENABLED = 'ENABLED',
75
+ DISABLED = 'DISABLED',
72
76
  }
73
77
 
74
78
  export const UserWithoutPasswordAccountZod = IBaseEntity.extend({
@@ -111,6 +115,10 @@ export enum HttpMethod {
111
115
  OPTIONS = 'OPTIONS',
112
116
  }
113
117
 
118
+ export enum UserStatus {
119
+ ACTIVE = 'ACTIVE',
120
+ LOCKED = 'LOCKED',
121
+ }
114
122
  export const SENSITIVE_PATTERNS = [
115
123
  /password/i,
116
124
  /credit.*card/i,
@@ -200,11 +208,6 @@ export enum UserType {
200
208
  DEMO = 'DEMO',
201
209
  }
202
210
 
203
- export enum UserStatus {
204
- ACTIVE = 'ACTIVE',
205
- LOCKED = 'LOCKED',
206
- }
207
-
208
211
  export enum UserRole {
209
212
  API_KEY = 'API_KEY',
210
213
  IMPORT = 'IMPORT',
@@ -306,7 +309,7 @@ export const AuthUserReq = BaseAuthReq.extend({
306
309
  lastName: z.string(),
307
310
  email: z.string().email(),
308
311
  provider: z.string(),
309
- status: z.lazy(() => z.nativeEnum(UserStatus)),
312
+ locked: z.boolean(),
310
313
  lastLoginAt: z.date().nullable(),
311
314
  loginCount: z.number(),
312
315
  requiresTwoFactorSetup: z.boolean().optional(),
@@ -809,6 +812,8 @@ export enum EventField {
809
812
  SA_STATUS = 'saStatus',
810
813
  ONBOARDING_STATUS = 'onboardingStatus',
811
814
  TRANSACTION_STATUS = 'transactionStatus',
815
+ ACTIVE = 'active',
816
+ LOCKED = 'locked',
812
817
  }
813
818
 
814
819
  export enum EventName {
@@ -1518,3 +1523,30 @@ export const SUBJECT_TYPE_MAP: Record<BulkExportType, string> = {
1518
1523
  [BulkExportType.SECONDARY_CUSTOMERS]: 'Secondary Customers',
1519
1524
  [BulkExportType.SECONDARY_TRADES]: 'Secondary Trades',
1520
1525
  };
1526
+
1527
+ export const StringToBooleanSchema = z.preprocess(
1528
+ (val) =>
1529
+ val === 'true' || val === '1'
1530
+ ? true
1531
+ : val === 'false' || val === '0'
1532
+ ? false
1533
+ : val,
1534
+ z.boolean(),
1535
+ );
1536
+ export type StringToBooleanSchema = z.infer<typeof StringToBooleanSchema>;
1537
+
1538
+ /**
1539
+ * Reusable email schema that validates email format, transforms to lowercase,
1540
+ * and includes OpenAPI metadata.
1541
+ *
1542
+ * @example
1543
+ * EmailSchema.parse('John.Doe@EXAMPLE.COM'); // returns 'john.doe@example.com'
1544
+ * EmailSchema.optional().parse(undefined); // returns undefined
1545
+ */
1546
+ export const EmailSchema = z
1547
+ .string()
1548
+ .email()
1549
+ .transform((val) => val.toLowerCase())
1550
+ .openapi({
1551
+ example: 'neil@dalmoregroup.com',
1552
+ });
@@ -1,7 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { extendZodWithOpenApi } from '@anatine/zod-openapi';
3
3
  import { TypeID } from 'typeid-js';
4
- import { BaseContactUsOptions, createUrlSchema } from './common.types';
4
+ import {
5
+ BaseContactUsOptions,
6
+ createUrlSchema,
7
+ EmailSchema,
8
+ } from './common.types';
5
9
  extendZodWithOpenApi(z);
6
10
 
7
11
  export const contactUsIdSchema = z.string().refine(
@@ -55,7 +59,7 @@ export const InvestorPostContactUsZod = PostContactUsZod.extend({
55
59
  export type InvestorPostContactUsZod = z.infer<typeof InvestorPostContactUsZod>;
56
60
 
57
61
  export const PublicInvestorContactUsZod = PostContactUsZod.extend({
58
- email: z.string().email().openapi({ example: 'email@example.com' }),
62
+ email: EmailSchema,
59
63
  firstName: z.string().min(1).max(100).openapi({ example: 'John' }),
60
64
  lastName: z.string().min(1).max(100).openapi({ example: 'Doe' }),
61
65
  contactOption: z.nativeEnum(InvestorContactOptions).openapi({
@@ -6,6 +6,7 @@ import {
6
6
  ComplianceReview,
7
7
  CoveredPersonsRoleType,
8
8
  CoveredPersonsStatus,
9
+ EmailSchema,
9
10
  IDType,
10
11
  IPaginationMeta,
11
12
  } from './common.types';
@@ -137,7 +138,7 @@ export const PatchCoveredPersonsZod = z
137
138
  .openapi({ example: CoveredPersonsRoleType.ACCOUNTING }),
138
139
 
139
140
  ownership: z.number().min(0).max(100).openapi({ example: 15 }).optional(),
140
- email: z.string().nullable().optional(),
141
+ email: EmailSchema.nullable().optional(),
141
142
  idType: z
142
143
  .nativeEnum(IDType)
143
144
  .optional()