@tenonhq/dovetail-servicenow 0.0.43 → 0.0.45

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.
@@ -14,6 +14,21 @@
14
14
  * ├─ sys_hub_step_ext_input (model_id → step)
15
15
  * └─ sys_hub_step_ext_output (model_id → step)
16
16
  *
17
+ * Plus two stores the action's IO depends on that are NOT FK children
18
+ * (verified live on tenonworkstudio 2026-09-30):
19
+ *
20
+ * sys_element_mapping output → step-output wiring, one row per mapped output
21
+ * (incl. __action_status__ / __dont_treat_as_error__):
22
+ * id = action sys_id, table = var__m_sys_hub_action_output_<action>,
23
+ * field = output element, value = the pill. Without these a
24
+ * clone publishes with every output unmapped (runs, returns empty).
25
+ * sys_documentation IO labels: name = var__m_sys_hub_action_{input,output}_<action>,
26
+ * element, label, language. Without these Flow Designer shows the
27
+ * element name as the label.
28
+ *
29
+ * Action IO rows are var_dictionary rows whose `name` IS their var__m table, so
30
+ * the clone retargets `name` onto the new action's var__m table too.
31
+ *
17
32
  * Publish reuses the proven snapshot path (publishActionType): the SOURCE
18
33
  * action's step graph is read from /step_instances, each step's `action` and
19
34
  * `sys_id` are remapped onto the clone (old→new sys_id map), optional StepOps
@@ -77,6 +92,12 @@ export interface CloneActionTypePlan {
77
92
  sourceScopeSysId: string;
78
93
  /** Old → new sys_id for every cloned step instance. */
79
94
  stepIdMap: Record<string, string>;
95
+ /**
96
+ * The sys_documentation (label) ops inside `ops`. They are UPSERTED after the
97
+ * graph is written (the dictionary insert may already have created the row),
98
+ * not run through executeWritePlan.
99
+ */
100
+ labelOpIds: Array<string>;
80
101
  }
