@getstrata/bootstrap 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ export interface SessionUser {
2
2
  id: number;
3
3
  name: string;
4
4
  email: string;
5
+ learn_subscriber?: boolean;
5
6
  }
6
7
  type SqlClient = {
7
8
  unsafe<T>(query: string, params?: readonly unknown[]): Promise<T[]>;
@@ -14,6 +15,7 @@ export declare class CookieSessionStore {
14
15
  constructor(sql: SqlClient, secret: string, cookieName?: string, maxAgeSeconds?: number);
15
16
  cookieHeader(_user: SessionUser, sessionId: string): string;
16
17
  clearCookieHeader(): string;
18
+ private withSecureFlag;
17
19
  create(user: SessionUser): Promise<string>;
18
20
  destroy(sessionId: string): Promise<void>;
19
21
  read(request: Request): Promise<SessionUser | null>;
@@ -0,0 +1,6 @@
1
+ import type { AuthUser } from "./authContext";
2
+ interface AbilityChecker {
3
+ tokenCan(user: AuthUser | null, ability: string): boolean;
4
+ requireAbility(user: AuthUser | null, ability: string): void;
5
+ }
6
+ export type { AbilityChecker };
@@ -9,7 +9,7 @@ declare class PolicyGate {
9
9
  constructor();
10
10
  private readonly policies;
11
11
  register(resource: string, policy: Policy): void;
12
- allows(resource: string, action: keyof Policy, user?: unknown, model?: unknown): boolean;
13
- authorize(resource: string, action: keyof Policy, user?: unknown, model?: unknown): void;
12
+ allows(resource: string, action: string, user?: unknown, model?: unknown): boolean;
13
+ authorize(resource: string, action: string, user?: unknown, model?: unknown): void;
14
14
  }
15
15
  export { Policy, PolicyGate };
@@ -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 };
@@ -0,0 +1,5 @@
1
+ import type { DatabaseConnection } from "./baseRepository";
2
+ declare function bindDatabaseConnection(connection: DatabaseConnection): void;
3
+ declare function getBoundDatabaseConnection(): DatabaseConnection | null;
4
+ declare function resetBoundDatabaseConnection(): void;
5
+ export { bindDatabaseConnection, getBoundDatabaseConnection, resetBoundDatabaseConnection };
@@ -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";
@@ -24,6 +24,7 @@ declare function buildProjectionQuery<TEntity>(table: TableDefinition<TEntity>,
24
24
  text: string;
25
25
  params: unknown[];
26
26
  };
27
+ declare function assertSafeProjectionExpression(expression: string): void;
27
28
  declare function buildGroupedCountQuery<TEntity, K extends keyof TEntity & string>(table: TableDefinition<TEntity>, column: K, where?: QueryWhere<TEntity>, options?: Pick<QueryOptions<TEntity>, "withTrashed" | "onlyTrashed">): {
28
29
  text: string;
29
30
  params: unknown[];
@@ -48,4 +49,4 @@ declare function buildDeleteByIdQuery<TEntity, PrimaryKey extends keyof TEntity
48
49
  text: string;
49
50
  params: unknown[];
50
51
  };
51
- export { buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, qualifyColumn, quoteIdentifier, resolveSoftDeleteColumn, };
52
+ export { assertSafeProjectionExpression, buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, qualifyColumn, quoteIdentifier, resolveSoftDeleteColumn, };
@@ -0,0 +1,4 @@
1
+ import type { DatabaseConnection } from "./baseRepository";
2
+ declare function resolveRepositoryConnection(): DatabaseConnection;
3
+ declare const repositoryConnection: DatabaseConnection;
4
+ export { repositoryConnection, resolveRepositoryConnection };
@@ -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 };
@@ -1,4 +1,4 @@
1
- import type TokenService from "../../modules/user/tokenService";
1
+ import type { AbilityChecker } from "../auth/abilityChecker";
2
2
  import type { Middleware } from "./middleware";
3
- declare function createRequireAbilityMiddleware(tokenService: TokenService): (ability: string) => Middleware;
3
+ declare function createRequireAbilityMiddleware(abilityChecker: AbilityChecker): (ability: string) => Middleware;
4
4
  export { createRequireAbilityMiddleware };
