@tstdl/base 0.93.9 → 0.93.11

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.
Files changed (46) hide show
  1. package/audit/module.d.ts +3 -3
  2. package/audit/module.js +3 -3
  3. package/document-management/api/document-management.api.d.ts +6 -6
  4. package/document-management/service-models/document.service-model.d.ts +3 -3
  5. package/injector/decorators.d.ts +7 -0
  6. package/injector/decorators.js +10 -6
  7. package/injector/injector.js +73 -30
  8. package/orm/decorators.d.ts +35 -3
  9. package/orm/decorators.js +6 -0
  10. package/orm/query.d.ts +65 -30
  11. package/orm/query.js +2 -6
  12. package/orm/repository.types.d.ts +72 -1
  13. package/orm/server/drizzle/schema-converter.js +31 -2
  14. package/orm/server/query-converter.d.ts +5 -7
  15. package/orm/server/query-converter.js +69 -15
  16. package/orm/server/repository.d.ts +19 -7
  17. package/orm/server/repository.js +144 -11
  18. package/orm/sqls.d.ts +153 -8
  19. package/orm/sqls.js +161 -8
  20. package/package.json +5 -5
  21. package/schema/schemas/object.js +1 -1
  22. package/test/drizzle/0000_sudden_sphinx.sql +9 -0
  23. package/test/drizzle/meta/0000_snapshot.json +79 -0
  24. package/test/drizzle/meta/_journal.json +13 -0
  25. package/test/drizzle.config.d.ts +2 -0
  26. package/test/drizzle.config.js +11 -0
  27. package/test/index.d.ts +3 -0
  28. package/test/index.js +3 -0
  29. package/test/module.d.ts +6 -0
  30. package/test/module.js +17 -0
  31. package/test/schemas.d.ts +3 -0
  32. package/test/schemas.js +4 -0
  33. package/test/test.model.d.ts +8 -0
  34. package/test/test.model.js +345 -0
  35. package/test1.d.ts +1 -0
  36. package/test1.js +59 -0
  37. package/test2.d.ts +1 -0
  38. package/test2.js +32 -0
  39. package/test3.d.ts +1 -0
  40. package/test3.js +47 -0
  41. package/test4.d.ts +23 -0
  42. package/test4.js +168 -0
  43. package/test5.d.ts +1 -0
  44. package/test5.js +22 -0
  45. package/test6.d.ts +1 -0
  46. package/test6.js +53 -0
package/audit/module.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type DatabaseConfig } from '../orm/server/index.js';
2
2
  /**
3
- * Configuration for {@link configureAuditServer}.
3
+ * Configuration for {@link configureAudit}.
4
4
  */
