@tenonhq/dovetail-servicenow 0.0.51 → 0.0.52
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 +68 -14
- package/dist/cli.js +88 -5
- package/dist/cliUsage.js +29 -3
- package/dist/createRecord.d.ts +2 -1
- package/dist/createRecord.js +12 -1
- package/dist/deleteRecord.d.ts +35 -12
- package/dist/deleteRecord.js +238 -38
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -3
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/mcp/registry.js +88 -13
- package/dist/mcp/schemas.d.ts +19 -0
- package/dist/mcp/schemas.js +21 -4
- package/dist/table/addColumn.d.ts +3 -2
- package/dist/table/addColumn.js +63 -35
- package/dist/table/addIndex.js +48 -2
- package/dist/table/createIndex.d.ts +14 -0
- package/dist/table/createIndex.js +205 -2
- package/dist/table/designAccess.d.ts +2 -2
- package/dist/table/designAccess.js +30 -7
- package/dist/table/listIndexes.d.ts +40 -0
- package/dist/table/listIndexes.js +159 -24
- package/dist/table/overrideColumn.d.ts +12 -18
- package/dist/table/overrideColumn.js +13 -41
- package/dist/table/setColumn.d.ts +5 -3
- package/dist/table/setColumn.js +27 -8
- package/dist/table/updateSetGuard.d.ts +23 -0
- package/dist/table/updateSetGuard.js +57 -0
- package/dist/uxEvents.d.ts +114 -0
- package/dist/uxEvents.js +407 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -742,8 +742,16 @@ substring-matched, because `"[owner_id]"` contains `"owner"`. `type` is `access_
|
|
|
742
742
|
**Uniqueness is not readable.** `v_db_index` has no uniqueness field, so a unique index
|
|
743
743
|
and an ordinary one are indistinguishable in it: `unique` is left **absent** rather than
|
|
744
744
|
guessed, and `uniqueness-enforced` is reported in `unverified` on every result. Only a
|
|
745
|
-
duplicate-insert test proves enforcement. An empty result
|
|
746
|
-
|
|
745
|
+
duplicate-insert test proves enforcement. An empty result for a table that does not exist
|
|
746
|
+
means the table name is wrong - every physical table has a `PRIMARY`.
|
|
747
|
+
|
|
748
|
+
**Table-per-hierarchy children list their storage root's indexes.** `v_db_index` lists
|
|
749
|
+
indexes by *physical* table, and a table stored in an ancestor's physical table (anything
|
|
750
|
+
extending `task`, for example) has no rows under its own name. When the table exists but
|
|
751
|
+
has no rows, its `super_class` chain is walked to the first ancestor that has them, and
|
|
752
|
+
that root's indexes are listed - with `storageTable` and the note naming the root.
|
|
753
|
+
`index-create` and `add-index` **refuse** such a child on the live path, naming the root,
|
|
754
|
+
rather than build on it or report a false "NOT created".
|
|
747
755
|
|
|
748
756
|
### Create an index (composite and non-unique included)
|
|
749
757
|
|
|
@@ -792,6 +800,15 @@ was a silent no-op for that reason.)
|
|
|
792
800
|
- **Idempotent.** On the live path `v_db_index` is read first, and an index over *exactly*
|
|
793
801
|
these columns short-circuits to `already-exists` with no pin, no form session and no
|
|
794
802
|
write. Column **order** is part of an index's identity - `[a;b]` is not `[b;a]`.
|
|
803
|
+
- **Table-per-hierarchy children are refused.** A table with no `v_db_index` rows of its
|
|
804
|
+
own whose ancestor has them is stored in that ancestor's physical table; the run stops
|
|
805
|
+
before any write and names the root (run against the root if that is what you want).
|
|
806
|
+
- **Identity check (warns, never refuses).** The pin runs through the REST client (an API
|
|
807
|
+
key when one is configured) but the build is scheduled by the form session (always
|
|
808
|
+
`SN_USER`), and the build captures into the *form* user's current set. The REST caller's
|
|
809
|
+
own `sys_user` row is read first; when it is not the form-login user (or cannot be read)
|
|
810
|
+
the run still goes ahead and every result note carries an `IDENTITY WARNING`. The capture
|
|
811
|
+
read-back then reports where the row actually landed (`captured` / `captureFoundIn`).
|
|
795
812
|
- **The pin is read back.** The set is pinned with Dovetail's own `changeUpdateSet` and
|
|
796
813
|
`currentUpdateSet` is read; a pin that did not take stops the run before the session
|
|
797
814
|
opens.
|
|
@@ -804,7 +821,8 @@ was a silent no-op for that reason.)
|
|
|
804
821
|
values (EMPTY counts). Then the capture row is looked for in the pinned set:
|
|
805
822
|
`captured:true` only when it was read back; an index that exists but was not captured
|
|
806
823
|
is `created:true, captured:false` with `update-set-capture` in `unverified` - and
|
|
807
|
-
exit code 2, because it will not travel.
|
|
824
|
+
exit code 2, because it will not travel. In that case the capture name is searched
|
|
825
|
+
across every set and the set(s) it actually landed in come back in `captureFoundIn`.
|
|
808
826
|
- **Uniqueness is still never claimed.** `uniqueness-enforced` stays in `unverified` on
|
|
809
827
|
every status.
|
|
810
828
|
|
|
@@ -859,7 +877,8 @@ npx dove-sn create-record \
|
|
|
859
877
|
switches the executing user's app scope + update set server-side, inserts, and
|
|
860
878
|
restores both — so the record is owned by the right app and the insert is captured
|
|
861
879
|
in the right update set. Like `set-field` it **refuses** schema tables and verifies
|
|
862
|
-
via read-back.
|
|
880
|
+
via read-back. It also **refuses** `sys_update_set` — the op cannot set an update
|
|
881
|
+
set's application, so create sets with `dove createUpdateSet` instead. `--scope` and `--update-set` are required; `--if-absent
|
|
863
882
|
"<encoded-query>"` makes re-runs idempotent (the insert is skipped when the query
|
|
864
883
|
already matches a row). Exit codes: `0` created / skipped-in-sync / dry-run, `1` bad
|
|
865
884
|
args, `2` write landed unverified (or skipped with drift). To **update** an existing
|
|
@@ -916,14 +935,19 @@ npx dove-sn delete-record \
|
|
|
916
935
|
`delete-record` wraps the core `deleteRecord` op. It is **dry-run by default** —
|
|
917
936
|
nothing is deleted without `--apply` (`--dry-run` wins if both are given). `--sys-id`
|
|
918
937
|
must be a 32-character lowercase hex id and `--table` a plain table name; both are
|
|
919
|
-
validated before any network call. `--update-set` is **required
|
|
920
|
-
|
|
921
|
-
[#297](https://github.com/TenonHQ/Dovetail/issues/297) ships server-side
|
|
922
|
-
`update_set_sys_id` and captures the delete into the session's **current** update set
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
`
|
|
926
|
-
`
|
|
938
|
+
validated before any network call. `--update-set` is **required**, must exist and be
|
|
939
|
+
**in progress** (checked on the dry-run too). Until
|
|
940
|
+
[#297](https://github.com/TenonHQ/Dovetail/issues/297) ships server-side the op ignores
|
|
941
|
+
`update_set_sys_id` and captures the delete into the session's **current** update set, so
|
|
942
|
+
the verb pins `--update-set` as current first (refusing, nothing deleted, if the pin does
|
|
943
|
+
not read back) and then reads the DELETE row back from `sys_update_xml`. The result carries
|
|
944
|
+
`captured`, `capturedInto` and `captureState` (`in-set` / `other-set` / `none` /
|
|
945
|
+
`unverified`). Like its siblings it **refuses** schema tables (`sys_db_object` /
|
|
946
|
+
`sys_dictionary`). A failed delete call never skips the read-back. Exit codes: `0` deleted
|
|
947
|
+
and captured in the set, a table that writes no capture at all (`none`, with a note), or a
|
|
948
|
+
dry-run; `1` bad args, no such record, or an unknown/closed update set; `2` the record is
|
|
949
|
+
**still present** on read-back (including a server-refused delete), its state is unknown,
|
|
950
|
+
the pin did not take, or the DELETE landed in a different set / could not be read back.
|
|
927
951
|
|
|
928
952
|
All three verbs are exported for programmatic use:
|
|
929
953
|
|
|
@@ -941,6 +965,36 @@ var r = await setField({
|
|
|
941
965
|
console.log(r.status, r.verified); // "applied" true
|
|
942
966
|
```
|
|
943
967
|
|
|
968
|
+
### Register UI component events (sync-ux-events)
|
|
969
|
+
|
|
970
|
+
UI Builder can only map an event a component dispatches when it exists as a
|
|
971
|
+
`sys_ux_event` record **and** is listed in the component macroponent's
|
|
972
|
+
`dispatched_events`. A component deploy does not reliably create either, so
|
|
973
|
+
`sync-ux-events` reads the component's `now-ui.json` and reconciles every
|
|
974
|
+
`components.<tag>.actions[]` entry against the instance.
|
|
975
|
+
|
|
976
|
+
```bash
|
|
977
|
+
# Dry-run (the default) — per action: ok / create / link / drift / ambiguous, plus orphans
|
|
978
|
+
npx dove-sn sync-ux-events --file path/to/now-ui.json
|
|
979
|
+
|
|
980
|
+
# Apply — creates the missing events and appends them to dispatched_events
|
|
981
|
+
npx dove-sn sync-ux-events --file path/to/now-ui.json \
|
|
982
|
+
--component cadso-journey-builder --update-set <sys_id> --apply
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
The macroponent is the `sys_ux_macroponent` (category `component`) whose
|
|
986
|
+
`root_component` is the `sys_ux_lib_component` with that tag; events are created in
|
|
987
|
+
the macroponent's scope via the scope-aware `createRecord` op and the list edit goes
|
|
988
|
+
through `pushWithUpdateSet`, both captured into `--update-set`. The write is
|
|
989
|
+
**append-only** — existing `dispatched_events` entries are never dropped or reordered —
|
|
990
|
+
and is re-read to verify. Label/description **drift** and **orphans** (linked but no
|
|
991
|
+
longer declared) are reported, never changed; an event name carried by several records
|
|
992
|
+
is **ambiguous** and never written. Event names and tags are charset-validated before
|
|
993
|
+
they reach a query. Re-runs are no-ops. Exit codes: `0` in sync / dry-run / applied and
|
|
994
|
+
verified; `1` bad args or unreadable file; `2` an unresolved or ambiguous
|
|
995
|
+
component/event, or a write that did not verify. MCP: `sync_ux_events` (dry-run unless
|
|
996
|
+
`confirm:true`).
|
|
997
|
+
|
|
944
998
|
### Invoke an arbitrary REST operation
|
|
945
999
|
|
|
946
1000
|
Invoke any authenticated ServiceNow REST operation — an application's own
|
|
@@ -1198,8 +1252,8 @@ existing record), `create_record` (insert one record) and `delete_record` (delet
|
|
|
1198
1252
|
record — dry-run by default, `confirm:true` to apply, `updateSetSysId` required, the
|
|
1199
1253
|
record read back before AND after so success is only reported once it is confirmed
|
|
1200
1254
|
gone) — all read-back-verified; `set_field` / `create_record` are captured in the
|
|
1201
|
-
update set you pass, while `delete_record`
|
|
1202
|
-
until [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships — `host_assets` (deploy a built
|
|
1255
|
+
update set you pass, while `delete_record` pins the set as current and reads the DELETE
|
|
1256
|
+
capture back (`captureState`) until [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships — `host_assets` (deploy a built
|
|
1203
1257
|
dist/), plus the Flow Designer
|
|
1204
1258
|
tools `flow_view` (read a flow/subflow's step graph), `action_view` (read an action
|
|
1205
1259
|
type's model), `action_edit` (structurally edit a published action type — per-step
|
package/dist/cli.js
CHANGED
|
@@ -86,6 +86,7 @@ const deleteRecord_1 = require("./deleteRecord");
|
|
|
86
86
|
const invokeRest_1 = require("./invokeRest");
|
|
87
87
|
const publishApp_1 = require("./publishApp");
|
|
88
88
|
const hostAssets_1 = require("./hostAssets");
|
|
89
|
+
const uxEvents_1 = require("./uxEvents");
|
|
89
90
|
const flowDesigner_formatter_2 = require("./flowDesigner-formatter");
|
|
90
91
|
function parseArgs(argv) {
|
|
91
92
|
// `dove-sn --help` has no verb: a leading flag means an empty command and the
|
|
@@ -1572,7 +1573,14 @@ async function runIndexList(flags) {
|
|
|
1572
1573
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1573
1574
|
return 0;
|
|
1574
1575
|
}
|
|
1575
|
-
process.stdout.write(result.table +
|
|
1576
|
+
process.stdout.write(result.table +
|
|
1577
|
+
" — " +
|
|
1578
|
+
result.indexes.length +
|
|
1579
|
+
" index(es)" +
|
|
1580
|
+
(result.storageTable && result.storageTable !== result.table
|
|
1581
|
+
? " (stored in " + result.storageTable + "'s physical table — these are its indexes)"
|
|
1582
|
+
: "") +
|
|
1583
|
+
"\n");
|
|
1576
1584
|
for (var i = 0; i < result.indexes.length; i += 1) {
|
|
1577
1585
|
var idx = result.indexes[i];
|
|
1578
1586
|
process.stdout.write(" " +
|
|
@@ -2006,18 +2014,76 @@ async function runCreateRecord(flags) {
|
|
|
2006
2014
|
return 2;
|
|
2007
2015
|
return 0;
|
|
2008
2016
|
}
|
|
2017
|
+
/**
|
|
2018
|
+
* dove-sn sync-ux-events:
|
|
2019
|
+
* --file <path/to/now-ui.json>
|
|
2020
|
+
* [--component <tag>]
|
|
2021
|
+
* [--update-set <sys_id>] (required with --apply)
|
|
2022
|
+
* [--apply] [--dry-run] [--json]
|
|
2023
|
+
* DRY-RUN BY DEFAULT. Exit codes: 0 in sync / dry-run / applied+verified, 1 bad args or
|
|
2024
|
+
* unreadable file, 2 unresolved/ambiguous component or event, or an unverified write.
|
|
2025
|
+
*/
|
|
2026
|
+
async function runSyncUxEvents(flags, bare) {
|
|
2027
|
+
var bareErr = bareStringFlagError("sync-ux-events", bare);
|
|
2028
|
+
if (bareErr) {
|
|
2029
|
+
process.stderr.write(bareErr);
|
|
2030
|
+
return 1;
|
|
2031
|
+
}
|
|
2032
|
+
var file = flags.file;
|
|
2033
|
+
if (!file) {
|
|
2034
|
+
process.stderr.write("sync-ux-events: --file <now-ui.json> is required\n");
|
|
2035
|
+
return 1;
|
|
2036
|
+
}
|
|
2037
|
+
var apply = flags.apply === "true" && flags["dry-run"] !== "true";
|
|
2038
|
+
var nowUi;
|
|
2039
|
+
try {
|
|
2040
|
+
nowUi = JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
|
|
2041
|
+
}
|
|
2042
|
+
catch (err) {
|
|
2043
|
+
process.stderr.write("sync-ux-events: cannot read " + file + ": " + (err instanceof Error ? err.message : String(err)) + "\n");
|
|
2044
|
+
return 1;
|
|
2045
|
+
}
|
|
2046
|
+
var result;
|
|
2047
|
+
try {
|
|
2048
|
+
result = await (0, uxEvents_1.syncUxEvents)({
|
|
2049
|
+
client: (0, client_1.createClient)({}),
|
|
2050
|
+
nowUi: nowUi,
|
|
2051
|
+
component: flags.component,
|
|
2052
|
+
updateSetSysId: flags["update-set"],
|
|
2053
|
+
apply: apply,
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
catch (err) {
|
|
2057
|
+
var message = err instanceof Error ? err.message : String(err);
|
|
2058
|
+
if (message.indexOf("ux-events: ") === 0)
|
|
2059
|
+
message = message.slice("ux-events: ".length);
|
|
2060
|
+
process.stderr.write("sync-ux-events: " + message + "\n");
|
|
2061
|
+
return 1;
|
|
2062
|
+
}
|
|
2063
|
+
if (flags.json === "true") {
|
|
2064
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
2065
|
+
}
|
|
2066
|
+
else {
|
|
2067
|
+
process.stdout.write((0, uxEvents_1.formatUxEventSync)(result) + "\n");
|
|
2068
|
+
}
|
|
2069
|
+
return result.ok ? 0 : 2;
|
|
2070
|
+
}
|
|
2009
2071
|
/**
|
|
2010
2072
|
* dove-sn delete-record:
|
|
2011
2073
|
* --table x_cadso_core_metric_point_type
|
|
2012
2074
|
* --sys-id <32-hex sys_id> (the record to delete)
|
|
2013
|
-
* --update-set <sys_id> (required —
|
|
2014
|
-
* session
|
|
2075
|
+
* --update-set <sys_id> (required — must exist and be in progress; pinned as
|
|
2076
|
+
* the session's current set before the delete, and the
|
|
2077
|
+
* DELETE capture row is read back from sys_update_xml)
|
|
2015
2078
|
* [--apply] (DRY-RUN BY DEFAULT — nothing is deleted without it)
|
|
2016
2079
|
* [--dry-run] [--json] (--dry-run wins over --apply)
|
|
2017
2080
|
* Reads the record BEFORE (a missing record is an error, not a no-op delete) and AFTER
|
|
2018
2081
|
* (success is only reported once the record is confirmed gone).
|
|
2019
|
-
* Exit codes: 0 deleted/dry-run, 1 bad args
|
|
2020
|
-
* record is STILL PRESENT on read-back
|
|
2082
|
+
* Exit codes: 0 deleted (and captured in the requested set) / dry-run, 1 bad args, missing
|
|
2083
|
+
* record or unknown/closed update set, 2 the record is STILL PRESENT on read-back (including a
|
|
2084
|
+
* delete the server refused with an error), its state is unknown, the update-set pin did not
|
|
2085
|
+
* take (nothing deleted), or it was deleted but the DELETE capture landed in a different set or
|
|
2086
|
+
* could not be read back. A table with no update-set capture at all exits 0 with a note.
|
|
2021
2087
|
*/
|
|
2022
2088
|
async function runDeleteRecord(flags) {
|
|
2023
2089
|
var table = flags.table;
|
|
@@ -2063,7 +2129,15 @@ async function runDeleteRecord(flags) {
|
|
|
2063
2129
|
result.sysId +
|
|
2064
2130
|
" → update set " +
|
|
2065
2131
|
result.updateSetSysId +
|
|
2132
|
+
(result.updateSetName ? " (" + result.updateSetName + ")" : "") +
|
|
2066
2133
|
(result.verified ? " — verified gone" : "") +
|
|
2134
|
+
(result.status === "deleted"
|
|
2135
|
+
? (result.captureState === "in-set"
|
|
2136
|
+
? ", capture verified"
|
|
2137
|
+
: result.captureState === "none"
|
|
2138
|
+
? ", no update-set capture (table not recorded?)"
|
|
2139
|
+
: ", NOT captured in that set")
|
|
2140
|
+
: "") +
|
|
2067
2141
|
"\n" +
|
|
2068
2142
|
result.note +
|
|
2069
2143
|
"\n");
|
|
@@ -2073,6 +2147,12 @@ async function runDeleteRecord(flags) {
|
|
|
2073
2147
|
}
|
|
2074
2148
|
if (result.status === "failed")
|
|
2075
2149
|
return 2;
|
|
2150
|
+
// Gone but the DELETE landed in ANOTHER set (the requested set would promote without it),
|
|
2151
|
+
// or the capture could not be read back: neither may read as success to a script. A table
|
|
2152
|
+
// that is not recorded in update sets at all ("none") is a plain data delete — exit 0, and
|
|
2153
|
+
// the note says the delete will not travel.
|
|
2154
|
+
if (result.status === "deleted" && (result.captureState === "other-set" || result.captureState === "unverified"))
|
|
2155
|
+
return 2;
|
|
2076
2156
|
return 0;
|
|
2077
2157
|
}
|
|
2078
2158
|
/**
|
|
@@ -2704,6 +2784,9 @@ async function dispatch(parsed) {
|
|
|
2704
2784
|
if (parsed.command === "create-record") {
|
|
2705
2785
|
return await runCreateRecord(parsed.flags);
|
|
2706
2786
|
}
|
|
2787
|
+
if (parsed.command === "sync-ux-events") {
|
|
2788
|
+
return await runSyncUxEvents(parsed.flags, parsed.bare);
|
|
2789
|
+
}
|
|
2707
2790
|
if (parsed.command === "delete-record") {
|
|
2708
2791
|
return await runDeleteRecord(parsed.flags);
|
|
2709
2792
|
}
|
package/dist/cliUsage.js
CHANGED
|
@@ -408,7 +408,7 @@ exports.VERB_USAGE = {
|
|
|
408
408
|
{
|
|
409
409
|
flag: "update-set",
|
|
410
410
|
value: "<sys_id>",
|
|
411
|
-
note: "
|
|
411
|
+
note: "Must exist and be in progress (checked on the dry-run too); pinned as current before the delete.",
|
|
412
412
|
},
|
|
413
413
|
],
|
|
414
414
|
optional: [
|
|
@@ -420,10 +420,36 @@ exports.VERB_USAGE = {
|
|
|
420
420
|
gateNote: "The dry-run prints the record snapshot that would be deleted.",
|
|
421
421
|
example: "dove-sn delete-record --table x_cadso_core_metric_point_type --sys-id <32-hex sys_id> --update-set <sys_id> --apply",
|
|
422
422
|
notes: [
|
|
423
|
-
"Reads the record BEFORE (a missing record is an error, never a no-op delete) and AFTER (exit 2 if it is still present).",
|
|
424
|
-
"Until TenonHQ/Dovetail#297 ships server-side the delete op IGNORES --update-set and captures into the session's current update set —
|
|
423
|
+
"Reads the record BEFORE (a missing record is an error, never a no-op delete) and AFTER (exit 2 if it is still present — including when the server refused the delete with an error).",
|
|
424
|
+
"Until TenonHQ/Dovetail#297 ships server-side the delete op IGNORES --update-set and captures into the session's current update set — so the verb pins --update-set as current first (refusing, nothing deleted, if the pin does not read back) and reads the DELETE row back from sys_update_xml: exit 2 when it landed in a different set or the read-back failed. A table that writes no update-set capture at all exits 0 with a note (captureState \"none\") — that delete will not travel.",
|
|
425
425
|
],
|
|
426
426
|
},
|
|
427
|
+
"sync-ux-events": {
|
|
428
|
+
summary: "Register a UI component's now-ui.json actions as sys_ux_event records on its macroponent",
|
|
429
|
+
required: [
|
|
430
|
+
{ flag: "file", value: "<now-ui.json>", note: "Path to the component's now-ui.json; every component with `actions` is synced." },
|
|
431
|
+
],
|
|
432
|
+
optional: [
|
|
433
|
+
{ flag: "component", value: "<tag>", note: "Limit to one component tag, e.g. cadso-journey-builder." },
|
|
434
|
+
{
|
|
435
|
+
flag: "update-set",
|
|
436
|
+
value: "<sys_id>",
|
|
437
|
+
note: "Required with --apply — the event creates and the macroponent edit are captured here.",
|
|
438
|
+
},
|
|
439
|
+
{ flag: "apply", note: "Write (without it the run is a dry-run)." },
|
|
440
|
+
{ flag: "dry-run", note: "Force a dry-run even with --apply." },
|
|
441
|
+
JSON_FLAG,
|
|
442
|
+
],
|
|
443
|
+
gate: "apply",
|
|
444
|
+
gateNote: "The dry-run prints each action as ok / create / link / drift / ambiguous, plus orphans.",
|
|
445
|
+
example: "dove-sn sync-ux-events --file path/to/now-ui.json --component cadso-journey-builder --update-set <sys_id> --apply",
|
|
446
|
+
notes: [
|
|
447
|
+
"Macroponent = sys_ux_macroponent whose root_component is the sys_ux_lib_component with that tag (category component). Events are created in the macroponent's scope.",
|
|
448
|
+
"Append-only: missing events are created and their sys_ids appended to dispatched_events; existing entries are never dropped or reordered. Label/description drift and orphaned (linked but undeclared) events are reported, never changed.",
|
|
449
|
+
"Exit codes: 0 in sync / dry-run / applied and verified, 1 bad args or unreadable file, 2 unresolved or ambiguous component/event, or a write that did not verify.",
|
|
450
|
+
],
|
|
451
|
+
stringFlags: ["file", "component", "update-set"],
|
|
452
|
+
},
|
|
427
453
|
"host-assets": {
|
|
428
454
|
summary: "Deploy a built dist/ to ServiceNow (carrier sys_ui_script + attachment + m2m)",
|
|
429
455
|
required: [
|
package/dist/createRecord.d.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* The INSERT counterpart to `set-field`.
|
|
10
10
|
*
|
|
11
11
|
* NOT for schema tables (sys_db_object / sys_dictionary) — that's
|
|
12
|
-
* create-table / add-column.
|
|
12
|
+
* create-table / add-column. NOT for sys_update_set — that's
|
|
13
|
+
* `dove createUpdateSet`. To UPDATE an existing record, use `set-field`.
|
|
13
14
|
*/
|
|
14
15
|
import type { ServiceNowClient } from "./client";
|
|
15
16
|
import type { RecordWriteResult } from "./setField";
|
package/dist/createRecord.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* The INSERT counterpart to `set-field`.
|
|
11
11
|
*
|
|
12
12
|
* NOT for schema tables (sys_db_object / sys_dictionary) — that's
|
|
13
|
-
* create-table / add-column.
|
|
13
|
+
* create-table / add-column. NOT for sys_update_set — that's
|
|
14
|
+
* `dove createUpdateSet`. To UPDATE an existing record, use `set-field`.
|
|
14
15
|
*/
|
|
15
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
17
|
exports.createRecord = createRecord;
|
|
@@ -19,6 +20,11 @@ const setField_1 = require("./setField");
|
|
|
19
20
|
// Same guard as set-field: schema tables are never written as data — routed to
|
|
20
21
|
// the dedicated schema verbs instead so we never orphan or corrupt metadata.
|
|
21
22
|
var REFUSED_TABLES = ["sys_db_object", "sys_dictionary"];
|
|
23
|
+
// The generic createRecord op never sets `application`, so an update set
|
|
24
|
+
// inserted through it lands in the session app while the read-back (name only)
|
|
25
|
+
// still reports success. Update sets go through the scope-correct
|
|
26
|
+
// createUpdateSet op instead.
|
|
27
|
+
var UPDATE_SET_TABLE = "sys_update_set";
|
|
22
28
|
async function createRecord(params) {
|
|
23
29
|
var client = params.client || (0, client_1.createClient)({});
|
|
24
30
|
var table = params.table;
|
|
@@ -29,6 +35,11 @@ async function createRecord(params) {
|
|
|
29
35
|
throw new Error("create-record: refusing to write " + table + " as data — it is a schema table. "
|
|
30
36
|
+ "Use add-column / create-table for schema changes.");
|
|
31
37
|
}
|
|
38
|
+
if (table === UPDATE_SET_TABLE) {
|
|
39
|
+
throw new Error("create-record: refusing to create a sys_update_set record — the generic createRecord op "
|
|
40
|
+
+ "does not set its application, so the set would land in the session app. "
|
|
41
|
+
+ "Use `dove createUpdateSet --name <name> --scope <scope>` instead.");
|
|
42
|
+
}
|
|
32
43
|
var fieldNames = params.fields ? Object.keys(params.fields) : [];
|
|
33
44
|
if (fieldNames.length === 0) {
|
|
34
45
|
throw new Error("create-record: at least one field (--fields key=value) is required.");
|
package/dist/deleteRecord.d.ts
CHANGED
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Wraps the Dovetail core Scripted REST `deleteRecord` op. DRY-RUN BY DEFAULT:
|
|
9
9
|
* without confirm:true nothing is deleted; dryRun:true forces a dry-run even
|
|
10
|
-
* with confirm. updateSetSysId is REQUIRED and
|
|
11
|
-
*
|
|
12
|
-
* op ignores update_set_sys_id and captures into the session's
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* with confirm. updateSetSysId is REQUIRED and resolved up front (it must exist
|
|
11
|
+
* and be in progress — checked on the dry-run too). Until TenonHQ/Dovetail#297
|
|
12
|
+
* ships the server op ignores update_set_sys_id and captures into the session's
|
|
13
|
+
* current update set, so the verb does what every other deleteRecord caller does:
|
|
14
|
+
* it pins the set as current (changeUpdateSet) and reads the pin back, refusing
|
|
15
|
+
* to delete when it did not take. After the delete it reads the DELETE row back
|
|
16
|
+
* from sys_update_xml and reports where it actually landed (captured /
|
|
17
|
+
* capturedInto) — the pin alone is not proof (#297 saw it not stick cross-scope).
|
|
18
|
+
* The client keeps sending the field so it takes effect once the server honours it.
|
|
16
19
|
*
|
|
17
20
|
* NOT for schema tables (sys_db_object / sys_dictionary) — dropping a table or
|
|
18
21
|
* column is a privileged lifecycle op, not a data delete.
|
|
@@ -24,9 +27,9 @@ export interface DeleteRecordParams {
|
|
|
24
27
|
/** sys_id of the record to delete — 32 lowercase hex characters. */
|
|
25
28
|
sysId: string;
|
|
26
29
|
/**
|
|
27
|
-
* Update set the delete should be captured into. Required and
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
+
* Update set the delete should be captured into. Required; must exist and be in progress.
|
|
31
|
+
* Pinned as the session's current set before the delete (the server op ignores the field
|
|
32
|
+
* until TenonHQ/Dovetail#297 ships) and verified via the sys_update_xml capture row.
|
|
30
33
|
*/
|
|
31
34
|
updateSetSysId?: string;
|
|
32
35
|
/** The delete gate: the record is only deleted when confirm is exactly true. */
|
|
@@ -43,14 +46,34 @@ export interface DeleteRecordResult {
|
|
|
43
46
|
before: Record<string, string>;
|
|
44
47
|
/** True only when the post-delete read-back confirmed the record is gone. */
|
|
45
48
|
verified: boolean;
|
|
49
|
+
/** Name of the requested update set, resolved before anything else is done. */
|
|
50
|
+
updateSetName: string;
|
|
51
|
+
/**
|
|
52
|
+
* True only when the DELETE's sys_update_xml capture row was read back IN the requested
|
|
53
|
+
* update set. A deleted record with captured:false will NOT travel with that set.
|
|
54
|
+
*/
|
|
55
|
+
captured: boolean;
|
|
56
|
+
/** The update set the DELETE capture row actually landed in; null when none was found. */
|
|
57
|
+
capturedInto: {
|
|
58
|
+
sysId: string;
|
|
59
|
+
name: string;
|
|
60
|
+
} | null;
|
|
61
|
+
/**
|
|
62
|
+
* Where the DELETE capture stands: "in-set" (captured in the requested set), "other-set"
|
|
63
|
+
* (captured, but into a different set — the requested set promotes without it),
|
|
64
|
+
* "none" (no capture row anywhere — the table is likely not recorded in update sets),
|
|
65
|
+
* "unverified" (the capture read-back failed), or "n/a" (dry-run / nothing deleted).
|
|
66
|
+
*/
|
|
67
|
+
captureState: "in-set" | "other-set" | "none" | "unverified" | "n/a";
|
|
46
68
|
note: string;
|
|
47
69
|
}
|
|
48
70
|
export declare var TABLE_NAME_PATTERN: RegExp;
|
|
49
71
|
export declare var SYS_ID_PATTERN: RegExp;
|
|
50
72
|
/**
|
|
51
|
-
* Appended to
|
|
52
|
-
* yet honour update_set_sys_id, so the
|
|
53
|
-
*
|
|
73
|
+
* Appended to the dry-run and deleted notes while TenonHQ/Dovetail#297 is open: the server op
|
|
74
|
+
* does not yet honour update_set_sys_id, so the delete is captured into the session's current
|
|
75
|
+
* update set — which is why the verb pins it first and reads the capture row back. Revisit
|
|
76
|
+
* (and its test) when #297 ships.
|
|
54
77
|
*/
|
|
55
78
|
export declare var UPDATE_SET_CAVEAT: string;
|
|
56
79
|
export declare function validateDeleteTable(table: unknown): string;
|