mojulo 0.0.0 → 0.1.0
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/README.md +53 -4
- package/lib/audit-logger-new.js +11 -0
- package/lib/auth/gate.js +25 -0
- package/lib/auth/service.js +17 -0
- package/lib/auth/session.js +63 -0
- package/lib/builder/chat-processor.js +607 -0
- package/lib/builder/composer-bridge.js +82 -0
- package/lib/builder/evaluator.js +159 -0
- package/lib/builder/executor.js +252 -0
- package/lib/builder/index.js +48 -0
- package/lib/builder/session.js +248 -0
- package/lib/builder/system-prompt.js +422 -0
- package/lib/builder/tone-presets.js +75 -0
- package/lib/builder/tool-executors.js +1418 -0
- package/lib/builder/tools.js +338 -0
- package/lib/builder/validators.js +239 -0
- package/lib/composer/composer.js +225 -0
- package/lib/composer/index.js +40 -0
- package/lib/composer/protocols/00_base.txt +19 -0
- package/lib/composer/protocols/01_knowledge.txt +9 -0
- package/lib/composer/protocols/02_form-gathering.txt +32 -0
- package/lib/composer/protocols/03_appointments.txt +16 -0
- package/lib/composer/protocols/04_triage.txt +15 -0
- package/lib/composer/protocols/05_optical-read.txt +22 -0
- package/lib/composer/response-builder.js +98 -0
- package/lib/config-builder.js +650 -0
- package/lib/db/ids.js +10 -0
- package/lib/db/index.js +179 -0
- package/lib/db/repositories/apiKeys.js +72 -0
- package/lib/db/repositories/auditLogs.js +12 -0
- package/lib/db/repositories/botSpaces.js +12 -0
- package/lib/db/repositories/builderSessions.js +312 -0
- package/lib/db/repositories/deploymentEvents.js +12 -0
- package/lib/db/repositories/deployments.js +385 -0
- package/lib/db/repositories/documents.js +68 -0
- package/lib/db/repositories/mcpJobs.js +84 -0
- package/lib/deployers/bot-fleet.js +110 -0
- package/lib/deployers/bot-proxy.js +72 -0
- package/lib/deployers/build.js +89 -0
- package/lib/deployers/cloud-deploy.js +310 -0
- package/lib/deployers/docker.js +439 -0
- package/lib/deployers/fly.js +432 -0
- package/lib/deployers/index.js +38 -0
- package/lib/deployment-auth.js +36 -0
- package/lib/document-parser.js +171 -0
- package/lib/embedder/chunker.js +93 -0
- package/lib/embedder/local.js +101 -0
- package/lib/embedder/preview-rag.js +93 -0
- package/lib/envelope-schema.js +54 -0
- package/lib/fleet/scoped-sql.js +342 -0
- package/lib/form-schema-config/base.js +135 -0
- package/lib/form-schema-config/index.js +286 -0
- package/lib/form-schema-config/locales/af-ZA.js +153 -0
- package/lib/form-schema-config/locales/ar-AE.js +142 -0
- package/lib/form-schema-config/locales/ar-SA.js +164 -0
- package/lib/form-schema-config/locales/de-DE.js +152 -0
- package/lib/form-schema-config/locales/en-AU.js +161 -0
- package/lib/form-schema-config/locales/en-CA.js +115 -0
- package/lib/form-schema-config/locales/en-GB.js +132 -0
- package/lib/form-schema-config/locales/en-IN.js +219 -0
- package/lib/form-schema-config/locales/en-MY.js +171 -0
- package/lib/form-schema-config/locales/en-NG.js +198 -0
- package/lib/form-schema-config/locales/en-PH.js +186 -0
- package/lib/form-schema-config/locales/en-SG.js +153 -0
- package/lib/form-schema-config/locales/en-US.js +138 -0
- package/lib/form-schema-config/locales/es-ES.js +171 -0
- package/lib/form-schema-config/locales/es-MX.js +193 -0
- package/lib/form-schema-config/locales/fr-CA.js +138 -0
- package/lib/form-schema-config/locales/fr-FR.js +155 -0
- package/lib/form-schema-config/locales/hi-IN.js +219 -0
- package/lib/form-schema-config/locales/it-IT.js +157 -0
- package/lib/form-schema-config/locales/ja-JP.js +169 -0
- package/lib/form-schema-config/locales/ko-KR.js +140 -0
- package/lib/form-schema-config/locales/nl-NL.js +149 -0
- package/lib/form-schema-config/locales/pt-BR.js +168 -0
- package/lib/form-schema-config/locales/zh-CN.js +172 -0
- package/lib/form-schema-config/locales/zh-HK.js +142 -0
- package/lib/form-structure-schema.js +191 -0
- package/lib/llm-providers.js +828 -0
- package/lib/markdown.js +197 -0
- package/lib/mcp/catalysts/appointment-to-calendar.md +84 -0
- package/lib/mcp/catalysts/conversations-to-channel-digest.md +104 -0
- package/lib/mcp/catalysts/document-extract-to-store.md +92 -0
- package/lib/mcp/catalysts/knowledge-gap-miner.md +96 -0
- package/lib/mcp/catalysts/loader.js +144 -0
- package/lib/mcp/catalysts/qualify-lead-to-crm.md +83 -0
- package/lib/mcp/catalysts/scan-conversations-for-signal.md +92 -0
- package/lib/mcp/catalysts/submission-to-ticket.md +83 -0
- package/lib/mcp/catalysts/submissions-to-warehouse.md +103 -0
- package/lib/mcp/catalysts/weekly-submissions-digest.md +82 -0
- package/lib/mcp/jobs.js +64 -0
- package/lib/mcp/server.js +184 -0
- package/lib/mcp/session-binding.js +130 -0
- package/lib/mcp/tools/build.js +123 -0
- package/lib/mcp/tools/catalysts.js +477 -0
- package/lib/mcp/tools/context.js +325 -0
- package/lib/mcp/tools/fleet.js +391 -0
- package/lib/mcp/tools/jobs-tools.js +240 -0
- package/lib/mcp/tools/operate.js +314 -0
- package/lib/preview/build-preview-config.js +136 -0
- package/lib/rate-limiter.js +11 -0
- package/lib/resolve-api-key.js +142 -0
- package/lib/storage/index.js +40 -0
- package/messages/de.json +2136 -0
- package/messages/en.json +2136 -0
- package/messages/es.json +2136 -0
- package/messages/fr.json +2136 -0
- package/messages/it.json +2136 -0
- package/messages/ja.json +2136 -0
- package/messages/ko.json +2136 -0
- package/messages/nl.json +2136 -0
- package/messages/pl.json +2136 -0
- package/messages/pt.json +2136 -0
- package/messages/ru.json +2136 -0
- package/messages/uk.json +2136 -0
- package/messages/zh.json +2136 -0
- package/package.json +61 -5
- package/scripts/mcp-config.mjs +162 -0
- package/scripts/mcp-stdio-loader.mjs +42 -0
- package/scripts/mcp-stdio.mjs +108 -0
- 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
|
+
};
|