@getstrata/core 0.3.0 → 0.3.2

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.
@@ -9,6 +9,7 @@ declare const CORE_CACHE_TOKEN = "core.cache";
9
9
  declare const CORE_QUEUE_TOKEN = "core.queue";
10
10
  declare const CORE_POLICY_GATE_TOKEN = "core.policyGate";
11
11
  declare const CORE_AUTH_TOKEN = "core.auth";
12
+ declare const CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
12
13
  declare const AUTH_DEV_HEADERS_CONFIG_KEY = "auth.allowDevHeaders";
13
14
  declare const DEFAULT_APP_PORT = 3000;
14
15
  declare const DEFAULT_CACHE_TTL_MS = 3600000;
@@ -16,4 +17,4 @@ declare const DEFAULT_CACHE_MAX_ENTRIES = 100;
16
17
  declare const DEFAULT_CACHE_DRIVER = "array";
17
18
  declare const DEFAULT_API_TOKEN = "";
18
19
  declare const DEFAULT_QUEUE_DRIVER = "sync";
19
- export { APP_PORT_CONFIG_KEY, AUTH_DEV_HEADERS_CONFIG_KEY, CACHE_DRIVER_CONFIG_KEY, CACHE_MAX_ENTRIES_CONFIG_KEY, CACHE_TTL_MS_CONFIG_KEY, CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, DATABASE_URL_CONFIG_KEY, DEFAULT_API_TOKEN, DEFAULT_APP_PORT, DEFAULT_CACHE_DRIVER, DEFAULT_CACHE_MAX_ENTRIES, DEFAULT_CACHE_TTL_MS, DEFAULT_QUEUE_DRIVER, REDIS_URL_CONFIG_KEY, };
20
+ export { APP_PORT_CONFIG_KEY, AUTH_DEV_HEADERS_CONFIG_KEY, CACHE_DRIVER_CONFIG_KEY, CACHE_MAX_ENTRIES_CONFIG_KEY, CACHE_TTL_MS_CONFIG_KEY, CORE_AUTH_TOKEN, CORE_CACHE_TOKEN, CORE_CONFIG_TOKEN, CORE_POLICY_GATE_TOKEN, CORE_QUEUE_TOKEN, CORE_TOKEN_SERVICE_TOKEN, DATABASE_URL_CONFIG_KEY, DEFAULT_API_TOKEN, DEFAULT_APP_PORT, DEFAULT_CACHE_DRIVER, DEFAULT_CACHE_MAX_ENTRIES, DEFAULT_CACHE_TTL_MS, DEFAULT_QUEUE_DRIVER, REDIS_URL_CONFIG_KEY, };
@@ -1,5 +1,6 @@
1
1
  import { type PaginatedResult } from "../pagination/index.ts";
2
2
  import { type BelongsToRelation, type HasManyRelation } from "./relationships.ts";
3
+ import { RepositoryQuery } from "./repositoryQuery.ts";
3
4
  import type { TableDefinition } from "./table.ts";
4
5
  import type { MutationValues, QueryOptions, QueryWhere, UpdateValues } from "./types.ts";
5
6
  interface DatabaseConnection {
@@ -27,6 +28,8 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
27
28
  forceDeleteById(id: TEntity[PrimaryKey]): Promise<boolean>;
28
29
  restoreById(id: TEntity[PrimaryKey]): Promise<TEntity | null>;
29
30
  withConnection(connection: DatabaseConnection): this;
31
+ getConnection(): DatabaseConnection;
32
+ query(where?: QueryWhere<TEntity>): RepositoryQuery<TEntity, PrimaryKey>;
30
33
  protected findWhere(where: QueryWhere<TEntity>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<TEntity[]>;
31
34
  protected countWhere(where?: QueryWhere<TEntity>, options?: Pick<QueryOptions<TEntity>, "withTrashed" | "onlyTrashed">): Promise<number>;
32
35
  protected averageColumn(column: keyof TEntity & string, where?: QueryWhere<TEntity>): Promise<number>;
@@ -36,9 +39,9 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
36
39
  value: TEntity[K] | null;
37
40
  count: number;
38
41
  }>>;
39
- protected findByHasManyRelation<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, parentId: TParent[LocalKey], options?: Omit<QueryOptions<TEntity>, "where">): Promise<TEntity[]>;
40
- protected loadHasManyForParents<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
41
- protected loadBelongsToForParents<TChild extends object, TParent extends object, ForeignKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], relation: BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>, parentRepository: BaseRepository<TParent, OwnerKey>, options?: Omit<QueryOptions<TParent>, "where">): Promise<Map<TChild[ForeignKey], TParent>>;
42
+ findByHasManyRelation<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, parentId: TParent[LocalKey], options?: Omit<QueryOptions<TEntity>, "where">): Promise<TEntity[]>;
43
+ loadHasManyForParents<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
44
+ loadBelongsToForParents<TChild extends object, TParent extends object, ForeignKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], relation: BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>, parentRepository: BaseRepository<TParent, OwnerKey>, options?: Omit<QueryOptions<TParent>, "where">): Promise<Map<TChild[ForeignKey], TParent>>;
42
45
  }
