@uipath/maestro-builder-sdk 6.65.0 → 6.66.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/dist/bpmn/bpmn-expr-check.d.ts +2 -1
- package/dist/bpmn/bpmn-expr-check.js +4 -2
- package/dist/bpmn/bpmn-sdk.js +57 -47
- package/dist/bpmn/connector.js +2 -1
- package/dist/bpmn/human-task-form.js +16 -15
- package/dist/bpmn/intsvc.js +19 -18
- package/dist/bpmn/load.js +4 -3
- package/dist/bpmn/serialize.js +29 -13
- package/dist/bpmn/typed-node.js +8 -7
- package/dist/bpmn/xml-ranges.d.ts +0 -19
- package/dist/bpmn/xml-ranges.js +5 -4
- package/dist/core/refusals.d.ts +130 -1
- package/dist/core/refusals.js +134 -5
- package/package.json +2 -2
package/dist/bpmn/intsvc.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createTypedNode, typedNodeOutputVar } from './typed-node.js';
|
|
2
2
|
import { connectorBindingRef } from './connector.js';
|
|
3
|
-
import { eventFilterJmes,
|
|
3
|
+
import { eventFilterJmes, jmesString, jmesStringProblem, readEventFilterLeaf } from '../core/event-filters.js';
|
|
4
4
|
import { resolveEventWhere } from '../core/lookups.js';
|
|
5
|
+
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
5
6
|
/**
|
|
6
7
|
* Which extension type an external invocation lowers to.
|
|
7
8
|
*
|
|
@@ -57,14 +58,14 @@ export function intsvcOutputVar(n) {
|
|
|
57
58
|
*/
|
|
58
59
|
export function createConnectorEvent(ctx, n) {
|
|
59
60
|
if (!ctx.library)
|
|
60
|
-
throw new
|
|
61
|
+
throw new CompileRefusal('LIBRARY_REQUIRED', `connector event "${n.id}" needs the connector library — pass { library } to serialize ` +
|
|
61
62
|
`or use uip maestro bpmn compile --library.`);
|
|
62
63
|
const rc = ctx.library.resolveEvent(n.subscribe, n.key, n.eventName, n.version, n.object);
|
|
63
64
|
const op = (rc.entry.operation ?? {});
|
|
64
65
|
const runtime = rc.entry.runtime ?? {};
|
|
65
66
|
const type = runtime.serviceType;
|
|
66
67
|
if (!type) {
|
|
67
|
-
throw new
|
|
68
|
+
throw new CompileRefusal('BPMN_EVENT_NO_SERVICE_TYPE', `connector event ${n.key}/${n.eventName} has no runtime.serviceType in the library; ` +
|
|
68
69
|
`it cannot be authored as a BPMN node.`);
|
|
69
70
|
}
|
|
70
71
|
// The PLACEMENT decides the extension type — `.eventTrigger()` starts the
|
|
@@ -79,13 +80,13 @@ export function createConnectorEvent(ctx, n) {
|
|
|
79
80
|
// .eventTrigger() instead", while the unprepared same node compiled.
|
|
80
81
|
const expected = intsvcServiceType(n);
|
|
81
82
|
if (type !== 'Intsvc.EventTrigger' && type !== 'Intsvc.WaitForEvent') {
|
|
82
|
-
throw new
|
|
83
|
+
throw new CompileRefusal('BPMN_EVENT_KIND_MISMATCH', `connector event "${n.id}" was authored as a ${n.subscribe} (${expected}), but the library ` +
|
|
83
84
|
`describes ${n.key}/${n.eventName} as ${type}, which is not a connector event.`);
|
|
84
85
|
}
|
|
85
86
|
const conn = connectorBindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
|
|
86
87
|
const folder = connectorBindingRef(ctx, n.folder, 'Connection', 'folderKey', 'FolderKey', conn?.resourceKey);
|
|
87
88
|
if (!conn || !folder) {
|
|
88
|
-
throw new
|
|
89
|
+
throw new CompileRefusal('BPMN_EVENT_BINDINGS_MISSING', `connector event "${n.id}" needs both a connection and a folder binding name — ` +
|
|
89
90
|
`the type declares connectionId, folderId, folderPath and folderKey all required.`);
|
|
90
91
|
}
|
|
91
92
|
// The registry marks `operation` and `objectName` required AND hidden, so an
|
|
@@ -144,20 +145,20 @@ export function eventFilterBody(nodeId, scope, filters) {
|
|
|
144
145
|
const scopeClauses = scopeEntries.map(([k, v]) => {
|
|
145
146
|
const problem = jmesStringProblem(String(v));
|
|
146
147
|
if (problem)
|
|
147
|
-
throw new
|
|
148
|
+
throw new CompileRefusal('EVENT_FILTER_BACKSLASH', `connector event "${nodeId}": \`filter.${k}\`: ${problem}`);
|
|
148
149
|
return `(${k} == ${jmesString(String(v))})`;
|
|
149
150
|
});
|
|
150
151
|
const conditions = (filters ?? []).map((leaf, i) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`
|
|
152
|
+
// The code `check` gives the same leaf (`core/event-checks.ts`).
|
|
153
|
+
const judged = readEventFilterLeaf(leaf);
|
|
154
|
+
if (judged.code !== undefined) {
|
|
155
|
+
throw new CompileRefusal(judged.code, judged.problem !== undefined
|
|
156
|
+
? `connector event "${nodeId}": filters[${i}] on \`${judged.read?.field}\`: ${judged.problem}`
|
|
157
|
+
: `connector event "${nodeId}": filters[${i}] must carry a field and exactly ONE operator ` +
|
|
158
|
+
`(contains, startsWith, endsWith, equals, notEquals, lessThan, lessThanOrEqual, greaterThan, ` +
|
|
159
|
+
`greaterThanOrEqual) — got ${JSON.stringify(leaf)}.`);
|
|
156
160
|
}
|
|
157
|
-
|
|
158
|
-
if (problem)
|
|
159
|
-
throw new Error(`connector event "${nodeId}": filters[${i}] on \`${read.field}\`: ${problem}`);
|
|
160
|
-
return `(${eventFilterJmes(read)})`;
|
|
161
|
+
return `(${eventFilterJmes(judged.read)})`;
|
|
161
162
|
});
|
|
162
163
|
const expression = [...scopeClauses, ...conditions].join(' && ');
|
|
163
164
|
const body = {};
|
|
@@ -176,7 +177,7 @@ export function eventFilterBody(nodeId, scope, filters) {
|
|
|
176
177
|
*/
|
|
177
178
|
export function createExternalTask(ctx, n) {
|
|
178
179
|
if (!ctx.library)
|
|
179
|
-
throw new
|
|
180
|
+
throw new CompileRefusal('LIBRARY_REQUIRED', `external ${n.family} "${n.id}" needs the connector library — pass { library } to serialize ` +
|
|
180
181
|
`or use uip maestro bpmn compile --library.`);
|
|
181
182
|
const rc = ctx.library.resolve(n.key, n.action, n.version);
|
|
182
183
|
const op = (rc.entry.operation ?? {});
|
|
@@ -186,14 +187,14 @@ export function createExternalTask(ctx, n) {
|
|
|
186
187
|
// validator accepts and the runtime dispatches to the wrong handler family —
|
|
187
188
|
// the Case SDK records the same trap in its Phase 4.4 notes.
|
|
188
189
|
if (op.subType !== wanted) {
|
|
189
|
-
throw new
|
|
190
|
+
throw new CompileRefusal('EXTERNAL_TASK_DESCRIPTOR_CONTRACT', `external ${n.family} "${n.id}" needs ${wanted === 'AgentExecution' ? 'an' : 'a'} ${wanted} descriptor, but ` +
|
|
190
191
|
`${n.key}/${n.action} is "${String(op.subType ?? 'untyped')}". ` +
|
|
191
192
|
`The shipped library has very few of these — ${wanted === 'AgentExecution' ? '2' : '1'} operation(s).`);
|
|
192
193
|
}
|
|
193
194
|
const conn = connectorBindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
|
|
194
195
|
const folder = connectorBindingRef(ctx, n.folder, 'Connection', 'folderKey', 'FolderKey', conn?.resourceKey);
|
|
195
196
|
if (!conn || !folder) {
|
|
196
|
-
throw new
|
|
197
|
+
throw new CompileRefusal(conn ? 'EXTERNAL_TASK_FOLDER' : 'EXTERNAL_TASK_CONNECTION', `external ${n.family} "${n.id}" needs both a connection and a folder binding name.`);
|
|
197
198
|
}
|
|
198
199
|
return createTypedNode(ctx.m, n.id, EXTERNAL_SERVICE_TYPE[n.family][n.mode], {
|
|
199
200
|
name: n.name ?? rc.entry.display?.label ?? n.action,
|
package/dist/bpmn/load.js
CHANGED
|
@@ -3,22 +3,23 @@ import { pathToFileURL } from 'node:url';
|
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
5
|
import { useThisSdkForAuthoredSource } from '../sdk-resolve.js';
|
|
6
|
+
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
6
7
|
/** Resolve a user argument (a `.ts` path or a BaseName) to a bpmn source file. */
|
|
7
8
|
export function resolveBpmnFile(arg) {
|
|
8
9
|
const candidates = [arg, `${arg}.bpmn.ts`, `${arg}.ts`];
|
|
9
10
|
for (const c of candidates)
|
|
10
11
|
if (existsSync(resolve(c)))
|
|
11
12
|
return resolve(c);
|
|
12
|
-
throw new
|
|
13
|
+
throw new CompileRefusal('BPMN_SOURCE_NOT_FOUND', `bpmn source not found: tried ${candidates.join(', ')}`);
|
|
13
14
|
}
|
|
14
15
|
export async function loadBuiltBpmn(file) {
|
|
15
16
|
useThisSdkForAuthoredSource();
|
|
16
17
|
const mod = await import(pathToFileURL(file).href);
|
|
17
18
|
const exp = mod.default ?? mod.bpmn;
|
|
18
19
|
if (!exp)
|
|
19
|
-
throw new
|
|
20
|
+
throw new CompileRefusal('BPMN_NO_DEFAULT_EXPORT', `${file} has no default export. Add: export default bpmn('id')...build();`);
|
|
20
21
|
const built = typeof exp.build === 'function' ? exp.build() : exp;
|
|
21
22
|
if (!built || !Array.isArray(built.nodes))
|
|
22
|
-
throw new
|
|
23
|
+
throw new CompileRefusal('BPMN_EXPORT_NOT_BUILT', `${file} default export is not a built bpmn. Did you call .build()?`);
|
|
23
24
|
return built;
|
|
24
25
|
}
|
package/dist/bpmn/serialize.js
CHANGED
|
@@ -22,6 +22,7 @@ import { connectorOutputVars, typedNodeDeclaredVars } from './declared-vars.js';
|
|
|
22
22
|
import { defaultEntryPointId } from './entry-point-id.js';
|
|
23
23
|
import { parseXmlRanges } from './xml-ranges.js';
|
|
24
24
|
import { ownEntry } from '../core/own-entry.js';
|
|
25
|
+
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
25
26
|
const require = createRequire(import.meta.url);
|
|
26
27
|
const uipathDescriptor = require('./uipath-moddle.v1.json');
|
|
27
28
|
const NS = {
|
|
@@ -251,7 +252,16 @@ function jsonInputBodiesAsCdata(xml) {
|
|
|
251
252
|
collect(node.children);
|
|
252
253
|
}
|
|
253
254
|
};
|
|
254
|
-
|
|
255
|
+
let ranges;
|
|
256
|
+
try {
|
|
257
|
+
ranges = parseXmlRanges(xml);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
// `xml` is moddle's own output, so malformed XML here is the serializer's
|
|
261
|
+
// defect, not the author's. BPMN_XML_MALFORMED stays for what `bpmn merge` reads.
|
|
262
|
+
throw new Error(`internal SDK error: the serializer wrote malformed XML: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
263
|
+
}
|
|
264
|
+
collect(ranges);
|
|
255
265
|
let out = xml;
|
|
256
266
|
// Splice from the end so the earlier offsets stay valid.
|
|
257
267
|
for (const input of bodies.sort((a, b) => b.innerStart - a.innerStart)) {
|
|
@@ -289,12 +299,12 @@ function cdata(name, text) {
|
|
|
289
299
|
value = JSON.parse(text);
|
|
290
300
|
}
|
|
291
301
|
catch {
|
|
292
|
-
throw new
|
|
302
|
+
throw new CompileRefusal('BPMN_JSON_INPUT_CDATA_END', `json input "${name}" contains "]]>" and is not valid JSON, so it cannot be written as the single CDATA section the engine reads.`);
|
|
293
303
|
}
|
|
294
304
|
const escaped = text.replaceAll(']]>', ']]\\u003e');
|
|
295
305
|
// Holds by the argument above; checked because a wrong payload here would be silent.
|
|
296
306
|
if (JSON.stringify(JSON.parse(escaped)) !== JSON.stringify(value)) {
|
|
297
|
-
throw new Error(`json input "${name}": escaping "]]>" changed its value.`);
|
|
307
|
+
throw new Error(`internal SDK error: json input "${name}": escaping "]]>" changed its value.`);
|
|
298
308
|
}
|
|
299
309
|
return `<![CDATA[${escaped}]]>`;
|
|
300
310
|
}
|
|
@@ -356,30 +366,35 @@ function stampEntryPointIds(ctx, built, flowElements, rootDefault) {
|
|
|
356
366
|
/** Build the flowElements array for one scope, wiring all references within it. */
|
|
357
367
|
function buildScope(ctx, nodes, flows) {
|
|
358
368
|
const { m } = ctx;
|
|
359
|
-
const
|
|
369
|
+
const nodeById = new Map();
|
|
370
|
+
const flowById = new Map();
|
|
360
371
|
const nodeEls = nodes.map((n) => {
|
|
361
372
|
const el = createNode(ctx, n);
|
|
362
|
-
|
|
373
|
+
nodeById.set(n.id, el);
|
|
363
374
|
return el;
|
|
364
375
|
});
|
|
365
376
|
const flowEls = flows.map((f) => {
|
|
366
377
|
const el = m.create('bpmn:SequenceFlow', { id: f.id, name: f.name });
|
|
367
378
|
if (f.condition !== undefined)
|
|
368
379
|
el.conditionExpression = m.create('bpmn:FormalExpression', { body: f.condition });
|
|
369
|
-
|
|
380
|
+
flowById.set(f.id, el);
|
|
370
381
|
return el;
|
|
371
382
|
});
|
|
372
|
-
|
|
383
|
+
// A flow endpoint and a boundary attachment name a node in this scope, as
|
|
384
|
+
// `check` resolves them (DANGLING_FLOW, BOUNDARY_UNATTACHED); a sequence flow
|
|
385
|
+
// is never one. A gateway default names a flow, or any element as before.
|
|
386
|
+
const ref = (byId, id, where, code) => {
|
|
373
387
|
const el = byId.get(id);
|
|
374
388
|
if (!el)
|
|
375
|
-
throw new
|
|
389
|
+
throw new CompileRefusal(code, `${where} references unknown element id "${id}".`);
|
|
376
390
|
return el;
|
|
377
391
|
};
|
|
392
|
+
const anyById = new Map([...nodeById, ...flowById]);
|
|
378
393
|
// Wire sequence flows to source/target and populate incoming/outgoing.
|
|
379
394
|
flows.forEach((f, i) => {
|
|
380
395
|
const flowEl = flowEls[i];
|
|
381
|
-
const src = ref(f.source, `sequenceFlow "${f.id}"
|
|
382
|
-
const tgt = ref(f.target, `sequenceFlow "${f.id}"
|
|
396
|
+
const src = ref(nodeById, f.source, `sequenceFlow "${f.id}"`, 'DANGLING_FLOW');
|
|
397
|
+
const tgt = ref(nodeById, f.target, `sequenceFlow "${f.id}"`, 'DANGLING_FLOW');
|
|
383
398
|
flowEl.sourceRef = src;
|
|
384
399
|
flowEl.targetRef = tgt;
|
|
385
400
|
(src.outgoing ??= []).push(flowEl);
|
|
@@ -389,9 +404,9 @@ function buildScope(ctx, nodes, flows) {
|
|
|
389
404
|
nodes.forEach((n, i) => {
|
|
390
405
|
const el = nodeEls[i];
|
|
391
406
|
if (n.kind === 'boundaryEvent')
|
|
392
|
-
el.attachedToRef = ref(n.attachedTo, `boundaryEvent "${n.id}"
|
|
407
|
+
el.attachedToRef = ref(nodeById, n.attachedTo, `boundaryEvent "${n.id}"`, 'BOUNDARY_UNATTACHED');
|
|
393
408
|
if ((n.kind === 'exclusiveGateway' || n.kind === 'inclusiveGateway') && n.default !== undefined)
|
|
394
|
-
el.default = ref(n.default, `gateway "${n.id}" default
|
|
409
|
+
el.default = ref(anyById, n.default, `gateway "${n.id}" default`, 'BPMN_GATEWAY_DEFAULT_UNKNOWN');
|
|
395
410
|
});
|
|
396
411
|
return [...nodeEls, ...flowEls];
|
|
397
412
|
}
|
|
@@ -729,8 +744,9 @@ function eventDefinition(ctx, def) {
|
|
|
729
744
|
*/
|
|
730
745
|
function moddleTaskType(element) {
|
|
731
746
|
const [ns, local] = element.split(':');
|
|
747
|
+
// `.plainTask()` refuses any element outside `PlainTaskElement` (BPMN_PLAIN_TASK_ELEMENT_INVALID).
|
|
732
748
|
if (!ns || !local)
|
|
733
|
-
throw new Error(`plainTask: "${element}" is not a qualified element name`);
|
|
749
|
+
throw new Error(`internal SDK error: plainTask: "${element}" is not a qualified element name`);
|
|
734
750
|
return `${ns}:${local.charAt(0).toUpperCase()}${local.slice(1)}`;
|
|
735
751
|
}
|
|
736
752
|
function outputRows(ctx, map, custom = false) {
|
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -3,6 +3,7 @@ import { resolveFromRegistry } from './registry-fallback.js';
|
|
|
3
3
|
import { nestConnectorBody } from '../core/connector-inputs.js';
|
|
4
4
|
import { types } from '../core/expr.js';
|
|
5
5
|
import { ownEntry } from '../core/own-entry.js';
|
|
6
|
+
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
6
7
|
const require = createRequire(import.meta.url);
|
|
7
8
|
const REGISTRY = require('./registry-types.json');
|
|
8
9
|
/** Every extension type in the snapshot, sorted. */
|
|
@@ -33,7 +34,7 @@ export function registryType(type, inline) {
|
|
|
33
34
|
// for one the snapshot describes wrongly. See `ActivityNodeOpts.spec`.
|
|
34
35
|
const spec = inline ?? REGISTRY.types[type] ?? resolveFromRegistry(type);
|
|
35
36
|
if (!spec) {
|
|
36
|
-
throw new
|
|
37
|
+
throw new CompileRefusal('BPMN_TYPE_UNKNOWN', `unknown extension type "${type}". The committed registry snapshot has ${REGISTRY.provenance.typeCount} types `
|
|
37
38
|
+ `(uip ${REGISTRY.provenance.uipVersion}) and the registry could not be asked for this one either — `
|
|
38
39
|
+ 'no `uip` on PATH, not logged in, or the type does not exist. '
|
|
39
40
|
+ 'Check the spelling with `uip maestro bpmn registry search <term>`, then refresh the snapshot with '
|
|
@@ -306,7 +307,7 @@ function contextRows(m, type, spec, supplied, verbatim = false) {
|
|
|
306
307
|
]);
|
|
307
308
|
const unknown = Object.keys(supplied).filter((k) => !known.has(k));
|
|
308
309
|
if (unknown.length) {
|
|
309
|
-
throw new
|
|
310
|
+
throw new CompileRefusal('BPMN_CONTEXT_FIELD_UNKNOWN', `context field(s) ${unknown.map((u) => `"${u}"`).join(', ')} are not declared by this extension type. ` +
|
|
310
311
|
`Declared: ${[...known].join(', ') || '(none)'}.`);
|
|
311
312
|
}
|
|
312
313
|
// Permitted-but-undeclared fields (a recorded `extraContext` gap) still have to
|
|
@@ -355,7 +356,7 @@ function inputRows(m, spec, inputs) {
|
|
|
355
356
|
const keys = Object.keys(inputs);
|
|
356
357
|
if (spec.inputPattern === 'none') {
|
|
357
358
|
if (keys.length) {
|
|
358
|
-
throw new
|
|
359
|
+
throw new CompileRefusal('BPMN_TYPE_NO_INPUTS', `this extension type takes no inputs, but ${keys.length} were supplied (${keys.join(', ')}).`);
|
|
359
360
|
}
|
|
360
361
|
return [];
|
|
361
362
|
}
|
|
@@ -502,7 +503,7 @@ export function createTypedNode(m, id, type, input,
|
|
|
502
503
|
varType) {
|
|
503
504
|
const spec = registryType(type, input.spec);
|
|
504
505
|
if (!spec.element || !spec.tag) {
|
|
505
|
-
throw new
|
|
506
|
+
throw new CompileRefusal('BPMN_TYPE_NOT_AUTHORABLE', `extension type "${type}" has no element/tag in the registry snapshot; it cannot be authored.`);
|
|
506
507
|
}
|
|
507
508
|
const payload = {
|
|
508
509
|
version: 'v1',
|
|
@@ -512,7 +513,7 @@ varType) {
|
|
|
512
513
|
// carry it, and moddle would silently drop it (see ActivityOpts in bpmn-sdk).
|
|
513
514
|
if (input.skipCondition !== undefined) {
|
|
514
515
|
if (spec.tag === 'uipath:mapping') {
|
|
515
|
-
throw new
|
|
516
|
+
throw new CompileRefusal('BPMN_TYPE_SKIP_UNSUPPORTED', `extension type "${type}" serializes a uipath:mapping, which cannot carry a skipCondition.`);
|
|
516
517
|
}
|
|
517
518
|
payload.skipCondition = input.skipCondition;
|
|
518
519
|
}
|
|
@@ -636,7 +637,7 @@ export function verbatimOutputRows(m, rows) {
|
|
|
636
637
|
export function createExtensionPayload(m, type, rows) {
|
|
637
638
|
const spec = registryType(type);
|
|
638
639
|
if (!spec.tag) {
|
|
639
|
-
throw new
|
|
640
|
+
throw new CompileRefusal('BPMN_TYPE_NOT_AUTHORABLE', `extension type "${type}" has no tag in the registry snapshot; it cannot be authored.`);
|
|
640
641
|
}
|
|
641
642
|
const payload = {
|
|
642
643
|
version: 'v1',
|
|
@@ -664,7 +665,7 @@ function finish(m, spec, id, name, payload) {
|
|
|
664
665
|
/** `bpmn:SendTask` is already the moddle type name; keep it, but validate the shape. */
|
|
665
666
|
function moddleElementOf(element) {
|
|
666
667
|
if (!/^bpmn:[A-Z]/.test(element)) {
|
|
667
|
-
throw new
|
|
668
|
+
throw new CompileRefusal('BPMN_TYPE_ELEMENT_INVALID', `registry element "${element}" is not a moddle type name (expected e.g. bpmn:SendTask).`);
|
|
668
669
|
}
|
|
669
670
|
return element;
|
|
670
671
|
}
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* xml-ranges — locate elements by their SOURCE RANGE in an XML document.
|
|
3
|
-
*
|
|
4
|
-
* A tolerant scanner, not a parser: it reports where each element's text begins and
|
|
5
|
-
* ends and nothing about what it means. That is all a merge needs, and it is the only
|
|
6
|
-
* shape that can preserve an element **byte for byte** — the merged document is the
|
|
7
|
-
* original text with a few ranges spliced, so anything untouched is untouched in the
|
|
8
|
-
* literal sense, down to attribute order, quoting and indentation.
|
|
9
|
-
*
|
|
10
|
-
* Why not `bpmn-moddle`, which the SDK already depends on: a moddle round trip
|
|
11
|
-
* normalises. It drops attributes its descriptor does not declare, and the whole point
|
|
12
|
-
* of a brownfield merge is to carry through exactly the payloads the builder does NOT
|
|
13
|
-
* model. Reading with moddle would lose the thing being preserved.
|
|
14
|
-
*
|
|
15
|
-
* Correspondingly, this file must not be mistaken for a general XML implementation. It
|
|
16
|
-
* handles what a `.bpmn` contains — elements, attributes, comments, CDATA, processing
|
|
17
|
-
* instructions, a doctype — and it assumes well-formed input, because every caller has
|
|
18
|
-
* either just written the file or is about to hand it to a real parser anyway.
|
|
19
|
-
*/
|
|
20
1
|
/** One element's place in the source text. */
|
|
21
2
|
export interface XmlRange {
|
|
22
3
|
/** Qualified name as written, e.g. `bpmn:scriptTask`. */
|
package/dist/bpmn/xml-ranges.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* instructions, a doctype — and it assumes well-formed input, because every caller has
|
|
18
18
|
* either just written the file or is about to hand it to a real parser anyway.
|
|
19
19
|
*/
|
|
20
|
+
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
20
21
|
const ATTRIBUTE = /([^\s=/>]+)\s*=\s*("([^"]*)"|'([^']*)')/g;
|
|
21
22
|
function parseAttributes(source) {
|
|
22
23
|
const attributes = {};
|
|
@@ -44,13 +45,13 @@ function endOfTag(xml, from) {
|
|
|
44
45
|
return i;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
throw new
|
|
48
|
+
throw new CompileRefusal('BPMN_XML_MALFORMED', `xml-ranges: unterminated tag at offset ${from}`);
|
|
48
49
|
}
|
|
49
50
|
/** Skip a construct that ends with a known terminator; returns the offset past it. */
|
|
50
51
|
function skipUntil(xml, from, terminator) {
|
|
51
52
|
const at = xml.indexOf(terminator, from);
|
|
52
53
|
if (at < 0)
|
|
53
|
-
throw new
|
|
54
|
+
throw new CompileRefusal('BPMN_XML_MALFORMED', `xml-ranges: unterminated ${terminator.trim()} at offset ${from}`);
|
|
54
55
|
return at + terminator.length;
|
|
55
56
|
}
|
|
56
57
|
function* rawTags(xml) {
|
|
@@ -122,7 +123,7 @@ export function parseXmlRanges(xml) {
|
|
|
122
123
|
if (tag.closing) {
|
|
123
124
|
const node = stack.pop();
|
|
124
125
|
if (!node)
|
|
125
|
-
throw new
|
|
126
|
+
throw new CompileRefusal('BPMN_XML_MALFORMED', `xml-ranges: stray </${tag.name}> at offset ${tag.start}`);
|
|
126
127
|
finish(tag, node, tag.end);
|
|
127
128
|
continue;
|
|
128
129
|
}
|
|
@@ -134,7 +135,7 @@ export function parseXmlRanges(xml) {
|
|
|
134
135
|
stack.push({ tag, children: [] });
|
|
135
136
|
}
|
|
136
137
|
if (stack.length > 0) {
|
|
137
|
-
throw new
|
|
138
|
+
throw new CompileRefusal('BPMN_XML_MALFORMED', `xml-ranges: unclosed <${stack[stack.length - 1].tag.name}>`);
|
|
138
139
|
}
|
|
139
140
|
return roots;
|
|
140
141
|
}
|