81
102
  export interface CloneActionTypeStepReport {
82
103
  /** Step summary of the cloned graph before stepOps. */
@@ -99,6 +120,10 @@ export interface CloneActionTypeResult {
99
120
  verify?: VerifyStepsResult;
100
121
  steps?: CloneActionTypeStepReport;
101
122
  }
123
+ /** var_dictionary table name for a model's variables: var__m_<table>_<model sys_id>. */
124
+ export declare function varTableName(table: string, modelSysId: string): string;
125
+ /** The encoded query for an action's output → step-output mappings. */
126
+ export declare function outputMappingQuery(actionSysId: string): string;
102
127
  /** Slug for internal_name: lowercase, runs of non-alphanumerics → "_", trimmed of "_". */
103
128
  export declare function slugInternalName(name: string): string;
104
129
  /**
@@ -117,3 +142,18 @@ export declare function resolveScope(client: ServiceNowClient, ref: string): Pro
117
142
  */
118
143
  export declare function remapClonedSteps(steps: Array<StepRecord>, newParentSysId: string, stepIdMap: Record<string, string>, idMap: Record<string, string>): Array<StepRecord>;
119
144
  export declare function cloneActionType(opts: CloneActionTypeParams): Promise<CloneActionTypeResult>;
145
+ /**
146
+ * Set `name` on a cloned var_dictionary row (action IO / step ext IO) to the
147
+ * clone's var__m table — but only when it named the SOURCE model's var__m table
148
+ * exactly; anything else is left as it was.
149
+ */
150
+ export declare function retargetVarName(fields: Record<string, unknown>, table: string, oldModelSysId: string, newModelSysId: string): void;
151
+ /**
152
+ * Compare the source action's output mappings with the clone's, by `field`.
153
+ * A source field absent on the clone fails; a differing value is noted but does
154
+ * not fail (publish may legitimately normalise a pill).
155
+ */
156
+ export declare function verifyOutputMappings(sourceRows: Array<Record<string, unknown>>, cloneRows: Array<Record<string, unknown>>): {
157
+ ok: boolean;
158
+ notes: Array<string>;
159
+ };
@@ -15,6 +15,21 @@
15
15
  * ├─ sys_hub_step_ext_input (model_id → step)
16
16
  * └─ sys_hub_step_ext_output (model_id → step)
17
17
  *
18
+ * Plus two stores the action's IO depends on that are NOT FK children
19
+ * (verified live on tenonworkstudio 2026-09-30):
20
+ *
21
+ * sys_element_mapping output → step-output wiring, one row per mapped output
22
+ * (incl. __action_status__ / __dont_treat_as_error__):
23
+ * id = action sys_id, table = var__m_sys_hub_action_output_<action>,
24
+ * field = output element, value = the pill. Without these a
25
+ * clone publishes with every output unmapped (runs, returns empty).
26
+ * sys_documentation IO labels: name = var__m_sys_hub_action_{input,output}_<action>,
27
+ * element, label, language. Without these Flow Designer shows the
28
+ * element name as the label.
29
+ *
30
+ * Action IO rows are var_dictionary rows whose `name` IS their var__m table, so
31
+ * the clone retargets `name` onto the new action's var__m table too.
32
+ *
18
33
  * Publish reuses the proven snapshot path (publishActionType): the SOURCE
19
34
  * action's step graph is read from /step_instances, each step's `action` and
20
35
  * `sys_id` are remapped onto the clone (old→new sys_id map), optional StepOps
@@ -32,10 +47,14 @@
32
47
  * docs/servicenow-flow-designer-headless-authoring.md.
33
48
  */
34
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.varTableName = varTableName;
51
+ exports.outputMappingQuery = outputMappingQuery;
35
52
  exports.slugInternalName = slugInternalName;
36
53
  exports.resolveScope = resolveScope;
37
54
  exports.remapClonedSteps = remapClonedSteps;
38
55
  exports.cloneActionType = cloneActionType;
56
+ exports.retargetVarName = retargetVarName;
57
+ exports.verifyOutputMappings = verifyOutputMappings;
39
58
  const shape_1 = require("./shape");
40
59
  const writeOrder_1 = require("./writeOrder");
41
60
  const actionTypeApi_1 = require("./actionTypeApi");
@@ -47,6 +66,16 @@ var OUTPUT_TABLE = "sys_hub_action_output";
47
66
  var STEP_TABLE = "sys_hub_step_instance";
48
67
  var EXT_INPUT_TABLE = "sys_hub_step_ext_input";
49
68
  var EXT_OUTPUT_TABLE = "sys_hub_step_ext_output";
69
+ var MAPPING_TABLE = "sys_element_mapping";
70
+ var LABEL_TABLE = "sys_documentation";
71
+ /** var_dictionary table name for a model's variables: var__m_<table>_<model sys_id>. */
72
+ function varTableName(table, modelSysId) {
73
+ return "var__m_" + table + "_" + modelSysId;
74
+ }
75
+ /** The encoded query for an action's output → step-output mappings. */
76
+ function outputMappingQuery(actionSysId) {
77
+ return "id=" + actionSysId + "^tableSTARTSWITH" + "var__m_" + OUTPUT_TABLE + "_";
78
+ }
50
79
  /** Rows per child query. A full page means the read may be truncated — refuse rather than half-clone. */
51
80
  var CHILD_LIMIT = 1000;
52
81
  /**
@@ -307,6 +336,10 @@ async function cloneActionType(opts) {
307
336
  scope: scope.name,
308
337
  },
309
338
  ];
339
+ // element → plan op id, per IO table, so label/mapping ops can depend on their row.
340
+ var ioOpByElement = {};
341
+ ioOpByElement[INPUT_TABLE] = {};
342
+ ioOpByElement[OUTPUT_TABLE] = {};
310
343
  var pushModelChildren = function (table, prefix, rows) {
311
344
  for (var r = 0; r < rows.length; r++) {
312
345
  var src = flattenRow(rows[r]);
@@ -314,6 +347,11 @@ async function cloneActionType(opts) {
314
347
  idMap[String(src.sys_id || "")] = childSysId;
315
348
  var fields = cloneFields(src, scope.sysId, childSysId);
316
349
  fields.model_id = newParentSysId;
350
+ retargetVarName(fields, table, opts.sourceSysId, newParentSysId);
351
+ var element = String(src.element || "");
352
+ if (element) {
353
+ ioOpByElement[table][element] = prefix + ":" + r;
354
+ }
317
355
  ops.push({
318
356
  id: prefix + ":" + r,
319
357
  logicalName: table + ":" + String(src.element || src.name || src.sys_id),
@@ -356,6 +394,7 @@ async function cloneActionType(opts) {
356
394
  idMap[String(srcExt.sys_id || "")] = newExtSysId;
357
395
  var extFields = cloneFields(srcExt, scope.sysId, newExtSysId);
358
396
  extFields.model_id = newStepSysId;
397
+ retargetVarName(extFields, extSpecs[x].table, oldStepSysId, newStepSysId);
359
398
  ops.push({
360
399
  id: stepOpId + ":" + extSpecs[x].prefix + ":" + e,
361
400
  logicalName: extSpecs[x].table + ":" + String(srcExt.element || srcExt.name || srcExt.sys_id),
@@ -367,13 +406,78 @@ async function cloneActionType(opts) {
367
406
  }
368
407
  }
369
408
  }
409
+ // 3b. Output → step-output wiring (sys_element_mapping). Step cids survive the
410
+ // clone, so field and value (the pill) carry over unchanged.
411
+ var newOutputVarTable = varTableName(OUTPUT_TABLE, newParentSysId);
412
+ var sourceMappings = await queryChildren(client, MAPPING_TABLE, outputMappingQuery(opts.sourceSysId));
413
+ for (var mi = 0; mi < sourceMappings.length; mi++) {
414
+ var srcMap = flattenRow(sourceMappings[mi]);
415
+ var mapField = String(srcMap.field || "");
416
+ var mapFields = cloneFields(srcMap, scope.sysId, (0, shape_1.generateSysId)());
417
+ mapFields.id = newParentSysId;
418
+ mapFields.table = newOutputVarTable;
419
+ // After the parent and its outputs (the matching output first), so a mapping never precedes the row it wires.
420
+ var mapDeps = ["parent"];
421
+ if (mapField && Object.prototype.hasOwnProperty.call(ioOpByElement[OUTPUT_TABLE], mapField)) {
422
+ mapDeps.push(ioOpByElement[OUTPUT_TABLE][mapField]);
423
+ }
424
+ var outputOpIds = Object.keys(ioOpByElement[OUTPUT_TABLE]).map(function (el) { return ioOpByElement[OUTPUT_TABLE][el]; });
425
+ for (var oi = 0; oi < outputOpIds.length; oi++) {
426
+ if (mapDeps.indexOf(outputOpIds[oi]) === -1) {
427
+ mapDeps.push(outputOpIds[oi]);
428
+ }
429
+ }
430
+ ops.push({
431
+ id: "mapping:" + mi,
432
+ logicalName: MAPPING_TABLE + ":" + (mapField || String(srcMap.sys_id || mi)),
433
+ table: MAPPING_TABLE,
434
+ fields: mapFields,
435
+ dependsOn: mapDeps,
436
+ scope: scope.name,
437
+ });
438
+ }
439
+ // 3c. IO labels (sys_documentation keyed on the var__m table), retargeted.
440
+ var labelOps = [];
441
+ var labelSpecs = [
442
+ { table: INPUT_TABLE, prefix: "label:input" },
443
+ { table: OUTPUT_TABLE, prefix: "label:output" },
444
+ ];
445
+ for (var li = 0; li < labelSpecs.length; li++) {
446
+ var spec = labelSpecs[li];
447
+ var srcVarTable = varTableName(spec.table, opts.sourceSysId);
448
+ var newVarTable = varTableName(spec.table, newParentSysId);
449
+ var labelRows = await queryChildren(client, LABEL_TABLE, "name=" + srcVarTable);
450
+ for (var lr = 0; lr < labelRows.length; lr++) {
451
+ var srcLabel = flattenRow(labelRows[lr]);
452
+ var labelElement = String(srcLabel.element || "");
453
+ if (!labelElement) {
454
+ continue;
455
+ }
456
+ var labelFields = cloneFields(srcLabel, scope.sysId, (0, shape_1.generateSysId)());
457
+ labelFields.name = newVarTable;
458
+ var labelDeps = ["parent"];
459
+ if (Object.prototype.hasOwnProperty.call(ioOpByElement[spec.table], labelElement)) {
460
+ labelDeps.push(ioOpByElement[spec.table][labelElement]);
461
+ }
462
+ labelOps.push({
463
+ id: spec.prefix + ":" + lr,
464
+ logicalName: LABEL_TABLE + ":" + newVarTable + "." + labelElement,
465
+ table: LABEL_TABLE,
466
+ fields: labelFields,
467
+ dependsOn: labelDeps,
468
+ scope: scope.name,
469
+ });
470
+ }
471
+ }
472
+ var allOps = ops.concat(labelOps);
370
473
  var plan = {
371
- ops: ops,
372
- counts: countByTable(ops),
373
- total: ops.length,
474
+ ops: allOps,
475
+ counts: countByTable(allOps),
476
+ total: allOps.length,
374
477
  scope: scope,
375
478
  sourceScopeSysId: sourceScopeSysId,
376
479
  stepIdMap: stepIdMap,
480
+ labelOpIds: labelOps.map(function (o) { return o.id; }),
377
481
  };
378
482
  // 4. The publishable step graph: the SOURCE's /step_instances, remapped.
379
483
  var sourceSteps = await (0, actionTypeApi_1.fetchActionSteps)(client, opts.sourceSysId, sourceScopeSysId);
@@ -412,6 +516,11 @@ async function cloneActionType(opts) {
412
516
  // 5. Write the graph, pinned to the update set.
413
517
  var updateSetSysId = opts.updateSetSysId;
414
518
  var written = await (0, writeOrder_1.executeWritePlan)(client, ops, updateSetSysId);
519
+ // Labels last: the IO dictionary inserts above may already have created a
520
+ // sys_documentation row for the new var__m table, so upsert rather than duplicate.
521
+ for (var lw = 0; lw < labelOps.length; lw++) {
522
+ written.push(await upsertLabel(client, labelOps[lw], updateSetSysId));
523
+ }
415
524
  var result = {
416
525
  sysId: newParentSysId,
417
526
  internalName: internalName,
@@ -440,16 +549,129 @@ async function cloneActionType(opts) {
440
549
  }
441
550
  // 7. Verify — a 201 means "compiled", not "landed as sent". Read the clone's steps back.
442
551
  var fresh = await (0, actionTypeApi_1.fetchActionSteps)(client, newParentSysId, scope.sysId);
552
+ var verify;
443
553
  if (fresh.length === 0) {
444
- result.verify = { ok: false, notes: ["read-back returned no steps — could not verify the publish"] };
554
+ verify = { ok: false, notes: ["read-back returned no steps — could not verify the publish"] };
445
555
  }
446
556
  else {
447
557
  var expected = stepReport.after;
448
- result.verify = (0, stepOps_1.verifySteps)(expected, (0, stepOps_1.summarizeSteps)(fresh), allCids(finalSteps));
558
+ verify = (0, stepOps_1.verifySteps)(expected, (0, stepOps_1.summarizeSteps)(fresh), allCids(finalSteps));
449
559
  if (fresh.length !== finalSteps.length) {
450
- result.verify.ok = false;
451
- result.verify.notes.push("step count on the instance is " + fresh.length + ", expected " + finalSteps.length);
560
+ verify.ok = false;
561
+ verify.notes.push("step count on the instance is " + fresh.length + ", expected " + finalSteps.length);
452
562
  }
453
563
  }
564
+ // Output mappings: every source mapping must exist on the clone, or its outputs come back empty.
565
+ var cloneMappings = await queryChildren(client, MAPPING_TABLE, outputMappingQuery(newParentSysId));
566
+ var mappingCheck = verifyOutputMappings(sourceMappings, cloneMappings);
567
+ if (!mappingCheck.ok) {
568
+ verify.ok = false;
569
+ }
570
+ for (var vn = 0; vn < mappingCheck.notes.length; vn++) {
571
+ verify.notes.push(mappingCheck.notes[vn]);
572
+ }
573
+ result.verify = verify;
454
574
  return result;
455
575
  }
576
+ /**
577
+ * Set `name` on a cloned var_dictionary row (action IO / step ext IO) to the
578
+ * clone's var__m table — but only when it named the SOURCE model's var__m table
579
+ * exactly; anything else is left as it was.
580
+ */
581
+ function retargetVarName(fields, table, oldModelSysId, newModelSysId) {
582
+ if (!Object.prototype.hasOwnProperty.call(fields, "name")) {
583
+ return;
584
+ }
585
+ if (String(fields.name) === varTableName(table, oldModelSysId)) {
586
+ fields.name = varTableName(table, newModelSysId);
587
+ }
588
+ }
589
+ /**
590
+ * Compare the source action's output mappings with the clone's, by `field`.
591
+ * A source field absent on the clone fails; a differing value is noted but does
592
+ * not fail (publish may legitimately normalise a pill).
593
+ */
594
+ function verifyOutputMappings(sourceRows, cloneRows) {
595
+ var cloneByField = {};
596
+ for (var c = 0; c < cloneRows.length; c++) {
597
+ var cr = flattenRow(cloneRows[c]);
598
+ var cf = String(cr.field || "");
599
+ if (cf) {
600
+ cloneByField[cf] = String(cr.value === undefined || cr.value === null ? "" : cr.value);
601
+ }
602
+ }
603
+ var missing = [];
604
+ var notes = [];
605
+ for (var s = 0; s < sourceRows.length; s++) {
606
+ var sr = flattenRow(sourceRows[s]);
607
+ var sf = String(sr.field || "");
608
+ if (!sf) {
609
+ continue;
610
+ }
611
+ if (!Object.prototype.hasOwnProperty.call(cloneByField, sf)) {
612
+ missing.push(sf);
613
+ continue;
614
+ }
615
+ var sv = String(sr.value === undefined || sr.value === null ? "" : sr.value);
616
+ if (cloneByField[sf] !== sv) {
617
+ notes.push("output mapping '" + sf + "' differs on the clone: '" + cloneByField[sf] + "' (source '" + sv + "')");
618
+ }
619
+ }
620
+ if (missing.length > 0) {
621
+ notes.unshift(missing.length + " output mapping(s) missing on the clone (" + MAPPING_TABLE + "): " + missing.join(", "));
622
+ }
623
+ else if (sourceRows.length > 0) {
624
+ notes.unshift("all " + sourceRows.length + " output mapping(s) present on the clone");
625
+ }
626
+ return { ok: missing.length === 0, notes: notes };
627
+ }
628
+ /**
629
+ * Write one cloned sys_documentation row: update the existing (name, element,
630
+ * language) row on the target if the platform already made one, else create it.
631
+ */
632
+ async function upsertLabel(client, op, updateSetSysId) {
633
+ var f = op.fields;
634
+ var keyParts = [String(f.name), String(f.element || ""), String(f.language || "")];
635
+ for (var k = 0; k < keyParts.length; k++) {
636
+ if (/[\^\r\n]/.test(keyParts[k])) {
637
+ throw new Error("cloneActionType: label key " + JSON.stringify(keyParts[k]) + " contains '^' or a line break.");
638
+ }
639
+ }
640
+ var query = "name=" + keyParts[0] + "^element=" + keyParts[1];
641
+ if (keyParts[2] !== "") {
642
+ query += "^language=" + keyParts[2];
643
+ }
644
+ try {
645
+ var hits = await client.table.query(LABEL_TABLE, query, { limit: 1, fields: ["sys_id"] });
646
+ var existingSysId = hits.length > 0 ? String(flattenRow(hits[0]).sys_id || "") : "";
647
+ if (RX_SYS_ID.test(existingSysId)) {
648
+ var update = Object.assign({}, f);
649
+ delete update.sys_id;
650
+ await client.claude.pushWithUpdateSet({
651
+ update_set_sys_id: updateSetSysId,
652
+ table: LABEL_TABLE,
653
+ record_sys_id: existingSysId,
654
+ fields: update,
655
+ });
656
+ return { id: op.id, logicalName: op.logicalName, table: op.table, sysId: existingSysId, action: "updated" };
657
+ }
658
+ var created = await client.claude.createRecord({
659
+ table: op.table,
660
+ fields: f,
661
+ scope: op.scope,
662
+ update_set_sys_id: updateSetSysId,
663
+ sys_id: String(f.sys_id),
664
+ });
665
+ return {
666
+ id: op.id,
667
+ logicalName: op.logicalName,
668
+ table: op.table,
669
+ sysId: created.sys_id ? String(created.sys_id) : String(f.sys_id),
670
+ action: "created",
671
+ };
672
+ }
673
+ catch (err) {
674
+ var msg = err instanceof Error ? err.message : String(err);
675
+ throw new Error("cloneActionType: label write failed at op '" + op.id + "' (" + op.table + "): " + msg);
676
+ }
677
+ }
@@ -301,5 +301,6 @@ export interface PlanActionDefinitionParams {
301
301
  * before/after views; throws on any invalid or dangling reference.
302
302
  */
303
303
  export declare function planActionDefinition(params: PlanActionDefinitionParams): DefineActionPlan;
304
+ export declare function compareForVerify(expected: ActionView, actual: ActionView): DefineActionVerify;
304
305
  export declare function defineActionType(params: DefineActionTypeParams): Promise<DefineActionTypeResult>;
305
306
  export {};
@@ -50,6 +50,7 @@ exports.canonValue = canonValue;
50
50
  exports.viewAction = viewAction;
51
51
  exports.diffViews = diffViews;
52
52
  exports.planActionDefinition = planActionDefinition;
53
+ exports.compareForVerify = compareForVerify;
53
54
  exports.defineActionType = defineActionType;
54
55
  const crypto_1 = require("crypto");
55
56
  const actionTypeApi_1 = require("./actionTypeApi");
@@ -1547,9 +1548,28 @@ async function resolveAliasDisplays(client, ids) {
1547
1548
  }
1548
1549
  return out;
1549
1550
  }
1551
+ // Variable attributes the server assigns a default to on save when the spec left
1552
+ // them unset (live-observed 2026-09-30: a new boolean input came back with
1553
+ // maxLength 40). A blank expected value for one of these is "unspecified", not a
1554
+ // mismatch; an explicitly specified value is still verified.
1555
+ var SERVER_DEFAULTED_FIELDS = ["maxLength"];
1556
+ function dropServerDefaults(changed) {
1557
+ return changed
1558
+ .map(function (c) {
1559
+ var kept = c.changes.filter(function (f) {
1560
+ return !(SERVER_DEFAULTED_FIELDS.indexOf(f.field) !== -1 && f.before === "");
1561
+ });
1562
+ return Object.assign({}, c, { changes: kept });
1563
+ })
1564
+ .filter(function (c) {
1565
+ return c.changes.length > 0;
1566
+ });
1567
+ }
1550
1568
  function compareForVerify(expected, actual) {
1551
1569
  var notes = [];
1552
1570
  var d = diffViews(expected, actual, {});
1571
+ d.inputs.changed = dropServerDefaults(d.inputs.changed);
1572
+ d.outputs.changed = dropServerDefaults(d.outputs.changed);
1553
1573
  // Expected -> actual: any difference means the save did not land as planned.
1554
1574
  d.action.forEach(function (c) {
1555
1575
  notes.push("action." + c.field + " is '" + c.after + "', expected '" + c.before + "'");
@@ -1585,10 +1605,13 @@ function compareForVerify(expected, actual) {
1585
1605
  d.steps.changed.forEach(function (s) {
1586
1606
  notes.push("step '" + s.label + "': " + s.changes.join("; "));
1587
1607
  });
1588
- if (notes.length === 0) {
1608
+ // ok is derived from the filtered notes, not d.empty: server-defaulted fields
1609
+ // were dropped above and must not fail the verify.
1610
+ var ok = notes.length === 0;
1611
+ if (ok) {
1589
1612
  notes.push("read-back matches the planned definition");
1590
1613
  }
1591
- return { ok: d.empty, notes: notes };
1614
+ return { ok: ok, notes: notes };
1592
1615
  }
1593
1616
  async function defineActionType(params) {
1594
1617
  var client = params.client;
@@ -36,8 +36,12 @@ export interface WriteOpResult {
36
36
  logicalName: string;
37
37
  table: string;
38
38
  sysId: string;
39
- /** Currently always "created" — claude.createRecord doesn't distinguish. */
40
- action: "created";
39
+ /**
40
+ * executeWritePlan always reports "created" — claude.createRecord doesn't
41
+ * distinguish. "updated" is reported by callers that upsert (cloneActionType's
42
+ * label rows, which the platform may already have created on the dictionary insert).
43
+ */
44
+ action: "created" | "updated";
41
45
  }
42
46
  export declare class WriteOrderError extends Error {
43
47
  readonly cycleIds?: Array<string> | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-servicenow",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },