joist-core 2.3.0-next.64 → 2.3.0-next.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/EntityMetadata.cjs.map +1 -1
- package/build/EntityMetadata.d.cts +2 -1
- package/build/EntityMetadata.d.cts.map +1 -1
- package/build/EntityMetadata.d.mts +2 -1
- package/build/EntityMetadata.d.mts.map +1 -1
- package/build/EntityMetadata.js.map +1 -1
- package/build/Tables.cjs +66 -10
- package/build/Tables.cjs.map +1 -1
- package/build/Tables.d.cts +26 -7
- package/build/Tables.d.cts.map +1 -1
- package/build/Tables.d.mts +26 -7
- package/build/Tables.d.mts.map +1 -1
- package/build/Tables.js +67 -11
- package/build/Tables.js.map +1 -1
- package/build/configure.cjs +1 -4
- package/build/configure.cjs.map +1 -1
- package/build/configure.js +1 -4
- package/build/configure.js.map +1 -1
- package/build/index.d.cts +2 -2
- package/build/index.d.mts +2 -2
- package/build/query.cjs +19 -45
- package/build/query.cjs.map +1 -1
- package/build/query.d.cts +11 -7
- package/build/query.d.cts.map +1 -1
- package/build/query.d.mts +11 -7
- package/build/query.d.mts.map +1 -1
- package/build/query.js +20 -46
- package/build/query.js.map +1 -1
- package/build/serde.cjs +5 -2
- package/build/serde.cjs.map +1 -1
- package/build/serde.d.cts +2 -1
- package/build/serde.d.cts.map +1 -1
- package/build/serde.d.mts +2 -1
- package/build/serde.d.mts.map +1 -1
- package/build/serde.js +5 -2
- package/build/serde.js.map +1 -1
- package/build/typeMap.d.cts +3 -1
- package/build/typeMap.d.cts.map +1 -1
- package/build/typeMap.d.mts +3 -1
- package/build/typeMap.d.mts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityMetadata.cjs","names":["getInstanceData","getMetadataForType","isEntity"],"sources":["../src/EntityMetadata.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type ConfigApi, type Reactable, type ReactiveRule } from \"./config.ts\";\nimport { getMetadataForType } from \"./configure.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type EntityManager, type MaybeAbstractEntityConstructor, type TimestampFields } from \"./EntityManager.ts\";\nimport { type EnumMetadata } from \"./EnumMetadata.ts\";\nimport { type DeepNew } from \"./loadHints.ts\";\nimport { type FieldSerde, type PolymorphicKeySerde } from \"./serde.ts\";\n\nexport function getMetadata<T extends Entity>(entity: T): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(type: MaybeAbstractEntityConstructor<T>): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(meta: EntityMetadata): EntityMetadata;\nexport function getMetadata<T extends Entity>(\n param: T | MaybeAbstractEntityConstructor<T> | EntityMetadata,\n): EntityMetadata {\n if (!param) throw new Error(`Cannot getMetadata of ${param}`);\n return (\n typeof param === \"function\" ? (param as any).metadata : \"cstr\" in param ? param : getInstanceData(param).metadata\n ) as EntityMetadata;\n}\n\n/** Returns the metadata layer that declares `fieldName`, i.e. `Task.tags` for `TaskOld.tags`. */\nexport function getMetadataForField(meta: EntityMetadata, fieldName: string): EntityMetadata {\n if (!meta.allFields[fieldName]) throw new Error(`Field '${fieldName}' not found on ${meta.type}`);\n // I.e. `TaskOld.tags` is an STI-inherited `Task.tags` and should walk up, but `SmallPublisherGroup.publishers`\n // is a CTI-specialized `PublisherGroup.publishers` and should stay on `SmallPublisherGroup`.\n while (!(fieldName in meta.fields) && meta.allFields[fieldName]?.specialized !== true && meta.baseType) {\n meta = getMetadataForType(meta.baseType);\n }\n return meta;\n}\n\n/**\n * Runtime metadata about an entity.\n *\n * The `joist-codegen` step will generate this by reading the database schema at build/codegen\n * time, along with any customizations in `joist-config.json`.\n *\n * Note: This has no generic, like `T extends Entity`, because `Entity<IdType>` i.e. with\n * an unknown string/id type, causes issues when we want to generically mix `EntityMetadata`\n * of different types, that even liberally using `EntityMetadata<any>` did not avoid.\n */\nexport interface EntityMetadata<T extends Entity = any> {\n cstr: MaybeAbstractEntityConstructor<T>;\n type: string;\n /** How the domain entity's id is represented. */\n idType: \"tagged-string\" | \"untagged-string\" | \"number\";\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n tableName: string;\n /** Whether codegen verified this table is a supported SQL mutation target; does not gate reads. */\n supportsEmExecute?: boolean;\n /** If we're a subtype, our immediate base type's name, e.g. for `SmallPublisher` this would be `Publisher`. */\n baseType: string | undefined;\n inheritanceType?: \"sti\" | \"cti\" | undefined;\n /** Indicates the field to use to derive which subtype to instantiate; only set on the base meta. */\n stiDiscriminatorField?: string;\n /** The discriminator enum value for this subtype; only set on sub metas. */\n stiDiscriminatorValue?: number;\n /** Whether this type is abstract (cannot be instantiated directly). */\n ctiAbstract?: boolean;\n tagName: string;\n fields: Record<string, Field>;\n /** Physical column names mapped to their owning domain fields, including inherited fields. */\n columns: Record<string, { fieldName: string }>;\n allFields: Record<string, Field & { aliasSuffix: string; specialized?: true }>;\n /** Usually polys are in `allFields`, but we pull the components out for comp-specific finds, like `parentBook`. */\n polyComponentFields?: Record<string, Field & { aliasSuffix: string }>;\n // Using `any` to avoid type errors between BaseType.metadata & SubType.metadata static fields\n config: ConfigApi<any, any>;\n /** The lazy list of non-read-only reactables for this metadata and its base types. */\n reactables?: Reactable[];\n /** The lazy lookup of non-read-only reactables by source field name. */\n reactablesByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of all reactables for this metadata and its base types. */\n reactablesIncludingReadOnly?: Reactable[];\n /** The lazy lookup of all reactables by source field name. */\n reactablesIncludingReadOnlyByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of reactive validation rules for this metadata and its subtypes. */\n reactiveRules?: ReactiveRule[];\n /** The lazy list of reactive *commit* rules (post-flush/pre-commit) for this metadata and its subtypes. */\n reactiveCommitRules?: ReactiveRule[];\n /** Lazily-cached: whether flushing an entity of this type could trigger any `addCommitRule` work. */\n hasCommitRules?: boolean;\n /** The lazily-cached set of own `lazy` primitive field names, i.e. columns excluded from the default SELECT. */\n lazyFieldNames?: ReadonlySet<string>;\n /** The lazily-cached list of sync-derived (`hasReactiveField`-less) field names, recalced during flush. */\n syncDerivedFields?: string[];\n /** Lazily-cached: whether this entity has any `lazy` columns, i.e. so the default SELECT lists columns explicitly. */\n hasLazyColumns?: boolean;\n orderBy: string | undefined;\n /** Flat field names that can be used to find existing rows before creating. I.e. [[\"email\"], [\"author\", \"title\"]]. */\n uniqueBy?: string[][];\n timestampFields: TimestampFields | undefined;\n // Ideally this would be the application-specific EntityManager (to avoid anys), but it doesn't really matter\n factory: (em: EntityManager<any, any, any>, opts?: any) => DeepNew<T>;\n /** The list of base types for this subtype, e.g. for Dog it'd be [Animal, Mammal]. */\n baseTypes: EntityMetadata[];\n /** The list of subtypes for this base type, e.g. for Animal it'd be `[Mammal, Dog]`. */\n subTypes: EntityMetadata[];\n /** Cached `[...baseTypes, this]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfMetas?: EntityMetadata[];\n /** Cached `[...baseTypes, this, ...subTypes]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfAndSubMetas?: EntityMetadata[];\n /** The lazy lookup of subtypes by type name, i.e. `Animal.metadata.subTypesByType.get(\"Dog\")`. */\n subTypesByType?: ReadonlyMap<string, EntityMetadata>;\n /** The lazy lookup of STI subtypes by discriminator value, i.e. `Task.metadata.subTypesByStiValue.get(1)`. */\n subTypesByStiValue?: ReadonlyMap<unknown, EntityMetadata>;\n /** The lazy STI discriminator column name, i.e. `Task.metadata.stiDiscriminatorColumnName`. */\n stiDiscriminatorColumnName?: string;\n /** If the schema is lacking deferred FKs, this is our insertion order. */\n nonDeferredFkOrder?: number;\n}\n\nexport type Field =\n | PrimaryKeyField\n | PrimitiveField\n | EnumField\n | OneToManyField\n | LargeOneToManyField\n | ManyToOneField\n | ManyToManyField\n | ManyToManyEnumField\n | OneToOneField\n | PolymorphicField;\n\n// Only the fields that have defined `serde` keys; should be a mapped type of Field\nexport type SerdeField = PrimaryKeyField | PrimitiveField | EnumField | ManyToOneField | PolymorphicField;\n\nexport type PrimaryKeyField = {\n kind: \"primaryKey\";\n fieldName: string;\n fieldIdName: undefined;\n required: true;\n serde: FieldSerde;\n immutable: true;\n};\n\nexport type PrimitiveField = {\n kind: \"primitive\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"orm\" | \"sync\" | \"async\" | false;\n protected: boolean;\n type: string | Function;\n serde: FieldSerde;\n immutable: boolean;\n citext?: boolean;\n default?: \"schema\" | \"config\";\n sanitize?: boolean;\n /** When true, this column is excluded from the entity's default SELECT and lazy-loaded via a `LazyField`. */\n lazy?: boolean;\n};\n\nexport type EnumField = {\n kind: \"enum\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"sync\" | \"async\" | false;\n enumDetailType: { getValues(): ReadonlyArray<unknown>; findById(id: any): unknown };\n serde: FieldSerde;\n immutable: boolean;\n default?: \"schema\" | \"config\";\n};\n\nexport type OneToManyField = {\n kind: \"o2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n orderBy?: { field: string; direction: \"ASC\" | \"DESC\" };\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\nexport type LargeOneToManyField = {\n kind: \"lo2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type ManyToOneField = {\n kind: \"m2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: FieldSerde;\n immutable: boolean;\n derived: \"async\" | false;\n default?: \"schema\" | \"config\";\n};\n\nexport type ManyToManyField = {\n kind: \"m2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n derived: \"async\" | \"otherSide\" | false;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\n/**\n * A many-to-many between an entity and an enum \"table\", e.g. `Publisher.logoColors` backed by a\n * `publisher_logo_colors` join table joining `publishers` to the `color` enum table.\n *\n * To the user this looks like an array of enum codes (`Color[]`), but unlike an enum-array column it\n * is lazy (needs a load hint) because it lives in its own join table.\n */\nexport type ManyToManyEnumField = {\n kind: \"m2mEnum\";\n fieldName: string;\n fieldIdName: undefined;\n required: false;\n derived: false;\n /** The enum's metadata, used to map enum codes <-> their numeric ids. */\n enumDetailType: EnumMetadata<any, any, number>;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n /** `[entityColumn, enumColumn]`, e.g. `[\"publisher_id\", \"color_id\"]`. */\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n};\n\nexport type OneToOneField = {\n kind: \"o2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type PolymorphicField = {\n kind: \"poly\";\n fieldName: string; // eg `parent`\n fieldIdName: string; // `parentId`\n required: boolean;\n components: PolymorphicFieldComponent[];\n serde: PolymorphicKeySerde;\n immutable: boolean;\n};\n\nexport type PolymorphicFieldComponent = {\n otherMetadata: () => EntityMetadata;\n otherFieldName: string; // eg `comment` or `comments`\n columnName: string; // eg `parent_book_id` or `parent_book_review_id`\n};\n\nexport function isOneToManyField(ormField: Field): ormField is OneToManyField {\n return ormField.kind === \"o2m\";\n}\n\nexport function isManyToOneField(ormField: Field): ormField is ManyToOneField {\n return ormField.kind === \"m2o\";\n}\n\nexport function isManyToManyField(ormField: Field): ormField is ManyToManyField {\n return ormField.kind === \"m2m\";\n}\n\nexport function isManyToManyEnumField(ormField: Field): ormField is ManyToManyEnumField {\n return ormField.kind === \"m2mEnum\";\n}\n\nexport function isOneToOneField(ormField: Field): ormField is OneToOneField {\n return ormField.kind === \"o2o\";\n}\n\nexport function isPolymorphicField(ormField: Field): ormField is PolymorphicField {\n return ormField.kind === \"poly\";\n}\n\nexport function isReferenceField(ormField: Field): ormField is ManyToOneField | OneToOneField | PolymorphicField {\n return ormField.kind === \"m2o\" || ormField.kind === \"o2o\" || ormField.kind === \"poly\";\n}\n\nexport function isCollectionField(ormField: Field): ormField is OneToManyField | ManyToManyField {\n return ormField.kind === \"o2m\" || ormField.kind === \"m2m\";\n}\n\nexport function getBaseAndSelfMetas(meta: EntityMetadata): EntityMetadata[];\nexport function getBaseAndSelfMetas(entity: Entity): EntityMetadata[];\nexport function getBaseAndSelfMetas(param: Entity | EntityMetadata): EntityMetadata[] {\n return isEntity(param)\n ? getBaseSelfAndSubMetas(getMetadata(param))\n : (param.baseSelfMetas ?? [...param.baseTypes, param]);\n}\n\nexport function getBaseSelfAndSubMetas(meta: EntityMetadata): EntityMetadata[] {\n return meta.baseSelfAndSubMetas ?? [...meta.baseTypes, meta, ...meta.subTypes];\n}\n\nexport function getSubMetas(meta: EntityMetadata): EntityMetadata[] {\n // We should do recursion at some point\n return meta.subTypes;\n}\n\nexport function getBaseMeta(meta: EntityMetadata): EntityMetadata {\n if (!meta.baseType) {\n return meta;\n } else {\n return meta.baseTypes[0];\n }\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,YACd,OACgB;CAChB,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yBAAyB,OAAO;CAC5D,OACE,OAAO,UAAU,aAAc,MAAc,WAAW,UAAU,QAAQ,QAAQA,mBAAAA,gBAAgB,KAAK,CAAC,CAAC;AAE7G;;AAGA,SAAgB,oBAAoB,MAAsB,WAAmC;CAC3F,IAAI,CAAC,KAAK,UAAU,YAAY,MAAM,IAAI,MAAM,UAAU,UAAU,iBAAiB,KAAK,MAAM;CAGhG,OAAO,EAAE,aAAa,KAAK,WAAW,KAAK,UAAU,UAAU,EAAE,gBAAgB,QAAQ,KAAK,UAC5F,OAAOC,kBAAAA,mBAAmB,KAAK,QAAQ;CAEzC,OAAO;AACT;AA0PA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,kBAAkB,UAA8C;CAC9E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,sBAAsB,UAAkD;CACtF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,gBAAgB,UAA4C;CAC1E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,mBAAmB,UAA+C;CAChF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAAgF;CAC/G,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS;AACjF;AAEA,SAAgB,kBAAkB,UAA+D;CAC/F,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS;AACtD;AAIA,SAAgB,oBAAoB,OAAkD;CACpF,OAAOC,eAAAA,SAAS,KAAK,IACjB,uBAAuB,YAAY,KAAK,CAAC,IACxC,MAAM,iBAAiB,CAAC,GAAG,MAAM,WAAW,KAAK;AACxD;AAEA,SAAgB,uBAAuB,MAAwC;CAC7E,OAAO,KAAK,uBAAuB;EAAC,GAAG,KAAK;EAAW;EAAM,GAAG,KAAK;CAAQ;AAC/E;AAEA,SAAgB,YAAY,MAAwC;CAElE,OAAO,KAAK;AACd;AAEA,SAAgB,YAAY,MAAsC;CAChE,IAAI,CAAC,KAAK,UACR,OAAO;MAEP,OAAO,KAAK,UAAU;AAE1B"}
|
|
1
|
+
{"version":3,"file":"EntityMetadata.cjs","names":["getInstanceData","getMetadataForType","isEntity"],"sources":["../src/EntityMetadata.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type ConfigApi, type Reactable, type ReactiveRule } from \"./config.ts\";\nimport { getMetadataForType } from \"./configure.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type EntityManager, type MaybeAbstractEntityConstructor, type TimestampFields } from \"./EntityManager.ts\";\nimport { type EnumMetadata } from \"./EnumMetadata.ts\";\nimport { type DeepNew } from \"./loadHints.ts\";\nimport { type FieldSerde, type PolymorphicKeySerde } from \"./serde.ts\";\n\nexport function getMetadata<T extends Entity>(entity: T): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(type: MaybeAbstractEntityConstructor<T>): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(meta: EntityMetadata): EntityMetadata;\nexport function getMetadata<T extends Entity>(\n param: T | MaybeAbstractEntityConstructor<T> | EntityMetadata,\n): EntityMetadata {\n if (!param) throw new Error(`Cannot getMetadata of ${param}`);\n return (\n typeof param === \"function\" ? (param as any).metadata : \"cstr\" in param ? param : getInstanceData(param).metadata\n ) as EntityMetadata;\n}\n\n/** Returns the metadata layer that declares `fieldName`, i.e. `Task.tags` for `TaskOld.tags`. */\nexport function getMetadataForField(meta: EntityMetadata, fieldName: string): EntityMetadata {\n if (!meta.allFields[fieldName]) throw new Error(`Field '${fieldName}' not found on ${meta.type}`);\n // I.e. `TaskOld.tags` is an STI-inherited `Task.tags` and should walk up, but `SmallPublisherGroup.publishers`\n // is a CTI-specialized `PublisherGroup.publishers` and should stay on `SmallPublisherGroup`.\n while (!(fieldName in meta.fields) && meta.allFields[fieldName]?.specialized !== true && meta.baseType) {\n meta = getMetadataForType(meta.baseType);\n }\n return meta;\n}\n\n/**\n * Runtime metadata about an entity.\n *\n * The `joist-codegen` step will generate this by reading the database schema at build/codegen\n * time, along with any customizations in `joist-config.json`.\n *\n * Note: This has no generic, like `T extends Entity`, because `Entity<IdType>` i.e. with\n * an unknown string/id type, causes issues when we want to generically mix `EntityMetadata`\n * of different types, that even liberally using `EntityMetadata<any>` did not avoid.\n */\nexport interface EntityMetadata<T extends Entity = any> {\n cstr: MaybeAbstractEntityConstructor<T>;\n type: string;\n /** How the domain entity's id is represented. */\n idType: \"tagged-string\" | \"untagged-string\" | \"number\";\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n tableName: string;\n /** Whether codegen verified this table is a supported SQL mutation target; does not gate reads. */\n supportsEmExecute?: boolean;\n /** If we're a subtype, our immediate base type's name, e.g. for `SmallPublisher` this would be `Publisher`. */\n baseType: string | undefined;\n inheritanceType?: \"sti\" | \"cti\" | undefined;\n /** Indicates the field to use to derive which subtype to instantiate; only set on the base meta. */\n stiDiscriminatorField?: string;\n /** The discriminator enum value for this subtype; only set on sub metas. */\n stiDiscriminatorValue?: number;\n /** Whether this type is abstract (cannot be instantiated directly). */\n ctiAbstract?: boolean;\n tagName: string;\n fields: Record<string, Field>;\n /** Physical column names mapped to original storage fields, without CTI base columns. */\n columns: Record<string, { fieldName: string; field: Field }>;\n allFields: Record<string, Field & { aliasSuffix: string; specialized?: true }>;\n /** Usually polys are in `allFields`, but we pull the components out for comp-specific finds, like `parentBook`. */\n polyComponentFields?: Record<string, Field & { aliasSuffix: string }>;\n // Using `any` to avoid type errors between BaseType.metadata & SubType.metadata static fields\n config: ConfigApi<any, any>;\n /** The lazy list of non-read-only reactables for this metadata and its base types. */\n reactables?: Reactable[];\n /** The lazy lookup of non-read-only reactables by source field name. */\n reactablesByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of all reactables for this metadata and its base types. */\n reactablesIncludingReadOnly?: Reactable[];\n /** The lazy lookup of all reactables by source field name. */\n reactablesIncludingReadOnlyByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of reactive validation rules for this metadata and its subtypes. */\n reactiveRules?: ReactiveRule[];\n /** The lazy list of reactive *commit* rules (post-flush/pre-commit) for this metadata and its subtypes. */\n reactiveCommitRules?: ReactiveRule[];\n /** Lazily-cached: whether flushing an entity of this type could trigger any `addCommitRule` work. */\n hasCommitRules?: boolean;\n /** The lazily-cached set of own `lazy` primitive field names, i.e. columns excluded from the default SELECT. */\n lazyFieldNames?: ReadonlySet<string>;\n /** The lazily-cached list of sync-derived (`hasReactiveField`-less) field names, recalced during flush. */\n syncDerivedFields?: string[];\n /** Lazily-cached: whether this entity has any `lazy` columns, i.e. so the default SELECT lists columns explicitly. */\n hasLazyColumns?: boolean;\n orderBy: string | undefined;\n /** Flat field names that can be used to find existing rows before creating. I.e. [[\"email\"], [\"author\", \"title\"]]. */\n uniqueBy?: string[][];\n timestampFields: TimestampFields | undefined;\n // Ideally this would be the application-specific EntityManager (to avoid anys), but it doesn't really matter\n factory: (em: EntityManager<any, any, any>, opts?: any) => DeepNew<T>;\n /** The list of base types for this subtype, e.g. for Dog it'd be [Animal, Mammal]. */\n baseTypes: EntityMetadata[];\n /** The list of subtypes for this base type, e.g. for Animal it'd be `[Mammal, Dog]`. */\n subTypes: EntityMetadata[];\n /** Cached `[...baseTypes, this]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfMetas?: EntityMetadata[];\n /** Cached `[...baseTypes, this, ...subTypes]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfAndSubMetas?: EntityMetadata[];\n /** The lazy lookup of subtypes by type name, i.e. `Animal.metadata.subTypesByType.get(\"Dog\")`. */\n subTypesByType?: ReadonlyMap<string, EntityMetadata>;\n /** The lazy lookup of STI subtypes by discriminator value, i.e. `Task.metadata.subTypesByStiValue.get(1)`. */\n subTypesByStiValue?: ReadonlyMap<unknown, EntityMetadata>;\n /** The lazy STI discriminator column name, i.e. `Task.metadata.stiDiscriminatorColumnName`. */\n stiDiscriminatorColumnName?: string;\n /** If the schema is lacking deferred FKs, this is our insertion order. */\n nonDeferredFkOrder?: number;\n}\n\nexport type Field =\n | PrimaryKeyField\n | PrimitiveField\n | EnumField\n | OneToManyField\n | LargeOneToManyField\n | ManyToOneField\n | ManyToManyField\n | ManyToManyEnumField\n | OneToOneField\n | PolymorphicField;\n\n// Only the fields that have defined `serde` keys; should be a mapped type of Field\nexport type SerdeField = PrimaryKeyField | PrimitiveField | EnumField | ManyToOneField | PolymorphicField;\n\nexport type PrimaryKeyField = {\n kind: \"primaryKey\";\n fieldName: string;\n fieldIdName: undefined;\n required: true;\n serde: FieldSerde;\n immutable: true;\n};\n\nexport type PrimitiveField = {\n kind: \"primitive\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"orm\" | \"sync\" | \"async\" | false;\n protected: boolean;\n type: string | Function;\n serde: FieldSerde;\n immutable: boolean;\n citext?: boolean;\n default?: \"schema\" | \"config\";\n sanitize?: boolean;\n /** When true, this column is excluded from the entity's default SELECT and lazy-loaded via a `LazyField`. */\n lazy?: boolean;\n};\n\nexport type EnumField = {\n kind: \"enum\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"sync\" | \"async\" | false;\n enumDetailType: { getValues(): ReadonlyArray<unknown>; findById(id: any): unknown };\n serde: FieldSerde;\n immutable: boolean;\n default?: \"schema\" | \"config\";\n};\n\nexport type OneToManyField = {\n kind: \"o2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n orderBy?: { field: string; direction: \"ASC\" | \"DESC\" };\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\nexport type LargeOneToManyField = {\n kind: \"lo2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type ManyToOneField = {\n kind: \"m2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: FieldSerde;\n immutable: boolean;\n derived: \"async\" | false;\n default?: \"schema\" | \"config\";\n};\n\nexport type ManyToManyField = {\n kind: \"m2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n derived: \"async\" | \"otherSide\" | false;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\n/**\n * A many-to-many between an entity and an enum \"table\", e.g. `Publisher.logoColors` backed by a\n * `publisher_logo_colors` join table joining `publishers` to the `color` enum table.\n *\n * To the user this looks like an array of enum codes (`Color[]`), but unlike an enum-array column it\n * is lazy (needs a load hint) because it lives in its own join table.\n */\nexport type ManyToManyEnumField = {\n kind: \"m2mEnum\";\n fieldName: string;\n fieldIdName: undefined;\n required: false;\n derived: false;\n /** The enum's metadata, used to map enum codes <-> their numeric ids. */\n enumDetailType: EnumMetadata<any, any, number>;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n /** `[entityColumn, enumColumn]`, e.g. `[\"publisher_id\", \"color_id\"]`. */\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n};\n\nexport type OneToOneField = {\n kind: \"o2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type PolymorphicField = {\n kind: \"poly\";\n fieldName: string; // eg `parent`\n fieldIdName: string; // `parentId`\n required: boolean;\n components: PolymorphicFieldComponent[];\n serde: PolymorphicKeySerde;\n immutable: boolean;\n};\n\nexport type PolymorphicFieldComponent = {\n otherMetadata: () => EntityMetadata;\n otherFieldName: string; // eg `comment` or `comments`\n columnName: string; // eg `parent_book_id` or `parent_book_review_id`\n};\n\nexport function isOneToManyField(ormField: Field): ormField is OneToManyField {\n return ormField.kind === \"o2m\";\n}\n\nexport function isManyToOneField(ormField: Field): ormField is ManyToOneField {\n return ormField.kind === \"m2o\";\n}\n\nexport function isManyToManyField(ormField: Field): ormField is ManyToManyField {\n return ormField.kind === \"m2m\";\n}\n\nexport function isManyToManyEnumField(ormField: Field): ormField is ManyToManyEnumField {\n return ormField.kind === \"m2mEnum\";\n}\n\nexport function isOneToOneField(ormField: Field): ormField is OneToOneField {\n return ormField.kind === \"o2o\";\n}\n\nexport function isPolymorphicField(ormField: Field): ormField is PolymorphicField {\n return ormField.kind === \"poly\";\n}\n\nexport function isReferenceField(ormField: Field): ormField is ManyToOneField | OneToOneField | PolymorphicField {\n return ormField.kind === \"m2o\" || ormField.kind === \"o2o\" || ormField.kind === \"poly\";\n}\n\nexport function isCollectionField(ormField: Field): ormField is OneToManyField | ManyToManyField {\n return ormField.kind === \"o2m\" || ormField.kind === \"m2m\";\n}\n\nexport function getBaseAndSelfMetas(meta: EntityMetadata): EntityMetadata[];\nexport function getBaseAndSelfMetas(entity: Entity): EntityMetadata[];\nexport function getBaseAndSelfMetas(param: Entity | EntityMetadata): EntityMetadata[] {\n return isEntity(param)\n ? getBaseSelfAndSubMetas(getMetadata(param))\n : (param.baseSelfMetas ?? [...param.baseTypes, param]);\n}\n\nexport function getBaseSelfAndSubMetas(meta: EntityMetadata): EntityMetadata[] {\n return meta.baseSelfAndSubMetas ?? [...meta.baseTypes, meta, ...meta.subTypes];\n}\n\nexport function getSubMetas(meta: EntityMetadata): EntityMetadata[] {\n // We should do recursion at some point\n return meta.subTypes;\n}\n\nexport function getBaseMeta(meta: EntityMetadata): EntityMetadata {\n if (!meta.baseType) {\n return meta;\n } else {\n return meta.baseTypes[0];\n }\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,YACd,OACgB;CAChB,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yBAAyB,OAAO;CAC5D,OACE,OAAO,UAAU,aAAc,MAAc,WAAW,UAAU,QAAQ,QAAQA,mBAAAA,gBAAgB,KAAK,CAAC,CAAC;AAE7G;;AAGA,SAAgB,oBAAoB,MAAsB,WAAmC;CAC3F,IAAI,CAAC,KAAK,UAAU,YAAY,MAAM,IAAI,MAAM,UAAU,UAAU,iBAAiB,KAAK,MAAM;CAGhG,OAAO,EAAE,aAAa,KAAK,WAAW,KAAK,UAAU,UAAU,EAAE,gBAAgB,QAAQ,KAAK,UAC5F,OAAOC,kBAAAA,mBAAmB,KAAK,QAAQ;CAEzC,OAAO;AACT;AA0PA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,kBAAkB,UAA8C;CAC9E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,sBAAsB,UAAkD;CACtF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,gBAAgB,UAA4C;CAC1E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,mBAAmB,UAA+C;CAChF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAAgF;CAC/G,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS;AACjF;AAEA,SAAgB,kBAAkB,UAA+D;CAC/F,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS;AACtD;AAIA,SAAgB,oBAAoB,OAAkD;CACpF,OAAOC,eAAAA,SAAS,KAAK,IACjB,uBAAuB,YAAY,KAAK,CAAC,IACxC,MAAM,iBAAiB,CAAC,GAAG,MAAM,WAAW,KAAK;AACxD;AAEA,SAAgB,uBAAuB,MAAwC;CAC7E,OAAO,KAAK,uBAAuB;EAAC,GAAG,KAAK;EAAW;EAAM,GAAG,KAAK;CAAQ;AAC/E;AAEA,SAAgB,YAAY,MAAwC;CAElE,OAAO,KAAK;AACd;AAEA,SAAgB,YAAY,MAAsC;CAChE,IAAI,CAAC,KAAK,UACR,OAAO;MAEP,OAAO,KAAK,UAAU;AAE1B"}
|
|
@@ -41,9 +41,10 @@ interface EntityMetadata<T extends Entity = any> {
|
|
|
41
41
|
ctiAbstract?: boolean;
|
|
42
42
|
tagName: string;
|
|
43
43
|
fields: Record<string, Field>;
|
|
44
|
-
/** Physical column names mapped to
|
|
44
|
+
/** Physical column names mapped to original storage fields, without CTI base columns. */
|
|
45
45
|
columns: Record<string, {
|
|
46
46
|
fieldName: string;
|
|
47
|
+
field: Field;
|
|
47
48
|
}>;
|
|
48
49
|
allFields: Record<string, Field & {
|
|
49
50
|
aliasSuffix: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityMetadata.d.cts","names":[],"sources":["../src/EntityMetadata.ts"],"mappings":";;;;;;;iBASgB,YAAY,UAAU,QAAQ,QAAQ,IAAI,eAAe;iBACzD,YAAY,UAAU,QAAQ,MAAM,+BAA+B,KAAK,eAAe;iBACvF,YAAY,UAAU,QAAQ,MAAM,iBAAiB;;iBAWrD,oBAAoB,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;UAoB7D,eAAe,UAAU;EACxC,MAAM,+BAA+B;EACrC;;EAEA;;EAEA;EACA;;EAEA;;EAEA;EACA;;EAEA;;EAEA;;EAEA;EACA;EACA,QAAQ,eAAe;;EAEvB,SAAS;IAAiB;;
|
|
1
|
+
{"version":3,"file":"EntityMetadata.d.cts","names":[],"sources":["../src/EntityMetadata.ts"],"mappings":";;;;;;;iBASgB,YAAY,UAAU,QAAQ,QAAQ,IAAI,eAAe;iBACzD,YAAY,UAAU,QAAQ,MAAM,+BAA+B,KAAK,eAAe;iBACvF,YAAY,UAAU,QAAQ,MAAM,iBAAiB;;iBAWrD,oBAAoB,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;UAoB7D,eAAe,UAAU;EACxC,MAAM,+BAA+B;EACrC;;EAEA;;EAEA;EACA;;EAEA;;EAEA;EACA;;EAEA;;EAEA;;EAEA;EACA;EACA,QAAQ,eAAe;;EAEvB,SAAS;IAAiB;IAAmB,OAAO;;EACpD,WAAW,eAAe;IAAU;IAAqB;;;EAEzD,sBAAsB,eAAe;IAAU;;EAE/C,QAAQ;;EAER,aAAa;;EAEb,oBAAoB,oBAAoB;;EAExC,8BAA8B;;EAE9B,qCAAqC,oBAAoB;;EAEzD,gBAAgB;;EAEhB,sBAAsB;;EAEtB;;EAEA,iBAAiB;;EAEjB;;EAEA;EACA;;EAEA;EACA,iBAAiB;EAEjB,UAAU,IAAI,8BAA8B,eAAe,QAAQ;;EAEnE,WAAW;;EAEX,UAAU;;EAEV,gBAAgB;;EAEhB,sBAAsB;;EAEtB,iBAAiB,oBAAoB;;EAErC,qBAAqB,qBAAqB;;EAE1C;;EAEA;;KAGU,QACR,kBACA,iBACA,YACA,iBACA,sBACA,iBACA,kBACA,sBACA,gBACA;KAGQ,aAAa,kBAAkB,iBAAiB,YAAY,iBAAiB;KAE7E;EACV;EACA;EACA;EACA;EACA,OAAO;EACP;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,eAAe;EACf,OAAO;EACP;EACA;EACA;EACA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;IAAkB,aAAa;IAAwB,SAAS;;EAChE,OAAO;EACP;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;EACA;IAAY;IAAe;;;EAE3B;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;EACA,OAAO;EACP;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;;;;;;;;KAUU;EACV;EACA;EACA;EACA;EACA;;EAEA,gBAAgB;EAChB;EACA;EACA;;EAEA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,YAAY;EACZ,OAAO;EACP;;KAGU;EACV,qBAAqB;EACrB;EACA;;iBAGc,iBAAiB,UAAU,QAAQ,YAAY;iBAI/C,iBAAiB,UAAU,QAAQ,YAAY;iBAI/C,kBAAkB,UAAU,QAAQ,YAAY;iBAIhD,sBAAsB,UAAU,QAAQ,YAAY;iBAIpD,gBAAgB,UAAU,QAAQ,YAAY;iBAI9C,mBAAmB,UAAU,QAAQ,YAAY;iBAIjD,iBAAiB,UAAU,QAAQ,YAAY,iBAAiB,gBAAgB;iBAIhF,kBAAkB,UAAU,QAAQ,YAAY,iBAAiB;iBAIjE,oBAAoB,MAAM,iBAAiB;iBAC3C,oBAAoB,QAAQ,SAAS;iBAOrC,uBAAuB,MAAM,iBAAiB;iBAI9C,YAAY,MAAM,iBAAiB;iBAKnC,YAAY,MAAM,iBAAiB"}
|
|
@@ -41,9 +41,10 @@ interface EntityMetadata<T extends Entity = any> {
|
|
|
41
41
|
ctiAbstract?: boolean;
|
|
42
42
|
tagName: string;
|
|
43
43
|
fields: Record<string, Field>;
|
|
44
|
-
/** Physical column names mapped to
|
|
44
|
+
/** Physical column names mapped to original storage fields, without CTI base columns. */
|
|
45
45
|
columns: Record<string, {
|
|
46
46
|
fieldName: string;
|
|
47
|
+
field: Field;
|
|
47
48
|
}>;
|
|
48
49
|
allFields: Record<string, Field & {
|
|
49
50
|
aliasSuffix: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityMetadata.d.mts","names":[],"sources":["../src/EntityMetadata.ts"],"mappings":";;;;;;;iBASgB,YAAY,UAAU,QAAQ,QAAQ,IAAI,eAAe;iBACzD,YAAY,UAAU,QAAQ,MAAM,+BAA+B,KAAK,eAAe;iBACvF,YAAY,UAAU,QAAQ,MAAM,iBAAiB;;iBAWrD,oBAAoB,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;UAoB7D,eAAe,UAAU;EACxC,MAAM,+BAA+B;EACrC;;EAEA;;EAEA;EACA;;EAEA;;EAEA;EACA;;EAEA;;EAEA;;EAEA;EACA;EACA,QAAQ,eAAe;;EAEvB,SAAS;IAAiB;;
|
|
1
|
+
{"version":3,"file":"EntityMetadata.d.mts","names":[],"sources":["../src/EntityMetadata.ts"],"mappings":";;;;;;;iBASgB,YAAY,UAAU,QAAQ,QAAQ,IAAI,eAAe;iBACzD,YAAY,UAAU,QAAQ,MAAM,+BAA+B,KAAK,eAAe;iBACvF,YAAY,UAAU,QAAQ,MAAM,iBAAiB;;iBAWrD,oBAAoB,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;UAoB7D,eAAe,UAAU;EACxC,MAAM,+BAA+B;EACrC;;EAEA;;EAEA;EACA;;EAEA;;EAEA;EACA;;EAEA;;EAEA;;EAEA;EACA;EACA,QAAQ,eAAe;;EAEvB,SAAS;IAAiB;IAAmB,OAAO;;EACpD,WAAW,eAAe;IAAU;IAAqB;;;EAEzD,sBAAsB,eAAe;IAAU;;EAE/C,QAAQ;;EAER,aAAa;;EAEb,oBAAoB,oBAAoB;;EAExC,8BAA8B;;EAE9B,qCAAqC,oBAAoB;;EAEzD,gBAAgB;;EAEhB,sBAAsB;;EAEtB;;EAEA,iBAAiB;;EAEjB;;EAEA;EACA;;EAEA;EACA,iBAAiB;EAEjB,UAAU,IAAI,8BAA8B,eAAe,QAAQ;;EAEnE,WAAW;;EAEX,UAAU;;EAEV,gBAAgB;;EAEhB,sBAAsB;;EAEtB,iBAAiB,oBAAoB;;EAErC,qBAAqB,qBAAqB;;EAE1C;;EAEA;;KAGU,QACR,kBACA,iBACA,YACA,iBACA,sBACA,iBACA,kBACA,sBACA,gBACA;KAGQ,aAAa,kBAAkB,iBAAiB,YAAY,iBAAiB;KAE7E;EACV;EACA;EACA;EACA;EACA,OAAO;EACP;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,eAAe;EACf,OAAO;EACP;EACA;EACA;EACA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;IAAkB,aAAa;IAAwB,SAAS;;EAChE,OAAO;EACP;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;EACA;IAAY;IAAe;;;EAE3B;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;EACA,OAAO;EACP;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;;;;;;;;KAUU;EACV;EACA;EACA;EACA;EACA;;EAEA,gBAAgB;EAChB;EACA;EACA;;EAEA;;EAEA;;KAGU;EACV;EACA;EACA;EACA;EACA,qBAAqB;EACrB;;EAEA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA,YAAY;EACZ,OAAO;EACP;;KAGU;EACV,qBAAqB;EACrB;EACA;;iBAGc,iBAAiB,UAAU,QAAQ,YAAY;iBAI/C,iBAAiB,UAAU,QAAQ,YAAY;iBAI/C,kBAAkB,UAAU,QAAQ,YAAY;iBAIhD,sBAAsB,UAAU,QAAQ,YAAY;iBAIpD,gBAAgB,UAAU,QAAQ,YAAY;iBAI9C,mBAAmB,UAAU,QAAQ,YAAY;iBAIjD,iBAAiB,UAAU,QAAQ,YAAY,iBAAiB,gBAAgB;iBAIhF,kBAAkB,UAAU,QAAQ,YAAY,iBAAiB;iBAIjE,oBAAoB,MAAM,iBAAiB;iBAC3C,oBAAoB,QAAQ,SAAS;iBAOrC,uBAAuB,MAAM,iBAAiB;iBAI9C,YAAY,MAAM,iBAAiB;iBAKnC,YAAY,MAAM,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityMetadata.js","names":[],"sources":["../src/EntityMetadata.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type ConfigApi, type Reactable, type ReactiveRule } from \"./config.ts\";\nimport { getMetadataForType } from \"./configure.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type EntityManager, type MaybeAbstractEntityConstructor, type TimestampFields } from \"./EntityManager.ts\";\nimport { type EnumMetadata } from \"./EnumMetadata.ts\";\nimport { type DeepNew } from \"./loadHints.ts\";\nimport { type FieldSerde, type PolymorphicKeySerde } from \"./serde.ts\";\n\nexport function getMetadata<T extends Entity>(entity: T): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(type: MaybeAbstractEntityConstructor<T>): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(meta: EntityMetadata): EntityMetadata;\nexport function getMetadata<T extends Entity>(\n param: T | MaybeAbstractEntityConstructor<T> | EntityMetadata,\n): EntityMetadata {\n if (!param) throw new Error(`Cannot getMetadata of ${param}`);\n return (\n typeof param === \"function\" ? (param as any).metadata : \"cstr\" in param ? param : getInstanceData(param).metadata\n ) as EntityMetadata;\n}\n\n/** Returns the metadata layer that declares `fieldName`, i.e. `Task.tags` for `TaskOld.tags`. */\nexport function getMetadataForField(meta: EntityMetadata, fieldName: string): EntityMetadata {\n if (!meta.allFields[fieldName]) throw new Error(`Field '${fieldName}' not found on ${meta.type}`);\n // I.e. `TaskOld.tags` is an STI-inherited `Task.tags` and should walk up, but `SmallPublisherGroup.publishers`\n // is a CTI-specialized `PublisherGroup.publishers` and should stay on `SmallPublisherGroup`.\n while (!(fieldName in meta.fields) && meta.allFields[fieldName]?.specialized !== true && meta.baseType) {\n meta = getMetadataForType(meta.baseType);\n }\n return meta;\n}\n\n/**\n * Runtime metadata about an entity.\n *\n * The `joist-codegen` step will generate this by reading the database schema at build/codegen\n * time, along with any customizations in `joist-config.json`.\n *\n * Note: This has no generic, like `T extends Entity`, because `Entity<IdType>` i.e. with\n * an unknown string/id type, causes issues when we want to generically mix `EntityMetadata`\n * of different types, that even liberally using `EntityMetadata<any>` did not avoid.\n */\nexport interface EntityMetadata<T extends Entity = any> {\n cstr: MaybeAbstractEntityConstructor<T>;\n type: string;\n /** How the domain entity's id is represented. */\n idType: \"tagged-string\" | \"untagged-string\" | \"number\";\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n tableName: string;\n /** Whether codegen verified this table is a supported SQL mutation target; does not gate reads. */\n supportsEmExecute?: boolean;\n /** If we're a subtype, our immediate base type's name, e.g. for `SmallPublisher` this would be `Publisher`. */\n baseType: string | undefined;\n inheritanceType?: \"sti\" | \"cti\" | undefined;\n /** Indicates the field to use to derive which subtype to instantiate; only set on the base meta. */\n stiDiscriminatorField?: string;\n /** The discriminator enum value for this subtype; only set on sub metas. */\n stiDiscriminatorValue?: number;\n /** Whether this type is abstract (cannot be instantiated directly). */\n ctiAbstract?: boolean;\n tagName: string;\n fields: Record<string, Field>;\n /** Physical column names mapped to their owning domain fields, including inherited fields. */\n columns: Record<string, { fieldName: string }>;\n allFields: Record<string, Field & { aliasSuffix: string; specialized?: true }>;\n /** Usually polys are in `allFields`, but we pull the components out for comp-specific finds, like `parentBook`. */\n polyComponentFields?: Record<string, Field & { aliasSuffix: string }>;\n // Using `any` to avoid type errors between BaseType.metadata & SubType.metadata static fields\n config: ConfigApi<any, any>;\n /** The lazy list of non-read-only reactables for this metadata and its base types. */\n reactables?: Reactable[];\n /** The lazy lookup of non-read-only reactables by source field name. */\n reactablesByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of all reactables for this metadata and its base types. */\n reactablesIncludingReadOnly?: Reactable[];\n /** The lazy lookup of all reactables by source field name. */\n reactablesIncludingReadOnlyByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of reactive validation rules for this metadata and its subtypes. */\n reactiveRules?: ReactiveRule[];\n /** The lazy list of reactive *commit* rules (post-flush/pre-commit) for this metadata and its subtypes. */\n reactiveCommitRules?: ReactiveRule[];\n /** Lazily-cached: whether flushing an entity of this type could trigger any `addCommitRule` work. */\n hasCommitRules?: boolean;\n /** The lazily-cached set of own `lazy` primitive field names, i.e. columns excluded from the default SELECT. */\n lazyFieldNames?: ReadonlySet<string>;\n /** The lazily-cached list of sync-derived (`hasReactiveField`-less) field names, recalced during flush. */\n syncDerivedFields?: string[];\n /** Lazily-cached: whether this entity has any `lazy` columns, i.e. so the default SELECT lists columns explicitly. */\n hasLazyColumns?: boolean;\n orderBy: string | undefined;\n /** Flat field names that can be used to find existing rows before creating. I.e. [[\"email\"], [\"author\", \"title\"]]. */\n uniqueBy?: string[][];\n timestampFields: TimestampFields | undefined;\n // Ideally this would be the application-specific EntityManager (to avoid anys), but it doesn't really matter\n factory: (em: EntityManager<any, any, any>, opts?: any) => DeepNew<T>;\n /** The list of base types for this subtype, e.g. for Dog it'd be [Animal, Mammal]. */\n baseTypes: EntityMetadata[];\n /** The list of subtypes for this base type, e.g. for Animal it'd be `[Mammal, Dog]`. */\n subTypes: EntityMetadata[];\n /** Cached `[...baseTypes, this]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfMetas?: EntityMetadata[];\n /** Cached `[...baseTypes, this, ...subTypes]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfAndSubMetas?: EntityMetadata[];\n /** The lazy lookup of subtypes by type name, i.e. `Animal.metadata.subTypesByType.get(\"Dog\")`. */\n subTypesByType?: ReadonlyMap<string, EntityMetadata>;\n /** The lazy lookup of STI subtypes by discriminator value, i.e. `Task.metadata.subTypesByStiValue.get(1)`. */\n subTypesByStiValue?: ReadonlyMap<unknown, EntityMetadata>;\n /** The lazy STI discriminator column name, i.e. `Task.metadata.stiDiscriminatorColumnName`. */\n stiDiscriminatorColumnName?: string;\n /** If the schema is lacking deferred FKs, this is our insertion order. */\n nonDeferredFkOrder?: number;\n}\n\nexport type Field =\n | PrimaryKeyField\n | PrimitiveField\n | EnumField\n | OneToManyField\n | LargeOneToManyField\n | ManyToOneField\n | ManyToManyField\n | ManyToManyEnumField\n | OneToOneField\n | PolymorphicField;\n\n// Only the fields that have defined `serde` keys; should be a mapped type of Field\nexport type SerdeField = PrimaryKeyField | PrimitiveField | EnumField | ManyToOneField | PolymorphicField;\n\nexport type PrimaryKeyField = {\n kind: \"primaryKey\";\n fieldName: string;\n fieldIdName: undefined;\n required: true;\n serde: FieldSerde;\n immutable: true;\n};\n\nexport type PrimitiveField = {\n kind: \"primitive\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"orm\" | \"sync\" | \"async\" | false;\n protected: boolean;\n type: string | Function;\n serde: FieldSerde;\n immutable: boolean;\n citext?: boolean;\n default?: \"schema\" | \"config\";\n sanitize?: boolean;\n /** When true, this column is excluded from the entity's default SELECT and lazy-loaded via a `LazyField`. */\n lazy?: boolean;\n};\n\nexport type EnumField = {\n kind: \"enum\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"sync\" | \"async\" | false;\n enumDetailType: { getValues(): ReadonlyArray<unknown>; findById(id: any): unknown };\n serde: FieldSerde;\n immutable: boolean;\n default?: \"schema\" | \"config\";\n};\n\nexport type OneToManyField = {\n kind: \"o2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n orderBy?: { field: string; direction: \"ASC\" | \"DESC\" };\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\nexport type LargeOneToManyField = {\n kind: \"lo2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type ManyToOneField = {\n kind: \"m2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: FieldSerde;\n immutable: boolean;\n derived: \"async\" | false;\n default?: \"schema\" | \"config\";\n};\n\nexport type ManyToManyField = {\n kind: \"m2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n derived: \"async\" | \"otherSide\" | false;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\n/**\n * A many-to-many between an entity and an enum \"table\", e.g. `Publisher.logoColors` backed by a\n * `publisher_logo_colors` join table joining `publishers` to the `color` enum table.\n *\n * To the user this looks like an array of enum codes (`Color[]`), but unlike an enum-array column it\n * is lazy (needs a load hint) because it lives in its own join table.\n */\nexport type ManyToManyEnumField = {\n kind: \"m2mEnum\";\n fieldName: string;\n fieldIdName: undefined;\n required: false;\n derived: false;\n /** The enum's metadata, used to map enum codes <-> their numeric ids. */\n enumDetailType: EnumMetadata<any, any, number>;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n /** `[entityColumn, enumColumn]`, e.g. `[\"publisher_id\", \"color_id\"]`. */\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n};\n\nexport type OneToOneField = {\n kind: \"o2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type PolymorphicField = {\n kind: \"poly\";\n fieldName: string; // eg `parent`\n fieldIdName: string; // `parentId`\n required: boolean;\n components: PolymorphicFieldComponent[];\n serde: PolymorphicKeySerde;\n immutable: boolean;\n};\n\nexport type PolymorphicFieldComponent = {\n otherMetadata: () => EntityMetadata;\n otherFieldName: string; // eg `comment` or `comments`\n columnName: string; // eg `parent_book_id` or `parent_book_review_id`\n};\n\nexport function isOneToManyField(ormField: Field): ormField is OneToManyField {\n return ormField.kind === \"o2m\";\n}\n\nexport function isManyToOneField(ormField: Field): ormField is ManyToOneField {\n return ormField.kind === \"m2o\";\n}\n\nexport function isManyToManyField(ormField: Field): ormField is ManyToManyField {\n return ormField.kind === \"m2m\";\n}\n\nexport function isManyToManyEnumField(ormField: Field): ormField is ManyToManyEnumField {\n return ormField.kind === \"m2mEnum\";\n}\n\nexport function isOneToOneField(ormField: Field): ormField is OneToOneField {\n return ormField.kind === \"o2o\";\n}\n\nexport function isPolymorphicField(ormField: Field): ormField is PolymorphicField {\n return ormField.kind === \"poly\";\n}\n\nexport function isReferenceField(ormField: Field): ormField is ManyToOneField | OneToOneField | PolymorphicField {\n return ormField.kind === \"m2o\" || ormField.kind === \"o2o\" || ormField.kind === \"poly\";\n}\n\nexport function isCollectionField(ormField: Field): ormField is OneToManyField | ManyToManyField {\n return ormField.kind === \"o2m\" || ormField.kind === \"m2m\";\n}\n\nexport function getBaseAndSelfMetas(meta: EntityMetadata): EntityMetadata[];\nexport function getBaseAndSelfMetas(entity: Entity): EntityMetadata[];\nexport function getBaseAndSelfMetas(param: Entity | EntityMetadata): EntityMetadata[] {\n return isEntity(param)\n ? getBaseSelfAndSubMetas(getMetadata(param))\n : (param.baseSelfMetas ?? [...param.baseTypes, param]);\n}\n\nexport function getBaseSelfAndSubMetas(meta: EntityMetadata): EntityMetadata[] {\n return meta.baseSelfAndSubMetas ?? [...meta.baseTypes, meta, ...meta.subTypes];\n}\n\nexport function getSubMetas(meta: EntityMetadata): EntityMetadata[] {\n // We should do recursion at some point\n return meta.subTypes;\n}\n\nexport function getBaseMeta(meta: EntityMetadata): EntityMetadata {\n if (!meta.baseType) {\n return meta;\n } else {\n return meta.baseTypes[0];\n }\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,YACd,OACgB;CAChB,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yBAAyB,OAAO;CAC5D,OACE,OAAO,UAAU,aAAc,MAAc,WAAW,UAAU,QAAQ,QAAQ,gBAAgB,KAAK,CAAC,CAAC;AAE7G;;AAGA,SAAgB,oBAAoB,MAAsB,WAAmC;CAC3F,IAAI,CAAC,KAAK,UAAU,YAAY,MAAM,IAAI,MAAM,UAAU,UAAU,iBAAiB,KAAK,MAAM;CAGhG,OAAO,EAAE,aAAa,KAAK,WAAW,KAAK,UAAU,UAAU,EAAE,gBAAgB,QAAQ,KAAK,UAC5F,OAAO,mBAAmB,KAAK,QAAQ;CAEzC,OAAO;AACT;AA0PA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,kBAAkB,UAA8C;CAC9E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,sBAAsB,UAAkD;CACtF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,gBAAgB,UAA4C;CAC1E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,mBAAmB,UAA+C;CAChF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAAgF;CAC/G,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS;AACjF;AAEA,SAAgB,kBAAkB,UAA+D;CAC/F,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS;AACtD;AAIA,SAAgB,oBAAoB,OAAkD;CACpF,OAAO,SAAS,KAAK,IACjB,uBAAuB,YAAY,KAAK,CAAC,IACxC,MAAM,iBAAiB,CAAC,GAAG,MAAM,WAAW,KAAK;AACxD;AAEA,SAAgB,uBAAuB,MAAwC;CAC7E,OAAO,KAAK,uBAAuB;EAAC,GAAG,KAAK;EAAW;EAAM,GAAG,KAAK;CAAQ;AAC/E;AAEA,SAAgB,YAAY,MAAwC;CAElE,OAAO,KAAK;AACd;AAEA,SAAgB,YAAY,MAAsC;CAChE,IAAI,CAAC,KAAK,UACR,OAAO;MAEP,OAAO,KAAK,UAAU;AAE1B"}
|
|
1
|
+
{"version":3,"file":"EntityMetadata.js","names":[],"sources":["../src/EntityMetadata.ts"],"sourcesContent":["import { getInstanceData } from \"./BaseEntity.ts\";\nimport { type ConfigApi, type Reactable, type ReactiveRule } from \"./config.ts\";\nimport { getMetadataForType } from \"./configure.ts\";\nimport { type Entity, isEntity } from \"./Entity.ts\";\nimport { type EntityManager, type MaybeAbstractEntityConstructor, type TimestampFields } from \"./EntityManager.ts\";\nimport { type EnumMetadata } from \"./EnumMetadata.ts\";\nimport { type DeepNew } from \"./loadHints.ts\";\nimport { type FieldSerde, type PolymorphicKeySerde } from \"./serde.ts\";\n\nexport function getMetadata<T extends Entity>(entity: T): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(type: MaybeAbstractEntityConstructor<T>): EntityMetadata<T>;\nexport function getMetadata<T extends Entity>(meta: EntityMetadata): EntityMetadata;\nexport function getMetadata<T extends Entity>(\n param: T | MaybeAbstractEntityConstructor<T> | EntityMetadata,\n): EntityMetadata {\n if (!param) throw new Error(`Cannot getMetadata of ${param}`);\n return (\n typeof param === \"function\" ? (param as any).metadata : \"cstr\" in param ? param : getInstanceData(param).metadata\n ) as EntityMetadata;\n}\n\n/** Returns the metadata layer that declares `fieldName`, i.e. `Task.tags` for `TaskOld.tags`. */\nexport function getMetadataForField(meta: EntityMetadata, fieldName: string): EntityMetadata {\n if (!meta.allFields[fieldName]) throw new Error(`Field '${fieldName}' not found on ${meta.type}`);\n // I.e. `TaskOld.tags` is an STI-inherited `Task.tags` and should walk up, but `SmallPublisherGroup.publishers`\n // is a CTI-specialized `PublisherGroup.publishers` and should stay on `SmallPublisherGroup`.\n while (!(fieldName in meta.fields) && meta.allFields[fieldName]?.specialized !== true && meta.baseType) {\n meta = getMetadataForType(meta.baseType);\n }\n return meta;\n}\n\n/**\n * Runtime metadata about an entity.\n *\n * The `joist-codegen` step will generate this by reading the database schema at build/codegen\n * time, along with any customizations in `joist-config.json`.\n *\n * Note: This has no generic, like `T extends Entity`, because `Entity<IdType>` i.e. with\n * an unknown string/id type, causes issues when we want to generically mix `EntityMetadata`\n * of different types, that even liberally using `EntityMetadata<any>` did not avoid.\n */\nexport interface EntityMetadata<T extends Entity = any> {\n cstr: MaybeAbstractEntityConstructor<T>;\n type: string;\n /** How the domain entity's id is represented. */\n idType: \"tagged-string\" | \"untagged-string\" | \"number\";\n /** The database column type, i.e. used to do `::type` casts in Postgres. */\n idDbType: \"bigint\" | \"int\" | \"uuid\" | \"text\";\n tableName: string;\n /** Whether codegen verified this table is a supported SQL mutation target; does not gate reads. */\n supportsEmExecute?: boolean;\n /** If we're a subtype, our immediate base type's name, e.g. for `SmallPublisher` this would be `Publisher`. */\n baseType: string | undefined;\n inheritanceType?: \"sti\" | \"cti\" | undefined;\n /** Indicates the field to use to derive which subtype to instantiate; only set on the base meta. */\n stiDiscriminatorField?: string;\n /** The discriminator enum value for this subtype; only set on sub metas. */\n stiDiscriminatorValue?: number;\n /** Whether this type is abstract (cannot be instantiated directly). */\n ctiAbstract?: boolean;\n tagName: string;\n fields: Record<string, Field>;\n /** Physical column names mapped to original storage fields, without CTI base columns. */\n columns: Record<string, { fieldName: string; field: Field }>;\n allFields: Record<string, Field & { aliasSuffix: string; specialized?: true }>;\n /** Usually polys are in `allFields`, but we pull the components out for comp-specific finds, like `parentBook`. */\n polyComponentFields?: Record<string, Field & { aliasSuffix: string }>;\n // Using `any` to avoid type errors between BaseType.metadata & SubType.metadata static fields\n config: ConfigApi<any, any>;\n /** The lazy list of non-read-only reactables for this metadata and its base types. */\n reactables?: Reactable[];\n /** The lazy lookup of non-read-only reactables by source field name. */\n reactablesByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of all reactables for this metadata and its base types. */\n reactablesIncludingReadOnly?: Reactable[];\n /** The lazy lookup of all reactables by source field name. */\n reactablesIncludingReadOnlyByField?: ReadonlyMap<string, Reactable[]>;\n /** The lazy list of reactive validation rules for this metadata and its subtypes. */\n reactiveRules?: ReactiveRule[];\n /** The lazy list of reactive *commit* rules (post-flush/pre-commit) for this metadata and its subtypes. */\n reactiveCommitRules?: ReactiveRule[];\n /** Lazily-cached: whether flushing an entity of this type could trigger any `addCommitRule` work. */\n hasCommitRules?: boolean;\n /** The lazily-cached set of own `lazy` primitive field names, i.e. columns excluded from the default SELECT. */\n lazyFieldNames?: ReadonlySet<string>;\n /** The lazily-cached list of sync-derived (`hasReactiveField`-less) field names, recalced during flush. */\n syncDerivedFields?: string[];\n /** Lazily-cached: whether this entity has any `lazy` columns, i.e. so the default SELECT lists columns explicitly. */\n hasLazyColumns?: boolean;\n orderBy: string | undefined;\n /** Flat field names that can be used to find existing rows before creating. I.e. [[\"email\"], [\"author\", \"title\"]]. */\n uniqueBy?: string[][];\n timestampFields: TimestampFields | undefined;\n // Ideally this would be the application-specific EntityManager (to avoid anys), but it doesn't really matter\n factory: (em: EntityManager<any, any, any>, opts?: any) => DeepNew<T>;\n /** The list of base types for this subtype, e.g. for Dog it'd be [Animal, Mammal]. */\n baseTypes: EntityMetadata[];\n /** The list of subtypes for this base type, e.g. for Animal it'd be `[Mammal, Dog]`. */\n subTypes: EntityMetadata[];\n /** Cached `[...baseTypes, this]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfMetas?: EntityMetadata[];\n /** Cached `[...baseTypes, this, ...subTypes]` set up by `configureMetadata`, so hot paths avoid re-allocating it. */\n baseSelfAndSubMetas?: EntityMetadata[];\n /** The lazy lookup of subtypes by type name, i.e. `Animal.metadata.subTypesByType.get(\"Dog\")`. */\n subTypesByType?: ReadonlyMap<string, EntityMetadata>;\n /** The lazy lookup of STI subtypes by discriminator value, i.e. `Task.metadata.subTypesByStiValue.get(1)`. */\n subTypesByStiValue?: ReadonlyMap<unknown, EntityMetadata>;\n /** The lazy STI discriminator column name, i.e. `Task.metadata.stiDiscriminatorColumnName`. */\n stiDiscriminatorColumnName?: string;\n /** If the schema is lacking deferred FKs, this is our insertion order. */\n nonDeferredFkOrder?: number;\n}\n\nexport type Field =\n | PrimaryKeyField\n | PrimitiveField\n | EnumField\n | OneToManyField\n | LargeOneToManyField\n | ManyToOneField\n | ManyToManyField\n | ManyToManyEnumField\n | OneToOneField\n | PolymorphicField;\n\n// Only the fields that have defined `serde` keys; should be a mapped type of Field\nexport type SerdeField = PrimaryKeyField | PrimitiveField | EnumField | ManyToOneField | PolymorphicField;\n\nexport type PrimaryKeyField = {\n kind: \"primaryKey\";\n fieldName: string;\n fieldIdName: undefined;\n required: true;\n serde: FieldSerde;\n immutable: true;\n};\n\nexport type PrimitiveField = {\n kind: \"primitive\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"orm\" | \"sync\" | \"async\" | false;\n protected: boolean;\n type: string | Function;\n serde: FieldSerde;\n immutable: boolean;\n citext?: boolean;\n default?: \"schema\" | \"config\";\n sanitize?: boolean;\n /** When true, this column is excluded from the entity's default SELECT and lazy-loaded via a `LazyField`. */\n lazy?: boolean;\n};\n\nexport type EnumField = {\n kind: \"enum\";\n fieldName: string;\n fieldIdName: undefined;\n required: boolean;\n derived: \"sync\" | \"async\" | false;\n enumDetailType: { getValues(): ReadonlyArray<unknown>; findById(id: any): unknown };\n serde: FieldSerde;\n immutable: boolean;\n default?: \"schema\" | \"config\";\n};\n\nexport type OneToManyField = {\n kind: \"o2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n orderBy?: { field: string; direction: \"ASC\" | \"DESC\" };\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\nexport type LargeOneToManyField = {\n kind: \"lo2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type ManyToOneField = {\n kind: \"m2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: FieldSerde;\n immutable: boolean;\n derived: \"async\" | false;\n default?: \"schema\" | \"config\";\n};\n\nexport type ManyToManyField = {\n kind: \"m2m\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n derived: \"async\" | \"otherSide\" | false;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n /** When `\"include\"`, this collection's `.get`/`.load` returns soft-deleted entities instead of hiding them. */\n softDeletes?: \"include\" | \"exclude\";\n};\n\n/**\n * A many-to-many between an entity and an enum \"table\", e.g. `Publisher.logoColors` backed by a\n * `publisher_logo_colors` join table joining `publishers` to the `color` enum table.\n *\n * To the user this looks like an array of enum codes (`Color[]`), but unlike an enum-array column it\n * is lazy (needs a load hint) because it lives in its own join table.\n */\nexport type ManyToManyEnumField = {\n kind: \"m2mEnum\";\n fieldName: string;\n fieldIdName: undefined;\n required: false;\n derived: false;\n /** The enum's metadata, used to map enum codes <-> their numeric ids. */\n enumDetailType: EnumMetadata<any, any, number>;\n serde: undefined;\n immutable: false;\n joinTableName: string;\n /** `[entityColumn, enumColumn]`, e.g. `[\"publisher_id\", \"color_id\"]`. */\n columnNames: [string, string];\n /** Whether the join table has a surrogate `id` PK; if false the FK pair is the composite PK. */\n hasJoinTableId: boolean;\n};\n\nexport type OneToOneField = {\n kind: \"o2o\";\n fieldName: string;\n fieldIdName: string;\n required: boolean;\n otherMetadata: () => EntityMetadata;\n otherFieldName: string;\n /** Useful when our other side is a poly, to find which component points back to us. */\n otherColumnName: string;\n serde: undefined;\n immutable: false;\n};\n\nexport type PolymorphicField = {\n kind: \"poly\";\n fieldName: string; // eg `parent`\n fieldIdName: string; // `parentId`\n required: boolean;\n components: PolymorphicFieldComponent[];\n serde: PolymorphicKeySerde;\n immutable: boolean;\n};\n\nexport type PolymorphicFieldComponent = {\n otherMetadata: () => EntityMetadata;\n otherFieldName: string; // eg `comment` or `comments`\n columnName: string; // eg `parent_book_id` or `parent_book_review_id`\n};\n\nexport function isOneToManyField(ormField: Field): ormField is OneToManyField {\n return ormField.kind === \"o2m\";\n}\n\nexport function isManyToOneField(ormField: Field): ormField is ManyToOneField {\n return ormField.kind === \"m2o\";\n}\n\nexport function isManyToManyField(ormField: Field): ormField is ManyToManyField {\n return ormField.kind === \"m2m\";\n}\n\nexport function isManyToManyEnumField(ormField: Field): ormField is ManyToManyEnumField {\n return ormField.kind === \"m2mEnum\";\n}\n\nexport function isOneToOneField(ormField: Field): ormField is OneToOneField {\n return ormField.kind === \"o2o\";\n}\n\nexport function isPolymorphicField(ormField: Field): ormField is PolymorphicField {\n return ormField.kind === \"poly\";\n}\n\nexport function isReferenceField(ormField: Field): ormField is ManyToOneField | OneToOneField | PolymorphicField {\n return ormField.kind === \"m2o\" || ormField.kind === \"o2o\" || ormField.kind === \"poly\";\n}\n\nexport function isCollectionField(ormField: Field): ormField is OneToManyField | ManyToManyField {\n return ormField.kind === \"o2m\" || ormField.kind === \"m2m\";\n}\n\nexport function getBaseAndSelfMetas(meta: EntityMetadata): EntityMetadata[];\nexport function getBaseAndSelfMetas(entity: Entity): EntityMetadata[];\nexport function getBaseAndSelfMetas(param: Entity | EntityMetadata): EntityMetadata[] {\n return isEntity(param)\n ? getBaseSelfAndSubMetas(getMetadata(param))\n : (param.baseSelfMetas ?? [...param.baseTypes, param]);\n}\n\nexport function getBaseSelfAndSubMetas(meta: EntityMetadata): EntityMetadata[] {\n return meta.baseSelfAndSubMetas ?? [...meta.baseTypes, meta, ...meta.subTypes];\n}\n\nexport function getSubMetas(meta: EntityMetadata): EntityMetadata[] {\n // We should do recursion at some point\n return meta.subTypes;\n}\n\nexport function getBaseMeta(meta: EntityMetadata): EntityMetadata {\n if (!meta.baseType) {\n return meta;\n } else {\n return meta.baseTypes[0];\n }\n}\n"],"mappings":";;;;;;;;AAYA,SAAgB,YACd,OACgB;CAChB,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,yBAAyB,OAAO;CAC5D,OACE,OAAO,UAAU,aAAc,MAAc,WAAW,UAAU,QAAQ,QAAQ,gBAAgB,KAAK,CAAC,CAAC;AAE7G;;AAGA,SAAgB,oBAAoB,MAAsB,WAAmC;CAC3F,IAAI,CAAC,KAAK,UAAU,YAAY,MAAM,IAAI,MAAM,UAAU,UAAU,iBAAiB,KAAK,MAAM;CAGhG,OAAO,EAAE,aAAa,KAAK,WAAW,KAAK,UAAU,UAAU,EAAE,gBAAgB,QAAQ,KAAK,UAC5F,OAAO,mBAAmB,KAAK,QAAQ;CAEzC,OAAO;AACT;AA0PA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAA6C;CAC5E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,kBAAkB,UAA8C;CAC9E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,sBAAsB,UAAkD;CACtF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,gBAAgB,UAA4C;CAC1E,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,mBAAmB,UAA+C;CAChF,OAAO,SAAS,SAAS;AAC3B;AAEA,SAAgB,iBAAiB,UAAgF;CAC/G,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS,SAAS;AACjF;AAEA,SAAgB,kBAAkB,UAA+D;CAC/F,OAAO,SAAS,SAAS,SAAS,SAAS,SAAS;AACtD;AAIA,SAAgB,oBAAoB,OAAkD;CACpF,OAAO,SAAS,KAAK,IACjB,uBAAuB,YAAY,KAAK,CAAC,IACxC,MAAM,iBAAiB,CAAC,GAAG,MAAM,WAAW,KAAK;AACxD;AAEA,SAAgB,uBAAuB,MAAwC;CAC7E,OAAO,KAAK,uBAAuB;EAAC,GAAG,KAAK;EAAW;EAAM,GAAG,KAAK;CAAQ;AAC/E;AAEA,SAAgB,YAAY,MAAwC;CAElE,OAAO,KAAK;AACd;AAEA,SAAgB,YAAY,MAAsC;CAChE,IAAI,CAAC,KAAK,UACR,OAAO;MAEP,OAAO,KAAK,UAAU;AAE1B"}
|
package/build/Tables.cjs
CHANGED
|
@@ -12,7 +12,6 @@ require("./index.cjs");
|
|
|
12
12
|
require("./EntityManager.cjs");
|
|
13
13
|
require("./Entity.cjs");
|
|
14
14
|
const require_configure = require("./configure.cjs");
|
|
15
|
-
const require_Aliases = require("./Aliases.cjs");
|
|
16
15
|
const require_QueryParser = require("./QueryParser.cjs");
|
|
17
16
|
let joist_utils = require("joist-utils");
|
|
18
17
|
//#region src/Tables.ts
|
|
@@ -37,6 +36,7 @@ function newTableProxy(cstr) {
|
|
|
37
36
|
tableName: meta.tableName,
|
|
38
37
|
meta
|
|
39
38
|
};
|
|
39
|
+
const subtypes = new Map(meta.subTypes.map((m) => [m.type[0].toLowerCase() + m.type.slice(1), m]));
|
|
40
40
|
const proxy = new Proxy(cstr, {
|
|
41
41
|
/** Create a physical column expression or a relation join factory. */
|
|
42
42
|
get(_, key) {
|
|
@@ -44,11 +44,20 @@ function newTableProxy(cstr) {
|
|
|
44
44
|
if (typeof key !== "string") return void 0;
|
|
45
45
|
const descriptor = Object.hasOwn(meta.columns, key) ? meta.columns[key] : void 0;
|
|
46
46
|
if (descriptor) {
|
|
47
|
-
const
|
|
47
|
+
const { field } = descriptor;
|
|
48
48
|
const column = field.serde?.columns.find((c) => c.columnName === key) ?? require_utils.fail(`No column ${key} on ${cstr.name}`);
|
|
49
49
|
return field.kind === "m2o" || field.kind === "poly" ? new EntityColumnImpl(meta, field, column, mgmt) : new PrimitiveColumnImpl(meta, field, column, mgmt);
|
|
50
50
|
}
|
|
51
|
-
const
|
|
51
|
+
const subtype = subtypes.get(key);
|
|
52
|
+
if (subtype) {
|
|
53
|
+
const join = new SubtypeJoinImpl(proxy, subtype);
|
|
54
|
+
return Object.assign(join.as.bind(join), {
|
|
55
|
+
as: join.as.bind(join),
|
|
56
|
+
inner: join.inner.bind(join),
|
|
57
|
+
left: join.left.bind(join)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const field = physicalRelation(meta, key) ?? require_utils.fail(`No physical field ${key} on ${cstr.name}; join its base table explicitly`);
|
|
52
61
|
switch (field.kind) {
|
|
53
62
|
case "m2o": return new ReferenceJoinImpl(new EntityColumnImpl(meta, field, field.serde.columns[0], mgmt));
|
|
54
63
|
case "poly": return new PolyReferenceImpl(meta, mgmt, field);
|
|
@@ -60,7 +69,7 @@ function newTableProxy(cstr) {
|
|
|
60
69
|
}
|
|
61
70
|
},
|
|
62
71
|
has(_, key) {
|
|
63
|
-
return key === tableMgmt || typeof key === "string" && (Object.hasOwn(meta.columns, key) ||
|
|
72
|
+
return key === tableMgmt || typeof key === "string" && (Object.hasOwn(meta.columns, key) || !!physicalRelation(meta, key) || subtypes.has(key));
|
|
64
73
|
}
|
|
65
74
|
});
|
|
66
75
|
return proxy;
|
|
@@ -108,9 +117,8 @@ var TableColumn = class extends require_Expr.BaseExpr {
|
|
|
108
117
|
}
|
|
109
118
|
toSql(ctx) {
|
|
110
119
|
const alias = ctx.aliasFor(this.mgmt);
|
|
111
|
-
const ctiAlias = require_Aliases.getMaybeCtiAlias(this.meta, this.field, this.meta, alias);
|
|
112
120
|
return {
|
|
113
|
-
sql: require_keywords.kqDot(
|
|
121
|
+
sql: require_keywords.kqDot(alias, this.column.columnName),
|
|
114
122
|
bindings: [],
|
|
115
123
|
refs: [alias]
|
|
116
124
|
};
|
|
@@ -431,8 +439,9 @@ var PolyReferenceImpl = class {
|
|
|
431
439
|
}
|
|
432
440
|
/** Returns the physical FK expression for one polymorphic target. */
|
|
433
441
|
componentColumn(comp) {
|
|
434
|
-
const
|
|
435
|
-
|
|
442
|
+
const descriptor = this.meta.columns[comp.columnName] ?? require_utils.fail(`No physical column ${comp.columnName} on ${this.meta.type}`);
|
|
443
|
+
const column = descriptor.field.serde?.columns.find((c) => c.columnName === comp.columnName) ?? require_utils.fail("Missing column");
|
|
444
|
+
return new EntityColumnImpl(this.meta, descriptor.field, column, this.mgmt);
|
|
436
445
|
}
|
|
437
446
|
};
|
|
438
447
|
/**
|
|
@@ -467,6 +476,38 @@ var CollectionJoinImpl = class {
|
|
|
467
476
|
return this.joinEntry("left", requireTable(other));
|
|
468
477
|
}
|
|
469
478
|
};
|
|
479
|
+
/** Joins a named subtype explicitly; only STI subtype sugar adds a discriminator to its ON. */
|
|
480
|
+
var SubtypeJoinImpl = class extends CollectionJoinImpl {
|
|
481
|
+
proxy;
|
|
482
|
+
subtype;
|
|
483
|
+
constructor(proxy, subtype) {
|
|
484
|
+
super();
|
|
485
|
+
this.proxy = proxy;
|
|
486
|
+
this.subtype = subtype;
|
|
487
|
+
}
|
|
488
|
+
joinEntry(kind, other) {
|
|
489
|
+
if (getTableMetadata(other) !== this.subtype) return require_utils.fail(`Expected table(${this.subtype.type}) for the explicit subtype join`);
|
|
490
|
+
const on = idColumnOf(this.proxy).eq(idColumnOf(other));
|
|
491
|
+
if (this.subtype.inheritanceType !== "sti" || this.subtype.stiDiscriminatorValue === void 0) return {
|
|
492
|
+
[kind]: other,
|
|
493
|
+
on
|
|
494
|
+
};
|
|
495
|
+
const base = this.subtype.baseTypes.find((m) => m.stiDiscriminatorField !== void 0);
|
|
496
|
+
const columnName = base.fields[base.stiDiscriminatorField].serde.columns[0].columnName;
|
|
497
|
+
const discriminator = require_Expr.deferredCondition((ctx) => {
|
|
498
|
+
const alias = ctx.aliasFor(other[tableMgmt]);
|
|
499
|
+
return {
|
|
500
|
+
sql: `${require_keywords.kqDot(alias, columnName)} = ?`,
|
|
501
|
+
bindings: [this.subtype.stiDiscriminatorValue],
|
|
502
|
+
refs: [alias]
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
return {
|
|
506
|
+
[kind]: other,
|
|
507
|
+
on: { and: [on, discriminator] }
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
};
|
|
470
511
|
/** An o2m/lo2m/o2o relation: the ON is the other side's FK (an m2o or a poly component) back to our id. */
|
|
471
512
|
var OneToManyJoinImpl = class extends CollectionJoinImpl {
|
|
472
513
|
proxy;
|
|
@@ -478,8 +519,11 @@ var OneToManyJoinImpl = class extends CollectionJoinImpl {
|
|
|
478
519
|
}
|
|
479
520
|
joinEntry(kind, other) {
|
|
480
521
|
const { field } = this;
|
|
481
|
-
const
|
|
482
|
-
const
|
|
522
|
+
const otherMeta = getTableMetadata(other);
|
|
523
|
+
const descriptor = otherMeta.columns[field.otherColumnName];
|
|
524
|
+
if (!descriptor) return require_utils.fail(`Cannot join ${getTableMetadata(this.proxy).type}.${field.fieldName} to ${otherMeta.type}: ${field.otherColumnName} is not physically present on ${otherMeta.tableName}; join the base table explicitly`);
|
|
525
|
+
const physicalColumn = descriptor.field.serde?.columns.find((c) => c.columnName === field.otherColumnName) ?? require_utils.fail("Missing column");
|
|
526
|
+
const on = new EntityColumnImpl(otherMeta, descriptor.field, physicalColumn, other[tableMgmt]).eq(this.proxy.id);
|
|
483
527
|
const filtered = field.kind === "o2m" ? field.softDeletes !== "include" : field.kind === "lo2m";
|
|
484
528
|
return {
|
|
485
529
|
[kind]: other,
|
|
@@ -576,6 +620,18 @@ function isRelation(field) {
|
|
|
576
620
|
"o2o"
|
|
577
621
|
].includes(field.kind);
|
|
578
622
|
}
|
|
623
|
+
/** Resolves relationship sugar only from domain fields owned by this physical table. */
|
|
624
|
+
function physicalRelation(meta, key) {
|
|
625
|
+
const field = [meta, ...require_EntityMetadata.getBaseSelfAndSubMetas(require_EntityMetadata.getBaseMeta(meta))].find((owner) => owner.tableName === meta.tableName && Object.hasOwn(owner.fields, key))?.fields[key];
|
|
626
|
+
if (!isRelation(field)) return void 0;
|
|
627
|
+
if (field?.kind === "m2o" || field?.kind === "poly") {
|
|
628
|
+
const columnName = field.serde.columns[0].columnName;
|
|
629
|
+
const storage = meta.columns[columnName]?.field;
|
|
630
|
+
if (!storage || storage.kind !== field.kind || !storage.serde.columns.every((column) => Object.hasOwn(meta.columns, column.columnName))) return void 0;
|
|
631
|
+
return storage;
|
|
632
|
+
}
|
|
633
|
+
return field;
|
|
634
|
+
}
|
|
579
635
|
//#endregion
|
|
580
636
|
exports.JoinTableHandle = JoinTableHandle;
|
|
581
637
|
exports.collectionJoin = collectionJoin;
|