@dudousxd/nestjs-catalog 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog.events.d.ts +26 -1
- package/dist/catalog.events.js +8 -0
- package/dist/catalog.pipeline.d.ts +713 -6
- package/dist/catalog.pipeline.js +487 -11
- package/dist/client.d.ts +2 -2
- package/dist/client.js +34 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +18 -3
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* schedules, retries and checkpoints, and writing a second one would mean two
|
|
8
8
|
* systems each believing they decide when a load runs.
|
|
9
9
|
*/
|
|
10
|
-
import type
|
|
10
|
+
import { type CatalogRevision } from './catalog.workspace';
|
|
11
11
|
/**
|
|
12
12
|
* Where a connector pulls from.
|
|
13
13
|
*
|
|
@@ -903,7 +903,7 @@ export declare function workflowRowY(row: number): number;
|
|
|
903
903
|
* vocabularies would let them disagree. Credentials stay out of the catalog
|
|
904
904
|
* here exactly as they do everywhere else.
|
|
905
905
|
*/
|
|
906
|
-
export interface WorkflowSourceNode extends WorkflowNodeBase {
|
|
906
|
+
export interface WorkflowSourceNode extends WorkflowNodeBase, ReusableNodeRef {
|
|
907
907
|
kind: 'source';
|
|
908
908
|
/** Named `sourceKind` rather than `kind`, which the union already uses. */
|
|
909
909
|
sourceKind: ConnectorKind;
|
|
@@ -925,6 +925,56 @@ export interface WorkflowTransformNode extends WorkflowNodeBase {
|
|
|
925
925
|
* logic used at three points in a graph is versioned once and fixed once.
|
|
926
926
|
*/
|
|
927
927
|
transformId: string;
|
|
928
|
+
/**
|
|
929
|
+
* Which version of that code to run. Absent follows the latest.
|
|
930
|
+
*
|
|
931
|
+
* ## The claim this field exists to make true
|
|
932
|
+
*
|
|
933
|
+
* The line above says a shared transform is "versioned once and fixed once",
|
|
934
|
+
* and until this field there was nothing here to fix it *to*. A transform node
|
|
935
|
+
* named a `transformId` and nothing else, `runTransform` resolved it with
|
|
936
|
+
* `getTransform`, and `getTransform` answers with whatever is in the row
|
|
937
|
+
* today. So editing a transform changed every graph that referenced it, at
|
|
938
|
+
* once, with nothing in anybody else's diff and nothing in their run history
|
|
939
|
+
* to explain the change — the graph's fingerprint does not move (see
|
|
940
|
+
* `workflowGraphHash`, which excludes the transform's version on purpose and
|
|
941
|
+
* still does) so there is not even a new graph version to look at.
|
|
942
|
+
*
|
|
943
|
+
* That was survivable only because almost nothing was shared. It stops being
|
|
944
|
+
* survivable the moment reusable nodes make sharing the point, which is why
|
|
945
|
+
* this landed with them rather than after them.
|
|
946
|
+
*
|
|
947
|
+
* The precedent is {@link WorkflowCallNode.callVersion}, whose docblock makes
|
|
948
|
+
* the same argument about somebody else's workflow: the version is authored,
|
|
949
|
+
* and a run that would have used a different one is refused rather than
|
|
950
|
+
* quietly run. This is that rule pointed at code stored in the same database.
|
|
951
|
+
*
|
|
952
|
+
* ## Why absent is allowed to mean "latest" rather than being backfilled
|
|
953
|
+
*
|
|
954
|
+
* Because that is what every graph already in a deployment means, exactly, and
|
|
955
|
+
* a backfill would be a behaviour change dressed as a migration. Pinning the
|
|
956
|
+
* live version at upgrade time freezes graphs whose authors have been relying
|
|
957
|
+
* on edits reaching them; pinning nothing but *refusing* an unpinned node
|
|
958
|
+
* stops every scheduled load on the deployment. Both are an upgrade that
|
|
959
|
+
* changes what runs, and neither is a decision this package gets to make for
|
|
960
|
+
* somebody. So absent keeps meaning precisely what it has always meant, and
|
|
961
|
+
* the repair is that following is now a **stated** position with a pinned
|
|
962
|
+
* alternative beside it, rather than the only position and an unstated one.
|
|
963
|
+
*
|
|
964
|
+
* What does change is that it is no longer silent: `describeTransformPin`
|
|
965
|
+
* turns either state into a sentence a screen can render, so "this follows
|
|
966
|
+
* whatever that code becomes" is something the author is told rather than
|
|
967
|
+
* something they find out.
|
|
968
|
+
*
|
|
969
|
+
* ## What a pin costs
|
|
970
|
+
*
|
|
971
|
+
* A pinned version is resolved out of `catalog_revision`, which is bounded per
|
|
972
|
+
* subject (`CATALOG_REVISION_LIMIT`). A pin to a version that has been evicted
|
|
973
|
+
* cannot be honoured, and the run fails saying so rather than falling back to
|
|
974
|
+
* the latest — a pin nobody could check is not a pin, which is the sentence
|
|
975
|
+
* `WorkflowRunSteps.checkCall` already stands on.
|
|
976
|
+
*/
|
|
977
|
+
transformVersion?: number;
|
|
928
978
|
}
|
|
929
979
|
/**
|
|
930
980
|
* Writes into an object type and commits.
|
|
@@ -937,9 +987,17 @@ export interface WorkflowTransformNode extends WorkflowNodeBase {
|
|
|
937
987
|
* convenience that two workflows already provide. Branching inside the graph
|
|
938
988
|
* stays fully supported; every path simply has to arrive here.
|
|
939
989
|
*/
|
|
940
|
-
export interface WorkflowSinkNode extends WorkflowNodeBase {
|
|
990
|
+
export interface WorkflowSinkNode extends WorkflowNodeBase, ReusableNodeRef {
|
|
941
991
|
kind: 'sink';
|
|
942
|
-
/**
|
|
992
|
+
/**
|
|
993
|
+
* Which object type the rows become.
|
|
994
|
+
*
|
|
995
|
+
* Stays on the node even when the node is an instance of a reusable one, and
|
|
996
|
+
* that is the one field a reusable body may **not** move under a graph. See
|
|
997
|
+
* {@link ReusableNodeRef} — the write grants a graph was checked against are
|
|
998
|
+
* checked against this string, so a reusable sink that could repoint it would
|
|
999
|
+
* be a way to write a type the author was never granted.
|
|
1000
|
+
*/
|
|
943
1001
|
targetType: string;
|
|
944
1002
|
/**
|
|
945
1003
|
* Whether the commit replaces the dataset or merges into it. Exactly the
|
|
@@ -1586,6 +1644,304 @@ export interface WorkflowFilterNode extends WorkflowNodeBase {
|
|
|
1586
1644
|
* a `transformId` and nothing catches it.
|
|
1587
1645
|
*/
|
|
1588
1646
|
export type WorkflowNode = WorkflowSourceNode | WorkflowTransformNode | WorkflowSinkNode | WorkflowCallNode | WorkflowIfNode | WorkflowFilterNode;
|
|
1647
|
+
/**
|
|
1648
|
+
* The node kinds that can be saved once and used in several graphs.
|
|
1649
|
+
*
|
|
1650
|
+
* Source and sink, and the reason is that those two are the only kinds whose
|
|
1651
|
+
* *composition* is worth a name. A connection is already a shared object, and it
|
|
1652
|
+
* answers "which database" — but nobody reaches for "the warehouse" when they
|
|
1653
|
+
* draw a graph, they reach for "the nightly MVR pull from the warehouse", which
|
|
1654
|
+
* is the connection **plus** the query, plus whether it reads everything or only
|
|
1655
|
+
* what changed, plus what the thing is called. That composition had nowhere to
|
|
1656
|
+
* live, so it was retyped per graph and the fourteenth copy was the one with the
|
|
1657
|
+
* typo in the `WHERE` clause.
|
|
1658
|
+
*
|
|
1659
|
+
* A transform is deliberately **not** here, and that is not an omission: a
|
|
1660
|
+
* transform is already a stored object referenced by id
|
|
1661
|
+
* ({@link WorkflowTransformNode.transformId}), so a reusable transform node
|
|
1662
|
+
* would be a second way to say the same thing. What it was missing is a version
|
|
1663
|
+
* pin, which is {@link WorkflowTransformNode.transformVersion}, not this.
|
|
1664
|
+
*
|
|
1665
|
+
* `call`, `if` and `filter` are not here either, and the record below is where
|
|
1666
|
+
* each of them says so — see {@link NODE_KIND_IS_REUSABLE}.
|
|
1667
|
+
*/
|
|
1668
|
+
export declare const REUSABLE_NODE_KINDS: readonly ["source", "sink"];
|
|
1669
|
+
export type ReusableNodeKind = (typeof REUSABLE_NODE_KINDS)[number];
|
|
1670
|
+
/** Same reason as {@link isConnectorKind}: one list, no second copy to drift. */
|
|
1671
|
+
export declare function isReusableNodeKind(value: unknown): value is ReusableNodeKind;
|
|
1672
|
+
/**
|
|
1673
|
+
* Whether each node kind may be saved as a reusable node.
|
|
1674
|
+
*
|
|
1675
|
+
* A record over every kind rather than a shorter list of the two that can,
|
|
1676
|
+
* because this codebase keeps being bitten by hand-maintained lists going quiet
|
|
1677
|
+
* — most recently the add-node row that shipped the `filter` node with no way to
|
|
1678
|
+
* create it. A kind added to {@link WORKFLOW_NODE_KINDS} without an entry here
|
|
1679
|
+
* is a type error in this file naming the decision it has not made.
|
|
1680
|
+
*
|
|
1681
|
+
* The second `satisfies` is the other half of the same guard, in the other
|
|
1682
|
+
* direction: anything named in {@link REUSABLE_NODE_KINDS} has to be `true`
|
|
1683
|
+
* here, so the list and this table cannot come apart. Adding `'filter'` to that
|
|
1684
|
+
* list without a `ReusableFilterBody` therefore fails to compile twice — once
|
|
1685
|
+
* here, and once at every narrowing over {@link ReusableNodeBody}.
|
|
1686
|
+
*
|
|
1687
|
+
* Why each `false`:
|
|
1688
|
+
*
|
|
1689
|
+
* - `transform` — already a reference to a stored object. See
|
|
1690
|
+
* {@link REUSABLE_NODE_KINDS}.
|
|
1691
|
+
* - `call` — already a reference to somebody else's registered workflow, pinned
|
|
1692
|
+
* by name and version. There is nothing left to name.
|
|
1693
|
+
* - `if` and `filter` — a predicate is *about* the rows in front of it. A gate
|
|
1694
|
+
* saved under a name and dropped into another graph tests a column that graph
|
|
1695
|
+
* may not have, and a filter is worse: {@link WorkflowFilterNode.narrows} is
|
|
1696
|
+
* an acknowledgement about *this* graph's sinks, so a shared one would carry
|
|
1697
|
+
* somebody else's acknowledgement into a graph they never saw.
|
|
1698
|
+
*/
|
|
1699
|
+
export declare const NODE_KIND_IS_REUSABLE: {
|
|
1700
|
+
readonly source: true;
|
|
1701
|
+
readonly transform: false;
|
|
1702
|
+
readonly sink: true;
|
|
1703
|
+
readonly call: false;
|
|
1704
|
+
readonly if: false;
|
|
1705
|
+
readonly filter: false;
|
|
1706
|
+
};
|
|
1707
|
+
/** Whether this kind can be saved as a reusable node. Reads {@link NODE_KIND_IS_REUSABLE}. */
|
|
1708
|
+
export declare function nodeKindIsReusable(kind: WorkflowNodeKind): boolean;
|
|
1709
|
+
/**
|
|
1710
|
+
* What a node carries when it is an instance of a reusable one.
|
|
1711
|
+
*
|
|
1712
|
+
* ## By reference, and that is the whole feature
|
|
1713
|
+
*
|
|
1714
|
+
* The cheap version of "save this node" copies its fields into the next graph
|
|
1715
|
+
* and forgets where they came from. It is cheaper in every way except the one
|
|
1716
|
+
* that was asked for: "quantos workflows tão usando quais nós" is unanswerable
|
|
1717
|
+
* about a copy, because after the copy there is nothing left that says the two
|
|
1718
|
+
* nodes are the same node. So the id stays on the node, `GET
|
|
1719
|
+
* reusable-nodes/:id/workflows` counts by it, and the count is exact rather than
|
|
1720
|
+
* a guess at which configurations look alike.
|
|
1721
|
+
*
|
|
1722
|
+
* ## The fields stay on the node as well, and are not the authority
|
|
1723
|
+
*
|
|
1724
|
+
* A source node that names a reusable node still carries its own `sourceKind`,
|
|
1725
|
+
* `config` and the rest. That is a **cache**, not a copy — the identical
|
|
1726
|
+
* arrangement `toGraph` already documents for a source that names a connector:
|
|
1727
|
+
* the fields are kept so that `validateWorkflow` stays pure and the canvas can
|
|
1728
|
+
* draw the node without a round trip, and execution re-reads the stored object,
|
|
1729
|
+
* so an edit takes effect on the next run.
|
|
1730
|
+
*
|
|
1731
|
+
* ## Which is exactly why {@link version} exists
|
|
1732
|
+
*
|
|
1733
|
+
* "An edit takes effect on the next run" is the useful behaviour and the
|
|
1734
|
+
* dangerous one, and which of the two it is depends on whether the person
|
|
1735
|
+
* editing knows who else is downstream. So the reference states its position:
|
|
1736
|
+
*
|
|
1737
|
+
* - **absent** — follows the latest. What a connector reference has always
|
|
1738
|
+
* meant, and the right default for "the warehouse pull" that four graphs
|
|
1739
|
+
* share and all four want fixed at once.
|
|
1740
|
+
* - **present** — pinned. The reusable node may move on and this graph does not,
|
|
1741
|
+
* until somebody edits *this* graph, which is a new version of it with a diff
|
|
1742
|
+
* to read.
|
|
1743
|
+
*
|
|
1744
|
+
* Both are in the graph fingerprint, so changing position is an edit; neither is
|
|
1745
|
+
* silent, which was the whole complaint. This is the same rule
|
|
1746
|
+
* {@link WorkflowTransformNode.transformVersion} states for transforms, and it
|
|
1747
|
+
* is stated twice on purpose rather than shared: they are two different stored
|
|
1748
|
+
* objects and a reader arriving at either should not have to find the other.
|
|
1749
|
+
*/
|
|
1750
|
+
export interface ReusableNodeRef {
|
|
1751
|
+
/**
|
|
1752
|
+
* The reusable node this is an instance of, or absent for a node configured
|
|
1753
|
+
* in place. Both remain first-class, indefinitely: a one-off source is not a
|
|
1754
|
+
* failure to reuse something.
|
|
1755
|
+
*/
|
|
1756
|
+
useId?: string;
|
|
1757
|
+
/** The pinned version of that reusable node. Absent follows the latest. */
|
|
1758
|
+
useVersion?: number;
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* The part of a source node that is worth saving under a name.
|
|
1762
|
+
*
|
|
1763
|
+
* Everything a source needs to read, and nothing that belongs to the graph it
|
|
1764
|
+
* sits in. Absent here, on purpose: `id`, which has to be unique within one
|
|
1765
|
+
* graph and is also a durable step name, and `position`, which is where somebody
|
|
1766
|
+
* dragged the box on one canvas.
|
|
1767
|
+
*
|
|
1768
|
+
* `name` is absent too, and that is the less obvious one. A reusable node has a
|
|
1769
|
+
* name — it is how "flip db sink" is a thing anybody can ask for — but it lives
|
|
1770
|
+
* on {@link CatalogReusableNode} rather than in the body, because a graph is
|
|
1771
|
+
* allowed to call its instance something else. Folding the name in would rename
|
|
1772
|
+
* every node in every graph the moment somebody tidied up the library's naming,
|
|
1773
|
+
* and a node's name is documented as cosmetic precisely so that it is nobody
|
|
1774
|
+
* else's business.
|
|
1775
|
+
*/
|
|
1776
|
+
export interface ReusableSourceBody {
|
|
1777
|
+
kind: 'source';
|
|
1778
|
+
sourceKind: ConnectorKind;
|
|
1779
|
+
connectionId?: string;
|
|
1780
|
+
config: Record<string, unknown>;
|
|
1781
|
+
secretEnvVar?: string;
|
|
1782
|
+
mode?: 'full' | 'incremental';
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* The part of a sink node that is worth saving under a name.
|
|
1786
|
+
*
|
|
1787
|
+
* `targetType` is in here, so "the Mvr full reload" is a thing that can be named
|
|
1788
|
+
* — and it is also the field {@link applyReusableNode} refuses to move under a
|
|
1789
|
+
* graph that already committed to a different one. Both are true at once and
|
|
1790
|
+
* they are not in tension: a graph adopting this body *takes* the type at the
|
|
1791
|
+
* moment it is saved, and is grant-checked for it then. What may not happen is
|
|
1792
|
+
* the type changing afterwards, under a graph whose author is not looking, into
|
|
1793
|
+
* one they were never granted.
|
|
1794
|
+
*/
|
|
1795
|
+
export interface ReusableSinkBody {
|
|
1796
|
+
kind: 'sink';
|
|
1797
|
+
targetType: string;
|
|
1798
|
+
mode?: 'full' | 'incremental';
|
|
1799
|
+
}
|
|
1800
|
+
export type ReusableNodeBody = ReusableSourceBody | ReusableSinkBody;
|
|
1801
|
+
/**
|
|
1802
|
+
* {@link unreachableNodeKind}, for reusable bodies, and for the identical
|
|
1803
|
+
* reason: every branch over {@link ReusableNodeBody} ends here, so a body added
|
|
1804
|
+
* to the union without a rule for folding it onto a node is a type error naming
|
|
1805
|
+
* the file rather than a graph that saves and then runs a node nobody
|
|
1806
|
+
* configured. It throws as well, because these arrive as JSON out of a column.
|
|
1807
|
+
*/
|
|
1808
|
+
export declare function unreachableReusableNodeKind(body: never, where: string): never;
|
|
1809
|
+
/**
|
|
1810
|
+
* A node body saved once, under a name, and used from several graphs.
|
|
1811
|
+
*
|
|
1812
|
+
* Versioned exactly as a {@link CatalogTransform} is, and archived in the same
|
|
1813
|
+
* `catalog_revision` table under its own subject — one table, one retention
|
|
1814
|
+
* rule, which is the argument `RevisionRow` already makes for holding transforms
|
|
1815
|
+
* and saved queries together. That is what makes
|
|
1816
|
+
* {@link ReusableNodeRef.version} resolvable rather than merely a number: a
|
|
1817
|
+
* graph pinned to v2 can still be handed v2's body after v3 exists.
|
|
1818
|
+
*
|
|
1819
|
+
* There is no library screen and there is deliberately not going to be one. A
|
|
1820
|
+
* reusable node is offered where a node is added and its usage count is shown on
|
|
1821
|
+
* the node itself, because the number changes a decision exactly at the moment
|
|
1822
|
+
* somebody is about to change something four other graphs depend on — which is
|
|
1823
|
+
* not a moment they spend on a listing page.
|
|
1824
|
+
*/
|
|
1825
|
+
export interface CatalogReusableNode {
|
|
1826
|
+
id: string;
|
|
1827
|
+
/**
|
|
1828
|
+
* What people ask for it by — "flip db sink". Unique across reusable nodes,
|
|
1829
|
+
* enforced in the store, because two of them called the same thing is a
|
|
1830
|
+
* picker that cannot be used.
|
|
1831
|
+
*/
|
|
1832
|
+
name: string;
|
|
1833
|
+
description?: string;
|
|
1834
|
+
/** Which node kind this stands for. Redundant with `body.kind` and indexed. */
|
|
1835
|
+
kind: ReusableNodeKind;
|
|
1836
|
+
body: ReusableNodeBody;
|
|
1837
|
+
/**
|
|
1838
|
+
* Counts saves that changed the **body**, exactly as a transform's counts
|
|
1839
|
+
* saves that changed the code: renaming a reusable node is not a new version
|
|
1840
|
+
* of it, and inflating the number would make a pin to it meaningless.
|
|
1841
|
+
*/
|
|
1842
|
+
version: number;
|
|
1843
|
+
createdBy: string;
|
|
1844
|
+
createdAt: string;
|
|
1845
|
+
updatedAt: string;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* How many graphs use a reusable node, and which.
|
|
1849
|
+
*
|
|
1850
|
+
* Shaped after `GET connections/:id/workflows`, which answers the same question
|
|
1851
|
+
* for a connection, because an operator asking either is about to do the same
|
|
1852
|
+
* thing: change something and want to know who is downstream. A count on its own
|
|
1853
|
+
* would be a number to be alarmed by; the list is what makes it actionable.
|
|
1854
|
+
*/
|
|
1855
|
+
export interface CatalogReusableNodeUse {
|
|
1856
|
+
workflowId: string;
|
|
1857
|
+
workflowName: string;
|
|
1858
|
+
status: WorkflowStatus;
|
|
1859
|
+
/** The node within that graph, and the position its reference states. */
|
|
1860
|
+
nodeId: string;
|
|
1861
|
+
nodeName: string;
|
|
1862
|
+
/** The pinned version, or absent for a reference that follows the latest. */
|
|
1863
|
+
pinnedVersion?: number;
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Fold a reusable body onto the node that references it.
|
|
1867
|
+
*
|
|
1868
|
+
* The one implementation, called by the store when a graph is saved and by the
|
|
1869
|
+
* runner when one is executed, so the node a canvas draws and the node that runs
|
|
1870
|
+
* cannot describe different reads. Pure, and it takes the body rather than
|
|
1871
|
+
* fetching one, for the reason `validateWorkflow` is pure: this file is imported
|
|
1872
|
+
* by the browser entry point.
|
|
1873
|
+
*
|
|
1874
|
+
* ## What it refuses
|
|
1875
|
+
*
|
|
1876
|
+
* A sink body whose `targetType` differs from the one already on the node. That
|
|
1877
|
+
* is not a tidiness check — it is the same shape as `WorkflowRunSteps.checkCall`
|
|
1878
|
+
* and it is load-bearing for the same reason. A graph's sinks are checked
|
|
1879
|
+
* against the author's write grants (`assertMayWriteTypes`) using the type on
|
|
1880
|
+
* the node, at save time. If a reusable body could repoint that afterwards, then
|
|
1881
|
+
* editing a shared sink would write into a type that nobody with access to this
|
|
1882
|
+
* graph was ever granted — and it would do it on a schedule, with the graph's
|
|
1883
|
+
* own diff showing nothing. So the disagreement fails, naming both types, and
|
|
1884
|
+
* the repair is that the referencing graph is re-saved and re-checked.
|
|
1885
|
+
*
|
|
1886
|
+
* A mismatched *kind* is refused for the plainer reason that there is nothing
|
|
1887
|
+
* sensible to do with it: a sink body on a source node is a reference somebody
|
|
1888
|
+
* repointed at the wrong object, and folding half of it in would produce a node
|
|
1889
|
+
* that is neither.
|
|
1890
|
+
*/
|
|
1891
|
+
export declare function applyReusableNode(node: WorkflowNode, body: ReusableNodeBody): WorkflowNode;
|
|
1892
|
+
/** Whether a stored value is a reusable body this build can execute. */
|
|
1893
|
+
export declare function isReusableNodeBody(value: unknown): value is ReusableNodeBody;
|
|
1894
|
+
/**
|
|
1895
|
+
* The body a node is currently carrying, ready to be saved under a name.
|
|
1896
|
+
*
|
|
1897
|
+
* The other direction of {@link applyReusableNode}, and the reason
|
|
1898
|
+
* save-as-reusable cannot silently deep-copy: this is what gets stored, the node
|
|
1899
|
+
* keeps a `useId` pointing at it, and nothing anywhere duplicates a graph.
|
|
1900
|
+
*
|
|
1901
|
+
* Answers `undefined` for a kind that cannot be reusable rather than throwing,
|
|
1902
|
+
* because the caller is a route answering a person who pressed a button on a
|
|
1903
|
+
* node — {@link nodeKindIsReusable} is what a screen asks before offering it,
|
|
1904
|
+
* and the route repeats the question rather than trusting the screen asked.
|
|
1905
|
+
*/
|
|
1906
|
+
export declare function reusableNodeBodyOf(node: WorkflowNode): ReusableNodeBody | undefined;
|
|
1907
|
+
/**
|
|
1908
|
+
* What a node's version discipline is, as a sentence a screen can render.
|
|
1909
|
+
*
|
|
1910
|
+
* Here rather than in the console for the reason `describeDurability`'s siblings
|
|
1911
|
+
* are: a screen that worked out its own wording would eventually describe
|
|
1912
|
+
* "follows the latest" as though it were a pin, which is the misunderstanding
|
|
1913
|
+
* this whole field exists to remove. One sentence, one place, and the console
|
|
1914
|
+
* renders it.
|
|
1915
|
+
*/
|
|
1916
|
+
export interface VersionPinCopy {
|
|
1917
|
+
pinned: boolean;
|
|
1918
|
+
label: string;
|
|
1919
|
+
detail: string;
|
|
1920
|
+
}
|
|
1921
|
+
export declare function describeVersionPin(version: number | undefined, subject: string): VersionPinCopy;
|
|
1922
|
+
/**
|
|
1923
|
+
* The same question about a whole graph: does it follow its latest save, or does
|
|
1924
|
+
* it run a version somebody chose?
|
|
1925
|
+
*
|
|
1926
|
+
* Shares {@link VersionPinCopy} and its two labels with
|
|
1927
|
+
* {@link describeVersionPin} deliberately. A console that said "pinned to v6"
|
|
1928
|
+
* about a transform node and invented different words for a graph would be two
|
|
1929
|
+
* vocabularies for one idea, and the reader would have to work out whether they
|
|
1930
|
+
* meant the same thing — which is the confusion the whole notion of a pin exists
|
|
1931
|
+
* to remove.
|
|
1932
|
+
*
|
|
1933
|
+
* The *detail* differs, and only where the facts do. Two of them:
|
|
1934
|
+
*
|
|
1935
|
+
* - A node's pin can outlive the revision it names, because `catalog_revision`
|
|
1936
|
+
* is capped. A graph's cannot: releases are never evicted, precisely because
|
|
1937
|
+
* the one a live pointer names is the graph production is running. So this
|
|
1938
|
+
* copy makes no eviction caveat, and must not acquire one.
|
|
1939
|
+
* - Following the latest is *cheap* for a node — everybody moves together, which
|
|
1940
|
+
* is often the point. For a graph it means editing is deploying, which is the
|
|
1941
|
+
* hazard this field was added to remove. So the unpinned sentence here is a
|
|
1942
|
+
* warning where the node's is a trade-off.
|
|
1943
|
+
*/
|
|
1944
|
+
export declare function describeLiveVersion(workflow: CatalogWorkflow): VersionPinCopy;
|
|
1589
1945
|
/**
|
|
1590
1946
|
* Which side of an {@link WorkflowIfNode} a wire leaves by.
|
|
1591
1947
|
*
|
|
@@ -1729,6 +2085,51 @@ export interface CatalogWorkflow {
|
|
|
1729
2085
|
version: number;
|
|
1730
2086
|
/** Fingerprint of the graph at this version. See {@link workflowGraphHash}. */
|
|
1731
2087
|
graphHash: string;
|
|
2088
|
+
/**
|
|
2089
|
+
* Which released version a run of this graph gets when nobody names one.
|
|
2090
|
+
*
|
|
2091
|
+
* **Absent means follow the latest**, which is what every graph in every
|
|
2092
|
+
* deployment does today and what every existing graph keeps doing until
|
|
2093
|
+
* somebody points this at something. Absence is a real answer rather than a
|
|
2094
|
+
* state waiting to be filled in — the same stance {@link WorkflowCallNode}
|
|
2095
|
+
* takes from the other side, where a call that named no version would run
|
|
2096
|
+
* whichever one is registered when the load happens.
|
|
2097
|
+
*
|
|
2098
|
+
* Present, it names a {@link CatalogWorkflowRelease} — and from that moment
|
|
2099
|
+
* **editing this graph stops being deploying it**. A save bumps
|
|
2100
|
+
* {@link version} as it always did; the next scheduled window still runs the
|
|
2101
|
+
* released version this names, because a cron tick that executed whatever the
|
|
2102
|
+
* canvas happened to hold is a deploy nobody performed. Moving it is
|
|
2103
|
+
* {@link CatalogWorkflowReleaseStore.setLiveWorkflowVersion}, which is a
|
|
2104
|
+
* deliberate act by a named principal, and moving it *backwards* is the whole
|
|
2105
|
+
* of rollback.
|
|
2106
|
+
*
|
|
2107
|
+
* ## Why this is a column here and not a row in an environments table
|
|
2108
|
+
*
|
|
2109
|
+
* Because the row is already per-environment. Environments in this catalog are
|
|
2110
|
+
* physically isolated — one database each, see `catalog.environment.ts` — so
|
|
2111
|
+
* this `catalog_workflow` row exists once per environment already, and a
|
|
2112
|
+
* second dimension keyed on the environment id would be a table whose every
|
|
2113
|
+
* query filtered on a constant. The environment is the connection, and it has
|
|
2114
|
+
* been since environments were added.
|
|
2115
|
+
*
|
|
2116
|
+
* That is also why the field is not called `productionVersion`. "Production"
|
|
2117
|
+
* in this catalog is the *name of an environment* — see `CatalogEnvironment`
|
|
2118
|
+
* in `catalog.environment.ts` — so a column called that, on a row which
|
|
2119
|
+
* already lives inside exactly one environment, would read as naming a
|
|
2120
|
+
* different one.
|
|
2121
|
+
*
|
|
2122
|
+
* ## Why it does not cross a promotion
|
|
2123
|
+
*
|
|
2124
|
+
* `planPromotion` is explicit that version numbers do not cross: a version
|
|
2125
|
+
* counts edits made in the environment it lives in, so dev's v7 and
|
|
2126
|
+
* production's v7 are unrelated numbers. A pointer *to* a version inherits
|
|
2127
|
+
* that argument whole — carrying this field would point the target's live
|
|
2128
|
+
* pointer at whatever its own seventh edit happened to be. So
|
|
2129
|
+
* `PromotableWorkflow` does not carry it, and a promoted graph arrives
|
|
2130
|
+
* following the latest, exactly as a newly created one does.
|
|
2131
|
+
*/
|
|
2132
|
+
liveVersion?: number;
|
|
1732
2133
|
/**
|
|
1733
2134
|
* The type the sink writes.
|
|
1734
2135
|
*
|
|
@@ -2088,7 +2489,7 @@ export interface CallableWorkflowBlock {
|
|
|
2088
2489
|
}
|
|
2089
2490
|
export declare function callableWorkflowBlock(ref: CallableWorkflowRef): CallableWorkflowBlock | undefined;
|
|
2090
2491
|
/** Every way a graph can be refused. Exported so a canvas can key off the code. */
|
|
2091
|
-
export declare const WORKFLOW_ISSUE_CODES: readonly ["empty", "invalid-node-id", "duplicate-node-id", "edge-endpoint-missing", "self-edge", "duplicate-edge", "cycle", "no-source", "source-has-input", "no-sink", "duplicate-sink-type", "sink-has-output", "unreachable", "dead-end", "transform-not-named", "call-not-named", "if-not-named", "if-threshold-invalid", "if-needs-one-input", "branch-not-labelled", "branch-on-plain-edge", "filter-predicate-invalid", "filter-narrows-unacknowledged", "filter-narrows-nothing"];
|
|
2492
|
+
export declare const WORKFLOW_ISSUE_CODES: readonly ["empty", "invalid-node-id", "duplicate-node-id", "edge-endpoint-missing", "self-edge", "duplicate-edge", "cycle", "no-source", "source-has-input", "no-sink", "duplicate-sink-type", "sink-has-output", "unreachable", "dead-end", "transform-not-named", "call-not-named", "if-not-named", "if-threshold-invalid", "if-needs-one-input", "branch-not-labelled", "branch-on-plain-edge", "filter-predicate-invalid", "filter-narrows-unacknowledged", "filter-narrows-nothing", "version-pin-invalid"];
|
|
2092
2493
|
export type WorkflowIssueCode = (typeof WORKFLOW_ISSUE_CODES)[number];
|
|
2093
2494
|
export interface WorkflowValidationIssue {
|
|
2094
2495
|
code: WorkflowIssueCode;
|
|
@@ -2385,6 +2786,172 @@ export interface CatalogWorkflowStore {
|
|
|
2385
2786
|
enabled?: boolean;
|
|
2386
2787
|
}, changedBy: string): Promise<CatalogWorkflow>;
|
|
2387
2788
|
}
|
|
2789
|
+
/**
|
|
2790
|
+
* One version of a graph, kept exactly as it was, because somebody said so.
|
|
2791
|
+
*
|
|
2792
|
+
* ## The archive `CatalogWorkflow` argues against, and why this is not it
|
|
2793
|
+
*
|
|
2794
|
+
* {@link CatalogWorkflow} states plainly that a graph is not revisioned, and the
|
|
2795
|
+
* decisive reason it gives is the counter: {@link CatalogWorkflow.version} is
|
|
2796
|
+
* bumped on **draft** edits by design, so archiving one body per version would
|
|
2797
|
+
* store every autosave of a canvas somebody is still dragging boxes around on —
|
|
2798
|
+
* and under a bounded archive that noise would evict the versions that actually
|
|
2799
|
+
* ran. **That argument is correct and nothing here weakens it.** It is an
|
|
2800
|
+
* argument against archiving *saves*, and this archives *releases*: a release is
|
|
2801
|
+
* minted only by `releaseWorkflow`, which is a route a person presses, and a
|
|
2802
|
+
* canvas autosave does not reach it. So the counter stays cheap to inflate, the
|
|
2803
|
+
* archive stays keyed on a deliberate act, and the two now compose because they
|
|
2804
|
+
* are counting different things.
|
|
2805
|
+
*
|
|
2806
|
+
* The second half of that docblock — that a graph is a structure and a line
|
|
2807
|
+
* differ over serialised JSON would report a dragged box as a change — is also
|
|
2808
|
+
* untouched. This is not a diff feature. It stores the graph so a version can be
|
|
2809
|
+
* *run*, and diffing graphs remains a graph problem deserving a screen that
|
|
2810
|
+
* draws one.
|
|
2811
|
+
*
|
|
2812
|
+
* ## Why not `catalog_revision`, which already archives versioned bodies
|
|
2813
|
+
*
|
|
2814
|
+
* Two reasons, and the second is the one that decides it.
|
|
2815
|
+
*
|
|
2816
|
+
* `CatalogRevision.body` is text a person typed — a transform's code, a saved
|
|
2817
|
+
* query's SQL — and every route over it is built to render text. A graph is
|
|
2818
|
+
* nodes and edges, and folding it into that column would put JSON nobody wrote
|
|
2819
|
+
* in front of a differ built for source.
|
|
2820
|
+
*
|
|
2821
|
+
* The one that decides it is {@link CATALOG_REVISION_LIMIT}. That cap is right
|
|
2822
|
+
* for code, for the reason its own docblock gives: revisions grow with how often
|
|
2823
|
+
* somebody edits, which nobody meters. A release does not grow that way — it
|
|
2824
|
+
* grows with how often somebody deliberately ships — and, crucially, a release
|
|
2825
|
+
* is the thing a live pointer names. An eviction rule over this table could
|
|
2826
|
+
* delete the graph that production is *currently running*, turning
|
|
2827
|
+
* {@link CatalogWorkflow.liveVersion} into a pin nothing can honour and stopping
|
|
2828
|
+
* a working pipeline on a retention policy. **So releases are never evicted.**
|
|
2829
|
+
* That makes this an unbounded table, which this codebase is careful about — and
|
|
2830
|
+
* it earns it on the same test `catalog_audit_event` and `catalog_connector_run`
|
|
2831
|
+
* pass: one row per thing a person deliberately did, at a rate an operator can
|
|
2832
|
+
* read off their own change process.
|
|
2833
|
+
*
|
|
2834
|
+
* ## Immutable, and there is no route that removes one
|
|
2835
|
+
*
|
|
2836
|
+
* Nothing edits a release and nothing deletes one. That is the strongest form of
|
|
2837
|
+
* "refuse to delete the version that is live": there is no operation to refuse.
|
|
2838
|
+
* The one exception is {@link CatalogWorkflowStore.deleteWorkflow}, which takes
|
|
2839
|
+
* the graph, its connector and its whole run history — releases go with it,
|
|
2840
|
+
* because nothing survives that could still name one. (The opposite call is made
|
|
2841
|
+
* for a transform, whose revisions outlive it precisely *because* runs that ran
|
|
2842
|
+
* them survive.)
|
|
2843
|
+
*/
|
|
2844
|
+
export interface CatalogWorkflowRelease {
|
|
2845
|
+
/**
|
|
2846
|
+
* `{workflowId}:{version}`, derived rather than random.
|
|
2847
|
+
*
|
|
2848
|
+
* The same construction `revisionKey` uses in the MikroORM store and for the
|
|
2849
|
+
* same property: releasing the same version twice cannot append a second copy,
|
|
2850
|
+
* because the second write would collide with the first rather than land
|
|
2851
|
+
* beside it.
|
|
2852
|
+
*/
|
|
2853
|
+
id: string;
|
|
2854
|
+
workflowId: string;
|
|
2855
|
+
/**
|
|
2856
|
+
* The {@link CatalogWorkflow.version} this release IS.
|
|
2857
|
+
*
|
|
2858
|
+
* The same number, not a parallel sequence. A release sequence of its own was
|
|
2859
|
+
* the alternative and it is worse in the one place it matters: a run records
|
|
2860
|
+
* `workflowVersion`, so a second numbering would mean a run naming "v3" and an
|
|
2861
|
+
* operator reading "release 3" could be two different graphs, which is exactly
|
|
2862
|
+
* the ambiguity the edit counter was made cheap to avoid.
|
|
2863
|
+
*/
|
|
2864
|
+
version: number;
|
|
2865
|
+
/** Fingerprint of the graph as released. See {@link workflowGraphHash}. */
|
|
2866
|
+
graphHash: string;
|
|
2867
|
+
nodes: WorkflowNode[];
|
|
2868
|
+
edges: WorkflowEdge[];
|
|
2869
|
+
/** The type the sink committed at this version. */
|
|
2870
|
+
targetType: string;
|
|
2871
|
+
/** Whatever the releaser wanted to say about it. */
|
|
2872
|
+
notes?: string;
|
|
2873
|
+
releasedBy: string;
|
|
2874
|
+
releasedAt: string;
|
|
2875
|
+
}
|
|
2876
|
+
/**
|
|
2877
|
+
* Minting releases, and pointing at one.
|
|
2878
|
+
*
|
|
2879
|
+
* Its own interface mixed in optionally, for the reason {@link
|
|
2880
|
+
* CatalogWorkflowStore} is: a store written against the previous shape must keep
|
|
2881
|
+
* compiling, and "this deployment cannot hold releases" has to be a sentence a
|
|
2882
|
+
* UI can say rather than a method missing at run time.
|
|
2883
|
+
*
|
|
2884
|
+
* The split from `CatalogWorkflowStore` is not only compatibility. These four
|
|
2885
|
+
* are the only members in this file that can change *what a cron executes*
|
|
2886
|
+
* without touching a graph, and keeping them behind their own predicate means a
|
|
2887
|
+
* store can hold graphs without acquiring that power by accident.
|
|
2888
|
+
*/
|
|
2889
|
+
export interface CatalogWorkflowReleaseStore {
|
|
2890
|
+
/**
|
|
2891
|
+
* Freeze the graph as it currently stands, under its current version.
|
|
2892
|
+
*
|
|
2893
|
+
* **The only thing that mints one.** Not `saveWorkflow`, which is the autosave
|
|
2894
|
+
* this whole feature exists to stop being a deploy; not `publishWorkflow`,
|
|
2895
|
+
* which is idempotent and is called by a promotion apply — a release minted as
|
|
2896
|
+
* a side effect of promoting configuration into an environment would be a
|
|
2897
|
+
* release nobody in that environment chose.
|
|
2898
|
+
*
|
|
2899
|
+
* Refuses a draft, for the reason `WORKFLOW_STATUSES` already gives about what
|
|
2900
|
+
* a connector may point at and what a promotion may carry: what gets shipped
|
|
2901
|
+
* should be something a person declared finished.
|
|
2902
|
+
*
|
|
2903
|
+
* **Idempotent per version.** Releasing a graph whose current version is
|
|
2904
|
+
* already released answers with the existing release rather than minting a
|
|
2905
|
+
* second or overwriting its notes — the graph has not changed, so a second
|
|
2906
|
+
* release would be a record of an event that did not happen, and re-attributing
|
|
2907
|
+
* the first one would erase who actually shipped it.
|
|
2908
|
+
*/
|
|
2909
|
+
releaseWorkflow(id: string, releasedBy: string, options?: {
|
|
2910
|
+
notes?: string;
|
|
2911
|
+
}): Promise<CatalogWorkflowRelease>;
|
|
2912
|
+
/** Newest first. */
|
|
2913
|
+
listWorkflowReleases(id: string): Promise<CatalogWorkflowRelease[]>;
|
|
2914
|
+
/**
|
|
2915
|
+
* The graph as it was at a released version, or `undefined`.
|
|
2916
|
+
*
|
|
2917
|
+
* `undefined` for a version that was never released as much as for a workflow
|
|
2918
|
+
* that does not exist, and callers must treat the two the same way: **fail,
|
|
2919
|
+
* never fall back to the latest.** Running the current graph because the named
|
|
2920
|
+
* one could not be produced is precisely the substitution a pin is written
|
|
2921
|
+
* down to prevent.
|
|
2922
|
+
*
|
|
2923
|
+
* Answers with a whole {@link CatalogWorkflow} rather than the bare release,
|
|
2924
|
+
* because that is what a run needs: the released graph, carried on the row's
|
|
2925
|
+
* present identity. The graph fields — nodes, edges, `targetType`,
|
|
2926
|
+
* `graphHash`, `version` — come from the release. The operational ones —
|
|
2927
|
+
* `name`, `status`, `schedule`, `enabled`, `liveVersion` — come from the row
|
|
2928
|
+
* as it is now, because they are not part of what was released. A cron somebody
|
|
2929
|
+
* changed this morning applies to the version that is live, not to whatever
|
|
2930
|
+
* cron was on the row the day it was released.
|
|
2931
|
+
*/
|
|
2932
|
+
getWorkflowAt(id: string, version: number): Promise<CatalogWorkflow | undefined>;
|
|
2933
|
+
/**
|
|
2934
|
+
* Point {@link CatalogWorkflow.liveVersion} at a released version — or clear it.
|
|
2935
|
+
*
|
|
2936
|
+
* One method for going live, for rolling back and for going back to following
|
|
2937
|
+
* the latest, because they are one act with a different argument. Rollback in
|
|
2938
|
+
* particular is not a separate mechanism to build and test: it is this call
|
|
2939
|
+
* with a smaller number, and it works because the older graph is still stored.
|
|
2940
|
+
*
|
|
2941
|
+
* Refuses a version with no release behind it, naming what there is. A pointer
|
|
2942
|
+
* accepted at a number nothing can produce would be a pipeline that stops at
|
|
2943
|
+
* its next window, discovered by a load failing rather than by the person who
|
|
2944
|
+
* typed it.
|
|
2945
|
+
*
|
|
2946
|
+
* `undefined` clears the pointer and takes the graph back to following the
|
|
2947
|
+
* latest. Allowed rather than refused — it is the state every graph in every
|
|
2948
|
+
* deployment is in, so refusing would strand anything that ever went live —
|
|
2949
|
+
* and stated as a decision because it hands back exactly the hazard the
|
|
2950
|
+
* pointer removes: from that moment, saving the graph changes what the next
|
|
2951
|
+
* window runs.
|
|
2952
|
+
*/
|
|
2953
|
+
setLiveWorkflowVersion(id: string, version: number | undefined, changedBy: string): Promise<CatalogWorkflow>;
|
|
2954
|
+
}
|
|
2388
2955
|
/**
|
|
2389
2956
|
* Where the rows between two nodes actually sit.
|
|
2390
2957
|
*
|
|
@@ -2434,6 +3001,36 @@ export interface CatalogStageStore {
|
|
|
2434
3001
|
* does, because a flag is a claim and a method is the thing itself.
|
|
2435
3002
|
*/
|
|
2436
3003
|
export declare function supportsWorkflows(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogWorkflowStore;
|
|
3004
|
+
/**
|
|
3005
|
+
* Whether this store can mint a release and be pointed at one.
|
|
3006
|
+
*
|
|
3007
|
+
* All four asked for by name, for the reason `supportsWorkflows` asks for
|
|
3008
|
+
* `publishWorkflow` and `saveWorkflowSchedule` by name rather than assuming they
|
|
3009
|
+
* arrive together: a store with the mint and not the pointer would narrow
|
|
3010
|
+
* cleanly here, let somebody release a graph, and then fail on the call that was
|
|
3011
|
+
* supposed to make it run.
|
|
3012
|
+
*
|
|
3013
|
+
* {@link getWorkflowAt} is the one whose absence is least visible and most
|
|
3014
|
+
* expensive. A store that could hold a `liveVersion` and not resolve it would
|
|
3015
|
+
* point a scheduled load at a version it cannot produce — and the only place
|
|
3016
|
+
* that shows up is a cron window that stops firing.
|
|
3017
|
+
*/
|
|
3018
|
+
export declare function supportsWorkflowReleases(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogWorkflowReleaseStore;
|
|
3019
|
+
/**
|
|
3020
|
+
* Which version of this graph a run gets when the caller names none.
|
|
3021
|
+
*
|
|
3022
|
+
* The one implementation of "follow the latest unless something is live", shared
|
|
3023
|
+
* by the scheduler and by the manual run route so the two cannot disagree about
|
|
3024
|
+
* what a cron does and what the button next to it does. That divergence is not
|
|
3025
|
+
* hypothetical: the schedule used to live on the connector and on the workflow
|
|
3026
|
+
* at once, and the whole of `ConnectorScheduler`'s docblock is about what it
|
|
3027
|
+
* cost to have two copies of one answer.
|
|
3028
|
+
*
|
|
3029
|
+
* Not a fallback in the defensive sense. `liveVersion` absent is a stated
|
|
3030
|
+
* position — this graph follows its head — and this function is where that
|
|
3031
|
+
* position is turned into a number, not where a missing value is patched over.
|
|
3032
|
+
*/
|
|
3033
|
+
export declare function liveWorkflowVersion(workflow: CatalogWorkflow): number;
|
|
2437
3034
|
/**
|
|
2438
3035
|
* Whether this store keeps a transform's history.
|
|
2439
3036
|
*
|
|
@@ -2441,6 +3038,34 @@ export declare function supportsWorkflows(store: CatalogPipelineStore): store is
|
|
|
2441
3038
|
* flag is a claim and a method is the thing itself.
|
|
2442
3039
|
*/
|
|
2443
3040
|
export declare function supportsTransformRevisions(store: CatalogPipelineStore): store is CatalogPipelineStore & Required<Pick<CatalogPipelineStore, 'listTransformRevisions'>>;
|
|
3041
|
+
/**
|
|
3042
|
+
* Whether this store can produce one particular version of a transform's code.
|
|
3043
|
+
*
|
|
3044
|
+
* Separate from {@link supportsTransformRevisions}, and not implied by it: one
|
|
3045
|
+
* answers "can a screen show the history", the other "can a run honour a pin".
|
|
3046
|
+
* A store could reasonably have the first and not the second, and folding them
|
|
3047
|
+
* together would let a graph be saved with a pin this deployment cannot resolve
|
|
3048
|
+
* — discovered mid-load rather than at the moment the pin was set.
|
|
3049
|
+
*/
|
|
3050
|
+
export declare function supportsTransformPins(store: CatalogPipelineStore): store is CatalogPipelineStore & Required<Pick<CatalogPipelineStore, 'getTransformAt'>>;
|
|
3051
|
+
/**
|
|
3052
|
+
* The four reads and two writes a reusable node needs, as one derived type.
|
|
3053
|
+
*
|
|
3054
|
+
* `Required<Pick<...>>` rather than a second interface, which is the lesson
|
|
3055
|
+
* {@link CatalogLoadExpectationStore} records: these are optional members OF the
|
|
3056
|
+
* pipeline store, so writing them out again here would be a copy that can drift.
|
|
3057
|
+
*/
|
|
3058
|
+
export type CatalogReusableNodeStore = Required<Pick<CatalogPipelineStore, 'listReusableNodes' | 'getReusableNode' | 'getReusableNodeAt' | 'saveReusableNode' | 'deleteReusableNode' | 'reusableNodeUses'>>;
|
|
3059
|
+
/**
|
|
3060
|
+
* Whether this store can hold reusable nodes.
|
|
3061
|
+
*
|
|
3062
|
+
* All six, and never a subset. A store with `getReusableNode` but no
|
|
3063
|
+
* `reusableNodeUses` could serve a picker and could not answer the question the
|
|
3064
|
+
* feature exists for — and the shape of that failure is a console offering to
|
|
3065
|
+
* share a node while being unable to say who already depends on it, which is
|
|
3066
|
+
* worse than not offering at all.
|
|
3067
|
+
*/
|
|
3068
|
+
export declare function supportsReusableNodes(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogReusableNodeStore;
|
|
2444
3069
|
export declare function supportsWorkflowStages(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogStageStore;
|
|
2445
3070
|
/**
|
|
2446
3071
|
* A store that really does hold operator-set expectations, all four members
|
|
@@ -2473,7 +3098,7 @@ export type CatalogLoadExpectationStore = Required<Pick<CatalogPipelineStore, 'l
|
|
|
2473
3098
|
* an editor whose save has nowhere to go.
|
|
2474
3099
|
*/
|
|
2475
3100
|
export declare function supportsLoadExpectations(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogLoadExpectationStore;
|
|
2476
|
-
export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Partial<CatalogStageStore> {
|
|
3101
|
+
export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Partial<CatalogWorkflowReleaseStore>, Partial<CatalogStageStore> {
|
|
2477
3102
|
listConnectors(): Promise<CatalogConnector[]>;
|
|
2478
3103
|
getConnector(id: string): Promise<CatalogConnector | undefined>;
|
|
2479
3104
|
saveConnector(input: Omit<CatalogConnector, 'id' | 'createdAt' | 'updatedAt' | 'createdBy'> & {
|
|
@@ -2526,6 +3151,88 @@ export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Par
|
|
|
2526
3151
|
* that bound costs.
|
|
2527
3152
|
*/
|
|
2528
3153
|
listTransformRevisions?(id: string): Promise<CatalogRevision[]>;
|
|
3154
|
+
/**
|
|
3155
|
+
* The code at one particular version, which is what a pin resolves through.
|
|
3156
|
+
*
|
|
3157
|
+
* Separate from {@link listTransformRevisions} rather than left to a caller
|
|
3158
|
+
* filtering that list, and the difference is the whole point: this is on the
|
|
3159
|
+
* hot path of every pinned transform node of every run, and reading up to
|
|
3160
|
+
* {@link CATALOG_REVISION_LIMIT} whole code bodies to keep one of them is a
|
|
3161
|
+
* cost paid per node per run. It is also a different answer — the list falls
|
|
3162
|
+
* back to a synthesised head for a subject that predates the revision table,
|
|
3163
|
+
* and this must not, because "the only version we can produce is the current
|
|
3164
|
+
* one" is exactly the case a pin needs to be told about rather than handed.
|
|
3165
|
+
*
|
|
3166
|
+
* `undefined` means the version cannot be produced: it predates the archive,
|
|
3167
|
+
* or it has fallen off the far end of the per-subject cap. The runner turns
|
|
3168
|
+
* that into a failed node rather than a fall-back to the latest, which is the
|
|
3169
|
+
* same stand `WorkflowRunSteps.checkCall` takes — a pin nobody could check is
|
|
3170
|
+
* not a pin.
|
|
3171
|
+
*
|
|
3172
|
+
* **Optional**, mixed in exactly as its neighbour above is and for the same
|
|
3173
|
+
* reason. {@link supportsTransformPins} is how a caller asks; a store without
|
|
3174
|
+
* it can still run graphs, and a graph with a pinned node is refused there
|
|
3175
|
+
* with a sentence rather than by a method that is missing at run time.
|
|
3176
|
+
*/
|
|
3177
|
+
getTransformAt?(id: string, version: number): Promise<CatalogTransform | undefined>;
|
|
3178
|
+
/**
|
|
3179
|
+
* Node bodies saved under a name and used from several graphs.
|
|
3180
|
+
*
|
|
3181
|
+
* **Optional**, mixed in for the reason every optional member here is: a store
|
|
3182
|
+
* written against the previous shape of this interface still satisfies it, and
|
|
3183
|
+
* a purely additive feature must not turn that into a compile error — or,
|
|
3184
|
+
* worse, into a run-time discovery, since the `supports*` probes narrow
|
|
3185
|
+
* structurally. {@link supportsReusableNodes} is how a caller asks.
|
|
3186
|
+
*
|
|
3187
|
+
* A deployment whose store implements none of these behaves exactly as it does
|
|
3188
|
+
* today: every node is configured in place, which is what they all are.
|
|
3189
|
+
*/
|
|
3190
|
+
listReusableNodes?(): Promise<CatalogReusableNode[]>;
|
|
3191
|
+
getReusableNode?(id: string): Promise<CatalogReusableNode | undefined>;
|
|
3192
|
+
/**
|
|
3193
|
+
* The body at one particular version, for a reference that pinned one.
|
|
3194
|
+
*
|
|
3195
|
+
* The sibling of {@link getTransformAt}, with the same contract and the same
|
|
3196
|
+
* `undefined`: a version the archive can no longer produce is reported as
|
|
3197
|
+
* absent and never substituted with the latest.
|
|
3198
|
+
*/
|
|
3199
|
+
getReusableNodeAt?(id: string, version: number): Promise<CatalogReusableNode | undefined>;
|
|
3200
|
+
/**
|
|
3201
|
+
* Bumps {@link CatalogReusableNode.version} when the **body** changed, and
|
|
3202
|
+
* archives it, exactly as {@link saveTransform} does for code.
|
|
3203
|
+
*
|
|
3204
|
+
* Editing a reusable node that other graphs pin therefore creates a new
|
|
3205
|
+
* version rather than refusing. Refusing was the other candidate and it is the
|
|
3206
|
+
* wrong one: this is the only editor there is, so a refusal would strand
|
|
3207
|
+
* whoever owns the node the moment anybody else pinned it, and would make
|
|
3208
|
+
* pinning a way to take something hostage. Creating a version costs the pinned
|
|
3209
|
+
* graphs nothing — they resolve through the archive and keep running the body
|
|
3210
|
+
* they named — and what the editor gets instead of a refusal is the count of
|
|
3211
|
+
* who is downstream, at the moment they are about to press save.
|
|
3212
|
+
*/
|
|
3213
|
+
saveReusableNode?(input: Pick<CatalogReusableNode, 'name' | 'body'> & {
|
|
3214
|
+
id?: string;
|
|
3215
|
+
description?: string;
|
|
3216
|
+
}, createdBy: string): Promise<CatalogReusableNode>;
|
|
3217
|
+
/**
|
|
3218
|
+
* Refuses while any graph still references it.
|
|
3219
|
+
*
|
|
3220
|
+
* The same refusal {@link deleteConnection} makes and for the same reason:
|
|
3221
|
+
* deleting one out from under its graphs turns every one of them into a load
|
|
3222
|
+
* that fails at run time, discovered on a schedule rather than at the moment
|
|
3223
|
+
* somebody decided.
|
|
3224
|
+
*/
|
|
3225
|
+
deleteReusableNode?(id: string): Promise<boolean>;
|
|
3226
|
+
/**
|
|
3227
|
+
* Which graphs use a reusable node, and at which node within each.
|
|
3228
|
+
*
|
|
3229
|
+
* On the store rather than derived in a controller from `listWorkflows`,
|
|
3230
|
+
* unlike `connections/:id/workflows` which does exactly that. The difference
|
|
3231
|
+
* is that this number is rendered *beside every entry of a picker*, so
|
|
3232
|
+
* deriving it would mean parsing every graph in the deployment once per
|
|
3233
|
+
* reusable node offered. A store can answer it from the rows it holds.
|
|
3234
|
+
*/
|
|
3235
|
+
reusableNodeUses?(id: string): Promise<CatalogReusableNodeUse[]>;
|
|
2529
3236
|
/**
|
|
2530
3237
|
* Per-type load expectations as an operator set them.
|
|
2531
3238
|
*
|