mojulo 0.0.0 → 0.1.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 (121) hide show
  1. package/README.md +54 -4
  2. package/lib/audit-logger-new.js +11 -0
  3. package/lib/auth/gate.js +25 -0
  4. package/lib/auth/service.js +17 -0
  5. package/lib/auth/session.js +63 -0
  6. package/lib/builder/chat-processor.js +607 -0
  7. package/lib/builder/composer-bridge.js +82 -0
  8. package/lib/builder/evaluator.js +159 -0
  9. package/lib/builder/executor.js +252 -0
  10. package/lib/builder/index.js +48 -0
  11. package/lib/builder/session.js +248 -0
  12. package/lib/builder/system-prompt.js +422 -0
  13. package/lib/builder/tone-presets.js +75 -0
  14. package/lib/builder/tool-executors.js +1527 -0
  15. package/lib/builder/tools.js +338 -0
  16. package/lib/builder/validators.js +239 -0
  17. package/lib/composer/composer.js +225 -0
  18. package/lib/composer/index.js +40 -0
  19. package/lib/composer/protocols/00_base.txt +19 -0
  20. package/lib/composer/protocols/01_knowledge.txt +9 -0
  21. package/lib/composer/protocols/02_form-gathering.txt +32 -0
  22. package/lib/composer/protocols/03_appointments.txt +16 -0
  23. package/lib/composer/protocols/04_triage.txt +15 -0
  24. package/lib/composer/protocols/05_optical-read.txt +22 -0
  25. package/lib/composer/response-builder.js +98 -0
  26. package/lib/config-builder.js +650 -0
  27. package/lib/db/ids.js +10 -0
  28. package/lib/db/index.js +179 -0
  29. package/lib/db/repositories/apiKeys.js +72 -0
  30. package/lib/db/repositories/auditLogs.js +12 -0
  31. package/lib/db/repositories/botSpaces.js +12 -0
  32. package/lib/db/repositories/builderSessions.js +312 -0
  33. package/lib/db/repositories/deploymentEvents.js +12 -0
  34. package/lib/db/repositories/deployments.js +385 -0
  35. package/lib/db/repositories/documents.js +68 -0
  36. package/lib/db/repositories/mcpJobs.js +84 -0
  37. package/lib/deployers/bot-fleet.js +110 -0
  38. package/lib/deployers/bot-proxy.js +72 -0
  39. package/lib/deployers/build.js +89 -0
  40. package/lib/deployers/cloud-deploy.js +310 -0
  41. package/lib/deployers/docker.js +439 -0
  42. package/lib/deployers/fly.js +432 -0
  43. package/lib/deployers/index.js +38 -0
  44. package/lib/deployment-auth.js +36 -0
  45. package/lib/document-parser.js +171 -0
  46. package/lib/embedder/chunker.js +93 -0
  47. package/lib/embedder/local.js +101 -0
  48. package/lib/embedder/preview-rag.js +93 -0
  49. package/lib/envelope-schema.js +54 -0
  50. package/lib/fleet/scoped-sql.js +342 -0
  51. package/lib/form-schema-config/base.js +135 -0
  52. package/lib/form-schema-config/index.js +286 -0
  53. package/lib/form-schema-config/locales/af-ZA.js +153 -0
  54. package/lib/form-schema-config/locales/ar-AE.js +142 -0
  55. package/lib/form-schema-config/locales/ar-SA.js +164 -0
  56. package/lib/form-schema-config/locales/de-DE.js +152 -0
  57. package/lib/form-schema-config/locales/en-AU.js +161 -0
  58. package/lib/form-schema-config/locales/en-CA.js +115 -0
  59. package/lib/form-schema-config/locales/en-GB.js +132 -0
  60. package/lib/form-schema-config/locales/en-IN.js +219 -0
  61. package/lib/form-schema-config/locales/en-MY.js +171 -0
  62. package/lib/form-schema-config/locales/en-NG.js +198 -0
  63. package/lib/form-schema-config/locales/en-PH.js +186 -0
  64. package/lib/form-schema-config/locales/en-SG.js +153 -0
  65. package/lib/form-schema-config/locales/en-US.js +138 -0
  66. package/lib/form-schema-config/locales/es-ES.js +171 -0
  67. package/lib/form-schema-config/locales/es-MX.js +193 -0
  68. package/lib/form-schema-config/locales/fr-CA.js +138 -0
  69. package/lib/form-schema-config/locales/fr-FR.js +155 -0
  70. package/lib/form-schema-config/locales/hi-IN.js +219 -0
  71. package/lib/form-schema-config/locales/it-IT.js +157 -0
  72. package/lib/form-schema-config/locales/ja-JP.js +169 -0
  73. package/lib/form-schema-config/locales/ko-KR.js +140 -0
  74. package/lib/form-schema-config/locales/nl-NL.js +149 -0
  75. package/lib/form-schema-config/locales/pt-BR.js +168 -0
  76. package/lib/form-schema-config/locales/zh-CN.js +172 -0
  77. package/lib/form-schema-config/locales/zh-HK.js +142 -0
  78. package/lib/form-structure-schema.js +191 -0
  79. package/lib/llm-providers.js +828 -0
  80. package/lib/markdown.js +197 -0
  81. package/lib/mcp/catalysts/appointment-to-calendar.md +84 -0
  82. package/lib/mcp/catalysts/conversations-to-channel-digest.md +104 -0
  83. package/lib/mcp/catalysts/document-extract-to-store.md +92 -0
  84. package/lib/mcp/catalysts/knowledge-gap-miner.md +96 -0
  85. package/lib/mcp/catalysts/loader.js +144 -0
  86. package/lib/mcp/catalysts/qualify-lead-to-crm.md +83 -0
  87. package/lib/mcp/catalysts/scan-conversations-for-signal.md +92 -0
  88. package/lib/mcp/catalysts/submission-to-ticket.md +83 -0
  89. package/lib/mcp/catalysts/submissions-to-warehouse.md +103 -0
  90. package/lib/mcp/catalysts/weekly-submissions-digest.md +82 -0
  91. package/lib/mcp/jobs.js +64 -0
  92. package/lib/mcp/server.js +184 -0
  93. package/lib/mcp/session-binding.js +130 -0
  94. package/lib/mcp/tools/build.js +123 -0
  95. package/lib/mcp/tools/catalysts.js +477 -0
  96. package/lib/mcp/tools/context.js +325 -0
  97. package/lib/mcp/tools/fleet.js +391 -0
  98. package/lib/mcp/tools/jobs-tools.js +240 -0
  99. package/lib/mcp/tools/operate.js +314 -0
  100. package/lib/preview/build-preview-config.js +136 -0
  101. package/lib/rate-limiter.js +11 -0
  102. package/lib/resolve-api-key.js +142 -0
  103. package/lib/storage/index.js +40 -0
  104. package/messages/de.json +2136 -0
  105. package/messages/en.json +2136 -0
  106. package/messages/es.json +2136 -0
  107. package/messages/fr.json +2136 -0
  108. package/messages/it.json +2136 -0
  109. package/messages/ja.json +2136 -0
  110. package/messages/ko.json +2136 -0
  111. package/messages/nl.json +2136 -0
  112. package/messages/pl.json +2136 -0
  113. package/messages/pt.json +2136 -0
  114. package/messages/ru.json +2136 -0
  115. package/messages/uk.json +2136 -0
  116. package/messages/zh.json +2136 -0
  117. package/package.json +68 -5
  118. package/scripts/mcp-config.mjs +162 -0
  119. package/scripts/mcp-stdio-loader.mjs +42 -0
  120. package/scripts/mcp-stdio.mjs +108 -0
  121. package/scripts/mojulo-paths.mjs +48 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Base Form Schema Configuration
