@salesforce/plugin-bre-to-cml 1.1.5 → 1.2.5
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 +27 -9
- package/lib/commands/cml/convert/surcharge-rules.d.ts +19 -3
- package/lib/commands/cml/convert/surcharge-rules.js +94 -13
- package/lib/commands/cml/convert/surcharge-rules.js.map +1 -1
- package/lib/commands/cml/convert/underwriting-rules.d.ts +16 -4
- package/lib/commands/cml/convert/underwriting-rules.js +37 -40
- package/lib/commands/cml/convert/underwriting-rules.js.map +1 -1
- package/lib/shared/insurance/insurance-cml-merge.d.ts +91 -0
- package/lib/shared/insurance/insurance-cml-merge.js +569 -0
- package/lib/shared/insurance/insurance-cml-merge.js.map +1 -0
- package/lib/shared/insurance/insurance-org.d.ts +19 -1
- package/lib/shared/insurance/insurance-org.js +9 -1
- package/lib/shared/insurance/insurance-org.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-convert-command.d.ts +46 -2
- package/lib/shared/insurance/insurance-rule-convert-command.js +213 -22
- package/lib/shared/insurance/insurance-rule-convert-command.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-generator.d.ts +38 -1
- package/lib/shared/insurance/insurance-rule-generator.js +165 -6
- package/lib/shared/insurance/insurance-rule-generator.js.map +1 -1
- package/lib/shared/insurance/models.d.ts +25 -0
- package/messages/cml.convert.insurance-shared.md +9 -1
- package/messages/cml.convert.surcharge-rules.md +1 -1
- package/messages/cml.convert.underwriting-rules.md +1 -1
- package/oclif.lock +291 -649
- package/oclif.manifest.json +5 -5
- package/package.json +10 -10
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import { CmlConstraint } from '../types/types.js';
|
|
2
|
+
import { buildConstraintDeclaration, collectEmittedAttributes, sanitizeName, buildStageTransition, } from './insurance-rule-generator.js';
|
|
3
|
+
import { quoteSoqlIdList } from './insurance-org.js';
|
|
4
|
+
/**
|
|
5
|
+
* MERGE MODE (insurance-only): instead of building a fresh single-type CML and PATCH-replacing
|
|
6
|
+
* the org's curated "Gold Standard" ConstraintModel, this module reads the existing model text,
|
|
7
|
+
* computes each surcharge's PLATFORM-COMPATIBLE pathed rule key, and inserts/updates the
|
|
8
|
+
* `rule(...)` statement inside the correct existing leaf `type` block. The result is the full
|
|
9
|
+
* merged model, written to the same `${cmlApi}.cml` path the common import reads — so the import
|
|
10
|
+
* still does a whole-file PATCH, but the file now contains the curated model + the surcharge rules
|
|
11
|
+
* nested correctly rather than a flat overwrite.
|
|
12
|
+
*
|
|
13
|
+
* Why pathed keys: when a ProductSurcharge is persisted as ConstraintEngine the platform
|
|
14
|
+
* auto-generates `RuleKey` as `SC` + sanitize(ProductCode) of every segment in ProductPath (in
|
|
15
|
+
* order) + sanitize(apiName), joined by `__`. The Core surcharge engine matches the fired CML rule
|
|
16
|
+
* key against that auto-generated RuleKey by exact string, so the CML rule key MUST be pathed too.
|
|
17
|
+
*/
|
|
18
|
+
export const SURCHARGE_RULE_ACTION = 'InsuranceSurchargeRule';
|
|
19
|
+
/**
|
|
20
|
+
* Builds the pathed surcharge rule key that mirrors the platform's auto-generated
|
|
21
|
+
* `ProductSurcharge.RuleKey`: prefix + every path-segment product code + apiName.
|
|
22
|
+
*/
|
|
23
|
+
export function buildPathedRuleKey(prefix, pathProductCodes, apiName, stageTransition) {
|
|
24
|
+
const parts = [prefix, ...pathProductCodes.map(sanitizeName)];
|
|
25
|
+
if (stageTransition)
|
|
26
|
+
parts.push(stageTransition);
|
|
27
|
+
parts.push(sanitizeName(apiName));
|
|
28
|
+
return parts.join('__');
|
|
29
|
+
}
|
|
30
|
+
/** Splits a `ProductPath` into its ordered Product2 ids (slash-separated). */
|
|
31
|
+
export function splitProductPath(productPath) {
|
|
32
|
+
return productPath
|
|
33
|
+
.split('/')
|
|
34
|
+
.map((s) => s.trim())
|
|
35
|
+
.filter((s) => s.length > 0);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Reads the org's existing ConstraintModel text for the given CML API name, mirroring the lookup
|
|
39
|
+
* the common import uses (ExpressionSetDefinition.DeveloperName -> ExpressionSetDefinitionVersion).
|
|
40
|
+
* Returns undefined when no model exists yet (caller should then fall back to the build path).
|
|
41
|
+
*/
|
|
42
|
+
export async function fetchExistingConstraintModel(conn, cmlApiName) {
|
|
43
|
+
const def = await conn.sobject('ExpressionSetDefinition').findOne({ DeveloperName: cmlApiName }, ['Id']);
|
|
44
|
+
const defId = def?.Id;
|
|
45
|
+
if (!defId)
|
|
46
|
+
return undefined;
|
|
47
|
+
const version = await conn
|
|
48
|
+
.sobject('ExpressionSetDefinitionVersion')
|
|
49
|
+
.findOne({ ExpressionSetDefinitionId: defId }, ['Id']);
|
|
50
|
+
const versionId = version?.Id;
|
|
51
|
+
if (!versionId)
|
|
52
|
+
return undefined;
|
|
53
|
+
// ConstraintModel is a blob field: a plain row GET returns only a URL pointing at the blob, not
|
|
54
|
+
// its content. Fetch the blob endpoint directly — it streams back the raw CML text (the platform
|
|
55
|
+
// base64-decodes what the import PATCHes), so the body IS the model text and needs no decoding.
|
|
56
|
+
const blob = await conn.request(`/services/data/v${conn.getApiVersion()}/sobjects/ExpressionSetDefinitionVersion/${versionId}/ConstraintModel`);
|
|
57
|
+
const cmlText = typeof blob === 'string' ? blob : '';
|
|
58
|
+
return { versionId, cmlText };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolves Product2 id -> CML type name from the authoritative source: ExpressionSetConstraintObj
|
|
62
|
+
* rows with ConstraintModelTagType='Type' (ReferenceObjectId is the Product2 id, ConstraintModelTag
|
|
63
|
+
* is the CML type name). This is how the curated model binds products to type blocks, so it's the
|
|
64
|
+
* correct way to find which existing `type` block a surcharge's leaf product nests into.
|
|
65
|
+
*/
|
|
66
|
+
export async function fetchProductTypeTags(conn, productIds) {
|
|
67
|
+
const idToTag = new Map();
|
|
68
|
+
const idList = quoteSoqlIdList(productIds);
|
|
69
|
+
if (!idList)
|
|
70
|
+
return idToTag;
|
|
71
|
+
const result = await conn.query(`SELECT ReferenceObjectId, ConstraintModelTag FROM ExpressionSetConstraintObj WHERE ReferenceObjectId IN (${idList}) AND ConstraintModelTagType = 'Type'`);
|
|
72
|
+
for (const r of result.records) {
|
|
73
|
+
if (r.ReferenceObjectId && r.ConstraintModelTag)
|
|
74
|
+
idToTag.set(r.ReferenceObjectId, r.ConstraintModelTag);
|
|
75
|
+
}
|
|
76
|
+
return idToTag;
|
|
77
|
+
}
|
|
78
|
+
function escapeRegExp(value) {
|
|
79
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* [Fix #4] Detects the dominant line ending of a CML text by counting CRLF vs bare-LF occurrences.
|
|
83
|
+
* Returns `\r\n` when at least one CRLF appears AND CRLFs are at least as common as bare LFs (mixed
|
|
84
|
+
* CRLF-majority files still get CRLF; pure-LF and empty files get `\n`). Used by the INSERT path so
|
|
85
|
+
* a CRLF-curated model stays byte-clean after splicing. The L2 replace path preserves the line
|
|
86
|
+
* ending of the REPLACED span directly, so this only matters for new inserts.
|
|
87
|
+
*/
|
|
88
|
+
function detectDominantLineEnding(cml) {
|
|
89
|
+
const crlfCount = (cml.match(/\r\n/g) ?? []).length;
|
|
90
|
+
// Subtract CRLFs from the total LF count to get the BARE-LF count (an LF preceded by CR is part of a CRLF).
|
|
91
|
+
const totalLf = (cml.match(/\n/g) ?? []).length;
|
|
92
|
+
const bareLfCount = totalLf - crlfCount;
|
|
93
|
+
return crlfCount > 0 && crlfCount >= bareLfCount ? '\r\n' : '\n';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Brace-matches the block whose opening `{` is at `openIdx` and returns the index of its matched
|
|
97
|
+
* closing `}`. The scanner ignores `{`/`}`/`"` that appear inside double-quoted string literals,
|
|
98
|
+
* `//` line comments, and `/* *\/` block comments — a curated Gold-Standard model routinely carries
|
|
99
|
+
* comments, and a stray `}` inside one (or inside a quoted rule value) must NOT be mistaken for the
|
|
100
|
+
* structural close, which would return a too-early closeIdx and splice a new rule mid-statement.
|
|
101
|
+
*/
|
|
102
|
+
function matchClosingBrace(cml, openIdx) {
|
|
103
|
+
let depth = 0;
|
|
104
|
+
let inString = false;
|
|
105
|
+
let inLineComment = false;
|
|
106
|
+
let inBlockComment = false;
|
|
107
|
+
for (let i = openIdx; i < cml.length; i++) {
|
|
108
|
+
const ch = cml[i];
|
|
109
|
+
const next = cml[i + 1];
|
|
110
|
+
if (inLineComment) {
|
|
111
|
+
if (ch === '\n')
|
|
112
|
+
inLineComment = false;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (inBlockComment) {
|
|
116
|
+
if (ch === '*' && next === '/') {
|
|
117
|
+
inBlockComment = false;
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (inString) {
|
|
123
|
+
if (ch === '\\') {
|
|
124
|
+
i++; // skip the escaped character
|
|
125
|
+
}
|
|
126
|
+
else if (ch === '"') {
|
|
127
|
+
inString = false;
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ch === '/' && next === '/') {
|
|
132
|
+
inLineComment = true;
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
else if (ch === '/' && next === '*') {
|
|
136
|
+
inBlockComment = true;
|
|
137
|
+
i++;
|
|
138
|
+
}
|
|
139
|
+
else if (ch === '"') {
|
|
140
|
+
inString = true;
|
|
141
|
+
}
|
|
142
|
+
else if (ch === '{') {
|
|
143
|
+
depth++;
|
|
144
|
+
}
|
|
145
|
+
else if (ch === '}') {
|
|
146
|
+
depth--;
|
|
147
|
+
if (depth === 0)
|
|
148
|
+
return i;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Locates a `type <name> [: Parent] { ... }` block and returns the index of its opening brace and
|
|
155
|
+
* its brace-matched closing brace. Block-less forward declarations (`type X : Y;`) are skipped
|
|
156
|
+
* because the regex requires a `{` before any `;`. Word-boundary after the name keeps `Auto` from
|
|
157
|
+
* matching `AutoSilver`/`AutoDriver`.
|
|
158
|
+
*
|
|
159
|
+
* M1/M5: when more than one block declares the same type name the result is AMBIGUOUS — rather than
|
|
160
|
+
* guessing the first lexical match (which could nest the surcharge under the wrong product), the
|
|
161
|
+
* caller is told via `undefined` + a distinct ambiguity reason. A single unique match resolves
|
|
162
|
+
* normally.
|
|
163
|
+
*/
|
|
164
|
+
function findTypeBlock(cml, typeName, scan = blankComments(cml)) {
|
|
165
|
+
// [Fix #1] Run the anchor regex against the comment-blanked, length-preserving SCAN view rather
|
|
166
|
+
// than the raw cml. matchClosingBrace below is comment-aware; if the anchor regex ran on raw cml a
|
|
167
|
+
// commented-out `type Collision { ... }` header could be picked up as a real declaration (and then
|
|
168
|
+
// matchClosingBrace, which ignores comment braces, would either return the WRONG closeIdx or
|
|
169
|
+
// confuse a real same-name type for an "ambiguous" duplicate). The scan view zeroes the comment
|
|
170
|
+
// characters to spaces while preserving offsets, so the indices returned here still slice the
|
|
171
|
+
// real cml correctly. The default-argument pattern lets callers reuse a single hoisted scan
|
|
172
|
+
// across multiple lookups (Fix #1 hoisting site).
|
|
173
|
+
const re = new RegExp(`(^|\\n)[ \\t]*type[ \\t]+${escapeRegExp(typeName)}\\b[^{;]*\\{`, 'g');
|
|
174
|
+
const matches = [];
|
|
175
|
+
let m;
|
|
176
|
+
while ((m = re.exec(scan)) !== null) {
|
|
177
|
+
const openIdx = scan.indexOf('{', m.index);
|
|
178
|
+
if (openIdx >= 0)
|
|
179
|
+
matches.push(openIdx);
|
|
180
|
+
}
|
|
181
|
+
if (matches.length === 0)
|
|
182
|
+
return undefined;
|
|
183
|
+
if (matches.length > 1)
|
|
184
|
+
return { ambiguous: true };
|
|
185
|
+
const openIdx = matches[0];
|
|
186
|
+
const closeIdx = matchClosingBrace(cml, openIdx);
|
|
187
|
+
if (closeIdx === undefined)
|
|
188
|
+
return undefined;
|
|
189
|
+
return { openIdx, closeIdx };
|
|
190
|
+
}
|
|
191
|
+
/** Builds the `rule(...)` statement for a surcharge, using the same constraint generator as build mode. */
|
|
192
|
+
export function buildSurchargeRuleStatement(declaration, ruleKey) {
|
|
193
|
+
return CmlConstraint.createRuleConstraint(declaration, SURCHARGE_RULE_ACTION, ruleKey, 'True').generateCml();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Prepares the pathed-rule descriptors for a set of parsed surcharge records.
|
|
197
|
+
* `productIdToCode` and `productIdToType` must already cover every ProductPath segment.
|
|
198
|
+
*/
|
|
199
|
+
export function buildPathedSurchargeRules(prefix, ruleDefs, productIdToCode, productIdToType) {
|
|
200
|
+
return ruleDefs.map(({ record, ruleDef }) => {
|
|
201
|
+
const apiName = ruleDef.apiName ?? record.Name;
|
|
202
|
+
const segments = splitProductPath(record.ProductPath);
|
|
203
|
+
const pathCodes = segments.map((id) => productIdToCode.get(id) ?? id);
|
|
204
|
+
const stageTransition = buildStageTransition(ruleDef.underwritingRuleGroup);
|
|
205
|
+
const ruleKey = buildPathedRuleKey(prefix, pathCodes, apiName, stageTransition);
|
|
206
|
+
const leafProductId = segments[segments.length - 1];
|
|
207
|
+
const typeName = leafProductId ? productIdToType.get(leafProductId) : undefined;
|
|
208
|
+
const declaration = buildConstraintDeclaration(ruleDef);
|
|
209
|
+
const statement = buildSurchargeRuleStatement(declaration, ruleKey);
|
|
210
|
+
// M7: only attributes from conditions that actually emitted CML (non-null buildConditionExpression)
|
|
211
|
+
// count as "referenced". Attributes from conditions the safe-literal guard / unknown-operator
|
|
212
|
+
// filter dropped never appear in the declaration, so warning about them would be spurious noise.
|
|
213
|
+
const referencedAttributes = Array.from(collectEmittedAttributes([{ ruleDef }]));
|
|
214
|
+
return {
|
|
215
|
+
recordId: record.Id,
|
|
216
|
+
recordName: record.Name,
|
|
217
|
+
apiName,
|
|
218
|
+
ruleKey,
|
|
219
|
+
pathProductCodes: pathCodes,
|
|
220
|
+
typeName,
|
|
221
|
+
statement,
|
|
222
|
+
referencedAttributes,
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Merges the pathed surcharge `rule(...)` statements into the existing CML text. If the rule key is
|
|
228
|
+
* already present, the existing statement line is replaced in place (idempotent). Otherwise the
|
|
229
|
+
* statement is inserted just before the closing brace of the leaf `type` block. Rules whose leaf type
|
|
230
|
+
* block can't be found are skipped (reported, never silently dropped).
|
|
231
|
+
*/
|
|
232
|
+
export function mergeSurchargeRules(existingCml, rules) {
|
|
233
|
+
let cml = existingCml;
|
|
234
|
+
const placements = [];
|
|
235
|
+
const skips = [];
|
|
236
|
+
const attributeWarnings = [];
|
|
237
|
+
// Capture the curated model text BEFORE any rule statements are spliced in. The attribute-presence
|
|
238
|
+
// check must run against this baseline, not the progressively-mutated `cml`: a rule's own inserted
|
|
239
|
+
// statement always contains its referenced attribute (the declaration sanitizes attribute names the
|
|
240
|
+
// same way), so checking the mutated text would always find it and suppress every warning.
|
|
241
|
+
const baseCml = existingCml;
|
|
242
|
+
// [Fix #1] Comment-blanked, length-preserving view of the BASELINE cml — shared by every
|
|
243
|
+
// collectTypeScopeText / baseline lookup so the attribute-presence anchor regex ignores comment
|
|
244
|
+
// contents without losing offset alignment. The per-iteration scan for findTypeBlock /
|
|
245
|
+
// findSurchargeStatement is computed against the current (possibly mutated) `cml` inside the loop.
|
|
246
|
+
const baseScan = blankComments(baseCml);
|
|
247
|
+
// [Fix #4] Detect the dominant line ending ONCE so the INSERT path can splice in the model's own
|
|
248
|
+
// convention (CRLF on Windows-curated files, LF elsewhere). Before this, a hardcoded `\n` mixed
|
|
249
|
+
// bare LFs into a CRLF model and produced a byte-unclean diff. The L2 replace path already
|
|
250
|
+
// preserves the original line ending of the replaced span; this guard does the same for inserts.
|
|
251
|
+
const eol = detectDominantLineEnding(existingCml);
|
|
252
|
+
// H1: keys this run has already placed. A second rule resolving to the same pathed key must be
|
|
253
|
+
// reported as a collision skip, NOT treated as an idempotent replace of the first rule's
|
|
254
|
+
// just-inserted statement (which would silently drop the second record's distinct declaration).
|
|
255
|
+
const placedKeys = new Set();
|
|
256
|
+
for (const rule of rules) {
|
|
257
|
+
if (placedKeys.has(rule.ruleKey)) {
|
|
258
|
+
skips.push({
|
|
259
|
+
rule,
|
|
260
|
+
reason: `duplicate pathed rule key '${rule.ruleKey}' collides with another rule in this run (${rule.recordName} skipped)`,
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
// [Fix #3] Refuse the destructive replace path for malformed/empty-ProductPath input. An empty
|
|
265
|
+
// pathProductCodes degenerates the rule key to `SC__<apiName>`, which could COINCIDENTALLY match
|
|
266
|
+
// a curated short-keyed line and clobber it; an absent typeName means we'd have nowhere to
|
|
267
|
+
// re-insert and would also have no way to scope the replace to the correct block. Short-circuit
|
|
268
|
+
// these as skips BEFORE findSurchargeStatement runs so a malformed surcharge can never reach the
|
|
269
|
+
// replace splice. The pre-existing intra-run duplicate guard above is preserved.
|
|
270
|
+
if (rule.pathProductCodes.length === 0) {
|
|
271
|
+
skips.push({
|
|
272
|
+
rule,
|
|
273
|
+
reason: `empty ProductPath for ${rule.recordName}; refusing to merge a non-pathed surcharge`,
|
|
274
|
+
});
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (!rule.typeName) {
|
|
278
|
+
skips.push({ rule, reason: `no CML type tag found for the leaf product of ${rule.recordName}` });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
// [Fix #1] One comment-blanked view of the current `cml` reused by both the replace-anchor
|
|
282
|
+
// search and the type-block search this iteration. Recomputed per-iteration because a prior
|
|
283
|
+
// rule's splice may have mutated `cml`.
|
|
284
|
+
const scan = blankComments(cml);
|
|
285
|
+
// C2/M4/L1: only a REAL surcharge `rule(...)` statement carrying this exact key in the
|
|
286
|
+
// action-scope slot counts as "present". A bare quoted-key substring inside an unrelated rule's
|
|
287
|
+
// value, a longer key, or a comment must NOT trigger a destructive line replace.
|
|
288
|
+
const stmt = findSurchargeStatement(cml, rule.ruleKey, scan);
|
|
289
|
+
if (stmt) {
|
|
290
|
+
// [Fix #2] Splice ONLY the matched statement span (`rule(...);`) rather than the entire
|
|
291
|
+
// physical line. A curated line carrying two `rule(...);` statements (rare but valid) keeps
|
|
292
|
+
// the unrelated statement intact. Block formatting is preserved as long as the matched
|
|
293
|
+
// statement is the only thing on its line (the common case): the original indent prefix lives
|
|
294
|
+
// in `cml[lineStart..stmt.start)` and is left untouched by the splice; only `cml[start..end)`
|
|
295
|
+
// is replaced. If other code shares the line, that code stays in place verbatim too.
|
|
296
|
+
cml = cml.slice(0, stmt.start) + rule.statement + cml.slice(stmt.end);
|
|
297
|
+
placements.push({ rule, status: 'replaced' });
|
|
298
|
+
placedKeys.add(rule.ruleKey);
|
|
299
|
+
collectAttributeWarning(baseCml, rule, attributeWarnings, baseScan);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const block = findTypeBlock(cml, rule.typeName, scan);
|
|
303
|
+
if (!block) {
|
|
304
|
+
skips.push({ rule, reason: `type block '${rule.typeName}' not found in existing model` });
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if ('ambiguous' in block) {
|
|
308
|
+
skips.push({
|
|
309
|
+
rule,
|
|
310
|
+
reason: `type block '${rule.typeName}' is ambiguous (multiple/duplicate declarations) in existing model; skipping ${rule.recordName} rather than guessing`,
|
|
311
|
+
});
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
// Insert before the closing brace, indented one level (4 spaces), with a leading blank line.
|
|
315
|
+
// [Fix #4] Use the dominant line ending of the original model, not a hardcoded `\n`.
|
|
316
|
+
const insertion = `${eol} ${rule.statement}${eol}`;
|
|
317
|
+
cml = cml.slice(0, block.closeIdx) + insertion + cml.slice(block.closeIdx);
|
|
318
|
+
placements.push({ rule, status: 'inserted' });
|
|
319
|
+
placedKeys.add(rule.ruleKey);
|
|
320
|
+
collectAttributeWarning(baseCml, rule, attributeWarnings, baseScan);
|
|
321
|
+
}
|
|
322
|
+
return { mergedCml: cml, placements, skips, attributeWarnings };
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* C2/M4/L1: finds the single-line surcharge `rule(...)` statement that carries `ruleKey` in the
|
|
326
|
+
* action-scope slot — i.e. `rule(<decl>, "InsuranceSurchargeRule", "<ruleKey>", ...`. Only such a
|
|
327
|
+
* real statement is a legitimate replace target. A bare `"<ruleKey>"` substring appearing inside an
|
|
328
|
+
* unrelated rule's VALUE, inside a `//` line comment, inside a `/* *\/` block comment, or as part of
|
|
329
|
+
* a LONGER key is deliberately NOT matched, so the caller falls through to the insert path instead of
|
|
330
|
+
* clobbering curated text.
|
|
331
|
+
*
|
|
332
|
+
* Comment-awareness: the anchor scan runs against a length-preserving COPY of `cml` in which every
|
|
333
|
+
* character inside a `//`/`/* *\/` comment is blanked to a space (newlines kept). Offsets into that
|
|
334
|
+
* copy therefore map 1:1 onto the original, so the returned line span still slices the real text —
|
|
335
|
+
* but a rule-shaped string sitting inside a (single- or multi-line) block comment can no longer match
|
|
336
|
+
* the anchor and be clobbered.
|
|
337
|
+
*
|
|
338
|
+
* Returns the start/end offsets of the matched line (lineEnd points at the newline / EOF, excluding
|
|
339
|
+
* any trailing `\r` so the caller can re-emit the original CRLF/LF).
|
|
340
|
+
*/
|
|
341
|
+
function findSurchargeStatement(cml, ruleKey, scan = blankComments(cml)) {
|
|
342
|
+
// [Fix #2] Match the surcharge rule statement and return the PRECISE span from the `rule(` token
|
|
343
|
+
// to just past its terminating `;`. The previous implementation returned a whole-line span which
|
|
344
|
+
// clobbered any OTHER `rule(...);` statement sharing the same physical line. The global flag is
|
|
345
|
+
// required so a coincidental earlier match (e.g. an unrelated rule whose VALUE quotes the key
|
|
346
|
+
// before the real statement) can be skipped by walking to subsequent matches via `exec`. We do
|
|
347
|
+
// NOT use a single-line anchor: a real surcharge statement is single-line by convention, but the
|
|
348
|
+
// anchor's `[^;\r\n]*` constraint already guarantees the prefix is single-line; what matters here
|
|
349
|
+
// is bounding the SPAN, not the search.
|
|
350
|
+
const anchor = new RegExp(`rule\\([^;\\r\\n]*"${escapeRegExp(SURCHARGE_RULE_ACTION)}"\\s*,\\s*"${escapeRegExp(ruleKey)}"\\s*,`, 'g');
|
|
351
|
+
let m;
|
|
352
|
+
while ((m = anchor.exec(scan)) !== null) {
|
|
353
|
+
const start = m.index;
|
|
354
|
+
// Walk forward in the SCAN view (comment chars blanked) to find the terminating `;`. Using scan
|
|
355
|
+
// keeps a `;` that lives inside a `// ...` or `/* ... */` comment from terminating the span.
|
|
356
|
+
// String-literal contents within the statement are kept intact by blankComments, so a `;` inside
|
|
357
|
+
// a quoted RHS value is also ignored — find the structural `;` by walking past string literals.
|
|
358
|
+
const semi = findStructuralSemicolon(scan, m.index + m[0].length);
|
|
359
|
+
if (semi === undefined) {
|
|
360
|
+
// Malformed unterminated rule(...) — refuse to splice rather than guessing.
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
return { start, end: semi + 1 };
|
|
364
|
+
}
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* [Fix #2] Walks forward from `from` returning the index of the structural `;` terminating the
|
|
369
|
+
* current statement. The scan is comment-blanked, so `;` inside `//` or `/* *\/` cannot terminate.
|
|
370
|
+
* Double-quoted string literals are still present in the scan (blankComments only blanks comments);
|
|
371
|
+
* skip them here so a `;` inside a quoted value is ignored.
|
|
372
|
+
*/
|
|
373
|
+
function findStructuralSemicolon(scan, from) {
|
|
374
|
+
let inString = false;
|
|
375
|
+
for (let i = from; i < scan.length; i++) {
|
|
376
|
+
const ch = scan[i];
|
|
377
|
+
if (inString) {
|
|
378
|
+
if (ch === '\\') {
|
|
379
|
+
i++;
|
|
380
|
+
}
|
|
381
|
+
else if (ch === '"') {
|
|
382
|
+
inString = false;
|
|
383
|
+
}
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (ch === '"') {
|
|
387
|
+
inString = true;
|
|
388
|
+
}
|
|
389
|
+
else if (ch === ';') {
|
|
390
|
+
return i;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Returns a copy of `cml` with identical length where every character inside a `//` line comment or
|
|
397
|
+
* a `/* *\/` block comment is replaced by a space (newlines preserved). Used so the replace-anchor
|
|
398
|
+
* scan can ignore comment contents without losing offset alignment with the original text. String
|
|
399
|
+
* literals are left intact: the anchor itself requires the literal `"InsuranceSurchargeRule"` action
|
|
400
|
+
* token, so a same-key VALUE inside another rule's string still won't satisfy the action-scope shape.
|
|
401
|
+
*/
|
|
402
|
+
function blankComments(cml) {
|
|
403
|
+
const out = cml.split('');
|
|
404
|
+
let inString = false;
|
|
405
|
+
let inLineComment = false;
|
|
406
|
+
let inBlockComment = false;
|
|
407
|
+
for (let i = 0; i < cml.length; i++) {
|
|
408
|
+
const ch = cml[i];
|
|
409
|
+
const next = cml[i + 1];
|
|
410
|
+
if (inLineComment) {
|
|
411
|
+
if (ch === '\n')
|
|
412
|
+
inLineComment = false;
|
|
413
|
+
else
|
|
414
|
+
out[i] = ' ';
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
if (inBlockComment) {
|
|
418
|
+
if (ch === '*' && next === '/') {
|
|
419
|
+
out[i] = ' ';
|
|
420
|
+
out[i + 1] = ' ';
|
|
421
|
+
i++;
|
|
422
|
+
inBlockComment = false;
|
|
423
|
+
}
|
|
424
|
+
else if (ch !== '\n') {
|
|
425
|
+
out[i] = ' ';
|
|
426
|
+
}
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (inString) {
|
|
430
|
+
if (ch === '\\') {
|
|
431
|
+
i++; // skip escaped char (left intact)
|
|
432
|
+
}
|
|
433
|
+
else if (ch === '"') {
|
|
434
|
+
inString = false;
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (ch === '/' && next === '/') {
|
|
439
|
+
inLineComment = true;
|
|
440
|
+
out[i] = ' ';
|
|
441
|
+
out[i + 1] = ' ';
|
|
442
|
+
i++;
|
|
443
|
+
}
|
|
444
|
+
else if (ch === '/' && next === '*') {
|
|
445
|
+
inBlockComment = true;
|
|
446
|
+
out[i] = ' ';
|
|
447
|
+
out[i + 1] = ' ';
|
|
448
|
+
i++;
|
|
449
|
+
}
|
|
450
|
+
else if (ch === '"') {
|
|
451
|
+
inString = true;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return out.join('');
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Surfaces (does not auto-fix) rule declarations that reference an attribute not present anywhere in
|
|
458
|
+
* the curated model. In merge mode we never inject `string <attr>;` into the curated model, so an
|
|
459
|
+
* unknown attribute would fail to compile on import — better to warn the engineer than to mangle the
|
|
460
|
+
* Gold Standard. Attributes already present are trusted to be visible via the coverage type hierarchy.
|
|
461
|
+
*
|
|
462
|
+
* IMPORTANT: `baseCml` must be the ORIGINAL model text, captured before any surcharge statement was
|
|
463
|
+
* spliced in. Checking the post-insertion text would always find the attribute inside the rule's own
|
|
464
|
+
* just-inserted declaration (which sanitizes attribute names identically), suppressing every warning.
|
|
465
|
+
*/
|
|
466
|
+
function collectAttributeWarning(baseCml, rule, warnings, baseScan) {
|
|
467
|
+
// H5/M3: scope the presence check to the leaf type block plus its `: Parent` ancestry, with
|
|
468
|
+
// comments and string literals stripped. CML attribute visibility is hierarchy-scoped, so an
|
|
469
|
+
// unscoped whole-file `\battr\b` test gives false negatives (an attribute named only in a comment,
|
|
470
|
+
// a string value, or an unrelated SIBLING type would wrongly suppress a real absent-attribute
|
|
471
|
+
// warning).
|
|
472
|
+
//
|
|
473
|
+
// H5 (fallback facet): when the leaf scope can't be resolved — typeName is undefined, or the leaf
|
|
474
|
+
// `type` block is ambiguous/duplicate so collectTypeScopeText returns undefined — we have NO
|
|
475
|
+
// hierarchy-scoped view to prove the attribute is visible. We must NOT widen to the whole model
|
|
476
|
+
// (that re-introduces the sibling-type false negative on exactly the records that hit the replace
|
|
477
|
+
// path before type resolution). Fail VISIBLE instead: treat the scope as empty so an attribute we
|
|
478
|
+
// cannot prove visible is reported, never silently suppressed.
|
|
479
|
+
const scope = rule.typeName ? collectTypeScopeText(baseCml, rule.typeName, undefined, baseScan) ?? '' : '';
|
|
480
|
+
for (const attr of rule.referencedAttributes) {
|
|
481
|
+
const present = new RegExp(`\\b${escapeRegExp(attr)}\\b`).test(scope);
|
|
482
|
+
if (!present) {
|
|
483
|
+
warnings.push(`${rule.recordName}: declaration references '${attr}' which is absent from the model`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Removes `//` line comments, `/* *\/` block comments, and double-quoted string-literal CONTENTS
|
|
489
|
+
* from CML text so that attribute-presence checks match only real declarations/expressions, never a
|
|
490
|
+
* name that merely appears in a comment or a string value. String delimiters are kept so structure
|
|
491
|
+
* is preserved; only the interior characters are blanked.
|
|
492
|
+
*/
|
|
493
|
+
function stripCommentsAndStrings(cml) {
|
|
494
|
+
let out = '';
|
|
495
|
+
let inString = false;
|
|
496
|
+
let inLineComment = false;
|
|
497
|
+
let inBlockComment = false;
|
|
498
|
+
for (let i = 0; i < cml.length; i++) {
|
|
499
|
+
const ch = cml[i];
|
|
500
|
+
const next = cml[i + 1];
|
|
501
|
+
if (inLineComment) {
|
|
502
|
+
if (ch === '\n') {
|
|
503
|
+
inLineComment = false;
|
|
504
|
+
out += ch;
|
|
505
|
+
}
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (inBlockComment) {
|
|
509
|
+
if (ch === '*' && next === '/') {
|
|
510
|
+
inBlockComment = false;
|
|
511
|
+
i++;
|
|
512
|
+
}
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (inString) {
|
|
516
|
+
if (ch === '\\') {
|
|
517
|
+
i++; // skip escaped char (and drop it)
|
|
518
|
+
}
|
|
519
|
+
else if (ch === '"') {
|
|
520
|
+
inString = false;
|
|
521
|
+
out += '"';
|
|
522
|
+
}
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (ch === '/' && next === '/') {
|
|
526
|
+
inLineComment = true;
|
|
527
|
+
i++;
|
|
528
|
+
}
|
|
529
|
+
else if (ch === '/' && next === '*') {
|
|
530
|
+
inBlockComment = true;
|
|
531
|
+
i++;
|
|
532
|
+
}
|
|
533
|
+
else if (ch === '"') {
|
|
534
|
+
inString = true;
|
|
535
|
+
out += '"';
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
out += ch;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return out;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Builds the comment/string-stripped text of the leaf `type <name> { ... }` block plus every
|
|
545
|
+
* `: Parent` ancestor block reachable in the model, concatenated. Returns undefined when the leaf
|
|
546
|
+
* block can't be resolved (or is ambiguous), so the caller can fall back. Bounded against cycles by
|
|
547
|
+
* a visited set.
|
|
548
|
+
*/
|
|
549
|
+
function collectTypeScopeText(cml, leafType, visited = new Set(), scan = blankComments(cml)) {
|
|
550
|
+
if (visited.has(leafType))
|
|
551
|
+
return '';
|
|
552
|
+
visited.add(leafType);
|
|
553
|
+
const block = findTypeBlock(cml, leafType, scan);
|
|
554
|
+
if (!block || 'ambiguous' in block)
|
|
555
|
+
return undefined;
|
|
556
|
+
const headerStart = cml.lastIndexOf('\n', block.openIdx) + 1;
|
|
557
|
+
const header = cml.slice(headerStart, block.openIdx);
|
|
558
|
+
const body = cml.slice(block.openIdx, block.closeIdx + 1);
|
|
559
|
+
let text = stripCommentsAndStrings(body);
|
|
560
|
+
// Resolve `type Leaf : Parent {` ancestry and append parent scope(s).
|
|
561
|
+
const parentMatch = /:\s*([A-Za-z_]\w*)/.exec(header);
|
|
562
|
+
if (parentMatch) {
|
|
563
|
+
const parentText = collectTypeScopeText(cml, parentMatch[1], visited, scan);
|
|
564
|
+
if (parentText)
|
|
565
|
+
text += '\n' + parentText;
|
|
566
|
+
}
|
|
567
|
+
return text;
|
|
568
|
+
}
|
|
569
|
+
//# sourceMappingURL=insurance-cml-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insurance-cml-merge.js","sourceRoot":"","sources":["../../../src/shared/insurance/insurance-cml-merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,5 +5,23 @@ import { Connection } from '@salesforce/core';
|
|
|
5
5
|
* --uw-file, so validating here prevents SOQL injection through the `IN (...)` clause.
|
|
6
6
|
*/
|
|
7
7
|
export declare function quoteSoqlIdList(ids: Iterable<string>): string;
|
|
8
|
-
|
|
8
|
+
/** Optional hooks for {@link fetchProductCodes}. Additive so existing callers need no changes. */
|
|
9
|
+
export type FetchProductCodesOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Invoked once per product id whose code had to be derived from a fallback (Name, then Id)
|
|
12
|
+
* because `ProductCode` was blank. The pathed rule key is built from these codes, so a fallback
|
|
13
|
+
* silently yields a key that will NOT match the platform's auto-generated RuleKey — the rule then
|
|
14
|
+
* imports cleanly but never fires. Surfacing the fallback lets the command layer warn instead of
|
|
15
|
+
* dropping the surcharge silently (M2).
|
|
16
|
+
*/
|
|
17
|
+
onFallback?: (productId: string) => void;
|
|
18
|
+
/**
|
|
19
|
+
* When provided, populated with each product's Name (Id -> Name) from the same query. The common
|
|
20
|
+
* `cml import as-expression-set` resolves a Type association's Product2 by Name, so the convert
|
|
21
|
+
* layer must emit Name — not ProductCode — as the association reference value. Collected here to
|
|
22
|
+
* avoid a second round-trip. Products with a null Name are omitted (they cannot match by name).
|
|
23
|
+
*/
|
|
24
|
+
collectNames?: Map<string, string>;
|
|
25
|
+
};
|
|
26
|
+
export declare function fetchProductCodes(conn: Connection, productIds: Set<string>, options?: FetchProductCodesOptions): Promise<Map<string, string>>;
|
|
9
27
|
export declare function discoverCmlApiByProducts(conn: Connection, productIds: Set<string>): Promise<string | undefined>;
|
|
@@ -11,14 +11,22 @@ export function quoteSoqlIdList(ids) {
|
|
|
11
11
|
.map((id) => `'${id}'`)
|
|
12
12
|
.join(',');
|
|
13
13
|
}
|
|
14
|
-
export async function fetchProductCodes(conn, productIds) {
|
|
14
|
+
export async function fetchProductCodes(conn, productIds, options = {}) {
|
|
15
15
|
const idToCode = new Map();
|
|
16
16
|
const idList = quoteSoqlIdList(productIds);
|
|
17
17
|
if (!idList)
|
|
18
18
|
return idToCode;
|
|
19
19
|
const result = await conn.query(`SELECT Id, ProductCode, Name FROM Product2 WHERE Id IN (${idList})`);
|
|
20
20
|
for (const p of result.records) {
|
|
21
|
+
// Keep the fallback chain (don't break callers), but make it observable: a null ProductCode
|
|
22
|
+
// means the resolved code did NOT come from the platform-authoritative field.
|
|
23
|
+
if (p.ProductCode == null) {
|
|
24
|
+
options.onFallback?.(p.Id);
|
|
25
|
+
}
|
|
21
26
|
idToCode.set(p.Id, p.ProductCode ?? p.Name ?? p.Id);
|
|
27
|
+
if (p.Name != null) {
|
|
28
|
+
options.collectNames?.set(p.Id, p.Name);
|
|
29
|
+
}
|
|
22
30
|
}
|
|
23
31
|
return idToCode;
|
|
24
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insurance-org.js","sourceRoot":"","sources":["../../../src/shared/insurance/insurance-org.ts"],"names":[],"mappings":"AAiBA,sEAAsE;AACtE,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAqB;IACnD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACnB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC9C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;SACtB,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;
|
|
1
|
+
{"version":3,"file":"insurance-org.js","sourceRoot":"","sources":["../../../src/shared/insurance/insurance-org.ts"],"names":[],"mappings":"AAiBA,sEAAsE;AACtE,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAqB;IACnD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SACnB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC9C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;SACtB,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAqBD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAgB,EAChB,UAAuB,EACvB,UAAoC,EAAE;IAEtC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAE7B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,2DAA2D,MAAM,GAAG,CACrE,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,4FAA4F;QAC5F,8EAA8E;QAC9E,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;YAC1B,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;YACnB,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAgB,EAAE,UAAuB;IACtF,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,KAAK,CAClC,sFAAsF,MAAM,+CAA+C,CAC5I,CAAC;IACF,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;IACpD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAExD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAsB,iDAAiD,IAAI,GAAG,CAAC,CAAC;IACjH,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEpD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACrC,CAAC"}
|