@api-client/core 0.15.1 → 0.16.1

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 (125) hide show
  1. package/TESTING_READY.md +114 -0
  2. package/TESTING_SETUP.md +198 -0
  3. package/build/src/modeling/Semantics.d.ts +126 -2
  4. package/build/src/modeling/Semantics.d.ts.map +1 -1
  5. package/build/src/modeling/Semantics.js +281 -13
  6. package/build/src/modeling/Semantics.js.map +1 -1
  7. package/build/src/modeling/definitions/Calculated.d.ts +54 -0
  8. package/build/src/modeling/definitions/Calculated.d.ts.map +1 -0
  9. package/build/src/modeling/definitions/Calculated.js +31 -0
  10. package/build/src/modeling/definitions/Calculated.js.map +1 -0
  11. package/build/src/modeling/definitions/Categories.d.ts +60 -0
  12. package/build/src/modeling/definitions/Categories.d.ts.map +1 -0
  13. package/build/src/modeling/definitions/Categories.js +33 -0
  14. package/build/src/modeling/definitions/Categories.js.map +1 -0
  15. package/build/src/modeling/definitions/Derived.d.ts +54 -0
  16. package/build/src/modeling/definitions/Derived.d.ts.map +1 -0
  17. package/build/src/modeling/definitions/Derived.js +31 -0
  18. package/build/src/modeling/definitions/Derived.js.map +1 -0
  19. package/build/src/modeling/definitions/Description.d.ts +36 -0
  20. package/build/src/modeling/definitions/Description.d.ts.map +1 -0
  21. package/build/src/modeling/definitions/Description.js +28 -0
  22. package/build/src/modeling/definitions/Description.js.map +1 -0
  23. package/build/src/modeling/definitions/Email.d.ts +66 -0
  24. package/build/src/modeling/definitions/Email.d.ts.map +1 -0
  25. package/build/src/modeling/definitions/Email.js +33 -0
  26. package/build/src/modeling/definitions/Email.js.map +1 -0
  27. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts +212 -0
  28. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts.map +1 -0
  29. package/build/src/modeling/definitions/GeospatialCoordinates.js +129 -0
  30. package/build/src/modeling/definitions/GeospatialCoordinates.js.map +1 -0
  31. package/build/src/modeling/definitions/HTML.d.ts +88 -0
  32. package/build/src/modeling/definitions/HTML.d.ts.map +1 -0
  33. package/build/src/modeling/definitions/HTML.js +42 -0
  34. package/build/src/modeling/definitions/HTML.js.map +1 -0
  35. package/build/src/modeling/definitions/Markdown.d.ts +84 -0
  36. package/build/src/modeling/definitions/Markdown.d.ts.map +1 -0
  37. package/build/src/modeling/definitions/Markdown.js +41 -0
  38. package/build/src/modeling/definitions/Markdown.js.map +1 -0
  39. package/build/src/modeling/definitions/Password.d.ts +112 -0
  40. package/build/src/modeling/definitions/Password.d.ts.map +1 -0
  41. package/build/src/modeling/definitions/Password.js +57 -0
  42. package/build/src/modeling/definitions/Password.js.map +1 -0
  43. package/build/src/modeling/definitions/Phone.d.ts +83 -0
  44. package/build/src/modeling/definitions/Phone.d.ts.map +1 -0
  45. package/build/src/modeling/definitions/Phone.js +39 -0
  46. package/build/src/modeling/definitions/Phone.js.map +1 -0
  47. package/build/src/modeling/definitions/Price.d.ts +102 -0
  48. package/build/src/modeling/definitions/Price.d.ts.map +1 -0
  49. package/build/src/modeling/definitions/Price.js +99 -0
  50. package/build/src/modeling/definitions/Price.js.map +1 -0
  51. package/build/src/modeling/definitions/PublicUniqueName.d.ts +69 -0
  52. package/build/src/modeling/definitions/PublicUniqueName.d.ts.map +1 -0
  53. package/build/src/modeling/definitions/PublicUniqueName.js +34 -0
  54. package/build/src/modeling/definitions/PublicUniqueName.js.map +1 -0
  55. package/build/src/modeling/definitions/SKU.d.ts +127 -0
  56. package/build/src/modeling/definitions/SKU.d.ts.map +1 -0
  57. package/build/src/modeling/definitions/SKU.js +142 -0
  58. package/build/src/modeling/definitions/SKU.js.map +1 -0
  59. package/build/src/modeling/definitions/Status.d.ts +150 -0
  60. package/build/src/modeling/definitions/Status.d.ts.map +1 -0
  61. package/build/src/modeling/definitions/Status.js +60 -0
  62. package/build/src/modeling/definitions/Status.js.map +1 -0
  63. package/build/src/modeling/definitions/Summary.d.ts +53 -0
  64. package/build/src/modeling/definitions/Summary.d.ts.map +1 -0
  65. package/build/src/modeling/definitions/Summary.js +50 -0
  66. package/build/src/modeling/definitions/Summary.js.map +1 -0
  67. package/build/src/modeling/definitions/Tags.d.ts +52 -0
  68. package/build/src/modeling/definitions/Tags.d.ts.map +1 -0
  69. package/build/src/modeling/definitions/Tags.js +32 -0
  70. package/build/src/modeling/definitions/Tags.js.map +1 -0
  71. package/build/src/modeling/definitions/URL.d.ts +68 -0
  72. package/build/src/modeling/definitions/URL.d.ts.map +1 -0
  73. package/build/src/modeling/definitions/URL.js +37 -0
  74. package/build/src/modeling/definitions/URL.js.map +1 -0
  75. package/build/src/modeling/validation/semantic_validation.d.ts +4 -0
  76. package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -1
  77. package/build/src/modeling/validation/semantic_validation.js +32 -1
  78. package/build/src/modeling/validation/semantic_validation.js.map +1 -1
  79. package/build/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/modeling/Semantics.ts +297 -14
  82. package/src/modeling/definitions/Calculated.ts +76 -0
  83. package/src/modeling/definitions/Categories.ts +84 -0
  84. package/src/modeling/definitions/Derived.ts +76 -0
  85. package/src/modeling/definitions/Description.ts +55 -0
  86. package/src/modeling/definitions/Email.ts +90 -0
  87. package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
  88. package/src/modeling/definitions/HTML.ts +121 -0
  89. package/src/modeling/definitions/Markdown.ts +116 -0
  90. package/src/modeling/definitions/Password.ts +156 -0
  91. package/src/modeling/definitions/Phone.ts +116 -0
  92. package/src/modeling/definitions/Price.examples.md +158 -0
  93. package/src/modeling/definitions/Price.ts +180 -0
  94. package/src/modeling/definitions/PublicUniqueName.ts +98 -0
  95. package/src/modeling/definitions/SKU.examples.md +230 -0
  96. package/src/modeling/definitions/SKU.ts +254 -0
  97. package/src/modeling/definitions/Status.ts +227 -0
  98. package/src/modeling/definitions/Summary.ts +73 -0
  99. package/src/modeling/definitions/Tags.ts +75 -0
  100. package/src/modeling/definitions/URL.ts +96 -0
  101. package/src/modeling/validation/semantic_validation.ts +35 -1
  102. package/tests/example-test-setup.ts +133 -0
  103. package/tests/template-node.spec.ts +75 -0
  104. package/tests/test-utils.ts +293 -0
  105. package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
  106. package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
  107. package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
  108. package/tests/unit/modeling/definitions/description.spec.ts +38 -0
  109. package/tests/unit/modeling/definitions/email.spec.ts +38 -0
  110. package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
  111. package/tests/unit/modeling/definitions/html.spec.ts +38 -0
  112. package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
  113. package/tests/unit/modeling/definitions/password.spec.ts +347 -0
  114. package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
  115. package/tests/unit/modeling/definitions/price.spec.ts +465 -0
  116. package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
  117. package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
  118. package/tests/unit/modeling/definitions/status.spec.ts +37 -0
  119. package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
  120. package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
  121. package/tests/unit/modeling/definitions/url.spec.ts +38 -0
  122. package/tests/unit/modeling/domain_property.spec.ts +106 -0
  123. package/tests/unit/modeling/domain_validation.spec.ts +5 -5
  124. package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
  125. package/tests/unit/modeling/semantics.spec.ts +52 -0
