@getstrata/core 1.0.4 → 1.0.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 1.0.6
4
+
5
+ - Query `pluck` and `value` on `RepositoryQuery`, `ModelQuery`, and relation queries. `pluck(column)` returns `T[]`; `pluck(column, keyBy)` returns a `Map`. Models apply `$casts` and skip hydration/observers. Generated apps and lockstep packages move to `^1.0.6`.
6
+
7
+ ## 1.0.5
8
+
9
+ - `@getstrata/core/database/mysqlConnection` re-exports the barrel so the barrel and the subpath share one lazy `mysql2` load. Importing both in one process no longer starts two independent loaders.
10
+ - npm publish uses GitHub Actions OIDC trusted publishing and provenance. Configure trusted publishers on all five packages before tagging `v1.0.5`. A failed or partial publish can be retried with Actions, Release, Run workflow; versions already on npm are skipped. The GitHub Release waits until npm succeeds.
11
+
3
12
  ## 1.0.4
4
13
 
5
14
  - `isProductionEnv()` treats `APP_ENV` or `NODE_ENV` `production` as production (case-insensitive), treats `staging` as production, and default-denies unrecognized `APP_ENV` values such as `prod`.
package/README.md CHANGED
@@ -39,7 +39,7 @@ The engine is your choice, and an app should have one primary engine. Postgres a
39
39
 
40
40
  Full-text `tsMatch` is PostgreSQL only.
41
41
 
42
- `eta` and `mysql2` are optional peers. Core loads them with `import()` the first time you render a view or open a MySQL connection. SQLite and Postgres apps do not install `mysql2`. Generated apps add `eta` because the welcome page uses it. MySQL apps also add `mysql2`. The MySQL helpers are exported from `@getstrata/core/database/mysqlConnection`, `@getstrata/core/database`, and the root barrel; importing any of them does not install `mysql2`, and apps should prefer the subpath as with every other core module.
42
+ `eta` and `mysql2` are optional peers. Core loads them with `import()` the first time you render a view or open a MySQL connection. SQLite and Postgres apps do not install `mysql2`. Generated apps add `eta` because the welcome page uses it. MySQL apps also add `mysql2`. The MySQL helpers are exported from `@getstrata/core/database/mysqlConnection`, `@getstrata/core/database`, and the root barrel; importing any of them does not install `mysql2`, and apps should prefer the subpath as with every other core module. The subpath re-exports the barrel, so both share one lazy `mysql2` load.
43
43
 
44
44
  ## Views
45
45
 
@@ -14,6 +14,7 @@ export type { MysqlConnection, MysqlExecutable, MysqlPool } from "./mysqlConnect
14
14
  export { createMysqlConnection, createMysqlConnectionFromPool, createMysqlPool, } from "./mysqlConnection.ts";
15
15
  export type { NamedConnectionEntry } from "./namedConnections.ts";
16
16
  export { getNamedConnection, hasNamedConnection, registerNamedConnection, resetNamedConnections, runOnNamedConnection, unregisterNamedConnection, } from "./namedConnections.ts";
17
+ export { projectPluck, uniqueColumnSelect } from "./pluck.ts";
17
18
  export { buildAdvancedWhereClause, buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildJoinClause, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, parseQualifiedColumn, qualifyColumn, quoteIdentifier, resolveQualifiedColumn, resolveSoftDeleteColumn, } from "./query.ts";
18
19
  export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "./relationships.ts";