43
46
  export default BaseRepository;
44
47
  export type { DatabaseConnection };
@@ -5,6 +5,7 @@ export { mapDatabaseError, withDatabaseErrorHandling } from "./errors.ts";
5
5
  export { buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, qualifyColumn, quoteIdentifier, resolveSoftDeleteColumn, } from "./query.ts";
6
6
  export type { BelongsToRelation, HasManyRelation } from "./relationships.ts";
7
7
  export { belongsTo, hasMany, indexBelongsToRelation, indexHasManyRelation, } from "./relationships.ts";
8
+ export { RepositoryQuery } from "./repositoryQuery.ts";
8
9
  export type { TableDefinition } from "./table.ts";
9
10
  export { defineTable } from "./table.ts";
10
11
  export { runInTransaction } from "./transaction.ts";
@@ -0,0 +1,20 @@
1
+ import type BaseRepository from "./baseRepository.ts";
2
+ import type { BelongsToRelation, HasManyRelation } from "./relationships.ts";
3
+ import type { QueryOptions, QueryWhere } from "./types.ts";
4
+ type LoadedRow = Record<string, unknown>;
5
+ declare class RepositoryQuery<TEntity extends object, PrimaryKey extends keyof TEntity & string> {
6
+ private readonly repository;
7
+ private whereClause;
8
+ private queryOptions;
9
+ private readonly eagerLoads;
10
+ constructor(repository: BaseRepository<TEntity, PrimaryKey>, whereClause?: QueryWhere<TEntity>, queryOptions?: Omit<QueryOptions<TEntity>, "where">);
11
+ where(where: QueryWhere<TEntity>): this;
12
+ orderBy(orderBy: QueryOptions<TEntity>["orderBy"]): this;
13
+ limit(limit: number): this;
14
+ withHasMany<TChild extends object, LocalKey extends keyof TEntity & string, ForeignKey extends keyof TChild & string, Alias extends string>(as: Alias, relation: HasManyRelation<TEntity, TChild, LocalKey, ForeignKey>, childRepository: BaseRepository<TChild, keyof TChild & string>, options?: Omit<QueryOptions<TChild>, "where">): this;
15
+ withBelongsTo<TParent extends object, ForeignKey extends keyof TEntity & string, OwnerKey extends keyof TParent & string, Alias extends string>(as: Alias, relation: BelongsToRelation<TEntity, TParent, ForeignKey, OwnerKey>, parentRepository: BaseRepository<TParent, OwnerKey>, options?: Omit<QueryOptions<TParent>, "where">): this;
16
+ get(): Promise<Array<TEntity & LoadedRow>>;
17
+ first(): Promise<(TEntity & LoadedRow) | null>;
18
+ private attach;
19
+ }
20
+ export { RepositoryQuery };
@@ -9,8 +9,13 @@ export { currentAuthUser, runWithAuthUser } from "../core/auth/authContext";
9
9
  export { Policy } from "../core/auth/policy";
10
10
  export { default as CacheRepository } from "../core/cache/repository";
11
11
  export { CACHE_TAGS } from "../core/cache/tags";
12
+ export type { DatabaseConnection } from "../core/database/baseRepository";
12
13
  export { default as BaseRepository } from "../core/database/baseRepository";
14
+ export type { BelongsToRelation, HasManyRelation } from "../core/database/relationships";
15
+ export { belongsTo, hasMany, indexBelongsToRelation, indexHasManyRelation, } from "../core/database/relationships";
16
+ export { RepositoryQuery } from "../core/database/repositoryQuery";
13
17
  export { defineTable } from "../core/database/table";
18
+ export type { QueryOptions, QueryOrder, QueryWhere, } from "../core/database/types";
14
19
  export { BadRequestError, ConflictError, ForbiddenError, NotFoundError, PreconditionFailedError, UnauthorizedError, UnprocessableEntityError, ValidationError, } from "../core/errors/http";
15
20
  export { EventBus } from "../core/events/eventBus";
16
21
  export { auth, cache, config, events, log, mail, policyGate, queue, storage, } from "../core/facades";
package/dist/index.js CHANGED
@@ -699,6 +699,18 @@ function buildDeleteByIdQuery(table, id) {
699
699
  }
700
700
 
701
701
  // ../../src/core/database/relationships.ts
