@dudousxd/nestjs-catalog 0.29.0 → 0.31.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.
@@ -3872,14 +3872,36 @@ export declare function readWorkflowCallOutput(value: unknown): WorkflowCallOutp
3872
3872
  */
3873
3873
  export interface CallableWorkflowRef {
3874
3874
  name: string;
3875
+ /**
3876
+ * How strong the fleet's claim about this entry is, mirroring durable core's
3877
+ * `AnnouncementEvidence` rather than re-deriving it.
3878
+ *
3879
+ * - `'declared'` — a live worker published a descriptor naming this workflow,
3880
+ * so everything populated here was stated by that worker.
3881
+ * - `'observed'` — nobody described it. What exists is a live routing token of
3882
+ * this name, which is exactly the condition convention routing uses, so the
3883
+ * name IS reachable — and nothing else is known: no version, no origin, no
3884
+ * runtime, and no assurance the token serves a workflow rather than a step
3885
+ * handler of the same name. A live queue, not a promise.
3886
+ *
3887
+ * Carried because the two tiers answer a question a picker cannot otherwise
3888
+ * put: **will a pin on this name mean anything?** An `'observed'` entry never
3889
+ * declares a version, so a version typed against it is compared, at run time,
3890
+ * against the engine's routing default — the run comes back tagged
3891
+ * `version:undeclared` and `checkCall` reports the pin as unverified rather
3892
+ * than kept. Absent when the deployment answering predates the distinction.
3893
+ */
3894
+ evidence?: 'declared' | 'observed';
3875
3895
  /**
3876
3896
  * The version to pin, and **absent is a real answer**.
3877
3897
  *
3878
3898
  * A worker that has not been upgraded announces a bare name with no version
3879
- * and no group. Silence is not a claim, so no version is invented for it from
3880
- * another announcer's and an entry without one cannot satisfy the pin, which
3881
- * is why {@link callableWorkflowBlock} refuses it rather than letting a picker
3882
- * offer a name that would run whatever is newest on the day it runs.
3899
+ * and no group or announces nothing at all and is here on its heartbeat
3900
+ * alone ({@link evidence} `'observed'`). Silence is not a claim, so no version
3901
+ * is invented for it from another announcer's and an entry without one
3902
+ * cannot satisfy the pin, which is why {@link callableWorkflowBlock} refuses
3903
+ * it rather than letting a picker offer a name that would run whatever is
3904
+ * newest on the day it runs.
3883
3905
  */
3884
3906
  version?: string;
3885
3907
  /** What it does, if the deployment publishes one. Shown beside the name. */
@@ -3931,11 +3953,17 @@ export interface CallableWorkflowDisagreement {
3931
3953
  * Two refusals, and they are refusals rather than warnings because in both cases
3932
3954
  * committing the entry would write a node whose meaning nobody can state:
3933
3955
  *
3934
- * - `no-version` — an un-upgraded worker announced a bare name. The call node's
3935
- * whole point is the pin; a node holding a name and no version follows
3936
- * whatever gets deployed next, which is the failure the version field exists
3937
- * to prevent. The name is still perfectly typeable by hand *with* a version
3938
- * the author knows, so this refuses the one-click commit and not the workflow.
3956
+ * - `no-version` — nothing live states a version for this name, either because an
3957
+ * un-upgraded worker announced a bare name or because nobody described it at
3958
+ * all and it is here on its heartbeat ({@link CallableWorkflowRef.evidence}
3959
+ * `'observed'`). The call node's whole point is the pin; a node holding a name
3960
+ * and no version follows whatever gets deployed next, which is the failure the
3961
+ * version field exists to prevent. The name is still perfectly typeable by hand
3962
+ * *with* a version the author knows — and the message says what that costs,
3963
+ * because a version typed against a callee that declares none is compared at run
3964
+ * time against the engine's routing default and comes back reported as
3965
+ * unverified, not as kept. That is the one thing an author choosing a pin from
3966
+ * this list could not otherwise find out until a load ran.
3939
3967
  * - `ambiguous-group` — two live workers claim this exact `name@version` from
3940
3968
  * different groups. Two groups means two queues, and nothing here can know
3941
3969
  * which one a run would land on, so the two bodies may not even be the same
@@ -2381,9 +2381,16 @@ function describeCount(value) {
2381
2381
  function callableWorkflowBlock(ref) {
2382
2382
  const version = typeof ref.version === 'string' ? ref.version.trim() : '';
2383
2383
  if (version.length === 0) {
2384
+ // The two tiers differ in what they can even be asked, so they get their own
2385
+ // sentence rather than one that fits neither: an `'observed'` entry has no
2386
+ // descriptor at all behind it, and calling that "not upgraded to publish its
2387
+ // registrations in full" would overstate what was seen.
2388
+ const seen = ref.evidence === 'observed'
2389
+ ? `Nothing describes "${ref.name}" — it is here because a live queue of that name exists, which is what an engine routes a call on and all it knows. No version, no origin and no runtime were stated, and not even that the queue serves a workflow rather than a step handler of the same name.`
2390
+ : `A live worker announces "${ref.name}" without saying which version it runs, which is what a worker announces before it has been upgraded to publish its registrations in full.`;
2384
2391
  return {
2385
2392
  code: 'no-version',
2386
- message: `A live worker announces "${ref.name}" without saying which version it runs, which is what a worker announces before it has been upgraded to publish its registrations in full. A name with no version cannot be pinned, so this cannot be chosen type the name and the version you mean.`,
2393
+ message: `${seen} A name with no version cannot be pinned, so this cannot be chosen type the name and the version you mean. Be aware of what that pin is worth: with nothing declaring a version, a run of it carries the engine's routing default and the load reports the pin as unverified rather than kept, until the worker serving it publishes one.`,
2387
2394
  };
2388
2395
  }
2389
2396
  const groups = ref.disagreements?.find((entry) => entry.axis === 'group')?.values ?? [];
@@ -9,6 +9,72 @@ import type { CatalogObjectQuery, CatalogObjectTypeDef } from './catalog.types';
9
9
  * the application's own tables, a separate warehouse schema, or a column store,
10
10
  * without the screens above it changing.
11
11
  */
12
+ /**
13
+ * Where a snapshot's rows have been copied to, outside the database.
14
+ *
15
+ * **Vocabulary only — this package writes none of these and reads none of them.**
16
+ * It is declared here because it belongs on {@link SnapshotRef}, and a
17
+ * `SnapshotRef` is what every screen and every caller already holds. The
18
+ * machinery that produces an archive lives in
19
+ * `@dudousxd/nestjs-catalog-pipeline`, which is the package already allowed to
20
+ * know what a bucket is; the catalog itself stays unable to name one.
21
+ *
22
+ * ## What its presence means, and what it does not
23
+ *
24
+ * Present means *a verified copy of this snapshot exists at `path`*. It says
25
+ * nothing about whether the rows are still in the database — those are two
26
+ * independent facts and a caller that conflates them gets the dangerous reading
27
+ * in both directions. The three states a console has to tell apart are:
28
+ *
29
+ * - **no `archive`** — the snapshot is in the database and nowhere else.
30
+ * - **`archive` present, rows still in the table** — copied, not moved. Reads
31
+ * are unchanged and cost what they always did.
32
+ * - **`archive` present, rows gone** — the snapshot lives in object storage.
33
+ * Still readable, still identified, and **not** the same price as a read of a
34
+ * hot snapshot. {@link bytes} is here so a screen can say which it is about
35
+ * to do rather than presenting the two as one click.
36
+ *
37
+ * A snapshot with no `SnapshotRef` at all is the fourth state, and it is the
38
+ * only one that means *gone*.
39
+ */
40
+ export interface SnapshotArchiveRef {
41
+ /** The only format written today. Named rather than assumed, so a second one can arrive. */
42
+ format: 'parquet';
43
+ /**
44
+ * The disk the bytes went to, when a host named one.
45
+ *
46
+ * Absent means `path` is resolved by whatever wrote it — a filesystem path in
47
+ * a test, a bucket a host configured. The disk is recorded rather than
48
+ * inferred because a deployment may mount several and "which one" is not
49
+ * recoverable from the path.
50
+ */
51
+ disk?: string;
52
+ /** The directory holding this snapshot's parts and its manifest, without a trailing slash. */
53
+ path: string;
54
+ /** Rows in the archive. Compared against the snapshot's own count when it was written. */
55
+ rowCount: number;
56
+ /** Size on the far end, for a screen that has to say what a read will cost. */
57
+ bytes: number;
58
+ /**
59
+ * SHA-256 over the row stream in `_row` order, as the manifest records it.
60
+ *
61
+ * A row count catches a truncated archive and nothing else: an archive with
62
+ * every row present and one value corrupted has exactly the right count. This
63
+ * is the check that fails for that, and it is only meaningful because the
64
+ * stream is ordered — see the store's `streamSnapshot`.
65
+ */
66
+ checksum: string;
67
+ writtenAt: string;
68
+ /**
69
+ * When the archive was last read back and found to match.
70
+ *
71
+ * Separate from {@link writtenAt} because they answer different questions and
72
+ * a caller about to delete rows needs the second one. Absent means written but
73
+ * not confirmed readable, which is exactly the state in which nothing may be
74
+ * deleted.
75
+ */
76
+ verifiedAt?: string;
77
+ }
12
78
  /** A point-in-time view of one object type. */
13
79
  export interface SnapshotRef {
14
80
  /**
@@ -27,6 +93,14 @@ export interface SnapshotRef {
27
93
  principalId: string;
28
94
  /** Free-form provenance: which base, which file, which workflow run. */
29
95
  labels?: Record<string, string>;
96
+ /**
97
+ * Where this snapshot's rows have been copied to, if anywhere.
98
+ *
99
+ * Optional, and absent is the answer for every snapshot in every deployment
100
+ * that archives nothing — which is all of them until a host asks. See
101
+ * {@link SnapshotArchiveRef} for the three states its presence distinguishes.
102
+ */
103
+ archive?: SnapshotArchiveRef;
30
104
  }
31
105
  /**
32
106
  * How a store holds history. One list, so nothing narrows a stored or
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ export { type AuditQuery, CATALOG_REVISION_LIMIT, CATALOG_TRACE_OUTCOMES, CATALO
23
23
  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';
24
24
  export * from './catalog.access';
25
25
  export * from './catalog.filters';
26
- export { assertNoColumnCollisions, assertSafeIdentifier, CATALOG_RESERVED_COLUMNS, CATALOG_SNAPSHOT_MODES, CATALOG_STORE, type CarryForwardResult, type CatalogColumnCollision, CatalogColumnCollisionError, type CatalogFilteringReadStore, supportsObjectFilters, type CatalogMergeStore, type CatalogReadQuery, type CatalogReadResult, type CatalogReadStore, type CatalogReservedColumn, type CatalogSnapshotMode, type CatalogSnapshotStreamStore, type CatalogStoreCapabilities, type CatalogWriteStore, type ColumnCollisionOptions, findColumnCollisions, isCatalogStoreCapabilities, isReservedColumn, isSafeIdentifier, isWriteStore, outputAlias, physicalColumn, type SnapshotRef, supportsCarryForward, supportsSnapshotStreams, UnsafeIdentifierError, } from './catalog.store';
26
+ export { assertNoColumnCollisions, assertSafeIdentifier, CATALOG_RESERVED_COLUMNS, CATALOG_SNAPSHOT_MODES, CATALOG_STORE, type CarryForwardResult, type CatalogColumnCollision, CatalogColumnCollisionError, type CatalogFilteringReadStore, supportsObjectFilters, type CatalogMergeStore, type CatalogReadQuery, type CatalogReadResult, type CatalogReadStore, type CatalogReservedColumn, type CatalogSnapshotMode, type CatalogSnapshotStreamStore, type CatalogStoreCapabilities, type CatalogWriteStore, type ColumnCollisionOptions, findColumnCollisions, isCatalogStoreCapabilities, isReservedColumn, isSafeIdentifier, isWriteStore, outputAlias, physicalColumn, type SnapshotArchiveRef, type SnapshotRef, supportsCarryForward, supportsSnapshotStreams, UnsafeIdentifierError, } from './catalog.store';
27
27
  export { MikroOrmReadStore } from './stores/mikro-orm-read.store';
28
28
  export type { CatalogGraph, CatalogObjectPage, CatalogObjectQuery, CatalogObjectTypeDef, CatalogOverlay, CatalogPropertyDef, CatalogRelationDef, CatalogSnapshot, RelationKind, ScalarType, } from './catalog.types';
29
29
  export { isRelationKind, RELATION_KINDS } from './catalog.types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-catalog",
3
- "version": "0.29.0",
3
+ "version": "0.31.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",