@danceroutine/tango-schema 1.7.0 → 1.8.1

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.
@@ -0,0 +1,6 @@
1
+ export declare const InternalReferentialAction: {
2
+ readonly CASCADE: "CASCADE";
3
+ readonly SET_NULL: "SET NULL";
4
+ readonly RESTRICT: "RESTRICT";
5
+ readonly NO_ACTION: "NO ACTION";
6
+ };
package/dist/index.d.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  export * as domain from './domain/index';
6
6
  export * as model from './model/index';
7
7
  export type { DeleteReferentialAction, Field, FieldType, IndexDef, ModelHookModel, ModelKeyOf, ModelAugmentations, ModelMetadata, PersistedModelOutput, ModelWriteHookManager, ModelWriteHooks, BeforeCreateHookArgs, AfterCreateHookArgs, BeforeUpdateHookArgs, AfterUpdateHookArgs, BeforeDeleteHookArgs, AfterDeleteHookArgs, BeforeBulkCreateHookArgs, AfterBulkCreateHookArgs, RelationDef, RelationType, UpdateReferentialAction, } from './domain/index';
8
- export { Model, RelationBuilder, ModelRegistry, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ResolvedRelationGraphArtifactFactory, registerModelAugmentor, Constraints, Decorators, Indexes, Meta, c, i, m, t, type ModelDefinition, type ForeignKeyDecoratorConfig, type FieldDecoratorBuilder, type DecoratedFieldKind, type ManyToManyDecoratorConfig, type ModelRef, type ModelRefTarget, type GeneratedRelationRegistryArtifact, type OneToOneDecoratorConfig, type RelationDecoratedSchema, type ResolvedRelationGraphSnapshot, type ResolvedRelationGraphSnapshotModel, type ResolvedRelationGraphSnapshotRelation, type TypedModelRef, } from './model/index';
8
+ export { Model, RelationBuilder, ModelRegistry, ImplicitManyToManyIdentifier, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ResolvedRelationGraphArtifactFactory, registerModelAugmentor, Constraints, Decorators, Indexes, Meta, c, i, m, t, type ModelDefinition, type ForeignKeyDecoratorConfig, type FieldDecoratorBuilder, type DecoratedFieldKind, type ManyToManyDecoratorConfig, type ModelRef, type ModelRefTarget, type GeneratedRelationRegistryArtifact, type OneToOneDecoratorConfig, type RelationDecoratedSchema, type ResolvedRelationGraphSnapshot, type ResolvedRelationGraphSnapshotModel, type ResolvedRelationGraphSnapshotRelation, type TypedModelRef, } from './model/index';
9
9
  export { createSchemaModuleAliases, resolveSchemaModuleEntrypoint } from './resolveSchemaModuleEntrypoint';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, Indexes, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, createSchemaModuleAliases, model_exports, registerModelAugmentor, resolveSchemaModuleEntrypoint } from "./model-BxkSqwrt.js";
1
+ import { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ImplicitManyToManyIdentifier, Indexes, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, createSchemaModuleAliases, model_exports, registerModelAugmentor, resolveSchemaModuleEntrypoint } from "./model-upj6jxaK.js";
2
2
  import { domain_exports } from "./domain-Cufz6y1q.js";
3
3
 
4
- export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, Indexes, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, Constraints as c, createSchemaModuleAliases, domain_exports as domain, Indexes as i, Meta as m, model_exports as model, registerModelAugmentor, resolveSchemaModuleEntrypoint, Decorators as t };
4
+ export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ImplicitManyToManyIdentifier, Indexes, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, Constraints as c, createSchemaModuleAliases, domain_exports as domain, Indexes as i, Meta as m, model_exports as model, registerModelAugmentor, resolveSchemaModuleEntrypoint, Decorators as t };
@@ -1,5 +1,6 @@
1
1
  import type { ZodTypeAny } from './ZodTypeAny';
2
2
  import type { ReferentialOptions } from './TangoFieldMeta';