702
+ function hasMany(definition) {
703
+ return {
704
+ type: "hasMany",
705
+ ...definition
706
+ };
707
+ }
708
+ function belongsTo(definition) {
709
+ return {
710
+ type: "belongsTo",
711
+ ...definition
712
+ };
713
+ }
702
714
  function indexHasManyRelation(parents, children, relation) {
703
715
  const groups = new Map;
704
716
  for (const parent of parents) {
@@ -730,6 +742,86 @@ function indexBelongsToRelation(children, parents, relation) {
730
742
  return result;
731
743
  }
732
744
 
745
+ // ../../src/core/database/repositoryQuery.ts
746
+ class RepositoryQuery {
747
+ repository;
748
+ whereClause;
749
+ queryOptions;
750
+ eagerLoads = [];
751
+ constructor(repository, whereClause = {}, queryOptions = {}) {
752
+ this.repository = repository;
753
+ this.whereClause = whereClause;
754
+ this.queryOptions = queryOptions;
755
+ }
756
+ where(where) {
757
+ this.whereClause = { ...this.whereClause, ...where };
758
+ return this;
759
+ }
760
+ orderBy(orderBy) {
761
+ this.queryOptions = { ...this.queryOptions, orderBy };
762
+ return this;
763
+ }
764
+ limit(limit) {
765
+ this.queryOptions = { ...this.queryOptions, limit };
766
+ return this;
767
+ }
768
+ withHasMany(as, relation, childRepository, options = {}) {
769
+ this.eagerLoads.push({
770
+ kind: "hasMany",
771
+ as,
772
+ relation,
773
+ repository: childRepository,
774
+ options
775
+ });
776
+ return this;
777
+ }
778
+ withBelongsTo(as, relation, parentRepository, options = {}) {
779
+ this.eagerLoads.push({
780
+ kind: "belongsTo",
781
+ as,
782
+ relation,
783
+ repository: parentRepository,
784
+ options
785
+ });
786
+ return this;
787
+ }
788
+ async get() {
789
+ const rows = await this.repository.findAll({
790
+ ...this.queryOptions,
791
+ where: this.whereClause
792
+ });
793
+ return await this.attach(rows);
794
+ }
795
+ async first() {
796
+ const rows = await this.get();
797
+ return rows[0] ?? null;
798
+ }
799
+ async attach(rows) {
800
+ if (rows.length === 0 || this.eagerLoads.length === 0) {
801
+ return rows.map((row) => ({ ...row }));
802
+ }
803
+ let result = rows.map((row) => ({ ...row }));
804
+ for (const load of this.eagerLoads) {
805
+ if (load.kind === "hasMany") {
806
+ const relation2 = load.relation;
807
+ const grouped2 = await load.repository.withConnection(this.repository.getConnection()).loadHasManyForParents(rows, relation2, load.options);
808
+ result = result.map((row) => ({
809
+ ...row,
810
+ [load.as]: grouped2.get(row[relation2.localKey]) ?? []
811
+ }));
812
+ continue;
813
+ }
814
+ const relation = load.relation;
815
+ const grouped = await this.repository.loadBelongsToForParents(rows, relation, load.repository, load.options);
816
+ result = result.map((row) => ({
817
+ ...row,
818
+ [load.as]: grouped.get(row[relation.foreignKey])
819
+ }));
820
+ }
821
+ return result;
822
+ }
823
+ }
824
+
733
825
  // ../../src/core/database/baseRepository.ts
734
826
  class BaseRepository {
735
827
  table;
@@ -866,6 +958,12 @@ class BaseRepository {
866
958
  clone.connection = connection;
867
959
  return clone;
868
960
  }
961
+ getConnection() {
962
+ return this.connection;
963
+ }
964
+ query(where = {}) {
965
+ return new RepositoryQuery(this, where);
966
+ }
869
967
  async findWhere(where, options = {}) {
870
968
  return await this.findAll({ ...options, where });
871
969
  }
@@ -2163,6 +2261,9 @@ export {
2163
2261
  log,
2164
2262
  jsonResponse,
2165
2263
  isEtagEnabled,
2264
+ indexHasManyRelation,
2265
+ indexBelongsToRelation,
2266
+ hasMany,
2166
2267
  events,
2167
2268
  etagFromResource,
2168
2269
  defineTable,
@@ -2172,6 +2273,7 @@ export {
2172
2273
  createMetricsMiddleware,
2173
2274
  config,
2174
2275
  cache,
2276
+ belongsTo,
2175
2277
  auth,
2176
2278
  assertIfMatch,
2177
2279
  WebFormRequest,
@@ -2181,6 +2283,7 @@ export {
2181
2283
  SyncQueue,
2182
2284
  StorageManager,
2183
2285
  ServiceContainer,
2286
+ RepositoryQuery,
2184
2287
  PrometheusRegistry,
2185
2288
  PreconditionFailedError,
2186
2289
  Policy,
@@ -1,7 +1,7 @@
1
1
  import type { ServiceProvider } from "../../bootstrap/contracts";
2
2
  declare const userRepositoryToken = "user.repository";
3
3
  declare const apiTokenRepositoryToken = "user.apiTokenRepository";
4
- declare const tokenServiceToken = "user.tokenService";
4
+ declare const tokenServiceToken = "core.tokenService";
5
5
  declare const authServiceToken = "user.authService";
6
6
  declare const oauthIdentityRepositoryToken = "user.oauthIdentityRepository";
7
7
  declare const notificationRepositoryToken = "user.notificationRepository";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Strata — Laravel-inspired Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",