3
+ * Universal patterns shared across all locales
4
+ */
5
+
6
+ export const FIELD_TYPES = [
7
+ 'text', 'email', 'tel', 'date', 'number',
8
+ 'dropdown', 'checkbox', 'textarea', 'radio', 'url'
9
+ ];
10
+
11
+ export const INPUT_MODES = {
12
+ text: 'text',
13
+ numeric: 'numeric',
14
+ decimal: 'decimal',
15
+ tel: 'tel',
16
+ email: 'email',
17
+ url: 'url',
18
+ search: 'search'
19
+ };
20
+
21
+ /**
22
+ * Universal validation patterns (locale-agnostic)
23
+ */
24
+ export const BASE_PATTERNS = {
25
+ email: {
26
+ pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
27
+ error: 'Please enter a valid email address',
28
+ description: 'Email address'
29
+ },
30
+ url: {
31
+ pattern: '^https?:\\/\\/[\\w.-]+(?:\\.[\\w.-]+)+[\\w.,@?^=%&:/~+#-]*$',
32
+ error: 'Please enter a valid URL',
33
+ description: 'Web URL'
34
+ },
35
+ alphanumeric: {
36
+ pattern: '^[a-zA-Z0-9]+$',
37
+ error: 'Only letters and numbers allowed',
38
+ description: 'Alphanumeric characters only'
39
+ },
40
+ numeric: {
41
+ pattern: '^\\d+$',
42
+ error: 'Only numbers allowed',
43
+ description: 'Numeric digits only'
44
+ }
45
+ };
46
+
47
+ /**
48
+ * Universal autocomplete mappings
49
+ */
50
+ export const BASE_AUTOCOMPLETE = {
51
+ // Names
52
+ fullName: 'name',
53
+ firstName: 'given-name',
54
+ lastName: 'family-name',
55
+ middleName: 'additional-name',
56
+
57
+ // Contact
58
+ email: 'email',
59
+ phone: 'tel',
60
+
61
+ // Generic address
62
+ streetAddress: 'street-address',
63
+ addressLine1: 'address-line1',
64
+ addressLine2: 'address-line2',
65
+ city: 'address-level2',
66
+ state: 'address-level1',
67
+ postalCode: 'postal-code',
68
+ country: 'country-name',
69
+
70
+ // Personal
71
+ birthday: 'bday',
72
+ organization: 'organization',
73
+ jobTitle: 'organization-title'
74
+ };
75
+
76
+ /**
77
+ * Universal field archetypes (locale will override specific patterns)
78
+ */
79
+ export const BASE_ARCHETYPES = {
80
+ email: {
81
+ type: 'email',
82
+ autocomplete: 'email',
83
+ inputMode: 'email',
84
+ pattern: BASE_PATTERNS.email.pattern,
85
+ patternError: BASE_PATTERNS.email.error,
86
+ pii: true
87
+ },
88
+ fullName: {
89
+ type: 'text',
90
+ autocomplete: 'name',
91
+ pii: true
92
+ },
93
+ firstName: {
94
+ type: 'text',
95
+ autocomplete: 'given-name',
96
+ pii: true
97
+ },
98
+ lastName: {
99
+ type: 'text',
100
+ autocomplete: 'family-name',
101
+ pii: true
102
+ },
103
+ dateOfBirth: {
104
+ type: 'date',
105
+ autocomplete: 'bday',
106
+ pii: true
107
+ },
108
+ website: {
109
+ type: 'url',
110
+ inputMode: 'url',
111
+ placeholder: 'https://',
112
+ pattern: BASE_PATTERNS.url.pattern,
113
+ patternError: BASE_PATTERNS.url.error
114
+ },
115
+ comments: {
116
+ type: 'textarea',
117
+ rows: 4,
118
+ maxLength: 1000
119
+ }
120
+ };
121
+
122
+ /**
123
+ * PII field indicators - keywords that suggest PII
124
+ */
125
+ export const PII_INDICATORS = [
126
+ 'name', 'fullName', 'firstName', 'lastName',
127
+ 'email', 'phone', 'mobile', 'cell', 'telephone',
128
+ 'address', 'street', 'postcode', 'postal', 'zip',
129
+ 'ssn', 'socialSecurity', 'sin', 'nationalInsurance', 'taxId',
130
+ 'dob', 'dateOfBirth', 'birthday', 'birthdate',
131
+ 'driverLicense', 'passport', 'nationalId',
132
+ 'creditCard', 'cardNumber', 'accountNumber',
133
+ 'bankAccount', 'iban', 'routingNumber',
134
+ 'salary', 'income'
135
+ ];
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Form Schema Configuration Index
3
+ *
4
+ * Resolves locale-specific configurations and builds LLM prompt injections.
5
+ */
6
+
7
+ import * as base from './base.js';
8
+ import * as enUS from './locales/en-US.js';
9
+ import * as enCA from './locales/en-CA.js';
10
+ import * as frCA from './locales/fr-CA.js';
11
+ import * as enGB from './locales/en-GB.js';
12
+ import * as deDE from './locales/de-DE.js';
13
+ import * as frFR from './locales/fr-FR.js';
14
+ import * as itIT from './locales/it-IT.js';
15
+ import * as nlNL from './locales/nl-NL.js';
16
+ import * as esES from './locales/es-ES.js';
17
+ import * as jaJP from './locales/ja-JP.js';
18
+ import * as koKR from './locales/ko-KR.js';
19
+ import * as ptBR from './locales/pt-BR.js';
20
+ import * as enAU from './locales/en-AU.js';
21
+ import * as esMX from './locales/es-MX.js';
22
+ import * as arAE from './locales/ar-AE.js';
23
+ import * as zhCN from './locales/zh-CN.js';
24
+ import * as enIN from './locales/en-IN.js';
25
+ import * as zhHK from './locales/zh-HK.js';
26
+ import * as enSG from './locales/en-SG.js';
27
+ import * as enPH from './locales/en-PH.js';
28
+ import * as enMY from './locales/en-MY.js';
29
+ import * as enNG from './locales/en-NG.js';
30
+
31
+ /**
32
+ * Available locales with their configurations
33
+ */
34
+ const LOCALES = {
35
+ 'en-US': enUS,
36
+ 'en-CA': enCA,
37
+ 'fr-CA': frCA,
38
+ 'en-GB': enGB,
39
+ 'de-DE': deDE,
40
+ 'fr-FR': frFR,
41
+ 'it-IT': itIT,
42
+ 'nl-NL': nlNL,
43
+ 'es-ES': esES,
44
+ 'ja-JP': jaJP,
45
+ 'ko-KR': koKR,
46
+ 'pt-BR': ptBR,
47
+ 'en-AU': enAU,
48
+ 'es-MX': esMX,
49
+ 'ar-AE': arAE,
50
+ 'zh-CN': zhCN,
51
+ 'en-IN': enIN,
52
+ 'zh-HK': zhHK,
53
+ 'en-SG': enSG,
54
+ 'en-PH': enPH,
55
+ 'en-MY': enMY,
56
+ 'en-NG': enNG
57
+ };
58
+
59
+ /**
60
+ * Locale metadata for UI display
61
+ */
62
+ export const SUPPORTED_LOCALES = [
63
+ { code: 'en-US', name: 'United States', flag: '🇺🇸', region: 'North America' },
64
+ { code: 'en-CA', name: 'Canada (English)', flag: '🇨🇦', region: 'North America' },
65
+ { code: 'fr-CA', name: 'Canada (Français)', flag: '🇨🇦', region: 'North America' },
66
+ { code: 'en-GB', name: 'United Kingdom', flag: '🇬🇧', region: 'Europe' },
67
+ { code: 'de-DE', name: 'Germany', flag: '🇩🇪', region: 'Europe' },
68
+ { code: 'fr-FR', name: 'France', flag: '🇫🇷', region: 'Europe' },
69
+ { code: 'it-IT', name: 'Italy', flag: '🇮🇹', region: 'Europe' },
70
+ { code: 'nl-NL', name: 'Netherlands', flag: '🇳🇱', region: 'Europe' },
71
+ { code: 'es-ES', name: 'Spain', flag: '🇪🇸', region: 'Europe' },
72
+ { code: 'ja-JP', name: 'Japan', flag: '🇯🇵', region: 'Asia' },
73
+ { code: 'ko-KR', name: 'South Korea', flag: '🇰🇷', region: 'Asia' },
74
+ { code: 'pt-BR', name: 'Brazil', flag: '🇧🇷', region: 'South America' },
75
+ { code: 'en-AU', name: 'Australia', flag: '🇦🇺', region: 'Oceania' },
76
+ { code: 'es-MX', name: 'Mexico', flag: '🇲🇽', region: 'North America' },
77
+ { code: 'ar-AE', name: 'United Arab Emirates', flag: '🇦🇪', region: 'Middle East' },
78
+ { code: 'zh-CN', name: 'China', flag: '🇨🇳', region: 'Asia' },
79
+ { code: 'en-IN', name: 'India', flag: '🇮🇳', region: 'Asia' },
80
+ { code: 'zh-HK', name: 'Hong Kong', flag: '🇭🇰', region: 'Asia' },
81
+ { code: 'en-SG', name: 'Singapore', flag: '🇸🇬', region: 'Asia' },
82
+ { code: 'en-PH', name: 'Philippines', flag: '🇵🇭', region: 'Asia' },
83
+ { code: 'en-MY', name: 'Malaysia', flag: '🇲🇾', region: 'Asia' },
84
+ { code: 'en-NG', name: 'Nigeria', flag: '🇳🇬', region: 'Africa' }
85
+ ];
86
+
87
+ /**
88
+ * Default locale fallback
89
+ */
90
+ export const DEFAULT_LOCALE = 'en-US';
91
+
92
+ /**
93
+ * Get merged configuration for a specific locale
94
+ *
95
+ * @param {string} localeCode - Locale code (e.g., 'en-US', 'de-DE')
96
+ * @returns {Object} Merged configuration with base + locale-specific patterns
97
+ */
98
+ export function getFormSchemaConfig(localeCode = DEFAULT_LOCALE) {
99
+ const locale = LOCALES[localeCode] || LOCALES[DEFAULT_LOCALE];
100
+
101
+ return {
102
+ localeInfo: locale.LOCALE_INFO,
103
+ patterns: {
104
+ ...base.BASE_PATTERNS,
105
+ ...locale.PATTERNS
106
+ },
107
+ autocomplete: base.BASE_AUTOCOMPLETE,
108
+ inputModes: base.INPUT_MODES,
109
+ archetypes: {
110
+ ...base.BASE_ARCHETYPES,
111
+ ...locale.ARCHETYPES
112
+ },
113
+ fieldLabels: locale.FIELD_LABELS || {},
114
+ piiIndicators: base.PII_INDICATORS,
115
+ gdprHints: locale.GDPR_HINTS || null
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Build validation patterns section for LLM prompt
121
+ */
122
+ function buildPatternsPrompt(patterns, localeInfo) {
123
+ const lines = [`**VALIDATION PATTERNS (${localeInfo.code}):**`];
124
+
125
+ for (const [name, config] of Object.entries(patterns)) {
126
+ lines.push(`- ${name}: "${config.pattern}" - ${config.description}`);
127
+ }
128
+
129
+ return lines.join('\n');
130
+ }
131
+
132
+ /**
133
+ * Build archetypes section for LLM prompt
134
+ */
135
+ function buildArchetypesPrompt(archetypes) {
136
+ const lines = [
137
+ '**FIELD ARCHETYPES (use these templates for common fields):**',
138
+ 'When generating these field types, include all specified attributes:'
139
+ ];
140
+
141
+ const priority = ['email', 'phone', 'postalCode', 'nationalId', 'iban', 'currency'];
142
+
143
+ for (const name of priority) {
144
+ if (archetypes[name]) {
145
+ const attrs = Object.entries(archetypes[name])
146
+ .filter(([k, v]) => v !== undefined && k !== 'options')
147
+ .map(([k, v]) => `${k}: ${JSON.stringify(v)}`)
148
+ .join(', ');
149
+ lines.push(`- ${name}: { ${attrs} }`);
150
+ }
151
+ }
152
+
153
+ return lines.join('\n');
154
+ }
155
+
156
+ /**
157
+ * Build autocomplete reference for LLM prompt
158
+ */
159
+ function buildAutocompletePrompt() {
160
+ return `**AUTOCOMPLETE ATTRIBUTES (for browser autofill):**
161
+ Use the "autocomplete" attribute for these field types:
162
+ - Names: name, given-name, family-name, additional-name
163
+ - Contact: email, tel
164
+ - Address: street-address, address-line1, address-line2, address-level2 (city), address-level1 (state/province), postal-code, country-name
165
+ - Personal: bday, organization, organization-title`;
166
+ }
167
+
168
+ /**
169
+ * Build input mode reference for LLM prompt
170
+ */
171
+ function buildInputModePrompt() {
172
+ return `**INPUT MODES (mobile keyboard optimization):**
173
+ Use the "inputMode" attribute:
174
+ - "numeric": ZIP codes, PINs, quantities (number pad)
175
+ - "tel": Phone numbers (phone keypad)
176
+ - "email": Email addresses (@ and . accessible)
177
+ - "decimal": Prices, measurements (numbers with decimal)
178
+ - "url": Website URLs (/ and . accessible)`;
179
+ }
180
+
181
+ /**
182
+ * Build PII guidance for LLM prompt
183
+ */
184
+ function buildPiiPrompt() {
185
+ return `**PII (Personally Identifiable Information):**
186
+ Mark fields as "pii": true when they contain:
187
+ - Names, addresses, contact info (email, phone)
188
+ - Government IDs (SSN, SIN, NI number, tax IDs)
189
+ - Financial info (bank accounts, IBAN, card numbers)
190
+ - Date of birth
191
+
192
+ PII fields receive special handling and are never sent to AI models.
193
+ For highly sensitive fields (SSN, passwords), also add "sensitive": true.`;
194
+ }
195
+
196
+ /**
197
+ * Build GDPR section if applicable
198
+ */
199
+ function buildGdprPrompt(gdprHints) {
200
+ if (!gdprHints) return null;
201
+
202
+ return `**GDPR COMPLIANCE (EU):**
203
+ For forms collecting personal data in the EU:
204
+ - Include a consent checkbox field with clear data processing language
205
+ - Mark all personal data fields with "pii": true
206
+ - Consider adding "helpText" explaining data usage on sensitive fields`;
207
+ }
208
+
209
+ /**
210
+ * Build locale-specific field label hints
211
+ */
212
+ function buildFieldLabelsPrompt(fieldLabels, localeInfo) {
213
+ if (!fieldLabels || Object.keys(fieldLabels).length === 0) return null;
214
+
215
+ const lines = [`**LOCALIZED FIELD LABELS (${localeInfo.name}):**`];
216
+ lines.push('Use these labels for common fields:');
217
+
218
+ for (const [key, label] of Object.entries(fieldLabels)) {
219
+ lines.push(`- ${key}: "${label}"`);
220
+ }
221
+
222
+ return lines.join('\n');
223
+ }
224
+
225
+ /**
226
+ * Build the complete prompt injection for a locale
227
+ *
228
+ * @param {string} localeCode - Locale code
229
+ * @returns {string} Complete prompt section to inject
230
+ */
231
+ export function buildFormSchemaPrompt(localeCode = DEFAULT_LOCALE) {
232
+ const config = getFormSchemaConfig(localeCode);
233
+ const { localeInfo, patterns, archetypes, fieldLabels, gdprHints } = config;
234
+
235
+ const sections = [
236
+ `**LOCALE: ${localeInfo.name} (${localeInfo.code})**`,
237
+ `Date format: ${localeInfo.dateFormat} | Currency: ${localeInfo.currency}`,
238
+ '',
239
+ buildPatternsPrompt(patterns, localeInfo),
240
+ '',
241
+ buildArchetypesPrompt(archetypes),
242
+ '',
243
+ buildAutocompletePrompt(),
244
+ '',
245
+ buildInputModePrompt(),
246
+ '',
247
+ buildPiiPrompt()
248
+ ];
249
+
250
+ // Add GDPR section for EU locales
251
+ const gdprSection = buildGdprPrompt(gdprHints);
252
+ if (gdprSection) {
253
+ sections.push('', gdprSection);
254
+ }
255
+
256
+ // Add localized field labels
257
+ const labelsSection = buildFieldLabelsPrompt(fieldLabels, localeInfo);
258
+ if (labelsSection) {
259
+ sections.push('', labelsSection);
260
+ }
261
+
262
+ return sections.filter(s => s !== null).join('\n');
263
+ }
264
+
265
+ /**
266
+ * Get locale info for UI display
267
+ *
268
+ * @param {string} localeCode - Locale code
269
+ * @returns {Object|null} Locale info or null if not found
270
+ */
271
+ export function getLocaleInfo(localeCode) {
272
+ return SUPPORTED_LOCALES.find(l => l.code === localeCode) || null;
273
+ }
274
+
275
+ /**
276
+ * Check if a locale is supported
277
+ *
278
+ * @param {string} localeCode - Locale code
279
+ * @returns {boolean}
280
+ */
281
+ export function isLocaleSupported(localeCode) {
282
+ return localeCode in LOCALES;
283
+ }
284
+
285
+ // Re-export base utilities
286
+ export { FIELD_TYPES, INPUT_MODES, PII_INDICATORS } from './base.js';
@@ -0,0 +1,153 @@
1
+ /**
2
+ * South African Afrikaans (af-ZA) Locale Configuration
3
+ */
4
+
5
+ export const LOCALE_INFO = {
6
+ code: 'af-ZA',
7
+ name: 'South Africa',
8
+ region: 'Africa',
9
+ currency: 'ZAR',
10
+ dateFormat: 'YYYY/MM/DD'
11
+ };
12
+
13
+ export const PATTERNS = {
14
+ phone: {
15
+ pattern: '^(\\+27|0)[1-9]\\d{8}$',
16
+ error: 'Voer asseblief \'n geldige telefoonnommer in',
17
+ description: 'South African phone number'
18
+ },
19
+ postalCode: {
20
+ pattern: '^\\d{4}$',
21
+ error: 'Voer asseblief \'n geldige poskode in (4 syfers)',
22
+ description: 'South African postal code'
23
+ },
24
+ idNumber: {
25
+ pattern: '^\\d{13}$',
26
+ error: 'Voer asseblief \'n geldige ID-nommer in (13 syfers)',
27
+ description: 'South African ID number'
28
+ },
29
+ passportNumber: {
30
+ pattern: '^[A-Z]\\d{8}$',
31
+ error: 'Voer asseblief \'n geldige paspoortnommer in',
32
+ description: 'South African passport number'
33
+ },
34
+ bankAccountNumber: {
35
+ pattern: '^\\d{9,12}$',
36
+ error: 'Voer asseblief \'n geldige bankrekeningnommer in',
37
+ description: 'South African bank account number'
38
+ },
39
+ branchCode: {
40
+ pattern: '^\\d{6}$',
41
+ error: 'Voer asseblief \'n geldige takkode in (6 syfers)',
42
+ description: 'South African bank branch code'
43
+ },
44
+ vatNumber: {
45
+ pattern: '^4\\d{9}$',
46
+ error: 'Voer asseblief \'n geldige BTW-nommer in (begin met 4, 10 syfers)',
47
+ description: 'South African VAT number'
48
+ },
49
+ companyRegistration: {
50
+ pattern: '^\\d{4}\\/\\d{6}\\/\\d{2}$',
51
+ error: 'Voer asseblief \'n geldige maatskappyregistrasienommer in (bv. 2020/123456/07)',
52
+ description: 'South African company registration number'
53
+ },
54
+ currency: {
55
+ pattern: '^R?\\s?\\d{1,3}(\\s?\\d{3})*([,.]\\d{2})?$',
56
+ error: 'Voer asseblief \'n geldige bedrag in',
57
+ description: 'South African Rand amount'
58
+ }
59
+ };
60
+
61
+ export const ARCHETYPES = {
62
+ phone: {
63
+ type: 'tel',
64
+ autocomplete: 'tel',
65
+ inputMode: 'tel',
66
+ placeholder: '082 123 4567',
67
+ pattern: PATTERNS.phone.pattern,
68
+ patternError: PATTERNS.phone.error,
69
+ pii: true
70
+ },
71
+ postalCode: {
72
+ type: 'text',
73
+ autocomplete: 'postal-code',
74
+ inputMode: 'numeric',
75
+ placeholder: '0001',
76
+ pattern: PATTERNS.postalCode.pattern,
77
+ patternError: PATTERNS.postalCode.error,
78
+ maxLength: 4
79
+ },
80
+ nationalId: {
81
+ type: 'text',
82
+ inputMode: 'numeric',
83
+ placeholder: '8001015009087',
84
+ pattern: PATTERNS.idNumber.pattern,
85
+ patternError: PATTERNS.idNumber.error,
86
+ pii: true,
87
+ sensitive: true,
88
+ helpText: 'Jou ID-nommer word veilig bewaar'
89
+ },
90
+ bankAccount: {
91
+ type: 'text',
92
+ inputMode: 'numeric',
93
+ placeholder: '123456789',
94
+ pattern: PATTERNS.bankAccountNumber.pattern,
95
+ patternError: PATTERNS.bankAccountNumber.error,
96
+ pii: true
97
+ },
98
+ branchCode: {
99
+ type: 'text',
100
+ inputMode: 'numeric',
101
+ placeholder: '250655',
102
+ pattern: PATTERNS.branchCode.pattern,
103
+ patternError: PATTERNS.branchCode.error,
104
+ maxLength: 6
105
+ },
106
+ province: {
107
+ type: 'dropdown',
108
+ autocomplete: 'address-level1',
109
+ options: [
110
+ { value: 'EC', label: 'Oos-Kaap' },
111
+ { value: 'FS', label: 'Vrystaat' },
112
+ { value: 'GP', label: 'Gauteng' },
113
+ { value: 'KZN', label: 'KwaZulu-Natal' },
114
+ { value: 'LP', label: 'Limpopo' },
115
+ { value: 'MP', label: 'Mpumalanga' },
116
+ { value: 'NC', label: 'Noord-Kaap' },
117
+ { value: 'NW', label: 'Noordwes' },
118
+ { value: 'WC', label: 'Wes-Kaap' }
119
+ ]
120
+ },
121
+ currency: {
122
+ type: 'text',
123
+ inputMode: 'decimal',
124
+ placeholder: 'R 0,00',
125
+ pattern: PATTERNS.currency.pattern,
126
+ patternError: PATTERNS.currency.error
127
+ }
128
+ };
129
+
130
+ /**
131
+ * Afrikaans field name mappings
132
+ */
133
+ export const FIELD_LABELS = {
134
+ postalCode: 'Poskode',
135
+ state: 'Provinsie',
136
+ nationalId: 'ID-nommer',
137
+ phone: 'Telefoonnommer',
138
+ firstName: 'Voornaam',
139
+ lastName: 'Van',
140
+ streetAddress: 'Straatadres',
141
+ city: 'Stad/Dorp',
142
+ email: 'E-posadres'
143
+ };
144
+
145
+ /**
146
+ * POPIA compliance hints (South Africa's Protection of Personal Information Act)
147
+ */
148
+ export const POPIA_HINTS = {
149
+ consentRequired: true,
150
+ dataRetentionNotice: true,
151
+ rightToErasure: true,
152
+ explicitConsentLanguage: 'Ek stem in tot die verwerking van my persoonlike inligting in ooreenstemming met die privaatheidsbeleid.'
153
+ };