@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,230 @@
1
+ # SKU Semantic Examples
2
+
3
+ This document provides examples of how to use the SKU semantic with different configurations.
4
+
5
+ ## Basic Usage
6
+
7
+ ```typescript
8
+ import { createSKUSemantic, SKU_PRESETS } from './SKU.js'
9
+
10
+ // Simple SKU with default settings
11
+ const basicSKU = createSKUSemantic()
12
+
13
+ // Using a preset for product catalogs
14
+ const productSKU = SKU_PRESETS.PRODUCT_STANDARD
15
+ ```
16
+
17
+ ## Configuration Examples
18
+
19
+ ### 1. Standard Product Catalog
20
+
21
+ ```typescript
22
+ const productCatalogSKU = createSKUSemantic({
23
+ validationMode: 'strict',
24
+ caseMode: 'uppercase',
25
+ prefix: 'PROD-',
26
+ enforceUniqueness: true,
27
+ validateReservedWords: true,
28
+ })
29
+ ```
30
+
31
+ ### 2. Simple Inventory System
32
+
33
+ ```typescript
34
+ const simpleSKU = createSKUSemantic({
35
+ validationMode: 'lenient',
36
+ caseMode: 'preserve',
37
+ enforceUniqueness: true,
38
+ })
39
+ ```
40
+
41
+ ### 3. Auto-generating SKUs
42
+
43
+ ```typescript
44
+ const autoGeneratedSKU = createSKUSemantic({
45
+ validationMode: 'strict',
46
+ caseMode: 'uppercase',
47
+ autoGenerate: true,
48
+ autoGenerateSource: 'name', // Generate from product name
49
+ enforceUniqueness: true,
50
+ })
51
+ ```
52
+
53
+ ### 4. Custom Pattern Validation
54
+
55
+ ```typescript
56
+ const customPatternSKU = createSKUSemantic({
57
+ validationMode: 'custom',
58
+ customPattern: '^[A-Z]{3}-\\d{4}-[A-Z]{2}$', // Format: ABC-1234-XY
59
+ caseMode: 'uppercase',
60
+ enforceUniqueness: true,
61
+ })
62
+ ```
63
+
64
+ ### 5. Flexible Multi-category System
65
+
66
+ ```typescript
67
+ const flexibleSKU = createSKUSemantic({
68
+ validationMode: 'lenient',
69
+ caseMode: 'preserve',
70
+ enforceUniqueness: true,
71
+ validateReservedWords: false, // Allow more flexibility
72
+ })
73
+ ```
74
+
75
+ ## Storage Format Details
76
+
77
+ ### Database Schema Generation
78
+
79
+ The runtime will generate appropriate database schemas with unique constraints:
80
+
81
+ ```sql
82
+ CREATE TABLE products (
83
+ id SERIAL PRIMARY KEY,
84
+ name VARCHAR(255),
85
+ sku VARCHAR(50) UNIQUE NOT NULL,
86
+ description TEXT
87
+ );
88
+
89
+ -- Automatically creates unique index
90
+ CREATE UNIQUE INDEX idx_products_sku ON products(sku);
91
+
92
+ -- For case-insensitive uniqueness (depending on configuration)
93
+ CREATE UNIQUE INDEX idx_products_sku_lower ON products(LOWER(sku));
94
+ ```
95
+
96
+ ### Case Transformation Examples
97
+
98
+ ```typescript
99
+ // Uppercase transformation
100
+ const uppercaseSKU = createSKUSemantic({ caseMode: 'uppercase' })
101
+ // Input: "prod-123" → Stored: "PROD-123"
102
+
103
+ // Lowercase transformation
104
+ const lowercaseSKU = createSKUSemantic({ caseMode: 'lowercase' })
105
+ // Input: "PROD-123" → Stored: "prod-123"
106
+
107
+ // Preserve original case
108
+ const preserveCaseSKU = createSKUSemantic({ caseMode: 'preserve' })
109
+ // Input: "Prod-123" → Stored: "Prod-123"
110
+ ```
111
+
112
+ ## Validation Examples
113
+
114
+ ### Strict Validation
115
+
116
+ ```typescript
117
+ const strictSKU = createSKUSemantic({ validationMode: 'strict' })
118
+
119
+ // Valid SKUs
120
+ strictSKU.validate('PRODUCT123') // ✓ Valid
121
+ strictSKU.validate('PROD-123') // ✓ Valid
122
+ strictSKU.validate('PROD_123') // ✓ Valid
123
+
124
+ // Invalid SKUs
125
+ strictSKU.validate('PROD.123') // ✗ Contains dot
126
+ strictSKU.validate('PROD@123') // ✗ Contains special character
127
+ strictSKU.validate('PROD 123') // ✗ Contains space
128
+ ```
129
+
130
+ ### Lenient Validation
131
+
132
+ ```typescript
133
+ const lenientSKU = createSKUSemantic({ validationMode: 'lenient' })
134
+
135
+ // Valid SKUs
136
+ lenientSKU.validate('PRODUCT123') // ✓ Valid
137
+ lenientSKU.validate('PROD-123') // ✓ Valid
138
+ lenientSKU.validate('PROD.123') // ✓ Valid (dots allowed)
139
+
140
+ // Invalid SKUs
141
+ lenientSKU.validate('PROD@123') // ✗ Special characters not allowed
142
+ lenientSKU.validate('PROD#123') // ✗ Hash not allowed
143
+ ```
144
+
145
+ ### Custom Pattern Validation
146
+
147
+ ```typescript
148
+ const customSKU = createSKUSemantic({
149
+ validationMode: 'custom',
150
+ customPattern: '^[A-Z]{2}\\d{4}$' // Two letters followed by four digits
151
+ })
152
+
153
+ // Valid SKUs
154
+ customSKU.validate('AB1234') // ✓ Valid
155
+ customSKU.validate('XY9999') // ✓ Valid
156
+
157
+ // Invalid SKUs
158
+ customSKU.validate('ABC123') // ✗ Wrong format
159
+ customSKU.validate('ab1234') // ✗ Lowercase letters
160
+ customSKU.validate('A1234') // ✗ Only one letter
161
+ ```
162
+
163
+ ## Reserved Words
164
+
165
+ The SKU semantic includes protection against common reserved values:
166
+
167
+ ```typescript
168
+ const skuConfig = createSKUSemantic({
169
+ validateReservedWords: true,
170
+ reservedValues: ['ADMIN', 'TEST', 'NULL', 'DEFAULT', 'SAMPLE']
171
+ })
172
+
173
+ // These will be rejected
174
+ skuConfig.validate('ADMIN') // ✗ Reserved word
175
+ skuConfig.validate('test') // ✗ Reserved word (case-insensitive)
176
+ skuConfig.validate('NULL') // ✗ Reserved word
177
+ ```
178
+
179
+ ## Auto-generation Behavior
180
+
181
+ When `autoGenerate` is enabled, the runtime automatically creates SKUs:
182
+
183
+ ```typescript
184
+ const autoSKU = createSKUSemantic({
185
+ autoGenerate: true,
186
+ autoGenerateSource: 'name',
187
+ prefix: 'PROD-',
188
+ caseMode: 'uppercase'
189
+ })
190
+
191
+ // For a product named "Blue Widget"
192
+ // Generated SKU might be: "PROD-BLUE-WIDGET" or "PROD-BLUE-WIDGET-001"
193
+ ```
194
+
195
+ ## Error Handling
196
+
197
+ ```typescript
198
+ import { validateSKUConfig, validateSKUValue } from './SKU.js'
199
+
200
+ // Validate configuration
201
+ const configErrors = validateSKUConfig({
202
+ validationMode: 'custom' // Invalid: missing customPattern
203
+ })
204
+ console.log(configErrors) // ['customPattern is required when validationMode is custom']
205
+
206
+ // Validate SKU values
207
+ const valueErrors = validateSKUValue('TEST')
208
+ console.log(valueErrors) // ['SKU cannot use reserved value: TEST']
209
+ ```
210
+
211
+ ## Integration with Domain Entities
212
+
213
+ ```typescript
214
+ // In your domain model
215
+ const productEntity = domain.addEntity({
216
+ name: 'Product',
217
+ properties: [
218
+ {
219
+ name: 'sku',
220
+ type: 'string',
221
+ semantics: [createSKUSemantic({
222
+ validationMode: 'strict',
223
+ caseMode: 'uppercase',
224
+ prefix: 'PROD-',
225
+ enforceUniqueness: true
226
+ })]
227
+ }
228
+ ]
229
+ })
230
+ ```
@@ -0,0 +1,254 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Supported SKU validation modes.
6
+ */
7
+ export type SKUValidationMode = 'strict' | 'lenient' | 'custom'
8
+
9
+ /**
10
+ * Supported SKU case transformation modes.
11
+ */
12
+ export type SKUCaseMode = 'uppercase' | 'lowercase' | 'preserve'
13
+
14
+ /**
15
+ * Configuration options for the SKU semantic.
16
+ * Controls SKU validation, formatting, uniqueness enforcement, and pattern matching.
17
+ */
18
+ export interface SKUConfig {
19
+ /**
20
+ * The validation mode for SKU format.
21
+ *
22
+ * - 'strict': Enforces alphanumeric characters with optional hyphens/underscores
23
+ * - 'lenient': Allows more special characters but still validates basic format
24
+ * - 'custom': Uses custom regex pattern defined in `customPattern`
25
+ */
26
+ validationMode?: SKUValidationMode
27
+
28
+ /**
29
+ * Custom regex pattern for SKU validation when using 'custom' validation mode.
30
+ * Only used when validationMode is 'custom'.
31
+ */
32
+ customPattern?: string
33
+
34
+ /**
35
+ * Case transformation to apply to SKUs.
36
+ * - 'uppercase': Convert to uppercase (recommended for consistency)
37
+ * - 'lowercase': Convert to lowercase
38
+ * - 'preserve': Keep original case
39
+ */
40
+ caseMode?: SKUCaseMode
41
+
42
+ /**
43
+ * Prefix to automatically add to SKUs if not present.
44
+ * Useful for organizing SKUs by category (e.g., 'PROD-', 'SKU-').
45
+ */
46
+ prefix?: string
47
+
48
+ /**
49
+ * Whether to enforce global uniqueness across all entities.
50
+ * When true, creates unique database constraints.
51
+ * Default: true
52
+ */
53
+ enforceUniqueness?: boolean
54
+
55
+ /**
56
+ * Whether to auto-generate SKUs when not provided.
57
+ * When true, generates SKUs based on other fields or random values.
58
+ * Default: false
59
+ */
60
+ autoGenerate?: boolean
61
+
62
+ /**
63
+ * Field name to use as source for auto-generation.
64
+ * Only used when autoGenerate is true.
65
+ * If not specified, uses random generation.
66
+ */
67
+ autoGenerateSource?: string
68
+
69
+ /**
70
+ * Whether to validate that SKU doesn't conflict with reserved words or patterns.
71
+ * Default: true
72
+ */
73
+ validateReservedWords?: boolean
74
+
75
+ /**
76
+ * List of reserved SKU values that cannot be used.
77
+ * Common examples: 'ADMIN', 'TEST', 'NULL', 'DEFAULT'
78
+ */
79
+ reservedValues?: string[]
80
+
81
+ /**
82
+ * Custom metadata for the SKU field.
83
+ */
84
+ metadata?: Record<string, unknown>
85
+
86
+ /**
87
+ * Index signature to allow additional properties.
88
+ */
89
+ [key: string]: unknown
90
+ }
91
+
92
+ /**
93
+ * Type-safe configuration for SKU semantic.
94
+ */
95
+ export interface AppliedSKUSemantic extends AppliedDataSemantic {
96
+ id: SemanticType.SKU
97
+ config?: SKUConfig
98
+ }
99
+
100
+ /**
101
+ * Type guard to check if a semantic is a SKU semantic.
102
+ */
103
+ export const isSKUSemantic = (semantic: AppliedDataSemantic): semantic is AppliedSKUSemantic => {
104
+ return semantic.id === SemanticType.SKU
105
+ }
106
+
107
+ /**
108
+ * Helper function to create a SKU semantic with configuration.
109
+ */
110
+ export const createSKUSemantic = (config: SKUConfig = {}): AppliedSKUSemantic => {
111
+ const mergedConfig = {
112
+ ...DEFAULT_SKU_CONFIG,
113
+ ...config,
114
+ }
115
+
116
+ // Merge metadata separately
117
+ if (config.metadata) {
118
+ mergedConfig.metadata = { ...config.metadata }
119
+ }
120
+
121
+ return {
122
+ id: SemanticType.SKU,
123
+ config: mergedConfig,
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Default configuration for SKU semantic.
129
+ * Optimized for common product catalog use cases.
130
+ */
131
+ export const DEFAULT_SKU_CONFIG: SKUConfig = {
132
+ validationMode: 'strict',
133
+ caseMode: 'uppercase',
134
+ enforceUniqueness: true,
135
+ autoGenerate: false,
136
+ validateReservedWords: true,
137
+ reservedValues: ['ADMIN', 'TEST', 'NULL', 'DEFAULT', 'UNDEFINED', 'SAMPLE', 'DEMO'],
138
+ }
139
+
140
+ /**
141
+ * Predefined configurations for common use cases.
142
+ */
143
+ export const SKU_PRESETS = {
144
+ /**
145
+ * Standard product SKU with strict validation and uppercase formatting.
146
+ */
147
+ PRODUCT_STANDARD: createSKUSemantic({
148
+ validationMode: 'strict',
149
+ caseMode: 'uppercase',
150
+ prefix: 'PROD-',
151
+ enforceUniqueness: true,
152
+ }),
153
+
154
+ /**
155
+ * Simple SKU configuration for basic catalogs.
156
+ */
157
+ SIMPLE: createSKUSemantic({
158
+ validationMode: 'lenient',
159
+ caseMode: 'preserve',
160
+ enforceUniqueness: true,
161
+ }),
162
+
163
+ /**
164
+ * Auto-generating SKU from product name.
165
+ */
166
+ AUTO_GENERATE: createSKUSemantic({
167
+ validationMode: 'strict',
168
+ caseMode: 'uppercase',
169
+ autoGenerate: true,
170
+ autoGenerateSource: 'name',
171
+ enforceUniqueness: true,
172
+ }),
173
+
174
+ /**
175
+ * Flexible SKU for variable product types.
176
+ */
177
+ FLEXIBLE: createSKUSemantic({
178
+ validationMode: 'lenient',
179
+ caseMode: 'preserve',
180
+ enforceUniqueness: true,
181
+ validateReservedWords: false,
182
+ }),
183
+ } as const
184
+
185
+ /**
186
+ * Helper function to validate a SKU configuration.
187
+ */
188
+ export const validateSKUConfig = (config: SKUConfig): string[] => {
189
+ const errors: string[] = []
190
+
191
+ if (config.validationMode === 'custom' && !config.customPattern) {
192
+ errors.push('customPattern is required when validationMode is custom')
193
+ }
194
+
195
+ if (config.customPattern) {
196
+ try {
197
+ new RegExp(config.customPattern)
198
+ } catch {
199
+ errors.push('customPattern must be a valid regular expression')
200
+ }
201
+ }
202
+
203
+ if (config.prefix && config.prefix.length === 0) {
204
+ errors.push('prefix cannot be empty string')
205
+ }
206
+
207
+ if (config.autoGenerateSource && !config.autoGenerate) {
208
+ errors.push('autoGenerate must be true when autoGenerateSource is specified')
209
+ }
210
+
211
+ return errors
212
+ }
213
+
214
+ /**
215
+ * Helper function to validate SKU value against configuration.
216
+ */
217
+ export const validateSKUValue = (value: string, config: SKUConfig = DEFAULT_SKU_CONFIG): string[] => {
218
+ const errors: string[] = []
219
+ const mergedConfig = { ...DEFAULT_SKU_CONFIG, ...config }
220
+
221
+ if (!value || typeof value !== 'string') {
222
+ errors.push('SKU value must be a non-empty string')
223
+ return errors
224
+ }
225
+
226
+ if (mergedConfig.validateReservedWords && mergedConfig.reservedValues) {
227
+ const normalizedValue = value.toUpperCase()
228
+ if (mergedConfig.reservedValues.some((reserved) => reserved.toUpperCase() === normalizedValue)) {
229
+ errors.push(`SKU cannot use reserved value: ${value}`)
230
+ }
231
+ }
232
+
233
+ // Validation pattern checks
234
+ if (mergedConfig.validationMode === 'strict') {
235
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) {
236
+ errors.push('SKU can only contain alphanumeric characters, hyphens, and underscores')
237
+ }
238
+ } else if (mergedConfig.validationMode === 'lenient') {
239
+ if (!/^[A-Za-z0-9_.-]+$/.test(value)) {
240
+ errors.push('SKU contains invalid characters')
241
+ }
242
+ } else if (mergedConfig.validationMode === 'custom' && mergedConfig.customPattern) {
243
+ try {
244
+ const regex = new RegExp(mergedConfig.customPattern)
245
+ if (!regex.test(value)) {
246
+ errors.push('SKU does not match the required pattern')
247
+ }
248
+ } catch {
249
+ errors.push('Invalid custom pattern configuration')
250
+ }
251
+ }
252
+
253
+ return errors
254
+ }
@@ -0,0 +1,227 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration options for the Status semantic.
6
+ * These options control state management and workflow behavior.
7
+ */
8
+ export interface StatusConfig {
9
+ /**
10
+ * Allowed states for this status field.
11
+ * Only required when the field doesn't have enum values defined in the schema.
12
+ * If enum values are defined in DomainProperty > Schema, those values will be used instead.
13
+ */
14
+ allowedStates?: string[]
15
+
16
+ /**
17
+ * Default state when creating new records.
18
+ * Must be one of the allowedStates (if specified) or enum values from the schema.
19
+ */
20
+ defaultState: string
21
+
22
+ /**
23
+ * State transitions configuration.
24
+ * Maps from current state to array of allowed next states.
25
+ */
26
+ transitions?: Record<string, string[]>
27
+
28
+ /**
29
+ * State-specific behaviors and permissions.
30
+ */
31
+ stateBehaviors?: Record<
32
+ string,
33
+ {
34
+ /**
35
+ * Whether records in this state are publicly visible.
36
+ * Defaults to true if not specified.
37
+ */
38
+ isPublic?: boolean
39
+
40
+ /**
41
+ * Whether records in this state can be edited.
42
+ * Defaults to true if not specified.
43
+ */
44
+ isEditable?: boolean
45
+
46
+ /**
47
+ * Whether records in this state can be deleted.
48
+ * Defaults to false if not specified.
49
+ */
50
+ isDeletable?: boolean
51
+
52
+ /**
53
+ * Whether this state requires approval before transition.
54
+ * Defaults to false if not specified.
55
+ */
56
+ requiresApproval?: boolean
57
+
58
+ /**
59
+ * Custom display name for this state.
60
+ */
61
+ displayName?: string
62
+
63
+ /**
64
+ * Custom description for this state.
65
+ */
66
+ description?: string
67
+
68
+ /**
69
+ * Color or visual indicator for this state.
70
+ */
71
+ color?: string
72
+
73
+ /**
74
+ * Icon for this state.
75
+ */
76
+ icon?: string
77
+ }
78
+ >
79
+
80
+ /**
81
+ * Workflow configuration for status management.
82
+ */
83
+ workflow?: {
84
+ /**
85
+ * Whether status changes require approval.
86
+ * Defaults to false if not specified.
87
+ */
88
+ requiresApproval?: boolean
89
+
90
+ /**
91
+ * Roles that can approve status changes.
92
+ * Required if requiresApproval is true.
93
+ */
94
+ approvalRoles?: string[]
95
+
96
+ /**
97
+ * Whether to send notifications on status changes.
98
+ * Defaults to false if not specified.
99
+ */
100
+ sendNotifications?: boolean
101
+
102
+ /**
103
+ * Notification channels to use.
104
+ */
105
+ notificationChannels?: ('email' | 'sms' | 'push' | 'webhook')[]
106
+
107
+ /**
108
+ * Whether to log status change history.
109
+ * Defaults to true if not specified.
110
+ */
111
+ logHistory?: boolean
112
+
113
+ /**
114
+ * Whether to allow bulk status changes.
115
+ * Defaults to false if not specified.
116
+ */
117
+ allowBulkChanges?: boolean
118
+ }
119
+
120
+ /**
121
+ * Auto-transitions based on conditions.
122
+ */
123
+ autoTransitions?: {
124
+ /**
125
+ * Current state that triggers the auto-transition.
126
+ */
127
+ from: string
128
+
129
+ /**
130
+ * Target state to transition to.
131
+ */
132
+ to: string
133
+
134
+ /**
135
+ * Condition that triggers the transition.
136
+ * Examples: "after 24 hours", "when approved", "when payment received"
137
+ */
138
+ condition: string
139
+
140
+ /**
141
+ * Whether this auto-transition requires approval.
142
+ * Defaults to false if not specified.
143
+ */
144
+ requiresApproval?: boolean
145
+ }[]
146
+
147
+ /**
148
+ * Custom metadata for the status field.
149
+ */
150
+ metadata?: Record<string, unknown>
151
+
152
+ /**
153
+ * Index signature to allow additional properties.
154
+ */
155
+ [key: string]: unknown
156
+ }
157
+
158
+ /**
159
+ * Type-safe configuration for Status semantic.
160
+ */
161
+ export interface AppliedStatusSemantic extends AppliedDataSemantic {
162
+ id: SemanticType.Status
163
+ config?: StatusConfig
164
+ }
165
+
166
+ /**
167
+ * Type guard to check if a semantic is a Status semantic.
168
+ */
169
+ export const isStatusSemantic = (semantic: AppliedDataSemantic): semantic is AppliedStatusSemantic => {
170
+ return semantic.id === SemanticType.Status
171
+ }
172
+
173
+ /**
174
+ * Helper function to create a Status semantic with configuration.
175
+ */
176
+ export const createStatusSemantic = (config: Partial<StatusConfig> = {}): AppliedStatusSemantic => {
177
+ const mergedConfig = {
178
+ ...DEFAULT_STATUS_CONFIG,
179
+ ...config,
180
+ // Deep merge nested objects
181
+ stateBehaviors: config.stateBehaviors
182
+ ? { ...DEFAULT_STATUS_CONFIG.stateBehaviors, ...config.stateBehaviors }
183
+ : DEFAULT_STATUS_CONFIG.stateBehaviors,
184
+ workflow: config.workflow
185
+ ? { ...DEFAULT_STATUS_CONFIG.workflow, ...config.workflow }
186
+ : DEFAULT_STATUS_CONFIG.workflow,
187
+ }
188
+
189
+ if (config.metadata) {
190
+ mergedConfig.metadata = { ...config.metadata }
191
+ }
192
+
193
+ return {
194
+ id: SemanticType.Status,
195
+ config: mergedConfig,
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Default configuration for Status semantic.
201
+ */
202
+ export const DEFAULT_STATUS_CONFIG: StatusConfig = {
203
+ defaultState: 'draft',
204
+ stateBehaviors: {
205
+ draft: {
206
+ isPublic: false,
207
+ isEditable: true,
208
+ isDeletable: true,
209
+ },
210
+ published: {
211
+ isPublic: true,
212
+ isEditable: true,
213
+ isDeletable: false,
214
+ },
215
+ archived: {
216
+ isPublic: false,
217
+ isEditable: false,
218
+ isDeletable: false,
219
+ },
220
+ },
221
+ workflow: {
222
+ requiresApproval: false,
223
+ sendNotifications: false,
224
+ logHistory: true,
225
+ allowBulkChanges: false,
226
+ },
227
+ }