5
5
  export declare class AuditModuleConfig {
6
6
  /**
@@ -13,10 +13,10 @@ export declare class AuditModuleConfig {
13
13
  * Configures audit server services.
14
14
  * @param config Configuration.
15
15
  */
16
- export declare function configureAuditServer(config: AuditModuleConfig): void;
16
+ export declare function configureAudit(config: AuditModuleConfig): void;
17
17
  /**
18
18
  * Migrates the audit database schema to the latest version.
19
- * It uses the database connection provided with {@link configureAuditServer},
19
+ * It uses the database connection provided with {@link configureAudit},
20
20
  * or falls back to the globally configured database if no specific configuration is provided.
21
21
  */
22
22
  export declare function migrateAuditSchema(): Promise<void>;
package/audit/module.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { inject, Injector } from '../injector/index.js';
2
2
  import { Database, migrate } from '../orm/server/index.js';
3
3
  /**
4
- * Configuration for {@link configureAuditServer}.
4
+ * Configuration for {@link configureAudit}.
5
5
  */
6
6
  export class AuditModuleConfig {
7
7
  /**
@@ -14,12 +14,12 @@ export class AuditModuleConfig {
14
14
  * Configures audit server services.
15
15
  * @param config Configuration.
16
16
  */
17
- export function configureAuditServer(config) {
17
+ export function configureAudit(config) {
18
18
  Injector.register(AuditModuleConfig, { useValue: config });
19
19
  }
20
20
  /**
21
21
  * Migrates the audit database schema to the latest version.
22
- * It uses the database connection provided with {@link configureAuditServer},
22
+ * It uses the database connection provided with {@link configureAudit},
23
23
  * or falls back to the globally configured database if no specific configuration is provided.
24
24
  */
25
25
  export async function migrateAuditSchema() {
@@ -127,8 +127,8 @@ export declare const documentManagementApiDefinition: {
127
127
  method: "POST";
128
128
  parameters: import("../../schema/index.js").ObjectSchema<{
129
129
  date?: import("../../orm/types.js").NumericDate | null | undefined;
130
- typeId?: import("../../orm/types.js").Uuid | null | undefined;
131
130
  title?: string | null | undefined;
131
+ typeId?: import("../../orm/types.js").Uuid | null | undefined;
132
132
  subtitle?: string | null | undefined;
133
133
  summary?: string | null | undefined;
134
134
  comment?: string | null | undefined;
@@ -326,8 +326,8 @@ export declare const documentManagementApiDefinition: {
326
326
  parameters: import("../../schema/index.js").ObjectSchema<{
327
327
  id: import("../../orm/types.js").IsPrimaryKey<import("../../orm/types.js").HasDefault<import("../../orm/types.js").Uuid>>;
328
328
  date?: import("../../orm/types.js").NumericDate | null | undefined;
329
- typeId?: import("../../orm/types.js").Uuid | null | undefined;
330
329
  title?: string | null | undefined;
330
+ typeId?: import("../../orm/types.js").Uuid | null | undefined;
331
331
  subtitle?: string | null | undefined;
332
332
  comment?: string | null | undefined;
333
333
  properties?: {
@@ -337,11 +337,11 @@ export declare const documentManagementApiDefinition: {
337
337
  attributes: import("../../orm/types.js").HasDefault<import("../../orm/types.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
338
338
  }> | undefined;
339
339
  }[] | undefined;
340
+ tags?: string[] | undefined;
340
341
  collections?: {
341
342
  assign?: string[] | undefined;
342
343
  archive?: string[] | undefined;
343
344
  } | undefined;
344
- tags?: string[] | undefined;
345
345
  metadata?: Partial<{
346
346
  attributes: import("../../orm/types.js").HasDefault<import("../../orm/types.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
347
347
  }> | undefined;
@@ -527,8 +527,8 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
527
527
  method: "POST";
528
528
  parameters: import("../../schema/index.js").ObjectSchema<{
529
529
  date?: import("../../orm/types.js").NumericDate | null | undefined;
530
- typeId?: import("../../orm/types.js").Uuid | null | undefined;
531
530
  title?: string | null | undefined;
531
+ typeId?: import("../../orm/types.js").Uuid | null | undefined;
532
532
  subtitle?: string | null | undefined;
533
533
  summary?: string | null | undefined;
534
534
  comment?: string | null | undefined;
@@ -726,8 +726,8 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
726
726
  parameters: import("../../schema/index.js").ObjectSchema<{
727
727
  id: import("../../orm/types.js").IsPrimaryKey<import("../../orm/types.js").HasDefault<import("../../orm/types.js").Uuid>>;
728
728
  date?: import("../../orm/types.js").NumericDate | null | undefined;
729
- typeId?: import("../../orm/types.js").Uuid | null | undefined;
730
729
  title?: string | null | undefined;
730
+ typeId?: import("../../orm/types.js").Uuid | null | undefined;
731
731
  subtitle?: string | null | undefined;
732
732
  comment?: string | null | undefined;
733
733
  properties?: {
@@ -737,11 +737,11 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
737
737
  attributes: import("../../orm/types.js").HasDefault<import("../../orm/types.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
738
738
  }> | undefined;
739
739
  }[] | undefined;
740
+ tags?: string[] | undefined;
740
741
  collections?: {
741
742
  assign?: string[] | undefined;
742
743
  archive?: string[] | undefined;
743
744
  } | undefined;
744
- tags?: string[] | undefined;
745
745
  metadata?: Partial<{
746
746
  attributes: import("../../orm/types.js").HasDefault<import("../../orm/types.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
747
747
  }> | undefined;
@@ -20,8 +20,8 @@ export declare const updateDocumentCollectionsParametersSchema: import("../../sc
20
20
  }>;
21
21
  export declare const createDocumentParametersSchema: import("../../schema/index.js").ObjectSchema<{
22
22
  date?: import("../../orm/index.js").NumericDate | null | undefined;
23
- typeId?: import("../../orm/index.js").Uuid | null | undefined;
24
23
  title?: string | null | undefined;
24
+ typeId?: import("../../orm/index.js").Uuid | null | undefined;
25
25
  subtitle?: string | null | undefined;
26
26
  summary?: string | null | undefined;
27
27
  comment?: string | null | undefined;
@@ -53,8 +53,8 @@ export declare const createDocumentParametersSchema: import("../../schema/index.
53
53
  export declare const updateDocumentParametersSchema: import("../../schema/index.js").ObjectSchema<{
54
54
  id: import("../../orm/index.js").IsPrimaryKey<import("../../orm/index.js").HasDefault<import("../../orm/index.js").Uuid>>;
55
55
  date?: import("../../orm/index.js").NumericDate | null | undefined;
56
- typeId?: import("../../orm/index.js").Uuid | null | undefined;
57
56
  title?: string | null | undefined;
57
+ typeId?: import("../../orm/index.js").Uuid | null | undefined;
58
58
  subtitle?: string | null | undefined;
59
59
  comment?: string | null | undefined;
60
60
  properties?: {
@@ -64,11 +64,11 @@ export declare const updateDocumentParametersSchema: import("../../schema/index.
64
64
  attributes: import("../../orm/index.js").HasDefault<import("../../orm/index.js").Json<import("../../orm/index.js").EntityMetadataAttributes>>;
65
65
  }> | undefined;
66
66
  }[] | undefined;
67
+ tags?: string[] | undefined;
67
68
  collections?: {
68
69
  assign?: string[] | undefined;
69
70
  archive?: string[] | undefined;
70
71
  } | undefined;
71
- tags?: string[] | undefined;
72
72
  metadata?: Partial<{
73
73
  attributes: import("../../orm/index.js").HasDefault<import("../../orm/index.js").Json<import("../../orm/index.js").EntityMetadataAttributes>>;
74
74
  }> | undefined;
@@ -10,6 +10,13 @@ export type InjectableOptions<T, A, C extends Record = Record> = RegistrationOpt
10
10
  alias?: OneOrMany<InjectionToken>;
11
11
  /** Custom provider. Useful for example if initialization is required */
12
12
  provider?: Provider<T, A, C>;
13
+ /**
14
+ * Which InjectableOptions to inherit from the parent (class which is extended from).
15
+ * If true (default), all options are inherited.
16
+ * If false, no options are inherited.
17
+ * If array of keys, only the provided options are inherited.
18
+ */
19
+ inheritOptions?: boolean | (keyof InjectableOptions<T, A, C>)[];
13
20
  };
14
21
  export type InjectableOptionsWithoutLifecycle<T, A, C extends Record = Record> = Simplify<TypedOmit<InjectableOptions<T, A, C>, 'lifecycle'>>;
15
22
  /**
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/naming-convention */
2
2
  import { createClassDecorator, createDecorator, reflectionRegistry } from '../reflection/index.js';
3
3
  import { toArray } from '../utils/array/array.js';
4
- import { isDefined, isFunction, isNotNull } from '../utils/type-guards.js';
4
+ import { filterObject } from '../utils/object/object.js';
5
+ import { isArray, isDefined, isFunction, isNotNull } from '../utils/type-guards.js';
5
6
  import { Injector } from './injector.js';
6
7
  import { injectMetadataSymbol, injectableMetadataSymbol, injectableOptionsSymbol } from './symbols.js';
7
8
  /**
@@ -33,20 +34,23 @@ export function Injectable(options = {}) {
33
34
  const { alias: aliases, provider, ...registrationOptions } = options;
34
35
  const token = data.constructor;
35
36
  let mergedRegistationOptions = registrationOptions;
36
- if (isNotNull(metadata.parent)) {
37
+ if (isNotNull(metadata.parent) && (options.inheritOptions != false)) {
37
38
  const parentOptions = reflectionRegistry.getMetadata(metadata.parent)?.data.tryGet(injectableOptionsSymbol);
38
39
  if (isDefined(parentOptions)) {
39
40
  const { alias: _, provider: __, ...parentRegistrationOptions } = parentOptions;
41
+ const optionsToInherit = isArray(options.inheritOptions)
42
+ ? filterObject(parentRegistrationOptions, (_value, key) => options.inheritOptions.includes(key))
43
+ : parentRegistrationOptions;
40
44
  mergedRegistationOptions = {
41
- ...parentRegistrationOptions,
45
+ ...optionsToInherit,
42
46
  ...registrationOptions,
43
- providers: [...(parentRegistrationOptions.providers ?? []), ...(registrationOptions.providers ?? [])],
47
+ providers: [...(optionsToInherit.providers ?? []), ...(registrationOptions.providers ?? [])],
44
48
  afterResolve: (instance, argument, context) => {
45
- parentRegistrationOptions.afterResolve?.(instance, argument, context);
49
+ optionsToInherit.afterResolve?.(instance, argument, context);
46
50
  registrationOptions.afterResolve?.(instance, argument, context);
47
51
  },
48
52
  metadata: {
49
- ...parentRegistrationOptions.metadata,
53
+ ...optionsToInherit.metadata,
50
54
  ...registrationOptions.metadata,
51
55
  },
52
56
  };
@@ -331,8 +331,16 @@ export class Injector {
331
331
  const resolutionScoped = registration.options.lifecycle == 'resolution';
332
332
  const injectorScoped = registration.options.lifecycle == 'injector';
333
333
  const singletonScoped = registration.options.lifecycle == 'singleton';
334
- const resolveArgument = argument ?? registration.options.defaultArgument ?? (registration.options.defaultArgumentProvider?.(injector.getResolveContext(resolutionTag, context, chain)));
335
- const argumentIdentity = resolveArgumentIdentity(registration, resolveArgument);
334
+ let resolveArgument = argument ?? registration.options.defaultArgument;
335
+ if (isUndefined(resolveArgument) && isFunction(registration.options.defaultArgumentProvider)) {
336
+ try {
337
+ resolveArgument = registration.options.defaultArgumentProvider(injector.getResolveContext(resolutionTag, context, chain));
338
+ }
339
+ catch (error) {
340
+ throw new ResolveError('Error in defaultArgumentProvider.', chain, error);
341
+ }
342
+ }
343
+ const argumentIdentity = resolveArgumentIdentity(registration, resolveArgument, chain);
336
344
  if (resolutionScoped && context.resolutionScopedResolutions.hasFlat(token, argumentIdentity)) {
337
345
  return context.resolutionScopedResolutions.getFlat(token, argumentIdentity).value;
338
346
  }
@@ -436,7 +444,13 @@ export class Injector {
436
444
  if (isDefined(injectMetadata.injectArgumentMapper) && (!this.hasRegistration(injectToken) || isDefined(resolveArgument) || isUndefined(injectToken))) {
437
445
  return injectMetadata.injectArgumentMapper(resolveArgument);
438
446
  }
439
- const parameterResolveArgument = injectMetadata.forwardArgumentMapper?.(resolveArgument) ?? injectMetadata.resolveArgumentProvider?.(this.getResolveContext(resolutionTag, context, getChain(injectToken)));
447
+ let parameterResolveArgument;
448
+ try {
449
+ parameterResolveArgument = injectMetadata.forwardArgumentMapper?.(resolveArgument) ?? injectMetadata.resolveArgumentProvider?.(this.getResolveContext(resolutionTag, context, getChain(injectToken)));
450
+ }
451
+ catch (error) {
452
+ throw new ResolveError('Error in parameter argument provider (forwardArgumentMapper or resolveArgumentProvider).', getChain(injectToken), error);
453
+ }
440
454
  const { forwardRef } = injectMetadata;
441
455
  if (isDefined(forwardRef) && isDefined(injectMetadata.mapper)) {
442
456
  const forwardToken = isFunction(forwardRef) ? forwardRef() : isBoolean(forwardRef) ? injectToken : forwardRef;
@@ -444,7 +458,15 @@ export class Injector {
444
458
  }
445
459
  const resolveFn = (injectMetadata.resolveAll == true) ? '_resolveAll' : '_resolve';
446
460
  const resolved = this[resolveFn](injectToken, parameterResolveArgument, { optional: injectMetadata.optional, forwardRef, forwardRefTypeHint: injectMetadata.forwardRefTypeHint }, context, getChain(injectToken));
447
- return isDefined(injectMetadata.mapper) ? injectMetadata.mapper(resolved) : resolved;
461
+ if (isDefined(injectMetadata.mapper)) {
462
+ try {
463
+ return injectMetadata.mapper(resolved);
464
+ }
465
+ catch (error) {
466
+ throw new ResolveError('Error in inject mapper.', getChain(injectToken), error);
467
+ }
468
+ }
469
+ return resolved;
448
470
  }
449
471
  resolveInjection(token, argument, options, context, injectIndex, chain) {
450
472
  return this._resolve(token, argument, options, context, chain.addInject(token, injectIndex));
@@ -546,21 +568,29 @@ function postProcess(context) {
546
568
  }
547
569
  derefForwardRefs(context);
548
570
  for (const resolution of context.resolutions) {
549
- for (const afterResolveHandler of resolution.afterResolveRegistrations) {
550
- const returnValue = afterResolveHandler(resolution.argument, resolution.afterResolveContext);
551
- throwOnPromise(returnValue, 'registerAfterResolve()', resolution.chain);
552
- }
553
- if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
554
- const returnValue = resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
555
- throwOnPromise(returnValue, '[afterResolve]', resolution.chain);
556
- }
557
- if (isProviderWithInitializer(resolution.registration.provider)) {
558
- const returnValue = resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
559
- throwOnPromise(returnValue, 'provider afterResolve handler', resolution.chain);
571
+ try {
572
+ for (const afterResolveHandler of resolution.afterResolveRegistrations) {
573
+ const returnValue = afterResolveHandler(resolution.argument, resolution.afterResolveContext);
574
+ throwOnPromise(returnValue, 'registerAfterResolve()', resolution.chain);
575
+ }
576
+ if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
577
+ const returnValue = resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
578
+ throwOnPromise(returnValue, '[afterResolve]', resolution.chain);
579
+ }
580
+ if (isProviderWithInitializer(resolution.registration.provider)) {
581
+ const returnValue = resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
582
+ throwOnPromise(returnValue, 'provider afterResolve handler', resolution.chain);
583
+ }
584
+ if (isDefined(resolution.registration.options.afterResolve)) {
585
+ const returnValue = resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
586
+ throwOnPromise(returnValue, 'registration afterResolve handler', resolution.chain);
587
+ }
560
588
  }
561
- if (isDefined(resolution.registration.options.afterResolve)) {
562
- const returnValue = resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
563
- throwOnPromise(returnValue, 'registration afterResolve handler', resolution.chain);
589
+ catch (error) {
590
+ if (error instanceof ResolveError) {
591
+ throw error;
592
+ }
593
+ throw new ResolveError('Error in afterResolve hook.', resolution.chain, error);
564
594
  }
565
595
  }
566
596
  }
@@ -570,23 +600,36 @@ async function postProcessAsync(context) {
570
600
  }
571
601
  derefForwardRefs(context);
572
602
  for (const resolution of context.resolutions) {
573
- for (const afterResolveHandler of resolution.afterResolveRegistrations) {
574
- await afterResolveHandler(resolution.argument, resolution.afterResolveContext);
575
- }
576
- if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
577
- await resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
578
- }
579
- if (isProviderWithInitializer(resolution.registration.provider)) {
580
- await resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
603
+ try {
604
+ for (const afterResolveHandler of resolution.afterResolveRegistrations) {
605
+ await afterResolveHandler(resolution.argument, resolution.afterResolveContext);
606
+ }
607
+ if (!isTokenProvider(resolution.registration.provider) && isFunction(resolution.value?.[afterResolve])) {
608
+ await resolution.value[afterResolve](resolution.argument, resolution.afterResolveContext);
609
+ }
610
+ if (isProviderWithInitializer(resolution.registration.provider)) {
611
+ await resolution.registration.provider.afterResolve?.(resolution.value, resolution.argument, resolution.afterResolveContext);
612
+ }
613
+ if (isDefined(resolution.registration.options.afterResolve)) {
614
+ await resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
615
+ }
581
616
  }
582
- if (isDefined(resolution.registration.options.afterResolve)) {
583
- await resolution.registration.options.afterResolve(resolution.value, resolution.argument, resolution.afterResolveContext);
617
+ catch (error) {
618
+ if (error instanceof ResolveError) {
619
+ throw error;
620
+ }
621
+ throw new ResolveError('Error in async afterResolve hook.', resolution.chain, error);
584
622
  }
585
623
  }
586
624
  }
587
- function resolveArgumentIdentity(registration, resolveArgument) {
625
+ function resolveArgumentIdentity(registration, resolveArgument, chain) {
588
626
  if (isDefined(registration.options.argumentIdentityProvider) && ((registration.options.lifecycle == 'resolution') || (registration.options.lifecycle == 'singleton'))) {
589
- return registration.options.argumentIdentityProvider(resolveArgument);
627
+ try {
628
+ return registration.options.argumentIdentityProvider(resolveArgument);
629
+ }
630
+ catch (error) {
631
+ throw new ResolveError('Error in argumentIdentityProvider.', chain, error);
632
+ }
590
633
  }
591
634
  return resolveArgument;
592
635
  }
@@ -5,14 +5,15 @@
5
5
  import type { SQL } from 'drizzle-orm';
6
6
  import type { LiteralUnion } from 'type-fest';
7
7
  import { type SpecificCreateDecoratorOptions } from '../reflection/index.js';
8
- import type { AbstractConstructor, TypedOmit } from '../types/index.js';
8
+ import type { AbstractConstructor, Record, TypedOmit } from '../types/index.js';
9
9
  import type { AnyEntity, Entity, EntityType, EntityWithoutMetadata } from './entity.js';
10
10
  import type { Query } from './query.js';
11
11
  import type { TargetColumnPaths } from './repository.types.js';
12
12
  import type { PgTableFromType } from './server/types.js';
13
13
  type IndexMethod = LiteralUnion<'hash' | 'btree' | 'gist' | 'spgist' | 'gin' | 'brin' | 'hnsw' | 'ivfflat', string>;
14
14
  type NamingStrategy = 'abbreviated-table';
15
- type Columns<T> = [Extract<keyof T, string>, ...Extract<keyof T, string>[]];
15
+ type Column<T> = Extract<keyof T, string>;
16
+ type Columns<T> = [Column<T>, ...Column<T>[]];
16
17
  /**
17
18
  * Builder function type for creating SQL check constraints.
18
19
  * @param table The Drizzle table object.
@@ -77,7 +78,7 @@ export type UniqueReflectionData = {
77
78
  * @template T The entity type.
78
79
  */
79
80
  export type IndexReflectionData<T extends Entity | EntityWithoutMetadata = any> = {
80
- columns?: (string | [string, 'asc' | 'desc'])[];
81
+ columns?: (string | [string, 'asc' | 'desc'] | SQL)[];
81
82
  order?: 'asc' | 'desc';
82
83
  options?: {
83
84
  name?: string;
@@ -86,6 +87,22 @@ export type IndexReflectionData<T extends Entity | EntityWithoutMetadata = any>
86
87
  unique?: boolean;
87
88
  where?: WhereBuilder<T>;
88
89
  nulls?: 'first' | 'last';
90
+ /**
91
+ * For full-text search indexes (e.g., GIN), specifies the text search configuration language (e.g., 'english').
92
+ * Can also be a SQL object for dynamic configuration.
93
+ */
94
+ language?: string | SQL;
95
+ /**
96
+ * For multi-column full-text search indexes, specifies how to create the tsvector.
97
+ * - 'concat': (Default) Concatenate columns into a single tsvector.
98
+ * - 'separate': Create a separate tsvector for each column, useful for indexing columns with different languages or weights.
99
+ */
100
+ vectors?: 'concat' | 'separate';
101
+ /**
102
+ * For multi-column full-text search indexes, specifies weights for columns.
103
+ * Keys are column names, values are 'A', 'B', 'C', or 'D'.
104
+ */
105
+ weights?: Partial<Record<TargetColumnPaths<any>, 'A' | 'B' | 'C' | 'D'>>;
89
106
  };
90
107
  };
91
108
  type CheckReflectionData = {
@@ -104,6 +121,10 @@ export type ForeignKeyReflectionData = {
104
121
  naming?: NamingStrategy;
105
122
  };
106
123
  };
124
+ export type GinIndexOptions<T extends Entity | EntityWithoutMetadata = any> = TypedOmit<NonNullable<IndexReflectionData<T>['options']>, 'using' | 'weights'> & {
125
+ weights?: Partial<Record<TargetColumnPaths<any>, 'A' | 'B' | 'C' | 'D'>>;
126
+ language?: string | SQL;
127
+ };
107
128
  /**
108
129
  * Factory function to create a class decorator for ORM table configuration.
109
130
  * Merges provided data with existing ORM reflection data on the class metadata.
@@ -204,6 +225,17 @@ export declare function Index<T extends Entity | EntityWithoutMetadata = any>(op
204
225
  * @param options Additional index options.
205
226
  */
206
227
  export declare function Index<T extends Entity | EntityWithoutMetadata = any>(columns: Columns<T>, options?: IndexReflectionData<T>['options']): ClassDecorator;
228
+ /**
229
+ * Defines a GIN index on a single column, often for full-text search.
230
+ * @param options GIN index options. For full-text search, `language` is required.
231
+ */
232
+ export declare function GinIndex<T extends Entity | EntityWithoutMetadata = any>(options: GinIndexOptions<T>): PropertyDecorator;
233
+ /**
234
+ * Defines a composite GIN index on multiple columns, often for full-text search.
235
+ * @param columnsOrSql The columns to include in the index.
236
+ * @param options GIN index options. For full-text search, providing a `language` is highly recommended.
237
+ */
238
+ export declare function GinIndex<T extends Entity | EntityWithoutMetadata = any>(columnsOrSql: (Column<T> | SQL)[], options?: GinIndexOptions<T>): ClassDecorator;
207
239
  /**
208
240
  * Automatically expire records after a certain time to live (TTL) based on the createTimestamp metadata. Requires extension of {@link Entity} instead of {@link EntityWithoutMetadata}.
209
241
  * @param ttl Time To Live in milliseconds.
package/orm/decorators.js CHANGED
@@ -137,6 +137,12 @@ export function Index(columnsOrOptions, options) {
137
137
  }
138
138
  return createColumnDecorator({ index: { options: columnsOrOptions } });
139
139
  }
140
+ export function GinIndex(columnsOrSqlOrOptions, options) {
141
+ if (isArray(columnsOrSqlOrOptions)) {
142
+ return createTableDecorator({ index: [{ columns: columnsOrSqlOrOptions, options: { ...options, using: 'gin' } }] });
143
+ }
144
+ return createColumnDecorator({ index: { options: { ...columnsOrSqlOrOptions, using: 'gin' } } });
145
+ }
140
146
  /**
141
147
  * Automatically expire records after a certain time to live (TTL) based on the createTimestamp metadata. Requires extension of {@link Entity} instead of {@link EntityWithoutMetadata}.
142
148
  * @param ttl Time To Live in milliseconds.
package/orm/query.d.ts CHANGED
@@ -1,11 +1,6 @@
1
- /**
2
- * @module
3
- * Defines types for constructing database queries using a MongoDB-like syntax.
4
- * Supports logical operators, comparison operators, and specialized queries like text search and geospatial queries.
5
- */
6
- import type { SQLWrapper } from 'drizzle-orm';
7
- import type { Flatten, Record } from '../types/index.js';
1
+ import type { SQL, SQLWrapper } from 'drizzle-orm';
8
2
  import type { Geometry } from '../types/geo-json.js';
3
+ import type { Flatten, Record } from '../types/index.js';
9
4
  import type { UntaggedDeep } from '../types/tagged.js';
10
5
  /** Represents a logical query combining multiple sub-queries (e.g., $and, $or, $nor). */
11
6
  export type LogicalQuery<T = any> = LogicalAndQuery<T> | LogicalOrQuery<T> | LogicalNorQuery<T>;
@@ -20,13 +15,14 @@ export type ComparisonQueryBody<T = any> = {
20
15
  /** Represents either a full comparison query object or a direct value for equality comparison. */
21
16
  export type ComparisonQueryOrValue<T = any> = ComparisonQuery<T> | ComparisonValue<T>;
22
17
  /** Represents a comparison query using various operators like $eq, $ne, $gt, $in, etc. */
23
- export type ComparisonQuery<T = any> = Partial<ComparisonAndQuery<T> & ComparisonOrQuery<T> & ComparisonNotQuery<T> & ComparisonEqualsQuery<T> & ComparisonNotEqualsQuery<T> & ComparisonExistsQuery & ComparisonItemQuery<T> & ComparisonInQuery<T> & ComparisonNotInQuery<T> & ComparisonAllQuery<T> & ComparisonGreaterThanQuery<T> & ComparisonGreaterThanOrEqualsQuery<T> & ComparisonLessThanQuery<T> & ComparisonLessThanOrEqualsQuery<T> & ComparisonRegexQuery & ComparisonTextQuery & ComparisonGeoShapeQuery & ComparisonGeoDistanceQuery>;
18
+ export type ComparisonQuery<T = any> = Partial<ComparisonAndQuery<T> & ComparisonOrQuery<T> & ComparisonNotQuery<T> & ComparisonEqualsQuery<T> & ComparisonNotEqualsQuery<T> & ComparisonExistsQuery & ComparisonItemQuery<T> & ComparisonInQuery<T> & ComparisonNotInQuery<T> & ComparisonAllQuery<T> & ComparisonGreaterThanQuery<T> & ComparisonGreaterThanOrEqualsQuery<T> & ComparisonLessThanQuery<T> & ComparisonLessThanOrEqualsQuery<T> & ComparisonRegexQuery & ComparisonFtsQuery & ComparisonGeoShapeQuery & ComparisonGeoDistanceQuery>;
19
+ export type FtsParser = 'raw' | 'plain' | 'phrase' | 'websearch';
24
20
  /** Union of keys representing comparison query operators. */
25
21
  export type ComparisonQueryTypes = keyof ComparisonQuery;
26
22
  /** Array containing all valid comparison query operator keys. */
27
23
  export declare const allComparisonQueryTypes: ComparisonQueryTypes[];
28
- /** Represents specialized query types beyond simple comparisons (e.g., $textSpan). */
29
- export type SpecialQuery<T = any> = Partial<TextSpanQuery<T>>;
24
+ /** Represents specialized query types beyond simple comparisons. */
25
+ export type SpecialQuery<T = any> = Partial<FullTextSearchQuery<T>>;
30
26
  /** Union of keys representing special query operators. */
31
27
  export type SpecialQueryTypes = keyof SpecialQuery;
32
28
  /** Array containing all valid special query operator keys. */
@@ -42,11 +38,7 @@ export type QueryObject<T> = LogicalQuery<T> | (ComparisonQueryBody<T> & Special
42
38
  /** Union of all possible query operator keys (logical, comparison, special). */
43
39
  export type QueryTypes = LogicalQueryTypes | ComparisonQueryTypes | SpecialQueryTypes;
44
40
  /** Array containing all valid query operator keys. */
45
- export declare const allQueryTypes: ("$and" | "$or" | "$nor" | "$not" | "$eq" | "$neq" | "$exists" | "$item" | "$in" | "$nin" | "$all" | "$gt" | "$gte" | "$lt" | "$lte" | "$regex" | "$text" | "$geoShape" | "$geoDistance" | "$textSpan")[];
46
- /** Logical operators used within certain query types like $text. */
47
- export type Operator = 'and' | 'or';
48
- /** Array containing all valid logical operators ('and', 'or'). */
49
- export declare const allOperators: Operator[];
41
+ export declare const allQueryTypes: ("$and" | "$or" | "$nor" | "$not" | "$eq" | "$neq" | "$exists" | "$item" | "$in" | "$nin" | "$all" | "$gt" | "$gte" | "$lt" | "$lte" | "$regex" | "$fts" | "$geoShape" | "$geoDistance")[];
50
42
  /** Represents an AND logical query. All sub-queries must be true. */
51
43
  export type LogicalAndQuery<T = any> = {
52
44
  $and: readonly Query<T>[];
@@ -126,11 +118,24 @@ export type ComparisonRegexQuery = {
126
118
  flags: string;
127
119
  };
128
120
  };
129
- /** Represents a full-text search query ($text). */
130
- export type ComparisonTextQuery = {
131
- $text: string | {
132
- text: string;
133
- operator?: Operator;
121
+ /** Represents a full-text search query. */
122
+ export type ComparisonFtsQuery = {
123
+ $fts: string | {
124
+ query: string;
125
+ /**
126
+ * The search method to use.
127
+ * - 'vector': (Default) Standard full-text search using tsvector and tsquery.
128
+ * - 'similarity': Trigram-based similarity search using the pg_trgm extension.
129
+ */
130
+ method?: 'vector' | 'similarity';
131
+ /**
132
+ * The parser to use for the query. Only applicable for 'vector' method.
133
+ */
134
+ parser?: FtsParser;
135
+ /**
136
+ * The text search configuration (e.g., 'english', 'simple'). Can also be a SQL object for dynamic configuration. Only applicable for 'vector' method.
137
+ */
138
+ language?: string | SQL;
134
139
  };
135
140
  };
136
141
  /** Defines the possible spatial relationships for geospatial shape queries. */
@@ -157,16 +162,46 @@ export type ComparisonGeoDistanceQuery = {
157
162
  minDistance?: number;
158
163
  };
159
164
  };
160
- /** Defines the modes for text span queries, affecting how multiple field matches are handled. */
161
- export type TextSpanQueryMode = 'best' | 'most' | 'cross';
162
- /** Array containing all valid text span query modes. */
163
- export declare const allTextSpanQueryModes: TextSpanQueryMode[];
164
- /** Represents a text span query ($textSpan), searching for text across multiple fields. */
165
- export type TextSpanQuery<T = any> = {
166
- $textSpan: {
165
+ /** Represents a full-text search query across one or more fields. Used by the `search` repository method. */
166
+ export type FullTextSearchQuery<T = any> = {
167
+ $fts: {
167
168
  fields: readonly (Extract<keyof T, string>)[];
168
- text: string;
169
- mode?: TextSpanQueryMode;
170
- operator?: Operator;
169
+ text: string | SQL<string>;
170
+ /**
171
+ * The search method to use.
172
+ * - 'vector': (Default) Standard full-text search using tsvector and tsquery.
173
+ * - 'trigram': Trigram-based similarity search using the pg_trgm extension.
174
+ */
175
+ method?: 'vector' | 'trigram';
176
+ vector?: {
177
+ /**
178
+ * The parser to use for the query. Only applicable for 'vector' method.
179
+ */
180
+ parser?: FtsParser;
181
+ /**
182
+ * The text search configuration (e.g., 'english', 'simple'). Can also be a SQL object for dynamic configuration. Only applicable for 'vector' method.
183
+ */
184
+ language?: string | SQL<string>;
185
+ /**
186
+ * Assigns weights to fields for ranking.
187
+ * Keys are field names from `fields`, values are 'A', 'B', 'C', or 'D'.
188
+ * Fields without a specified weight will use the default. Only applicable for 'vector' method.
189
+ */
190
+ weights?: Partial<Record<Extract<keyof T, string>, 'A' | 'B' | 'C' | 'D'>>;
191
+ };
192
+ trigram?: {
193
+ /**
194
+ * Type of similarity to use for 'trigram' search.
195
+ * - 'normal': Standard trigram similarity (default).
196
+ * - 'word': Word-based similarity.
197
+ * - 'strict-word': Strict word-based similarity.
198
+ * @default 'normal'
199
+ */
200
+ type?: 'phrase' | 'word' | 'strict-word';
201
+ /**
202
+ * Threshold for similarity matching (0 to 1). Only applicable for 'trigram' method.
203
+ */
204
+ threshold?: number | SQL<number>;
205
+ };
171
206
  };
172
207
  };
package/orm/query.js CHANGED
@@ -1,12 +1,8 @@
1
1
  /** Array containing all valid logical query operator keys. */
2
2
  export const allLogicalQueryTypes = ['$and', '$or', '$nor'];
3
3
  /** Array containing all valid comparison query operator keys. */
4
- export const allComparisonQueryTypes = ['$all', '$not', '$eq', '$exists', '$gt', '$gte', '$in', '$item', '$lt', '$lte', '$neq', '$nin', '$regex', '$text', '$geoDistance', '$geoShape'];
4
+ export const allComparisonQueryTypes = ['$all', '$not', '$eq', '$exists', '$gt', '$gte', '$in', '$item', '$lt', '$lte', '$neq', '$nin', '$regex', '$fts', '$geoDistance', '$geoShape'];
5
5
  /** Array containing all valid special query operator keys. */
6
- export const allSpecialQueryTypes = ['$textSpan'];
6
+ export const allSpecialQueryTypes = ['$fts'];
7
7
  /** Array containing all valid query operator keys. */
8
8
  export const allQueryTypes = [...allLogicalQueryTypes, ...allComparisonQueryTypes, ...allSpecialQueryTypes];
9
- /** Array containing all valid logical operators ('and', 'or'). */
10
- export const allOperators = ['and', 'or'];
11
- /** Array containing all valid text span query modes. */
12
- export const allTextSpanQueryModes = ['best', 'most', 'cross'];