3
+ import type { ModelRef } from './ModelRef';
3
4
  /**
4
5
  * Config object for `t.foreignKey(...)`.
5
6
  *
@@ -32,4 +33,7 @@ export interface ManyToManyDecoratorConfig<TField extends ZodTypeAny = ZodTypeAn
32
33
  field?: TField;
33
34
  name?: string;
34
35
  relatedName?: never;
36
+ through?: ModelRef;
37
+ throughSourceFieldName?: string;
38
+ throughTargetFieldName?: string;
35
39
  }
@@ -1,30 +1,89 @@
1
1
  import type { DeleteReferentialAction, UpdateReferentialAction } from '../../../domain';
2
2
  import type { DecoratedFieldKind } from './DecoratedFieldKind';
3
3
  import type { ModelRef } from './ModelRef';
4
+ /**
5
+ * Options attached to a {@link TangoFieldMeta.references} block for foreign-key-style fields.
6
+ */
4
7
  export interface ReferentialOptions {
8
+ /**
9
+ * Column on the **referenced** model that this link targets. When omitted, the target model’s
10
+ * primary key is used.
11
+ */
5
12
  column?: string;
13
+ /** `ON DELETE` action for the underlying foreign key constraint. */
6
14
  onDelete?: DeleteReferentialAction;
15
+ /** `ON UPDATE` action for the underlying foreign key constraint. */
7
16
  onUpdate?: UpdateReferentialAction;
8
17
  }
18
+ /**
19
+ * Field-level metadata attached to a Zod schema via decorators. Migrations and relation wiring
20
+ * read this shape when inferring columns, constraints, and graph edges.
21
+ */
9
22
  export interface TangoFieldMeta {
23
+ /** Marks the backing column as the table primary key. */
10
24
  primaryKey?: boolean;
25
+ /** Adds a uniqueness constraint at the database layer. */
11
26
  unique?: boolean;
27
+ /** `NOT NULL` for the stored column when applicable. */
12
28
  notNull?: boolean;
29
+ /** Creates a btree index on the stored column. */
13
30
  dbIndex?: boolean;
31
+ /**
32
+ * Database column name. When set, migrations and DDL use this instead of the object property
33
+ * name.
34
+ */
14
35
  dbColumn?: string;
36
+ /**
37
+ * Application-level default used by schema inference and validation (`DEFAULT` clause is
38
+ * derived separately unless {@link dbDefault} is set).
39
+ */
15
40
  default?: string | {
16
41
  now: true;
17
42
  } | null;
43
+ /** Raw SQL expression for the column’s `DEFAULT` in generated DDL. */
18
44
  dbDefault?: string;
45
+ /** Human-oriented description for forms, admin UIs, and generated docs. */
19
46
  helpText?: string;
47
+ /** Enumerated allowed values when the field is presented as a choice list. */
20
48
  choices?: readonly unknown[];
49
+ /** Runtime validators applied when coercing or checking incoming values. */
21
50
  validators?: readonly ((value: unknown) => unknown)[];
51
+ /** Map of validator key to user-facing error message overrides. */
22
52
  errorMessages?: Record<string, string>;
53
+ /**
54
+ * Declares how this field relates to another model: plain FK (`target`), optional explicit
55
+ * many-to-many through model (`through`), and the through-side field names that pair the source
56
+ * and target endpoints.
57
+ */
23
58
  references?: {
59
+ /** Symbolic reference to the related model (same resolution rules as elsewhere in schema). */
24
60
  target: ModelRef;
25
61
  options?: ReferentialOptions;
62
+ /** Explicit join model for a many-to-many when not using an implicit through table. */
63
+ through?: ModelRef;
64
+ /**
65
+ * Schema field key on the through model that holds the foreign key **from** this model’s
66
+ * side of the association.
67
+ */
68
+ throughSourceFieldName?: string;
69
+ /**
70
+ * Schema field key on the through model that holds the foreign key **to** the related
71
+ * model’s side of the association.
72
+ */
73
+ throughTargetFieldName?: string;
26
74
  };
75
+ /**
76
+ * Classifies decorated relation endpoints (`foreignKey`, `oneToOne`, `manyToMany`) for
77
+ * normalization and graph construction.
78
+ */
27
79
  relationKind?: DecoratedFieldKind;
80
+ /**
81
+ * Preferred name for the forward edge from this model toward {@link references.target}
82
+ * (overrides inferred naming).
83
+ */
28
84
  forwardName?: string;
85
+ /**
86
+ * Preferred name for the reverse edge back from the related model (overrides inferred naming).
87
+ */
29
88
  reverseName?: string;
30
89
  }