@@ -0,0 +1,156 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Supported encryption algorithms for password hashing.
6
+ */
7
+ export enum PasswordEncryptionAlgorithm {
8
+ /**
9
+ * bcrypt - Industry standard, secure, and widely supported
10
+ */
11
+ Bcrypt = 'bcrypt',
12
+ /**
13
+ * Argon2 - Winner of the Password Hashing Competition, very secure
14
+ */
15
+ Argon2 = 'argon2',
16
+ /**
17
+ * scrypt - Memory-hard function, good for high-security applications
18
+ */
19
+ Scrypt = 'scrypt',
20
+ }
21
+
22
+ /**
23
+ * Configuration options for the Password semantic.
24
+ * These options control password validation and encryption behavior.
25
+ */
26
+ export interface PasswordConfig {
27
+ /**
28
+ * Whether to require special characters.
29
+ * Defaults to false if not specified.
30
+ */
31
+ requireSpecialChars?: boolean
32
+
33
+ /**
34
+ * Whether to require numbers.
35
+ * Defaults to false if not specified.
36
+ */
37
+ requireNumbers?: boolean
38
+
39
+ /**
40
+ * Whether to require uppercase letters.
41
+ * Defaults to false if not specified.
42
+ */
43
+ requireUppercase?: boolean
44
+
45
+ /**
46
+ * Whether to require lowercase letters.
47
+ * Defaults to false if not specified.
48
+ */
49
+ requireLowercase?: boolean
50
+
51
+ /**
52
+ * Encryption algorithm to use for password hashing.
53
+ * Defaults to 'bcrypt' if not specified.
54
+ */
55
+ encryptionAlgorithm?: PasswordEncryptionAlgorithm
56
+
57
+ /**
58
+ * Number of salt rounds for bcrypt.
59
+ * Defaults to 12 if not specified.
60
+ */
61
+ saltRounds?: number
62
+
63
+ /**
64
+ * Maximum age of password in days before requiring change.
65
+ * Defaults to null (no expiration) if not specified.
66
+ */
67
+ maxAge?: number
68
+
69
+ /**
70
+ * Whether to prevent reuse of recent passwords.
71
+ * Defaults to false if not specified.
72
+ */
73
+ preventReuse?: boolean
74
+
75
+ /**
76
+ * Number of recent passwords to prevent reuse.
77
+ * Defaults to 5 if preventReuse is true.
78
+ */
79
+ preventReuseCount?: number
80
+
81
+ /**
82
+ * Custom password validation regex pattern.
83
+ * If specified, overrides other validation rules.
84
+ */
85
+ customPattern?: string
86
+
87
+ /**
88
+ * Custom error message for validation failures.
89
+ */
90
+ customErrorMessage?: string
91
+
92
+ /**
93
+ * Whether to allow common passwords.
94
+ * Defaults to false if not specified.
95
+ */
96
+ allowCommonPasswords?: boolean
97
+
98
+ /**
99
+ * Custom metadata for the password field.
100
+ */
101
+ metadata?: Record<string, unknown>
102
+
103
+ /**
104
+ * Index signature to allow additional properties.
105
+ */
106
+ [key: string]: unknown
107
+ }
108
+
109
+ /**
110
+ * Type-safe configuration for Password semantic.
111
+ */
112
+ export interface AppliedPasswordSemantic extends AppliedDataSemantic {
113
+ id: SemanticType.Password
114
+ config?: PasswordConfig
115
+ }
116
+
117
+ /**
118
+ * Type guard to check if a semantic is a Password semantic.
119
+ */
120
+ export const isPasswordSemantic = (semantic: AppliedDataSemantic): semantic is AppliedPasswordSemantic => {
121
+ return semantic.id === SemanticType.Password
122
+ }
123
+
124
+ /**
125
+ * Helper function to create a Password semantic with configuration.
126
+ */
127
+ export const createPasswordSemantic = (config: PasswordConfig = {}): AppliedPasswordSemantic => {
128
+ const mergedConfig = {
129
+ ...DEFAULT_PASSWORD_CONFIG,
130
+ ...config,
131
+ }
132
+ if (config.metadata) {
133
+ mergedConfig.metadata = { ...config.metadata }
134
+ }
135
+
136
+ return {
137
+ id: SemanticType.Password,
138
+ config: mergedConfig,
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Default configuration for Password semantic.
144
+ */
145
+ export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
146
+ requireSpecialChars: true,
147
+ requireNumbers: true,
148
+ requireUppercase: true,
149
+ requireLowercase: true,
150
+ encryptionAlgorithm: PasswordEncryptionAlgorithm.Bcrypt,
151
+ saltRounds: 12,
152
+ maxAge: undefined,
153
+ preventReuse: false,
154
+ preventReuseCount: 5,
155
+ allowCommonPasswords: false,
156
+ }
@@ -0,0 +1,116 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration options for the Phone semantic.
6
+ * Controls validation, formatting, and verification of phone numbers.
7
+ */
8
+ export interface PhoneConfig {
9
+ /**
10
+ * List of allowed country codes (ISO 3166-1 alpha-2 format).
11
+ * If not specified, all countries are allowed.
12
+ * Examples: ['US', 'CA', 'GB', 'DE']
13
+ */
14
+ allowedCountries?: string[]
15
+
16
+ /**
17
+ * Whether the phone number must include a country code.
18
+ * Defaults to true for E.164 format.
19
+ */
20
+ requireCountryCode?: boolean
21
+
22
+ /**
23
+ * Format for phone number validation and display.
24
+ * - 'E.164': International format with + prefix (e.g., +1234567890)
25
+ * - 'national': Country-specific format without country code
26
+ * - 'international': International format with country code
27
+ * - 'custom': Use customFormat pattern
28
+ */
29
+ format?: 'E.164' | 'national' | 'international' | 'custom'
30
+
31
+ /**
32
+ * Custom format pattern for phone number validation.
33
+ * Only used when format is set to 'custom'.
34
+ * Examples: '###-###-####', '(###) ###-####'
35
+ */
36
+ customFormat?: string
37
+
38
+ /**
39
+ * Whether to allow phone extensions (e.g., +1234567890 ext 123).
40
+ * Defaults to false.
41
+ */
42
+ allowExtension?: boolean
43
+
44
+ /**
45
+ * Whether phone number verification is required.
46
+ * Defaults to false.
47
+ */
48
+ requireVerification?: boolean
49
+
50
+ /**
51
+ * Method to use for phone verification.
52
+ * - 'sms': Send verification code via SMS
53
+ * - 'call': Make verification call
54
+ * - 'none': No verification required
55
+ */
56
+ verificationMethod?: 'sms' | 'call' | 'none'
57
+
58
+ /**
59
+ * Custom metadata for the phone field.
60
+ */
61
+ metadata?: Record<string, unknown>
62
+
63
+ /**
64
+ * Index signature to allow additional properties.
65
+ */
66
+ [key: string]: unknown
67
+ }
68
+
69
+ /**
70
+ * Type-safe configuration for Phone semantic.
71
+ */
72
+ export interface AppliedPhoneSemantic extends AppliedDataSemantic {
73
+ id: SemanticType.Phone
74
+ config?: PhoneConfig
75
+ }
76
+
77
+ /**
78
+ * Type guard to check if a semantic is a Phone semantic.
79
+ * @param semantic - The semantic to check
80
+ * @returns True if the semantic is a Phone semantic
81
+ */
82
+ export const isPhoneSemantic = (semantic: AppliedDataSemantic): semantic is AppliedPhoneSemantic => {
83
+ return semantic.id === SemanticType.Phone
84
+ }
85
+
86
+ /**
87
+ * Helper function to create a Phone semantic with configuration.
88
+ * @param config - Configuration options for the phone semantic
89
+ * @returns AppliedPhoneSemantic with the specified configuration
90
+ */
91
+ export const createPhoneSemantic = (config: PhoneConfig = {}): AppliedPhoneSemantic => {
92
+ const mergedConfig = {
93
+ ...DEFAULT_PHONE_CONFIG,
94
+ ...config,
95
+ }
96
+ if (config.metadata) {
97
+ mergedConfig.metadata = { ...config.metadata }
98
+ }
99
+
100
+ return {
101
+ id: SemanticType.Phone,
102
+ config: mergedConfig,
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Default configuration for Phone semantic.
108
+ * Uses E.164 format with country code required and SMS verification.
109
+ */
110
+ export const DEFAULT_PHONE_CONFIG: PhoneConfig = {
111
+ requireCountryCode: true,
112
+ format: 'E.164',
113
+ allowExtension: false,
114
+ requireVerification: false,
115
+ verificationMethod: 'sms',
116
+ }
@@ -0,0 +1,158 @@
1
+ # Price Semantic Examples
2
+
3
+ This document provides examples of how to use the Price semantic with different configurations.
4
+
5
+ ## Basic Usage
6
+
7
+ ```typescript
8
+ import { createPriceSemantic, PRICE_PRESETS } from './Price.js'
9
+
10
+ // Simple USD decimal storage
11
+ const basicPrice = createPriceSemantic({
12
+ storageFormat: 'decimal',
13
+ defaultCurrency: 'USD',
14
+ decimalPlaces: 2,
15
+ allowNegative: false,
16
+ })
17
+
18
+ // Using a preset
19
+ const usdDecimal = PRICE_PRESETS.USD_DECIMAL
20
+ ```
21
+
22
+ ## Configuration Examples
23
+
24
+ ### 1. Simple E-commerce Store (USD only)
25
+
26
+ ```typescript
27
+ const ecommercePrice = createPriceSemantic({
28
+ storageFormat: 'decimal',
29
+ defaultCurrency: 'USD',
30
+ decimalPlaces: 2,
31
+ allowNegative: false,
32
+ })
33
+ ```
34
+
35
+ ### 2. International E-commerce (Multi-currency)
36
+
37
+ ```typescript
38
+ const internationalPrice = createPriceSemantic({
39
+ storageFormat: 'complex_object',
40
+ allowedCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD'],
41
+ decimalPlaces: 2,
42
+ allowNegative: false,
43
+ })
44
+ ```
45
+
46
+ ### 3. Financial System (High precision, allows negatives)
47
+
48
+ ```typescript
49
+ const financialPrice = createPriceSemantic({
50
+ storageFormat: 'integer_cents',
51
+ defaultCurrency: 'USD',
52
+ decimalPlaces: 4,
53
+ allowNegative: true,
54
+ })
55
+ ```
56
+
57
+ ### 4. Cryptocurrency Trading
58
+
59
+ ```typescript
60
+ const cryptoPrice = createPriceSemantic({
61
+ storageFormat: 'decimal',
62
+ defaultCurrency: 'BTC',
63
+ decimalPlaces: 8,
64
+ allowNegative: false,
65
+ })
66
+ ```
67
+
68
+ ## Storage Format Details
69
+
70
+ ### Decimal Format
71
+
72
+ - **Best for**: Simple single-currency applications
73
+ - **PostgreSQL Type**: DECIMAL(precision, scale)
74
+ - **Example Value**: 19.99
75
+ - **Pros**: Human-readable, direct calculations
76
+ - **Cons**: Currency must be stored separately if needed
77
+
78
+ ### Integer Cents Format
79
+
80
+ - **Best for**: Financial calculations requiring precision
81
+ - **PostgreSQL Type**: BIGINT
82
+ - **Example Value**: 1999 (represents $19.99)
83
+ - **Pros**: No floating-point errors, efficient storage
84
+ - **Cons**: Requires conversion for display
85
+
86
+ ### Complex Object Format
87
+
88
+ - **Best for**: Multi-currency applications
89
+ - **PostgreSQL Type**: JSONB
90
+ - **Example Value**: {"amount": 1999, "currency": "USD"}
91
+ - **Pros**: Stores amount and currency together, flexible
92
+ - **Cons**: More complex queries, larger storage
93
+
94
+ ## Database Schema Generation Examples
95
+
96
+ The runtime will generate appropriate PostgreSQL schemas based on the semantic configuration:
97
+
98
+ ### For Decimal Format
99
+
100
+ Based on `storageFormat: 'decimal'` configuration, the runtime should generate:
101
+
102
+ ```sql
103
+ CREATE TABLE products (
104
+ id SERIAL PRIMARY KEY,
105
+ name VARCHAR(255),
106
+ price DECIMAL(19, 2) NOT NULL,
107
+ currency CHAR(3) DEFAULT 'USD'
108
+ );
109
+
110
+ CREATE INDEX idx_products_price ON products(price);
111
+ ```
112
+
113
+ ### For Integer Cents Format
114
+
115
+ Based on `storageFormat: 'integer_cents'` configuration, the runtime should generate:
116
+
117
+ ```sql
118
+ CREATE TABLE products (
119
+ id SERIAL PRIMARY KEY,
120
+ name VARCHAR(255),
121
+ price_cents BIGINT NOT NULL,
122
+ currency CHAR(3) DEFAULT 'USD'
123
+ );
124
+
125
+ CREATE INDEX idx_products_price_cents ON products(price_cents);
126
+ ```
127
+
128
+ ### For Complex Object Format
129
+
130
+ Based on `storageFormat: 'complex_object'` configuration, the runtime should generate:
131
+
132
+ ```sql
133
+ CREATE TABLE products (
134
+ id SERIAL PRIMARY KEY,
135
+ name VARCHAR(255),
136
+ price JSONB NOT NULL
137
+ );
138
+
139
+ CREATE INDEX idx_products_price_amount ON products USING GIN ((price->>'amount'));
140
+ CREATE INDEX idx_products_price_currency ON products USING GIN ((price->>'currency'));
141
+ ```
142
+
143
+ ## Validation Examples
144
+
145
+ The Price semantic includes built-in validation:
146
+
147
+ ```typescript
148
+ import { validatePriceConfig } from './Price.js'
149
+
150
+ const config = {
151
+ storageFormat: 'decimal' as const,
152
+ // Missing defaultCurrency - this will cause an error
153
+ decimalPlaces: 2,
154
+ }
155
+
156
+ const errors = validatePriceConfig(config)
157
+ console.log(errors) // ['defaultCurrency is required when storageFormat is not complex_object']
158
+ ```
@@ -0,0 +1,180 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Supported storage formats for monetary values.
6
+ */
7
+ export type PriceStorageFormat = 'decimal' | 'integer_cents' | 'complex_object'
8
+
9
+ /**
10
+ * Configuration options for the Price semantic.
11
+ * Controls monetary value storage, validation, currency handling, and precision.
12
+ */
13
+ export interface PriceConfig {
14
+ /**
15
+ * How the monetary value is stored in the database.
16
+ *
17
+ * - 'decimal': Store as DECIMAL/NUMERIC type (e.g., 19.99)
18
+ * - 'integer_cents': Store as INTEGER in smallest currency unit (e.g., 1999 for $19.99)
19
+ * - 'complex_object': Store as JSON/JSONB with amount and currency (e.g., {"amount": 1999, "currency": "USD"})
20
+ */
21
+ storageFormat?: PriceStorageFormat
22
+
23
+ /**
24
+ * Default currency code (ISO 4217) when not specified.
25
+ * Required when using 'decimal' or 'integer_cents' format.
26
+ */
27
+ defaultCurrency?: string
28
+
29
+ /**
30
+ * List of allowed currency codes (ISO 4217).
31
+ * If not specified, all valid ISO 4217 currencies are allowed.
32
+ */
33
+ allowedCurrencies?: string[]
34
+
35
+ /**
36
+ * Number of decimal places for precision.
37
+ * Default: 2 (for most currencies like USD, EUR)
38
+ * Some currencies may need 0 (JPY, KRW) or 3 (BHD, KWD)
39
+ */
40
+ decimalPlaces?: number
41
+
42
+ /**
43
+ * Whether negative values are allowed (for refunds, discounts, etc.).
44
+ */
45
+ allowNegative?: boolean
46
+
47
+ /**
48
+ * Whether to automatically validate currency codes against ISO 4217.
49
+ */
50
+ validateCurrencyCode?: boolean
51
+
52
+ /**
53
+ * Custom metadata for the price field.
54
+ */
55
+ metadata?: Record<string, unknown>
56
+
57
+ /**
58
+ * Index signature to allow additional properties.
59
+ */
60
+ [key: string]: unknown
61
+ }
62
+
63
+ /**
64
+ * Type-safe configuration for Price semantic.
65
+ */
66
+ export interface AppliedPriceSemantic extends AppliedDataSemantic {
67
+ id: SemanticType.Price
68
+ config?: PriceConfig
69
+ }
70
+
71
+ /**
72
+ * Type guard to check if a semantic is a Price semantic.
73
+ */
74
+ export const isPriceSemantic = (semantic: AppliedDataSemantic): semantic is AppliedPriceSemantic => {
75
+ return semantic.id === SemanticType.Price
76
+ }
77
+
78
+ /**
79
+ * Helper function to create a Price semantic with configuration.
80
+ */
81
+ export const createPriceSemantic = (config: PriceConfig = {}): AppliedPriceSemantic => {
82
+ const mergedConfig = {
83
+ ...DEFAULT_PRICE_CONFIG,
84
+ ...config,
85
+ }
86
+
87
+ // Merge metadata separately
88
+ if (config.metadata) {
89
+ mergedConfig.metadata = { ...config.metadata }
90
+ }
91
+
92
+ return {
93
+ id: SemanticType.Price,
94
+ config: mergedConfig,
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Default configuration for Price semantic.
100
+ * Optimized for common e-commerce use cases with USD currency.
101
+ */
102
+ export const DEFAULT_PRICE_CONFIG: PriceConfig = {
103
+ storageFormat: 'decimal',
104
+ defaultCurrency: 'USD',
105
+ decimalPlaces: 2,
106
+ allowNegative: false,
107
+ validateCurrencyCode: true,
108
+ }
109
+
110
+ /**
111
+ * Predefined configurations for common use cases.
112
+ */
113
+ export const PRICE_PRESETS = {
114
+ /**
115
+ * Simple USD decimal storage - good for most e-commerce sites.
116
+ */
117
+ USD_DECIMAL: createPriceSemantic({
118
+ storageFormat: 'decimal',
119
+ defaultCurrency: 'USD',
120
+ decimalPlaces: 2,
121
+ allowNegative: false,
122
+ }),
123
+
124
+ /**
125
+ * Integer cents storage - avoids floating point issues, good for financial calculations.
126
+ */
127
+ USD_CENTS: createPriceSemantic({
128
+ storageFormat: 'integer_cents',
129
+ defaultCurrency: 'USD',
130
+ decimalPlaces: 2,
131
+ allowNegative: false,
132
+ }),
133
+
134
+ /**
135
+ * Multi-currency support with complex object storage.
136
+ */
137
+ MULTI_CURRENCY: createPriceSemantic({
138
+ storageFormat: 'complex_object',
139
+ allowedCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'CAD'],
140
+ decimalPlaces: 2,
141
+ allowNegative: false,
142
+ }),
143
+
144
+ /**
145
+ * Configuration that allows negative values for refunds, discounts, etc.
146
+ */
147
+ WITH_NEGATIVES: createPriceSemantic({
148
+ storageFormat: 'decimal',
149
+ defaultCurrency: 'USD',
150
+ decimalPlaces: 2,
151
+ allowNegative: true,
152
+ }),
153
+
154
+ /**
155
+ * High-precision configuration for cryptocurrency or precious metals.
156
+ */
157
+ HIGH_PRECISION: createPriceSemantic({
158
+ storageFormat: 'decimal',
159
+ defaultCurrency: 'BTC',
160
+ decimalPlaces: 8,
161
+ allowNegative: false,
162
+ }),
163
+ } as const
164
+
165
+ /**
166
+ * Helper function to validate a price configuration.
167
+ */
168
+ export const validatePriceConfig = (config: PriceConfig): string[] => {
169
+ const errors: string[] = []
170
+
171
+ if (config.storageFormat !== 'complex_object' && !config.defaultCurrency) {
172
+ errors.push('defaultCurrency is required when storageFormat is not complex_object')
173
+ }
174
+
175
+ if (config.decimalPlaces !== undefined && config.decimalPlaces < 0) {
176
+ errors.push('decimalPlaces must be non-negative')
177
+ }
178
+
179
+ return errors
180
+ }
@@ -0,0 +1,98 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration options for the PublicUniqueName (slug) semantic.
6
+ * Controls how slugs are generated and validated.
7
+ */
8
+ export interface PublicUniqueNameConfig {
9
+ /**
10
+ * The source field to generate the slug from (e.g., 'title').
11
+ * Default to the field that is annotated with the Title semantic.
12
+ */
13
+ sourceField?: string
14
+ /**
15
+ * Separator character to use in the slug (default: '-').
16
+ */
17
+ separator?: string
18
+ /**
19
+ * Maximum length of the slug (default: 64).
20
+ */
21
+ maxLength?: number
22
+ /**
23
+ * Whether the slug is case sensitive (default: false).
24
+ */
25
+ caseSensitive?: boolean
26
+ /**
27
+ * Whether to allow Unicode characters in the slug (default: false).
28
+ */
29
+ allowUnicode?: boolean
30
+ /**
31
+ * Name of a custom generator function to use for slug creation.
32
+ */
33
+ customGenerator?: string
34
+ /**
35
+ * Whether to allow duplicate slugs (default: false).
36
+ */
37
+ allowDuplicates?: boolean
38
+ /**
39
+ * Fallback field to use if the source field is empty or invalid.
40
+ */
41
+ fallbackField?: string
42
+ /**
43
+ * Custom metadata for the slug field.
44
+ */
45
+ metadata?: Record<string, unknown>
46
+ /**
47
+ * Index signature to allow additional properties.
48
+ */
49
+ [key: string]: unknown
50
+ }
51
+
52
+ /**
53
+ * Type-safe configuration for PublicUniqueName semantic.
54
+ */
55
+ export interface AppliedPublicUniqueNameSemantic extends AppliedDataSemantic {
56
+ id: SemanticType.PublicUniqueName
57
+ config?: PublicUniqueNameConfig
58
+ }
59
+
60
+ /**
61
+ * Type guard to check if a semantic is a PublicUniqueName semantic.
62
+ */
63
+ export const isPublicUniqueNameSemantic = (
64
+ semantic: AppliedDataSemantic
65
+ ): semantic is AppliedPublicUniqueNameSemantic => {
66
+ return semantic.id === SemanticType.PublicUniqueName
67
+ }
68
+
69
+ /**
70
+ * Helper function to create a PublicUniqueName semantic with configuration.
71
+ */
72
+ export const createPublicUniqueNameSemantic = (
73
+ config: PublicUniqueNameConfig = {}
74
+ ): AppliedPublicUniqueNameSemantic => {
75
+ const mergedConfig = {
76
+ ...DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG,
77
+ ...config,
78
+ }
79
+ if (config.metadata) {
80
+ mergedConfig.metadata = { ...config.metadata }
81
+ }
82
+
83
+ return {
84
+ id: SemanticType.PublicUniqueName,
85
+ config: mergedConfig,
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Default configuration for PublicUniqueName semantic.
91
+ */
92
+ export const DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG: PublicUniqueNameConfig = {
93
+ separator: '-',
94
+ maxLength: 64,
95
+ caseSensitive: false,
96
+ allowUnicode: false,
97
+ allowDuplicates: false,
98
+ }