package/dist/index.js CHANGED
@@ -951,12 +951,12 @@ async function requestIdMiddleware(request, next) {
951
951
  }
952
952
 
953
953
  // ../../src/core/http/requireAbilityMiddleware.ts
954
- function createRequireAbilityMiddleware(tokenService) {
954
+ function createRequireAbilityMiddleware(abilityChecker) {
955
955
  return (ability) => {
956
956
  return async (_request, next) => {
957
957
  const user = currentAuthUser();
958
958
  try {
959
- tokenService.requireAbility(user, ability);
959
+ abilityChecker.requireAbility(user, ability);
960
960
  } catch (error) {
961
961
  if (error instanceof ForbiddenError) {
962
962
  return Response.json({ error: error.message }, { status: error.status });
@@ -1508,8 +1508,8 @@ class HttpKernel {
1508
1508
  }
1509
1509
  wrapWebAbility(ability, handler) {
1510
1510
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
1511
- const tokenService = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
1512
- const requireAbility = createRequireAbilityMiddleware(tokenService);
1511
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
1512
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
1513
1513
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
1514
1514
  return withMiddleware(...middleware)(handler);
1515
1515
  }
@@ -1532,8 +1532,8 @@ class HttpKernel {
1532
1532
  return withMiddleware(...middleware)(handler);
1533
1533
  }
1534
1534
  wrapAbility(ability, handler) {
1535
- const tokenService = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
1536
- const requireAbility = createRequireAbilityMiddleware(tokenService);
1535
+ const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
1536
+ const requireAbility = createRequireAbilityMiddleware(abilityChecker);
1537
1537
  const middleware = [...this.group("authenticated"), requireAbility(ability)];
1538
1538
  return withMiddleware(...middleware)(handler);
1539
1539
  }
@@ -1670,10 +1670,16 @@ class CookieSessionStore {
1670
1670
  }
1671
1671
  cookieHeader(_user, sessionId) {
1672
1672
  const payload = `${sessionId}.${this.sign(sessionId)}`;
1673
- return `${this.cookieName}=${payload}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${this.maxAgeSeconds}`;
1673
+ return this.withSecureFlag(`${this.cookieName}=${payload}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${this.maxAgeSeconds}`);
1674
1674
  }
1675
1675
  clearCookieHeader() {
1676
- return `${this.cookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
1676
+ return this.withSecureFlag(`${this.cookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`);
1677
+ }
1678
+ withSecureFlag(header) {
1679
+ if (true) {
1680
+ return header;
1681
+ }
1682
+ return header.includes("Secure") ? header : `${header}; Secure`;
1677
1683
  }
1678
1684
  async create(user) {
1679
1685
  const id = randomBytes3(32).toString("hex");
@@ -1697,14 +1703,19 @@ class CookieSessionStore {
1697
1703
  if (!sessionId || !signature || signature !== this.sign(sessionId)) {
1698
1704
  return null;
1699
1705
  }
1700
- const rows = await this.sql.unsafe(`SELECT s.id, s.user_id, s.expires_at, u.name, u.email
1706
+ const rows = await this.sql.unsafe(`SELECT s.id, s.user_id, s.expires_at, u.name, u.email, u.learn_subscriber
1701
1707
  FROM sessions s
1702
1708
  INNER JOIN users u ON u.id = s.user_id
1703
1709
  WHERE s.id = $1 AND s.expires_at > NOW()`, [sessionId]);
1704
1710
  const row = rows[0];
1705
1711
  if (!row)
1706
1712
  return null;
1707
- return { id: row.user_id, name: row.name, email: row.email };
1713
+ return {
1714
+ id: row.user_id,
1715
+ name: row.name,
1716
+ email: row.email,
1717
+ learn_subscriber: row.learn_subscriber
1718
+ };
1708
1719
  }
1709
1720
  sign(value) {
1710
1721
  return createHash("sha256").update(`${value}.${this.secret}`).digest("hex").slice(0, 32);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "access": "public"
33
33
  },
34
34
  "peerDependencies": {
35
- "@getstrata/core": "^0.3.0",
35
+ "@getstrata/core": "^0.4.0",
36
36
  "typescript": "^5.9.0"
37
37
  }
38
38
  }