@dudousxd/nestjs-catalog 0.12.0 → 0.13.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.
@@ -106,10 +106,17 @@ export interface CatalogEventPayloads {
106
106
  snapshotId: string;
107
107
  };
108
108
  /**
109
- * Someone changed a label, description, unit or visibility.
109
+ * Someone changed a label, description, unit or visibility — or stated how a
110
+ * type reconciles deletes.
110
111
  *
111
- * Presentation-only, and emitted anyway: "who renamed this column and when"
112
- * is a governance question, and the answer is otherwise nowhere.
112
+ * Presentation was the whole of it once, and emitted anyway: "who renamed this
113
+ * column and when" is a governance question whose answer is otherwise nowhere.
114
+ * Per-type load expectations then arrived on the same event, and a delete
115
+ * strategy is not presentation — it decides whether an incremental load of the
116
+ * type may commit at all. `changed` tells the two apart (`expectation.deletes`
117
+ * and `expectation.cleared` against the field names a rename carries), which is
118
+ * why widening this event was better than minting a second one nobody's
119
+ * recorder would have been reading.
113
120
  */
114
121
  'type.curated': {
115
122
  typeName: string;
@@ -122,6 +122,211 @@ export interface CatalogConnector {
122
122
  lastRunAt?: string;
123
123
  lastRunStatus?: 'succeeded' | 'failed' | 'running';
124
124
  }
125
+ /**
126
+ * What a load has to be true of before it is allowed to become the data
127
+ * everybody reads.
128
+ *
129
+ * Two failures live here, and they are the same failure seen from two ends: a
130
+ * load that is *fresh and wrong*. Every signal this catalog publishes about a
131
+ * type — `lastCommittedAt`, the age badge on the Model screen, a green run in
132
+ * the runs list — reports on whether a load HAPPENED. None of them reports on
133
+ * whether what it loaded resembles the dataset it replaced, and a snapshot
134
+ * commit is atomic, so the moment a wrong load commits it is indistinguishable
135
+ * from a right one until somebody counts rows by hand.
136
+ *
137
+ * - **Deletes.** An incremental connector asks its source for what changed
138
+ * since a watermark. A row physically removed from the source never changes
139
+ * again, so it is never returned again, so `carryForward` copies it into
140
+ * every subsequent snapshot forever. The catalog does not go wrong at any
141
+ * point; it simply never finds out. See {@link DeleteReconciliation}.
142
+ * - **Collapse.** A source-side filter change, a broken `WHERE`, a partial
143
+ * outage: the connector returns 12 rows where it returned 40,000, the
144
+ * snapshot commits, and the freshness signals all say healthy — correctly,
145
+ * because it IS fresh. See {@link RowCountBound}.
146
+ *
147
+ * **Why a policy object and not a column on the connector.** Both facts are
148
+ * statements about a *type*, not about the reader of a source. "It is
149
+ * acceptable that `Employee` accumulates rows deleted upstream" and "`Employee`
150
+ * must never lose half its rows in one load" stay true whether the rows arrive
151
+ * from a connector, from a workflow sink, or from an application POSTing to the
152
+ * publish API — and all three of those paths end at the same two methods on
153
+ * `PublishService`, which is where these are enforced. A per-connector field
154
+ * would have covered one of the three and would have had to be checked in three
155
+ * places to cover the rest.
156
+ *
157
+ * The second reason is who should be able to change it. Accepting that a
158
+ * dataset silently accumulates deleted rows is not a checkbox decision; it is
159
+ * the kind of thing that should appear in a diff with a reason attached, which
160
+ * is why {@link DeleteReconciliation} makes the reason a required field.
161
+ */
162
+ export interface CatalogLoadExpectations {
163
+ /** Applied to every type that has no entry of its own. */
164
+ default?: LoadExpectation;
165
+ /**
166
+ * Keyed by object type name. Merged OVER {@link default} field by field, so a
167
+ * host can set one house-wide row-count bound and still say something about
168
+ * deletes for the three types that are loaded incrementally.
169
+ */
170
+ byType?: Record<string, LoadExpectation>;
171
+ }
172
+ export interface LoadExpectation {
173
+ /**
174
+ * How deletions at the source reach this type. **Absent means the load is
175
+ * refused**, which is the whole mechanism — see {@link
176
+ * refuseUndeclaredDeletes}.
177
+ */
178
+ deletes?: DeleteReconciliation;
179
+ /**
180
+ * How far one load may move this type's row count. Merged over
181
+ * {@link DEFAULT_ROW_COUNT_BOUND}, so a host that only wants to raise
182
+ * `maxShrink` writes exactly that one field.
183
+ */
184
+ rowCount?: Partial<RowCountBound>;
185
+ }
186
+ /**
187
+ * How a type that is loaded incrementally learns about rows that were deleted.
188
+ *
189
+ * Three answers, and the honest thing to say about them up front is that only
190
+ * one is *policed*. What this file enforces is that somebody chose one and
191
+ * wrote down why — because the state being prevented is nobody having thought
192
+ * about it at all, and that state is invisible by construction.
193
+ *
194
+ * The fourth answer, tombstones off a change feed, is the correct one and is
195
+ * deliberately not here. It needs the source to publish a delete stream, the
196
+ * catalog to hold a delete log per type, and the merge to apply it — which is
197
+ * a larger machine than the problem justifies today, and adding a strategy name
198
+ * that nothing implements would be exactly the dropdown-with-a-lie this
199
+ * codebase refuses everywhere else.
200
+ */
201
+ export type DeleteReconciliation =
202
+ /**
203
+ * Nothing reconciles them, and that is a decision somebody made.
204
+ *
205
+ * The legitimate cases are real and common: an append-only ledger where rows
206
+ * are never removed, a source that only ever soft-retires records by changing
207
+ * a status the transform can see, or a dataset where a handful of stale rows
208
+ * is genuinely cheaper than a nightly full read. What is not legitimate is
209
+ * arriving here by default, which is why {@link because} cannot be omitted.
210
+ */
211
+ {
212
+ strategy: 'accepted';
213
+ because: string;
214
+ }
215
+ /**
216
+ * The source marks a deletion instead of performing one, and the watermark
217
+ * therefore sees it — a `deleted_at` that moves, a status column that flips —
218
+ * so the deleted row arrives as an ordinary change and the transform drops it
219
+ * or the type keeps it flagged.
220
+ *
221
+ * The strongest of the three, and the one that pushes a requirement onto a
222
+ * source that may refuse it. Not verifiable from here: the catalog cannot
223
+ * tell a source that soft-deletes from one that claims to, so this is a
224
+ * declaration like the one above. It is a separate value anyway because the
225
+ * two say completely different things to the next person who reads the
226
+ * config, and collapsing them would lose that.
227
+ */
228
+ | {
229
+ strategy: 'soft-deleted-at-source';
230
+ because: string;
231
+ column?: string;
232
+ }
233
+ /**
234
+ * Full reads reconcile, incremental reads fill the gaps between them.
235
+ *
236
+ * The interval is the trade-off, and it is stated in time rather than in runs
237
+ * because "reconciled daily" is what anybody actually means and because the
238
+ * only thing the catalog can count is the snapshots a store chooses to
239
+ * report, which is a window of unknown depth. {@link refuseStaleReconciliation}
240
+ * makes the interval real: once the newest full load of the type is older
241
+ * than `withinMs`, incremental loads of it stop committing.
242
+ */
243
+ | {
244
+ strategy: 'periodic-full-reload';
245
+ because: string;
246
+ withinMs: number;
247
+ };
248
+ /**
249
+ * How far a single load may move a type's row count before it is refused.
250
+ *
251
+ * **Asymmetric on purpose.** A type that doubles has usually had a good day —
252
+ * a backfill landed, a new base was onboarded, a source finished catching up.
253
+ * A type that loses 90% has almost never had a good day. Bounding both sides by
254
+ * the same number would mean picking a growth bound loose enough to be useless
255
+ * as a shrink bound, or a shrink bound tight enough to refuse every backfill.
256
+ *
257
+ * **Conditional on the store, and a host configuring this should know which
258
+ * condition.** {@link refuseRowCountDrift} is pure and decides on two numbers;
259
+ * somebody has to fetch them, and both come from members that are optional on
260
+ * the store interface. Without `currentSnapshot` there is no served baseline;
261
+ * without `listSnapshots`, or from a `listSnapshots` whose window does not
262
+ * reach the snapshot about to be committed, there is no count for the pending
263
+ * one. Either way the bound is not applied to that commit. That is the same
264
+ * permissive-rather-than-punishing stance {@link CARRIED_FROM_LABEL} takes for
265
+ * the same reason — an adapter that records less than the bundled one is not
266
+ * the failure this file exists for — but it means a number written here is a
267
+ * bound the store has to be able to measure, not one it is guaranteed to have.
268
+ * `PublishService.assertRowCountIsPlausible` is where that is decided; a skip
269
+ * that is not said out loud there is a bound believed to be on and off, which
270
+ * is the one outcome neither this file nor that one may produce.
271
+ */
272
+ export interface RowCountBound {
273
+ /**
274
+ * The largest fraction of the previously served snapshot a load may lose.
275
+ * `0.5` refuses a load that comes back with less than half of what is live.
276
+ */
277
+ maxShrink: number;
278
+ /**
279
+ * The ratio above which growth is refused — `10` refuses a load ten times the
280
+ * size of the previous one. **Absent means growth is never refused**, which is
281
+ * the default, because the failure this file exists for is collapse and a
282
+ * growth bound that fires on a legitimate backfill teaches people to raise
283
+ * every bound in this object until none of them do anything.
284
+ */
285
+ maxGrowth?: number;
286
+ /**
287
+ * Below this many rows in the previously served snapshot, no ratio applies.
288
+ *
289
+ * A percentage of a small number is noise. A four-row lookup table dropping to
290
+ * one is a 75% collapse and is also a Tuesday, and a bound that fires on it is
291
+ * a bound somebody switches off — taking the forty-thousand-row types with it.
292
+ */
293
+ minRows: number;
294
+ }
295
+ /**
296
+ * A per-type expectation as an operator set it, with who and when.
297
+ *
298
+ * The layer between a host's `byType` entry and its `default`. It exists because
299
+ * the control the docblocks above argue for was never "it must be in code" —
300
+ * it is that **somebody chose a strategy and wrote down why**, which needs
301
+ * attribution and visibility rather than compilation. A host object gives the
302
+ * reason a place to live and gives attribution to nobody: a `git blame` on a
303
+ * deployment's wiring names whoever last reformatted the file. So the reason
304
+ * arrives with the principal that set it and the instant they did, and the
305
+ * declaration requirement is unchanged — {@link refuseUndeclaredDeletes} asks
306
+ * the same question of a stored row as it does of a host one.
307
+ *
308
+ * The grain is still the type, and only the type. A connector, a workflow sink
309
+ * and an application POSTing to the publish API all end at the same two
310
+ * `PublishService` methods and all have the same delete problem, so a per-
311
+ * connector or per-workflow row would give one dataset several answers to one
312
+ * question — see {@link CatalogLoadExpectations}, which argues it at length and
313
+ * is unaffected by this layer existing.
314
+ *
315
+ * Both policy fields are optional, and a row may carry either, both or neither:
316
+ * precedence is resolved field by field, so an operator raising a shrink bound
317
+ * says nothing about deletes and does not have to.
318
+ */
319
+ export interface StoredLoadExpectation {
320
+ typeName: string;
321
+ deletes?: DeleteReconciliation;
322
+ rowCount?: Partial<RowCountBound>;
323
+ /** Principal id of whoever set it. */
324
+ setBy: string;
325
+ /** Actor id when a person was behind the principal — the audit's real subject. */
326
+ setByActor?: string;
327
+ /** ISO 8601. */
328
+ setAt: string;
329
+ }
125
330
  /**
126
331
  * TypeScript is Node's own type stripping, so it costs no compiler and no build
127
332
  * step — and types are erased, never checked. A transform with a wrong type
@@ -852,6 +1057,37 @@ export declare function supportsWorkflows(store: CatalogPipelineStore): store is
852
1057
  */
853
1058
  export declare function supportsTransformRevisions(store: CatalogPipelineStore): store is CatalogPipelineStore & Required<Pick<CatalogPipelineStore, 'listTransformRevisions'>>;
854
1059
  export declare function supportsWorkflowStages(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogStageStore;
1060
+ /**
1061
+ * A store that really does hold operator-set expectations, all four members
1062
+ * present.
1063
+ *
1064
+ * Derived from {@link CatalogPipelineStore} rather than declared as a separate
1065
+ * interface the way {@link CatalogWorkflowStore} is, and the difference is not
1066
+ * stylistic: these four are optional members OF the pipeline store, so writing
1067
+ * them out a second time here would be a copy that can drift from the one the
1068
+ * signatures are read from. `CatalogWorkflowStore` predates that lesson and is
1069
+ * mixed in through `Partial<>`, which reaches the same place from the other
1070
+ * side.
1071
+ */
1072
+ export type CatalogLoadExpectationStore = Required<Pick<CatalogPipelineStore, 'listLoadExpectations' | 'getLoadExpectation' | 'saveLoadExpectation' | 'clearLoadExpectation'>>;
1073
+ /**
1074
+ * Whether an operator can set a load expectation on this deployment at all.
1075
+ *
1076
+ * The methods rather than a flag, the same argument as {@link supportsWorkflows}
1077
+ * — and all four of them by name rather than one standing in for the rest, for
1078
+ * that function's other reason: the write path and the read path are used at
1079
+ * different moments, so a store with the getter and not the setter would narrow
1080
+ * cleanly here and fail on the save, after the screen had already offered an
1081
+ * editor.
1082
+ *
1083
+ * A store that has none of them is not broken and is not second-class. It
1084
+ * behaves exactly as every store did before this existed: the host's
1085
+ * `CATALOG_LOAD_EXPECTATIONS` object is the only layer, which is a complete and
1086
+ * supported answer. What this probe buys is that the console can say "this
1087
+ * deployment's store cannot hold operator-set expectations" instead of offering
1088
+ * an editor whose save has nowhere to go.
1089
+ */
1090
+ export declare function supportsLoadExpectations(store: CatalogPipelineStore): store is CatalogPipelineStore & CatalogLoadExpectationStore;
855
1091
  export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Partial<CatalogStageStore> {
856
1092
  listConnectors(): Promise<CatalogConnector[]>;
857
1093
  getConnector(id: string): Promise<CatalogConnector | undefined>;
@@ -905,6 +1141,53 @@ export interface CatalogPipelineStore extends Partial<CatalogWorkflowStore>, Par
905
1141
  * that bound costs.
906
1142
  */
907
1143
  listTransformRevisions?(id: string): Promise<CatalogRevision[]>;
1144
+ /**
1145
+ * Per-type load expectations as an operator set them.
1146
+ *
1147
+ * **Optional**, and here more deliberately than anywhere else in this
1148
+ * interface. `@dudousxd/nestjs-catalog-store-mikro-orm` is not the only
1149
+ * implementation — a host may have written its own against an earlier shape of
1150
+ * this file — and every one of them satisfies `CatalogPipelineStore` today.
1151
+ * Widening it with four required members would turn all of them into compile
1152
+ * errors for a feature that is purely additive, and, worse, would do it
1153
+ * *silently* to the ones checked structurally: `isPipelineStore` and the
1154
+ * `supports*` probes narrow on methods, so a store that no longer satisfies
1155
+ * the interface is discovered by a caller, at run time, rather than by a build.
1156
+ * {@link supportsLoadExpectations} is how a caller asks, and a store that
1157
+ * implements none of these behaves exactly as it does today — the host's
1158
+ * `CATALOG_LOAD_EXPECTATIONS` object is then the whole policy.
1159
+ *
1160
+ * These hold rows; they do not resolve them. Precedence — a host's `byType`
1161
+ * entry over a stored row over the host's `default`, field by field — is the
1162
+ * pipeline package's business, beside the enforcement functions that consume
1163
+ * it, and it stays pure and synchronous. A store that resolved would be a
1164
+ * second place the precedence is decided, which for a policy whose whole point
1165
+ * is "somebody decided this" is the one duplication that cannot be tolerated.
1166
+ */
1167
+ listLoadExpectations?(): Promise<StoredLoadExpectation[]>;
1168
+ getLoadExpectation?(typeName: string): Promise<StoredLoadExpectation | undefined>;
1169
+ /**
1170
+ * Upsert, keyed by type name, recording the principal and the instant.
1171
+ *
1172
+ * `setBy` and `setByActor` are arguments rather than fields on the
1173
+ * `expectation` for the reason `startRun` records attribution the way it does:
1174
+ * a caller cannot claim them. `setAt` is not an input at all — a stored
1175
+ * timestamp a client could choose is not an audit record.
1176
+ *
1177
+ * `setByActor` is the person behind the principal when there was one. The
1178
+ * write route requires a human, so in practice there always is; it is
1179
+ * separate from `setBy` because a principal is a key and an actor is a
1180
+ * subject, and the trail needs the second to answer "who decided this".
1181
+ */
1182
+ saveLoadExpectation?(typeName: string, expectation: Pick<StoredLoadExpectation, 'deletes' | 'rowCount'>, setBy: string, setByActor?: string): Promise<StoredLoadExpectation>;
1183
+ /**
1184
+ * Drop the stored row for a type. The host's layer is untouched, so a type the
1185
+ * deployment declared in code keeps that declaration.
1186
+ *
1187
+ * `false` means there was nothing stored, which is a fact a caller may report
1188
+ * and never an error.
1189
+ */
1190
+ clearLoadExpectation?(typeName: string): Promise<boolean>;
908
1191
  startRun(input: {
909
1192
  connectorId: string;
910
1193
  snapshotId: string;
@@ -23,6 +23,7 @@ exports.isWorkflowEdge = isWorkflowEdge;
23
23
  exports.supportsWorkflows = supportsWorkflows;
24
24
  exports.supportsTransformRevisions = supportsTransformRevisions;
25
25
  exports.supportsWorkflowStages = supportsWorkflowStages;
26
+ exports.supportsLoadExpectations = supportsLoadExpectations;
26
27
  exports.isPipelineStore = isPipelineStore;
27
28
  /**
28
29
  * Where a connector pulls from.
@@ -729,6 +730,29 @@ function supportsTransformRevisions(store) {
729
730
  function supportsWorkflowStages(store) {
730
731
  return typeof store.writeStage === 'function' && typeof store.readStage === 'function';
731
732
  }
733
+ /**
734
+ * Whether an operator can set a load expectation on this deployment at all.
735
+ *
736
+ * The methods rather than a flag, the same argument as {@link supportsWorkflows}
737
+ * — and all four of them by name rather than one standing in for the rest, for
738
+ * that function's other reason: the write path and the read path are used at
739
+ * different moments, so a store with the getter and not the setter would narrow
740
+ * cleanly here and fail on the save, after the screen had already offered an
741
+ * editor.
742
+ *
743
+ * A store that has none of them is not broken and is not second-class. It
744
+ * behaves exactly as every store did before this existed: the host's
745
+ * `CATALOG_LOAD_EXPECTATIONS` object is the only layer, which is a complete and
746
+ * supported answer. What this probe buys is that the console can say "this
747
+ * deployment's store cannot hold operator-set expectations" instead of offering
748
+ * an editor whose save has nowhere to go.
749
+ */
750
+ function supportsLoadExpectations(store) {
751
+ return (typeof store.listLoadExpectations === 'function' &&
752
+ typeof store.getLoadExpectation === 'function' &&
753
+ typeof store.saveLoadExpectation === 'function' &&
754
+ typeof store.clearLoadExpectation === 'function');
755
+ }
732
756
  exports.CATALOG_PIPELINE_STORE = Symbol('CATALOG_PIPELINE_STORE');
733
757
  function isPipelineStore(store) {
734
758
  return (typeof store === 'object' &&
@@ -345,6 +345,31 @@ export interface CatalogMergeStore extends CatalogWriteStore {
345
345
  export declare const CATALOG_RESERVED_COLUMNS: readonly ["_snapshot_id", "_principal_id", "_loaded_at", "_batch", "_row"];
346
346
  export type CatalogReservedColumn = (typeof CATALOG_RESERVED_COLUMNS)[number];
347
347
  export declare function isReservedColumn(column: string): boolean;
348
+ /**
349
+ * Why a name cannot be written into SQL, in the words a publisher is given.
350
+ *
351
+ * One class for the whole ecosystem rather than one per adapter, so
352
+ * `instanceof` is a usable question across packages. The publish-time check in
353
+ * the pipeline package catches this to tell "that name cannot be an identifier"
354
+ * from "something else failed inside the store", and with a class per adapter
355
+ * that check would re-throw the moment the mounted store was not the one it
356
+ * imported — turning a 400 that names the property into a 500 that names
357
+ * nothing.
358
+ */
359
+ export declare class UnsafeIdentifierError extends Error {
360
+ constructor(value: string);
361
+ }
362
+ /** Whether a name can be written into SQL as it stands. */
363
+ export declare function isSafeIdentifier(value: string): boolean;
364
+ /**
365
+ * Refuse a name that cannot be a SQL identifier.
366
+ *
367
+ * Throws rather than answering, because the caller's next line writes the value
368
+ * into a statement: a boolean that can be ignored is a boolean that eventually
369
+ * is. {@link isSafeIdentifier} is there for the callers that are asking rather
370
+ * than about to build.
371
+ */
372
+ export declare function assertSafeIdentifier(value: string): void;
348
373
  /** One property, and the column it cannot have. */
349
374
  export interface CatalogColumnCollision {
350
375
  /** `reserved` — it lands on a store column. `shared` — two properties collide. */
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CATALOG_STORE = exports.CatalogColumnCollisionError = exports.CATALOG_RESERVED_COLUMNS = exports.CATALOG_SNAPSHOT_MODES = void 0;
3
+ exports.CATALOG_STORE = exports.CatalogColumnCollisionError = exports.UnsafeIdentifierError = exports.CATALOG_RESERVED_COLUMNS = exports.CATALOG_SNAPSHOT_MODES = void 0;
4
4
  exports.isCatalogStoreCapabilities = isCatalogStoreCapabilities;
5
5
  exports.isReservedColumn = isReservedColumn;
6
+ exports.isSafeIdentifier = isSafeIdentifier;
7
+ exports.assertSafeIdentifier = assertSafeIdentifier;
6
8
  exports.findColumnCollisions = findColumnCollisions;
7
9
  exports.assertNoColumnCollisions = assertNoColumnCollisions;
8
10
  exports.isWriteStore = isWriteStore;
@@ -72,6 +74,73 @@ exports.CATALOG_RESERVED_COLUMNS = [
72
74
  function isReservedColumn(column) {
73
75
  return exports.CATALOG_RESERVED_COLUMNS.some((reserved) => reserved === column.toLowerCase());
74
76
  }
77
+ /**
78
+ * What a name has to look like before a store will write it into SQL.
79
+ *
80
+ * Identifiers are *rejected*, never escaped and never sanitised. Every table
81
+ * and column name a store emits arrives from another application over HTTP and
82
+ * ends up in DDL and in SELECT lists, where no placeholder can stand in for it,
83
+ * so anything outside this character set never becomes SQL at all.
84
+ *
85
+ * Here, beside {@link CATALOG_RESERVED_COLUMNS}, for the same reason: it is
86
+ * part of what the catalog promises a *publisher*. Refuse a property name and
87
+ * the sentence explaining why is the only statement of the rule most people
88
+ * will ever read, so it belongs to the contract rather than to whichever
89
+ * adapter happens to be mounted.
90
+ *
91
+ * And for one more reason. It used to be two copies — `store-mikro-orm` and
92
+ * `store-clickhouse` each carried this pattern and this sentence, byte for
93
+ * byte — and the publish-time refusal in the pipeline package borrowed the
94
+ * MySQL one so that publish-time and DDL-time could not disagree about the
95
+ * character set, the length or the wording. That bought the guarantee for a
96
+ * MySQL deployment and left a ClickHouse-only one trusting two files to be
97
+ * edited together. One definition is the guarantee; two identical ones are a
98
+ * habit.
99
+ *
100
+ * 63 characters because it is under MySQL's 64-character ceiling and no engine
101
+ * a store here targets refuses a name that short, and because the number is
102
+ * quoted in the refusal below: a per-store limit would mean a publisher being
103
+ * told a different rule depending on what is mounted, for a name the catalog
104
+ * would then be unable to promise anything about across a fan-out.
105
+ *
106
+ * Not exported. A `RegExp` is mutable and shared state, and the two questions
107
+ * anyone has of it — "may I?" and "why not?" — are {@link isSafeIdentifier} and
108
+ * {@link UnsafeIdentifierError}.
109
+ */
110
+ const SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/;
111
+ /**
112
+ * Why a name cannot be written into SQL, in the words a publisher is given.
113
+ *
114
+ * One class for the whole ecosystem rather than one per adapter, so
115
+ * `instanceof` is a usable question across packages. The publish-time check in
116
+ * the pipeline package catches this to tell "that name cannot be an identifier"
117
+ * from "something else failed inside the store", and with a class per adapter
118
+ * that check would re-throw the moment the mounted store was not the one it
119
+ * imported — turning a 400 that names the property into a 500 that names
120
+ * nothing.
121
+ */
122
+ class UnsafeIdentifierError extends Error {
123
+ constructor(value) {
124
+ super(`Refusing to use "${value}" as a SQL identifier: letters, digits and underscore only, starting with a letter or underscore, 63 characters max.`);
125
+ }
126
+ }
127
+ exports.UnsafeIdentifierError = UnsafeIdentifierError;
128
+ /** Whether a name can be written into SQL as it stands. */
129
+ function isSafeIdentifier(value) {
130
+ return SAFE_IDENTIFIER.test(value);
131
+ }
132
+ /**
133
+ * Refuse a name that cannot be a SQL identifier.
134
+ *
135
+ * Throws rather than answering, because the caller's next line writes the value
136
+ * into a statement: a boolean that can be ignored is a boolean that eventually
137
+ * is. {@link isSafeIdentifier} is there for the callers that are asking rather
138
+ * than about to build.
139
+ */
140
+ function assertSafeIdentifier(value) {
141
+ if (!isSafeIdentifier(value))
142
+ throw new UnsafeIdentifierError(value);
143
+ }
75
144
  /**
76
145
  * Every way a type's properties would fight over a physical column.
77
146
  *
package/dist/client.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  * own UI. The endpoints alone are not an API; the endpoints plus the response
10
10
  * types are.
11
11
  */
12
+ import type { DeleteReconciliation, LoadExpectation, RowCountBound, StoredLoadExpectation } from './catalog.pipeline';
12
13
  export type { AuditQuery, CatalogAuditEvent, CatalogRevision, Dashboard, DashboardCard, QueryVisualization, SaveQueryInput, SavedQuery, } from './catalog.workspace';
13
14
  export { CATALOG_REVISION_LIMIT } from './catalog.workspace';
14
15
  export type { CatalogQueryRelation, CatalogQueryRequest, CatalogQueryResult, } from './catalog.query';
@@ -106,3 +107,111 @@ export { CONNECTOR_KINDS, isConnectorKind, isTransformLanguage, isWorkflowEdge,
106
107
  export { validateWorkflow, WORKFLOW_EXECUTION_MODES, WORKFLOW_ISSUE_CODES, WORKFLOW_NODE_ID_PATTERN, WORKFLOW_NODE_KINDS, WORKFLOW_STATUSES, workflowGraphHash, workflowRunOrder, isWorkflowExecutionMode, isWorkflowNodeKind, isWorkflowStatus, } from './catalog.pipeline';
107
108
  export type { WorkflowStatus } from './catalog.pipeline';
108
109
  export type { CatalogTrace, CatalogTraceList, CatalogTraceOutcome, CatalogTraceSpan, TraceQuery, } from './catalog.workspace';
110
+ export type { CatalogLoadExpectations, DeleteReconciliation, LoadExpectation, RowCountBound, StoredLoadExpectation, } from './catalog.pipeline';
111
+ /**
112
+ * The three answers to "how do deletions at the source reach this type", as a
113
+ * value.
114
+ *
115
+ * A list rather than only the union, because the editor on the Model screen has
116
+ * to offer them and a hand-written array in a component is the copy that drifts
117
+ * — the same argument {@link CONNECTOR_KINDS} makes one screen over. The server
118
+ * validates against this too: a `strategy` off the wire is `string` until
119
+ * something checks it, and the check and the dropdown reading one list is what
120
+ * stops the two from disagreeing about what is acceptable.
121
+ *
122
+ * **There are three and there is deliberately no fourth.** Tombstones off a
123
+ * change feed is the correct answer and needs machinery nothing here has; a
124
+ * strategy name that nothing implements is a dropdown with a lie in it. See
125
+ * `DeleteReconciliation` for the whole argument.
126
+ */
127
+ export declare const DELETE_RECONCILIATION_STRATEGIES: readonly ["accepted", "soft-deleted-at-source", "periodic-full-reload"];
128
+ export type DeleteReconciliationStrategy = (typeof DELETE_RECONCILIATION_STRATEGIES)[number];
129
+ /** Whether a value off the wire names one of the three. */
130
+ export declare function isDeleteReconciliationStrategy(value: unknown): value is DeleteReconciliationStrategy;
131
+ /**
132
+ * What an operator sends to set a type's expectation.
133
+ *
134
+ * Both fields optional and both meaning "leave this alone" when absent, which is
135
+ * why they are not simply `LoadExpectation`: a request that omits `rowCount`
136
+ * has said nothing about row counts, and reading that as "clear it" would let a
137
+ * form that only renders the delete strategy silently drop a bound somebody set.
138
+ * Clearing the whole stored row is `DELETE`, which is a decision with a verb on
139
+ * it.
140
+ */
141
+ export interface LoadExpectationInput {
142
+ deletes?: DeleteReconciliation;
143
+ rowCount?: Partial<RowCountBound>;
144
+ }
145
+ /**
146
+ * The resolved expectation for one type, and which layer won each field.
147
+ *
148
+ * The provenance is the whole reason this is not just a `LoadExpectation`. The
149
+ * policy is sourced from three layers — `host.byType[type]`, then the stored row
150
+ * an operator set, then `host.default` — and a screen that showed only the
151
+ * answer would let somebody edit a field this deployment has pinned in code and
152
+ * watch the edit vanish on the next read, with nothing anywhere saying why.
153
+ * {@link hostLocked} is what lets it say "this deployment fixed it" instead.
154
+ */
155
+ export interface ResolvedLoadExpectation {
156
+ typeName: string;
157
+ /** The three layers merged, field by field. What the load is actually judged against. */
158
+ resolved: LoadExpectation;
159
+ /**
160
+ * Which layer supplied the delete strategy.
161
+ *
162
+ * `'default'` means the host's house-wide `default` did. `'none'` means
163
+ * nothing did, anywhere — which is not a gap to be filled in silently: it is
164
+ * the state that refuses every incremental load of this type, and the one the
165
+ * screen most needs to name.
166
+ */
167
+ deletesFrom: 'host' | 'stored' | 'default' | 'none';
168
+ /**
169
+ * Which layer supplied the row-count bound — the strongest one that set any
170
+ * field of it, since the three are merged key by key rather than replaced
171
+ * whole.
172
+ *
173
+ * There is no `'none'`, and that is a statement rather than an omission: a
174
+ * bound always applies. Where no layer says anything the built-in
175
+ * `DEFAULT_ROW_COUNT_BOUND` does, and that is what `'default'` covers as well
176
+ * as the host's own `default`.
177
+ */
178
+ rowCountFrom: 'host' | 'stored' | 'default';
179
+ /** The operator's row, present whether or not it won anything. */
180
+ stored?: StoredLoadExpectation;
181
+ /**
182
+ * Which fields this deployment declared in code, per field.
183
+ *
184
+ * True means a stored value for that field would never apply, so the editor
185
+ * shows it disabled and says so. Only `host.byType[type]` locks: `host.default`
186
+ * is the weakest layer and an operator's row beats it, so a house-wide default
187
+ * is not a lock and must not be drawn as one.
188
+ */
189
+ hostLocked: {
190
+ deletes: boolean;
191
+ rowCount: boolean;
192
+ };
193
+ }
194
+ /**
195
+ * Where the per-type expectations sit, relative to wherever the pipeline
196
+ * controller was mounted.
197
+ *
198
+ * A function of the base path rather than a frozen object like
199
+ * {@link catalogRoutes}, and the difference is the one `routes.ts` in the React
200
+ * package draws: the catalog controller's paths cannot move, and these move with
201
+ * whatever `path` the host passed to `CatalogPipelineModule.forRoot` — the
202
+ * routes below are `<path>/pipeline/expectations`, and `/pipeline` is only the
203
+ * default.
204
+ *
205
+ * Two builders for four routes: `PUT` and `DELETE` address the same path a `GET`
206
+ * of one type does, and a builder per verb would be three names for one string.
207
+ */
208
+ export declare function pipelineExpectationRoutes(basePath?: string): {
209
+ /** Every stored row, plus the types the host has locked. `GET`, `catalog:read`. */
210
+ readonly expectations: () => string;
211
+ /**
212
+ * One type: `GET` for the resolved expectation and its provenance, `PUT` to
213
+ * set the stored row, `DELETE` to drop it. The writes need `catalog:curate`
214
+ * and a signed-in person.
215
+ */
216
+ readonly expectation: (typeName: string) => string;
217
+ };
package/dist/client.js CHANGED
@@ -11,7 +11,9 @@
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 = exports.CATALOG_REVISION_LIMIT = void 0;
14
+ exports.DELETE_RECONCILIATION_STRATEGIES = 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
+ exports.isDeleteReconciliationStrategy = isDeleteReconciliationStrategy;
16
+ exports.pipelineExpectationRoutes = pipelineExpectationRoutes;
15
17
  // A value, not a type: a screen saying how far back the history goes should read
16
18
  // the number rather than print one of its own. See its docblock for what the cap
17
19
  // costs.
@@ -113,3 +115,56 @@ Object.defineProperty(exports, "workflowRunOrder", { enumerable: true, get: func
113
115
  Object.defineProperty(exports, "isWorkflowExecutionMode", { enumerable: true, get: function () { return catalog_pipeline_2.isWorkflowExecutionMode; } });
114
116
  Object.defineProperty(exports, "isWorkflowNodeKind", { enumerable: true, get: function () { return catalog_pipeline_2.isWorkflowNodeKind; } });
115
117
  Object.defineProperty(exports, "isWorkflowStatus", { enumerable: true, get: function () { return catalog_pipeline_2.isWorkflowStatus; } });
118
+ /**
119
+ * The three answers to "how do deletions at the source reach this type", as a
120
+ * value.
121
+ *
122
+ * A list rather than only the union, because the editor on the Model screen has
123
+ * to offer them and a hand-written array in a component is the copy that drifts
124
+ * — the same argument {@link CONNECTOR_KINDS} makes one screen over. The server
125
+ * validates against this too: a `strategy` off the wire is `string` until
126
+ * something checks it, and the check and the dropdown reading one list is what
127
+ * stops the two from disagreeing about what is acceptable.
128
+ *
129
+ * **There are three and there is deliberately no fourth.** Tombstones off a
130
+ * change feed is the correct answer and needs machinery nothing here has; a
131
+ * strategy name that nothing implements is a dropdown with a lie in it. See
132
+ * `DeleteReconciliation` for the whole argument.
133
+ */
134
+ exports.DELETE_RECONCILIATION_STRATEGIES = [
135
+ 'accepted',
136
+ 'soft-deleted-at-source',
137
+ 'periodic-full-reload',
138
+ ];
139
+ /** Whether a value off the wire names one of the three. */
140
+ function isDeleteReconciliationStrategy(value) {
141
+ return (typeof value === 'string' &&
142
+ exports.DELETE_RECONCILIATION_STRATEGIES.includes(value));
143
+ }
144
+ /**
145
+ * Where the per-type expectations sit, relative to wherever the pipeline
146
+ * controller was mounted.
147
+ *
148
+ * A function of the base path rather than a frozen object like
149
+ * {@link catalogRoutes}, and the difference is the one `routes.ts` in the React
150
+ * package draws: the catalog controller's paths cannot move, and these move with
151
+ * whatever `path` the host passed to `CatalogPipelineModule.forRoot` — the
152
+ * routes below are `<path>/pipeline/expectations`, and `/pipeline` is only the
153
+ * default.
154
+ *
155
+ * Two builders for four routes: `PUT` and `DELETE` address the same path a `GET`
156
+ * of one type does, and a builder per verb would be three names for one string.
157
+ */
158
+ function pipelineExpectationRoutes(basePath = '/pipeline') {
159
+ const base = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
160
+ return {
161
+ /** Every stored row, plus the types the host has locked. `GET`, `catalog:read`. */
162
+ expectations: () => `${base}/expectations`,
163
+ /**
164
+ * One type: `GET` for the resolved expectation and its provenance, `PUT` to
165
+ * set the stored row, `DELETE` to drop it. The writes need `catalog:curate`
166
+ * and a signed-in person.
167
+ */
168
+ expectation: (typeName) => `${base}/expectations/${encodeURIComponent(typeName)}`,
169
+ };
170
+ }
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ 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, 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';
11
+ export { CATALOG_PIPELINE_STORE, type CatalogConnection, type CatalogConnector, type ConnectionCheck, CONNECTOR_KINDS, type CatalogLoadExpectations, type CatalogLoadExpectationStore, type CatalogPipelineStore, type CatalogStageStore, type CatalogTransform, type CatalogWorkflow, type CatalogWorkflowCapabilities, type CatalogWorkflowStore, type ConnectorKind, type ConnectorRun, type DeleteReconciliation, isConnectorKind, isPipelineStore, isTransformLanguage, type LoadExpectation, type RowCountBound, type StoredLoadExpectation, supportsLoadExpectations, 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';
@@ -18,7 +18,7 @@ export type { CatalogSearchField, CatalogSearchHit, CatalogSearchKind, CatalogSe
18
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
- 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';
21
+ export { assertNoColumnCollisions, assertSafeIdentifier, 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, isSafeIdentifier, isWriteStore, type SnapshotRef, supportsCarryForward, UnsafeIdentifierError, } from './catalog.store';
22
22
  export { MikroOrmReadStore } from './stores/mikro-orm-read.store';
23
23
  export type { CatalogGraph, CatalogObjectPage, CatalogObjectQuery, CatalogObjectTypeDef, CatalogOverlay, CatalogPropertyDef, CatalogRelationDef, CatalogSnapshot, RelationKind, ScalarType, } from './catalog.types';
24
24
  export { REQUIRED_SCOPES, REQUIRES_HUMAN, RequireHuman, RequireScopes, } from './catalog.route-auth';
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.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;
17
+ 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.supportsLoadExpectations = 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.UnsafeIdentifierError = exports.supportsCarryForward = exports.isWriteStore = exports.isSafeIdentifier = exports.isReservedColumn = exports.isCatalogStoreCapabilities = exports.findColumnCollisions = exports.CatalogColumnCollisionError = exports.CATALOG_STORE = exports.CATALOG_SNAPSHOT_MODES = exports.CATALOG_RESERVED_COLUMNS = exports.assertSafeIdentifier = 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 = exports.bestMatch = 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, "supportsLoadExpectations", { enumerable: true, get: function () { return catalog_pipeline_1.supportsLoadExpectations; } });
62
63
  Object.defineProperty(exports, "supportsTransformRevisions", { enumerable: true, get: function () { return catalog_pipeline_1.supportsTransformRevisions; } });
63
64
  Object.defineProperty(exports, "isWorkflowEdge", { enumerable: true, get: function () { return catalog_pipeline_1.isWorkflowEdge; } });
64
65
  Object.defineProperty(exports, "isWorkflowExecutionMode", { enumerable: true, get: function () { return catalog_pipeline_1.isWorkflowExecutionMode; } });
@@ -133,6 +134,7 @@ Object.defineProperty(exports, "StaticKeyPrincipalResolver", { enumerable: true,
133
134
  __exportStar(require("./catalog.access"), exports);
134
135
  var catalog_store_1 = require("./catalog.store");
135
136
  Object.defineProperty(exports, "assertNoColumnCollisions", { enumerable: true, get: function () { return catalog_store_1.assertNoColumnCollisions; } });
137
+ Object.defineProperty(exports, "assertSafeIdentifier", { enumerable: true, get: function () { return catalog_store_1.assertSafeIdentifier; } });
136
138
  Object.defineProperty(exports, "CATALOG_RESERVED_COLUMNS", { enumerable: true, get: function () { return catalog_store_1.CATALOG_RESERVED_COLUMNS; } });
137
139
  Object.defineProperty(exports, "CATALOG_SNAPSHOT_MODES", { enumerable: true, get: function () { return catalog_store_1.CATALOG_SNAPSHOT_MODES; } });
138
140
  Object.defineProperty(exports, "CATALOG_STORE", { enumerable: true, get: function () { return catalog_store_1.CATALOG_STORE; } });
@@ -140,8 +142,10 @@ Object.defineProperty(exports, "CatalogColumnCollisionError", { enumerable: true
140
142
  Object.defineProperty(exports, "findColumnCollisions", { enumerable: true, get: function () { return catalog_store_1.findColumnCollisions; } });
141
143
  Object.defineProperty(exports, "isCatalogStoreCapabilities", { enumerable: true, get: function () { return catalog_store_1.isCatalogStoreCapabilities; } });
142
144
  Object.defineProperty(exports, "isReservedColumn", { enumerable: true, get: function () { return catalog_store_1.isReservedColumn; } });
145
+ Object.defineProperty(exports, "isSafeIdentifier", { enumerable: true, get: function () { return catalog_store_1.isSafeIdentifier; } });
143
146
  Object.defineProperty(exports, "isWriteStore", { enumerable: true, get: function () { return catalog_store_1.isWriteStore; } });
144
147
  Object.defineProperty(exports, "supportsCarryForward", { enumerable: true, get: function () { return catalog_store_1.supportsCarryForward; } });
148
+ Object.defineProperty(exports, "UnsafeIdentifierError", { enumerable: true, get: function () { return catalog_store_1.UnsafeIdentifierError; } });
145
149
  var mikro_orm_read_store_1 = require("./stores/mikro-orm-read.store");
146
150
  Object.defineProperty(exports, "MikroOrmReadStore", { enumerable: true, get: function () { return mikro_orm_read_store_1.MikroOrmReadStore; } });
147
151
  var catalog_route_auth_1 = require("./catalog.route-auth");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-catalog",
3
- "version": "0.12.0",
3
+ "version": "0.13.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",