@@ -12,6 +12,7 @@ export * as registry from './registry/index';
12
12
  export * as relations from './relations/index';
13
13
  export type { ModelDefinition } from './ModelDefinition';
14
14
  export { RelationBuilder } from './relations/index';
15
+ export { ImplicitManyToManyIdentifier } from './relations/ImplicitManyToManyIdentifier';
15
16
  export { Model } from './Model';
16
17
  export { registerModelAugmentor } from './ModelAugmentorRegistry';
17
18
  export { Decorators, t } from './decorators/index';
@@ -1,3 +1,3 @@
1
- import { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, constraints_exports, createSchemaModuleAliases, createTypedModelRef, decorators_exports, isTypedModelRef, meta_exports, registerModelAugmentor, registry_exports, relations_exports, resolveSchemaModuleEntrypoint } from "../model-BxkSqwrt.js";
1
+ import { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ImplicitManyToManyIdentifier, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, constraints_exports, createSchemaModuleAliases, createTypedModelRef, decorators_exports, isTypedModelRef, meta_exports, registerModelAugmentor, registry_exports, relations_exports, resolveSchemaModuleEntrypoint } from "../model-upj6jxaK.js";
2
2
 
3
- export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, Constraints as c, constraints_exports as constraints, createSchemaModuleAliases, createTypedModelRef, decorators_exports as decorators, Indexes as i, isTypedModelRef, Meta as m, meta_exports as meta, registerModelAugmentor, registry_exports as registry, relations_exports as relations, resolveSchemaModuleEntrypoint, Decorators as t };
3
+ export { Constraints, Decorators, GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_METADATA_VERSION, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ImplicitManyToManyIdentifier, Indexes, InternalDecoratedFieldKind, Meta, Model, ModelRegistry, RelationBuilder, ResolvedRelationGraphArtifactFactory, Constraints as c, constraints_exports as constraints, createSchemaModuleAliases, createTypedModelRef, decorators_exports as decorators, Indexes as i, isTypedModelRef, Meta as m, meta_exports as meta, registerModelAugmentor, registry_exports as registry, relations_exports as relations, resolveSchemaModuleEntrypoint, Decorators as t };
@@ -118,6 +118,7 @@ export declare class ModelRegistry {
118
118
  */
119
119
  values(): readonly Model[];
120
120
  private bumpVersion;
121
+ private stripImplicitManyToManyModels;
121
122
  private freezeFields;
122
123
  private inferPrimaryKeyName;
123
124
  private mergeStorageFields;
@@ -1,26 +1,110 @@
1
1
  import type { RelationCardinality, RelationPublicKind, RelationStorageStrategy } from '../relations/RelationSpec';
2
+ /**
3
+ * Serializable snapshot of a single relation edge from the resolved relation
4
+ * graph. Snapshots are the stable, build-time wire format consumed by codegen
5
+ * and by tooling that compares the graph across runs.
6
+ *
7
+ * A snapshot row captures both the abstract edge identity (who connects to
8
+ * whom, under what public name) and the physical storage details (local
9
+ * fields, join columns, through-model coordinates) needed to translate the
10
+ * edge into SQL later.
11
+ */
2
12
  export type ResolvedRelationGraphSnapshotRelation = {
13
+ /**
14
+ * Stable identifier for this edge within the graph. Used to cross-reference
15
+ * an edge with its inverse and to detect drift between snapshots.
16
+ */
3
17
  edgeId: string;
18
+ /** Model key of the endpoint that owns this edge in the resolved graph. */
4
19
  sourceModelKey: string;
20
+ /** Model key of the endpoint this edge points at. */
5
21
  targetModelKey: string;
22
+ /**
23
+ * Public relation name exposed to application code and query builders
24
+ * (for example `author` or `tags`).
25
+ */
6
26
  name: string;
27
+ /**
28
+ * Identifier of the paired edge on the opposite endpoint, when one exists.
29
+ * Bidirectional relations populate this; one-way edges leave it unset.
30
+ */
7
31
  inverseEdgeId?: string;
32
+ /**
33
+ * Public relation kind (`belongsTo`, `hasMany`, `manyToMany`, ...).
34
+ */
8
35
  kind: RelationPublicKind;
36
+ /**
37
+ * How the edge is physically stored. Distinguishes direct foreign-key
38
+ * references from reverse references and join-table backed relations.
39
+ */
9
40
  storageStrategy: RelationStorageStrategy;
41
+ /** Whether the edge yields one target (`single`) or many (`many`). */
10
42
  cardinality: RelationCardinality;
43
+ /**
44
+ * Owner-side column that stores the foreign key, when the edge is backed
45
+ * by a local reference column.
46
+ */
11
47
  localFieldName?: string;
48
+ /**
49
+ * Target-side column the foreign key resolves against, when applicable.
50
+ */
12
51
  targetFieldName?: string;
52
+ /**
53
+ * Model key of the synthesized or user-provided through model for
54
+ * many-to-many edges.
55
+ */
56
+ throughModelKey?: string;
57
+ /** Physical join-table name for many-to-many edges. */
58
+ throughTable?: string;
59
+ /** Through-model schema field on the source side, if resolved. */
60
+ throughSourceFieldName?: string;
61
+ /** Through-model schema field on the target side, if resolved. */
62
+ throughTargetFieldName?: string;
63
+ /**
64
+ * Physical join-table column that stores the owner-side primary key for
65
+ * many-to-many edges.
66
+ */
67
+ throughSourceKey?: string;
68
+ /**
69
+ * Physical join-table column that stores the target-side primary key for
70
+ * many-to-many edges.
71
+ */
72
+ throughTargetKey?: string;
73
+ /**
74
+ * Alias used when the edge participates in SQL joins. Derived
75
+ * deterministically so compiled SQL is stable across runs.
76
+ */
13
77
  alias: string;
78
+ /**
79
+ * What this edge is allowed to participate in. The builder uses these
80
+ * flags to decide whether migrations emit DDL, queries can select over
81
+ * the edge, and hydration can populate the related attribute.
82
+ */
14
83
  capabilities: {
84
+ /** Whether migration tooling should emit DDL for this edge. */
15
85
  migratable: boolean;
86
+ /** Whether the edge can appear as a hop in a query path. */
16
87
  queryable: boolean;
88
+ /** Whether hydrators can populate the related attribute on a row. */
17
89
  hydratable: boolean;
18
90
  };
19
91
  };
92
+ /**
93
+ * Snapshot of one model's relation edges. Grouping edges by source model
94
+ * lets codegen walk the graph model-by-model without having to re-index.
95
+ */
20
96
  export type ResolvedRelationGraphSnapshotModel = {
97
+ /** Model key these relations source from. */
21
98
  key: string;
99
+ /** Resolved outgoing relation edges in registration order. */
22
100
  relations: ResolvedRelationGraphSnapshotRelation[];
23
101
  };
102
+ /**
103
+ * Top-level serializable snapshot of the resolved relation graph for a
104
+ * registry. Written to disk by codegen and diffed across runs to detect
105
+ * registry drift.
106
+ */
24
107
  export type ResolvedRelationGraphSnapshot = {
108
+ /** Per-model relation snapshots in registration order. */
25
109
  models: ResolvedRelationGraphSnapshotModel[];
26
110
  };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Single source of truth for identity of Tango-synthesized many-to-many
3
+ * through models. Other parts of the schema package interact with synthesized
4
+ * models exclusively through this class so they do not have to know the
5
+ * namespace or digest scheme those keys encode.
6
+ */
7
+ export declare class ImplicitManyToManyIdentifier {
8
+ private static readonly NAMESPACE;
9
+ /**
10
+ * Stable model key for the synthesized through model connecting
11
+ * `sourceModelKey` to `targetModelKey` via the schema field
12
+ * `sourceSchemaFieldKey`.
13
+ *
14
+ * The returned key is deterministic across runs so storage and hydration
15
+ * artifacts stay stable as long as the inputs match.
16
+ */
17
+ static getModelKey(sourceModelKey: string, sourceSchemaFieldKey: string, targetModelKey: string): string;
18
+ /**
19
+ * Deterministic short digest used to derive the physical join-table name
20
+ * for a synthesized through model. Shorter than the model-key digest so
21
+ * table names stay within common SQL identifier limits.
22
+ */
23
+ static getTableBaseDigest(sourceModelKey: string, sourceSchemaFieldKey: string, targetModelKey: string): string;
24
+ /**
25
+ * True when `modelKey` was produced by {@link getModelKey} and therefore
26
+ * identifies a synthesized through model. Callers use this instead of
27
+ * comparing namespace prefixes so the namespace remains an implementation
28
+ * detail of this class.
29
+ */
30
+ static isImplicitManyToManyModel(modelKey: string): boolean;
31
+ /**
32
+ * Namespace under which synthesized through models are registered.
33
+ * Exposed so {@link ImplicitManyToManyThroughFactory} can construct the
34
+ * model with the correct namespace. External callers that want to ask
35
+ * "is this an implicit model" should prefer {@link isImplicitManyToManyModel}.
36
+ */
37
+ static getNamespace(): string;
38
+ /**
39
+ * Extract the `m2m_<digest>` component of a synthesized model key so the
40
+ * factory can register the through model with a deterministic name while
41
+ * keeping the namespace owned by this class.
42
+ */
43
+ static getModelName(modelKey: string): string;
44
+ private static digest;
45
+ }
@@ -0,0 +1,14 @@
1
+ import type { Model } from '../../domain/index';
2
+ import type { ModelRegistry } from '../registry/ModelRegistry';
3
+ export declare class ImplicitManyToManyThroughFactory {
4
+ static throughFieldNames(sourceModel: Model, targetModel: Model): {
5
+ throughSourceFieldName: string;
6
+ throughTargetFieldName: string;
7
+ };
8
+ static buildModels(registry: ModelRegistry): Model[];
9
+ private static unwrapForForeignKeyField;
10
+ private static clonePrimaryKeySchemaForForeignKey;
11
+ private static readSinglePrimaryKey;
12
+ private static collectImplicitDescriptors;
13
+ private static allocateTableName;
14
+ }
@@ -19,6 +19,12 @@ export interface ResolvedRelationDescriptor {
19
19
  cardinality: RelationCardinality;
20
20
  localFieldName?: string;
21
21
  targetFieldName?: string;
22
+ throughModelKey?: string;
23
+ throughTable?: string;
24
+ throughSourceFieldName?: string;
25
+ throughTargetFieldName?: string;
26
+ throughSourceKey?: string;
27
+ throughTargetKey?: string;
22
28
  capabilities: {
23
29
  migratable: boolean;
24
30
  queryable: boolean;
@@ -1,7 +1,7 @@
1
1
  import type { Model } from '../../domain/index';
2
2
  import type { FinalizedStorageArtifacts } from '../fields/FinalizedStorageArtifacts';
3
3
  import type { ResolvedRelationGraphSnapshot } from '../registry/ResolvedRelationGraphSnapshot';
4
- import type { NormalizedRelationStorageDescriptor } from './NormalizedRelationStorageDescriptor';
4
+ import { type NormalizedRelationStorageDescriptor } from './NormalizedRelationStorageDescriptor';
5
5
  import type { ResolvedRelationGraph } from './ResolvedRelationGraph';
6
6
  type GraphBuilderOptions = {
7
7
  version: number;
@@ -9,3 +9,4 @@
9
9
  export declare function toSnakeCase(value: string): string;
10
10
  export declare function pluralize(value: string): string;
11
11
  export declare function deriveTableName(name: string): string;
12
+ export declare function decapitalizeModelName(name: string): string;