@tenonhq/dovetail-servicenow 0.0.44 → 0.0.46
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 +73 -1
- package/dist/cli.js +149 -0
- package/dist/flowDesigner/cloneActionType.d.ts +40 -0
- package/dist/flowDesigner/cloneActionType.js +229 -7
- package/dist/flowDesigner/writeOrder.d.ts +6 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -2
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/mcp/registry.js +68 -0
- package/dist/mcp/schemas.d.ts +41 -0
- package/dist/mcp/schemas.js +26 -1
- package/dist/table/createIndex.d.ts +118 -0
- package/dist/table/createIndex.js +460 -0
- package/dist/table/formSession.d.ts +24 -0
- package/dist/table/formSession.js +61 -0
- package/dist/table/index.d.ts +6 -2
- package/dist/table/index.js +11 -1
- package/dist/table/listIndexes.d.ts +68 -0
- package/dist/table/listIndexes.js +110 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -652,6 +652,73 @@ Three consequences, each reported rather than hidden:
|
|
|
652
652
|
single request goes out. Exit codes: `0` created / skipped / dry-run, `1` bad args, `2`
|
|
653
653
|
failed (the lying-row case included).
|
|
654
654
|
|
|
655
|
+
### List a table's indexes
|
|
656
|
+
|
|
657
|
+
```bash
|
|
658
|
+
npx dove-sn index-list --table x_cadso_automate_message_batch_recipient --json
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
Read-only: no form session, no writes. **`v_db_index` is the index read surface** - and
|
|
662
|
+
the only one. `sys_index` fails an API-LEVEL ACL (HTTP 403) for every identity including
|
|
663
|
+
admin, and `sys_index_column` does not exist at all (HTTP 400 `Invalid table`), so there
|
|
664
|
+
is no two-table index model to join and nothing to cross-check against.
|
|
665
|
+
|
|
666
|
+
Each row comes back as `{ name, columns, type, rawColumns }`. `columns` is the view's
|
|
667
|
+
bracketed `column_names` cell (`"[phone]"`, `"[a,b]"`) **parsed** into a list - never
|
|
668
|
+
substring-matched, because `"[owner_id]"` contains `"owner"`. `type` is `access_method`.
|
|
669
|
+
|
|
670
|
+
**Uniqueness is not readable.** `v_db_index` has no uniqueness field, so a unique index
|
|
671
|
+
and an ordinary one are indistinguishable in it: `unique` is left **absent** rather than
|
|
672
|
+
guessed, and `uniqueness-enforced` is reported in `unverified` on every result. Only a
|
|
673
|
+
duplicate-insert test proves enforcement. An empty result more likely means the table name
|
|
674
|
+
is wrong than that the table is unindexed - every physical table has a `PRIMARY`.
|
|
675
|
+
|
|
676
|
+
### Create an index (composite and non-unique included)
|
|
677
|
+
|
|
678
|
+
> **A DATABASE INDEX IS A PHYSICAL, PER-INSTANCE CHANGE. IT IS NOT CAPTURED IN AN UPDATE
|
|
679
|
+
> SET AND DOES NOT TRAVEL WITH A PROMOTION.** Re-run `index-create` against every
|
|
680
|
+
> environment that needs the index (dev, test, uat, staging, prod). There is deliberately
|
|
681
|
+
> no `--update-set` - passing one is an error, not a silent no-op.
|
|
682
|
+
|
|
683
|
+
```bash
|
|
684
|
+
# Dry-run (the DEFAULT) - sends nothing and reads nothing
|
|
685
|
+
npx dove-sn index-create --table x_cadso_journey_instance --columns state,created_on
|
|
686
|
+
|
|
687
|
+
# Send it
|
|
688
|
+
npx dove-sn index-create \
|
|
689
|
+
--table x_cadso_journey_instance --columns state,created_on --confirm --json
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
This is what `add-index` cannot do. `sys_dictionary.unique` - the only record-shaped lever
|
|
693
|
+
- is **per-column and unique-only**, so composite and plain indexes have no record path at
|
|
694
|
+
all. `index-create` instead replays the platform's own index-creator form
|
|
695
|
+
(`sys_action=create_index`, `sysparm_index_table`, `sysparm_fields`,
|
|
696
|
+
`sysparm_unique_index_SKIP`) over a form-login session. That contract is lifted from the
|
|
697
|
+
instance's shipped `index_creator_information` UI macro, not from a guess, and the POST
|
|
698
|
+
target is taken from the rendered page's own `<form action>`.
|
|
699
|
+
|
|
700
|
+
- **Dry-run by default.** Without `--confirm` nothing is sent *and nothing is read*;
|
|
701
|
+
`--dry-run` forces a plan even with `--confirm`.
|
|
702
|
+
- **Idempotent.** On the live path `v_db_index` is read first, and an index over *exactly*
|
|
703
|
+
these columns short-circuits to `already-exists` with no form session and no write.
|
|
704
|
+
Column **order** is part of an index's identity - `[a,b]` is not `[b,a]`.
|
|
705
|
+
- **`--name` is refused.** The platform's form has no name input; ServiceNow names the
|
|
706
|
+
index itself. Reporting a name the instance does not carry would be a lie, so the
|
|
707
|
+
created index's *real* name is returned in `name` instead.
|
|
708
|
+
- **The read-back is the proof.** After the POST the index is polled for in `v_db_index`
|
|
709
|
+
(default 10 checks, 3 s apart - a build on a populated table is asynchronous). If it
|
|
710
|
+
never appears the status is `failed`: a form processor returning a page is not evidence
|
|
711
|
+
an ALTER ran, and a unique index cannot build over duplicate values (EMPTY counts).
|
|
712
|
+
- **Uniqueness is still never claimed.** `uniqueness-enforced` stays in `unverified` on
|
|
713
|
+
every status.
|
|
714
|
+
|
|
715
|
+
**Requires a username+password identity that can form-log-in.** An instance on
|
|
716
|
+
API-key-only auth, SSO or MFA rejects the form login however valid the API key is; the
|
|
717
|
+
verb fails at the session with that diagnosis rather than a mystery 302, and no `.do`
|
|
718
|
+
replay (including `create-table`'s) can work in that state.
|
|
719
|
+
|
|
720
|
+
Exit codes: `0` created / already-exists / dry-run, `1` bad args, `2` failed.
|
|
721
|
+
|
|
655
722
|
### Set a field on a record
|
|
656
723
|
|
|
657
724
|
Set scalar field value(s) on an **existing** data record, capture the change into
|
|
@@ -954,7 +1021,12 @@ Claude Code and agents: `create_view`, `set_list_layout`, `set_form_layout`,
|
|
|
954
1021
|
`set_related_lists`, `add_choices_to_field`, the schema verbs `create_table` /
|
|
955
1022
|
`add_column` / `add_index` (a single-column unique index via `sys_dictionary.unique`,
|
|
956
1023
|
read back from the `v_db_index` view - uniqueness enforcement is always reported
|
|
957
|
-
unverified)
|
|
1024
|
+
unverified) / `index_list` (read-only: a table's database indexes from `v_db_index`,
|
|
1025
|
+
the only index read surface - `sys_index` is API-level-ACL 403 and `sys_index_column`
|
|
1026
|
+
does not exist) / `index_create` (create an index, composite and non-unique included, by
|
|
1027
|
+
replaying the platform index-creator form; dry-run by default, idempotent, read back from
|
|
1028
|
+
`v_db_index` - and **not** captured in an update set, because a database index is a
|
|
1029
|
+
physical per-instance change), the record-write verbs `set_field` (update scalar fields on an
|
|
958
1030
|
existing record) and `create_record` (insert one record) — both update-set-captured
|
|
959
1031
|
and read-back-verified — `host_assets` (deploy a built dist/), plus the Flow Designer
|
|
960
1032
|
tools `flow_view` (read a flow/subflow's step graph), `action_view` (read an action
|
package/dist/cli.js
CHANGED
|
@@ -1300,6 +1300,25 @@ function printHelp() {
|
|
|
1300
1300
|
" is treated exactly like a proven collision. Success is\n" +
|
|
1301
1301
|
" read back from v_db_index; that view has no uniqueness field, so\n" +
|
|
1302
1302
|
" ENFORCEMENT is always reported unverified.\n" +
|
|
1303
|
+
" index-list List a table's DATABASE INDEXES from the v_db_index view (read-only)\n" +
|
|
1304
|
+
" (--table <name> [--json])\n" +
|
|
1305
|
+
" sys_index is API-level-ACL 403 and sys_index_column does not\n" +
|
|
1306
|
+
" exist, so v_db_index is the only index read surface. It has no\n" +
|
|
1307
|
+
" uniqueness field, so WHICH indexes are unique is always\n" +
|
|
1308
|
+
" reported unverified.\n" +
|
|
1309
|
+
" index-create Create a DATABASE INDEX (composite and non-unique included) by\n" +
|
|
1310
|
+
" replaying the platform index-creator form, then read it back\n" +
|
|
1311
|
+
" DRY-RUN BY DEFAULT — nothing is sent or read without --confirm\n" +
|
|
1312
|
+
" (--table <name> --columns <a[,b,...]> [--unique]\n" +
|
|
1313
|
+
" [--access-method <m>] [--confirm] [--dry-run]\n" +
|
|
1314
|
+
" [--poll-attempts <n>] [--poll-interval-ms <n>] [--debug] [--json])\n" +
|
|
1315
|
+
" A DATABASE INDEX IS PHYSICAL AND PER-INSTANCE: it is NOT captured\n" +
|
|
1316
|
+
" in an update set and does NOT travel with a promotion — re-run it\n" +
|
|
1317
|
+
" against every environment. There is no --update-set for that\n" +
|
|
1318
|
+
" reason. Idempotent: an index over exactly those columns already\n" +
|
|
1319
|
+
" present returns already-exists with no write. --name is REFUSED —\n" +
|
|
1320
|
+
" the platform's form has no name input; the real name is returned.\n" +
|
|
1321
|
+
" Needs a username+password identity that can form-log-in.\n" +
|
|
1303
1322
|
" set-column Update an EXISTING column's SCHEMA (label/mandatory/default/read-only/max-length),\n" +
|
|
1304
1323
|
" into an update set, then verify against the instance\n" +
|
|
1305
1324
|
" (--table <t> --column <c> --update-set <sys_id>\n" +
|
|
@@ -1662,6 +1681,130 @@ async function runAddIndex(flags) {
|
|
|
1662
1681
|
return 2;
|
|
1663
1682
|
return 0;
|
|
1664
1683
|
}
|
|
1684
|
+
/**
|
|
1685
|
+
* dove-sn index-list:
|
|
1686
|
+
* --table x_cadso_automate_message_batch_recipient [--json]
|
|
1687
|
+
*
|
|
1688
|
+
* Read-only. Lists the table's database indexes from the v_db_index view — the only
|
|
1689
|
+
* index read surface an instance exposes (sys_index is API-level-ACL 403,
|
|
1690
|
+
* sys_index_column does not exist).
|
|
1691
|
+
*
|
|
1692
|
+
* Exit codes: 0 read, 1 bad args.
|
|
1693
|
+
*/
|
|
1694
|
+
async function runIndexList(flags) {
|
|
1695
|
+
if (!flags.table) {
|
|
1696
|
+
process.stderr.write("index-list: --table <name> is required\n");
|
|
1697
|
+
return 1;
|
|
1698
|
+
}
|
|
1699
|
+
var result = await (0, table_1.listIndexes)({
|
|
1700
|
+
client: (0, client_1.createClient)({}),
|
|
1701
|
+
table: flags.table,
|
|
1702
|
+
});
|
|
1703
|
+
if (flags.json === "true") {
|
|
1704
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1705
|
+
return 0;
|
|
1706
|
+
}
|
|
1707
|
+
process.stdout.write(result.table + " — " + result.indexes.length + " index(es)\n");
|
|
1708
|
+
for (var i = 0; i < result.indexes.length; i += 1) {
|
|
1709
|
+
var idx = result.indexes[i];
|
|
1710
|
+
process.stdout.write(" " +
|
|
1711
|
+
(idx.name || "(unnamed)") +
|
|
1712
|
+
" [" +
|
|
1713
|
+
idx.columns.join(", ") +
|
|
1714
|
+
"] " +
|
|
1715
|
+
(idx.type || "(no access_method)") +
|
|
1716
|
+
"\n");
|
|
1717
|
+
}
|
|
1718
|
+
process.stdout.write(result.note + "\nUNVERIFIED: " + result.unverified.join(", ") + "\n");
|
|
1719
|
+
return 0;
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* dove-sn index-create:
|
|
1723
|
+
* --table x_cadso_core_u_smoke --columns a,b [--unique] [--access-method <m>]
|
|
1724
|
+
* [--confirm] [--dry-run] [--poll-attempts <n>] [--poll-interval-ms <n>]
|
|
1725
|
+
* [--debug] [--json]
|
|
1726
|
+
*
|
|
1727
|
+
* DRY-RUN BY DEFAULT — nothing is sent (and nothing is even READ) without --confirm;
|
|
1728
|
+
* --dry-run forces a plan even with it. There is deliberately NO --update-set: a
|
|
1729
|
+
* database index is physical and is not captured in one.
|
|
1730
|
+
*
|
|
1731
|
+
* Exit codes: 0 created / already-exists / dry-run, 1 bad args, 2 failed (which
|
|
1732
|
+
* includes "the form was posted but no index was read back").
|
|
1733
|
+
*/
|
|
1734
|
+
async function runIndexCreate(flags) {
|
|
1735
|
+
var columns = splitList(flags.columns || "");
|
|
1736
|
+
if (!flags.table || columns.length === 0) {
|
|
1737
|
+
process.stderr.write("index-create: --table <name> and --columns <a[,b,...]> are required\n");
|
|
1738
|
+
return 1;
|
|
1739
|
+
}
|
|
1740
|
+
if (flags["update-set"]) {
|
|
1741
|
+
process.stderr.write("index-create: --update-set is not accepted. A database index is a PHYSICAL, " +
|
|
1742
|
+
"PER-INSTANCE change — it is NOT captured in an update set and does not " +
|
|
1743
|
+
"travel with a promotion. Run index-create against each environment.\n");
|
|
1744
|
+
return 1;
|
|
1745
|
+
}
|
|
1746
|
+
var params = {
|
|
1747
|
+
client: (0, client_1.createClient)({}),
|
|
1748
|
+
table: flags.table,
|
|
1749
|
+
columns: columns,
|
|
1750
|
+
unique: flags.unique === "true",
|
|
1751
|
+
confirm: flags.confirm === "true",
|
|
1752
|
+
dryRun: flags["dry-run"] === "true",
|
|
1753
|
+
};
|
|
1754
|
+
if (flags.name !== undefined)
|
|
1755
|
+
params.name = flags.name;
|
|
1756
|
+
if (flags["access-method"])
|
|
1757
|
+
params.accessMethod = flags["access-method"];
|
|
1758
|
+
if (flags["form-path"])
|
|
1759
|
+
params.formPath = flags["form-path"];
|
|
1760
|
+
if (flags.debug === "true")
|
|
1761
|
+
params.debug = true;
|
|
1762
|
+
// A non-integer poll setting would make the bounded wait unbounded (or zero).
|
|
1763
|
+
// Reject it here with a named message instead of letting NaN reach the loop.
|
|
1764
|
+
var numeric = [
|
|
1765
|
+
["poll-attempts", "pollAttempts"],
|
|
1766
|
+
["poll-interval-ms", "pollIntervalMs"],
|
|
1767
|
+
];
|
|
1768
|
+
for (var n = 0; n < numeric.length; n += 1) {
|
|
1769
|
+
var raw = flags[numeric[n][0]];
|
|
1770
|
+
if (raw === undefined)
|
|
1771
|
+
continue;
|
|
1772
|
+
var value = Number(raw);
|
|
1773
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
1774
|
+
process.stderr.write("index-create: --" +
|
|
1775
|
+
numeric[n][0] +
|
|
1776
|
+
" must be a positive integer (got '" +
|
|
1777
|
+
raw +
|
|
1778
|
+
"')\n");
|
|
1779
|
+
return 1;
|
|
1780
|
+
}
|
|
1781
|
+
params[numeric[n][1]] = value;
|
|
1782
|
+
}
|
|
1783
|
+
var result = await (0, table_1.createIndex)(params);
|
|
1784
|
+
if (flags.json === "true") {
|
|
1785
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1786
|
+
}
|
|
1787
|
+
else {
|
|
1788
|
+
process.stdout.write("[" +
|
|
1789
|
+
result.status +
|
|
1790
|
+
"] " +
|
|
1791
|
+
result.table +
|
|
1792
|
+
" [" +
|
|
1793
|
+
result.columns.join(", ") +
|
|
1794
|
+
"]" +
|
|
1795
|
+
(result.name ? " -> " + result.name : "") +
|
|
1796
|
+
(result.instance ? " on " + result.instance : "") +
|
|
1797
|
+
(result.verified ? " — verified" : "") +
|
|
1798
|
+
"\n" +
|
|
1799
|
+
result.note +
|
|
1800
|
+
"\nUNVERIFIED: " +
|
|
1801
|
+
result.unverified.join(", ") +
|
|
1802
|
+
"\n");
|
|
1803
|
+
}
|
|
1804
|
+
if (result.status === "failed")
|
|
1805
|
+
return 2;
|
|
1806
|
+
return 0;
|
|
1807
|
+
}
|
|
1665
1808
|
/** Parse a CLI boolean flag. Bare `--mandatory` means true; `--mandatory false` means
|
|
1666
1809
|
* false. Anything else is rejected rather than quietly coerced to `true`. */
|
|
1667
1810
|
function parseBoolFlag(name, raw, verb = "set-column") {
|
|
@@ -2544,6 +2687,12 @@ async function main() {
|
|
|
2544
2687
|
if (parsed.command === "add-index") {
|
|
2545
2688
|
return await runAddIndex(parsed.flags);
|
|
2546
2689
|
}
|
|
2690
|
+
if (parsed.command === "index-list") {
|
|
2691
|
+
return await runIndexList(parsed.flags);
|
|
2692
|
+
}
|
|
2693
|
+
if (parsed.command === "index-create") {
|
|
2694
|
+
return await runIndexCreate(parsed.flags);
|
|
2695
|
+
}
|
|
2547
2696
|
if (parsed.command === "set-column") {
|
|
2548
2697
|
return await runSetColumn(parsed.flags, parsed.bare);
|
|
2549
2698
|
}
|
|
@@ -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:
|
|
372
|
-
counts: countByTable(
|
|
373
|
-
total:
|
|
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
|
-
|
|
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
|
-
|
|
558
|
+
verify = (0, stepOps_1.verifySteps)(expected, (0, stepOps_1.summarizeSteps)(fresh), allCids(finalSteps));
|
|
449
559
|
if (fresh.length !== finalSteps.length) {
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
}
|
|
@@ -36,8 +36,12 @@ export interface WriteOpResult {
|
|
|
36
36
|
logicalName: string;
|
|
37
37
|
table: string;
|
|
38
38
|
sysId: string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
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/dist/index.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export { sincPlugin } from "./plugin";
|
|
|
21
21
|
export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, resolveScope, slugInternalName, fetchActionSteps, findStepInput, triggerPublication, publishActionType, editActionType, defineActionType, planActionDefinition, validateDefineSpec, applyStepOps, verifySteps, summarizeSteps, formatStepPill, readFlow, readActionType, publishFlow, copyFlow, createFlow, buildPublishModel, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError, } from "./flowDesigner";
|
|
22
22
|
export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, CloneActionTypePlan, CloneActionTypeStepReport, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, EditActionTypeParams, EditActionTypeResult, EditActionTypeOps, DefineActionSpec, DefineActionInputSpec, DefineActionOutputSpec, DefineStepSpec, DefineStepVarSpec, DefineStepOutputSpec, DefineStepValue, DefineActionTypeParams, DefineActionTypeResult, DefineActionDiff, DefineActionPlan, PlanActionDefinitionParams, ActionView, StepView, StepOps, StepRecord, StepSummary, StepIoSummary, PatchStepScriptOp, SetStepInputOp, AddStepOutputOp, AddStepInputOp, ApplyStepOpsResult, VerifyStepsResult, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, CopyFlowParams, CopyFlowResult, CreateFlowParams, CreateFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult, } from "./flowDesigner";
|
|
23
23
|
export type { ServiceNowClientConfig, ChoiceValue, ChoiceType, AddChoicesParams, AddChoicesResult, ChoiceActionResult, RemoveChoicesParams, RemoveChoicesResult, ChoiceRemovalResult, DictionaryRecord, UpdateSetRecord, LayoutAction, LayoutRecordResult, LayoutResult, CreateViewParams, CreateViewResult, FormSectionSpec, SetFormLayoutParams, SetListLayoutParams, SetRelatedListsParams, ChunkRole, ChunkInfo, ChunkResult, PrunedResult, HostAssetsParams, HostAssetsResult, } from "./types";
|
|
24
|
-
export { createTable, projectTableGraph, buildColumnXml, normalizeColumns, resolveType, applyTableSaveOverlay, defaultAccessFlags, TYPE_MAP, DEFAULT_SUPER_CLASS, DEFAULT_SAVE_ACTION, addColumn, deriveElement, addIndex, parseIndexColumns, indexMatchesColumns, setColumn, resolveAttributes, toStoredValue, setTable, resolveTableAttributes, } from "./table";
|
|
25
|
-
export type { CreateTableParams, CreateTableResult, AddColumnParams, AddColumnResult, AddIndexParams, AddIndexResult, AddIndexVerification, SetColumnParams, SetColumnResult, ColumnAttributes, AttributeChange, SetTableParams, SetTableResult, TableAttributes, TableGraph, NormalizedColumn, ColumnSpec, AccessFlags, OverlaySpec, } from "./table";
|
|
24
|
+
export { createTable, projectTableGraph, buildColumnXml, normalizeColumns, resolveType, applyTableSaveOverlay, defaultAccessFlags, TYPE_MAP, DEFAULT_SUPER_CLASS, DEFAULT_SAVE_ACTION, addColumn, deriveElement, addIndex, parseIndexColumns, indexMatchesColumns, listIndexes, createIndex, validateCreateIndex, DEFAULT_INDEX_FORM_PATH, NOT_IN_UPDATE_SET, setColumn, resolveAttributes, toStoredValue, setTable, resolveTableAttributes, } from "./table";
|
|
25
|
+
export type { CreateTableParams, CreateTableResult, AddColumnParams, AddColumnResult, AddIndexParams, AddIndexResult, AddIndexVerification, ListIndexesParams, ListIndexesResult, TableIndex, CreateIndexParams, CreateIndexResult, SetColumnParams, SetColumnResult, ColumnAttributes, AttributeChange, SetTableParams, SetTableResult, TableAttributes, TableGraph, NormalizedColumn, ColumnSpec, AccessFlags, OverlaySpec, } from "./table";
|
|
26
26
|
export { setField } from "./setField";
|
|
27
27
|
export { createRecord } from "./createRecord";
|
|
28
28
|
export type { RecordWriteResult, SetFieldParams, SetFieldResult, } from "./setField";
|