@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.
- package/TESTING_READY.md +114 -0
- package/TESTING_SETUP.md +198 -0
- package/build/src/modeling/Semantics.d.ts +126 -2
- package/build/src/modeling/Semantics.d.ts.map +1 -1
- package/build/src/modeling/Semantics.js +281 -13
- package/build/src/modeling/Semantics.js.map +1 -1
- package/build/src/modeling/definitions/Calculated.d.ts +54 -0
- package/build/src/modeling/definitions/Calculated.d.ts.map +1 -0
- package/build/src/modeling/definitions/Calculated.js +31 -0
- package/build/src/modeling/definitions/Calculated.js.map +1 -0
- package/build/src/modeling/definitions/Categories.d.ts +60 -0
- package/build/src/modeling/definitions/Categories.d.ts.map +1 -0
- package/build/src/modeling/definitions/Categories.js +33 -0
- package/build/src/modeling/definitions/Categories.js.map +1 -0
- package/build/src/modeling/definitions/Derived.d.ts +54 -0
- package/build/src/modeling/definitions/Derived.d.ts.map +1 -0
- package/build/src/modeling/definitions/Derived.js +31 -0
- package/build/src/modeling/definitions/Derived.js.map +1 -0
- package/build/src/modeling/definitions/Description.d.ts +36 -0
- package/build/src/modeling/definitions/Description.d.ts.map +1 -0
- package/build/src/modeling/definitions/Description.js +28 -0
- package/build/src/modeling/definitions/Description.js.map +1 -0
- package/build/src/modeling/definitions/Email.d.ts +66 -0
- package/build/src/modeling/definitions/Email.d.ts.map +1 -0
- package/build/src/modeling/definitions/Email.js +33 -0
- package/build/src/modeling/definitions/Email.js.map +1 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.d.ts +212 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.d.ts.map +1 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.js +129 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.js.map +1 -0
- package/build/src/modeling/definitions/HTML.d.ts +88 -0
- package/build/src/modeling/definitions/HTML.d.ts.map +1 -0
- package/build/src/modeling/definitions/HTML.js +42 -0
- package/build/src/modeling/definitions/HTML.js.map +1 -0
- package/build/src/modeling/definitions/Markdown.d.ts +84 -0
- package/build/src/modeling/definitions/Markdown.d.ts.map +1 -0
- package/build/src/modeling/definitions/Markdown.js +41 -0
- package/build/src/modeling/definitions/Markdown.js.map +1 -0
- package/build/src/modeling/definitions/Password.d.ts +112 -0
- package/build/src/modeling/definitions/Password.d.ts.map +1 -0
- package/build/src/modeling/definitions/Password.js +57 -0
- package/build/src/modeling/definitions/Password.js.map +1 -0
- package/build/src/modeling/definitions/Phone.d.ts +83 -0
- package/build/src/modeling/definitions/Phone.d.ts.map +1 -0
- package/build/src/modeling/definitions/Phone.js +39 -0
- package/build/src/modeling/definitions/Phone.js.map +1 -0
- package/build/src/modeling/definitions/Price.d.ts +102 -0
- package/build/src/modeling/definitions/Price.d.ts.map +1 -0
- package/build/src/modeling/definitions/Price.js +99 -0
- package/build/src/modeling/definitions/Price.js.map +1 -0
- package/build/src/modeling/definitions/PublicUniqueName.d.ts +69 -0
- package/build/src/modeling/definitions/PublicUniqueName.d.ts.map +1 -0
- package/build/src/modeling/definitions/PublicUniqueName.js +34 -0
- package/build/src/modeling/definitions/PublicUniqueName.js.map +1 -0
- package/build/src/modeling/definitions/SKU.d.ts +127 -0
- package/build/src/modeling/definitions/SKU.d.ts.map +1 -0
- package/build/src/modeling/definitions/SKU.js +142 -0
- package/build/src/modeling/definitions/SKU.js.map +1 -0
- package/build/src/modeling/definitions/Status.d.ts +150 -0
- package/build/src/modeling/definitions/Status.d.ts.map +1 -0
- package/build/src/modeling/definitions/Status.js +60 -0
- package/build/src/modeling/definitions/Status.js.map +1 -0
- package/build/src/modeling/definitions/Summary.d.ts +53 -0
- package/build/src/modeling/definitions/Summary.d.ts.map +1 -0
- package/build/src/modeling/definitions/Summary.js +50 -0
- package/build/src/modeling/definitions/Summary.js.map +1 -0
- package/build/src/modeling/definitions/Tags.d.ts +52 -0
- package/build/src/modeling/definitions/Tags.d.ts.map +1 -0
- package/build/src/modeling/definitions/Tags.js +32 -0
- package/build/src/modeling/definitions/Tags.js.map +1 -0
- package/build/src/modeling/definitions/URL.d.ts +68 -0
- package/build/src/modeling/definitions/URL.d.ts.map +1 -0
- package/build/src/modeling/definitions/URL.js +37 -0
- package/build/src/modeling/definitions/URL.js.map +1 -0
- package/build/src/modeling/validation/semantic_validation.d.ts +4 -0
- package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -1
- package/build/src/modeling/validation/semantic_validation.js +32 -1
- package/build/src/modeling/validation/semantic_validation.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/modeling/Semantics.ts +297 -14
- package/src/modeling/definitions/Calculated.ts +76 -0
- package/src/modeling/definitions/Categories.ts +84 -0
- package/src/modeling/definitions/Derived.ts +76 -0
- package/src/modeling/definitions/Description.ts +55 -0
- package/src/modeling/definitions/Email.ts +90 -0
- package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
- package/src/modeling/definitions/HTML.ts +121 -0
- package/src/modeling/definitions/Markdown.ts +116 -0
- package/src/modeling/definitions/Password.ts +156 -0
- package/src/modeling/definitions/Phone.ts +116 -0
- package/src/modeling/definitions/Price.examples.md +158 -0
- package/src/modeling/definitions/Price.ts +180 -0
- package/src/modeling/definitions/PublicUniqueName.ts +98 -0
- package/src/modeling/definitions/SKU.examples.md +230 -0
- package/src/modeling/definitions/SKU.ts +254 -0
- package/src/modeling/definitions/Status.ts +227 -0
- package/src/modeling/definitions/Summary.ts +73 -0
- package/src/modeling/definitions/Tags.ts +75 -0
- package/src/modeling/definitions/URL.ts +96 -0
- package/src/modeling/validation/semantic_validation.ts +35 -1
- package/tests/example-test-setup.ts +133 -0
- package/tests/template-node.spec.ts +75 -0
- package/tests/test-utils.ts +293 -0
- package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
- package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
- package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
- package/tests/unit/modeling/definitions/description.spec.ts +38 -0
- package/tests/unit/modeling/definitions/email.spec.ts +38 -0
- package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
- package/tests/unit/modeling/definitions/html.spec.ts +38 -0
- package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
- package/tests/unit/modeling/definitions/password.spec.ts +347 -0
- package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
- package/tests/unit/modeling/definitions/price.spec.ts +465 -0
- package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
- package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
- package/tests/unit/modeling/definitions/status.spec.ts +37 -0
- package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
- package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
- package/tests/unit/modeling/definitions/url.spec.ts +38 -0
- package/tests/unit/modeling/domain_property.spec.ts +106 -0
- package/tests/unit/modeling/domain_validation.spec.ts +5 -5
- package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
- 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
|
+
}
|