feat: Template-Spec v1 Phase C — IF-Renderer + HOSTING/FEATURES + 4 neue DE-Templates
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s
- contextBridge.ts: HostingCtx + FeaturesCtx (35 Felder), ~50 neue Platzhalter-Aliases - ruleEngine.ts: buildBoolContext() + applyConditionalBlocks() (IF/IF_NOT/IF_ANY) - ruleEngine.test.ts: 67 Tests (+18 für Phase C), alle grün - page.tsx: IF-Renderer in Pipeline, HOSTING+FEATURES Formular-Sections, erweiterter SDK-Prefill - scripts/apply_templates_023.py: 4 neue DE-Templates (Cookie v2, DSE, AGB, Impressum) - migrations/023_new_templates_de.sql: Dokumentation + Verifikations-Query Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -359,6 +359,83 @@ export function getDocType(templateType: string, language: string): string {
|
||||
return DOC_TYPE_MAP[key] ?? `${templateType}_${language}`
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// buildBoolContext: combines computed flags + derived + FEATURES booleans
|
||||
// =============================================================================
|
||||
|
||||
export function buildBoolContext(ctx: TemplateContext, flags: ComputedFlags): Record<string, boolean> {
|
||||
const f = ctx.FEATURES
|
||||
return {
|
||||
// --- From computed flags ---
|
||||
IS_B2C: flags.IS_B2C,
|
||||
IS_B2B: flags.IS_B2B,
|
||||
SERVICE_IS_SAAS: flags.SERVICE_IS_SAAS,
|
||||
SERVICE_IS_HYBRID: flags.SERVICE_IS_HYBRID,
|
||||
HAS_PENALTY: flags.HAS_PENALTY,
|
||||
HAS_ANALYTICS: flags.HAS_ANALYTICS,
|
||||
ANALYTICS_ENABLED: flags.HAS_ANALYTICS, // alias used in cookie banner template
|
||||
HAS_MARKETING: flags.HAS_MARKETING,
|
||||
MARKETING_ENABLED: flags.HAS_MARKETING, // alias
|
||||
|
||||
// --- Derived from PROVIDER ---
|
||||
HAS_REGISTER: !!(ctx.PROVIDER.REGISTER_COURT),
|
||||
HAS_VAT_ID: !!(ctx.PROVIDER.VAT_ID),
|
||||
CONTACT_PHONE: !!(ctx.PROVIDER.PHONE),
|
||||
|
||||
// --- Derived from PRIVACY ---
|
||||
HAS_DPO: !!(ctx.PRIVACY.DPO_NAME),
|
||||
|
||||
// --- From FEATURES booleans ---
|
||||
THIRD_COUNTRY_POSSIBLE: f.HAS_THIRD_COUNTRY,
|
||||
HAS_THIRD_COUNTRY: f.HAS_THIRD_COUNTRY,
|
||||
FUNCTIONAL_ENABLED: f.HAS_FUNCTIONAL_COOKIES,
|
||||
HAS_FUNCTIONAL_COOKIES: f.HAS_FUNCTIONAL_COOKIES,
|
||||
CMP_LOGS_CONSENTS: f.CMP_LOGS_CONSENTS,
|
||||
HAS_NEWSLETTER: f.HAS_NEWSLETTER,
|
||||
HAS_ACCOUNT: f.HAS_ACCOUNT,
|
||||
HAS_PAYMENTS: f.HAS_PAYMENTS,
|
||||
HAS_SUPPORT: f.HAS_SUPPORT,
|
||||
HAS_SOCIAL_MEDIA: f.HAS_SOCIAL_MEDIA,
|
||||
HAS_PAID_PLANS: f.HAS_PAID_PLANS,
|
||||
HAS_SLA: f.HAS_SLA,
|
||||
HAS_EXPORT_POLICY: f.HAS_EXPORT_POLICY,
|
||||
HAS_WITHDRAWAL: f.HAS_WITHDRAWAL,
|
||||
HAS_REGULATED_PROFESSION: f.HAS_REGULATED_PROFESSION,
|
||||
HAS_EDITORIAL_RESPONSIBLE: f.HAS_EDITORIAL_RESPONSIBLE,
|
||||
HAS_DISPUTE_RESOLUTION: f.HAS_DISPUTE_RESOLUTION,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// applyConditionalBlocks: processes {{#IF}}/{{#IF_NOT}}/{{#IF_ANY}} directives
|
||||
// Runs BEFORE placeholder substitution.
|
||||
// =============================================================================
|
||||
|
||||
export function applyConditionalBlocks(content: string, boolCtx: Record<string, boolean>): string {
|
||||
let result = content
|
||||
|
||||
// {{#IF_NOT COND}}...{{/IF_NOT}} — process before IF to avoid conflicts
|
||||
result = result.replace(
|
||||
/\{\{#IF_NOT ([A-Z_]+)\}\}([\s\S]*?)\{\{\/IF_NOT\}\}/g,
|
||||
(_, cond: string, body: string) => (boolCtx[cond] ? '' : body)
|
||||
)
|
||||
|
||||
// {{#IF_ANY A B C}}...{{/IF_ANY}}
|
||||
result = result.replace(
|
||||
/\{\{#IF_ANY ([A-Z_ ]+)\}\}([\s\S]*?)\{\{\/IF_ANY\}\}/g,
|
||||
(_, conds: string, body: string) =>
|
||||
conds.trim().split(/\s+/).some((c) => boolCtx[c]) ? body : ''
|
||||
)
|
||||
|
||||
// {{#IF COND}}...{{/IF}}
|
||||
result = result.replace(
|
||||
/\{\{#IF ([A-Z_]+)\}\}([\s\S]*?)\{\{\/IF\}\}/g,
|
||||
(_, cond: string, body: string) => (boolCtx[cond] ? body : '')
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// applyBlockRemoval: removes [BLOCK:ID]…[/BLOCK:ID] markers from content
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user