@dudousxd/nestjs-catalog 0.11.0 → 0.12.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.
@@ -82,10 +82,12 @@ function createCatalogController(path, guards, decorators = []) {
82
82
  registry;
83
83
  service;
84
84
  traces;
85
- constructor(registry, service, traces) {
85
+ workspace;
86
+ constructor(registry, service, traces, workspace) {
86
87
  this.registry = registry;
87
88
  this.service = service;
88
89
  this.traces = traces;
90
+ this.workspace = workspace;
89
91
  }
90
92
  requireTraces() {
91
93
  if (!this.traces) {
@@ -283,6 +285,28 @@ function createCatalogController(path, guards, decorators = []) {
283
285
  response.setHeader('content-disposition', `attachment; filename="${filename}"`);
284
286
  return (0, catalog_query_cache_1.toCsv)(result);
285
287
  }
288
+ /**
289
+ * Every SQL this query has ever been, newest first.
290
+ *
291
+ * `catalog:read`, the same scope `GET saved-queries/:id` asks for, because
292
+ * it hands back the same field one version older. Gating history harder than
293
+ * the current text would be gating the diff rather than the data — the
294
+ * caller can already read what the statement says today, and the authoring
295
+ * scope that `POST`/`PATCH` require is about *choosing what SQL runs*, which
296
+ * reading an old body is not.
297
+ *
298
+ * An empty list is a real answer: a query nobody has edited since revisions
299
+ * shipped may have nothing recorded. A store that keeps none at all says so
300
+ * rather than answering `[]`, because "we do not keep these" and "nothing
301
+ * has changed" are different facts and a screen would draw them the same.
302
+ */
303
+ savedQueryRevisions(id) {
304
+ const workspace = this.workspace;
305
+ if (!workspace || !(0, catalog_workspace_1.supportsSavedQueryRevisions)(workspace)) {
306
+ throw new common_1.BadRequestException("This catalog's workspace store keeps no revisions, so a saved query's SQL can be read but not compared with what it used to be.");
307
+ }
308
+ return workspace.listSavedQueryRevisions(id);
309
+ }
286
310
  dashboards() {
287
311
  return this.service.listDashboards();
288
312
  }
@@ -555,6 +579,14 @@ function createCatalogController(path, guards, decorators = []) {
555
579
  __metadata("design:paramtypes", [String, Object]),
556
580
  __metadata("design:returntype", Promise)
557
581
  ], CatalogController.prototype, "exportSavedQuery", null);
582
+ __decorate([
583
+ (0, common_1.Get)('saved-queries/:id/revisions'),
584
+ (0, catalog_route_auth_1.RequireScopes)('catalog:read'),
585
+ __param(0, (0, common_1.Param)('id')),
586
+ __metadata("design:type", Function),
587
+ __metadata("design:paramtypes", [String]),
588
+ __metadata("design:returntype", void 0)
589
+ ], CatalogController.prototype, "savedQueryRevisions", null);
558
590
  __decorate([
559
591
  (0, common_1.Get)('dashboards'),
560
592
  (0, catalog_route_auth_1.RequireScopes)('catalog:read'),
@@ -667,8 +699,10 @@ function createCatalogController(path, guards, decorators = []) {
667
699
  (0, common_1.Controller)(path),
668
700
  __param(2, (0, common_1.Optional)()),
669
701
  __param(2, (0, common_1.Inject)(catalog_workspace_1.CATALOG_TRACE_STORE)),
702
+ __param(3, (0, common_1.Optional)()),
703
+ __param(3, (0, common_1.Inject)(catalog_workspace_1.CATALOG_WORKSPACE_STORE)),
670
704
  __metadata("design:paramtypes", [catalog_registry_base_1.CatalogRegistry,
671
- catalog_service_1.CatalogService, Object])
705
+ catalog_service_1.CatalogService, Object, Object])
672
706
  ], CatalogController);
673
707
  if (guards.length > 0) {
674
708
  (0, common_1.UseGuards)(...guards)(CatalogController);
@@ -7,6 +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 { CatalogRevision } from './catalog.workspace';
10
11
  /**
11
12
  * Where a connector pulls from.
12
13
  *
@@ -136,6 +137,14 @@ export declare function isTransformLanguage(value: unknown): value is TransformL
136
137
  * Versioned, because a load that produced surprising numbers is investigated
137
138
  * afterwards, and "which code ran" is the first question. Bumping the version
138
139
  * on every change costs a row and answers it.
140
+ *
141
+ * The version used to be the *whole* answer, and it was half of one: it named
142
+ * code that no longer existed anywhere, because one row per transform is
143
+ * overwritten in place. Each version's code is now recorded as a
144
+ * {@link CatalogRevision}, read through
145
+ * {@link CatalogPipelineStore.listTransformRevisions}, so the number on a run
146
+ * and the text it names are both retrievable. `version` still counts saves that
147
+ * changed the code and nothing else — see `saveTransform`.
139
148
  */
140
149
  export interface CatalogTransform {
141
150
  id: string;
@@ -223,17 +232,26 @@ export interface ConnectorRun {
223
232
  error?: string;
224
233
  startedAt: string;
225
234
  finishedAt?: string;
226
- /** Which transform version ran, so a surprising load can be traced to code. */
235
+ /**
236
+ * Which transform version ran, so a surprising load can be traced to code.
237
+ *
238
+ * Traced to the code itself, now, and not only to a number: the version this
239
+ * names is the version of a {@link CatalogRevision}, so `transforms/:id/revisions`
240
+ * answers with the body that produced these rows. Until that route existed
241
+ * this field could only establish *that* the transform had been edited since.
242
+ */
227
243
  transformVersion?: number;
228
244
  /** Which workflow ran, when the connector delegated to one. */
229
245
  workflowId?: string;
230
246
  /**
231
247
  * Which *version* of it ran.
232
248
  *
233
- * The same question `transformVersion` answers, asked of the graph. A
234
- * workflow keeps only its latest shape exactly as a transform keeps only
235
- * its latest code so this number is what connects a run to the graph that
236
- * produced it, and the only way to know a graph has changed since.
249
+ * The same question `transformVersion` answers, asked of the graph — and no
250
+ * longer answered as well, which is worth knowing before relying on it. A
251
+ * workflow keeps only its latest shape (see {@link CatalogWorkflow} for why it
252
+ * is excluded from revisions while a transform is not), so this number
253
+ * connects a run to the graph that produced it and is the only way to know a
254
+ * graph has changed since. It cannot produce the graph.
237
255
  */
238
256
  workflowVersion?: number;
239
257
  /**
@@ -435,12 +453,34 @@ export interface WorkflowGraph {
435
453
  * the code; for a workflow it means the code *and* the wiring, so both the
436
454
  * graph version and the per-node transform versions are recorded on the run.
437
455
  *
438
- * Like a transform, only the latest shape is kept. Storing every past graph was
439
- * the alternative and was rejected for consistency: transforms already answer
440
- * "which code ran" with a number and no history, and a model where the graph is
441
- * fully recoverable but the code inside it is not would give false confidence in
442
- * an audit. The limitation is real and worth stating plainly — an edited graph
443
- * cannot be reconstructed from an old run, only identified as different.
456
+ * **Only the latest shape is kept, and unlike a transform it is not
457
+ * revisioned.** That asymmetry is a decision rather than an oversight, and this
458
+ * is where somebody looking for the missing feature will look, so it is argued
459
+ * here.
460
+ *
461
+ * A transform's code and a saved query's SQL are text a person typed, and
462
+ * {@link CatalogRevision} archives text: two bodies, a line differ, done. A
463
+ * graph is a structure. Its "body" would be JSON nobody wrote, and a text diff
464
+ * over it is dominated by key order and canvas positions — it would report a
465
+ * dragged box as a change to what the load does, which is the opposite of what
466
+ * {@link workflowGraphHash} is careful to exclude. Diffing graphs is a graph
467
+ * problem and deserves a screen that draws one, not a line differ pointed at
468
+ * serialised nodes.
469
+ *
470
+ * The decisive reason is the counter. {@link version} is bumped on **draft**
471
+ * edits deliberately — see the note on it — so that a run's `workflowVersion`
472
+ * can never mean two different graphs. Archiving one body per version would
473
+ * therefore store every autosave of a canvas somebody is still dragging boxes
474
+ * around on, and under the per-subject cap that {@link CATALOG_REVISION_LIMIT}
475
+ * imposes, that noise would evict the versions that actually ran. A counter
476
+ * designed to be cheap to inflate and an archive designed to be bounded do not
477
+ * compose; making them compose means keying the archive on behaviour rather than
478
+ * on saves, which is what `graphHash` already is, and that is a different
479
+ * feature from this one.
480
+ *
481
+ * So the limitation stays, stated plainly: an edited graph cannot be
482
+ * reconstructed from an old run, only identified as different. A diff screen
483
+ * answers for the code and the SQL and not for the wiring.
444
484
  */
445
485
  /**
446
486
  * Whether this graph is still being drawn, or is something somebody declared
@@ -804,6 +844,13 @@ export interface CatalogStageStore {
804
844
  * does, because a flag is a claim and a method is the thing itself.
805
845
  */
806
846
  export declare function supportsWorkflows(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogWorkflowStore;
847
+ /**
848
+ * Whether this store keeps a transform's history.
849
+ *
850
+ * The method rather than a flag, exactly as {@link supportsWorkflows} argues: a
851
+ * flag is a claim and a method is the thing itself.
852
+ */
853
+ export declare function supportsTransformRevisions(store: CatalogPipelineStore): store is CatalogPipelineStore & Required<Pick<CatalogPipelineStore, 'listTransformRevisions'>>;
807
854
  export declare function supportsWorkflowStages(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogStageStore;
808
855
  export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Partial<CatalogStageStore> {
809
856
  listConnectors(): Promise<CatalogConnector[]>;
@@ -842,6 +889,22 @@ export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Par
842
889
  description?: string;
843
890
  }, createdBy: string): Promise<CatalogTransform>;
844
891
  deleteTransform(id: string): Promise<boolean>;
892
+ /**
893
+ * Every version of this transform's code, newest first.
894
+ *
895
+ * **Optional**, mixed in for the same reason {@link CatalogWorkflowStore} is:
896
+ * a store written against the previous shape of this interface still
897
+ * implements it, and turning that into a compile error would be a breaking
898
+ * change for an additive feature. {@link supportsTransformRevisions} is how a
899
+ * caller asks, so a deployment whose store keeps no history gets a sentence
900
+ * rather than a method that is missing at run time.
901
+ *
902
+ * The list is what makes `transformVersion` on a run mean something: the
903
+ * revision whose {@link CatalogRevision.version} equals it holds the code that
904
+ * produced those rows. Bounded — see {@link CATALOG_REVISION_LIMIT} for what
905
+ * that bound costs.
906
+ */
907
+ listTransformRevisions?(id: string): Promise<CatalogRevision[]>;
845
908
  startRun(input: {
846
909
  connectorId: string;
847
910
  snapshotId: string;
@@ -21,6 +21,7 @@ exports.workflowGraphHash = workflowGraphHash;
21
21
  exports.isWorkflowNode = isWorkflowNode;
22
22
  exports.isWorkflowEdge = isWorkflowEdge;
23
23
  exports.supportsWorkflows = supportsWorkflows;
24
+ exports.supportsTransformRevisions = supportsTransformRevisions;
24
25
  exports.supportsWorkflowStages = supportsWorkflowStages;
25
26
  exports.isPipelineStore = isPipelineStore;
26
27
  /**
@@ -146,12 +147,34 @@ exports.WORKFLOW_NODE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
146
147
  * the code; for a workflow it means the code *and* the wiring, so both the
147
148
  * graph version and the per-node transform versions are recorded on the run.
148
149
  *
149
- * Like a transform, only the latest shape is kept. Storing every past graph was
150
- * the alternative and was rejected for consistency: transforms already answer
151
- * "which code ran" with a number and no history, and a model where the graph is
152
- * fully recoverable but the code inside it is not would give false confidence in
153
- * an audit. The limitation is real and worth stating plainly — an edited graph
154
- * cannot be reconstructed from an old run, only identified as different.
150
+ * **Only the latest shape is kept, and unlike a transform it is not
151
+ * revisioned.** That asymmetry is a decision rather than an oversight, and this
152
+ * is where somebody looking for the missing feature will look, so it is argued
153
+ * here.
154
+ *
155
+ * A transform's code and a saved query's SQL are text a person typed, and
156
+ * {@link CatalogRevision} archives text: two bodies, a line differ, done. A
157
+ * graph is a structure. Its "body" would be JSON nobody wrote, and a text diff
158
+ * over it is dominated by key order and canvas positions — it would report a
159
+ * dragged box as a change to what the load does, which is the opposite of what
160
+ * {@link workflowGraphHash} is careful to exclude. Diffing graphs is a graph
161
+ * problem and deserves a screen that draws one, not a line differ pointed at
162
+ * serialised nodes.
163
+ *
164
+ * The decisive reason is the counter. {@link version} is bumped on **draft**
165
+ * edits deliberately — see the note on it — so that a run's `workflowVersion`
166
+ * can never mean two different graphs. Archiving one body per version would
167
+ * therefore store every autosave of a canvas somebody is still dragging boxes
168
+ * around on, and under the per-subject cap that {@link CATALOG_REVISION_LIMIT}
169
+ * imposes, that noise would evict the versions that actually ran. A counter
170
+ * designed to be cheap to inflate and an archive designed to be bounded do not
171
+ * compose; making them compose means keying the archive on behaviour rather than
172
+ * on saves, which is what `graphHash` already is, and that is a different
173
+ * feature from this one.
174
+ *
175
+ * So the limitation stays, stated plainly: an edited graph cannot be
176
+ * reconstructed from an old run, only identified as different. A diff screen
177
+ * answers for the code and the SQL and not for the wiring.
155
178
  */
156
179
  /**
157
180
  * Whether this graph is still being drawn, or is something somebody declared
@@ -694,6 +717,15 @@ function supportsWorkflows(store) {
694
717
  // transforms into the target.
695
718
  typeof store.publishWorkflow === 'function');
696
719
  }
720
+ /**
721
+ * Whether this store keeps a transform's history.
722
+ *
723
+ * The method rather than a flag, exactly as {@link supportsWorkflows} argues: a
724
+ * flag is a claim and a method is the thing itself.
725
+ */
726
+ function supportsTransformRevisions(store) {
727
+ return typeof store.listTransformRevisions === 'function';
728
+ }
697
729
  function supportsWorkflowStages(store) {
698
730
  return typeof store.writeStage === 'function' && typeof store.readStage === 'function';
699
731
  }
@@ -11,6 +11,16 @@ export interface SavedQuery {
11
11
  id: string;
12
12
  name: string;
13
13
  description?: string;
14
+ /**
15
+ * The statement, as it is now.
16
+ *
17
+ * Overwritten in place by {@link CatalogWorkspaceStore.updateSavedQuery}, and
18
+ * for a long time that was the end of it — a report that started answering
19
+ * differently left nothing to compare against, not even a version number. What
20
+ * it used to say is kept as {@link CatalogRevision}s now, read through
21
+ * {@link CatalogWorkspaceStore.listSavedQueryRevisions}; this field stays the
22
+ * one a run of the query executes.
23
+ */
14
24
  sql: string;
15
25
  /** Free-form grouping, the way a folder would work without being one. */
16
26
  folder?: string;
@@ -71,6 +81,122 @@ export interface SaveQueryInput {
71
81
  visualization?: QueryVisualization;
72
82
  shared?: boolean;
73
83
  }
84
+ /**
85
+ * One recorded revision of something whose text a person edits.
86
+ *
87
+ * The same shape for a transform's code and for a saved query's SQL, and that is
88
+ * the point rather than a saving: the two are edited the same way and go wrong
89
+ * the same way — somebody changes the text, a load or a report starts coming out
90
+ * different, and the question afterwards is what the text used to say. `body` is
91
+ * named for that. `code` would have been a lie on half of its uses.
92
+ *
93
+ * ## What this exists to fix
94
+ *
95
+ * A {@link ConnectorRun} has always recorded `transformVersion`, so the catalog
96
+ * already knew *which* version produced a given load. What it did not keep was
97
+ * the text of that version: one row per transform, overwritten in place, the
98
+ * counter bumped and the previous code gone. A saved query had not even the
99
+ * counter. Meanwhile the runs list renders `code v3`, which reads as a reference
100
+ * to something retrievable — so an operator was told a version number, believed
101
+ * the source was recoverable, and it was not.
102
+ *
103
+ * The number on the run and the {@link version} here are **the same number**.
104
+ * That is the whole contract: "this load ran v3" becomes something a person can
105
+ * open.
106
+ *
107
+ * ## No `kind` field, deliberately
108
+ *
109
+ * A revision is always read through a route that already names its subject —
110
+ * `transforms/:id/revisions`, `saved-queries/:id/revisions` — so a discriminator
111
+ * here would be a field whose only possible value the caller had just supplied.
112
+ * The *store* keys by one, because one table holds both kinds; that is a storage
113
+ * concern and it stays in the store.
114
+ *
115
+ * ## What is NOT revisioned, and why
116
+ *
117
+ * A workflow graph, which has the identical "latest only" limitation and is
118
+ * deliberately left with it. See {@link CatalogWorkflow}, which makes the
119
+ * argument where somebody looking for the missing feature will find it.
120
+ */
121
+ export interface CatalogRevision {
122
+ /**
123
+ * Stable id of this revision.
124
+ *
125
+ * Derived from the subject and the version rather than random — see
126
+ * `revisionKey` in the MikroORM store — so recording the same version twice
127
+ * replaces it instead of appending a second copy, and a screen may key a list
128
+ * on it across refetches.
129
+ */
130
+ id: string;
131
+ /** What it belongs to — a transform id or a saved-query id. */
132
+ subjectId: string;
133
+ /** The version this revision IS. Matches `transformVersion` on a run. */
134
+ version: number;
135
+ /** The text as it was: the transform's code, or the query's SQL. */
136
+ body: string;
137
+ /**
138
+ * Who saved it.
139
+ *
140
+ * Exact for a transform, which is saved through a store method that is given
141
+ * the actor. **Approximate for a saved query**, whose update path is given
142
+ * none — `updateSavedQuery` takes an id and a patch, and `CatalogService`
143
+ * keeps the actor for the audit event it emits — so a saved query's revisions
144
+ * are attributed to the query's `createdBy`. That is who created it, not
145
+ * necessarily who last edited it, and it is recorded that way rather than
146
+ * invented: a name here that was picked to fill the field would be read as
147
+ * evidence. Threading the editor through `updateSavedQuery` is what would fix
148
+ * it, and it is a change to that method's contract rather than to this one.
149
+ */
150
+ authoredBy: string;
151
+ authoredAt: string;
152
+ }
153
+ /**
154
+ * How many revisions are kept per subject. Writing a newer one drops the oldest
155
+ * beyond this.
156
+ *
157
+ * ## Why there is a cap at all
158
+ *
159
+ * This is append-only text that grows forever, and it is the fourth append-only
160
+ * table in the bundled store. The other three earn their unboundedness and this
161
+ * one does not. An audit event and a connector run are each one small row per
162
+ * *thing that happened*, at a rate an operator can read off their own load
163
+ * schedule; staged rows are dropped the moment the run that produced them is
164
+ * finished with. A revision is neither: it grows with how often somebody edits,
165
+ * which nobody meters, and every row carries a whole code body rather than a
166
+ * counter. Unpredictable in rate *and* large per row is the combination worth
167
+ * bounding — and "this grows; here is the query to prune it" would have been a
168
+ * fourth unbounded table with a paragraph in front of it.
169
+ *
170
+ * ## Why a count per subject rather than an age bound
171
+ *
172
+ * An age bound keys the wrong thing. A transform edited twice in 2019 and relied
173
+ * on ever since would lose both revisions, while one edited daily keeps
174
+ * everything — exactly backwards. What makes a revision unrecoverable is being
175
+ * superseded, so that is what the bound counts.
176
+ *
177
+ * ## What it costs, stated rather than implied
178
+ *
179
+ * A run's `transformVersion` can name a revision that has been evicted: a
180
+ * transform saved more than this many times can no longer produce its earliest
181
+ * code. That loss is real. It is strictly smaller than the one it replaces —
182
+ * where every version but the newest was unrecoverable — and it is visible
183
+ * rather than silent, because a caller holding a version older than the oldest
184
+ * revision in the list can see that the list does not reach that far.
185
+ *
186
+ * At the cap a 4 KB body costs 200 KB per subject, so a thousand heavily-edited
187
+ * subjects cost roughly 200 MB. That is a ceiling, which is the point of having
188
+ * one.
189
+ *
190
+ * ## Why a constant and not a module option
191
+ *
192
+ * The number is part of what this table promises, and a console should be able
193
+ * to print "the last 50 are kept" without a round trip to ask which deployment
194
+ * it is talking to. A knob is also a promise to support every value of it,
195
+ * including the one that switches the feature off and is then reported as a bug.
196
+ * It becomes an option on the day there is a deployment it is wrong for, rather
197
+ * than in anticipation of one.
198
+ */
199
+ export declare const CATALOG_REVISION_LIMIT = 50;
74
200
  export interface Dashboard {
75
201
  id: string;
76
202
  name: string;
@@ -480,6 +606,26 @@ export interface CatalogWorkspaceStore {
480
606
  * first and decides.
481
607
  */
482
608
  deleteSavedQuery(id: string): Promise<boolean>;
609
+ /**
610
+ * Every SQL this saved query has ever been, newest first.
611
+ *
612
+ * **Optional**, and mixed in here rather than made a member every store must
613
+ * have, for the reason {@link CatalogWorkflowStore} gives about the same
614
+ * decision: a store written against the previous shape of this interface —
615
+ * including the routing proxy in the MikroORM package — implements
616
+ * `CatalogWorkspaceStore` today, and turning that into a compile error would
617
+ * be a breaking change for a feature that is purely additive.
618
+ * {@link supportsSavedQueryRevisions} is how a caller asks, so "this store
619
+ * keeps no revisions" is a sentence a route can say rather than a method that
620
+ * is missing at run time.
621
+ *
622
+ * An EMPTY list is a real answer and never an error: a query nobody has edited
623
+ * since this shipped may genuinely have nothing recorded. What the bundled
624
+ * store does about that — see `readRevisions` — is a store's decision, and a
625
+ * consumer must read "nothing recorded" as itself rather than as "nothing has
626
+ * changed".
627
+ */
628
+ listSavedQueryRevisions?(id: string): Promise<CatalogRevision[]>;
483
629
  listDashboards(): Promise<Dashboard[]>;
484
630
  getDashboard(id: string): Promise<Dashboard | undefined>;
485
631
  saveDashboard(input: {
@@ -498,5 +644,13 @@ export interface CatalogWorkspaceStore {
498
644
  recordEvent(event: Omit<CatalogAuditEvent, 'id'>): Promise<void>;
499
645
  listEvents(query: AuditQuery): Promise<CatalogAuditEvent[]>;
500
646
  }
647
+ /**
648
+ * Whether this store keeps a saved query's history.
649
+ *
650
+ * Checks the method rather than a flag, the same way {@link isWorkspaceStore}
651
+ * and `supportsWorkflows` do: a flag is a claim and a method is the thing
652
+ * itself.
653
+ */
654
+ export declare function supportsSavedQueryRevisions(store: CatalogWorkspaceStore): store is CatalogWorkspaceStore & Required<Pick<CatalogWorkspaceStore, 'listSavedQueryRevisions'>>;
501
655
  export declare function isWorkspaceStore(store: unknown): store is CatalogWorkspaceStore;
502
656
  export declare const CATALOG_WORKSPACE_STORE: unique symbol;
@@ -9,12 +9,60 @@
9
9
  * mount the catalog without any of this and notice nothing missing.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.CATALOG_WORKSPACE_STORE = exports.CATALOG_TRACE_STORE = exports.CATALOG_TRACE_OUTCOMES = void 0;
12
+ exports.CATALOG_WORKSPACE_STORE = exports.CATALOG_TRACE_STORE = exports.CATALOG_TRACE_OUTCOMES = exports.CATALOG_REVISION_LIMIT = void 0;
13
13
  exports.isCatalogTraceOutcome = isCatalogTraceOutcome;
14
14
  exports.traceOutcomeFilter = traceOutcomeFilter;
15
15
  exports.isTraceStore = isTraceStore;
16
16
  exports.embeddedVisualization = embeddedVisualization;
17
+ exports.supportsSavedQueryRevisions = supportsSavedQueryRevisions;
17
18
  exports.isWorkspaceStore = isWorkspaceStore;
19
+ /**
20
+ * How many revisions are kept per subject. Writing a newer one drops the oldest
21
+ * beyond this.
22
+ *
23
+ * ## Why there is a cap at all
24
+ *
25
+ * This is append-only text that grows forever, and it is the fourth append-only
26
+ * table in the bundled store. The other three earn their unboundedness and this
27
+ * one does not. An audit event and a connector run are each one small row per
28
+ * *thing that happened*, at a rate an operator can read off their own load
29
+ * schedule; staged rows are dropped the moment the run that produced them is
30
+ * finished with. A revision is neither: it grows with how often somebody edits,
31
+ * which nobody meters, and every row carries a whole code body rather than a
32
+ * counter. Unpredictable in rate *and* large per row is the combination worth
33
+ * bounding — and "this grows; here is the query to prune it" would have been a
34
+ * fourth unbounded table with a paragraph in front of it.
35
+ *
36
+ * ## Why a count per subject rather than an age bound
37
+ *
38
+ * An age bound keys the wrong thing. A transform edited twice in 2019 and relied
39
+ * on ever since would lose both revisions, while one edited daily keeps
40
+ * everything — exactly backwards. What makes a revision unrecoverable is being
41
+ * superseded, so that is what the bound counts.
42
+ *
43
+ * ## What it costs, stated rather than implied
44
+ *
45
+ * A run's `transformVersion` can name a revision that has been evicted: a
46
+ * transform saved more than this many times can no longer produce its earliest
47
+ * code. That loss is real. It is strictly smaller than the one it replaces —
48
+ * where every version but the newest was unrecoverable — and it is visible
49
+ * rather than silent, because a caller holding a version older than the oldest
50
+ * revision in the list can see that the list does not reach that far.
51
+ *
52
+ * At the cap a 4 KB body costs 200 KB per subject, so a thousand heavily-edited
53
+ * subjects cost roughly 200 MB. That is a ceiling, which is the point of having
54
+ * one.
55
+ *
56
+ * ## Why a constant and not a module option
57
+ *
58
+ * The number is part of what this table promises, and a console should be able
59
+ * to print "the last 50 are kept" without a round trip to ask which deployment
60
+ * it is talking to. A knob is also a promise to support every value of it,
61
+ * including the one that switches the feature off and is then reported as a bug.
62
+ * It becomes an option on the day there is a deployment it is wrong for, rather
63
+ * than in anticipation of one.
64
+ */
65
+ exports.CATALOG_REVISION_LIMIT = 50;
18
66
  /**
19
67
  * The same events, told as stories instead of as a list.
20
68
  *
@@ -102,6 +150,16 @@ function embeddedVisualization(saved, cardLibrary) {
102
150
  const base = saved ?? { kind: 'table' };
103
151
  return cardLibrary ? { ...base, library: cardLibrary } : base;
104
152
  }
153
+ /**
154
+ * Whether this store keeps a saved query's history.
155
+ *
156
+ * Checks the method rather than a flag, the same way {@link isWorkspaceStore}
157
+ * and `supportsWorkflows` do: a flag is a claim and a method is the thing
158
+ * itself.
159
+ */
160
+ function supportsSavedQueryRevisions(store) {
161
+ return typeof store.listSavedQueryRevisions === 'function';
162
+ }
105
163
  function isWorkspaceStore(store) {
106
164
  return (typeof store === 'object' &&
107
165
  store !== null &&
package/dist/client.d.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  * own UI. The endpoints alone are not an API; the endpoints plus the response
10
10
  * types are.
11
11
  */
12
- export type { AuditQuery, CatalogAuditEvent, Dashboard, DashboardCard, QueryVisualization, SaveQueryInput, SavedQuery, } from './catalog.workspace';
12
+ export type { AuditQuery, CatalogAuditEvent, CatalogRevision, Dashboard, DashboardCard, QueryVisualization, SaveQueryInput, SavedQuery, } from './catalog.workspace';
13
+ export { CATALOG_REVISION_LIMIT } from './catalog.workspace';
13
14
  export type { CatalogQueryRelation, CatalogQueryRequest, CatalogQueryResult, } from './catalog.query';
14
15
  export type { CatalogSearchField, CatalogSearchHit, CatalogSearchKind, CatalogSearchRank, CatalogSearchResult, } from './search.types';
15
16
  export type { CatalogGraph, CatalogObjectPage, CatalogObjectQuery, CatalogObjectTypeDef, CatalogOverlay, CatalogPropertyDef, CatalogRelationDef, CatalogSnapshot, RelationKind, ScalarType, } from './catalog.types';
@@ -66,6 +67,15 @@ export declare const catalogRoutes: {
66
67
  readonly workspaceCapabilities: () => string;
67
68
  readonly savedQueries: () => string;
68
69
  readonly savedQuery: (id: string) => string;
70
+ /**
71
+ * Every SQL this query has ever been.
72
+ *
73
+ * A sub-resource of the saved query rather than a `?version=` on it, because
74
+ * the question a diff screen asks first is "what were all of them" — it has to
75
+ * see the list before it knows which two to compare, and one request that
76
+ * answers that beats a list plus two fetches.
77
+ */
78
+ readonly savedQueryRevisions: (id: string) => string;
69
79
  readonly runSavedQuery: (id: string) => string;
70
80
  readonly exportSavedQuery: (id: string) => string;
71
81
  readonly dashboards: () => string;
package/dist/client.js CHANGED
@@ -11,7 +11,12 @@
11
11
  * types are.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.isWorkflowStatus = exports.isWorkflowNodeKind = exports.isWorkflowExecutionMode = exports.workflowRunOrder = exports.workflowGraphHash = exports.WORKFLOW_STATUSES = exports.WORKFLOW_NODE_KINDS = exports.WORKFLOW_NODE_ID_PATTERN = exports.WORKFLOW_ISSUE_CODES = exports.WORKFLOW_EXECUTION_MODES = exports.validateWorkflow = exports.TRANSFORM_LANGUAGES = exports.isWorkflowNode = exports.isWorkflowEdge = exports.isTransformLanguage = exports.isConnectorKind = exports.CONNECTOR_KINDS = exports.catalogRoutes = void 0;
14
+ exports.isWorkflowStatus = exports.isWorkflowNodeKind = exports.isWorkflowExecutionMode = exports.workflowRunOrder = exports.workflowGraphHash = exports.WORKFLOW_STATUSES = exports.WORKFLOW_NODE_KINDS = exports.WORKFLOW_NODE_ID_PATTERN = exports.WORKFLOW_ISSUE_CODES = exports.WORKFLOW_EXECUTION_MODES = exports.validateWorkflow = exports.TRANSFORM_LANGUAGES = exports.isWorkflowNode = exports.isWorkflowEdge = exports.isTransformLanguage = exports.isConnectorKind = exports.CONNECTOR_KINDS = exports.catalogRoutes = exports.CATALOG_REVISION_LIMIT = void 0;
15
+ // A value, not a type: a screen saying how far back the history goes should read
16
+ // the number rather than print one of its own. See its docblock for what the cap
17
+ // costs.
18
+ var catalog_workspace_1 = require("./catalog.workspace");
19
+ Object.defineProperty(exports, "CATALOG_REVISION_LIMIT", { enumerable: true, get: function () { return catalog_workspace_1.CATALOG_REVISION_LIMIT; } });
15
20
  /**
16
21
  * Builds the paths the catalog controller serves, relative to wherever it was
17
22
  * mounted. Kept as string builders rather than a fetch wrapper so the host
@@ -40,6 +45,15 @@ exports.catalogRoutes = {
40
45
  workspaceCapabilities: () => '/catalog/workspace/capabilities',
41
46
  savedQueries: () => '/catalog/saved-queries',
42
47
  savedQuery: (id) => `/catalog/saved-queries/${encodeURIComponent(id)}`,
48
+ /**
49
+ * Every SQL this query has ever been.
50
+ *
51
+ * A sub-resource of the saved query rather than a `?version=` on it, because
52
+ * the question a diff screen asks first is "what were all of them" — it has to
53
+ * see the list before it knows which two to compare, and one request that
54
+ * answers that beats a list plus two fetches.
55
+ */
56
+ savedQueryRevisions: (id) => `/catalog/saved-queries/${encodeURIComponent(id)}/revisions`,
43
57
  runSavedQuery: (id) => `/catalog/saved-queries/${encodeURIComponent(id)}/run`,
44
58
  exportSavedQuery: (id) => `/catalog/saved-queries/${encodeURIComponent(id)}/export.csv`,
45
59
  dashboards: () => '/catalog/dashboards',
package/dist/index.d.ts CHANGED
@@ -8,14 +8,14 @@ export { type CatalogOverlayStore, FileCatalogOverlayStore, InMemoryCatalogOverl
8
8
  export { CATALOG_OVERLAY_STORE } from './catalog.overlay-store.token';
9
9
  export { MikroOrmCatalogRegistry } from './catalog.registry';
10
10
  export { CatalogRegistry } from './catalog.registry.base';
11
- export { CATALOG_PIPELINE_STORE, type CatalogConnection, type CatalogConnector, type ConnectionCheck, CONNECTOR_KINDS, type CatalogPipelineStore, type CatalogStageStore, type CatalogTransform, type CatalogWorkflow, type CatalogWorkflowCapabilities, type CatalogWorkflowStore, type ConnectorKind, type ConnectorRun, isConnectorKind, isPipelineStore, isTransformLanguage, isWorkflowEdge, isWorkflowExecutionMode, isWorkflowNode, isWorkflowNodeKind, isWorkflowStatus, supportsWorkflows, supportsWorkflowStages, TRANSFORM_RUNNER, TRANSFORM_LANGUAGES, type TransformLanguage, type TransformResult, type TransformRunner, validateWorkflow, WORKFLOW_EXECUTION_MODES, WORKFLOW_ISSUE_CODES, WORKFLOW_NODE_ID_PATTERN, WORKFLOW_NODE_KINDS, WORKFLOW_STATUSES, type WorkflowEdge, type WorkflowExecutionMode, type WorkflowGraph, workflowGraphHash, type WorkflowIssueCode, type WorkflowNode, type WorkflowNodeKind, type WorkflowNodeOutcome, type WorkflowNodeStepInput, type WorkflowNodeStepOutput, workflowRunOrder, type WorkflowSinkNode, type WorkflowSourceNode, type WorkflowStageRef, type WorkflowStatus, type WorkflowTransformNode, type WorkflowValidationIssue, } from './catalog.pipeline';
11
+ export { CATALOG_PIPELINE_STORE, type CatalogConnection, type CatalogConnector, type ConnectionCheck, CONNECTOR_KINDS, type CatalogPipelineStore, type CatalogStageStore, type CatalogTransform, type CatalogWorkflow, type CatalogWorkflowCapabilities, type CatalogWorkflowStore, type ConnectorKind, type ConnectorRun, isConnectorKind, isPipelineStore, isTransformLanguage, supportsTransformRevisions, isWorkflowEdge, isWorkflowExecutionMode, isWorkflowNode, isWorkflowNodeKind, isWorkflowStatus, supportsWorkflows, supportsWorkflowStages, TRANSFORM_RUNNER, TRANSFORM_LANGUAGES, type TransformLanguage, type TransformResult, type TransformRunner, validateWorkflow, WORKFLOW_EXECUTION_MODES, WORKFLOW_ISSUE_CODES, WORKFLOW_NODE_ID_PATTERN, WORKFLOW_NODE_KINDS, WORKFLOW_STATUSES, type WorkflowEdge, type WorkflowExecutionMode, type WorkflowGraph, workflowGraphHash, type WorkflowIssueCode, type WorkflowNode, type WorkflowNodeKind, type WorkflowNodeOutcome, type WorkflowNodeStepInput, type WorkflowNodeStepOutput, workflowRunOrder, type WorkflowSinkNode, type WorkflowSourceNode, type WorkflowStageRef, type WorkflowStatus, type WorkflowTransformNode, type WorkflowValidationIssue, } from './catalog.pipeline';
12
12
  export * from './catalog.environment';
13
13
  export { QueryCache, toCsv } from './catalog.query-cache';
14
14
  export { SubprocessTransformRunner, type TransformRunnerOptions, } from './transform-runner';
15
15
  export { CatalogService } from './catalog.service';
16
16
  export { DEFAULT_SEARCH_LIMIT, MAX_SEARCH_LIMIT, bestMatch, emptySearch, maySearch, type SearchInput, type SearchableDashboard, type SearchableSavedQuery, searchCatalog, visibleToPrincipal, } from './search';
17
17
  export type { CatalogSearchField, CatalogSearchHit, CatalogSearchKind, CatalogSearchRank, CatalogSearchResult, } from './search.types';
18
- export { type AuditQuery, CATALOG_TRACE_OUTCOMES, CATALOG_TRACE_STORE, CATALOG_WORKSPACE_STORE, type CatalogAuditEvent, type CatalogTrace, type CatalogTraceList, type CatalogTraceOutcome, type CatalogTraceSpan, type CatalogTraceStore, type CatalogTraceTotals, type CatalogUnlinkedList, type CatalogWorkspaceStore, type Dashboard, type DashboardCard, type EmbeddedChart, type EmbeddedChartPlacement, type EmbeddedDashboard, embeddedVisualization, isCatalogTraceOutcome, isTraceStore, isWorkspaceStore, type QueryVisualization, type SaveQueryInput, type SavedQuery, type TraceQuery, traceOutcomeFilter, } from './catalog.workspace';
18
+ export { type AuditQuery, CATALOG_REVISION_LIMIT, CATALOG_TRACE_OUTCOMES, CATALOG_TRACE_STORE, CATALOG_WORKSPACE_STORE, type CatalogAuditEvent, type CatalogRevision, type CatalogTrace, type CatalogTraceList, type CatalogTraceOutcome, type CatalogTraceSpan, type CatalogTraceStore, type CatalogTraceTotals, type CatalogUnlinkedList, type CatalogWorkspaceStore, type Dashboard, type DashboardCard, type EmbeddedChart, type EmbeddedChartPlacement, type EmbeddedDashboard, embeddedVisualization, isCatalogTraceOutcome, isTraceStore, isWorkspaceStore, type QueryVisualization, type SaveQueryInput, type SavedQuery, supportsSavedQueryRevisions, type TraceQuery, traceOutcomeFilter, } from './catalog.workspace';
19
19
  export { CATALOG_PRINCIPAL_RESOLVER, type CatalogActor, type CatalogGrants, type CatalogPrincipal, type CatalogPrincipalResolver, type CatalogScope, composePrincipalId, delegatePrincipal, expandScopes, hasScope, parsePrincipalId, PRINCIPAL_ACTOR_SEPARATOR, maySeeClassification, mayRead, mayWrite, readableObjectPage, StaticKeyPrincipalResolver, } from './catalog.principal';
20
20
  export * from './catalog.access';
21
21
  export { assertNoColumnCollisions, CATALOG_RESERVED_COLUMNS, CATALOG_SNAPSHOT_MODES, CATALOG_STORE, type CarryForwardResult, type CatalogColumnCollision, CatalogColumnCollisionError, type CatalogMergeStore, type CatalogReadQuery, type CatalogReadResult, type CatalogReadStore, type CatalogReservedColumn, type CatalogSnapshotMode, type CatalogStoreCapabilities, type CatalogWriteStore, type ColumnCollisionOptions, findColumnCollisions, isCatalogStoreCapabilities, isReservedColumn, isWriteStore, type SnapshotRef, supportsCarryForward, } from './catalog.store';
package/dist/index.js CHANGED
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.emptySearch = exports.bestMatch = exports.MAX_SEARCH_LIMIT = exports.DEFAULT_SEARCH_LIMIT = exports.CatalogService = exports.SubprocessTransformRunner = exports.toCsv = exports.QueryCache = exports.workflowRunOrder = exports.workflowGraphHash = exports.WORKFLOW_STATUSES = exports.WORKFLOW_NODE_KINDS = exports.WORKFLOW_NODE_ID_PATTERN = exports.WORKFLOW_ISSUE_CODES = exports.WORKFLOW_EXECUTION_MODES = exports.validateWorkflow = exports.TRANSFORM_LANGUAGES = exports.TRANSFORM_RUNNER = exports.supportsWorkflowStages = exports.supportsWorkflows = exports.isWorkflowStatus = exports.isWorkflowNodeKind = exports.isWorkflowNode = exports.isWorkflowExecutionMode = exports.isWorkflowEdge = exports.isTransformLanguage = exports.isPipelineStore = exports.isConnectorKind = exports.CONNECTOR_KINDS = exports.CATALOG_PIPELINE_STORE = exports.CatalogRegistry = exports.MikroOrmCatalogRegistry = exports.CATALOG_OVERLAY_STORE = exports.InMemoryCatalogOverlayStore = exports.FileCatalogOverlayStore = exports.CATALOG_OPTIONS = exports.isQueryStore = exports.assertReadOnlyShape = exports.CatalogModule = exports.emitCatalog = exports.curationActor = exports.channelNameFor = exports.catalogEventPhase = exports.UNATTRIBUTED_PRINCIPAL_ID = exports.CATALOG_LIB = exports.CATALOG_EVENTS = exports.CATALOG_EVENT_PHASE_FALLBACK = exports.CATALOG_EVENT_PHASE = exports.CatalogType = exports.CatalogProperty = void 0;
18
- exports.RequireScopes = exports.RequireHuman = exports.REQUIRES_HUMAN = exports.REQUIRED_SCOPES = exports.MikroOrmReadStore = exports.supportsCarryForward = exports.isWriteStore = exports.isReservedColumn = exports.isCatalogStoreCapabilities = exports.findColumnCollisions = exports.CatalogColumnCollisionError = exports.CATALOG_STORE = exports.CATALOG_SNAPSHOT_MODES = exports.CATALOG_RESERVED_COLUMNS = exports.assertNoColumnCollisions = exports.StaticKeyPrincipalResolver = exports.readableObjectPage = exports.mayWrite = exports.mayRead = exports.maySeeClassification = exports.PRINCIPAL_ACTOR_SEPARATOR = exports.parsePrincipalId = exports.hasScope = exports.expandScopes = exports.delegatePrincipal = exports.composePrincipalId = exports.CATALOG_PRINCIPAL_RESOLVER = exports.traceOutcomeFilter = exports.isWorkspaceStore = exports.isTraceStore = exports.isCatalogTraceOutcome = exports.embeddedVisualization = exports.CATALOG_WORKSPACE_STORE = exports.CATALOG_TRACE_STORE = exports.CATALOG_TRACE_OUTCOMES = exports.visibleToPrincipal = exports.searchCatalog = exports.maySearch = void 0;
17
+ exports.bestMatch = exports.MAX_SEARCH_LIMIT = exports.DEFAULT_SEARCH_LIMIT = exports.CatalogService = exports.SubprocessTransformRunner = exports.toCsv = exports.QueryCache = exports.workflowRunOrder = exports.workflowGraphHash = exports.WORKFLOW_STATUSES = exports.WORKFLOW_NODE_KINDS = exports.WORKFLOW_NODE_ID_PATTERN = exports.WORKFLOW_ISSUE_CODES = exports.WORKFLOW_EXECUTION_MODES = exports.validateWorkflow = exports.TRANSFORM_LANGUAGES = exports.TRANSFORM_RUNNER = exports.supportsWorkflowStages = exports.supportsWorkflows = exports.isWorkflowStatus = exports.isWorkflowNodeKind = exports.isWorkflowNode = exports.isWorkflowExecutionMode = exports.isWorkflowEdge = exports.supportsTransformRevisions = exports.isTransformLanguage = exports.isPipelineStore = exports.isConnectorKind = exports.CONNECTOR_KINDS = exports.CATALOG_PIPELINE_STORE = exports.CatalogRegistry = exports.MikroOrmCatalogRegistry = exports.CATALOG_OVERLAY_STORE = exports.InMemoryCatalogOverlayStore = exports.FileCatalogOverlayStore = exports.CATALOG_OPTIONS = exports.isQueryStore = exports.assertReadOnlyShape = exports.CatalogModule = exports.emitCatalog = exports.curationActor = exports.channelNameFor = exports.catalogEventPhase = exports.UNATTRIBUTED_PRINCIPAL_ID = exports.CATALOG_LIB = exports.CATALOG_EVENTS = exports.CATALOG_EVENT_PHASE_FALLBACK = exports.CATALOG_EVENT_PHASE = exports.CatalogType = exports.CatalogProperty = void 0;
18
+ exports.RequireScopes = exports.RequireHuman = exports.REQUIRES_HUMAN = exports.REQUIRED_SCOPES = exports.MikroOrmReadStore = exports.supportsCarryForward = exports.isWriteStore = exports.isReservedColumn = exports.isCatalogStoreCapabilities = exports.findColumnCollisions = exports.CatalogColumnCollisionError = exports.CATALOG_STORE = exports.CATALOG_SNAPSHOT_MODES = exports.CATALOG_RESERVED_COLUMNS = exports.assertNoColumnCollisions = exports.StaticKeyPrincipalResolver = exports.readableObjectPage = exports.mayWrite = exports.mayRead = exports.maySeeClassification = exports.PRINCIPAL_ACTOR_SEPARATOR = exports.parsePrincipalId = exports.hasScope = exports.expandScopes = exports.delegatePrincipal = exports.composePrincipalId = exports.CATALOG_PRINCIPAL_RESOLVER = exports.traceOutcomeFilter = exports.supportsSavedQueryRevisions = exports.isWorkspaceStore = exports.isTraceStore = exports.isCatalogTraceOutcome = exports.embeddedVisualization = exports.CATALOG_WORKSPACE_STORE = exports.CATALOG_TRACE_STORE = exports.CATALOG_TRACE_OUTCOMES = exports.CATALOG_REVISION_LIMIT = exports.visibleToPrincipal = exports.searchCatalog = exports.maySearch = exports.emptySearch = void 0;
19
19
  var catalog_decorators_1 = require("./catalog.decorators");
20
20
  Object.defineProperty(exports, "CatalogProperty", { enumerable: true, get: function () { return catalog_decorators_1.CatalogProperty; } });
21
21
  Object.defineProperty(exports, "CatalogType", { enumerable: true, get: function () { return catalog_decorators_1.CatalogType; } });
@@ -59,6 +59,7 @@ Object.defineProperty(exports, "CONNECTOR_KINDS", { enumerable: true, get: funct
59
59
  Object.defineProperty(exports, "isConnectorKind", { enumerable: true, get: function () { return catalog_pipeline_1.isConnectorKind; } });
60
60
  Object.defineProperty(exports, "isPipelineStore", { enumerable: true, get: function () { return catalog_pipeline_1.isPipelineStore; } });
61
61
  Object.defineProperty(exports, "isTransformLanguage", { enumerable: true, get: function () { return catalog_pipeline_1.isTransformLanguage; } });
62
+ Object.defineProperty(exports, "supportsTransformRevisions", { enumerable: true, get: function () { return catalog_pipeline_1.supportsTransformRevisions; } });
62
63
  Object.defineProperty(exports, "isWorkflowEdge", { enumerable: true, get: function () { return catalog_pipeline_1.isWorkflowEdge; } });
63
64
  Object.defineProperty(exports, "isWorkflowExecutionMode", { enumerable: true, get: function () { return catalog_pipeline_1.isWorkflowExecutionMode; } });
64
65
  Object.defineProperty(exports, "isWorkflowNode", { enumerable: true, get: function () { return catalog_pipeline_1.isWorkflowNode; } });
@@ -102,6 +103,7 @@ Object.defineProperty(exports, "maySearch", { enumerable: true, get: function ()
102
103
  Object.defineProperty(exports, "searchCatalog", { enumerable: true, get: function () { return search_1.searchCatalog; } });
103
104
  Object.defineProperty(exports, "visibleToPrincipal", { enumerable: true, get: function () { return search_1.visibleToPrincipal; } });
104
105
  var catalog_workspace_1 = require("./catalog.workspace");
106
+ Object.defineProperty(exports, "CATALOG_REVISION_LIMIT", { enumerable: true, get: function () { return catalog_workspace_1.CATALOG_REVISION_LIMIT; } });
105
107
  Object.defineProperty(exports, "CATALOG_TRACE_OUTCOMES", { enumerable: true, get: function () { return catalog_workspace_1.CATALOG_TRACE_OUTCOMES; } });
106
108
  Object.defineProperty(exports, "CATALOG_TRACE_STORE", { enumerable: true, get: function () { return catalog_workspace_1.CATALOG_TRACE_STORE; } });
107
109
  Object.defineProperty(exports, "CATALOG_WORKSPACE_STORE", { enumerable: true, get: function () { return catalog_workspace_1.CATALOG_WORKSPACE_STORE; } });
@@ -109,6 +111,7 @@ Object.defineProperty(exports, "embeddedVisualization", { enumerable: true, get:
109
111
  Object.defineProperty(exports, "isCatalogTraceOutcome", { enumerable: true, get: function () { return catalog_workspace_1.isCatalogTraceOutcome; } });
110
112
  Object.defineProperty(exports, "isTraceStore", { enumerable: true, get: function () { return catalog_workspace_1.isTraceStore; } });
111
113
  Object.defineProperty(exports, "isWorkspaceStore", { enumerable: true, get: function () { return catalog_workspace_1.isWorkspaceStore; } });
114
+ Object.defineProperty(exports, "supportsSavedQueryRevisions", { enumerable: true, get: function () { return catalog_workspace_1.supportsSavedQueryRevisions; } });
112
115
  Object.defineProperty(exports, "traceOutcomeFilter", { enumerable: true, get: function () { return catalog_workspace_1.traceOutcomeFilter; } });
113
116
  var catalog_principal_1 = require("./catalog.principal");
114
117
  Object.defineProperty(exports, "CATALOG_PRINCIPAL_RESOLVER", { enumerable: true, get: function () { return catalog_principal_1.CATALOG_PRINCIPAL_RESOLVER; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-catalog",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "A metadata registry for NestJS: object types, properties and relations, derived from your ORM and enriched with decorators.",
5
5
  "license": "MIT",
6
6
  "author": "Davide Carvalho",