19
20
  export { belongsTo, belongsToMany, hasMany, hasManyThrough, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasManyThroughRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "./relationships.ts";
@@ -61,6 +61,10 @@ declare class ModelQuery {
61
61
  onlyTrashed(): this;
62
62
  get(): Promise<Array<Model<Record<string, unknown>, "id">>>;
63
63
  first(): Promise<Model<Record<string, unknown>, "id"> | null>;
64
+ count(): Promise<number>;
65
+ pluck(column: string): Promise<unknown[]>;
66
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
67
+ value(column: string): Promise<unknown>;
64
68
  find(id: unknown): Promise<Model<Record<string, unknown>, "id"> | null>;
65
69
  findOrFail(id: unknown, errorFactory?: (id: unknown) => Error): Promise<Model<Record<string, unknown>, "id">>;
66
70
  then(onfulfilled?: ((value: Array<Model<Record<string, unknown>, "id">>) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
@@ -129,6 +133,10 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
129
133
  static findOrFail(this: object, id: unknown, errorFactory?: (id: unknown) => Error): Promise<Model<Record<string, unknown>, "id">>;
130
134
  static all(this: object, options?: Omit<QueryOptions<object>, "where">): Promise<Array<Model<Record<string, unknown>, "id">>>;
131
135
  static where(this: object, where: QueryWhere<object>): ModelQuery;
136
+ static count(this: object): Promise<number>;
137
+ static pluck(this: object, column: string): Promise<unknown[]>;
138
+ static pluck(this: object, column: string, keyBy: string): Promise<Map<unknown, unknown>>;
139
+ static value(this: object, column: string): Promise<unknown>;
132
140
  static firstWhere(this: object, where: QueryWhere<object>, options?: Omit<QueryOptions<object>, "where">): Promise<Model<Record<string, unknown>, "id"> | null>;
133
141
  static firstOrNew(this: object, where: QueryWhere<object>, values?: Record<string, unknown>): Promise<Model<Record<string, unknown>, "id">>;
134
142
  static firstOrCreate(this: object, where: QueryWhere<object>, values?: Record<string, unknown>): Promise<Model<Record<string, unknown>, "id">>;
@@ -0,0 +1,5 @@
1
+ import type { QuerySelectItem } from "./types.ts";
2
+ declare function uniqueColumnSelect(table: string, columns: readonly string[]): QuerySelectItem[];
3
+ declare function projectPluck<T extends object, K extends keyof T & string>(rows: readonly T[], column: K): Array<T[K]>;
4
+ declare function projectPluck<T extends object, K extends keyof T & string, KK extends keyof T & string>(rows: readonly T[], column: K, keyBy: KK): Map<T[KK], T[K]>;
5
+ export { projectPluck, uniqueColumnSelect };
@@ -40,6 +40,9 @@ declare class HasManyRelationQuery<TParent extends object, ParentKey extends key
40
40
  get(): Promise<RelatedRecord[]>;
41
41
  first(): Promise<RelatedRecord | null>;
42
42
  count(): Promise<number>;
43
+ pluck(column: string): Promise<unknown[]>;
44
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
45
+ value(column: string): Promise<unknown>;
43
46
  then(onfulfilled?: ((value: RelatedRecord[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
44
47
  create(attributes?: Record<string, unknown>): Promise<RelatedRecord>;
45
48
  save(related: RelatedRecord | (Record<string, unknown> & {
@@ -61,6 +64,9 @@ declare class HasOneRelationQuery<TParent extends object, ParentKey extends keyo
61
64
  get(): Promise<RelatedRecord | null>;
62
65
  first(): Promise<RelatedRecord | null>;
63
66
  count(): Promise<number>;
67
+ pluck(column: string): Promise<unknown[]>;
68
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
69
+ value(column: string): Promise<unknown>;
64
70
  then(onfulfilled?: ((value: RelatedRecord | null) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
65
71
  create(attributes?: Record<string, unknown>): Promise<RelatedRecord>;
66
72
  save(related: RelatedRecord | Record<string, unknown>): Promise<RelatedRecord>;
@@ -80,6 +86,10 @@ declare class BelongsToRelationQuery<TChild extends object, ChildKey extends key
80
86
  toExistsClause(parentTable: string): ExistsClause;
81
87
  get(): Promise<RelatedRecord | null>;
82
88
  first(): Promise<RelatedRecord | null>;
89
+ pluck(column: string): Promise<unknown[]>;
90
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
91
+ value(column: string): Promise<unknown>;
92
+ private relatedQuery;
83
93
  then(onfulfilled?: ((value: RelatedRecord | null) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
84
94
  associate(owner: {
85
95
  id?: unknown;
@@ -105,6 +115,10 @@ declare class BelongsToManyRelationQuery<TParent extends object, ParentKey exten
105
115
  get(): Promise<RelatedRecord[]>;
106
116
  first(): Promise<RelatedRecord | null>;
107
117
  count(): Promise<number>;
118
+ pluck(column: string): Promise<unknown[]>;
119
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
120
+ value(column: string): Promise<unknown>;
121
+ private relatedQuery;
108
122
  then(onfulfilled?: ((value: RelatedRecord[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
109
123
  attach(ids: unknown | readonly unknown[]): Promise<void>;
110
124
  toggle(ids: unknown | readonly unknown[]): Promise<void>;
@@ -124,9 +138,13 @@ declare class MorphManyRelationQuery<TParent extends object, ParentKey extends k
124
138
  applyEagerLoad(query: RepositoryQuery<TParent, ParentKey>, alias: string): void;
125
139
  hydrateEager(row: Record<string, unknown>, alias: string): unknown;
126
140
  toExistsClause(parentTable: string): ExistsClause;
141
+ private scopedQuery;
127
142
  get(): Promise<RelatedRecord[]>;
128
143
  first(): Promise<RelatedRecord | null>;
129
144
  count(): Promise<number>;
145
+ pluck(column: string): Promise<unknown[]>;
146
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
147
+ value(column: string): Promise<unknown>;
130
148
  then(onfulfilled?: ((value: RelatedRecord[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
131
149
  create(attributes?: Record<string, unknown>): Promise<RelatedRecord>;
132
150
  }
@@ -142,6 +160,9 @@ declare class MorphOneRelationQuery<TParent extends object, ParentKey extends ke
142
160
  get(): Promise<RelatedRecord | null>;
143
161
  first(): Promise<RelatedRecord | null>;
144
162
  count(): Promise<number>;
163
+ pluck(column: string): Promise<unknown[]>;
164
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
165
+ value(column: string): Promise<unknown>;
145
166
  then(onfulfilled?: ((value: RelatedRecord | null) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
146
167
  create(attributes?: Record<string, unknown>): Promise<RelatedRecord>;
147
168
  }
@@ -157,6 +178,11 @@ declare class MorphToRelationQuery<TChild extends object, ChildKey extends keyof
157
178
  hydrateEager(row: Record<string, unknown>, alias: string): unknown;
158
179
  toExistsClause(parentTable: string): ExistsClause;
159
180
  get(): Promise<RelatedRecord | null>;
181
+ pluck(column: string): Promise<unknown[]>;
182
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
183
+ value(column: string): Promise<unknown>;
184
+ private relatedForCurrentType;
185
+ private relatedQuery;
160
186
  then(onfulfilled?: ((value: RelatedRecord | null) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
161
187
  }
162
188
  declare class HasManyThroughRelationQuery<TParent extends object, ParentKey extends keyof TParent & string, TFar extends object, FarKey extends keyof TFar & string> {
@@ -176,6 +202,10 @@ declare class HasManyThroughRelationQuery<TParent extends object, ParentKey exte
176
202
  get(): Promise<RelatedRecord[]>;
177
203
  first(): Promise<RelatedRecord | null>;
178
204
  count(): Promise<number>;
205
+ pluck(column: string): Promise<unknown[]>;
206
+ pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
207
+ value(column: string): Promise<unknown>;
208
+ private farRows;
179
209
  then(onfulfilled?: ((value: RelatedRecord[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
180
210
  }
181
211
  type AnyRelationQuery = {
@@ -37,6 +37,9 @@ declare class RepositoryQuery<TEntity extends object, PrimaryKey extends keyof T
37
37
  get(): Promise<Array<TEntity & LoadedRow>>;
38
38
  first(): Promise<(TEntity & LoadedRow) | null>;
39
39
  count(): Promise<number>;
40
+ pluck<K extends keyof TEntity & string>(column: K): Promise<Array<TEntity[K]>>;
41
+ pluck<K extends keyof TEntity & string, KK extends keyof TEntity & string>(column: K, keyBy: KK): Promise<Map<TEntity[KK], TEntity[K]>>;
42
+ value<K extends keyof TEntity & string>(column: K): Promise<TEntity[K] | null>;
40
43
  attachToRows(rows: readonly TEntity[]): Promise<Array<TEntity & LoadedRow>>;
41
44
  paginate(options: {
42
45
  page: number;
@@ -47,4 +50,5 @@ declare class RepositoryQuery<TEntity extends object, PrimaryKey extends keyof T
47
50
  private attach;
48
51
  private hydrateEagerLoad;
49
52
  }
53
+ export { projectPluck, uniqueColumnSelect } from "./pluck.ts";
50
54
  export { RepositoryQuery };
@@ -458,6 +458,30 @@ function buildDeleteByIdQuery(table, id) {
458
458
  };
459
459
  }
460
460
 
461
+ // ../../src/core/database/pluck.ts
462
+ function uniqueColumnSelect(table, columns) {
463
+ const seen = new Set;
464
+ const select = [];
465
+ for (const column of columns) {
466
+ if (seen.has(column)) {
467
+ continue;
468
+ }
469
+ seen.add(column);
470
+ select.push({ kind: "column", table, column, as: column });
471
+ }
472
+ return select;
473
+ }
474
+ function projectPluck(rows, column, keyBy) {
475
+ if (keyBy === undefined) {
476
+ return rows.map((row) => row[column]);
477
+ }
478
+ const keyed = new Map;
479
+ for (const row of rows) {
480
+ keyed.set(row[keyBy], row[column]);
481
+ }
482
+ return keyed;
483
+ }
484
+
461
485
  // ../../src/core/database/relationQuery.ts
462
486
  function asWhere(where) {
463
487
  return where;
@@ -474,6 +498,12 @@ function ownerId(owner, ownerKey) {
474
498
  function thenGet(get, onfulfilled, onrejected) {
475
499
  return get().then(onfulfilled ?? undefined, onrejected ?? undefined);
476
500
  }
501
+ function pluckFromQuery(query, column, keyBy) {
502
+ if (!query) {
503
+ return Promise.resolve(keyBy === undefined ? [] : new Map);
504
+ }
505
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
506
+ }
477
507
 
478
508
  class HasManyRelationQuery {
479
509
  parent;
@@ -539,6 +569,12 @@ class HasManyRelationQuery {
539
569
  async count() {
540
570
  return this.scopedQuery().count();
541
571
  }
572
+ async pluck(column, keyBy) {
573
+ return pluckFromQuery(this.scopedQuery(), column, keyBy);
574
+ }
575
+ async value(column) {
576
+ return this.scopedQuery().value(column);
577
+ }
542
578
  then(onfulfilled, onrejected) {
543
579
  return thenGet(() => this.get(), onfulfilled, onrejected);
544
580
  }
@@ -604,6 +640,13 @@ class HasOneRelationQuery {
604
640
  async count() {
605
641
  return this.inner.count();
606
642
  }
643
+ async pluck(column, keyBy) {
644
+ const query = this.inner.limit(1);
645
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
646
+ }
647
+ async value(column) {
648
+ return this.inner.limit(1).value(column);
649
+ }
607
650
  then(onfulfilled, onrejected) {
608
651
  return thenGet(() => this.get(), onfulfilled, onrejected);
609
652
  }
@@ -650,6 +693,23 @@ class BelongsToRelationQuery {
650
693
  return { sql, params: extra.params };
651
694
  }
652
695
  async get() {
696
+ const query = this.relatedQuery();
697
+ if (!query) {
698
+ return null;
699
+ }
700
+ const row = await query.first();
701
+ return row ? this.related.newFromRecord(row) : null;
702
+ }
703
+ async first() {
704
+ return this.get();
705
+ }
706
+ async pluck(column, keyBy) {
707
+ return pluckFromQuery(this.relatedQuery(), column, keyBy);
708
+ }
709
+ async value(column) {
710
+ return this.relatedQuery()?.value(column) ?? null;
711
+ }
712
+ relatedQuery() {
653
713
  const foreign = this.parent.get(this.relation.foreignKey);
654
714
  if (foreign === null || foreign === undefined) {
655
715
  return null;
@@ -659,11 +719,7 @@ class BelongsToRelationQuery {
659
719
  if (this.extraOptions.orderBy) {
660
720
  query = query.orderBy(this.extraOptions.orderBy);
661
721
  }
662
- const row = await query.first();
663
- return row ? this.related.newFromRecord(row) : null;
664
- }
665
- async first() {
666
- return this.get();
722
+ return query;
667
723
  }
668
724
  then(onfulfilled, onrejected) {
669
725
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -751,6 +807,31 @@ class BelongsToManyRelationQuery {
751
807
  const rows = await this.connection().unsafe(`SELECT COUNT(*) AS count FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
752
808
  return Number(rows[0]?.count ?? 0);
753
809
  }
810
+ async pluck(column, keyBy) {
811
+ return pluckFromQuery(await this.relatedQuery(), column, keyBy);
812
+ }
813
+ async value(column) {
814
+ const query = await this.relatedQuery();
815
+ return query ? query.value(column) : null;
816
+ }
817
+ async relatedQuery() {
818
+ const parentId = this.parent.get(this.relation.parentKey);
819
+ const pivotRows = await this.connection().unsafe(`SELECT * FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
820
+ if (pivotRows.length === 0) {
821
+ return null;
822
+ }
823
+ const relatedIds = [
824
+ ...new Set(pivotRows.map((row) => row[this.relation.relatedPivotKey]))
825
+ ];
826
+ let query = this.related.repository().withConnection(this.connection()).query(asWhere({
827
+ [this.relation.relatedKey]: relatedIds,
828
+ ...this.extraWhere
829
+ }));
830
+ if (this.extraOptions.orderBy) {
831
+ query = query.orderBy(this.extraOptions.orderBy);
832
+ }
833
+ return query;
834
+ }
754
835
  then(onfulfilled, onrejected) {
755
836
  return thenGet(() => this.get(), onfulfilled, onrejected);
756
837
  }
@@ -834,13 +915,15 @@ class MorphManyRelationQuery {
834
915
  const sql = `SELECT 1 FROM ${quoteIdentifier(childTable)} WHERE ${qualifyColumn(childTable, this.relation.morphIdKey)} = ${qualifyColumn(parentTable, this.relation.localKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
835
916
  return { sql, params: extra.params };
836
917
  }
837
- async get() {
838
- const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
839
- const rows = await repository.query(asWhere({
918
+ scopedQuery() {
919
+ return this.related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({
840
920
  [this.relation.morphTypeKey]: this.relation.morphType,
841
921
  [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
842
922
  ...this.extraWhere
843
- })).get();
923
+ }));
924
+ }
925
+ async get() {
926
+ const rows = await this.scopedQuery().get();
844
927
  return rows.map((row) => this.related.newFromRecord(row));
845
928
  }
846
929
  async first() {
@@ -848,12 +931,13 @@ class MorphManyRelationQuery {
848
931
  return rows[0] ?? null;
849
932
  }
850
933
  async count() {
851
- const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
852
- return repository.query(asWhere({
853
- [this.relation.morphTypeKey]: this.relation.morphType,
854
- [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
855
- ...this.extraWhere
856
- })).count();
934
+ return this.scopedQuery().count();
935
+ }
936
+ async pluck(column, keyBy) {
937
+ return pluckFromQuery(this.scopedQuery(), column, keyBy);
938
+ }
939
+ async value(column) {
940
+ return this.scopedQuery().value(column);
857
941
  }
858
942
  then(onfulfilled, onrejected) {
859
943
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -904,6 +988,12 @@ class MorphOneRelationQuery {
904
988
  async count() {
905
989
  return this.inner.count();
906
990
  }
991
+ async pluck(column, keyBy) {
992
+ return keyBy === undefined ? this.inner.pluck(column) : this.inner.pluck(column, keyBy);
993
+ }
994
+ async value(column) {
995
+ return this.inner.value(column);
996
+ }
907
997
  then(onfulfilled, onrejected) {
908
998
  return thenGet(() => this.get(), onfulfilled, onrejected);
909
999
  }
@@ -952,15 +1042,31 @@ class MorphToRelationQuery {
952
1042
  };
953
1043
  }
954
1044
  async get() {
1045
+ const query = this.relatedQuery();
1046
+ if (!query) {
1047
+ return null;
1048
+ }
1049
+ const row = await query.first();
1050
+ return row ? this.relatedForCurrentType()?.newFromRecord(row) ?? null : null;
1051
+ }
1052
+ async pluck(column, keyBy) {
1053
+ return pluckFromQuery(this.relatedQuery(), column, keyBy);
1054
+ }
1055
+ async value(column) {
1056
+ return this.relatedQuery()?.value(column) ?? null;
1057
+ }
1058
+ relatedForCurrentType() {
955
1059
  const type = String(this.parent.get(this.relation.morphTypeKey) ?? "");
1060
+ return this.relatedByType[type];
1061
+ }
1062
+ relatedQuery() {
956
1063
  const id = this.parent.get(this.relation.morphIdKey);
957
- const related = this.relatedByType[type];
1064
+ const related = this.relatedForCurrentType();
958
1065
  if (!related || id === null || id === undefined) {
959
1066
  return null;
960
1067
  }
961
1068
  const table = related.repository().getTable();
962
- const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere })).first();
963
- return row ? related.newFromRecord(row) : null;
1069
+ return related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere }));
964
1070
  }
965
1071
  then(onfulfilled, onrejected) {
966
1072
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -1007,10 +1113,7 @@ class HasManyThroughRelationQuery {
1007
1113
  return { sql, params: extra.params };
1008
1114
  }
1009
1115
  async get() {
1010
- const rows = await this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
1011
- ...this.extraOptions,
1012
- where: this.extraWhere
1013
- });
1116
+ const rows = await this.farRows();
1014
1117
  return rows.map((row) => this.related.newFromRecord(row));
1015
1118
  }
1016
1119
  async first() {
@@ -1021,6 +1124,20 @@ class HasManyThroughRelationQuery {
1021
1124
  const rows = await this.get();
1022
1125
  return rows.length;
1023
1126
  }
1127
+ async pluck(column, keyBy) {
1128
+ const rows = await this.farRows();
1129
+ return keyBy === undefined ? projectPluck(rows, column) : projectPluck(rows, column, keyBy);
1130
+ }
1131
+ async value(column) {
1132
+ const values = await this.limit(1).pluck(column);
1133
+ return values[0] ?? null;
1134
+ }
1135
+ async farRows() {
1136
+ return this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
1137
+ ...this.extraOptions,
1138
+ where: this.extraWhere
1139
+ });
1140
+ }
1024
1141
  then(onfulfilled, onrejected) {
1025
1142
  return thenGet(() => this.get(), onfulfilled, onrejected);
1026
1143
  }
@@ -1476,6 +1593,10 @@ function applyCasts(values, casts, direction) {
1476
1593
  }
1477
1594
  return result;
1478
1595
  }
1596
+ function castPluckedValue(modelClass, column, value) {
1597
+ const cast = modelStatics(modelClass).$casts?.[column];
1598
+ return cast ? hydrateValue(value, cast) : value;
1599
+ }
1479
1600
  function applyTimestampsOnCreate(columns, values, enabled) {
1480
1601
  if (!enabled) {
1481
1602
  return values;
@@ -1630,6 +1751,25 @@ class ModelQuery {
1630
1751
  const models = await this.get();
1631
1752
  return models[0] ?? null;
1632
1753
  }
1754
+ async count() {
1755
+ return this.query.count();
1756
+ }
1757
+ async pluck(column, keyBy) {
1758
+ if (keyBy === undefined) {
1759
+ const values = await this.query.pluck(column);
1760
+ return values.map((value) => castPluckedValue(this.modelClass, column, value));
1761
+ }
1762
+ const keyed = await this.query.pluck(column, keyBy);
1763
+ const result = new Map;
1764
+ for (const [key, value] of keyed) {
1765
+ result.set(castPluckedValue(this.modelClass, keyBy, key), castPluckedValue(this.modelClass, column, value));
1766
+ }
1767
+ return result;
1768
+ }
1769
+ async value(column) {
1770
+ const value = await this.query.value(column);
1771
+ return castPluckedValue(this.modelClass, column, value);
1772
+ }
1633
1773
  async find(id) {
1634
1774
  const primaryKey = resolveModelRepository(this.modelClass).getTable().primaryKey;
1635
1775
  return this.where({ [primaryKey]: id }).first();
@@ -1877,6 +2017,16 @@ class Model {
1877
2017
  static where(where) {
1878
2018
  return Model.query.call(this).where(where);
1879
2019
  }
2020
+ static async count() {
2021
+ return Model.query.call(this).count();
2022
+ }
2023
+ static pluck(column, keyBy) {
2024
+ const query = Model.query.call(this);
2025
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
2026
+ }
2027
+ static value(column) {
2028
+ return Model.query.call(this).value(column);
2029
+ }
1880
2030
  static async firstWhere(where, options = {}) {
1881
2031
  let query = Model.query.call(this).where(where);
1882
2032
  if (options.orderBy) {
@@ -1,132 +1 @@
1
- // @bun
2
- // ../../src/core/runtime/optionalPeer.ts
3
- function missingOptionalPeer(packageName, reason, error) {
4
- return new Error(`Install ${packageName} ${reason} (\`bun add ${packageName}\`).`, {
5
- cause: error
6
- });
7
- }
8
-
9
- // ../../src/core/database/mysqlConnection.ts
10
- var MYSQL_SESSION_UTC = "SET time_zone = '+00:00'";
11
- var mysqlModule;
12
- var mysqlPending;
13
- var importMysql = defaultImportMysql;
14
- async function defaultImportMysql() {
15
- return import("mysql2/promise");
16
- }
17
- function resetMysqlLoaderForTests(importer) {
18
- mysqlModule = undefined;
19
- mysqlPending = undefined;
20
- importMysql = importer ? async () => await importer() : defaultImportMysql;
21
- }
22
- function mysqlApi(mod) {
23
- if (typeof mod.createPool === "function") {
24
- return mod;
25
- }
26
- const withDefault = mod;
27
- if (typeof withDefault.default?.createPool === "function") {
28
- return withDefault.default;
29
- }
30
- throw new Error("mysql2/promise did not export createPool.");
31
- }
32
- async function loadMysql() {
33
- if (mysqlModule) {
34
- return mysqlModule;
35
- }
36
- if (!mysqlPending) {
37
- mysqlPending = (async () => {
38
- let mod;
39
- try {
40
- mod = await importMysql();
41
- } catch (error) {
42
- mysqlPending = undefined;
43
- throw missingOptionalPeer("mysql2", "to open a MySQL connection", error);
44
- }
45
- try {
46
- mysqlModule = mysqlApi(mod);
47
- return mysqlModule;
48
- } catch (error) {
49
- mysqlPending = undefined;
50
- throw error;
51
- }
52
- })();
53
- }
54
- return mysqlPending;
55
- }
56
- function rowsFromResult(result) {
57
- if (Array.isArray(result)) {
58
- return result;
59
- }
60
- if (result && typeof result === "object") {
61
- return [result];
62
- }
63
- return [];
64
- }
65
- function createMysqlConnectionFromPool(pool) {
66
- return {
67
- async unsafe(query, params = []) {
68
- const [result] = await pool.execute(query, [...params]);
69
- return rowsFromResult(result);
70
- },
71
- async close() {
72
- if (typeof pool.end === "function") {
73
- await pool.end();
74
- }
75
- }
76
- };
77
- }
78
- function pinSessionToUtc(connection) {
79
- connection.query(MYSQL_SESSION_UTC, (error) => {
80
- if (error) {
81
- console.warn(`[mysql] Could not set the session time zone to UTC; DATETIME comparisons may drift: ${error instanceof Error ? error.message : String(error)}`);
82
- }
83
- });
84
- }
85
- function createPoolFromModule(mysql, url) {
86
- const pool = mysql.createPool({ uri: url, timezone: "Z" });
87
- pool.on("connection", (connection) => {
88
- pinSessionToUtc(connection);
89
- });
90
- return pool;
91
- }
92
- async function createMysqlPool(url) {
93
- return createPoolFromModule(await loadMysql(), url);
94
- }
95
- function createMysqlConnection(url) {
96
- if (!url.trim()) {
97
- throw new Error("MYSQL_URL is not configured. Set url before creating a MySQL pool.");
98
- }
99
- let poolPending;
100
- function ensurePool() {
101
- if (!poolPending) {
102
- poolPending = createMysqlPool(url).catch((error) => {
103
- poolPending = undefined;
104
- throw error;
105
- });
106
- }
107
- return poolPending;
108
- }
109
- return {
110
- async unsafe(query, params = []) {
111
- const [result] = await (await ensurePool()).execute(query, [...params]);
112
- return rowsFromResult(result);
113
- },
114
- async close() {
115
- if (!poolPending) {
116
- return;
117
- }
118
- const pending = poolPending;
119
- poolPending = undefined;
120
- const pool = await pending.catch(() => {
121
- return;
122
- });
123
- await pool?.end();
124
- }
125
- };
126
- }
127
- export {
128
- createMysqlConnection,
129
- createMysqlConnectionFromPool,
130
- createMysqlPool,
131
- resetMysqlLoaderForTests
132
- };
1
+ export * from "../../index.js";
@@ -1,4 +1,28 @@
1
1
  // @bun
2
+ // ../../src/core/database/pluck.ts
3
+ function uniqueColumnSelect(table, columns) {
4
+ const seen = new Set;
5
+ const select = [];
6
+ for (const column of columns) {
7
+ if (seen.has(column)) {
8
+ continue;
9
+ }
10
+ seen.add(column);
11
+ select.push({ kind: "column", table, column, as: column });
12
+ }
13
+ return select;
14
+ }
15
+ function projectPluck(rows, column, keyBy) {
16
+ if (keyBy === undefined) {
17
+ return rows.map((row) => row[column]);
18
+ }
19
+ const keyed = new Map;
20
+ for (const row of rows) {
21
+ keyed.set(row[keyBy], row[column]);
22
+ }
23
+ return keyed;
24
+ }
25
+
2
26
  // ../../src/core/database/query.ts
3
27
  import { currentSqlDialect } from "@getstrata/core/database/dialect";
4
28
  function quoteIdentifier(identifier) {
@@ -875,6 +899,28 @@ class RepositoryQuery {
875
899
  async count() {
876
900
  return await this.repository.count(this.buildOptions());
877
901
  }
902
+ async pluck(column, keyBy) {
903
+ const rows = await this.repository.findAll({
904
+ ...this.buildOptions(),
905
+ select: uniqueColumnSelect(this.repository.getTable().name, keyBy === undefined || keyBy === column ? [column] : [column, keyBy])
906
+ });
907
+ if (keyBy === undefined) {
908
+ return projectPluck(rows, column);
909
+ }
910
+ return projectPluck(rows, column, keyBy);
911
+ }
912
+ async value(column) {
913
+ const rows = await this.repository.findAll({
914
+ ...this.buildOptions(),
915
+ select: uniqueColumnSelect(this.repository.getTable().name, [column]),
916
+ limit: 1
917
+ });
918
+ const row = rows[0];
919
+ if (row === undefined) {
920
+ return null;
921
+ }
922
+ return row[column];
923
+ }
878
924
  async attachToRows(rows) {
879
925
  return await this.attach(rows);
880
926
  }
@@ -980,5 +1026,7 @@ class RepositoryQuery {
980
1026
  }
981
1027
  }
982
1028
  export {
983
- RepositoryQuery
1029
+ RepositoryQuery,
1030
+ projectPluck,
1031
+ uniqueColumnSelect
984
1032
  };
@@ -45,14 +45,14 @@ export type { Migration, MigrationDatabase, MigrationStatus, } from "../core/dat
45
45
  export type { CastType, GlobalScopeFn, ModelConstructor } from "../core/database/model.ts";
46
46
  export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, hydrateValue, Model, ModelQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelClass, registerModelRepository, } from "../core/database/model.ts";
47
47
  export type { MysqlConnection, MysqlExecutable, MysqlPool, } from "../core/database/mysqlConnection.ts";
48
- export { createMysqlConnection, createMysqlConnectionFromPool, createMysqlPool, } from "../core/database/mysqlConnection.ts";
48
+ export { createMysqlConnection, createMysqlConnectionFromPool, createMysqlPool, resetMysqlLoaderForTests, } from "../core/database/mysqlConnection.ts";
49
49
  export { getNamedConnection, hasNamedConnection, registerNamedConnection, resetNamedConnections, runOnNamedConnection, unregisterNamedConnection, } from "../core/database/namedConnections.ts";
50
50
  export { buildAdvancedWhereClause, buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildJoinClause, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, parseQualifiedColumn, qualifyColumn, quoteIdentifier, resolveQualifiedColumn, resolveSoftDeleteColumn, } from "../core/database/query.ts";
51
51
  export { createDatabaseQueryProxy } from "../core/database/queryProxy.ts";
52
52
  export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "../core/database/relationships.ts";
53
53
  export { belongsTo, belongsToMany, hasMany, hasManyThrough, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasManyThroughRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "../core/database/relationships.ts";
54
54
  export { repositoryConnection, resolveRepositoryConnection, } from "../core/database/repositoryConnection.ts";
55
- export { RepositoryQuery } from "../core/database/repositoryQuery.ts";
55
+ export { projectPluck, RepositoryQuery, uniqueColumnSelect, } from "../core/database/repositoryQuery.ts";
56
56
  export type { BlueprintAction, BlueprintCallback, ColumnKind, DatabaseDriver, ForeignKeyOptions, Grammar, IndexDefinition, IndexKind, ResolveDatabaseDriverOptions, SchemaBuilder, } from "../core/database/schema/index.ts";
57
57
  export { Blueprint, ColumnDefinition, compileBlueprint, createSchemaBuilder, ForeignIdColumnDefinition, grammarForDriver, inferReferencedTable, MySqlGrammar, PostgresGrammar, resolveDatabaseDriver, Schema, SqliteGrammar, UnsupportedSchemaFeatureError, } from "../core/database/schema/index.ts";
58
58
  export { loadSeedersFromDirectory, runSeedersFromDirectory, } from "../core/database/seeders/runner.ts";
package/dist/index.js CHANGED
@@ -2857,6 +2857,30 @@ function indexMorphToRelation(children, parentsByType, relation) {
2857
2857
  return result;
2858
2858
  }
2859
2859
 
2860
+ // ../../src/core/database/pluck.ts
2861
+ function uniqueColumnSelect(table, columns) {
2862
+ const seen = new Set;
2863
+ const select = [];
2864
+ for (const column of columns) {
2865
+ if (seen.has(column)) {
2866
+ continue;
2867
+ }
2868
+ seen.add(column);
2869
+ select.push({ kind: "column", table, column, as: column });
2870
+ }
2871
+ return select;
2872
+ }
2873
+ function projectPluck(rows, column, keyBy) {
2874
+ if (keyBy === undefined) {
2875
+ return rows.map((row) => row[column]);
2876
+ }
2877
+ const keyed = new Map;
2878
+ for (const row of rows) {
2879
+ keyed.set(row[keyBy], row[column]);
2880
+ }
2881
+ return keyed;
2882
+ }
2883
+
2860
2884
  // ../../src/core/database/whereBuilder.ts
2861
2885
  class WhereBuilder {
2862
2886
  nodes = [];
@@ -3054,6 +3078,28 @@ class RepositoryQuery {
3054
3078
  async count() {
3055
3079
  return await this.repository.count(this.buildOptions());
3056
3080
  }
3081
+ async pluck(column, keyBy) {
3082
+ const rows = await this.repository.findAll({
3083
+ ...this.buildOptions(),
3084
+ select: uniqueColumnSelect(this.repository.getTable().name, keyBy === undefined || keyBy === column ? [column] : [column, keyBy])
3085
+ });
3086
+ if (keyBy === undefined) {
3087
+ return projectPluck(rows, column);
3088
+ }
3089
+ return projectPluck(rows, column, keyBy);
3090
+ }
3091
+ async value(column) {
3092
+ const rows = await this.repository.findAll({
3093
+ ...this.buildOptions(),
3094
+ select: uniqueColumnSelect(this.repository.getTable().name, [column]),
3095
+ limit: 1
3096
+ });
3097
+ const row = rows[0];
3098
+ if (row === undefined) {
3099
+ return null;
3100
+ }
3101
+ return row[column];
3102
+ }
3057
3103
  async attachToRows(rows) {
3058
3104
  return await this.attach(rows);
3059
3105
  }
@@ -3882,6 +3928,12 @@ function ownerId(owner, ownerKey) {
3882
3928
  function thenGet(get, onfulfilled, onrejected) {
3883
3929
  return get().then(onfulfilled ?? undefined, onrejected ?? undefined);
3884
3930
  }
3931
+ function pluckFromQuery(query, column, keyBy) {
3932
+ if (!query) {
3933
+ return Promise.resolve(keyBy === undefined ? [] : new Map);
3934
+ }
3935
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
3936
+ }
3885
3937
 
3886
3938
  class HasManyRelationQuery {
3887
3939
  parent;
@@ -3947,6 +3999,12 @@ class HasManyRelationQuery {
3947
3999
  async count() {
3948
4000
  return this.scopedQuery().count();
3949
4001
  }
4002
+ async pluck(column, keyBy) {
4003
+ return pluckFromQuery(this.scopedQuery(), column, keyBy);
4004
+ }
4005
+ async value(column) {
4006
+ return this.scopedQuery().value(column);
4007
+ }
3950
4008
  then(onfulfilled, onrejected) {
3951
4009
  return thenGet(() => this.get(), onfulfilled, onrejected);
3952
4010
  }
@@ -4012,6 +4070,13 @@ class HasOneRelationQuery {
4012
4070
  async count() {
4013
4071
  return this.inner.count();
4014
4072
  }
4073
+ async pluck(column, keyBy) {
4074
+ const query = this.inner.limit(1);
4075
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
4076
+ }
4077
+ async value(column) {
4078
+ return this.inner.limit(1).value(column);
4079
+ }
4015
4080
  then(onfulfilled, onrejected) {
4016
4081
  return thenGet(() => this.get(), onfulfilled, onrejected);
4017
4082
  }
@@ -4058,6 +4123,23 @@ class BelongsToRelationQuery {
4058
4123
  return { sql, params: extra.params };
4059
4124
  }
4060
4125
  async get() {
4126
+ const query = this.relatedQuery();
4127
+ if (!query) {
4128
+ return null;
4129
+ }
4130
+ const row = await query.first();
4131
+ return row ? this.related.newFromRecord(row) : null;
4132
+ }
4133
+ async first() {
4134
+ return this.get();
4135
+ }
4136
+ async pluck(column, keyBy) {
4137
+ return pluckFromQuery(this.relatedQuery(), column, keyBy);
4138
+ }
4139
+ async value(column) {
4140
+ return this.relatedQuery()?.value(column) ?? null;
4141
+ }
4142
+ relatedQuery() {
4061
4143
  const foreign = this.parent.get(this.relation.foreignKey);
4062
4144
  if (foreign === null || foreign === undefined) {
4063
4145
  return null;
@@ -4067,11 +4149,7 @@ class BelongsToRelationQuery {
4067
4149
  if (this.extraOptions.orderBy) {
4068
4150
  query = query.orderBy(this.extraOptions.orderBy);
4069
4151
  }
4070
- const row = await query.first();
4071
- return row ? this.related.newFromRecord(row) : null;
4072
- }
4073
- async first() {
4074
- return this.get();
4152
+ return query;
4075
4153
  }
4076
4154
  then(onfulfilled, onrejected) {
4077
4155
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -4159,6 +4237,31 @@ class BelongsToManyRelationQuery {
4159
4237
  const rows = await this.connection().unsafe(`SELECT COUNT(*) AS count FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
4160
4238
  return Number(rows[0]?.count ?? 0);
4161
4239
  }
4240
+ async pluck(column, keyBy) {
4241
+ return pluckFromQuery(await this.relatedQuery(), column, keyBy);
4242
+ }
4243
+ async value(column) {
4244
+ const query = await this.relatedQuery();
4245
+ return query ? query.value(column) : null;
4246
+ }
4247
+ async relatedQuery() {
4248
+ const parentId = this.parent.get(this.relation.parentKey);
4249
+ const pivotRows = await this.connection().unsafe(`SELECT * FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
4250
+ if (pivotRows.length === 0) {
4251
+ return null;
4252
+ }
4253
+ const relatedIds = [
4254
+ ...new Set(pivotRows.map((row) => row[this.relation.relatedPivotKey]))
4255
+ ];
4256
+ let query = this.related.repository().withConnection(this.connection()).query(asWhere({
4257
+ [this.relation.relatedKey]: relatedIds,
4258
+ ...this.extraWhere
4259
+ }));
4260
+ if (this.extraOptions.orderBy) {
4261
+ query = query.orderBy(this.extraOptions.orderBy);
4262
+ }
4263
+ return query;
4264
+ }
4162
4265
  then(onfulfilled, onrejected) {
4163
4266
  return thenGet(() => this.get(), onfulfilled, onrejected);
4164
4267
  }
@@ -4242,13 +4345,15 @@ class MorphManyRelationQuery {
4242
4345
  const sql = `SELECT 1 FROM ${quoteIdentifier(childTable)} WHERE ${qualifyColumn(childTable, this.relation.morphIdKey)} = ${qualifyColumn(parentTable, this.relation.localKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
4243
4346
  return { sql, params: extra.params };
4244
4347
  }
4245
- async get() {
4246
- const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
4247
- const rows = await repository.query(asWhere({
4348
+ scopedQuery() {
4349
+ return this.related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({
4248
4350
  [this.relation.morphTypeKey]: this.relation.morphType,
4249
4351
  [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
4250
4352
  ...this.extraWhere
4251
- })).get();
4353
+ }));
4354
+ }
4355
+ async get() {
4356
+ const rows = await this.scopedQuery().get();
4252
4357
  return rows.map((row) => this.related.newFromRecord(row));
4253
4358
  }
4254
4359
  async first() {
@@ -4256,12 +4361,13 @@ class MorphManyRelationQuery {
4256
4361
  return rows[0] ?? null;
4257
4362
  }
4258
4363
  async count() {
4259
- const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
4260
- return repository.query(asWhere({
4261
- [this.relation.morphTypeKey]: this.relation.morphType,
4262
- [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
4263
- ...this.extraWhere
4264
- })).count();
4364
+ return this.scopedQuery().count();
4365
+ }
4366
+ async pluck(column, keyBy) {
4367
+ return pluckFromQuery(this.scopedQuery(), column, keyBy);
4368
+ }
4369
+ async value(column) {
4370
+ return this.scopedQuery().value(column);
4265
4371
  }
4266
4372
  then(onfulfilled, onrejected) {
4267
4373
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -4312,6 +4418,12 @@ class MorphOneRelationQuery {
4312
4418
  async count() {
4313
4419
  return this.inner.count();
4314
4420
  }
4421
+ async pluck(column, keyBy) {
4422
+ return keyBy === undefined ? this.inner.pluck(column) : this.inner.pluck(column, keyBy);
4423
+ }
4424
+ async value(column) {
4425
+ return this.inner.value(column);
4426
+ }
4315
4427
  then(onfulfilled, onrejected) {
4316
4428
  return thenGet(() => this.get(), onfulfilled, onrejected);
4317
4429
  }
@@ -4360,15 +4472,31 @@ class MorphToRelationQuery {
4360
4472
  };
4361
4473
  }
4362
4474
  async get() {
4475
+ const query = this.relatedQuery();
4476
+ if (!query) {
4477
+ return null;
4478
+ }
4479
+ const row = await query.first();
4480
+ return row ? this.relatedForCurrentType()?.newFromRecord(row) ?? null : null;
4481
+ }
4482
+ async pluck(column, keyBy) {
4483
+ return pluckFromQuery(this.relatedQuery(), column, keyBy);
4484
+ }
4485
+ async value(column) {
4486
+ return this.relatedQuery()?.value(column) ?? null;
4487
+ }
4488
+ relatedForCurrentType() {
4363
4489
  const type = String(this.parent.get(this.relation.morphTypeKey) ?? "");
4490
+ return this.relatedByType[type];
4491
+ }
4492
+ relatedQuery() {
4364
4493
  const id = this.parent.get(this.relation.morphIdKey);
4365
- const related = this.relatedByType[type];
4494
+ const related = this.relatedForCurrentType();
4366
4495
  if (!related || id === null || id === undefined) {
4367
4496
  return null;
4368
4497
  }
4369
4498
  const table = related.repository().getTable();
4370
- const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere })).first();
4371
- return row ? related.newFromRecord(row) : null;
4499
+ return related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere }));
4372
4500
  }
4373
4501
  then(onfulfilled, onrejected) {
4374
4502
  return thenGet(() => this.get(), onfulfilled, onrejected);
@@ -4415,10 +4543,7 @@ class HasManyThroughRelationQuery {
4415
4543
  return { sql, params: extra.params };
4416
4544
  }
4417
4545
  async get() {
4418
- const rows = await this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
4419
- ...this.extraOptions,
4420
- where: this.extraWhere
4421
- });
4546
+ const rows = await this.farRows();
4422
4547
  return rows.map((row) => this.related.newFromRecord(row));
4423
4548
  }
4424
4549
  async first() {
@@ -4429,6 +4554,20 @@ class HasManyThroughRelationQuery {
4429
4554
  const rows = await this.get();
4430
4555
  return rows.length;
4431
4556
  }
4557
+ async pluck(column, keyBy) {
4558
+ const rows = await this.farRows();
4559
+ return keyBy === undefined ? projectPluck(rows, column) : projectPluck(rows, column, keyBy);
4560
+ }
4561
+ async value(column) {
4562
+ const values = await this.limit(1).pluck(column);
4563
+ return values[0] ?? null;
4564
+ }
4565
+ async farRows() {
4566
+ return this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
4567
+ ...this.extraOptions,
4568
+ where: this.extraWhere
4569
+ });
4570
+ }
4432
4571
  then(onfulfilled, onrejected) {
4433
4572
  return thenGet(() => this.get(), onfulfilled, onrejected);
4434
4573
  }
@@ -4641,6 +4780,10 @@ function applyCasts(values, casts, direction) {
4641
4780
  }
4642
4781
  return result;
4643
4782
  }
4783
+ function castPluckedValue(modelClass, column, value) {
4784
+ const cast = modelStatics(modelClass).$casts?.[column];
4785
+ return cast ? hydrateValue(value, cast) : value;
4786
+ }
4644
4787
  function applyTimestampsOnCreate(columns, values, enabled) {
4645
4788
  if (!enabled) {
4646
4789
  return values;
@@ -4795,6 +4938,25 @@ class ModelQuery {
4795
4938
  const models = await this.get();
4796
4939
  return models[0] ?? null;
4797
4940
  }
4941
+ async count() {
4942
+ return this.query.count();
4943
+ }
4944
+ async pluck(column, keyBy) {
4945
+ if (keyBy === undefined) {
4946
+ const values = await this.query.pluck(column);
4947
+ return values.map((value) => castPluckedValue(this.modelClass, column, value));
4948
+ }
4949
+ const keyed = await this.query.pluck(column, keyBy);
4950
+ const result = new Map;
4951
+ for (const [key, value] of keyed) {
4952
+ result.set(castPluckedValue(this.modelClass, keyBy, key), castPluckedValue(this.modelClass, column, value));
4953
+ }
4954
+ return result;
4955
+ }
4956
+ async value(column) {
4957
+ const value = await this.query.value(column);
4958
+ return castPluckedValue(this.modelClass, column, value);
4959
+ }
4798
4960
  async find(id) {
4799
4961
  const primaryKey = resolveModelRepository(this.modelClass).getTable().primaryKey;
4800
4962
  return this.where({ [primaryKey]: id }).first();
@@ -5042,6 +5204,16 @@ class Model {
5042
5204
  static where(where) {
5043
5205
  return Model.query.call(this).where(where);
5044
5206
  }
5207
+ static async count() {
5208
+ return Model.query.call(this).count();
5209
+ }
5210
+ static pluck(column, keyBy) {
5211
+ const query = Model.query.call(this);
5212
+ return keyBy === undefined ? query.pluck(column) : query.pluck(column, keyBy);
5213
+ }
5214
+ static value(column) {
5215
+ return Model.query.call(this).value(column);
5216
+ }
5045
5217
  static async firstWhere(where, options = {}) {
5046
5218
  let query = Model.query.call(this).where(where);
5047
5219
  if (options.orderBy) {
@@ -5306,6 +5478,11 @@ var importMysql = defaultImportMysql;
5306
5478
  async function defaultImportMysql() {
5307
5479
  return import("mysql2/promise");
5308
5480
  }
5481
+ function resetMysqlLoaderForTests(importer) {
5482
+ mysqlModule = undefined;
5483
+ mysqlPending = undefined;
5484
+ importMysql = importer ? async () => await importer() : defaultImportMysql;
5485
+ }
5309
5486
  function mysqlApi(mod) {
5310
5487
  if (typeof mod.createPool === "function") {
5311
5488
  return mod;
@@ -10178,6 +10355,7 @@ export {
10178
10355
  parseQualifiedColumn,
10179
10356
  pivotTableName,
10180
10357
  policyGate,
10358
+ projectPluck,
10181
10359
  prometheusRegistry,
10182
10360
  qualifyColumn,
10183
10361
  queue,
@@ -10219,6 +10397,7 @@ export {
10219
10397
  resetMembershipLookupForTests,
10220
10398
  resetMemoryLoginThrottleForTests,
10221
10399
  resetMemoryThrottleForTests,
10400
+ resetMysqlLoaderForTests,
10222
10401
  resetNamedConnections,
10223
10402
  resetSqlDialect,
10224
10403
  resolveApplicationAuth,
@@ -10294,6 +10473,7 @@ export {
10294
10473
  toResourceCollection,
10295
10474
  traceContextStorage,
10296
10475
  trustForwardedFor,
10476
+ uniqueColumnSelect,
10297
10477
  unregisterNamedConnection,
10298
10478
  useSqlDialect,
10299
10479
  validateObject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Strata Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -898,7 +898,7 @@
898
898
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external mysql2",
899
899
  "build:types": "tsc -p tsconfig.types.json",
900
900
  "prepublishOnly": "bun run build && bun ../../scripts/prepare-core-package-publish.ts",
901
- "build:subpaths": "bun build entries/auth/accessControl.ts entries/auth/abilityChecker.ts entries/auth/emailVerification.ts entries/auth/jwt.ts entries/auth/jwtGuard.ts entries/auth/basicAuthGuard.ts entries/auth/tokenAbilityChecker.ts entries/auth/membershipMiddleware.ts entries/auth/membershipScope.ts entries/auth/membershipService.ts entries/auth/oauth/oidcProvider.ts entries/auth/oauth/providers.ts entries/auth/oauth/samlProvider.ts entries/auth/oauth/types.ts entries/auth/password.ts entries/auth/intendedUrlCookie.ts entries/auth/passwordConfirmCookie.ts entries/auth/policy.ts entries/auth/scimAuthMiddleware.ts entries/auth/sessionCookie.ts entries/auth/sessionGuard.ts entries/auth/tokenHash.ts entries/audit/exportAuditLogs.ts entries/audit/siemFormatter.ts entries/admin/formatValue.ts entries/admin/registry.ts entries/admin/types.ts entries/cache/tags.ts entries/cache/createCacheStore.ts entries/cache/repository.ts entries/cache/simpleCache.ts entries/cache/simpleCacheStore.ts entries/config/envSchema.ts entries/contracts/serviceTokens.ts entries/contracts/authUserDirectory.ts entries/contracts/container.ts entries/contracts/di.ts entries/crypto/fieldEncryption.ts entries/crypto/mfaSecret.ts entries/database/mysqlConnection.ts entries/database/errors.ts entries/database/factory.ts entries/database/migrations.ts entries/database/migrations/types.ts entries/database/model.ts entries/database/query.ts entries/database/relationships.ts entries/database/repositoryQuery.ts entries/database/seeders.ts entries/database/seeders/types.ts entries/database/schema.ts entries/database/sqliteConnection.ts entries/database/table.ts entries/database/types.ts entries/database/whereBuilder.ts entries/facades.ts entries/http/authMiddleware.ts entries/http/authorizeMiddleware.ts entries/http/bodySizeLimitMiddleware.ts entries/http/clientIp.ts entries/http/cookies.ts entries/http/contentNegotiation.ts entries/http/conditionalResponse.ts entries/http/corsMiddleware.ts entries/http/csrfMiddleware.ts entries/http/csrfProtection.ts entries/http/csrfToken.ts entries/http/etag.ts entries/http/flashSession.ts entries/http/flashMiddleware.ts entries/http/formRequest.ts entries/http/metricsMiddleware.ts entries/http/memoryThrottleMiddleware.ts entries/http/pagination.ts entries/http/parseFormBody.ts entries/http/parseMultipartUpload.ts entries/http/requireAbilityMiddleware.ts entries/http/requireAuthMiddleware.ts entries/http/requireGlobalAdminMiddleware.ts entries/http/requireWebAuthMiddleware.ts entries/http/requireVerifiedMiddleware.ts entries/http/requirePasswordConfirmMiddleware.ts entries/http/resources.ts entries/http/response.ts entries/http/route.ts entries/http/routeMiddleware.ts entries/http/routeModelBinding.ts entries/http/safeInternalPath.ts entries/http/signedUrl.ts entries/http/statelessAuth.ts entries/http/scimThrottleMiddleware.ts entries/http/securityHeadersMiddleware.ts entries/http/securedRouteModelBinding.ts entries/http/webFormRequest.ts entries/http/throttleMiddleware.ts entries/http/validation.ts entries/jobs/exportAuditLogsJob.ts entries/jobs/invalidateCacheTagsJob.ts entries/logging/logger.ts entries/logging/requestLoggingMiddleware.ts entries/mail/mailer.ts entries/mail/markdownMail.ts entries/mail/markdownMailable.ts entries/mail/sanitizeMailHtml.ts entries/media/imageTransform.ts entries/metrics/prometheus.ts entries/openapi/generator.ts entries/openapi/registeredRoute.ts entries/openapi/validate.ts entries/pagination.ts entries/queue.ts entries/queue/createAppQueue.ts entries/queue/failedJobRepository.ts entries/queue/failedJobService.ts entries/queue/jobRunner.ts entries/queue/queueMetrics.ts entries/queue/publicQueue.ts entries/queue/redisQueue.ts entries/queue/types.ts entries/runtime/appEnv.ts entries/runtime/frontendMode.ts entries/runtime/asyncContextStore.ts entries/scheduler/schedule.ts entries/scheduler/osCron.ts entries/security/oauthState.ts entries/security/publicReads.ts entries/security/recoveryCodes.ts entries/security/safeFetch.ts entries/security/scimTenantTokens.ts entries/security/stripeWebhook.ts entries/security/timingSafeCompare.ts entries/security/tokenExpiry.ts entries/security/totp.ts entries/storage/storage.ts entries/tenant/tenancyConfig.ts entries/tenant/tenantDatabaseScope.ts entries/tenant/databaseTenantContext.ts entries/terminal/runShell.ts entries/tracing/tracingMiddleware.ts entries/validation/rules.ts --outdir dist --root . --target bun --external bun --external eta --external mysql2 --external @getstrata/core --external @getstrata/core/auth/accessControl --external @getstrata/core/auth/abilityChecker --external @getstrata/core/auth/authContext --external @getstrata/core/auth/emailVerification --external @getstrata/core/auth/guard --external @getstrata/core/auth/jwt --external @getstrata/core/auth/jwtGuard --external @getstrata/core/auth/basicAuthGuard --external @getstrata/core/auth/tokenAbilityChecker --external @getstrata/core/auth/membershipContext --external @getstrata/core/auth/membershipMiddleware --external @getstrata/core/auth/membershipScope --external @getstrata/core/auth/membershipService --external @getstrata/core/auth/oauth/oidcProvider --external @getstrata/core/auth/oauth/providers --external @getstrata/core/auth/oauth/samlProvider --external @getstrata/core/auth/oauth/types --external @getstrata/core/auth/password --external @getstrata/core/auth/intendedUrlCookie --external @getstrata/core/auth/passwordConfirmCookie --external @getstrata/core/auth/policy --external @getstrata/core/auth/scimAuthMiddleware --external @getstrata/core/auth/sessionCookie --external @getstrata/core/auth/sessionGuard --external @getstrata/core/auth/tokenHash --external @getstrata/core/audit/exportAuditLogs --external @getstrata/core/audit/siemFormatter --external @getstrata/core/admin/formatValue --external @getstrata/core/admin/registry --external @getstrata/core/admin/types --external @getstrata/core/cache/tags --external @getstrata/core/cache/createCacheStore --external @getstrata/core/cache/repository --external @getstrata/core/cache/simpleCache --external @getstrata/core/cache/simpleCacheStore --external @getstrata/core/config/envSchema --external @getstrata/core/contracts/serviceTokens --external @getstrata/core/contracts/authUserDirectory --external @getstrata/core/contracts/container --external @getstrata/core/contracts/di --external @getstrata/core/crypto/fieldEncryption --external @getstrata/core/crypto/mfaSecret --external @getstrata/core/database --external @getstrata/core/database/baseRepository --external @getstrata/core/database/bindConnection --external @getstrata/core/database/boundConnection --external @getstrata/core/database/bunSql --external @getstrata/core/database/connection --external @getstrata/core/database/connectionContext --external @getstrata/core/database/defaultConnection --external @getstrata/core/database/dialect --external @getstrata/core/database/mysqlConnection --external @getstrata/core/database/namedConnections --external @getstrata/core/database/errors --external @getstrata/core/database/factory --external @getstrata/core/database/migrations --external @getstrata/core/database/migrations/types --external @getstrata/core/database/model --external @getstrata/core/database/query --external @getstrata/core/database/relationships --external @getstrata/core/database/repositoryConnection --external @getstrata/core/database/repositoryQuery --external @getstrata/core/database/seeders --external @getstrata/core/database/seeders/types --external @getstrata/core/database/schema --external @getstrata/core/database/sqliteConnection --external @getstrata/core/database/table --external @getstrata/core/database/transaction --external @getstrata/core/database/types --external @getstrata/core/database/whereBuilder --external @getstrata/core/errors/http --external @getstrata/core/events --external @getstrata/core/facades --external @getstrata/core/http --external @getstrata/core/http/authMiddleware --external @getstrata/core/http/authorizeMiddleware --external @getstrata/core/http/bodySizeLimitMiddleware --external @getstrata/core/http/clientIp --external @getstrata/core/http/cookies --external @getstrata/core/http/contentNegotiation --external @getstrata/core/http/contentSecurityPolicy --external @getstrata/core/http/conditionalResponse --external @getstrata/core/http/corsMiddleware --external @getstrata/core/http/csrfMiddleware --external @getstrata/core/http/csrfProtection --external @getstrata/core/http/csrfToken --external @getstrata/core/http/etag --external @getstrata/core/http/flashSession --external @getstrata/core/http/flashMiddleware --external @getstrata/core/http/formRequest --external @getstrata/core/http/middleware --external @getstrata/core/http/metricsMiddleware --external @getstrata/core/http/loginThrottleMiddleware --external @getstrata/core/http/memoryThrottleMiddleware --external @getstrata/core/http/pagination --external @getstrata/core/http/parseFormBody --external @getstrata/core/http/parseMultipartUpload --external @getstrata/core/http/requireAbilityMiddleware --external @getstrata/core/http/requireAuthMiddleware --external @getstrata/core/http/requireGlobalAdminMiddleware --external @getstrata/core/http/requireWebAuthMiddleware --external @getstrata/core/http/requireVerifiedMiddleware --external @getstrata/core/http/requirePasswordConfirmMiddleware --external @getstrata/core/http/resources --external @getstrata/core/http/response --external @getstrata/core/http/route --external @getstrata/core/http/routeMiddleware --external @getstrata/core/http/routeModelBinding --external @getstrata/core/http/safeInternalPath --external @getstrata/core/http/signedUrl --external @getstrata/core/http/statelessAuth --external @getstrata/core/http/scimThrottleMiddleware --external @getstrata/core/http/securityHeadersMiddleware --external @getstrata/core/http/securedRouteModelBinding --external @getstrata/core/http/requestMetaContext --external @getstrata/core/http/webErrorResponse --external @getstrata/core/http/webFormRequest --external @getstrata/core/http/throttleMiddleware --external @getstrata/core/http/validation --external @getstrata/core/jobs/exportAuditLogsJob --external @getstrata/core/jobs/invalidateCacheTagsJob --external @getstrata/core/lifecycle/gracefulShutdown --external @getstrata/core/logging/logger --external @getstrata/core/logging/requestLoggingMiddleware --external @getstrata/core/mail/mailer --external @getstrata/core/mail/markdownMail --external @getstrata/core/mail/markdownMailable --external @getstrata/core/mail/sanitizeMailHtml --external @getstrata/core/media/imageTransform --external @getstrata/core/metrics/prometheus --external @getstrata/core/notifications --external @getstrata/core/openapi/generator --external @getstrata/core/openapi/registeredRoute --external @getstrata/core/openapi/validate --external @getstrata/core/pagination --external @getstrata/core/queue --external @getstrata/core/queue/createAppQueue --external @getstrata/core/queue/failedJobRepository --external @getstrata/core/queue/failedJobService --external @getstrata/core/queue/jobRegistry --external @getstrata/core/queue/jobRunner --external @getstrata/core/queue/queueMetrics --external @getstrata/core/queue/publicQueue --external @getstrata/core/queue/redisQueue --external @getstrata/core/queue/types --external @getstrata/core/runtime/appEnv --external @getstrata/core/runtime/appKeyPrefix --external @getstrata/core/runtime/frontendMode --external @getstrata/core/runtime/applicationRegistry --external @getstrata/core/runtime/asyncContextStore --external @getstrata/core/scheduler/schedule --external @getstrata/core/scheduler/osCron --external @getstrata/core/security/oauthState --external @getstrata/core/security/publicReads --external @getstrata/core/security/recoveryCodes --external @getstrata/core/security/safeFetch --external @getstrata/core/security/safeUrl --external @getstrata/core/security/scimTenantTokens --external @getstrata/core/security/securityEvents --external @getstrata/core/security/stripeWebhook --external @getstrata/core/security/timingSafeCompare --external @getstrata/core/security/tokenExpiry --external @getstrata/core/security/totp --external @getstrata/core/storage/storage --external @getstrata/core/tenant/tenancyConfig --external @getstrata/core/tenant/tenantContext --external @getstrata/core/tenant/tenantDatabaseScope --external @getstrata/core/tenant/databaseTenantContext --external @getstrata/core/tenant/tenantMiddleware --external @getstrata/core/terminal/runShell --external @getstrata/core/tracing/traceContext --external @getstrata/core/tracing/tracingMiddleware --external @getstrata/core/validation/rules --external @getstrata/core/view",
901
+ "build:subpaths": "bun build entries/auth/accessControl.ts entries/auth/abilityChecker.ts entries/auth/emailVerification.ts entries/auth/jwt.ts entries/auth/jwtGuard.ts entries/auth/basicAuthGuard.ts entries/auth/tokenAbilityChecker.ts entries/auth/membershipMiddleware.ts entries/auth/membershipScope.ts entries/auth/membershipService.ts entries/auth/oauth/oidcProvider.ts entries/auth/oauth/providers.ts entries/auth/oauth/samlProvider.ts entries/auth/oauth/types.ts entries/auth/password.ts entries/auth/intendedUrlCookie.ts entries/auth/passwordConfirmCookie.ts entries/auth/policy.ts entries/auth/scimAuthMiddleware.ts entries/auth/sessionCookie.ts entries/auth/sessionGuard.ts entries/auth/tokenHash.ts entries/audit/exportAuditLogs.ts entries/audit/siemFormatter.ts entries/admin/formatValue.ts entries/admin/registry.ts entries/admin/types.ts entries/cache/tags.ts entries/cache/createCacheStore.ts entries/cache/repository.ts entries/cache/simpleCache.ts entries/cache/simpleCacheStore.ts entries/config/envSchema.ts entries/contracts/serviceTokens.ts entries/contracts/authUserDirectory.ts entries/contracts/container.ts entries/contracts/di.ts entries/crypto/fieldEncryption.ts entries/crypto/mfaSecret.ts entries/database/errors.ts entries/database/factory.ts entries/database/migrations.ts entries/database/migrations/types.ts entries/database/model.ts entries/database/query.ts entries/database/relationships.ts entries/database/repositoryQuery.ts entries/database/seeders.ts entries/database/seeders/types.ts entries/database/schema.ts entries/database/sqliteConnection.ts entries/database/table.ts entries/database/types.ts entries/database/whereBuilder.ts entries/facades.ts entries/http/authMiddleware.ts entries/http/authorizeMiddleware.ts entries/http/bodySizeLimitMiddleware.ts entries/http/clientIp.ts entries/http/cookies.ts entries/http/contentNegotiation.ts entries/http/conditionalResponse.ts entries/http/corsMiddleware.ts entries/http/csrfMiddleware.ts entries/http/csrfProtection.ts entries/http/csrfToken.ts entries/http/etag.ts entries/http/flashSession.ts entries/http/flashMiddleware.ts entries/http/formRequest.ts entries/http/metricsMiddleware.ts entries/http/memoryThrottleMiddleware.ts entries/http/pagination.ts entries/http/parseFormBody.ts entries/http/parseMultipartUpload.ts entries/http/requireAbilityMiddleware.ts entries/http/requireAuthMiddleware.ts entries/http/requireGlobalAdminMiddleware.ts entries/http/requireWebAuthMiddleware.ts entries/http/requireVerifiedMiddleware.ts entries/http/requirePasswordConfirmMiddleware.ts entries/http/resources.ts entries/http/response.ts entries/http/route.ts entries/http/routeMiddleware.ts entries/http/routeModelBinding.ts entries/http/safeInternalPath.ts entries/http/signedUrl.ts entries/http/statelessAuth.ts entries/http/scimThrottleMiddleware.ts entries/http/securityHeadersMiddleware.ts entries/http/securedRouteModelBinding.ts entries/http/webFormRequest.ts entries/http/throttleMiddleware.ts entries/http/validation.ts entries/jobs/exportAuditLogsJob.ts entries/jobs/invalidateCacheTagsJob.ts entries/logging/logger.ts entries/logging/requestLoggingMiddleware.ts entries/mail/mailer.ts entries/mail/markdownMail.ts entries/mail/markdownMailable.ts entries/mail/sanitizeMailHtml.ts entries/media/imageTransform.ts entries/metrics/prometheus.ts entries/openapi/generator.ts entries/openapi/registeredRoute.ts entries/openapi/validate.ts entries/pagination.ts entries/queue.ts entries/queue/createAppQueue.ts entries/queue/failedJobRepository.ts entries/queue/failedJobService.ts entries/queue/jobRunner.ts entries/queue/queueMetrics.ts entries/queue/publicQueue.ts entries/queue/redisQueue.ts entries/queue/types.ts entries/runtime/appEnv.ts entries/runtime/frontendMode.ts entries/runtime/asyncContextStore.ts entries/scheduler/schedule.ts entries/scheduler/osCron.ts entries/security/oauthState.ts entries/security/publicReads.ts entries/security/recoveryCodes.ts entries/security/safeFetch.ts entries/security/scimTenantTokens.ts entries/security/stripeWebhook.ts entries/security/timingSafeCompare.ts entries/security/tokenExpiry.ts entries/security/totp.ts entries/storage/storage.ts entries/tenant/tenancyConfig.ts entries/tenant/tenantDatabaseScope.ts entries/tenant/databaseTenantContext.ts entries/terminal/runShell.ts entries/tracing/tracingMiddleware.ts entries/validation/rules.ts --outdir dist --root . --target bun --external bun --external eta --external mysql2 --external @getstrata/core --external @getstrata/core/auth/accessControl --external @getstrata/core/auth/abilityChecker --external @getstrata/core/auth/authContext --external @getstrata/core/auth/emailVerification --external @getstrata/core/auth/guard --external @getstrata/core/auth/jwt --external @getstrata/core/auth/jwtGuard --external @getstrata/core/auth/basicAuthGuard --external @getstrata/core/auth/tokenAbilityChecker --external @getstrata/core/auth/membershipContext --external @getstrata/core/auth/membershipMiddleware --external @getstrata/core/auth/membershipScope --external @getstrata/core/auth/membershipService --external @getstrata/core/auth/oauth/oidcProvider --external @getstrata/core/auth/oauth/providers --external @getstrata/core/auth/oauth/samlProvider --external @getstrata/core/auth/oauth/types --external @getstrata/core/auth/password --external @getstrata/core/auth/intendedUrlCookie --external @getstrata/core/auth/passwordConfirmCookie --external @getstrata/core/auth/policy --external @getstrata/core/auth/scimAuthMiddleware --external @getstrata/core/auth/sessionCookie --external @getstrata/core/auth/sessionGuard --external @getstrata/core/auth/tokenHash --external @getstrata/core/audit/exportAuditLogs --external @getstrata/core/audit/siemFormatter --external @getstrata/core/admin/formatValue --external @getstrata/core/admin/registry --external @getstrata/core/admin/types --external @getstrata/core/cache/tags --external @getstrata/core/cache/createCacheStore --external @getstrata/core/cache/repository --external @getstrata/core/cache/simpleCache --external @getstrata/core/cache/simpleCacheStore --external @getstrata/core/config/envSchema --external @getstrata/core/contracts/serviceTokens --external @getstrata/core/contracts/authUserDirectory --external @getstrata/core/contracts/container --external @getstrata/core/contracts/di --external @getstrata/core/crypto/fieldEncryption --external @getstrata/core/crypto/mfaSecret --external @getstrata/core/database --external @getstrata/core/database/baseRepository --external @getstrata/core/database/bindConnection --external @getstrata/core/database/boundConnection --external @getstrata/core/database/bunSql --external @getstrata/core/database/connection --external @getstrata/core/database/connectionContext --external @getstrata/core/database/defaultConnection --external @getstrata/core/database/dialect --external @getstrata/core/database/mysqlConnection --external @getstrata/core/database/namedConnections --external @getstrata/core/database/errors --external @getstrata/core/database/factory --external @getstrata/core/database/migrations --external @getstrata/core/database/migrations/types --external @getstrata/core/database/model --external @getstrata/core/database/query --external @getstrata/core/database/relationships --external @getstrata/core/database/repositoryConnection --external @getstrata/core/database/repositoryQuery --external @getstrata/core/database/seeders --external @getstrata/core/database/seeders/types --external @getstrata/core/database/schema --external @getstrata/core/database/sqliteConnection --external @getstrata/core/database/table --external @getstrata/core/database/transaction --external @getstrata/core/database/types --external @getstrata/core/database/whereBuilder --external @getstrata/core/errors/http --external @getstrata/core/events --external @getstrata/core/facades --external @getstrata/core/http --external @getstrata/core/http/authMiddleware --external @getstrata/core/http/authorizeMiddleware --external @getstrata/core/http/bodySizeLimitMiddleware --external @getstrata/core/http/clientIp --external @getstrata/core/http/cookies --external @getstrata/core/http/contentNegotiation --external @getstrata/core/http/contentSecurityPolicy --external @getstrata/core/http/conditionalResponse --external @getstrata/core/http/corsMiddleware --external @getstrata/core/http/csrfMiddleware --external @getstrata/core/http/csrfProtection --external @getstrata/core/http/csrfToken --external @getstrata/core/http/etag --external @getstrata/core/http/flashSession --external @getstrata/core/http/flashMiddleware --external @getstrata/core/http/formRequest --external @getstrata/core/http/middleware --external @getstrata/core/http/metricsMiddleware --external @getstrata/core/http/loginThrottleMiddleware --external @getstrata/core/http/memoryThrottleMiddleware --external @getstrata/core/http/pagination --external @getstrata/core/http/parseFormBody --external @getstrata/core/http/parseMultipartUpload --external @getstrata/core/http/requireAbilityMiddleware --external @getstrata/core/http/requireAuthMiddleware --external @getstrata/core/http/requireGlobalAdminMiddleware --external @getstrata/core/http/requireWebAuthMiddleware --external @getstrata/core/http/requireVerifiedMiddleware --external @getstrata/core/http/requirePasswordConfirmMiddleware --external @getstrata/core/http/resources --external @getstrata/core/http/response --external @getstrata/core/http/route --external @getstrata/core/http/routeMiddleware --external @getstrata/core/http/routeModelBinding --external @getstrata/core/http/safeInternalPath --external @getstrata/core/http/signedUrl --external @getstrata/core/http/statelessAuth --external @getstrata/core/http/scimThrottleMiddleware --external @getstrata/core/http/securityHeadersMiddleware --external @getstrata/core/http/securedRouteModelBinding --external @getstrata/core/http/requestMetaContext --external @getstrata/core/http/webErrorResponse --external @getstrata/core/http/webFormRequest --external @getstrata/core/http/throttleMiddleware --external @getstrata/core/http/validation --external @getstrata/core/jobs/exportAuditLogsJob --external @getstrata/core/jobs/invalidateCacheTagsJob --external @getstrata/core/lifecycle/gracefulShutdown --external @getstrata/core/logging/logger --external @getstrata/core/logging/requestLoggingMiddleware --external @getstrata/core/mail/mailer --external @getstrata/core/mail/markdownMail --external @getstrata/core/mail/markdownMailable --external @getstrata/core/mail/sanitizeMailHtml --external @getstrata/core/media/imageTransform --external @getstrata/core/metrics/prometheus --external @getstrata/core/notifications --external @getstrata/core/openapi/generator --external @getstrata/core/openapi/registeredRoute --external @getstrata/core/openapi/validate --external @getstrata/core/pagination --external @getstrata/core/queue --external @getstrata/core/queue/createAppQueue --external @getstrata/core/queue/failedJobRepository --external @getstrata/core/queue/failedJobService --external @getstrata/core/queue/jobRegistry --external @getstrata/core/queue/jobRunner --external @getstrata/core/queue/queueMetrics --external @getstrata/core/queue/publicQueue --external @getstrata/core/queue/redisQueue --external @getstrata/core/queue/types --external @getstrata/core/runtime/appEnv --external @getstrata/core/runtime/appKeyPrefix --external @getstrata/core/runtime/frontendMode --external @getstrata/core/runtime/applicationRegistry --external @getstrata/core/runtime/asyncContextStore --external @getstrata/core/scheduler/schedule --external @getstrata/core/scheduler/osCron --external @getstrata/core/security/oauthState --external @getstrata/core/security/publicReads --external @getstrata/core/security/recoveryCodes --external @getstrata/core/security/safeFetch --external @getstrata/core/security/safeUrl --external @getstrata/core/security/scimTenantTokens --external @getstrata/core/security/securityEvents --external @getstrata/core/security/stripeWebhook --external @getstrata/core/security/timingSafeCompare --external @getstrata/core/security/tokenExpiry --external @getstrata/core/security/totp --external @getstrata/core/storage/storage --external @getstrata/core/tenant/tenancyConfig --external @getstrata/core/tenant/tenantContext --external @getstrata/core/tenant/tenantDatabaseScope --external @getstrata/core/tenant/databaseTenantContext --external @getstrata/core/tenant/tenantMiddleware --external @getstrata/core/terminal/runShell --external @getstrata/core/tracing/traceContext --external @getstrata/core/tracing/tracingMiddleware --external @getstrata/core/validation/rules --external @getstrata/core/view",
902
902
  "build:shims": "bun ../../scripts/write-core-shared-shims.ts"
903
903
  },
904
904
  "publishConfig": {